diff --git a/apps/i2pbote/WebContent/META-INF/MANIFEST.MF b/apps/i2pbote/WebContent/META-INF/MANIFEST.MF
deleted file mode 100644
index 5e9495128c0376427420c4189993b3851770b702..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0
-Class-Path: 
-
diff --git a/apps/i2pbote/WebContent/WEB-INF/classes/commons-logging.properties b/apps/i2pbote/WebContent/WEB-INF/classes/commons-logging.properties
deleted file mode 100644
index 8cc45de2c72fdedfe26f504217545289cbe3fa48..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/WEB-INF/classes/commons-logging.properties
+++ /dev/null
@@ -1 +0,0 @@
-org.apache.commons.logging.Log=org.apache.commons.logging.impl.SimpleLog
diff --git a/apps/i2pbote/WebContent/WEB-INF/lib/mail.jar b/apps/i2pbote/WebContent/WEB-INF/lib/mail.jar
deleted file mode 100644
index 09222952eca85801a9ab78763e7ef652c6f8ad45..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/WEB-INF/lib/mail.jar and /dev/null differ
diff --git a/apps/i2pbote/WebContent/WEB-INF/tags/emailIdentity.tag b/apps/i2pbote/WebContent/WEB-INF/tags/emailIdentity.tag
deleted file mode 100644
index 05608be272c995e26110d4cb639a6f325d573c28..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/WEB-INF/tags/emailIdentity.tag
+++ /dev/null
@@ -1,2 +0,0 @@
-<%@ attribute name="index" required="true" description="0-based index of the email identity" %>
-test 
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/WEB-INF/tlds/i2pbote.tld b/apps/i2pbote/WebContent/WEB-INF/tlds/i2pbote.tld
deleted file mode 100644
index bfb1fa4f8f40b85e61b14fa0892ef77752a773b9..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/WEB-INF/tlds/i2pbote.tld
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
- 
-<tlib-version>1.0</tlib-version>
-<short-name>i2pbote</short-name>
-<uri>I2pBoteTags</uri>
-
-<tag>
-    <name>sendEmail</name>
-    <tag-class>i2p.bote.web.SendEmailTag</tag-class>
-    <body-content>empty</body-content>
-    <attribute>
-        <name>recipient</name>
-        <rtexprvalue>true</rtexprvalue>
-        <required>true</required>
-    </attribute>
-    <attribute>
-        <name>message</name>
-        <rtexprvalue>true</rtexprvalue>
-        <required>true</required>
-    </attribute>
-</tag>
-
-<tag>
-    <name>checkForMail</name>
-    <tag-class>i2p.bote.web.CheckMailTag</tag-class>
-    <body-content>empty</body-content>
-</tag>
-
-<tag>
-    <name>numDhtPeers</name>
-    <tag-class>i2p.bote.web.PrintNumDhtPeersTag</tag-class>
-    <body-content>empty</body-content>
-</tag>
-
-<tag>
-    <name>numRelayPeers</name>
-    <tag-class>i2p.bote.web.PrintNumRelayPeersTag</tag-class>
-    <body-content>empty</body-content>
-</tag>
-
-<function>
-    <name>getIdentities</name>
-    <function-class>i2p.bote.web.JSPHelper</function-class>
-    <function-signature>
-        i2p.bote.Identities getIdentities()
-    </function-signature>
-</function>
-
-<function>
-    <name>saveIdentity</name>
-    <function-class>i2p.bote.web.JSPHelper</function-class>
-    <function-signature>
-        boolean saveIdentity(java.lang.String, java.lang.String, java.lang.String, java.lang.String)
-    </function-signature>
-</function>
-    
-<function>
-    <name>deleteIdentity</name>
-    <function-class>i2p.bote.web.JSPHelper</function-class>
-    <function-signature>
-        boolean deleteIdentity(java.lang.String)
-    </function-signature>
-</function>
-
-<function>
-    <name>isCheckingForMail</name>
-    <function-class>i2p.bote.web.JSPHelper</function-class>
-    <function-signature>
-        boolean isCheckingForMail()
-    </function-signature>
-</function>
-
-<function>
-    <name>getMailFolder</name>
-    <function-class>i2p.bote.web.JSPHelper</function-class>
-    <function-signature>
-        i2p.bote.EmailFolder getMailFolder(java.lang.String)
-    </function-signature>
-</function>
-
-</taglib>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/WEB-INF/web.xml b/apps/i2pbote/WebContent/WEB-INF/web.xml
deleted file mode 100644
index 3aa2f2fc9645b429daa5587e279d8ab19fa12fbd..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/WEB-INF/web.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
-	<display-name>i2pbote</display-name>
-	<welcome-file-list>
-		<welcome-file>index.html</welcome-file>
-		<welcome-file>index.htm</welcome-file>
-		<welcome-file>index.jsp</welcome-file>
-		<welcome-file>default.html</welcome-file>
-		<welcome-file>default.htm</welcome-file>
-		<welcome-file>default.jsp</welcome-file>
-	</welcome-file-list>
-	
-    <listener>
-        <listener-class>i2p.bote.web.ServiceInitializer</listener-class>
-    </listener>
-</web-app>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/buttonFrame.jsp b/apps/i2pbote/WebContent/buttonFrame.jsp
deleted file mode 100644
index 3e519a4adb562a74ae70d697932963cb1df94904..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/buttonFrame.jsp
+++ /dev/null
@@ -1,47 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<c:if test="${ib:isCheckingForMail()}">
-    <c:set var="checkingForMail" value="true"/>
-</c:if>
-
-<html>
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <link rel="stylesheet" href="i2pbote.css" />
-    <c:if test="${checkingForMail}">
-        <meta http-equiv="refresh" content="20" />
-    </c:if>
-</head>
-
-<body style="background-color: transparent; margin: 0px;">
-
-<table><tr>
-    <td>
-        <c:if test="${checkingForMail}">
-            <div class="checkmail">
-                <img src="images/wait.gif"/>Checking for mail...
-            </div>
-        </c:if>
-        <c:if test="${!checkingForMail}">
-            <div class="checkmail">
-                <form action="checkMail.jsp" target="_top" method="GET">
-                    <input type="hidden" name="path" value="Inbox"/>
-                    <button type="submit" value="Check Mail">Check Mail</button>
-                </form>
-            </div>
-        </c:if>
-    </td>
-    <td>
-        <form action="newEmail.jsp" target="_top" method="GET">
-            <button type="submit" value="New">New</button>
-        </form>
-    </td>
-</tr></table>
-
-</body>
-</html>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/checkMail.jsp b/apps/i2pbote/WebContent/checkMail.jsp
deleted file mode 100644
index ac66b1e7effd515cb151d146b242ef8512791877..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/checkMail.jsp
+++ /dev/null
@@ -1,26 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<c:choose>
-    <c:when test="${empty ib:getIdentities().all}">
-        <jsp:forward page="noIdentities.jsp"/>
-    </c:when>
-    <c:otherwise>
-        <ib:checkForMail/>
-        <c:if test="${empty param.nextPage}">
-            <c:set var="param.nextPage" value="index.jsp"/>
-        </c:if>
-        <jsp:forward page="${param.nextPage}"/>
-    </c:otherwise>
-</c:choose>
-
-<p>
-TODO checkMailTag, show status at the bottom (or top?),
-point the Inbox link here or just check periodically and when the Check Email button is clicked?
-</p>
-
-<jsp:include page="footer.jsp"/>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/deleteIdentity.jsp b/apps/i2pbote/WebContent/deleteIdentity.jsp
deleted file mode 100644
index 3b6451bcc4cfeede6c9e0f059f2cc18a083db5b9..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/deleteIdentity.jsp
+++ /dev/null
@@ -1,21 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<c:set var="errorMessage" value="${ib:deleteIdentity(param.key)}"/>
-
-<c:if test="${empty errorMessage}">
-    <jsp:forward page="identities.jsp">
-        <jsp:param name="message" value="The email identity has been deleted."/>
-    </jsp:forward>
-</c:if>
-<c:if test="${!empty errorMessage}">
-    <jsp:include page="header.jsp"/>
-    <div class="main">
-        Error: ${errorMessage}
-    </div>
-    <jsp:include page="footer.jsp"/>
-</c:if>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/editIdentity.jsp b/apps/i2pbote/WebContent/editIdentity.jsp
deleted file mode 100644
index 4cd5181ff877d74801069357e66159cda0bb607f..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/editIdentity.jsp
+++ /dev/null
@@ -1,82 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<c:choose>
-    <c:when test="${param.new}">
-        <c:set var="title" value="New Email Identity"/>
-        <c:set var="commitAction" value="Create"/>
-    </c:when>
-    <c:otherwise>
-        <c:set var="title" value="Edit Email Identity"/>
-        <c:set var="commitAction" value="Save"/>
-    </c:otherwise>
-</c:choose>
-
-<jsp:include page="header.jsp">
-    <jsp:param name="title" value="${title}"/>
-</jsp:include>
-
-<div class="errorMessage">
-    ${param.errorMessage}
-</div>
-
-<div class="main">
-    <form name="form" action="saveIdentity.jsp">
-        <table>
-            <tr>
-                <td>
-                    <div style="font-weight: bold;">Public Name:</div>
-                    <div style="font-size: 0.8em;">(required field, shown to recipients)</div>
-                </td>
-                <td>
-                    <input type="text" size="25" name="publicName" value="${param.publicName}"/>
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <div style="font-weight: bold;">Description:</div>
-                    <div style="font-size: 0.8em;">(optional, kept private)</div>
-                </td>
-                <td>
-                    <input type="text" size="25" name="description" value="${param.description}"/>
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <div style="font-weight: bold;">Email Address:</div>
-                    <div style="font-size: 0.8em;">(optional)</div>
-                </td>
-                <td>
-                    <input type="text" size="50" name="emailAddress" value="${param.emailAddress}"/>
-                </td>
-            </tr>
-            <c:if test="${!empty param.key}">
-            <tr>
-                <td style="font-weight: bold; vertical-align: top;">
-                    Email Destination:
-                </td>
-                <td style="font-family: monospace; padding-left: 5px">
-                    <c:set var="key" value="${param.key}"/>
-                    <c:forEach var="i" begin="0" end="${fn:length(key)}" step="64">
-                        ${fn:substring(key, i, i+64)}<br/>
-                    </c:forEach>
-                    <input type="hidden" name="key" value="${param.key}"/>
-                </td>
-            </tr>
-            </c:if>
-        </table>
-        <input type="submit" name="action" value="${commitAction}"/>
-        <input type="submit" name="action" value="Cancel"/>
-    </form>
-
-    <script type="text/javascript" language="JavaScript">
-        document.forms['form'].elements['name'].focus();
-    </script>
-</div>
-
-<jsp:include page="footer.jsp"/>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/folder.jsp b/apps/i2pbote/WebContent/folder.jsp
deleted file mode 100644
index 436301662b39ebb0f84598750b5428584a28848e..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/folder.jsp
+++ /dev/null
@@ -1,26 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<jsp:include page="header.jsp">
-    <jsp:param name="title" value="${param.path}"/>
-</jsp:include>
-
-This is the <b>${param.path}</b> folder.
-
-<table>
-    <tr>
-        <th>Sender</th><th>Subject</th><th>Sent Date</th>
-    </tr>
-    <c:set var="folderName" value="Inbox"/>
-    <c:forEach items="${ib:getMailFolder(folderName).elements}" var="email">
-        <tr>
-            <td>${email.sender}</td><td>${email.subject}</td><td>${email.sentDate}</td>
-        </tr>
-    </c:forEach>
-</table>
-
-<jsp:include page="footer.jsp"/>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/footer.jsp b/apps/i2pbote/WebContent/footer.jsp
deleted file mode 100644
index 691287b6e353f56675fffd37fe96ead589956f33..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/footer.jsp
+++ /dev/null
@@ -1,2 +0,0 @@
-</body>
-</html>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/header.jsp b/apps/i2pbote/WebContent/header.jsp
deleted file mode 100644
index 8075e233c4525596712f42d97548f554c7ed0fa9..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/header.jsp
+++ /dev/null
@@ -1,53 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<html>
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
-    <link rel="stylesheet" href="i2pbote.css" />
-    <link rel="icon" type="image/png" href="images/favicon16.png" />
-    <title>${param.title} - I2P-Bote</title>
-</head>
-
-<body>
-
-<div class="titlebar" style="cursor:pointer" onclick="document.location='.'">
-    <div class="title">I2P-Bote</div>
-    <br/>
-    <div class="subtitle">Secure Distributed Email</div>
-</div>
-
-<div class="menubox">
-    <iframe src="buttonFrame.jsp" width="100%" height="40px" scrolling="no" frameborder="0" allowtransparency="true"></iframe>
-</div>
-
-<div class="menubox">
-    <h2>Folders</h2>
-    <a href=folder.jsp?path=Inbox><img src="images/folder.png"/>Inbox</a><br/>
-    <a href=folder.jsp?path=Outbox><img src="images/folder.png"/>Outbox</a><br/>
-    <a href=folder.jsp?path=Sent><img src="images/folder.png"/>Sent</a><br/>
-    <a href=folder.jsp?path=Drafts><img src="images/folder.png"/>Drafts</a><br/>
-    <a href=folder.jsp?path=Trash><img src="images/folder.png"/>Trash</a><br/>
-    <hr/>
-    <a href=folder.jsp?path=User_created_Folder_1><img src="images/folder.png"/>User_created_Folder_1</a><br/>
-    <a href=folder.jsp?path=User_created_Folder_2><img src="images/folder.png"/>User_created_Folder_2</a><br/>
-    <a href=folder.jsp?path=User_created_Folder_3><img src="images/folder.png"/>User_created_Folder_3</a><br/>
-    <hr/>
-    <a href=.>Manage Folders</a>
-</div>
-
-<div class="menubox">
-    <h2>Addresses</h2>
-    <a href=identities.jsp>Identities</a><br/>
-    <a href=.>Private Address Book</a><br/>
-    <a href=.>Public Address Directory</a><br/>
-</div>
-
-<div class="menubox">
-    <h2>Configuration</h2>
-    <a href=.>Settings</a><br/>
-</div>
diff --git a/apps/i2pbote/WebContent/i2pbote.css b/apps/i2pbote/WebContent/i2pbote.css
deleted file mode 100644
index 0e56379b31fba86a4ebddeee0d46d0436dc93ee0..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/i2pbote.css
+++ /dev/null
@@ -1,318 +0,0 @@
-/* Based on the router console css */
-
-body {
-     margin: 15px 0 0 10px;
-     padding: 0em;
-     text-align: center;
-     background: #eef;
-     color: #000;
-     font: 9pt/130% "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-}
-
-.titlebar {
-    width: 800px;
-    margin: 0px 20px 20px 240px;
-    background-color: #fff;
-    color: #310;
-    padding: 0 15px 15px 25px;
-    -moz-border-radius: 4px;
-    -khtml-border-radius: 4px;
-    border-radius: 4px;
-    border: 1px solid #001;
-    text-align: center;
-    -moz-box-shadow: inset 0px 0px 1px 0px #002;
-    white-space: normal;
-    background: #ddf url('images/lightbluetile.png');
-    opacity: 1.0;
-}
-
-.title {
-    font: normal bold 20pt/120% 'Lucida Sans Unicode', 'Bitstream Vera Sans', Verdana, Tahoma, Helvetica, sans-serif;
-    font-weight: bold;
-    letter-spacing: 0.15em;
-    text-shadow: 0px 0px 1px rgba(0, 0, 148, 0.9);
-}
-
-.subtitle {
-    font-size: 8pt;
-    color: #cf0606;
-    letter-spacing: 5pt;
-    text-shadow: 0px 0px 1px rgba(148, 0, 0, 0.9);
-}
-
-.checkmail {
-    font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-    color: #555;
-}
-
-.menubox {
-    float: left;
-    width: 175px;
-    margin: 0px 0px 10px 10px;
-    padding: 10px;
-    text-align: left;
-    border: 1px solid #000033;
-    background: #ddf url('images/lightbluetile.png');
-    color: #000;
-    font-size: 8pt;
-    clear: left;
-    -moz-border-radius: 4px;
-    -khtml-border-radius: 4px;
-    border-radius: 4px;
-    -moz-box-shadow: inset 0px 0px 1px 0px #002;
-}
-
-.menubox a:link, .menubox a:visited {
-    text-shadow: 0px 0px 1px rgba(0, 0, 32, 0.5);
-}
-
-.menubox a:hover {
-    text-shadow: 0px 0px 0.5px rgba(255, 255, 255, 0.7);
-    color: #f60;
-}
-
-.menubox h2 {
-    font-size: 12pt;
-    color: #001;
-    letter-spacing: 0.05em;
-    text-shadow: 0px 0px 1px rgba(0, 0, 64, 0.5);
-    wordwrap: none;
-}
-
-.main {
-    width: 800px;
-    margin: 0px 20px 20px 240px;
-    padding: 0 15px 15px 25px;
-    background: #eef;
-    font: 9pt/130% "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-    text-align: left;
-    color: #001;
-    border: 1px solid #000033;
-    background: #ddf url('images/lightbluetile.png');
-    -moz-border-radius: 4px;
-    -khtml-border-radius: 4px;
-    border-radius: 4px;
-    -moz-box-shadow: inset 0px 0px 1px 0px #002;
-}
-
-.infoMessage {
-    margin: 0px 20px 5px 240px;
-    text-align: left;
-    color: green;
-}
-    
-.errorMessage {
-    margin: 0px 20px 5px 240px;
-    text-align: left;
-    color: red;
-}
-    
-img {
-    border: none;
-}
-
-a:active {
-    color: #900;
-}
-
-a:link {
-    color: #007;
-    text-decoration: none;
-    font-weight: bold;
-    word-wrap: break-word;
-}
-
-a:visited {
-    color: #606;
-    text-decoration: none;
-    font-weight: bold;
-}
-
-a:hover {
-    color: #f60;
-    text-decoration: underline;
-    font-weight: bold;
-}
-
-a:active {
-    color: #f93;
-    text-decoration: underline;
-    font-weight: bold;
-}
-
-button, button:visited {
-    font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-    border: 1px outset #999;
-    padding: 1px 3px;
-    background: #ddf !important;
-    text-decoration: none;
-    border-radius: 4px;
-    -moz-border-radius: 4px;
-    -khtml-border-radius: 4px;
-    margin: 0 1px;
-    text-align: center;
-    min-width: 80px;
-    -moz-box-shadow: inset 0px 2px 8px 0px #fff;
-    color: #006;
-}
-
-button:hover {
-    border: 1px solid #f60;
-    background: #f60 !important;
-    color: #fff;
-    -moz-box-shadow: inset 0px 0px 0px 1px #fff;
-}
-
-button:active {
-    border: 1px solid #f60;
-    background: #001 !important;
-    color: #f60;
-    -moz-box-shadow: inset 0px 0px 0px 1px #f60;
-}
-
-.underline {
-    border-bottom: 1px solid #000022;
-    padding: 5px 0px 5px 0px;
-    margin: 0px 0px 10px 0px;
-}
-
-input {
-    background: #eef;
-    color: #001;
-    margin: 5px 10px 5px 10px;
-    padding: 4px 2px;
-    font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-    border: 1px solid #001;
-    text-decoration: none;
-    min-width: 110px;
-}
-
-input, input:visited {
-    border: 1px outset #999;
-    background: #ddf;
-    color: #001;
-    margin: 5px;
-    font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-    padding: 1px 2px;
-    text-decoration: none;
-    min-width: 110px;
-    border-radius: 4px;
-    -moz-border-radius: 4px;
-    -khtml-border-radius: 4px;
-    -moz-box-shadow: inset 0px 2px 8px 0px #fff;
-    color: #006;
-    opacity: 0.9;
-}
-
-input:hover {
-    background: #f60;
-    color: #fff;
-    border: 1px solid #f60;
-    opacity: 1.0;
-    -moz-box-shadow: inset 0px 0px 0px 1px #fff;
-}
-
-input:active {
-    background: #002;
-    color: #f60;
-    border: 1px solid #f60;
-    opacity: 1.0;
-    -moz-box-shadow: inset 0px 0px 0px 1px #f60;
-}
-
-input[type=text] {
-    width: 100%;
-    background: #eef;
-    color: #001;
-    margin: 5px;
-    padding: 5px;
-    font: bold 8pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-    border: 1px solid #001;
-    text-decoration: none;
-}
-
-input checkbox {
-    border: 0 !important;
-}
-
-textarea {
-    width: 100%;
-    padding: 5px;
-    margin: 5px;
-    background: #eef;
-    color: #003;
-    border-radius: 4px;
-    -moz-border-radius: 4px;
-    -khtml-border-radius: 4px;
-    font: 8pt "Lucida Console", "DejaVu Sans Mono", Courier, mono;
-    min-height: 100px;
-    border: 1px solid #001;
-}
-
-submit {
-    background: #f00;
-    color: #eef;
-    margin: 10px 2px 10px 0;
-    padding: 2px;
-    font-family: "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-    font-weight: bold;
-    border: 1px solid #001;
-    text-decoration: none;
-}
-
-select {
-    background: #eef;
-    color: #003;
-    padding: 2px;
-    margin: 5px;
-    border: 1px solid #001;
-    font: 9pt "Lucida Sans Unicode", "Bitstream Vera Sans", Verdana, Tahoma, Helvetica, sans-serif;
-    border-radius: 4px;
-    -moz-border-radius: 4px;
-    -khtml-border-radius: 4px;
-    text-align: left !important;
-}
-
-.identities table {
-    table-layout: fixed;
-    width: 800px;
-}
-
-.identities td, .identities th {
-    vertical-align: top;
-    padding: 0px 30px 0px 0px;
-    white-space: nowrap;
-    overflow-x: hidden;
-}
-
-.ellipsis
-{
-    line-height: 1.2em;
-    height: 1.2em;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-    width: 100%;
-    -o-text-overflow: ellipsis;
-    -moz-binding: url(moz_fix2.xml#ellipsis);
-}
-
-.moz-ellipsis > DIV:first-child
-{
-    float: left;
-    margin-right: -26px;
-}
-
-.moz-ellipsis > DIV + DIV
-{
-    float: right;
-    margin-top: -1.2em;
-    background: url('images/lightbluefade.png');
-    padding-left: 26px;
-}
-
-.moz-ellipsis > DIV + DIV::after
-{
-    background: url('images/lightbluetile.png');
-    content: '...';
-}
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/identities.jsp b/apps/i2pbote/WebContent/identities.jsp
deleted file mode 100644
index aec9ac4e9e2d8c568b9db7dfe00768829573f49a..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/identities.jsp
+++ /dev/null
@@ -1,72 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<jsp:include page="header.jsp">
-    <jsp:param name="title" value="Identities"/>
-</jsp:include>
-
-<div class="infoMessage">
-    ${param.infoMessage}
-</div>
-
-<div class="main">
-    <h2>
-        Email Identities
-    </h2>
-
-    <c:if test="${empty ib:getIdentities().all}">
-        No email identities are defined.
-    </c:if>
-    
-    <div class="identities">
-    <table>
-    <tr>
-        <th>Description</th>
-        <th>Public Name</th>
-        <th>Email Address</th>
-        <th>Key</th>
-        <th style="width: 20px; padding: 0px"></th>
-    </tr>
-    <c:forEach items="${ib:getIdentities().all}" var="identity">
-        <tr>
-        <td style="width: 100px;">
-            <div class="ellipsis">
-                <a href="editIdentity.jsp?new=false&key=${identity.key}&publicName=${identity.publicName}&description=${identity.description}&emailAddress=${identity.emailAddress}">
-                    ${identity.publicName}
-                </a>
-            </div>
-        </td>
-        <td style="width: 150px;">
-            <div class="ellipsis">
-                ${identity.description}
-            </div>
-        </td>
-        <td style="width: 150px;">
-            <div class="ellipsis">
-                ${identity.emailAddress}
-            </div>
-        </td>
-        <td style="width: 100px;">
-            <div class="ellipsis">
-                ${identity.key}
-            </div>
-        </td>
-        <td>
-            <a href="deleteIdentity.jsp?key=${identity.key}"><img src="images/delete.png" alt="Delete" title="Delete this identity"/></a>
-        </td>
-        </tr>
-    </c:forEach>
-    </table>
-    </div>
-    
-    <p/>
-    <form action="editIdentity.jsp?new=true" method="POST">
-        <button type="submit" value="New">New Identity</button>
-    </form>
-</div>
-
-<jsp:include page="footer.jsp"/>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/images/darkbluetile.png b/apps/i2pbote/WebContent/images/darkbluetile.png
deleted file mode 100644
index 89df70b960574329efe68890e5c3303e8bbefef4..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/darkbluetile.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/darkerbluetile.png b/apps/i2pbote/WebContent/images/darkerbluetile.png
deleted file mode 100644
index 854d46f40c06681bd6ad567f51264a5198679350..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/darkerbluetile.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/delete.png b/apps/i2pbote/WebContent/images/delete.png
deleted file mode 100644
index 61e146d8135435f8ff628edd61b84bd9921fcbad..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/delete.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/errortriangle.png b/apps/i2pbote/WebContent/images/errortriangle.png
deleted file mode 100644
index f1730ccbf6280167680a8ac21b942faa282a7f21..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/errortriangle.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/favicon16.png b/apps/i2pbote/WebContent/images/favicon16.png
deleted file mode 100644
index ba34d12c7a2c69c077e853bc5f055b20f7697d61..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/favicon16.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/folder.png b/apps/i2pbote/WebContent/images/folder.png
deleted file mode 100644
index fdc6ed0325d19e82bca7c932c237d394ea801a1d..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/folder.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/help.png b/apps/i2pbote/WebContent/images/help.png
deleted file mode 100644
index 4c5136f45383ee25cf0c0b49c39ddb884ae019c3..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/help.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/i2pbote_de.png b/apps/i2pbote/WebContent/images/i2pbote_de.png
deleted file mode 100644
index cac902340ccfcc9b492368af11a619fa39d40d55..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/i2pbote_de.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/i2pbote_en.png b/apps/i2pbote/WebContent/images/i2pbote_en.png
deleted file mode 100644
index 1daf2af2eace06ffd22994041843476786b94f7a..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/i2pbote_en.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/info.png b/apps/i2pbote/WebContent/images/info.png
deleted file mode 100644
index 13f25a9aefc55baaa710c82cf6158c38f890e8e4..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/info.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/lightbluefade.png b/apps/i2pbote/WebContent/images/lightbluefade.png
deleted file mode 100644
index b79947d3efacd5956ec7eb7fc3542bb434687a51..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/lightbluefade.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/lightbluetile.png b/apps/i2pbote/WebContent/images/lightbluetile.png
deleted file mode 100644
index e7fc6c1f12ab57637c3c0858a74ad87643a76b28..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/lightbluetile.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/link.png b/apps/i2pbote/WebContent/images/link.png
deleted file mode 100644
index e0eba7969888b86b7f9b554434dc9750c1218c9e..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/link.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/mail_folder_sent.png b/apps/i2pbote/WebContent/images/mail_folder_sent.png
deleted file mode 100644
index 53298cae7e8b3aa98b23b3e6ccbea36b755a53ba..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/mail_folder_sent.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/mail_mark_unread.png b/apps/i2pbote/WebContent/images/mail_mark_unread.png
deleted file mode 100644
index e07161c1620aaa3ba390f5e177ae4f81986ffcd0..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/mail_mark_unread.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/sandtile.png b/apps/i2pbote/WebContent/images/sandtile.png
deleted file mode 100644
index 4d88cb84740316d941ccd5b6a5ef0de5ccf4e519..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/sandtile.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/tabletile.png b/apps/i2pbote/WebContent/images/tabletile.png
deleted file mode 100644
index f34c0632479b019d2b68c852a705323e6e5767ce..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/tabletile.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/tabletile_alt.png b/apps/i2pbote/WebContent/images/tabletile_alt.png
deleted file mode 100644
index ea691b8e20a44f5e4ac278ad6693b95984c58bb4..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/tabletile_alt.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/tabletilelighter.png b/apps/i2pbote/WebContent/images/tabletilelighter.png
deleted file mode 100644
index 5586bb0635f0adfd89b8898a6e29a60833c7afe7..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/tabletilelighter.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/tabletitlelight.png b/apps/i2pbote/WebContent/images/tabletitlelight.png
deleted file mode 100644
index 9c9092e7372739d8bd1deeabd0c125a81dc29c6e..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/tabletitlelight.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/wait.gif b/apps/i2pbote/WebContent/images/wait.gif
deleted file mode 100644
index cce9681fca0f7aeac8bc6159a4bf734463e920ea..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/wait.gif and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/warning.png b/apps/i2pbote/WebContent/images/warning.png
deleted file mode 100644
index c2b7e8c098c275410860bf6ac25743c25c8b782d..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/warning.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/images/yellowtile.png b/apps/i2pbote/WebContent/images/yellowtile.png
deleted file mode 100644
index 4deb2668419a4e9cea5a0f4951fd9b917cb9e552..0000000000000000000000000000000000000000
Binary files a/apps/i2pbote/WebContent/images/yellowtile.png and /dev/null differ
diff --git a/apps/i2pbote/WebContent/index.jsp b/apps/i2pbote/WebContent/index.jsp
deleted file mode 100644
index 296b30743b854efd96d2d77211db1357e1413297..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/index.jsp
+++ /dev/null
@@ -1,8 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags"  %>
-
-<jsp:forward page="folder.jsp?path=Inbox"/>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/moz_fix2.xml b/apps/i2pbote/WebContent/moz_fix2.xml
deleted file mode 100644
index d308c4a9785ac1f577f0b7369d5d45ef56f1f79b..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/moz_fix2.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0"?>
-<bindings xmlns="http://www.mozilla.org/xbl" xmlns:html="http://www.w3.org/1999/xhtml">
-
-<binding id="ellipsis" applyauthorstyles="false">
-    <implementation>
-        <constructor><![CDATA[
-            (function(block){
-                setTimeout(function(){
-                    block.style.mozBinding = '';
-                    var t = document.createElement('DIV');
-                    block.appendChild(t);
-                    block.appendChild(document.createElement('DIV'));
-                    while (block.firstChild != t)
-                        t.appendChild(block.firstChild);
-                    block.className = block.className + ' moz-ellipsis';
-                }, 0);
-            })(this);
-        ]]></constructor>
-    </implementation>
-</binding>
-
-</bindings>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/newEmail.jsp b/apps/i2pbote/WebContent/newEmail.jsp
deleted file mode 100644
index 6495c50e59bd846b7c9ec11d00bfd579bb70e8bd..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/newEmail.jsp
+++ /dev/null
@@ -1,59 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<jsp:include page="header.jsp">
-    <jsp:param name="title" value="New Email"/>
-</jsp:include>
-
-<div class="main">
-    <form action="sendEmail.jsp">
-        <table>
-            <tr>
-                <td>
-                    From:
-                </td>
-                <td>
-                    <select name="sender">
-                        <option value="anonymous">Anonymous</option>
-                        <c:forEach items="${ib:getIdentities().all}" var="identity">
-                            <option value="${identity.key}">
-                                ${identity.publicName}
-                                <c:if test="${!empty identity.description}"> - ${identity.description}</c:if>
-                            </option>
-                        </c:forEach>
-                    </select>
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <select name="recipientType0">
-                        <option value="to">To:</option>
-                        <option value="cc">CC:</option>
-                        <option value="bcc">BCC:</option>
-                        <option value="reply_to">Reply To:</option>
-                    </select>
-                </td>
-                <td>
-                    <input type="text" size="80" name="recipient0"/>
-                </td>
-            </tr>
-            <tr>
-                <td valign="top"><br/>Message:</td>
-                <td><textarea rows="30" cols="80" name="message"></textarea></td>
-            </tr>
-            <tr>
-                <td colspan=2 align="center">
-                    <input type="submit" value="Send"/>
-                    <input type="submit" value="Cancel"/>
-                    <input type="submit" value="Save"/>
-                </td>
-            </tr>
-        </table>
-    </form>
-</div>
-
-<jsp:include page="footer.jsp"/>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/newIdentity.jsp b/apps/i2pbote/WebContent/newIdentity.jsp
deleted file mode 100644
index e4eb5813801c0b7af10771dd30e7805a06e7a368..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/newIdentity.jsp
+++ /dev/null
@@ -1,13 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-<jsp:include page="header.jsp">
-    <jsp:param name="title" value="New Email Identity"/>
-</jsp:include>
-
-<ib:createKeyPair/>
-
-<jsp:include page="footer.jsp"/>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/noIdentities.jsp b/apps/i2pbote/WebContent/noIdentities.jsp
deleted file mode 100644
index c63b550c87bb902f009bba099b78528738cff63c..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/noIdentities.jsp
+++ /dev/null
@@ -1,43 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<jsp:include page="header.jsp">
-    <jsp:param name="title" value="No Identity"/>
-</jsp:include>
-
-<div class="main">
-    <h2>No Email Identity Defined</h2>
-    <p>
-    In order to receive email from other people, you need to create an email identity
-    first.
-    </p><p>
-    This is similar to traditional email where you first have to set up an email account
-    with an email provider. The difference is that in I2P-Bote, there is no provider that
-    can read all your email because I2P-Bote stores all emails encrypted on the network.
-    </p><p>
-    I2P-Bote automatically decrypts emails sent to you, using the email identity you
-    created.
-    </p><p>
-    An email identity has a secret key, and a public key. Anybody who has the secret key
-    can download and decrypt your emails, so you should never give it to anybody. The
-    public key, on the other hand, allows people to send email to you. Think of it as
-    the equivalent of a traditional email address. Give it to everybody you want to be
-    able to contact you, or make it public.
-    </p><p>
-    [TODO paragraph about spam, HashCash].
-    </p><p>
-    You will find that email identities are long combinations of numbers and letters,
-    and that they are impossible to memorize. I2P-Bote lets you assign an email address
-    to an email identity to make it easier for you and your contacts to remember.
-    </p><p>
-    [TODO name attribute, address attribute].
-    The public addressbook should not be 100% trusted. Only destination keys are secure.
-    </p><p>
-    [TODO private address book, address directory].
-    <form action="editIdentity.jsp?new=true" method="POST">
-        <button type="submit" value="New">Create a New Email Identity</button>
-    </form>
-</div>
-
-<jsp:include page="footer.jsp"/>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/saveIdentity.jsp b/apps/i2pbote/WebContent/saveIdentity.jsp
deleted file mode 100644
index f865ab9961fa60feccafa7f2e2cb6acbe2c47add..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/saveIdentity.jsp
+++ /dev/null
@@ -1,31 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
-
-
-<c:if test="${param.action == 'Cancel'}">
-    <jsp:forward page="identities.jsp"/>
-</c:if>
-
-<c:choose>
-    <c:when test="${empty param.publicName}">
-        <c:set var="errorMessage" value="Please fill in the Public Name field."/>
-    </c:when>
-    <c:otherwise>
-        <c:set var="errorMessage" value="${ib:saveIdentity(param.key, param.publicName, param.description, param.emailAddress)}"/>
-    </c:otherwise>
-</c:choose>
-
-<c:if test="${empty errorMessage}">
-    <jsp:forward page="identities.jsp">
-        <jsp:param name="infoMessage" value="The email identity has been saved."/>
-    </jsp:forward>
-</c:if>
-<c:if test="${!empty errorMessage}">
-    <jsp:forward page="editIdentity.jsp">
-        <jsp:param name="errorMessage" value="${errorMessage}"/>
-    </jsp:forward>
-</c:if>
\ No newline at end of file
diff --git a/apps/i2pbote/WebContent/sendEmail.jsp b/apps/i2pbote/WebContent/sendEmail.jsp
deleted file mode 100644
index aa74b3b1d8cfc35449d2b066a4927cf18542e890..0000000000000000000000000000000000000000
--- a/apps/i2pbote/WebContent/sendEmail.jsp
+++ /dev/null
@@ -1,13 +0,0 @@
-<%@ page language="java" contentType="text/html; charset=UTF-8"
-    pageEncoding="UTF-8"%>
-<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-
-<%@ taglib prefix="ib" uri="I2pBoteTags" %>
- 
-<jsp:include page="header.jsp">
-    <jsp:param name="title" value="New Email"/>
-</jsp:include>
-
-<ib:sendEmail recipient="${param.recipient0}" message="${param.message}" />
-
-<jsp:include page="footer.jsp"/>
\ No newline at end of file
diff --git a/apps/i2pbote/doc/techdoc.txt b/apps/i2pbote/doc/techdoc.txt
deleted file mode 100644
index 575f0c66075ce86c658e24d5db9f7ca4d4c1bd60..0000000000000000000000000000000000000000
--- a/apps/i2pbote/doc/techdoc.txt
+++ /dev/null
@@ -1,462 +0,0 @@
-I2P-Bote Ver. 0.1
------------------
-
-1) Introduction
-
-I2P-Bote is a serverless pseudonymous email exchange service for the I2P network. Emails are
-stored encrypted on other I2P-bote nodes.
-There is a SMTP/POP interface [TO BE IMPLEMENTED] to use with an email client, and a web interface
-[TO BE IMPLEMENTED] that lets you change settings or send/read email.
-
-Email can be sent through a number of other nodes (relays) for increased security, or directly to
-a set of storage nodes for faster delivery. The same applies to retrieving email.
-All nodes are created equal. There are no "supernodes" or designated relay/storage nodes.
-Everybody acts as a potential relay and storage node. The maximum amount of disk space used for
-relayed/stored email packets can be configured by the user.
-Before an email is sent to a relay, it is broken up into packets and encrypted with the recipient's
-public key.
-
-Email packets are stored redundantly in a distributed hash table (DHT). Stored email packets are
-kept for at least 100 days, during which the recipient can download them.
-For relays, the guaranteed retention time is only 7 days.
-If a node runs out of email storage space, and there are no old packets that can be deleted, the
-node refuses storage requests.
-
-〮Email addresses are entered in the format "username@domain.i2p"; they must match an entry in the
-local address book or the distributed email directory. An exception are addresses of the form
-"dest_key@bote.i2p", where dest_key is a 516-byte base64-encoded email keypair. No lookup is done
-for these addresses; the dest_key part is directly used for routing the email.
-
-Below is a diagram of how an email packet is routed from a sender to a recipient:
-
-
-                    .-------.         .-------.         .--------.
-                    | Relay |  ---->  | Relay |  ---->  | Storer |  --------------------.
-                _   `-------'         `-------'         `--------'                       `\
-                /`                                                                         |
-               /                                                                           |
- .--------.   /      .-------.        .-------.         .--------.                         |
- | Sender |  ----->  | Relay |  --->  | Relay |  ---->  | Storer |  -------------.         |
- `--------'   \      `-------'        `-------'         `--------'                `\       |
-               \                                                                    |      |
-               _\/                                                                  |      |
-                    .-------.         .-------.         .--------.                  |      |
-                    | Relay |  ---->  | Relay |  ---->  | Storer |  ------.         |      |
-                    `-------'         `-------'         `--------'         `\       |      |
-                                                                             |      |      |
-                                                                             V      V      V
-
-                                           .--------------- Kademlia DHT -------------------------.
-                                           |                                .---------.           |
-                                           |   .---------.                  | Storage |           |
-                                           |   | Storage |     .---------.  |  Node   |           |
-                                           |   |  Node   |     | Storage |  `---------'           |
-                                           |   `---------'     |  Node   |                        |
-                                           |                   `---------'            .---------. |
-                                           |  .---------.   .---------.               | Storage | |
-                                           |  | Storage |   | Storage |  .---------.  |  Node   | |
-                                           |  |  Node   |   |  Node   |  | Storage |  `---------' |
-                                           |  `---------'   `---------'  |  Node   |              |
-                                           |                             `---------'              |
-                                           `------------------------------------------------------'
-
-                                                                             |      |      |
-                     .-------.          .-------.         .---------.       _'      |      |
-                     | Relay |  <-----  | Relay |  <----  | Fetcher |  <---'        |      |
-                  /  `-------'          `-------'         `---------'               |      |
-                 /                                                                  |      |
-               \/_                                                                  |      |
- .-----------.         .-------.        .-------.         .---------.              _'      |
- | Recipient |  <----  | Relay |  <---  | Relay |  <----  | Fetcher |  <----------'        |
- `-----------'  _      `-------'        `-------'         `---------'                      |
-               |\                                                                          |
-                 \                                                                         |
-                  \  .-------.          .-------.         .---------.                     _'
-                     | Relay |  <-----  | Relay |  <----  | Fetcher |  <-----------------'
-                     `-------'          `-------'         `---------'
-
-
-The number of relays for sending or retrieving an email is user-configurable. For higher performance (but
-reduced anonymity), it is possible to use no relays, no storers/fetchers, i.e. sender and recipient
-talk to the storage nodes directly.
-
-TODO explain how an index packet is relayed back to the recipient, who then uses another chain to get the email packets
-referenced in the index packet.
-
-The code is licensed under the GPL. The author can be reached at HungryHobo@mail.i2p, either in
-German or English.
-
-
-2. Kademlia
-
-TODO explain modified Kademlia: index packets, 2-stage retrieval;
-exhaustive Kademlia search for index packets, standard Kademlia search for email packets;
-no need for exponential expiration time (used in standard kademlia to prevent over-caching of popular files) because
-emails are only retrieved once (or a few times if there is a transmission error).
-
-TODO mention the problem of highly unbalanced trees and the sibling list solution.
-
-
-3. Packet Types
-
-3.1. Communication Packets
-
-  Communication packets are used for sending data between two I2P-Bote nodes. They contain a
-  payload packet, see 3.2.
-  All Communication Packets start with a four-byte prefix, followed by one byte for the packet
-  type, one byte for the packet version, and a 32-byte packet ID.
-  Strings are UTF8 encoded.
-
-   Packet Type         | Field | Data Type  | Description
-  ---------------------+-------+------------+----------------------------------------------------
-   Relay Request       |       |            | Request to a node to forward a Relay Packet
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'Y'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet ID, used for delivery confirmation
-                       | HLEN  | 2 bytes    | HashCash length
-                       | HK    | byte[]     | HashCash token (HLEN bytes)
-                       | DLEN  | 2 bytes    | Length of DATA field in bytes, up to ~30k
-                       | DATA  | byte[]     | A Relay Packet
-  ---------------------+-------+------------+----------------------------------------------------
-   Fetch Request       |       |            | Request to a chain end point to fetch emails
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'T'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet ID, used for delivery confirmation
-                       | DTYP  | 1 byte     | Type of data to retrieve:
-                       | KEY   | 32 bytes   | DHT key to look up
-                       | KPR   | 384 bytes  | Email keypair of recipient
-                       | RLEN  | 2 bytes    | Length of RET field
-                       | RET   | byte[]     | Relay packet, contains the return chain (RLEN bytes)
-  ---------------------+-------+------------+----------------------------------------------------
-   Response Packet     |       |            | Response to a Retrieve Request, Fetch Request,
-                       |       |            | Find Close Peers Request, or a Relay List Request
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'N'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet Id of the request packet
-                       | STA   | 1 byte     | Status code:
-                       |       |            |   0 = OK
-                       |       |            |   1 = General error
-                       |       |            |   2 = No data found
-                       |       |            |   3 = Invalid packet
-                       |       |            |   4 = Invalid HashCash
-                       |       |            |   5 = Not enough HashCash provided
-                       |       |            |   6 = No disk space left
-                       | DLEN  | 2 bytes    | Length of DATA field; can be 0 if no payload
-                       | DATA  | byte[]     | A Payload Packet
-  ---------------------+-------+------------+----------------------------------------------------
-   Delete Request      |       |            | Request to delete all copies of a packet by DHT key
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'D'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | KEY   | 32 bytes   | DHT key of the packet that is to be deleted
-                       | DEL   | 32 bytes   | Deletion key, encrypts to DEL value in the email pkt
-  ---------------------+-------+------------+-------------------------------------------------
-   Ping                |       |            | Check to see if a host is still up.
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'P'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet ID
-  ---------------------+-------+------------+-------------------------------------------------
-   Pong                |       |            | Let the the pinger know we're still there.
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'O'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet ID of the corresponding Ping packet
-                       | HKS   | 1 byte     | Minimum HashCash strength this node will accept
-  ---------------------+-------+------------+----------------------------------------------------
-   Relay List Request  |       |            | "Send me your list of relay peers"
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'A'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet ID
-  ---------------------+-------+------------+----------------------------------------------------
-
-  DHT Packets
-  
-   Packet Type         | Field | Data Type  | Description
-  ---------------------+-------+------------+----------------------------------------------------
-   Retrieve Request    |       |            | DHT Request for a value for a key
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'Q'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet ID, used for delivery confirmation
-                       | DTYP  | 1 byte     | Type of data to retrieve:
-                       |       |            |   'I' = Index Packet
-                       |       |            |   'E' = Email Packet
-                       |       |            |   'K' = Email destination
-                       | KEY   | 32 bytes   | DHT key to look up
-  ---------------------+-------+------------+----------------------------------------------------
-   Store Request       |       |            | DHT Store Request
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'S'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet ID, used for delivery confirmation
-                       | HLEN  | 2 bytes    | HashCash length
-                       | HK    | byte[]     | HashCash token (HLEN bytes)
-                       | DLEN  | 2 bytes    | Length of DATA field
-                       | DATA  | byte[]     | Payload packet to store (DLEN bytes). Can be an
-                       |       |            | Index Pkt / Email Pkt / Email Destination)
-  ---------------------+-------+------------+----------------------------------------------------
-   Find Close Peers    |       |            | Request for k peers close to a key
-                       | PFX   | 4 bytes    | Packet prefix, must be 0x6D 0x30 0x52 0xE9
-                       | TYPE  | 1 byte     | Value = 'F'
-                       | VER   | 1 byte     | Format version (only ver. 1 is supported)
-                       | PID   | 32 bytes   | Packet ID, used for delivery confirmation
-                       | KEY   | 32 bytes   | DHT key
-  ---------------------+-------+------------+----------------------------------------------------
-
-3.2. Data Packets
-
-  These are always sent wrapped in a Communication Packet (see 3.1).
-  E denotes an encrypted field.
-
-   Packet Type         | Field | Data Type  | Description
-  ---------------------+-------+------------+----------------------------------------------------
-   Email Packet        |       |            | A email or email fragment, 1 recipient.
-                       | TYPE  | 1 byte     | Value = 'E'
-                       | KEY   | 32 bytes   | The DHT key of the packet (generated randomly)
-                       | DELP  | 32 bytes   | Deletion key in plaintext, zeroed out for retrieving
-                       | LEN   | 2 bytes    | Length of the encrypted part of the packet
-                       | DELE  | 32 bytes E | Deletion key, encrypted w/ recipient's key
-                       | MSID  | 32 bytes E | Message ID in binary format
-                       | FRID  | 2 bytes  E | Fragment Index of this packet (0..NFR-1)
-                       | NFR   | 2 bytes  E | Number of fragments in the email
-                       | MLEN  | 2 bytes  E | Length of the MSG field
-                       | MSG   | byte[]   E | encrypted email or fragment (MLEN bytes)
-  ---------------------+-------+------------+----------------------------------------------------
-   Index Packet        |       |            | Contains the DHT keys of one or more Email Packets.
-                       | TYPE  | 1 byte     | Value = 'I'
-                       | DH    | 32 bytes   | SHA-256 hash of the recipient's email destination
-                       | NP    | 1 byte     | Number of keys in the packet
-                       | KEY1  | 256 bytes  | DHT key of the first Email Packet
-                       | KEY1  | 256 bytes  | DHT key of the second Email Packet
-                       | ...   | ...        | ...
-                       | KEYn  | 256 bytes  | DHT key of the n-th Email Packet
-  ---------------------+-------+------------+----------------------------------------------------
-   Relay Packet        |       |            | The payload of a Relay Request
-                       | TYPE  | 1 byte     | Value = 'R'
-                       | TMIN  | 4 bytes    | Earliest send time
-                       | TMAX  | 4 bytes    | Latest sent time (no guarantee!)
-                       | XK    | 32 bytes   | Key to XOR the payload with
-                       | NEXT  | 384 bytes  | Destination to forward the packet to
-                       | DLEN  | 2 bytes    | Length of DATA field in bytes, up to ~30k
-                       | DATA  | byte[]   E | Payload, encrypted with NEXT's key (DLEN bytes)
-                       |       |            | (can contain another Relay Packet, Email Packet,
-                       |       |            | or Retrieve Request)
-  ---------------------+-------+------------+-------------------------------------------------
-   Peer List           |       |            | Response to a Find Close Peers Request or
-                       |       |            | a Relay List Request
-                       | TYPE  | 1 byte     | Value = 'L'
-                       | NUMP  | 2 bytes    | Number of peers in the list
-                       | P1    | 384 bytes  | Destination key
-                       | P2    | 384 bytes  | Destination key
-                       | ...   | ...        | ...
-                       | Pn    | 384 bytes  | Destination key
-  ---------------------+-------+------------+-------------------------------------------------
-   Email Destination   |       |            | TBD
-                       |       |            | 
-                       |       |            | 
-                       |       |            | 
-                       |       |            | 
-                       |       |            | 
-  ---------------------+-------+------------+-------------------------------------------------
-
-
-4. Protocols For Inter-Node Communication
-
-4.1. Sending Email
-  I2P nodes involved: A=Sender, R1...Rn=Relays, S1...Sm=Storage Nodes
-
-  1) A sends an Relay Packet to R1.
-  2) R1 decrypts the packet, waits a random amount of time, and sends it to R2.
-  3) R2 confirms delivery with R1.
-  4) Repeat until packet arrives at Rn.
-  5) Rn decrypts the Relay Packet into an Email Packet.
-  6) Rn sends the packet to S1,...,Sm through a Kademlia STORE
-
-4.2. Retrieving Email
-  I2P nodes involved: A=Address Owner, O1...On=Outbound Relays, I1...In=Inbound Relays, S1...Sm=Storage Nodes
-
-  1) A sends a Relay Packet to O1.
-  2) O1 decrypts the packet, waits a random amount of time, and sends it to O2.
-  3) O2 confirms delivery with O1.
-  4) Repeat until packet arrives at On.
-  5) On decrypts the Relay Packet into a Retrieve Request.
-  6) On does a Kademlia LOOKUP for the email address.
-  7) Nodes S1,...,Sm verify the expiration time, request number, and A's signature on the Retrieve Request.
-  8) Nodes S1,...,Sm send their stored Email Packets to Rn.
-  9) On confirms delivery with S1,...,Sm
-  10) On makes a Relay Packet from the Email Packet and sends it to I1.
-  11) I1 confirms delivery with On.
-  12) I1 sends each packet to I2.
-  13) I2 confirms delivery with I1.
-  14) Repeat the last two steps until packet arrives at A.
-  15) A decrypts the packet into a plaintext email or email fragment.
-
-  Note: The system purposely does not prevent retrieval of email by unauthorized nodes, in order to
-  allow a node to test the reliability of a storage node before using it to store an Email Packet.
-
-
-4.3. Pinging a node
-
-4.4. Responding to a Ping
-
-4.5. Storing an Email Packet on a storage node
-
-4.6. Sending a Retrieve Request to a Node
-
-4.7. Deleting an Email Packet
-
-  1) Recipient knows the deletion key after it decrypts the email packet (the packet also contains a
-     "plaintext deletion key" field, which is all zeros after it leaves a storage node).
-  2) Recipient sends a Delete Request to all storage nodes close to the DHT key of the Email Packet
-
-4.8. Looking up an email address in the directory
-
-4.9. Announcing a new email address to the directory
-
-4.10. Updating or deleting an email address from the directory
-
-
-5. Algorithms Used By Nodes Locally
-
-5.1. Sending Email   *** OUTDATED ***
-  
-  1) Given a MIME email, GZip it, sign it, store it in the local outbox, split it into 30kB
-     packets plus an Index Packet, pad the packets if necessary, and store them locally.
-  2) Pick a random packet/recipient combination for which the wait time is up, and for which
-     less than <storage redundancy> distinct delivery confirmations have been received within the
-     timeout period, and the maximum number of retries has not been reached.
-  3) Set a random packet ID on the packet.
-  4) Encrypt the packet with the public key for the recipient
-  5) Encrypt the packet with the public keys of relay n, relay n-1, ..., relay 1, and add
-     the destination key of the next relay each time. Also included is a minimum/maximum delay
-     value for each relay.
-  6) Queue the packet for sending to the first relay.
-  7) Repeat from step 2 until no more packets left.
-  8) When all packets have been sent, move the email from the outbox to the sent folder.
-  
-  Rationale for storing the whole email, not the packets, on disk: When sending to multiple
-  recipients, only one copy of the email needs to be stored.
-     
-5.2. Retrieving Email
-
-   1) Randomly choose a set of n relay nodes,  R1...Rn (outgoing chain for request)
-   2) Randomly choose a set of m relay nodes, S1...Sm.(return chain / inbound mail chain)
-        - with Sm being the node closest to the receiver
-          and S1 the chain node closest to fetcher
-   3) Generate m+1 random 32-byte arrays (for XORing the return packets), X1...Xm+1
-   4) With Sm's public key, encrypt the local destination key and Xm+1
-   5) With Sm-1's public key, encrypt Sm's destination key, Xm, and the data from step 4)
-   6) With Sm-2's public key, encrypt Sm-1's destination key, Xm-1, and the data from step 5)
-   7) Repeat until the entire return chain, S1...Sm, has been onion-encrypted,
-        and also include a minimum/maximum delay value for each relay.
-   8) Add S1's destination key and X1 to the data from step 7)
-   9) Add the data (which is a Relay Packet) to a Retrieve Request packet.
-  10) Encrypt the resulting packet, using the public keys of relays
-      Rn (Fetcher), Rn-1, ..., R1, and add the destination key of the next relay each time.
-      Also included is a minimum/maximum delay value for each relay.
-  11) Set a random packet ID on the packet at each layer
-  12) Queue the packet for immediate sending to the first relay.
-  
-  The following is done continuously and asynchronously, regardless of whether there a Retrieve
-  Request has been sent recently:
-  
-  1) Nodes are always ready for incoming Email Packets.
-  2) For every email packet that is received, a Delete Request is sent out to the Kademlia network
-     via a relay chain.
-  
-5.3. Fetching Email Packets For an Address Through Kademlia
-
-  1) Do a Kademlia lookup for the keypair in the Retrieve Request
-  2) Extract packet IDs from the Index Packet(s)
-  3) Do a Kademlia lookup for each packet ID
-  4) Check the packet ID on the Email Packets and send them back to the first return relay
-  
-5.4. Relaying a Packet
-
-  1) Decrypt packet
-  2) Verify signature
-  3) XOR payload with the key included in the packet.
-  4) Read wait time range from packet
-  5) Wait for a random amount of time within the wait time range.
-  6) Queue packet for sending with that delay.
-  7) If no delivery confirmation within timeout period, wait for $resend_delay and repeat from step 5
-
-5.5. Storing an Email Packet
-
-  1) If packet can't fit in storage space, delete stored email packets that are more than 100 days old.
-  2) If still no room, send back negative response
-  3) Otherwise, write packet to file and send positive response
-
-
-6. Local Address Book
-
-
-7. Distributed Email Directory
-
-Each adress-keypair mapping is stored at multiple nodes. Nodes can only add an address to the
-distributed address directory, but they cannot edit or delete them. An address must be renewed
-periodically, or it expires after a year and is deleted from the address directory.
-A node only stores one keypair per email address. Whichever came first, stays in the address
-directory.
-When doing an address lookup, a error message is displayed to the user if lookup results from two
-nodes are different.
-It is important for the user to understand that using the distributed email directory is not as
-secure as manually adding keypairs to the local address book, although it is more convenient.
-
-
-8. UI
-
-8.1. Retrieving email
-
-  * Wait 20 seconds for any emails to arrive, then return from HTTP request or POP3 request.
-    All other emails will be received in the background.
-  * The "high priority" setting found in most email clients sets all relaying delays to zero.
-
-
-9. Files
-
-9.1. Identities file
-
-  Stores all email identities the local user has created. One line per entry.
-  Format: <base64 key><tab><public name>[<tab><description>][<tab><email address>]
-
-
-10. To Do
-
-  * Replication per the Kademlia spec
-  * Have an option to disable automatic lookups in the distributed address directory for your own address
-  * Automatically create a different sender address for each recipient
-  * test relays periodically
-  * test storage nodes periodically  
-  * optionally pad all packets to a system-wide fixed size
-  * Always use the same amount of bandwidth, testing nodes or sending dummy messages
-    if necessary.
-  * Always keep incoming bandwidth the same as outgoing bandwidth
-  * Pad packets before encrypting to prevent tagging attacks
-  * packet id for confirmation
-  * onion packets/regular packets
-  * delivery confirmation to sender
-  * What about incoming bandwith, can it be limited?
-  * Turnkey outproxy feature?
-  * All nodes keep track of I2PBote peers through Peer List Requests and by pinging known peers
-    periodically.
-  * optionally, packets are padded to a constant size
-  * select different set of hops for each packet
-  * instead of sending dummy messages for cover traffic, test gateways by routing packet to self
-  * keep track of uptimes + reliability of paths (or gateways), have a "minimum 
-    uptime/min reliability percentage" setting for selecting gateways
-  * Use three different I2P destinations for sending and receiving data
-  * Use more storage nodes if email destination is known to be heavily frequented (like Kademlia does for popular hashes)
-  * mention that min/max delay = earliest/latest send time (explain that there is no guarantee for the send time being between min and max, it can be after max)
-  * Consider replacing ElGamal with 256-bit ECC encryption (=86 base64 chars) which comes standard in Java 7
-  * If an email fits in one packet, don't bother with an index packet
-  * Emails in the Outbox: Show addt'l "progress" progress column which is clickable and brings up detailed info.
-  * On first run, offer to create a new email identity, and optionally assign an email address to it.
-  * Folder structure on disk should reflect folders in app. All email folders visible to the user have the same root directory: [app dir]/folders.
-  * ask user for confirmation before deleting an email identity
-  * have a view of the incomplete folder, so users can look at incomplete emails
diff --git a/apps/i2pbote/doc/userdoc.txt b/apps/i2pbote/doc/userdoc.txt
deleted file mode 100644
index de252cc93b701149900bec05431732b83609f2d4..0000000000000000000000000000000000000000
--- a/apps/i2pbote/doc/userdoc.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-I2P-Bote
---------
-
-This file explains how to use I2P-Bote. For technical documentation, see techdoc.txt.
-
-1. To install the app, [TODO]
-
-2. Set up an email address [TODO]
-
-3. Send a test email to yourself [TODO]
-
-4. Checking for email
-
-When you view your inbox through the web interface or click the "Get Mail" button on your email
-client, two things happen: New downloaded emails are shown, if there are any, and the I2P-Bote
-network is contacted to see if more emails are waiting to be downloaded.
-
-5. The Configuration File
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/CheckEmailTask.java b/apps/i2pbote/src/i2p/bote/CheckEmailTask.java
deleted file mode 100644
index 0f847bc14e21a9bee6aff135ec88722abf2a9d2e..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/CheckEmailTask.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package i2p.bote;
-
-import i2p.bote.folder.IncompleteEmailFolder;
-import i2p.bote.network.DHT;
-import i2p.bote.network.PeerManager;
-import i2p.bote.packet.EmailPacket;
-import i2p.bote.packet.IndexPacket;
-import i2p.bote.packet.UniqueId;
-import i2p.bote.packet.dht.DhtStorablePacket;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-/**
- * Gets email packets from the DHT for one email identity. A separate thread is used for
- * each packet in order to speed things up, and because the packets are in different places
- * on the network.
- *
- * @author HungryHobo@mail.i2p
- */
-public class CheckEmailTask implements Runnable {
-    private static final int MAX_THREADS = 50;
-    private static final int THREAD_STACK_SIZE = 64 * 1024;   // TODO find a safe low value (default in 64-bit Java 1.6 = 1MByte)
-    private static final ThreadFactory EMAIL_PACKET_TASK_THREAD_FACTORY = Util.createThreadFactory("EmailPktTask", THREAD_STACK_SIZE);
-    private ExecutorService executor;
-    
-    private Log log = new Log(CheckEmailTask.class);
-    private EmailIdentity identity;
-    private DHT dht;
-    private PeerManager peerManager;
-    private IncompleteEmailFolder incompleteEmailFolder;
-
-    public CheckEmailTask(EmailIdentity identity, DHT dht, PeerManager peerManager, IncompleteEmailFolder incompleteEmailFolder) {
-        this.identity = identity;
-        this.dht = dht;
-        this.peerManager = peerManager;
-        this.incompleteEmailFolder = incompleteEmailFolder;
-    }
-    
-    @Override
-    public void run() {
-        Collection<Hash> emailPacketKeys = findEmailPacketKeys();
-        
-        executor = Executors.newFixedThreadPool(MAX_THREADS, EMAIL_PACKET_TASK_THREAD_FACTORY);
-        for (Hash dhtKey: emailPacketKeys)
-            executor.submit(new EmailPacketTask(dhtKey));
-        executor.shutdown();
-        try {
-            executor.awaitTermination(1, TimeUnit.DAYS);
-        } catch (InterruptedException e) {
-            log.error("Interrupted while checking for mail.", e);
-            executor.shutdownNow();
-        }
-    }
-    
-    /**
-     * Queries the DHT for new index packets and returns the DHT keys contained in them.
-     * @return A <code>Collection</code> containing zero or more elements
-     */
-    private Collection<Hash> findEmailPacketKeys() {
-        log.debug("Querying the DHT for index packets with key " + identity.getHash());
-        Collection<DhtStorablePacket> packets = dht.findAll(identity.getHash(), IndexPacket.class);
-        
-        // build an Collection of index packets
-        Collection<IndexPacket> indexPackets = new ArrayList<IndexPacket>();
-        for (DhtStorablePacket packet: packets)
-            if (packet instanceof IndexPacket)
-                indexPackets.add((IndexPacket)packet);
-            else
-                log.error("DHT returned packet of class " + packet.getClass().getSimpleName() + ", expected IndexPacket.");
-        
-        IndexPacket mergedPacket = new IndexPacket(indexPackets);
-        log.debug("Found " + mergedPacket.getDhtKeys().size() + " Email Packet keys.");
-        return mergedPacket.getDhtKeys();
-    }
-    
-    /**
-     * Queries the DHT for an email packet, adds the packet to the {@link IncompleteEmailFolder},
-     * and deletes the packet from the DHT.
-     *
-     * @author HungryHobo@mail.i2p
-     */
-    private class EmailPacketTask implements Runnable {
-        private Hash dhtKey;
-        
-        /**
-         * 
-         * @param dhtKey The DHT key of the email packet to retrieve
-         */
-        public EmailPacketTask(Hash dhtKey) {
-            this.dhtKey = dhtKey;
-        }
-        
-        @Override
-        public void run() {
-            DhtStorablePacket packet = dht.findOne(dhtKey, EmailPacket.class);
-            if (packet instanceof EmailPacket) {
-                EmailPacket emailPacket = (EmailPacket)packet;
-                try {
-                    emailPacket.decrypt(identity);
-                }
-                catch (DataFormatException e) {
-                    log.error("Can't decrypt email packet: " + emailPacket, e);
-                    // TODO propagate error message to UI
-                }
-                incompleteEmailFolder.add(emailPacket);
-                sendDeleteRequest(dhtKey, emailPacket.getEncryptedDeletionKey());
-            }
-            else
-                log.error("DHT returned packet of class " + packet.getClass().getSimpleName() + ", expected EmailPacket.");
-        }
-        
-        /**
-         * Sends a delete request to the DHT.
-         * @param dhtKey The DHT key of the email packet that is to be deleted
-         * @param deletionKey The deletion key for the email packet
-         */
-        private void sendDeleteRequest(Hash dhtKey, UniqueId deletionKey) {
-            // TODO
-        }
-   }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/Configuration.java b/apps/i2pbote/src/i2p/bote/Configuration.java
deleted file mode 100644
index d7fd06c37dcc58e04687fe5941aefe0473b8910a..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/Configuration.java
+++ /dev/null
@@ -1,192 +0,0 @@
-package i2p.bote;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.Properties;
-
-import net.i2p.I2PAppContext;
-import net.i2p.data.DataHelper;
-import net.i2p.util.Log;
-
-public class Configuration extends Properties {
-    private static final long serialVersionUID = -6318245413106186095L;
-	private static final String I2P_BOTE_SUBDIR = ".i2pbote";       // relative to the I2P app dir
-	private static final String CONFIG_FILE_NAME = "i2pbote.config";
-    private static final String DEST_KEY_FILE_NAME = "local_dest.key";
-    private static final String PEER_FILE_NAME = "peers.txt";
-    private static final String IDENTITIES_FILE_NAME = "identities.txt";
-    private static final String OUTBOX_DIR = "outbox";              // relative to I2P_BOTE_SUBDIR
-    private static final String OUTBOX_SUBDIR_LOCAL = "local";      // relative to OUTBOX_DIR
-    private static final String OUTBOX_SUBDIR_RELAY = "relay";      // relative to OUTBOX_DIR
-    private static final String INCOMPLETE_SUBDIR = "incomplete";   // relative to I2P_BOTE_SUBDIR
-    private static final String EMAIL_DHT_SUBDIR = "dht_email_pkt";    // relative to I2P_BOTE_SUBDIR
-    private static final String INDEX_PACKET_DHT_SUBDIR = "dht_index_pkt";    // relative to I2P_BOTE_SUBDIR
-    private static final String INBOX_SUBDIR = "inbox";             // relative to I2P_BOTE_SUBDIR
-	
-	// Parameter names in the config file
-	private static final String PARAMETER_REDUNDANCY = "redundancy";
-	private static final String PARAMETER_STORAGE_SPACE_INBOX = "storageSpaceInbox";
-	private static final String PARAMETER_STORAGE_SPACE_RELAY = "storageSpaceRelay";
-	private static final String PARAMETER_STORAGE_TIME = "storageTime";
-	private static final String PARAMETER_MAX_FRAGMENT_SIZE = "maxFragmentSize";
-	private static final String PARAMETER_HASHCASH_STRENGTH = "hashCashStrength";
-	private static final String PARAMETER_SMTP_PORT = "smtpPort";
-    private static final String PARAMETER_POP3_PORT = "pop3Port";
-    
-	// Defaults for each parameter
-	private static final int DEFAULT_REDUNDANCY = 2;
-	private static final int DEFAULT_STORAGE_SPACE_INBOX = 1024 * 1024 * 1024;
-	private static final int DEFAULT_STORAGE_SPACE_RELAY = 100 * 1024 * 1024;
-	private static final int DEFAULT_STORAGE_TIME = 31;   // in days
-	private static final int DEFAULT_MAX_FRAGMENT_SIZE = 10 * 1024 * 1024;   // the maximum size one email fragment can be, in bytes
-	private static final int DEFAULT_HASHCASH_STRENGTH = 10;
-    private static final int DEFAULT_SMTP_PORT = 7661;
-    private static final int DEFAULT_POP3_PORT = 7662;
-	
-	private Log log = new Log(Configuration.class);
-	private File i2pBoteDir;
-	
-	/**
-	 * Reads configuration settings from the <code>I2P_BOTE_SUBDIR</code> subdirectory under
-	 * the I2P application directory. The I2P application directory can be changed via the
-	 * <code>i2p.dir.app</code> system property.
-	 * 
-	 * Logging is done through the I2P logger. I2P reads the log configuration from the
-	 * <code>logger.config</code> file whose location is determined by the
-	 * <code>i2p.dir.config</code> system property.
-	 * @return
-	 */
-	public Configuration() {
-		// get the I2PBote directory and make sure it exists
-		i2pBoteDir = getI2PBoteDirectory();
-		if (!i2pBoteDir.exists())
-		    i2pBoteDir.mkdirs();
-
-		// read the configuration file
-        File configFile = new File(i2pBoteDir, CONFIG_FILE_NAME);
-        boolean configurationLoaded = false;
-        if (configFile.exists()) {
-			log.debug("Loading config file <" + configFile.getAbsolutePath() + ">");
-			
-            try {
-                DataHelper.loadProps(this, configFile);
-                configurationLoaded = true;
-            } catch (IOException e) {
-            	log.error("Error loading configuration file <" + configFile.getAbsolutePath() + ">", e);
-            }
-        }
-        if (!configurationLoaded)
-            log.info("Can't read configuration file <" + configFile.getAbsolutePath() + ">, using default settings.");
-	}
-
-	public File getDestinationKeyFile() {
-	    return new File(i2pBoteDir, DEST_KEY_FILE_NAME);
-	}
-	
-	public File getPeerFile() {
-	    return new File(i2pBoteDir, PEER_FILE_NAME);
-	}
-	
-    public File getIdentitiesFile() {
-        return new File(i2pBoteDir, IDENTITIES_FILE_NAME);
-    }
-    
-	public File getLocalOutboxDir() {
-	    return new File(getOutboxBaseDir(), OUTBOX_SUBDIR_LOCAL);	    
-	}
-	
-    public File getRelayOutboxDir() {
-        return new File(getOutboxBaseDir(), OUTBOX_SUBDIR_RELAY);       
-    }
-    
-	private File getOutboxBaseDir() {
-	    return new File(i2pBoteDir, OUTBOX_DIR);
-	}
-	
-    public File getInboxDir() {
-        return new File(i2pBoteDir, INBOX_SUBDIR);       
-    }
-    
-    public File getIncompleteDir() {
-        return new File(i2pBoteDir, INCOMPLETE_SUBDIR);       
-    }
-    
-    public File getEmailDhtStorageDir() {
-        return new File(i2pBoteDir, EMAIL_DHT_SUBDIR);       
-    }
-    
-    public File getIndexPacketDhtStorageDir() {
-        return new File(i2pBoteDir, INDEX_PACKET_DHT_SUBDIR);
-    }
-    
-    private static File getI2PBoteDirectory() {
-        // the parent directory of the I2PBote directory ($HOME or the value of the i2p.dir.app property)
-        File i2pAppDir = I2PAppContext.getGlobalContext().getAppDir();
-        
-        return new File(i2pAppDir, I2P_BOTE_SUBDIR);
-    }
-    
-	/**
-	 * Save the configuration
-	 * @param configFile
-	 * @throws IOException
-	 */
-	public void saveToFile(File configFile) throws IOException {
-		log.debug("Saving config file <" + configFile.getAbsolutePath() + ">");
-		DataHelper.storeProps(this, configFile);
-	}
-
-	/**
-	 * Returns the number of relays to use for sending and receiving email. Zero is a legal value.
-	 * @return
-	 */
-	public int getRedundancy() {
-		return getIntParameter(PARAMETER_REDUNDANCY, DEFAULT_REDUNDANCY);
-	}
-
-	/**
-	 * Returns the maximum size (in bytes) the inbox can take up.
-	 * @return
-	 */
-	public int getStorageSpaceInbox() {
-		return getIntParameter(PARAMETER_STORAGE_SPACE_INBOX, DEFAULT_STORAGE_SPACE_INBOX);
-	}
-	
-	/**
-	 * Returns the maximum size (in bytes) all messages stored for relaying can take up.
-	 * @return
-	 */
-	public int getStorageSpaceRelay() {
-		return getIntParameter(PARAMETER_STORAGE_SPACE_RELAY, DEFAULT_STORAGE_SPACE_RELAY);
-	}
-	
-	/**
-	 * Returns the time (in milliseconds) after which an email is deleted from the outbox if it cannot be sent / relayed.
-	 * @return
-	 */
-	public long getStorageTime() {
-		return 24L * 3600 * 1000 * getIntParameter(PARAMETER_STORAGE_TIME, DEFAULT_STORAGE_TIME);
-	}
-
-	public int getMaxFragmentSize() {
-		return getIntParameter(PARAMETER_MAX_FRAGMENT_SIZE, DEFAULT_MAX_FRAGMENT_SIZE);
-	}
-	
-	public int getHashCashStrength() {
-		return getIntParameter(PARAMETER_HASHCASH_STRENGTH, DEFAULT_HASHCASH_STRENGTH);
-	}
-	
-	private int getIntParameter(String parameterName, int defaultValue) {
-		String stringValue = getProperty(parameterName);
-		if (stringValue == null)
-			return defaultValue;
-		else
-			try {
-				return new Integer(getProperty(parameterName));
-			}
-			catch (NumberFormatException e) {
-				log.warn("Can't convert value <" + stringValue + "> for parameter <" + parameterName + "> to int, using default.");
-				return defaultValue;
-			}
-	}
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/EmailDestination.java b/apps/i2pbote/src/i2p/bote/EmailDestination.java
deleted file mode 100644
index 32b848c43801e4251f36604508c9ebbf1f71e1e8..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/EmailDestination.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package i2p.bote;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
-import net.i2p.client.I2PSession;
-import net.i2p.crypto.SHA256Generator;
-import net.i2p.data.Base64;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-import net.i2p.data.Hash;
-import net.i2p.data.PublicKey;
-import net.i2p.data.SigningPublicKey;
-import net.i2p.util.Log;
-
-/**
- * Uniquely identifies an email recipient. This implementation uses I2P keypairs.
- *
- * @author HungryHobo@mail.i2p
- */
-public class EmailDestination {
-    private Log log = new Log(EmailDestination.class);
-    private PublicKey publicEncryptionKey;
-    private SigningPublicKey publicSigningKey;
-    
-    /**
-     * Creates a fresh <code>EmailDestination</code>.
-     */
-    public EmailDestination() {
-        try {
-            I2PClient i2pClient = I2PClientFactory.createClient();
-            ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
-            i2pClient.createDestination(arrayStream);
-            byte[] destinationArray = arrayStream.toByteArray();
-            I2PSession i2pSession = i2pClient.createSession(new ByteArrayInputStream(destinationArray), null);
-            
-            initKeys(i2pSession);
-        }
-        catch (Exception e) {
-            log.error("Can't generate EmailDestination.", e);
-        }
-    }
-
-    /**
-     * Creates a <code>EmailDestination</code> using data read from a {@link ByteBuffer}.
-     * @param buffer
-     */
-    public EmailDestination(ByteBuffer buffer) {
-        byte[] encryptionKeyArray = new byte[PublicKey.KEYSIZE_BYTES];
-        buffer.get(encryptionKeyArray);
-        publicEncryptionKey = new PublicKey(encryptionKeyArray);
-        
-        byte[] signingKeyArray = new byte[SigningPublicKey.KEYSIZE_BYTES];
-        buffer.get(signingKeyArray);
-        publicSigningKey = new SigningPublicKey(signingKeyArray);
-    }
-    
-    public EmailDestination(String base64Data) {
-        try {
-            base64Data += "AAAA";   // add a null certificate
-            Destination i2pDestination = new Destination(base64Data);
-            publicEncryptionKey = i2pDestination.getPublicKey();
-            publicSigningKey = i2pDestination.getSigningPublicKey();
-        } catch (DataFormatException e) {
-            log.error("Can't generate EmailDestination.", e);
-        }
-    }
-    
-/*    public EmailDestination(byte[] data) {
-        try {
-            byte[] destinationPlusCert = addNullCertificate(data);
-            ByteArrayInputStream byteStream = new ByteArrayInputStream(destinationPlusCert);
-            I2PSession i2pSession = I2PClientFactory.createClient().createSession(byteStream, null);
-            initKeys(i2pSession);
-        }
-        catch (I2PSessionException e) {
-            log.error("Can't generate EmailDestination.", e);
-        }
-    }*/
-
-/*    private byte[] addNullCertificate(byte[] data) {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        try {
-            outputStream.write(data);
-            outputStream.write(new Certificate().toByteArray());
-            // Add an extra zero byte so I2PSessionImpl.readDestination doesn't fall on its face. I believe this is a bug.
-            outputStream.write(0);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        return outputStream.toByteArray();
-    }*/
-    
-    protected void initKeys(I2PSession i2pSession) {
-        publicEncryptionKey = i2pSession.getMyDestination().getPublicKey();
-        publicSigningKey = i2pSession.getMyDestination().getSigningPublicKey();
-    }
-    
-    public PublicKey getPublicEncryptionKey() {
-        return publicEncryptionKey;
-    }
-
-    public SigningPublicKey getPublicSigningKey() {
-        return publicSigningKey;
-    }
-    
-    private byte[] getKeysAsArray() {
-        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
-        try {
-            writeTo(byteStream);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        return byteStream.toByteArray();
-    }
-    
-    private void writeTo(OutputStream outputStream) throws IOException {
-        try {
-            publicEncryptionKey.writeBytes(outputStream);
-            publicSigningKey.writeBytes(outputStream);
-        }
-        catch (DataFormatException e) {
-            log.error("Invalid encryption key or signing key.", e);
-        }
-    }
-    
-    public Hash getHash() {
-        // TODO cache the hash value?
-        return SHA256Generator.getInstance().calculateHash(getKeysAsArray());
-    }
-    
-    /**
-     * Returns the two public keys in Base64 representation.
-     */
-    public String getKey() {
-        return Base64.encode(getKeysAsArray());
-    }
-    
-    public String toBase64() {
-        return getKey();
-    }
-    
-    @Override
-    public String toString() {
-        return getKey();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/EmailIdentity.java b/apps/i2pbote/src/i2p/bote/EmailIdentity.java
deleted file mode 100644
index e21ef19d5f0a714ba89b6394955c7ace06ff323d..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/EmailIdentity.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package i2p.bote;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
-import net.i2p.client.I2PSession;
-import net.i2p.client.I2PSessionException;
-import net.i2p.data.Base64;
-import net.i2p.data.PrivateKey;
-import net.i2p.data.SigningPrivateKey;
-import net.i2p.util.Log;
-
-public class EmailIdentity extends EmailDestination {
-    private Log log = new Log(EmailIdentity.class);
-    private PrivateKey privateEncryptionKey;
-    private SigningPrivateKey privateSigningKey;
-    private String publicName;
-    private String description;   // optional
-    private String emailAddress;   // optional
-
-    /**
-     * Creates a random <code>EmailIdentity</code>.
-     */
-    public EmailIdentity() {
-        // key initialization happens in the super constructor, which calls initKeys
-    }
-
-    /**
-     * Creates a <code>EmailIdentity</code> from a Base64-encoded string. The format is the same as
-     * for Base64-encoded local I2P destinations, except there is no null certificate.
-     * @param key
-     */
-    public EmailIdentity(String key) {
-        try {
-            key = key.substring(0, 512) + "AAAA" + key.substring(512);   // insert a null certificate for I2PClient.createSession()
-            ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64.decode(key));
-            
-            I2PClient i2pClient = I2PClientFactory.createClient();
-            I2PSession i2pSession = i2pClient.createSession(inputStream, null);
-            initKeys(i2pSession);
-        }
-        catch (I2PSessionException e) {
-            log.error("Can't generate EmailIdentity.", e);
-        }
-    }
-    
-    public PrivateKey getPrivateEncryptionKey() {
-        return privateEncryptionKey;
-    }
-    
-    public SigningPrivateKey getPrivateSigningKey() {
-        return privateSigningKey;
-    }
-
-    public void setPublicName(String publicName) {
-        this.publicName = publicName;
-    }
-
-    public String getPublicName() {
-        return publicName;
-    }
-
-    public void setDescription(String name) {
-        this.description = name;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setEmailAddress(String emailAddress) {
-        this.emailAddress = emailAddress;
-    }
-
-    public String getEmailAddress() {
-        return emailAddress;
-    }
-
-    protected void initKeys(I2PSession i2pSession) {
-        super.initKeys(i2pSession);
-        privateEncryptionKey = i2pSession.getDecryptionKey();
-        privateSigningKey = i2pSession.getPrivateKey();
-    }
-    
-    private byte[] getKeysAsArray() {
-        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
-        try {
-            byteStream.write(getPublicEncryptionKey().getData());
-            byteStream.write(getPublicSigningKey().getData());
-            byteStream.write(getPrivateEncryptionKey().getData());
-            byteStream.write(getPrivateSigningKey().getData());
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        return byteStream.toByteArray();
-    }
-    
-    /**
-     * Returns the two key pairs (public + private) as one Base64-encoded string.
-     * @return
-     */
-    public String getFullKey() {
-        return Base64.encode(getKeysAsArray());
-    }
-    
-    @Override
-    public String toString() {
-        return getKey() + " address=<" + getEmailAddress() + "> identity name=<" + getDescription() + "> visible name=<" + getPublicName() + ">";
-    }
-    
-/*    @Override
-    public boolean equals(Object anotherObject) {
-        if (anotherObject instanceof EmailIdentity) {
-            EmailIdentity otherIdentity = (EmailIdentity)anotherObject;
-            return Arrays.equals(getKeysAsArray(), otherIdentity.getKeysAsArray());
-        }
-        return false;
-    }*/
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/I2PBote.java b/apps/i2pbote/src/i2p/bote/I2PBote.java
deleted file mode 100644
index 10787ea9ab2931ecc8e1377ea3e32decf010719e..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/I2PBote.java
+++ /dev/null
@@ -1,256 +0,0 @@
-package i2p.bote;
-
-import i2p.bote.folder.DhtPacketFolder;
-import i2p.bote.folder.EmailFolder;
-import i2p.bote.folder.IncompleteEmailFolder;
-import i2p.bote.folder.IndexPacketFolder;
-import i2p.bote.folder.Outbox;
-import i2p.bote.folder.PacketFolder;
-import i2p.bote.network.DHT;
-import i2p.bote.network.I2PPacketDispatcher;
-import i2p.bote.network.I2PSendQueue;
-import i2p.bote.network.PeerManager;
-import i2p.bote.network.kademlia.KademliaDHT;
-import i2p.bote.packet.DataPacket;
-import i2p.bote.packet.Email;
-import i2p.bote.packet.EmailPacket;
-import i2p.bote.packet.IndexPacket;
-import i2p.bote.packet.RelayPacket;
-import i2p.bote.service.OutboxProcessor;
-import i2p.bote.service.POP3Service;
-import i2p.bote.service.RelayPacketSender;
-import i2p.bote.service.SMTPService;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.util.Collection;
-
-import net.i2p.I2PException;
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
-import net.i2p.client.I2PSession;
-import net.i2p.client.I2PSessionException;
-import net.i2p.data.Base64;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-/**
- * This is the core class of the application. Is is implemented as a singleton.
- * 
- * @author HungryHobo@mail.i2p
- */
-public class I2PBote {
-	private static I2PBote instance;
-	
-    private Log log = new Log(I2PBote.class);
-	private I2PClient i2pClient;
-	private I2PSession i2pSession;
-	private Configuration configuration;
-	private Identities identities;
-	private I2PSendQueue sendQueue;
-	private Outbox outbox;   // stores outgoing emails for all local users
-    private EmailFolder inbox;   // stores incoming emails for all local users
-	private PacketFolder<RelayPacket> relayPacketFolder;   // stores email packets we're forwarding for other machines
-	private IncompleteEmailFolder incompleteEmailFolder;   // stores email packets addressed to a local user
-    private DhtPacketFolder<? extends DataPacket> emailDhtStorageFolder;   // stores email packets and index packets for other peers
-    private IndexPacketFolder indexPacketDhtStorageFolder;   // stores index packets
-//TODO    private PacketFolder<> addressDhtStorageFolder;   // stores email address-destination mappings
-	private SMTPService smtpService;
-	private POP3Service pop3Service;
-	private OutboxProcessor outboxProcessor;   // reads emails stored in the outbox and sends them
-	private RelayPacketSender relayPacketSender;   // reads packets stored in the relayPacketFolder and sends them
-	private DHT dht;
-	private PeerManager peerManager;
-
-	private I2PBote() {
-	    Thread.currentThread().setName("I2PBoteMain");
-	
-        initializeLogging();
-        
-		i2pClient = I2PClientFactory.createClient();
-		configuration = new Configuration();
-		
-		identities = new Identities(configuration.getIdentitiesFile());
-		initializeSession();
-		initializeFolderAccess();
-		initializeServices();
-		startAllServices();
-	}
-
-    private void initializeLogging() {
-    }
-    
-	/**
-	 * Initializes objects for accessing emails and packet files on the filesystem.
-	 */
-	private void initializeFolderAccess() {
-	    inbox = new EmailFolder(configuration.getInboxDir());
-		outbox = new Outbox(configuration.getLocalOutboxDir());
-		relayPacketFolder = new PacketFolder<RelayPacket>(configuration.getRelayOutboxDir());
-		incompleteEmailFolder = new IncompleteEmailFolder(configuration.getIncompleteDir(), inbox);
-		emailDhtStorageFolder = new DhtPacketFolder<EmailPacket>(configuration.getEmailDhtStorageDir());
-        indexPacketDhtStorageFolder = new IndexPacketFolder(configuration.getIndexPacketDhtStorageDir());
-	}
-
-	/**
-	 * Sets up a {@link I2PSession}, using the I2P destination stored on disk or creating a new I2P
-	 * destination if no key file exists.
-	 */
-	private void initializeSession() {
-        // read the local destination key from the key file if it exists
-        File destinationKeyFile = configuration.getDestinationKeyFile();
-        try {
-            FileReader fileReader = new FileReader(destinationKeyFile);
-            char[] destKeyBuffer = new char[(int)destinationKeyFile.length()];
-            fileReader.read(destKeyBuffer);
-            byte[] localDestinationKey = Base64.decode(new String(destKeyBuffer));
-            ByteArrayInputStream inputStream = new ByteArrayInputStream(localDestinationKey);
-       		i2pSession = i2pClient.createSession(inputStream, null);
-        	i2pSession.connect();
-        }
-        catch (IOException e) {
-        	log.debug("Destination key file doesn't exist or isn't readable: " + e);
-        } catch (I2PSessionException e) {
-        	log.warn("Error creating I2PSession", e);
-		}
-        
-		// if the local destination key can't be read or is invalid, create a new one
-        if (i2pSession == null) {
-			log.debug("Creating new local destination key");
-			try {
-				ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
-				i2pClient.createDestination(arrayStream);
-			    byte[] localDestinationArray = arrayStream.toByteArray();
-				
-				i2pSession = i2pClient.createSession(new ByteArrayInputStream(localDestinationArray), null);
-	        	i2pSession.connect();
-	        	
-	        	saveLocalDestinationKeys(destinationKeyFile, localDestinationArray);
-			} catch (I2PException e) {
-				log.error("Error creating local destination key or I2PSession.", e);
-			} catch (IOException e) {
-				log.error("Error writing local destination key to file.", e);
-			}
-        }
-        
-        Destination localDestination = i2pSession.getMyDestination();
-        log.debug("Local destination key = " + localDestination.toBase64());
-        log.debug("Local destination hash = " + localDestination.calculateHash().toBase64());
-	}
-	
-	/**
-	 * Initializes daemon threads, doesn't start them yet.
-	 */
-    private void initializeServices() {
-        I2PPacketDispatcher dispatcher = new I2PPacketDispatcher();
-        i2pSession.addSessionListener(dispatcher, I2PSession.PROTO_ANY, I2PSession.PORT_ANY);
-        
-        smtpService = new SMTPService();
-        pop3Service = new POP3Service();
-        outboxProcessor = new OutboxProcessor(i2pSession, outbox, configuration, peerManager);
-        relayPacketSender = new RelayPacketSender(sendQueue, relayPacketFolder);
-        sendQueue = new I2PSendQueue(i2pSession, dispatcher);
-        
-        dht = new KademliaDHT(i2pSession.getMyDestination(), sendQueue, dispatcher, configuration.getPeerFile());
-        
-        dht.setStorageHandler(EmailPacket.class, emailDhtStorageFolder);
-        dht.setStorageHandler(IndexPacket.class, indexPacketDhtStorageFolder);
-//TODO        dht.setStorageHandler(AddressPacket.class, );
-        
-        peerManager = new PeerManager();
-    }
-
-    /**
-     * Writes private + public keys for the local destination out to a file.
-     * @param keyFile
-     * @param localDestinationArray
-     * @throws DataFormatException
-     * @throws IOException
-     */
-	private void saveLocalDestinationKeys(File keyFile, byte[] localDestinationArray) throws DataFormatException, IOException {
-		if (keyFile.exists()) {
-			File oldKeyFile = new File(keyFile.getPath() + "_backup");
-			keyFile.renameTo(oldKeyFile);
-		}
-		else
-		    keyFile.createNewFile();
-		
-        FileWriter fileWriter = new FileWriter(keyFile);
-        fileWriter.write(Base64.encode(localDestinationArray));
-        fileWriter.close();
-	}
-	
-	public static void startUp() {
-		getInstance();
-	}
-	
-	public static void shutDown() {
-		if (instance != null)
-			instance.stopAllServices();
-	}
-
-	public static I2PBote getInstance() {
-		if (instance == null)
-			instance = new I2PBote();
-		return instance;
-	}
-	
-	public Identities getIdentities() {
-	    return identities;
-	}
-	
-	public void sendEmail(Email email) throws Exception {
-/*XXX*/
-javax.mail.Address recipient = email.getAllRecipients()[0];
-String address = ((javax.mail.internet.InternetAddress)recipient).getAddress();
-if (address.indexOf('@')>=0)
-    address = address.substring(0, address.indexOf('@'));
-EmailDestination emailDestination = new EmailDestination(address);
-Collection<EmailPacket> emailPackets = email.createEmailPackets(emailDestination);
-for (EmailPacket packet: emailPackets)
-    dht.store(packet);
-dht.store(new IndexPacket(emailPackets, emailDestination));
-	    // TODO uncomment for next milestone, move code above into OutboxProcessor
-/*		outbox.add(email);
-		outboxProcessor.checkForEmail();*/
-	}
-
-	public Runnable createCheckMailTask(EmailIdentity identity) {
-	    return new CheckEmailTask(identity, dht, peerManager, incompleteEmailFolder);
-	}
-	
-    public EmailFolder getInbox() {
-        return inbox;
-    }
-
-    public int getNumDhtPeers() {
-        return dht.getNumPeers();
-    }
-    
-    public int getNumRelayPeers() {
-        return peerManager.getNumPeers();
-    }
-    
-    private void startAllServices()  {
-        dht.start();
-		outboxProcessor.start();
-		relayPacketSender.start();
-		smtpService.start();
-		pop3Service.start();
-		sendQueue.start();
-	}
-
-	private void stopAllServices()  {
-        dht.shutDown();
-		outboxProcessor.shutDown();
-		relayPacketSender.requestShutdown();
-		smtpService.shutDown();
-		pop3Service.shutDown();
-        sendQueue.requestShutdown();
-	}
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/Identities.java b/apps/i2pbote/src/i2p/bote/Identities.java
deleted file mode 100644
index e525bbb297d3a4ff3e16ff4999d83f0668d7ba9b..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/Identities.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package i2p.bote;
-
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.regex.PatternSyntaxException;
-
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-public class Identities implements Iterable<EmailIdentity> {
-    private Log log = new Log(Identities.class);
-    private File identitiesFile;
-    private List<EmailIdentity> identities;
-
-    public Identities(File identitiesFile) {
-        this.identitiesFile = identitiesFile;
-        identities = Collections.synchronizedList(new ArrayList<EmailIdentity>());
-        
-        if (!identitiesFile.exists()) {
-            log.debug("Identities file does not exist: <" + identitiesFile.getAbsolutePath() + ">");
-            return;
-        }
-        
-        log.debug("Reading identities file: <" + identitiesFile.getAbsolutePath() + ">");
-        BufferedReader input = null;
-        try {
-            input = new BufferedReader(new FileReader(identitiesFile));
-            
-            while (true) {
-                String line = input.readLine();
-                if (line == null)   // EOF
-                    break;
-                
-                EmailIdentity identity = parse(line);
-                if (identity != null)
-                    identities.add(identity);
-            }
-        } catch (IOException e) {
-            log.error("Can't read identities file.", e);
-        }
-        finally {
-            if (input != null)
-                try {
-                    input.close();
-                }
-                catch (IOException e) {
-                    log.error("Error closing input stream.", e);
-                }
-        }
-    }
- 
-    private EmailIdentity parse(String emailIdentityString) {
-        try {
-            String[] fields = emailIdentityString.split("\\t", 4);
-            if (fields.length < 2) {
-                log.debug("Unparseable email identity: <" + emailIdentityString + ">");
-                return null;
-            }
-            EmailIdentity identity = new EmailIdentity(fields[0]);
-            if (fields.length > 1)
-                identity.setPublicName(fields[1]);
-            if (fields.length > 2)
-                identity.setDescription(fields[2]);
-            if (fields.length > 3)
-                identity.setEmailAddress(fields[3]);
-            return identity;
-        }
-        catch (PatternSyntaxException e) {
-            log.debug("Unparseable email identity: <" + emailIdentityString + ">");
-            return null;
-        }
-    }
-    
-    /**
-     * This is the counterpart of the <code>parse</code> method. It encodes a {@link EmailIdentity} into
-     * an entry for the identities file.
-     * @param identity
-     * @return
-     */
-    private String toFileFormat(EmailIdentity identity) {
-        StringBuilder string = new StringBuilder();
-        string = string.append(identity.getFullKey());
-        string = string.append("\t");
-        string = string.append(identity.getPublicName());
-        string = string.append("\t");
-        if (identity.getDescription() != null)
-            string = string.append(identity.getDescription());
-        string = string.append("\t");
-        if (identity.getEmailAddress() != null)
-            string = string.append(identity.getEmailAddress());
-        return string.toString();
-    }
-    
-    public void save() throws IOException {
-        String newLine = System.getProperty("line.separator");
-        try {
-            Writer writer = new BufferedWriter(new FileWriter(identitiesFile));
-            for (EmailIdentity identity: identities)
-                writer.write(toFileFormat(identity) + newLine);
-            writer.close();
-        }
-        catch (IOException e) {
-            log.error("Can't save email identities to file <" + identitiesFile.getAbsolutePath() + ">.", e);
-            throw e;
-        }
-    }
-    
-    public void add(EmailIdentity identity) {
-        identities.add(identity);
-    }
-    
-    public void remove(String key) {
-        EmailIdentity identity = get(key);
-        if (identity != null)
-            identities.remove(identity);
-    }
-    
-    public EmailIdentity get(int i) {
-        return identities.get(i);
-    }
-
-    /**
-     * Looks up an {@link EmailIdentity} by its Base64 key. If none is found,
-     * <code>null</code> is returned.
-     * @param key
-     * @return
-     */
-    public EmailIdentity get(String key) {
-        if (key==null || key.isEmpty())
-            return null;
-        
-        for (EmailIdentity identity: identities)
-            if (key.equals(identity.getKey()))
-                return identity;
-        return null;
-    }
-    
-    public Collection<EmailIdentity> getAll() {
-        return identities;
-    }
-    
-    public EmailIdentity[] getArray() {
-        return identities.toArray(new EmailIdentity[0]);
-    }
-    
-    public int size() {
-        return identities.size();
-    }
-    
-    public boolean contains(Hash emailDestination) {
-        // TODO
-        return true;
-    }
-    
-    @Override
-    public Iterator<EmailIdentity> iterator() {
-        return identities.iterator();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/Util.java b/apps/i2pbote/src/i2p/bote/Util.java
deleted file mode 100644
index 24f6e50b1eb28996f70faf2009fe4ae653df6a0f..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/Util.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package i2p.bote;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.Properties;
-import java.util.concurrent.ThreadFactory;
-
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
-import net.i2p.client.I2PSession;
-import net.i2p.client.I2PSessionException;
-import net.i2p.client.streaming.I2PSocketManager;
-import net.i2p.client.streaming.I2PSocketManagerFactory;
-import net.i2p.data.DataFormatException;
-
-public class Util {
-	private static final int BUFFER_SIZE = 32 * 1024;
-
-	private Util() { }
-	
-	public static void copyBytes(InputStream inputStream, OutputStream outputStream) throws IOException {
-		byte[] buffer = new byte[BUFFER_SIZE];
-		boolean done = false;
-		while (!done) {
-			int bytesRead = inputStream.read(buffer);
-			outputStream.write(buffer);
-			if (bytesRead < 0)
-				done = true;
-		}
-	}
-
-	public static void writeKeyStream(I2PSession i2pSession, OutputStream outputStream) throws DataFormatException, IOException {
-		i2pSession.getMyDestination().writeBytes(outputStream);
-		i2pSession.getDecryptionKey().writeBytes(outputStream);
-		i2pSession.getPrivateKey().writeBytes(outputStream);
-	}
-
-	public static InputStream createKeyStream(I2PSession i2pSession) throws DataFormatException, IOException {
-		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-		writeKeyStream(i2pSession, outputStream);
-		return new ByteArrayInputStream(outputStream.toByteArray());
-	}	
-
-	public static I2PSession createNewSession(I2PClient i2pClient, I2PSession existingSession, Properties options) throws I2PSessionException, DataFormatException, IOException {
-	    return I2PClientFactory.createClient().createSession(createKeyStream(existingSession), options);
-	}
-	
-    public static I2PSocketManager getSocketManager(I2PSession i2pSession) throws DataFormatException, IOException {
-        ByteArrayOutputStream keyOutputStream = new ByteArrayOutputStream();
-        Util.writeKeyStream(i2pSession, keyOutputStream);
-        InputStream keyInputStream = new ByteArrayInputStream(keyOutputStream.toByteArray());
-        return I2PSocketManagerFactory.createManager(keyInputStream);
-    }
-
-    public static byte[] readInputStream(InputStream inputStream) throws IOException {
-        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
-        byte[] buffer = new byte[32*1024];
-        while (true) {
-            int bytesToRead = Math.min(inputStream.available(), buffer.length);
-            if (bytesToRead <= 0)
-                break;
-            else {
-                int bytesRead = inputStream.read(buffer, 0, bytesToRead);
-                byteStream.write(buffer, 0, bytesRead);
-            }
-        }
-        return byteStream.toByteArray();
-    }
-
-    public static ThreadFactory createThreadFactory(final String threadName, final int stackSize) {
-        return new ThreadFactory() {
-            @Override
-            public Thread newThread(Runnable runnable) {
-                return new Thread(null, runnable, threadName, stackSize);
-            }
-        };
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/folder/DhtPacketFolder.java b/apps/i2pbote/src/i2p/bote/folder/DhtPacketFolder.java
deleted file mode 100644
index cc4f60a36dca0c968c000a37b2a56db7f857b18f..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/folder/DhtPacketFolder.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package i2p.bote.folder;
-
-import i2p.bote.network.DhtStorageHandler;
-import i2p.bote.packet.dht.DhtStorablePacket;
-
-import java.io.File;
-import java.io.FilenameFilter;
-
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-/**
- * This class uses dht keys for file names.
- *
- * @author HungryHobo@mail.i2p
- * @param <T> The type of DHT data stored in this folder
- */
-public class DhtPacketFolder<T extends DhtStorablePacket> extends PacketFolder<T> implements DhtStorageHandler {
-    private Log log = new Log(DhtPacketFolder.class);
-
-    public DhtPacketFolder(File storageDir) {
-        super(storageDir);
-    }
-    
-    @Override
-    public void store(DhtStorablePacket packetToStore) {
-        add(packetToStore);
-    }
-    
-    @Override
-    public DhtStorablePacket retrieve(Hash dhtKey) {
-        final String base64Key = dhtKey.toBase64();
-        
-        File[] files = storageDir.listFiles(new FilenameFilter() {
-            @Override
-            public boolean accept(File dir, String name) {
-                return filenameMatches(name, base64Key);
-            }
-        });
-        
-        if (files.length > 1)
-            log.warn("More than one packet files found for DHT key " + dhtKey);
-        if (files.length > 0) {
-            File file = files[0];
-            return DhtStorablePacket.createPacket(file);
-        }
-        return null;
-    }
-
-    protected boolean filenameMatches(String filename, String base64DhtKey) {
-        return filename.startsWith(base64DhtKey);
-    }
-    
-    @Override
-    protected String getFilename(DhtStorablePacket packet) {
-        return packet.getDhtKey().toBase64() + PACKET_FILE_EXTENSION;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/folder/EmailFolder.java b/apps/i2pbote/src/i2p/bote/folder/EmailFolder.java
deleted file mode 100644
index cf71f27586ae0c968120f60ee4bba0f2c8028fc7..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/folder/EmailFolder.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package i2p.bote.folder;
-
-import i2p.bote.packet.Email;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-
-import javax.mail.MessagingException;
-
-import net.i2p.util.Log;
-
-/**
- * Stores emails in a directory on the filesystem. Each email is stored in one file.
- * The filename is the message Id plus an extension.
- * 
- * @author HungryHobo@mail.i2p
- */
-public class EmailFolder extends Folder<Email> {
-    protected static final String EMAIL_FILE_EXTENSION = ".mail";
-    
-    private Log log = new Log(EmailFolder.class);
-    
-    public EmailFolder(File storageDir) {
-        super(storageDir, EMAIL_FILE_EXTENSION);
-    }
-    
-    // store an email file
-    public void add(Email email) throws MessagingException, IOException {
-        // write out the email file
-        File emailFile = getEmailFile(email);
-        log.info("Storing email in outbox: '"+ emailFile.getAbsolutePath() + "'");
-        OutputStream emailOutputStream = new FileOutputStream(emailFile);
-        email.writeTo(emailOutputStream);
-        emailOutputStream.close();
-    }
-    
-    private File getEmailFile(Email email) throws MessagingException {
-        return new File(storageDir, email.getMessageID() + EMAIL_FILE_EXTENSION);
-    }
-
-    public void delete(Email email) throws MessagingException {
-        if (!getEmailFile(email).delete())
-            log.error("Cannot delete file: '" + getEmailFile(email) + "'");
-    }
-
-    @Override
-    protected Email createFolderElement(File file) throws Exception {
-        FileInputStream inputStream = new FileInputStream(file);
-        return new Email(inputStream);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/folder/Folder.java b/apps/i2pbote/src/i2p/bote/folder/Folder.java
deleted file mode 100644
index d3bb0c37d312b0c57d0a98454a5e856281174936..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/folder/Folder.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package i2p.bote.folder;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-import java.util.Iterator;
-
-import net.i2p.util.Log;
-
-/**
- * 
- * @author HungryHobo@mail.i2p
- *
- * @param <T> The type of objects the folder can store.
- */
-public abstract class Folder<T extends FolderElement> implements Iterable<T> {
-    private Log log = new Log(Folder.class);
-    protected File storageDir;
-    protected String fileExtension;
-
-    protected Folder(File storageDir, String fileExtension) {
-        this.storageDir = storageDir;
-        this.fileExtension = fileExtension;
-        
-        if (!storageDir.exists() && !storageDir.mkdirs())
-            log.error("Can't create directory: '" + storageDir + "'");
-    }
-
-    public File getStorageDirectory() {
-        return storageDir;
-    }
-
-    public Collection<T> getElements() {
-        Collection<T> elements = new ArrayList<T>();
-        Iterator<T> iterator = iterator();
-        while (iterator.hasNext())
-            elements.add(iterator.next());
-        return elements;
-    }
-    
-    // An {@link Iterator} implementation that loads one file into memory at a time.
-    @Override
-    public final Iterator<T> iterator() {
-        final File[] files = storageDir.listFiles(new FilenameFilter() {
-            @Override
-            public boolean accept(File dir, String name) {
-                return name.toUpperCase().endsWith(fileExtension.toUpperCase());
-            }
-        });
-        log.debug(files.length + " files with the extension '" + fileExtension + "' found in '" + storageDir + "'.");
-
-        // sort files by date, newest first
-        Arrays.sort(files, new Comparator<File>() {
-            @Override
-            public int compare(File f1, File f2) {
-                return (int)Math.signum(f1.lastModified() - f2.lastModified());
-            }
-        });
-
-        return new Iterator<T>() {
-            int nextIndex = 0;
-            
-            @Override
-            public boolean hasNext() {
-                return nextIndex < files.length;
-            }
-
-            @Override
-            public T next() {
-                File file = files[nextIndex];
-                String filePath = file.getAbsolutePath();
-                log.info("Reading file: '" + filePath + "'");
-                try {
-                    T nextElement = createFolderElement(file);
-                    nextElement.setFile(file);
-                    nextIndex++;
-                    return nextElement;
-                }
-                catch (Exception e) {
-                    log.error("Can't create a FolderElement from file: " + filePath, e);
-                    return null;
-                }
-            }
-            
-            @Override
-            public void remove() {
-                throw new UnsupportedOperationException("Use the Folder instance to delete a folder element.");
-            }
-        };
-    }
-    
-    protected abstract T createFolderElement(File file) throws Exception;
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/folder/FolderElement.java b/apps/i2pbote/src/i2p/bote/folder/FolderElement.java
deleted file mode 100644
index c31cc4eb35d5b62d8a64078cd834bf8ee9e897d0..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/folder/FolderElement.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package i2p.bote.folder;
-
-import java.io.File;
-import java.io.OutputStream;
-
-public interface FolderElement {
-
-    void setFile(File file);
-    
-    /**
-     * Returns the {@link File} from which this packet was read, or <code>null</code>.
-     * @return
-     */
-    File getFile();
-    
-    void writeTo(OutputStream outputStream) throws Exception;
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/folder/IncompleteEmailFolder.java b/apps/i2pbote/src/i2p/bote/folder/IncompleteEmailFolder.java
deleted file mode 100644
index 03ea8ee37b650252c82b9e923d84312ab426dcca..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/folder/IncompleteEmailFolder.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package i2p.bote.folder;
-
-import i2p.bote.packet.DataPacket;
-import i2p.bote.packet.Email;
-import i2p.bote.packet.EmailPacket;
-import i2p.bote.packet.I2PBotePacket;
-import i2p.bote.packet.UniqueId;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Comparator;
-
-import net.i2p.util.Log;
-
-/**
- * File name format: <email dest hash>_<message id>.pkt
- *
- * @author HungryHobo@mail.i2p
- */
-public class IncompleteEmailFolder extends DhtPacketFolder<EmailPacket> {
-    private Log log = new Log(IncompleteEmailFolder.class);
-    private EmailFolder inbox;
-
-    public IncompleteEmailFolder(File storageDir, EmailFolder inbox) {
-        super(storageDir);
-        this.inbox = inbox;
-    }
-    
-    public void add(EmailPacket packetToStore) {
-        super.add(packetToStore);
-        
-        // TODO possible optimization: if getNumFragments == 1, no need to check for other packet files
-        File[] finishedPacketFiles = getAllMatchingFiles(packetToStore.getMessageId());
-        
-        // if all packets of the email are available, assemble them into an email
-        if (finishedPacketFiles.length == packetToStore.getNumFragments())
-            assemble(finishedPacketFiles);
-    }
-    
-    private void assemble(File[] packetFiles) {
-        // No need to do this in a separate thread
-        new AssembleTask(packetFiles, inbox).run();
-    }
-    
-    private File[] getAllMatchingFiles(UniqueId messageId) {
-        final String base64Id = messageId.toBase64();
-        
-        return storageDir.listFiles(new FilenameFilter() {
-            @Override
-            public boolean accept(File dir, String name) {
-                int _index = name.indexOf('_');
-                if (_index >= 0)
-                    return name.substring(_index).startsWith(base64Id);
-                else {
-                    log.error("Invalid file name: <" + name + ">, directory: <" + dir.getAbsolutePath() + ">");
-                    return false;
-                }
-            }
-        });
-    }
-    
-    // Overridden to include the message id in the file name
-    protected String getFilename(EmailPacket packet) {
-        return packet.getDhtKey().toBase64() + packet.getMessageId() + PACKET_FILE_EXTENSION;
-    }
-
-    // Overridden because the file format is different than what the superclass uses
-    @Override
-    protected boolean filenameMatches(String filename, String base64DhtKey) {
-        return filename.startsWith(base64DhtKey);
-    }
-    
-    /**
-     * Makes a set of {@link EmailPacket}s into an {@link Email}, stores the email in the <code>inbox</code>
-     * folder, and deletes the packet files.
-     *
-     * @author HungryHobo@mail.i2p
-     */
-    private class AssembleTask implements Runnable {
-        File[] packetFiles;
-        
-        public AssembleTask(File[] packetFiles, EmailFolder inbox) {
-            this.packetFiles = packetFiles;
-        }
-
-        @Override
-        public void run() {
-            EmailPacket[] packets = getEmailPackets(packetFiles).toArray(new EmailPacket[0]);
-            
-            // sort by fragment index
-            Arrays.sort(packets, new Comparator<EmailPacket>() {
-                @Override
-                public int compare(EmailPacket packet1, EmailPacket packet2) {
-                    return new Integer(packet1.getFragmentIndex()).compareTo(packet2.getFragmentIndex());
-                }
-            });
-
-            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-            try {
-                for (EmailPacket packet: packets)
-                    outputStream.write(packet.getContent());
-                Email email = new Email(outputStream.toByteArray());
-                inbox.add(email);
-                
-                // delete packets
-                for (File file: packetFiles)
-                    if (!file.delete())
-                        log.warn("Email packet file not deleted: <" + file.getAbsolutePath() + ">");
-            }
-            catch (Exception e) {
-                log.error("Error assembling/storing email, or deleting email packets. ", e);
-                return;
-            }
-        }
-        
-        private Collection<EmailPacket> getEmailPackets(File[] files) {
-            Collection<EmailPacket> packets = new ArrayList<EmailPacket>();
-            for (File file: files) {
-                I2PBotePacket packet = DataPacket.createPacket(file);
-                if (packet instanceof EmailPacket)
-                    packets.add((EmailPacket)packet);
-                else
-                    log.error("Non-Email Packet found in the IncompleteEmailFolder, file: <" + file.getAbsolutePath() + ">");
-            }
-            return packets;
-        }
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/folder/IndexPacketFolder.java b/apps/i2pbote/src/i2p/bote/folder/IndexPacketFolder.java
deleted file mode 100644
index 4879e3617b240add93d70c38ba91095292a10928..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/folder/IndexPacketFolder.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package i2p.bote.folder;
-
-import i2p.bote.packet.I2PBotePacket;
-import i2p.bote.packet.IndexPacket;
-import i2p.bote.packet.dht.DhtStorablePacket;
-
-import java.io.File;
-
-import net.i2p.util.Log;
-
-/**
- * This class differs from {@link DhtPacketFolder} in that it doesn't overwrite an existing
- * packet when a new packet is stored under the same key, but 
- *
- * @author HungryHobo@mail.i2p
- */
-public class IndexPacketFolder extends DhtPacketFolder<IndexPacket> {
-    private final Log log = new Log(I2PBotePacket.class);
-
-    public IndexPacketFolder(File storageDir) {
-        super(storageDir);
-    }
-
-    @Override
-    public void store(DhtStorablePacket packetToStore) {
-        if (!(packetToStore instanceof IndexPacket))
-            throw new IllegalArgumentException("This class only stores packets of type " + IndexPacket.class.getSimpleName() + ".");
-        
-        IndexPacket indexPacketToStore = (IndexPacket)packetToStore;
-        DhtStorablePacket existingPacket = retrieve(packetToStore.getDhtKey());
-        
-        // If an index packet with the same key exists in the folder, merge the two packets.
-        if (existingPacket instanceof IndexPacket) {
-            packetToStore = new IndexPacket(indexPacketToStore, (IndexPacket)existingPacket);
-            if (packetToStore.isTooBig())
-                // TODO make two new index packets, put half the email packet keys in each one, store the two index packets on the DHT, and put the two index packet keys into the local index file (only keep those two).
-                log.error("After merging, IndexPacket is too big for a datagram: size=" + packetToStore.getSize());
-        }
-        
-        super.store(packetToStore);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/folder/Outbox.java b/apps/i2pbote/src/i2p/bote/folder/Outbox.java
deleted file mode 100644
index 3bacd86b06ce99f4b68b3bbd7258a61686fe3d4f..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/folder/Outbox.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package i2p.bote.folder;
-
-import i2p.bote.packet.Email;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-
-import javax.mail.MessagingException;
-
-import net.i2p.util.Log;
-
-/**
- * Stores emails in a directory on the filesystem. For each email, two files are created; the actual
- * email and a status file.
- * Status files and email files have the same name, except for the extension.
- * Even emails that need to be fragmented are stored as a whole.
- * Message IDs are used for filenames.
- * 
- * Status files contain a status for each recipient address.
- * 
- * @author HungryHobo@mail.i2p
- *
- */
-public class Outbox extends EmailFolder implements Iterable<Email> {
-	private static final String STATUS_FILE_EXTENSION = ".status";
-//	private static final String PARAM_QUEUE_DATE = "X-QueueDate";
-	private static final Log log = new Log(Outbox.class);
-	
-	public Outbox(File storageDir) {
-		super(storageDir);
-	}
-	
-	// store one email file + one status file.
-	@Override
-	public void add(Email email) throws MessagingException, IOException {
-        // write out the email file
-	    super.add(email);
-		
-		// collect info for status file
-		String queueDate = String.valueOf(System.currentTimeMillis());
-		
-		// write out the status file
-		File statusFile = getStatusFile(email);
-		FileWriter statusFileWriter = new FileWriter(statusFile);
-		statusFileWriter.write(queueDate);
-		statusFileWriter.close();
-	}
-	
-	private File getStatusFile(Email email) throws MessagingException {
-		return new File(storageDir, email.getMessageID() + STATUS_FILE_EXTENSION);
-	}
-
-	// delete an email file + the status file
-    @Override
-    public void delete(Email email) throws MessagingException {
-	    super.delete(email);
-	    
-        if (!getStatusFile(email).delete())
-            log.error("Cannot delete file: '" + getStatusFile(email) + "'");
-    }
-
-	/**
-	 * 
-	 * @param email
-	 * @param relayInfo A 0-length array means no relays were used, i.e. the email was sent directly to the recipient.
-	 * @param statusMessage
-	 */
-	public void updateStatus(Email email, int[] relayInfo, String statusMessage) {
-		// TODO write out a new status file. filename is the msg id, statusMessage goes into the file.
-	}
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/folder/PacketFolder.java b/apps/i2pbote/src/i2p/bote/folder/PacketFolder.java
deleted file mode 100644
index 7bd7d6ab5689836bfd94ddacbbc9e7d62b87c0a6..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/folder/PacketFolder.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package i2p.bote.folder;
-
-import i2p.bote.packet.DataPacket;
-import i2p.bote.packet.UniqueId;
-import i2p.bote.packet.dht.DhtStorablePacket;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-import net.i2p.util.Log;
-
-/**
- * This class stores new files under a random file name with the .pkt extension.
- *
- * @author HungryHobo@mail.i2p
- * @param <T> The type of data stored in this folder
- */
-public class PacketFolder<T extends DataPacket> extends Folder<T> {
-    protected static final String PACKET_FILE_EXTENSION = ".pkt";
-    
-    private Log log = new Log(PacketFolder.class);
-
-    public PacketFolder(File storageDir) {
-        super(storageDir, PACKET_FILE_EXTENSION);
-    }
-    
-    public void add(DhtStorablePacket packetToStore) {
-        String filename = getFilename(packetToStore);
-        FileOutputStream outputStream = null;
-        try {
-            File file = new File(storageDir, filename);
-            outputStream = new FileOutputStream(file);
-            packetToStore.writeTo(outputStream);
-        } catch (Exception e) {
-            log.error("Can't save packet to file: <" + filename + ">", e);
-        }
-        finally {
-            if (outputStream != null)
-                try {
-                    outputStream.close();
-                }
-                catch (IOException e) {
-                    log.error("Can't close file: <" + filename + ">", e);
-                }
-        }
-    }
-    
-    public void delete(UniqueId packetId) {
-        // TODO
-    }
-
-    protected String getFilename(DhtStorablePacket packet) {
-        return new UniqueId().toBase64() + PACKET_FILE_EXTENSION;
-    }
-    
-    @Override
-    @SuppressWarnings("unchecked")
-    protected T createFolderElement(File file) throws Exception {
-        return (T)DataPacket.createPacket(file);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/DHT.java b/apps/i2pbote/src/i2p/bote/network/DHT.java
deleted file mode 100644
index a997227a58feeb8fbe2087bed03fde8a4cb23172..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/DHT.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.packet.dht.DhtStorablePacket;
-
-import java.util.Collection;
-
-import net.i2p.data.Hash;
-
-public interface DHT {
-
-    void store(DhtStorablePacket packet) throws Exception;
-    
-    DhtStorablePacket findOne(Hash key, Class<? extends DhtStorablePacket> dataType);
-
-    Collection<DhtStorablePacket> findAll(Hash key, Class<? extends DhtStorablePacket> dataType);
-
-    /**
-     * Registers a <code>DhtStorageHandler</code> that handles incoming storage requests of a certain
-     * type (but not its subclasses).
-     * @param packetType
-     * @param storageHandler
-     */
-    void setStorageHandler(Class<? extends DhtStorablePacket> packetType, DhtStorageHandler storageHandler);
-
-    /**
-     * Returns the current number of known active peers.
-     * @return
-     */
-    int getNumPeers();
-    
-    void start();
-    
-    void shutDown();
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/DhtStorageHandler.java b/apps/i2pbote/src/i2p/bote/network/DhtStorageHandler.java
deleted file mode 100644
index b99e057fefae2c0c6c966b8676152e90e1a6eeed..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/DhtStorageHandler.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.packet.dht.DhtStorablePacket;
-import net.i2p.data.Hash;
-
-/**
- * Defines methods for accessing a local DHT store.
- *
- * @author HungryHobo@mail.i2p
- */
-public interface DhtStorageHandler {
-
-    void store(DhtStorablePacket packetToStore);
-    
-    // Retrieves a packet by DHT key. If no matching packet is found, <code>null</code> is returned.
-    DhtStorablePacket retrieve(Hash dhtKey);
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/EmailAddressResolver.java b/apps/i2pbote/src/i2p/bote/network/EmailAddressResolver.java
deleted file mode 100644
index f58652caba3e45cc186ab94c31897f43ea4b9277..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/EmailAddressResolver.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.EmailDestination;
-
-public class EmailAddressResolver {
-
-    /**
-     * Looks up a key pair for an email address. This method blocks.
-     * The local address book is searched first, then the distributed
-     * email directory.
-     * @param emailAddress
-     * @return An <code>EmailDestination</code>, or <code>null</code> if none is found
-     */
-    public EmailDestination getDestination(String emailAddress) {
-        // TODO
-        return null;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/I2PPacketDispatcher.java b/apps/i2pbote/src/i2p/bote/network/I2PPacketDispatcher.java
deleted file mode 100644
index 983ca79bbfbf218214b40494ed6c4ced8063410d..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/I2PPacketDispatcher.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.packet.I2PBotePacket;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import net.i2p.client.I2PSession;
-import net.i2p.client.I2PSessionException;
-import net.i2p.client.I2PSessionListener;
-import net.i2p.client.datagram.I2PDatagramDissector;
-import net.i2p.client.datagram.I2PInvalidDatagramException;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-/**
- * An {@link I2PSessionListener} that receives datagrams from the I2P network and notifies {@link PacketListener}s.
- *
- * @author HungryHobo@mail.i2p
- */
-public class I2PPacketDispatcher implements I2PSessionListener {
-    private Log log = new Log(I2PPacketDispatcher.class);
-    private List<PacketListener> packetListeners;
-
-	public I2PPacketDispatcher() {
-		packetListeners = Collections.synchronizedList(new ArrayList<PacketListener>());
-	}
-    
-    public void addPacketListener(PacketListener listener) {
-        packetListeners.add(listener);
-    }
-    
-    public void removePacketListener(PacketListener listener) {
-        packetListeners.remove(listener);
-    }
-    
-    private void firePacketReceivedEvent(CommunicationPacket packet, Destination sender) {
-        for (PacketListener listener: packetListeners)
-            listener.packetReceived(packet, sender, System.currentTimeMillis());
-    }
-            
-	public void shutDown() {
-	}
-
-	// I2PSessionListener implementation follows
-	
-    @Override
-    public void reportAbuse(I2PSession session, int severity) {
-    }
-    
-    @Override
-    public void messageAvailable(I2PSession session, int msgId, long size) {
-        byte[] msg = new byte[0];
-        try {
-            msg = session.receiveMessage(msgId);
-        } catch (I2PSessionException e) {
-            log.error("Can't get new message from I2PSession.", e);
-        }
-        I2PDatagramDissector datagramDissector = new I2PDatagramDissector();
-        try {
-            datagramDissector.loadI2PDatagram(msg);
-            datagramDissector.verifySignature();   // TODO keep this line or remove it?
-            byte[] payload = datagramDissector.extractPayload();
-            Destination sender = datagramDissector.getSender();
-
-            CommunicationPacket packet = CommunicationPacket.createPacket(payload);
-            if (packet == null)
-                log.debug("Ignoring unparseable packet.");
-            else {
-                logPacket(packet, sender);
-                firePacketReceivedEvent(packet, sender);
-            }
-        }
-        catch (DataFormatException e) {
-            log.error("Invalid datagram received.", e);
-            e.printStackTrace();
-        }
-        catch (I2PInvalidDatagramException e) {
-            log.error("Datagram failed verification.", e);
-            e.printStackTrace();
-        }
-    }
-
-    private void logPacket(I2PBotePacket packet, Destination sender) {
-        String senderHash = sender.calculateHash().toBase64().substring(0, 8) + "...";
-        log.debug("I2P packet received: [" + packet + "] Sender: [" + senderHash + "]");
-    }
-    
-    @Override
-    public void errorOccurred(I2PSession session, String message, Throwable error) {
-        log.error("Router says: " + message, error);
-    }
-    
-    @Override
-    public void disconnected(I2PSession session) {
-        log.warn("I2P session disconnected.");
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/I2PSendQueue.java b/apps/i2pbote/src/i2p/bote/network/I2PSendQueue.java
deleted file mode 100644
index 8f3e26e23619784909e500d96ccb2c9607ba5b3a..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/I2PSendQueue.java
+++ /dev/null
@@ -1,354 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.packet.DataPacket;
-import i2p.bote.packet.RelayPacket;
-import i2p.bote.packet.RelayRequest;
-import i2p.bote.packet.ResponsePacket;
-import i2p.bote.packet.StatusCode;
-import i2p.bote.packet.UniqueId;
-import i2p.bote.service.I2PBoteThread;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentSkipListSet;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import net.i2p.client.I2PSession;
-import net.i2p.client.I2PSessionException;
-import net.i2p.client.datagram.I2PDatagramMaker;
-import net.i2p.data.Destination;
-import net.i2p.util.ConcurrentHashSet;
-import net.i2p.util.Log;
-
-import com.nettgryppa.security.HashCash;
-
-/**
- * All outgoing I2P traffic goes through this class.
- * Packets are sent at a rate no greater than specified by the
- * <CODE>maxBandwidth</CODE> property.
- * 
- * @author HungryHobo@mail.i2p
- *
- */
-public class I2PSendQueue extends I2PBoteThread implements PacketListener {
-    private Log log = new Log(I2PSendQueue.class);
-    private I2PSession i2pSession;
-    private SortedSet<ScheduledPacket> packetQueue;
-    private Map<UniqueId, ScheduledPacket> outstandingRequests;   // sent packets that haven't been responded to
-    private Set<PacketBatch> runningBatches;
-    private int maxBandwidth;
-    private Collection<SendQueuePacketListener> sendQueueListeners;
-    private Map<SendQueuePacketListener, Long> timeoutValues;
-
-    /**
-     * @param i2pSession
-     * @param i2pReceiver
-     */
-    public I2PSendQueue(I2PSession i2pSession, I2PPacketDispatcher i2pReceiver) {
-        super("I2PSendQueue");
-        
-        this.i2pSession = i2pSession;
-        i2pReceiver.addPacketListener(this);
-        packetQueue = new ConcurrentSkipListSet<ScheduledPacket>();
-        outstandingRequests = new ConcurrentHashMap<UniqueId, ScheduledPacket>();
-        runningBatches = new ConcurrentHashSet<PacketBatch>();
-        sendQueueListeners = Collections.synchronizedCollection(new ArrayList<SendQueuePacketListener>());
-        timeoutValues = new HashMap<SendQueuePacketListener, Long>();
-    }
-
-    /**
-     * Queues a packet for sending ASAP.
-     * @param packet
-     * @param destination
-     */
-    public void send(CommunicationPacket packet, Destination destination) {
-        send(packet, destination, 0);
-    }
-    
-    /**
-     * Queues a packet for sending ASAP and waits for up to <code>timeoutSeconds</code> for a response.
-     * @param packet
-     * @param destination
-     * @param timeoutMilliSeconds
-     * @return The response packet, or <code>null</code> if a timeout occurred.
-     * @throws InterruptedException 
-     */
-    public CommunicationPacket sendAndWait(CommunicationPacket packet, Destination destination, long timeoutMilliSeconds) throws InterruptedException {
-        ScheduledPacket scheduledPacket = new ScheduledPacket(packet, destination, 0);
-        
-        long scheduleTime = System.currentTimeMillis();
-        packetQueue.add(scheduledPacket);
-        scheduledPacket.awaitSending();
-        outstandingRequests.put(packet.getPacketId(), scheduledPacket);
-        scheduledPacket.awaitResponse(timeoutMilliSeconds, TimeUnit.MILLISECONDS);
-        
-        if (log.shouldLog(Log.DEBUG)) {
-            long responseTime = scheduledPacket.getResponseTime();
-            String responseInfo = responseTime<0?"timed out":"took " + (responseTime-scheduledPacket.getSentTime()) + "ms.";
-            log.debug("Packet with id " + packet.getPacketId() + " was queued for " + (scheduledPacket.getSentTime()-scheduleTime) + " ms, response " + responseInfo);
-        }
-        
-        return scheduledPacket.getResponse();
-    }
-    
-    /**
-     * Queues a packet for sending at or after a certain time.
-     * @param packet
-     * @param destination
-     * @param earliestSendTime
-     */
-    public void send(CommunicationPacket packet, Destination destination, long earliestSendTime) {
-        packetQueue.add(new ScheduledPacket(packet, destination, earliestSendTime));
-    }
-
-    public void sendRelayRequest(RelayPacket relayPacket, HashCash hashCash, long earliestSendTime) {
-        RelayRequest relayRequest = new RelayRequest(hashCash, relayPacket);
-        ScheduledPacket scheduledPacket = new ScheduledPacket(relayRequest, relayPacket.getNextDestination(), earliestSendTime);
-        packetQueue.add(scheduledPacket);
-    }
-    
-    /**
-     * Sends a Response Packet to a {@link Destination}, with the status code "OK".
-     * @param packet
-     * @param destination
-     * @param requestPacketId The packet id of the packet we're responding to
-     */
-    public void sendResponse(DataPacket packet, Destination destination, UniqueId requestPacketId) {
-        sendResponse(packet, destination, StatusCode.OK, requestPacketId);
-    }
-    
-    /**
-     * Sends a Response Packet to a {@link Destination}.
-     * @param packet
-     * @param destination
-     * @param statusCode
-     * @param requestPacketId The packet id of the packet we're responding to
-     */
-    public void sendResponse(DataPacket packet, Destination destination, StatusCode statusCode, UniqueId requestPacketId) {
-        send(new ResponsePacket(packet, statusCode, requestPacketId), destination);
-    }
-    
-    /**
-     * Sends a batch of packets, each to its own destination.
-     * Replies to the batch's packets are received until {@link remove(PacketBatch)} is called.
-     * @param batch
-     */
-    public void send(PacketBatch batch) {
-        for (PacketBatchItem batchItem: batch) {
-            ScheduledPacket scheduledPacket = new ScheduledPacket(batchItem.getPacket(), batchItem.getDestination(), 0, batch);
-            packetQueue.add(scheduledPacket);
-        }
-    }
-
-    public void remove(PacketBatch batch) {
-        runningBatches.remove(batch);
-    }
-    
-    public int getQueueLength() {
-        return packetQueue.size();
-    }
-    
-    /**
-     * Set the maximum outgoing bandwidth in kbits/s
-     * @param maxBandwidth
-     */
-    public void setMaxBandwidth(int maxBandwidth) {
-        this.maxBandwidth = maxBandwidth;
-    }
-
-    /**
-     * Return the maximum outgoing bandwidth in kbits/s
-     * @return
-     */
-    public int getMaxBandwidth() {
-        return maxBandwidth;
-    }
-    
-    public void addSendQueueListener(SendQueuePacketListener listener) {
-        addSendQueueListener(listener, null);
-    }
-    
-    /**
-     * Add a {@link SendQueuePacketListener} that is automatically removed after
-     * <code>timeout</code> milliseconds.
-     * @param listener
-     * @param timeout
-     */
-    public void addSendQueueListener(SendQueuePacketListener listener, Long timeout) {
-        sendQueueListeners.add(listener);
-        timeoutValues.put(listener, timeout);
-    }
-    
-    public void removeSendQueueListener(SendQueuePacketListener listener) {
-        sendQueueListeners.remove(listener);
-        timeoutValues.remove(listener);
-    }
-    
-    private void firePacketListeners(CommunicationPacket packet) {
-        for (SendQueuePacketListener listener: sendQueueListeners)
-            listener.packetSent(packet);
-    }
-
-    // Implementation of PacketListener
-    @Override
-    public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
-        if (packet instanceof ResponsePacket) {
-            log.debug("Response Packet received: Packet Id = " + packet.getPacketId() + " Sender = " + sender);
-            
-            UniqueId packetId = packet.getPacketId();
-
-            for (PacketBatch batch: runningBatches)
-                if (batch.contains(packetId))
-                    batch.addResponsePacket(packet);
-
-            // Remove the original request if it is on the list, and notify objects waiting on a response
-            ScheduledPacket requestPacket = outstandingRequests.remove(packetId);
-            if (requestPacket != null) {
-                requestPacket.setResponse(packet);
-                requestPacket.decrementResponseLatch();
-            }
-            
-            // If there is a packet file for the original packet, it can be deleted now.
-            // Use the cached filename if the packet was in the cache; otherwise, find the file by packet Id
-/*            ScheduledPacket cachedPacket = recentlySentCache.remove(packetId);
-            if (cachedPacket != null)
-                fileManager.removePacketFile(cachedPacket);
-            else
-                fileManager.removePacketFile(packetId);*/
-        }
-    }
-    
-    @Override
-    public void run() {
-        I2PDatagramMaker datagramMaker = new I2PDatagramMaker(i2pSession);
-        
-        while (true) {
-            if (packetQueue.isEmpty())
-                try {
-                    TimeUnit.SECONDS.sleep(1);
-                }
-                catch (InterruptedException e) {
-                    log.warn("Interrupted while waiting for new packets.", e);
-                }
-            else {
-                ScheduledPacket scheduledPacket = packetQueue.last();
-                CommunicationPacket i2pBotePacket = scheduledPacket.data;
-                
-                // wait long enough so rate <= maxBandwidth;
-                if (maxBandwidth > 0) {
-                    int packetSizeBits = i2pBotePacket.getSize() * 8;
-                    int maxBWBitsPerSecond = maxBandwidth * 1024;
-                    long waitTimeMsecs = 1000L * packetSizeBits / maxBWBitsPerSecond;
-                    if (System.currentTimeMillis()+waitTimeMsecs < scheduledPacket.earliestSendTime)
-                        waitTimeMsecs = scheduledPacket.earliestSendTime;
-                    try {
-                        TimeUnit.MILLISECONDS.sleep(waitTimeMsecs);
-                    }
-                    catch (InterruptedException e) {
-                        log.warn("Interrupted while waiting to send packet.", e);
-                    }
-                }
-                
-                log.debug("Sending packet: [" + i2pBotePacket + "] to peer: " + scheduledPacket.destination.toBase64());
-                byte[] replyableDatagram = datagramMaker.makeI2PDatagram(i2pBotePacket.toByteArray());
-                try {
-                    i2pSession.sendMessage(scheduledPacket.destination, replyableDatagram);
-                    
-                    // set sentTime; update queue+cache, update countdown latch, fire packet listeners
-                    scheduledPacket.data.setSentTime(System.currentTimeMillis());
-                    packetQueue.remove(scheduledPacket);
-                    if (scheduledPacket.batch != null)
-                        scheduledPacket.batch.decrementSentLatch();
-                    scheduledPacket.decrementSentLatch();
-                    firePacketListeners(scheduledPacket.data);
-                }
-                catch (I2PSessionException e) {
-                    log.error("Can't send packet.", e);
-                }
-            }
-        }
-    }
-    
-    private class ScheduledPacket implements Comparable<ScheduledPacket> {
-        CommunicationPacket data;
-        Destination destination;
-        long earliestSendTime;
-        PacketBatch batch;   // the batch this packet belongs to, or null if not part of a batch
-        long sentTime;
-        long responseTime;
-        CountDownLatch sentSignal;
-        CountDownLatch responseSignal;
-        CommunicationPacket response;
-        
-        public ScheduledPacket(CommunicationPacket packet, Destination destination, long earliestSendTime) {
-            this(packet, destination, earliestSendTime, null);
-        }
-
-        public ScheduledPacket(CommunicationPacket packet, Destination destination, long earliestSendTime, PacketBatch batch) {
-            this.data = packet;
-            this.destination = destination;
-            this.earliestSendTime = earliestSendTime;
-            this.batch = batch;
-            this.sentSignal = new CountDownLatch(1);
-            this.responseSignal = new CountDownLatch(1);
-            this.responseTime = -1;
-        }
-
-        @Override
-        public int compareTo(ScheduledPacket anotherPacket) {
-            return new Long(earliestSendTime).compareTo(anotherPacket.earliestSendTime);
-        }
-        
-        public void decrementSentLatch() {
-            sentTime = System.currentTimeMillis();
-            sentSignal.countDown();
-        }
-        
-        public void awaitSending() throws InterruptedException {
-            sentSignal.await();
-        }
-
-        public long getSentTime() {
-            return sentTime;
-        }
-        
-        /**
-         * Returns the time at which a response packet was received, or -1 if no response.
-         * @return
-         */
-        public long getResponseTime() {
-            return responseTime;
-        }
-        
-        public void decrementResponseLatch() {
-            responseTime = System.currentTimeMillis();
-            responseSignal.countDown();
-        }
-        
-        public void awaitResponse(long timeout, TimeUnit unit) throws InterruptedException {
-            responseSignal.await(timeout, unit);
-        }
-        
-        public void setResponse(CommunicationPacket response) {
-            this.response = response;
-        }
-
-        /**
-         * If this packet was sent via <code>sendAndWait</code>, and a response packet has been
-         * received (i.e., a packet with the same packet id), this method returns the response packet.
-         * Otherwise, <code>null</code> is returned.
-         * @return
-         */
-        public CommunicationPacket getResponse() {
-            return response;
-        }
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/PacketBatch.java b/apps/i2pbote/src/i2p/bote/network/PacketBatch.java
deleted file mode 100644
index dec2b8df3804dff53ae0e25dcb7c16b9448ee0c2..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/PacketBatch.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.packet.I2PBotePacket;
-import i2p.bote.packet.UniqueId;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import net.i2p.data.Destination;
-import net.i2p.util.ConcurrentHashSet;
-
-/**
- * This class is used for sending a number of packets to other nodes,
- * and collecting the response packets.
- * 
- * @author HungryHobo@mail.i2p
- *
- */
-// TODO use I2PSendQueue.sendAndWait(), get rid of PacketBatch.sentSignal, etc?
-public class PacketBatch implements Iterable<PacketBatchItem> {
-    private Map<UniqueId, PacketBatchItem> outgoingPackets;
-    private Set<I2PBotePacket> incomingPackets;
-    private CountDownLatch sentSignal;   // this field is lazy-initialized
-    private CountDownLatch firstReplyReceivedSignal;
-
-    public PacketBatch() {
-        outgoingPackets = new ConcurrentHashMap<UniqueId, PacketBatchItem>();
-        incomingPackets = new ConcurrentHashSet<I2PBotePacket>();
-        firstReplyReceivedSignal = new CountDownLatch(1);
-    }
-    
-    public void putPacket(CommunicationPacket packet, Destination destination) {
-        outgoingPackets.put(packet.getPacketId(), new PacketBatchItem(packet, destination));
-    }
-
-    /**
-     * Return <code>true</code> if the batch contains a packet with a given {@link UniqueId}.
-     * @param packetId
-     * @return
-     */
-    public boolean contains(final UniqueId packetId) {
-        return outgoingPackets.containsKey(packetId);
-    }
-    
-    public int getPacketCount() {
-        return outgoingPackets.keySet().size();
-    }
-
-    public Iterator<PacketBatchItem> iterator() {
-        return outgoingPackets.values().iterator();
-    }
-    
-/*    private void decrementConfirmedLatch() {
-        initSignals();
-        confirmedSignal.countDown();
-    }
-
-    boolean areAllPacketsConfirmed() {
-        return confirmedSignal.getCount() == 0;
-    }
-
-    boolean isPacketConfirmed(UniqueId packetId) {
-        decrementConfirmedLatch();
-        return packetMap.get(packetId).isDeliveryConfirmed();
-    }*/
-
-    /**
-     * Notify the <code>PacketBatch</code> that delivery confirmation has been received for
-     * a packet.
-     * @param packetId
-     */
-/*    void confirmDelivery(UniqueId packetId) {
-        if (outgoingPackets.containsKey(packetId))
-            outgoingPackets.get(packetId).confirmDelivery();
-    }*/
-    
-    void addResponsePacket(I2PBotePacket packet) {
-        incomingPackets.add(packet);
-        firstReplyReceivedSignal.countDown();
-    }
-    
-    public Set<I2PBotePacket> getResponsePackets() {
-        return incomingPackets;
-    }
-    
-    void decrementSentLatch() {
-        getSentSignal().countDown();
-    }
-    
-    private synchronized CountDownLatch getSentSignal() {
-        if (sentSignal == null)
-            sentSignal = new CountDownLatch(getPacketCount());
-        return sentSignal;
-    }
-    
-    public void awaitSendCompletion() throws InterruptedException {
-        getSentSignal().await(5, TimeUnit.MINUTES);
-    }
-    
-    public void awaitFirstReply(long timeout, TimeUnit timeoutUnit) throws InterruptedException {
-        firstReplyReceivedSignal.await(timeout, timeoutUnit);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/PacketBatchItem.java b/apps/i2pbote/src/i2p/bote/network/PacketBatchItem.java
deleted file mode 100644
index 8afe58e99b9342d0dcf5ab1e43a8f806004f8c85..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/PacketBatchItem.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.packet.CommunicationPacket;
-import net.i2p.data.Destination;
-
-public class PacketBatchItem {
-    private CommunicationPacket packet;
-    private Destination destination;
-    private boolean confirmed;
-    
-    PacketBatchItem(CommunicationPacket packet, Destination destination) {
-        this.packet = packet;
-        this.destination = destination;
-        confirmed = false;
-    }
-    
-    public CommunicationPacket getPacket() {
-        return packet;
-    }
-    
-    public Destination getDestination() {
-        return destination;
-    }
-
-    void confirmDelivery() {
-        confirmed = true;
-    }
-    
-    boolean isDeliveryConfirmed() {
-        return confirmed;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/PacketListener.java b/apps/i2pbote/src/i2p/bote/network/PacketListener.java
deleted file mode 100644
index 42e020cef18b4ff806b762b4bf5fcb14f5048f6d..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/PacketListener.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.packet.CommunicationPacket;
-
-import java.util.EventListener;
-
-import net.i2p.data.Destination;
-
-public interface PacketListener extends EventListener {
-
-    void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime);
-}
diff --git a/apps/i2pbote/src/i2p/bote/network/PeerManager.java b/apps/i2pbote/src/i2p/bote/network/PeerManager.java
deleted file mode 100644
index eada4f5a202295ee48dc627e6be661b02ba8325c..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/PeerManager.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package i2p.bote.network;
-
-import net.i2p.data.Destination;
-
-public class PeerManager {
-
-    public PeerManager() {
-    }
-    
-    /**
-     * Return a random active peer.
-     * 
-     * The peers are managed independently of the Kademlia peers because they need to
-     * be uniformly distributed across the key space to prevent leaking information about
-     * the local destination key to nodes that could link it to the local email address.
-     * @return
-     */
-    public Destination getRandomPeer() {
-        return null;
-    }
-
-    public int getNumPeers() {
-        // TODO
-        return 0;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/SendQueuePacketListener.java b/apps/i2pbote/src/i2p/bote/network/SendQueuePacketListener.java
deleted file mode 100644
index 60fd5717358ac7849abe3487a4662dbd1ee6ddc6..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/SendQueuePacketListener.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package i2p.bote.network;
-
-import i2p.bote.packet.I2PBotePacket;
-
-public interface SendQueuePacketListener {
-
-    /**
-     * 
-     * @param packet
-     */
-    void packetSent(I2PBotePacket packet);
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/kademlia/BucketManager.java b/apps/i2pbote/src/i2p/bote/network/kademlia/BucketManager.java
deleted file mode 100644
index 57c950e71cff6fd58398ad5383f53df36844cedb..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/kademlia/BucketManager.java
+++ /dev/null
@@ -1,323 +0,0 @@
-package i2p.bote.network.kademlia;
-
-import i2p.bote.network.I2PSendQueue;
-import i2p.bote.network.PacketListener;
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.service.I2PBoteThread;
-
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-import net.i2p.data.Destination;
-import net.i2p.data.Hash;
-import net.i2p.util.ConcurrentHashSet;
-import net.i2p.util.Log;
-import net.i2p.util.RandomSource;
-
-// TODO if a sibling times out, refill the sibling table
-public class BucketManager extends I2PBoteThread implements PacketListener {
-    private static final int INTERVAL = 5 * 60 * 1000;
-    private static final int PING_TIMEOUT = 20 * 1000;
-
-    private Log log = new Log(BucketManager.class);
-    private I2PSendQueue sendQueue;
-    private List<KBucket> kBuckets;
-    private KBucket siblingBucket;   // TODO [ordered furthest away to closest in terms of hash distance to local node]
-    private Hash localDestinationHash;
-
-    public BucketManager(I2PSendQueue sendQueue, Collection<KademliaPeer> initialPeers, Hash localDestinationHash) {
-        super("BucketMgr");
-        this.sendQueue = sendQueue;
-        this.localDestinationHash = localDestinationHash;
-        kBuckets = Collections.synchronizedList(new ArrayList<KBucket>());
-        kBuckets.add(new KBucket(KBucket.MIN_HASH_VALUE, KBucket.MAX_HASH_VALUE, 0, true));   // this is the root bucket, so depth=0
-        siblingBucket = new KBucket(KBucket.MIN_HASH_VALUE, KBucket.MAX_HASH_VALUE, 0, false);
-        addAll(initialPeers);
-    }
-    
-    public void addAll(Collection<KademliaPeer> nodes) {
-        for (KademliaPeer node: nodes)
-            addOrUpdate(node);
-    }
-    
-    /**
-     * Add a <code>{@link KademliaPeer}</code> to the sibling list or a bucket.
-     * @param peer
-     */
-    public void addOrUpdate(KademliaPeer peer) {
-        Hash peerHash = peer.getDestinationHash();
-        log.debug("Adding/updating peer: Hash = " + peerHash);
-
-        peer.resetStaleCounter();
-        
-        synchronized(this) {
-            if (!siblingBucket.isFull() || siblingBucket.contains(peer)) {
-                siblingBucket.addOrUpdate(peer);
-                getBucket(peerHash).remove(peer);
-            }
-            else if (isCloserSibling(peer)) {
-                KademliaPeer ejectedPeer = siblingBucket.getMostDistantPeer(localDestinationHash);
-                
-                addToBucket(ejectedPeer);
-                siblingBucket.remove(ejectedPeer);
-                
-                siblingBucket.addOrUpdate(peer);
-                getBucket(peerHash).remove(peer);
-            }
-            else
-                addToBucket(peer);
-        }
-        logBucketStats();
-            
-/*        KademliaPeer ejectedPeer = addSibling(peer);
-        // if the peer was added as a sibling, it may need to be removed from a bucket
-        if (ejectedPeer != peer)
-            getBucket(peerHash).remove(peer);
-        // if the peer didn't get added as a sibling, try a bucket
-        else
-            addToBucket(peer);
-        // if adding the peer to the list of siblings replaced another sibling, add the old sibling to a bucket
-        if (ejectedPeer != null)
-            addToBucket(ejectedPeer);*/
-        
-/*TODO        synchronized(siblings) {
-            if (siblings.isFull()) {
-                KBucket bucket = getBucket(nodeHash);
-                KademliaPeer mostDistantSibling = getMostDistantSibling();
-                if (getDistance(node.getDestinationHash()) < getDistance(mostDistantSibling)) {
-                    bucket.addOrUpdate(mostDistantSibling);
-                    siblings.remove(mostDistantSibling);
-                    siblings.add(node);
-                }
-                else
-                    bucket.addOrUpdate(node);
-            }
-            else {
-                siblings.add(node);
-        }*/
-        
-    }
-
-    private void logBucketStats() {
-        int numBuckets = kBuckets.size();
-        int numPeers = getAllPeers().size();
-        
-        log.debug("#buckets=" + numBuckets + "+sibBkt, #peers=" + numPeers);
-    }
-    
-    /**
-     * Add a <code>{@link KademliaPeer}</code> to the appropriate bucket.
-     * @param peer
-     */
-    private void addToBucket(KademliaPeer peer) {
-        Hash nodeHash = peer.getDestinationHash();
-        KBucket bucket = getBucket(nodeHash);
-        KBucket newBucket = bucket.addOrSplit(peer);
-        if (newBucket != null)
-            kBuckets.add(newBucket);
-    }
-
-    /**
-     * Return <code>true</code> if a given peer is closer to the local node than at
-     * least one sibling. In other words, test if <code>peer</code> should replace
-     * an existing sibling.
-     * @param peer
-     * @return
-     */
-    private boolean isCloserSibling(KademliaPeer peer) {
-        BigInteger peerDistance = KademliaUtil.getDistance(peer, localDestinationHash);
-        for (KademliaPeer sibling: siblingBucket) {
-            BigInteger siblingDistance = KademliaUtil.getDistance(sibling.getDestinationHash(), localDestinationHash);
-            if (peerDistance.compareTo(siblingDistance) < 0)
-                return true;
-        }
-        return false;
-    }
-    
-    /**
-     * Add a peer to the sibling list if the list is not full, or if there is a node that can be
-     * replaced.
-     * 
-     * If <code>peer</code> replaced an existing sibling, that sibling is returned.
-     * If <code>peer</code> could not be added to the list, <code>peer</code> is returned.
-     * If the list was not full, <code>null</code> is returned.
-     * @param peer
-     * @return
-     */
-/*    private KademliaPeer addSibling(KademliaPeer peer) {
-        // no need to handle a replacement cache because the sibling bucket has none.
-        KademliaPeer mostDistantSibling = siblingBucket.getMostDistantPeer(localDestinationHash);
-        if (!siblingBucket.isFull()) {
-            siblingBucket.add(peer);
-            return null;
-        }
-        else if (new PeerDistanceComparator(localDestinationHash).compare(peer, mostDistantSibling) < 0) {
-            siblingBucket.remove(mostDistantSibling);
-            siblingBucket.add(peer);
-            return mostDistantSibling;
-        }
-        else
-            return peer;
-    }*/
-    
-    public void remove(KademliaPeer peer) {
-        Hash nodeHash = peer.getDestinationHash();
-        getBucket(nodeHash).remove(peer);
-    }
-    
-    /**
-     * Do a binary search for the index of the bucket whose key range contains a given {@link Hash}.
-     * @param key
-     * @return
-     */
-    private int getBucketIndex(Hash key) {
-        // initially, the search interval is 0..n-1
-        int lowEnd = 0;
-        int highEnd = kBuckets.size();
-        
-        BigInteger keyValue = new BigInteger(key.getData());
-        while (lowEnd < highEnd) {
-            int centerIndex = (highEnd + lowEnd) / 2;
-            if (keyValue.compareTo(kBuckets.get(centerIndex).getStartId()) < 0)
-                highEnd = centerIndex;
-            else if (keyValue.compareTo(kBuckets.get(centerIndex).getEndId()) > 0)
-                lowEnd = centerIndex;
-            else
-                return centerIndex;
-        }
-     
-        log.error("This should never happen! No k-bucket found for hash: " + key);
-        return -1;
-    }
-    
-    /**
-     * Do a binary search for the bucket whose key range contains a given {@link Hash}.
-     * @param key
-     * @return
-     */
-    private KBucket getBucket(Hash key) {
-        return kBuckets.get(getBucketIndex(key));
-    }
-    
-    /**
-     * Return the <code>count</code> peers that are closest to a given key.
-     * Less than <code>count</code> peers may be returned if there aren't
-     * enough peers in the k-buckets.
-     * @param key
-     * @param count
-     * @return
-     */
-    public Collection<KademliaPeer> getClosestPeers(Hash key, int count) {
-        Collection<KademliaPeer> closestPeers = new ConcurrentHashSet<KademliaPeer>();
-        
-        // TODO don't put all peers in one huge list, only use two buckets at a time
-        KademliaPeer[] allPeers = getAllPeersSortedByDistance(key);
-        
-        for (int i=0; i<count && i<allPeers.length; i++)
-            closestPeers.add(allPeers[i]);
-        
-        return closestPeers;
-    }
-
-    private KademliaPeer[] getAllPeersSortedByDistance(Hash key) {
-        List<KademliaPeer> allPeers = getAllPeers();
-        KademliaPeer[] peerArray = getAllPeers().toArray(new KademliaPeer[allPeers.size()]);
-        Arrays.sort(peerArray, new PeerDistanceComparator(key));
-        return peerArray;
-    }
-    
-    private List<KademliaPeer> getAllPeers() {
-        List<KademliaPeer> allPeers = new ArrayList<KademliaPeer>();
-        for (KBucket bucket: kBuckets)
-            allPeers.addAll(bucket.getNodes());
-        allPeers.addAll(siblingBucket.getNodes());
-        return allPeers;
-    }
-    
-    /**
-     * Return all siblings of the local node (siblings are an S/Kademlia feature).
-     * @return
-     */
-    public List<KademliaPeer> getSiblings() {
-        List<KademliaPeer> siblingDestinations = new ArrayList<KademliaPeer>();
-        for (KademliaPeer sibling: siblingBucket)
-            siblingDestinations.add(sibling);
-        return siblingDestinations;
-    }
-
-    /**
-     * Return the total number of known Kademlia peers.
-     * @return
-     */
-    public int getPeerCount() {
-        int count = 0;
-        for (KBucket bucket: kBuckets)
-            count += bucket.size();
-        count += siblingBucket.size();
-        return count;
-    }
-    
-    /**
-     * "refresh all k-buckets further away than the closest neighbor. This refresh is just a lookup of a random key that is within that k-bucket range."
-     */
-    public void refreshAll() {
-        for (KBucket bucket: kBuckets)
-            refresh(bucket);
-    }
-    
-    private void refresh(KBucket bucket) {
-        byte[] randomHash = new byte[Hash.HASH_LENGTH];
-        for (int i=0; i<Hash.HASH_LENGTH; i++)
-            randomHash[i] = (byte)RandomSource.getInstance().nextInt(256);
-        Hash key = new Hash(randomHash);
-        getClosestPeers(key, KademliaConstants.K);
-    }
-    
-/*    private void updatePeerList() {
-        for (Destination peer: peers) {
-            if (ping(peer))
-                requestPeerList(peer);
-        }
-    }*/
-    
-    @Override
-    public void run() {
-        while (!shutdownRequested()) {
-            try {
-                // TODO replicate();
-                // TODO updatePeerList(); refresh every bucket to which we haven't performed a node lookup in the past hour. Refreshing means picking a random ID in the bucket's range and performing a node search for that ID.
-                sleep(INTERVAL);
-            }
-            catch (InterruptedException e) {
-                log.debug("Thread '" + getName() + "' + interrupted", e);
-            }
-        }
-    }
-
-    // PacketListener implementation
-    @Override
-    public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
-        // any type of incoming packet updates the peer's record in the bucket/sibling list, or adds the peer to the bucket/sibling list
-        addOrUpdate(new KademliaPeer(sender, receiveTime));
-  }
-
-    private class PeerDistanceComparator implements Comparator<KademliaPeer> {
-        private Hash reference;
-        
-        PeerDistanceComparator(Hash reference) {
-            this.reference = reference;
-        }
-        
-        @Override
-        public int compare(KademliaPeer peer1, KademliaPeer peer2) {
-            BigInteger distance1 = KademliaUtil.getDistance(peer1.getDestinationHash(), reference);
-            BigInteger distance2 = KademliaUtil.getDistance(peer2.getDestinationHash(), reference);
-            return distance1.compareTo(distance2);
-        }
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/kademlia/ClosestNodesLookupTask.java b/apps/i2pbote/src/i2p/bote/network/kademlia/ClosestNodesLookupTask.java
deleted file mode 100644
index d98e78fdf221f97fc1c40e76406907c4a86f0f33..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/kademlia/ClosestNodesLookupTask.java
+++ /dev/null
@@ -1,198 +0,0 @@
-package i2p.bote.network.kademlia;
-
-import i2p.bote.network.I2PPacketDispatcher;
-import i2p.bote.network.I2PSendQueue;
-import i2p.bote.network.PacketListener;
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.packet.DataPacket;
-import i2p.bote.packet.PeerList;
-import i2p.bote.packet.ResponsePacket;
-import i2p.bote.packet.UniqueId;
-import i2p.bote.packet.dht.FindClosePeersPacket;
-
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-import java.util.Random;
-import java.util.Set;
-import java.util.TreeSet;
-
-import net.i2p.data.Destination;
-import net.i2p.data.Hash;
-import net.i2p.util.ConcurrentHashSet;
-import net.i2p.util.Log;
-
-public class ClosestNodesLookupTask implements Runnable {
-    private static final int REQUEST_TIMEOUT = 30 * 1000;
-    private static final int CLOSEST_NODES_LOOKUP_TIMEOUT = 2 * 60 * 1000;   // the maximum amount of time a FIND_CLOSEST_NODES can take
-    
-    private Log log = new Log(ClosestNodesLookupTask.class);
-    private Hash key;
-    private I2PPacketDispatcher i2pReceiver;
-    private BucketManager bucketManager;
-    private Random randomNumberGenerator;
-    private I2PSendQueue sendQueue;
-    private Set<Destination> responseReceived;
-    private Set<KademliaPeer> notQueriedYet;
-    private Set<FindClosePeersPacket> pendingRequests;
-    
-    public ClosestNodesLookupTask(Hash key, I2PSendQueue sendQueue, I2PPacketDispatcher i2pReceiver, BucketManager bucketManager) {
-        this.key = key;
-        this.sendQueue = sendQueue;
-        this.i2pReceiver = i2pReceiver;
-        this.bucketManager = bucketManager;
-        randomNumberGenerator = new Random(getTime());
-        responseReceived = new TreeSet<Destination>(new HashDistanceComparator(key));   // nodes that have responded to a query; sorted by distance to the key
-        notQueriedYet = new ConcurrentHashSet<KademliaPeer>();   // peers we haven't contacted yet
-        pendingRequests = new ConcurrentHashSet<FindClosePeersPacket>();   // outstanding queries
-    }
-    
-    @Override
-    public void run() {
-        log.debug("Looking up nodes closest to " + key);
-        
-        PacketListener packetListener = new IncomingPacketHandler();
-        i2pReceiver.addPacketListener(packetListener);
-        
-        // prepare a list of close nodes (might need more than alpha if they don't all respond)
-        notQueriedYet.addAll(bucketManager.getClosestPeers(key, KademliaConstants.S));
-        
-        long startTime = getTime();
-        while (true) {
-            // send new requests if less than alpha are pending
-            while (pendingRequests.size()<KademliaConstants.ALPHA && !notQueriedYet.isEmpty()) {
-                KademliaPeer peer = selectRandom(notQueriedYet);
-                notQueriedYet.remove(peer);
-                FindClosePeersPacket packet = new FindClosePeersPacket(key);
-                pendingRequests.add(packet);
-                try {
-                    CommunicationPacket response = sendQueue.sendAndWait(packet, peer.getDestination(), REQUEST_TIMEOUT);
-                    if (response == null)   // timeout occurred
-                        peer.incrementStaleCounter();
-                    else
-                        peer.resetStaleCounter();
-                }
-                catch (InterruptedException e) {
-                    log.warn("Interrupted while waiting on a lookup request.", e);
-                }
-            }
-            
-            if (responseReceived.size() >= KademliaConstants.S)
-                break;
-            if (hasTimedOut(startTime, CLOSEST_NODES_LOOKUP_TIMEOUT)) {
-                log.error("Lookup for closest nodes timed out.");
-                break;
-            }
-        }
-        log.debug(responseReceived.size() + " nodes found.");
-        
-        i2pReceiver.removePacketListener(packetListener);
-    }
-
-    private KademliaPeer selectRandom(Collection<KademliaPeer> collection) {
-        KademliaPeer[] array = new KademliaPeer[collection.size()];
-        int index = randomNumberGenerator.nextInt(array.length);
-        return collection.toArray(array)[index];
-    }
-    
-    private long getTime() {
-        return System.currentTimeMillis();
-    }
-    
-    private boolean hasTimedOut(long startTime, long timeout) {
-        return getTime() > startTime + timeout;
-    }
-    
-    /**
-     * Return up to <code>s</code> peers.
-     * @return
-     */
-    public List<Destination> getResults() {
-        List<Destination> resultsList = new ArrayList<Destination>();
-        for (Destination destination: responseReceived)
-            resultsList.add(destination);
-        Collections.sort(resultsList, new HashDistanceComparator(key));
-        
-        // trim the list to the k closest nodes
-        if (resultsList.size() > KademliaConstants.S)
-            resultsList = resultsList.subList(0, KademliaConstants.S);
-        return resultsList;
-    }
-
-    /**
-     * Return <code>true</code> if a set of peers contains a given peer.
-     * @param peerSet
-     * @param peerToFind
-     * @return
-     */
-    private boolean contains(Set<KademliaPeer> peerSet, KademliaPeer peerToFind) {
-        Hash peerHash = peerToFind.getDestinationHash();
-        for (KademliaPeer peer: peerSet)
-            if (peer.getDestinationHash().equals(peerHash))
-                return true;
-        return false;
-    }
-    
-    // compares two Destinations in terms of closeness to <code>reference</code>
-    private class HashDistanceComparator implements Comparator<Destination> {
-        private Hash reference;
-        
-        public HashDistanceComparator(Hash reference) {
-            this.reference = reference;
-        }
-        
-        public int compare(Destination dest1, Destination dest2) {
-            BigInteger dest1Distance = KademliaUtil.getDistance(dest1.calculateHash(), reference);
-            BigInteger dest2Distance = KademliaUtil.getDistance(dest2.calculateHash(), reference);
-            return dest1Distance.compareTo(dest2Distance);
-        }
-    };
-    
-    private class IncomingPacketHandler implements PacketListener {
-        @Override
-        public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
-            if (packet instanceof ResponsePacket) {
-                ResponsePacket responsePacket = (ResponsePacket)packet;
-                DataPacket payload = responsePacket.getPayload();
-                if (payload instanceof PeerList)
-                    addPeers(responsePacket, (PeerList)payload, sender, receiveTime);
-            }
-        }
-    
-        private void addPeers(ResponsePacket responsePacket, PeerList peerListPacket, Destination sender, long receiveTime) {
-            log.debug("Peer List Packet received: #peers=" + peerListPacket.getPeers().size() + ", sender="+ sender);
-            
-            // if the packet is in response to a pending request, update the three Sets
-            FindClosePeersPacket request = getPacketById(pendingRequests, responsePacket.getPacketId());   // find the request the node list is in response to
-            if (request != null) {
-                // TODO make responseReceived and pendingRequests a parameter in the constructor?
-                responseReceived.add(sender);
-                Collection<KademliaPeer> peersReceived = peerListPacket.getPeers();
-                for (KademliaPeer peer: peersReceived)
-                    if (contains(notQueriedYet, peer))
-                        notQueriedYet.add(peer);
-                pendingRequests.remove(request);
-                // TODO synchronize access to shortList and pendingRequests
-            }
-            else
-                log.debug("No Find Close Nodes packet found for Peer List: " + peerListPacket);
-        }
-
-        /**
-         * Returns a packet that matches a given {@link UniqueId} from a {@link Collection} of packets, or
-         * <code>null</code> if no match.
-         * @param packets
-         * @param packetId
-         * @return
-         */
-        private FindClosePeersPacket getPacketById(Collection<FindClosePeersPacket> packets, UniqueId packetId) {
-            for (FindClosePeersPacket packet: packets)
-                if (packetId.equals(packet.getPacketId()))
-                    return packet;
-            return null;
-        }
-    };
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/kademlia/KBucket.java b/apps/i2pbote/src/i2p/bote/network/kademlia/KBucket.java
deleted file mode 100644
index dafecbc00e24ddde00f50d21ca2efdc76cab204a..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/kademlia/KBucket.java
+++ /dev/null
@@ -1,260 +0,0 @@
-package i2p.bote.network.kademlia;
-
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ConcurrentSkipListSet;
-
-import net.i2p.data.Destination;
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-/**
- * Peers are sorted by the time of the most recent communication: Index 0 = least recent,
- * index n-1 = most recent.
- * 
- * @author HungryHobo@mail.i2p
- */
-class KBucket implements Iterable<KademliaPeer> {
-    static final BigInteger MIN_HASH_VALUE = BigInteger.ONE.negate().shiftLeft(Hash.HASH_LENGTH*8);   // system-wide minimum hash value
-    static final BigInteger MAX_HASH_VALUE = BigInteger.ONE.shiftLeft(Hash.HASH_LENGTH*8).subtract(BigInteger.ONE);   // system-wide maximum hash value
-    private static final int CAPACITY = KademliaConstants.K;   // The maximum number of peers the bucket can hold
-    
-    private Log log = new Log(KBucket.class);
-    private BigInteger startId;
-    private BigInteger endId;
-    private List<KademliaPeer> peers;   // the list is always kept sorted by "last seen" time
-    private Set<KademliaPeer> replacementCache;
-    private int depth;
-    private boolean replacementCacheEnabled;
-
-    KBucket(BigInteger startId, BigInteger endId, int depth, boolean replacementCacheEnabled) {
-        this.startId = startId;
-        this.endId = endId;
-        Comparator<KademliaPeer> peerComparator = createLastReceptionComparator();
-        peers = new ArrayList<KademliaPeer>();
-        replacementCache = new ConcurrentSkipListSet<KademliaPeer>(peerComparator);
-        this.depth = depth;
-        this.replacementCacheEnabled = replacementCacheEnabled;
-    }
-    
-    BigInteger getStartId() {
-        return startId;
-    }
-    
-    BigInteger getEndId() {
-        return endId;
-    }
-    
-    List<KademliaPeer> getNodes() {
-        // TODO only return peers that are not locked
-        return peers;
-    }
-    
-    void add(KademliaPeer node) {
-        if (isFull())
-            log.error("Error: adding a node to a full k-bucket. Bucket needs to be split first. Size=" + size() + ", capacity=" + CAPACITY);
-        
-        peers.add(node);
-    }
-    
-    /**
-     * Add a node to the bucket, splitting the bucket if necessary. If the bucket is split,
-     * the newly created bucket is returned. Otherwise, return <code>null</code>.
-     * 
-     * If the bucket is full but cannot be split, the new node is added to the replacement
-     * cache and <code>null</code> is returned.
-     * @param peer
-     * @return
-     */
-    synchronized KBucket addOrSplit(KademliaPeer peer) {
-        if (!rangeContains(peer))
-            log.error("Attempt to add a node whose hash is outside the bucket's range! Bucket start=" + startId + " Bucket end=" + endId + " peer hash=" + new BigInteger(peer.getDestinationHash().getData()));
-        
-        if (isFull() && !contains(peer)) {
-            if (canSplit(peer)) {
-                KBucket newBucket = split(peer.getDestinationHash());
-                if (rangeContains(peer))
-                    add(peer);
-                else if (newBucket.rangeContains(peer))
-                    newBucket.add(peer);
-                else
-                    log.error("After splitting a bucket, node is outside of both buckets' ranges.");
-                return newBucket;
-            }
-            else {
-                replacementCache.add(peer);
-                return null;
-            }
-        }
-        else {
-            addOrUpdate(peer);
-            return null;
-        }
-    }
-    
-    /**
-     * Return <code>true</code> if the bucket should be split in order to make room for a new peer.
-     * @return
-     */
-    private boolean canSplit(KademliaPeer peer) {
-        return depth%KademliaConstants.B!=0 || rangeContains(peer);
-    }
-    
-    /**
-     * Update a known peer, or add the peer if it isn't known.
-     * TODO If the bucket is full, the peer is added to the bucket's replacement cache.
-     * @param destination
-     * @return <code>true</code> if the peer was added (or replacement-cached),
-     * <code>false</code> if it was updated.
-     */
-    boolean addOrUpdate(KademliaPeer peer) {
-        // TODO log an error if peer outside bucket's range
-        // TODO handle stale peers
-        // TODO manage replacement cache
-    	KademliaPeer existingPeer = getPeer(peer.getDestination());
-        if (existingPeer == null) {
-            add(peer);
-            return true;
-        }
-        else {
-        	existingPeer.setLastReception(peer.getLastReception());
-        	// TODO move to end of list if lastReception is highest value, which it should be most of the time
-        	return false;
-        }
-    }
-    
-    /**
-     * Return <code>true</code> if a peer exists in the bucket.
-     * @param peer
-     * @return
-     */
-    boolean contains(KademliaPeer peer) {
-        return getPeer(peer.getDestination()) != null;
-    }
-
-    /**
-     * Return <code>true</code> if the bucket's Id range contains the hash of a given
-     * peer, regardless if the bucket contains the peer; <code>false</code> if the hash
-     * is outside the range.
-     * @param peer
-     * @return
-     */
-    private boolean rangeContains(KademliaPeer peer) {
-        BigInteger peerHash = new BigInteger(peer.getDestinationHash().getData());
-        return (startId.compareTo(peerHash)<=0 || endId.compareTo(peerHash)>0);
-    }
-    
-    /**
-     * Return a peer with a given I2P destination from the bucket, or <code>null</code> if the
-     * peer isn't in the bucket.
-     * @param destination
-     * @return
-     */
-    KademliaPeer getPeer(Destination destination) {
-        for (KademliaPeer peer: peers)
-            if (peer.getDestination().equals(destination))
-                return peer;
-        return null;
-    }
-    
-    KademliaPeer getClosestPeer(Hash key) {
-        KademliaPeer closestPeer = null;
-        BigInteger minDistance = MAX_HASH_VALUE;
-        for (KademliaPeer peer: peers) {
-            BigInteger distance = KademliaUtil.getDistance(key, peer.getDestination().calculateHash());
-            if (distance.compareTo(minDistance) < 0) {
-                closestPeer = peer;
-                minDistance = distance;
-            }
-        }
-        return closestPeer;
-    }
-
-/*    KademliaPeer getMostDistantPeer(KademliaPeer node) {
-        return getMostDistantPeer(node.getDestinationHash());
-    }*/
-    
-    KademliaPeer getMostDistantPeer(Hash key) {
-        KademliaPeer mostDistantPeer = null;
-        BigInteger maxDistance = BigInteger.ZERO;
-        for (KademliaPeer peer: peers) {
-            BigInteger distance = KademliaUtil.getDistance(key, peer.getDestination().calculateHash());
-            if (distance.compareTo(maxDistance) > 0) {
-                mostDistantPeer = peer;
-                maxDistance = distance;
-            }
-        }
-        return mostDistantPeer;
-    }
-    
-    @Override
-    public Iterator<KademliaPeer> iterator() {
-        return peers.iterator();
-    }
-    
-    void remove(Destination destination) {
-        peers.remove(getPeer(destination));
-    }
-
-    /**
-     * Remove a peer from the bucket. If the peer doesn't exist in the bucket, nothing happens.
-     * @param node
-     */
-    void remove(KademliaPeer node) {
-        peers.remove(node);
-    }
-
-    int size() {
-        return peers.size();
-    }
-    
-    boolean isFull() {
-        return size() >= CAPACITY;
-    }
-
-    /**
-     * Move half the nodes into a new bucket.
-     * @return The new bucket
-     */
-    KBucket split() {
-        depth++;
-        KBucket newBucket = new KBucket(startId, endId, depth, replacementCacheEnabled);
-        for (int i=0; i<peers.size()/2; i++) {
-            KademliaPeer peer = peers.get(i);
-            newBucket.add(peer);
-            remove(peer);
-        }
-        return newBucket;
-    }
-
-    KBucket split(Hash hash) {
-        return split(new BigInteger(hash.toBase64()));
-    }
-    
-    KBucket split(BigInteger pivot) {
-        depth++;
-        KBucket newBucket = new KBucket(startId, pivot.subtract(BigInteger.ONE), depth, replacementCacheEnabled);
-        startId = pivot;
-        for (KademliaPeer peer: peers) {
-            BigInteger nodeId = new BigInteger(peer.getDestination().calculateHash().getData());
-            if (nodeId.compareTo(pivot) >= 0) {
-                newBucket.add(peer);
-                remove(peer);
-            }
-        }
-        return newBucket;
-    }
-    
-    private Comparator<KademliaPeer> createLastReceptionComparator() {
-        return new Comparator<KademliaPeer>() {
-            @Override
-            public int compare(KademliaPeer peer1, KademliaPeer peer2) {
-                return Long.valueOf(peer1.getLastReception()).compareTo(peer2.getLastReception());
-            }
-        };
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaConstants.java b/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaConstants.java
deleted file mode 100644
index bb3b5926265a22c8563ce22766778dce78289f7a..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaConstants.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package i2p.bote.network.kademlia;
-
-public class KademliaConstants {
-    public static final int K = 2;   // Number of redundant storage nodes.
-    public static final int S = 3;   // The size of the sibling list for S/Kademlia.
-//    public static final int B = 5;   // This is the value from the original Kademlia paper.
-    public static final int B = 1;
-    public static final int ALPHA = 3;   // According to the literature, this is the optimum choice for alpha.
-    public static final int REFRESH_TIMEOUT = 3600;
-    public static final int REPLICATE_INTERVAL = 3600;   // TODO would it be better for REPLICATE_INTERVAL to be slightly longer than REFRESH_TIMEOUT?
-
-    private KademliaConstants() { }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaDHT.java b/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaDHT.java
deleted file mode 100644
index 83b8ab651d79dc72a60a4bb611673b8488a05e7b..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaDHT.java
+++ /dev/null
@@ -1,376 +0,0 @@
-package i2p.bote.network.kademlia;
-
-import i2p.bote.network.DHT;
-import i2p.bote.network.I2PPacketDispatcher;
-import i2p.bote.network.I2PSendQueue;
-import i2p.bote.network.PacketBatch;
-import i2p.bote.network.PacketListener;
-import i2p.bote.network.DhtStorageHandler;
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.packet.I2PBotePacket;
-import i2p.bote.packet.PeerList;
-import i2p.bote.packet.ResponsePacket;
-import i2p.bote.packet.StatusCode;
-import i2p.bote.packet.dht.DhtStorablePacket;
-import i2p.bote.packet.dht.FindClosePeersPacket;
-import i2p.bote.packet.dht.RetrieveRequest;
-import i2p.bote.packet.dht.StoreRequest;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.security.NoSuchAlgorithmException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.TimeUnit;
-
-import com.nettgryppa.security.HashCash;
-
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-import net.i2p.data.Hash;
-import net.i2p.util.ConcurrentHashSet;
-import net.i2p.util.Log;
-
-/**
- * The main class of the Kademlia implementation. All the high-level Kademlia logic
- * is in here.
- * 
- * Resources used:
- *   [1] http://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf
- *   [2] http://xlattice.sourceforge.net/components/protocol/kademlia/specs.html
- *   [3] http://en.wikipedia.org/wiki/Kademlia
- *   [4] http://www.barsoom.org/papers/infocom-2006-kad.pdf
- *   [5] http://doc.tm.uka.de/SKademlia_2007.pdf
- *   [6] OverSim (http://www.oversim.org/), which includes a S/Kademlia implementation
- *   
- * @author HungryHobo@mail.i2p
- */
-public class KademliaDHT implements DHT, PacketListener {
-    private Log log = new Log(KademliaDHT.class);
-    private Hash localDestinationHash;
-    private I2PSendQueue sendQueue;
-    private I2PPacketDispatcher i2pReceiver;
-    private File peerFile;
-    private Collection<KademliaPeer> initialPeers;
-    private BucketManager bucketManager;
-    private Map<Class<? extends DhtStorablePacket>, DhtStorageHandler> storageHandlers;
-
-    public KademliaDHT(Destination localDestination, I2PSendQueue sendQueue, I2PPacketDispatcher i2pReceiver, File peerFile) {
-        localDestinationHash = localDestination.calculateHash();
-        this.sendQueue = sendQueue;
-        this.i2pReceiver = i2pReceiver;
-        this.peerFile = peerFile;
-        initialPeers = readPeersFromFile(peerFile);
-        bucketManager = new BucketManager(sendQueue, initialPeers, localDestination.calculateHash());
-        storageHandlers = new ConcurrentHashMap<Class<? extends DhtStorablePacket>, DhtStorageHandler>();
-    }
-    
-    /**
-     * Returns the S nodes closest to a given key by querying peers.
-     * This method blocks. It returns after <code>CLOSEST_NODES_LOOKUP_TIMEOUT+1</code> seconds at
-     * the longest.
-     *
-     * The number of pending requests never exceeds ALPHA. According to [4], this is the most efficient.
-     * 
-     * If there are less than <code>s</code> results after the kademlia lookup finishes, nodes from
-     * the sibling list are used.
-     */
-    private Collection<Destination> getClosestNodes(Hash key) {
-        ClosestNodesLookupTask lookupTask = new ClosestNodesLookupTask(key, sendQueue, i2pReceiver, bucketManager);
-        lookupTask.run();
-        return lookupTask.getResults();
-    }
-
-    @Override
-    public DhtStorablePacket findOne(Hash key, Class<? extends DhtStorablePacket> dataType) {
-        Collection<DhtStorablePacket> results = find(key, dataType, false);
-        if (results.isEmpty())
-            return null;
-        else
-            return results.iterator().next();
-    }
-
-    @Override
-    public Collection<DhtStorablePacket> findAll(Hash key, Class<? extends DhtStorablePacket> dataType) {
-        return find(key, dataType, true);
-    }
-
-    @Override
-    public void setStorageHandler(Class<? extends DhtStorablePacket> packetType, DhtStorageHandler storageHandler) {
-        storageHandlers.put(packetType, storageHandler);
-    }
-    
-    @Override
-    public int getNumPeers() {
-        return bucketManager.getPeerCount();
-    }
-    
-    private Collection<DhtStorablePacket> find(Hash key, Class<? extends DhtStorablePacket> dataType, boolean exhaustive) {
-        final Collection<Destination> closeNodes = getClosestNodes(key);
-        log.debug("Querying " + closeNodes.size() + " nodes with Kademlia key " + key);
-        
-        final Collection<I2PBotePacket> receivedPackets = new ConcurrentHashSet<I2PBotePacket>();   // avoid adding duplicate packets
-        
-        PacketListener packetListener = new PacketListener() {
-            @Override
-            public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
-                // add packet to list of received packets if the packet is in response to a RetrieveRequest
-                if (packet instanceof RetrieveRequest && closeNodes.contains(sender))
-                    receivedPackets.add(packet);
-            }
-        };
-        i2pReceiver.addPacketListener(packetListener);
-        
-        // Send the retrieve requests
-        PacketBatch batch = new PacketBatch();
-        for (Destination node: closeNodes)
-            batch.putPacket(new RetrieveRequest(key, dataType), node);
-        sendQueue.send(batch);
-        try {
-            batch.awaitSendCompletion();
-        }
-        catch (InterruptedException e) {
-            log.warn("Interrupted while waiting for Retrieve Requests to be sent.", e);
-        }
-
-        // wait for replies
-        try {
-            if (exhaustive)
-                TimeUnit.SECONDS.sleep(60);
-            else
-                batch.awaitFirstReply(30, TimeUnit.SECONDS);
-        }
-        catch (InterruptedException e) {
-            log.warn("Interrupted while waiting for responses to Retrieve Requests.", e);
-        }
-        log.debug(batch.getResponsePackets().size() + " response packets received for hash " + key + " and data type " + dataType);
-        
-        sendQueue.remove(batch);
-        i2pReceiver.removePacketListener(packetListener);
-        
-        ConcurrentHashSet<DhtStorablePacket> storablePackets = getStorablePackets(batch);
-        DhtStorablePacket localResult = findLocally(key, dataType);
-        if (localResult != null)
-            storablePackets.add(localResult);
-        return storablePackets;
-    }
-
-    private DhtStorablePacket findLocally(Hash key, Class<? extends DhtStorablePacket> dataType) {
-        DhtStorageHandler storageHandler = storageHandlers.get(dataType);
-        if (storageHandler != null)
-            return storageHandler.retrieve(key);
-        else
-            return null;
-    }
-    
-    /**
-     * Returns all <code>DhtStorablePacket</code> packets that have been received as a response to a send batch.
-     * @param batch
-     * @return
-     */
-    private ConcurrentHashSet<DhtStorablePacket> getStorablePackets(PacketBatch batch) {
-        ConcurrentHashSet<DhtStorablePacket> storablePackets = new ConcurrentHashSet<DhtStorablePacket>();
-        for (I2PBotePacket packet: batch.getResponsePackets())
-            if (packet instanceof DhtStorablePacket)
-                storablePackets.add((DhtStorablePacket)packet);
-        return storablePackets;
-    }
-    
-    @Override
-    public void store(DhtStorablePacket packet) throws NoSuchAlgorithmException {
-        Hash key = packet.getDhtKey();
-        
-        Collection<Destination> closeNodes = getClosestNodes(key);
-        log.debug("Storing a " + packet.getClass().getSimpleName() + " with key " + key + " on " + closeNodes.size() + " nodes");
-        
-        HashCash hashCash = HashCash.mintCash("", 1);   // TODO
-        StoreRequest storageRequest = new StoreRequest(hashCash, packet);
-        PacketBatch batch = new PacketBatch();
-        for (Destination node: closeNodes)
-            batch.putPacket(storageRequest, node);
-        sendQueue.send(batch);
-        
-        try {
-            batch.awaitSendCompletion();
-        }
-        catch (InterruptedException e) {
-            log.warn("Interrupted while waiting for responses to Storage Requests to be sent.", e);
-        }
-        
-        sendQueue.remove(batch);
-    }
-
-    @Override
-    public void start() {
-        i2pReceiver.addPacketListener(this);
-        bucketManager.start();
-        bootstrap();
-    }
-    
-    @Override
-    public void shutDown() {
-        i2pReceiver.removePacketListener(this);
-        bucketManager.requestShutdown();
-        writePeersToFile(peerFile);
-    }
-    
-    private void bootstrap() {
-        new BootstrapTask().start();
-    }
-    
-    private class BootstrapTask extends Thread {
-        public BootstrapTask() {
-            setDaemon(true);
-        }
-        
-        @Override
-        public void run() {
-            log.debug("Bootstrap start");
-            while (true) {
-                for (KademliaPeer bootstrapNode: initialPeers) {
-                    bootstrapNode.setLastReception(-1);
-                    bucketManager.addOrUpdate(bootstrapNode);
-                    Collection<Destination> closestNodes = getClosestNodes(localDestinationHash);
-                    // if last reception time is not set, the node didn't respond, so remove it
-                    if (bootstrapNode.getLastReception() <= 0)
-                        bucketManager.remove(bootstrapNode);
-                    
-                    if (closestNodes.isEmpty()) {
-                        log.debug("No response from bootstrap node " + bootstrapNode);
-                        bucketManager.remove(bootstrapNode);
-                    }
-                    else {
-                        bucketManager.refreshAll();
-                        log.info("Bootstrapping finished. Number of peers = " + bucketManager.getPeerCount());
-                        return;
-                    }
-                }
-                
-                log.warn("Can't bootstrap off any known peer, will retry shortly.");
-                try {
-                    TimeUnit.MINUTES.sleep(1);
-                } catch (InterruptedException e) {
-                    log.error("Interrupted while pausing after unsuccessful bootstrap attempt.", e);
-                }
-            }
-        }
-    }
-    
-    private void writePeersToFile(File peerFile) {
-        // TODO
-    }
-    
-    private Collection<KademliaPeer> readPeersFromFile(File peerFile) {
-        log.info("Reading peers from file: '" + peerFile.getAbsolutePath() + "'");
-        Collection<KademliaPeer> peers = new ArrayList<KademliaPeer>();
-
-        FileInputStream fileInput = null;
-        try {
-            fileInput = new FileInputStream(peerFile);
-        }
-        catch (FileNotFoundException notFoundExc) {
-            log.error("Peer file not found, creating a new one: '" + peerFile.getAbsolutePath() + "'");
-            log.error("Please provide a peer file with at least one active peer and restart the application.");
-            try {
-                createPeerFile(peerFile);
-            }
-            catch (IOException ioExc) {
-                log.error("Can't create peer file.", ioExc);
-                return peers;
-            }
-        }
-        
-        BufferedReader inputBuffer = new BufferedReader(new InputStreamReader(fileInput));
-        
-        while (true) {
-            String line = null;
-            try {
-                line = inputBuffer.readLine();
-            }
-            catch (IOException e) {
-                log.error("Error reading peer file.", e);
-            }
-            if (line == null)
-                break;
-            
-            if (!line.startsWith("#"))
-                // TODO read "up since" time if present, use a separate method for parsing lines
-                // TODO write "up since" time back to the peer file
-                try {
-                	Destination destination = new Destination(line);
-                	KademliaPeer peer = new KademliaPeer(destination, 0);
-                	
-                    // don't add the local destination as a peer
-                    if (!peer.getDestinationHash().equals(localDestinationHash))
-                        peers.add(peer);
-                }
-                catch (DataFormatException e) {
-                    log.error("Invalid destination key in '" + peerFile + "': " + line, e);
-                }
-        }
-        
-        return peers;
-    }
-    
-    private void createPeerFile(File file) throws IOException {
-        String lineFeed = System.getProperty("line.separator");
-        
-        file.createNewFile();
-        FileWriter fileWriter = new FileWriter(file);
-        fileWriter.write("# Each line in this file should begin with a 516-byte I2P destination key in" + lineFeed);
-        fileWriter.write("# Base64 format. Optionally, the destination key can be followed by a space" + lineFeed);
-        fileWriter.write("# and an \"active since\" time." + lineFeed);
-        fileWriter.write("# Lines beginning with a # are ignored." + lineFeed);
-        fileWriter.close();
-    }
-
-    private void sendPeerList(FindClosePeersPacket packet, Destination destination) {
-        Collection<KademliaPeer> closestPeers = bucketManager.getClosestPeers(packet.getKey(), KademliaConstants.K);
-        PeerList peerList = new PeerList(closestPeers);
-        sendQueue.sendResponse(peerList, destination, packet.getPacketId());
-    }
-
-    // PacketListener implementation
-    @Override
-    public void packetReceived(CommunicationPacket packet, Destination sender, long receiveTime) {
-        if (packet instanceof FindClosePeersPacket)
-            sendPeerList((FindClosePeersPacket)packet, sender);
-        else if (packet instanceof StoreRequest) {
-            DhtStorablePacket packetToStore = ((StoreRequest)packet).getPacketToStore();
-            if (packetToStore != null) {
-                DhtStorageHandler storageHandler = storageHandlers.get(packetToStore.getClass());
-                if (storageHandler != null)
-                    storageHandler.store(packetToStore);
-                else
-                    log.warn("No storage handler found for type " + packetToStore.getClass().getSimpleName() + ".");
-            }
-        }
-        else if (packet instanceof RetrieveRequest) {
-            RetrieveRequest retrieveRequest = (RetrieveRequest)packet;
-            DhtStorageHandler storageHandler = storageHandlers.get(retrieveRequest.getDataType());
-            if (storageHandler != null) {
-                DhtStorablePacket storedPacket = storageHandler.retrieve(retrieveRequest.getKey());
-                // if requested packet found, send it to the requester
-                if (storedPacket != null) {
-                    log.debug("Packet found for retrieve request: [" + retrieveRequest + "], replying to sender: [" + sender + "]");
-                    ResponsePacket response = new ResponsePacket(storedPacket, StatusCode.OK, retrieveRequest.getPacketId());
-                    sendQueue.send(response, sender);
-                }
-                else
-                    log.debug("No matching packet found for retrieve request: [" + retrieveRequest + "]");
-            }
-            else
-                log.warn("No storage handler found for type " + packet.getClass().getSimpleName() + ".");
-        }
-        
-        // bucketManager is not registered as a PacketListener, so notify it here
-        bucketManager.packetReceived(packet, sender, receiveTime);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaException.java b/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaException.java
deleted file mode 100644
index 9c60dc54d9edc40f3dbf2f9e68e1b8adc0eb8f00..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package i2p.bote.network.kademlia;
-
-public class KademliaException extends Exception {
-    private static final long serialVersionUID = -1286128818859245017L;
-
-    public KademliaException(String message) {
-        super(message);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaPeer.java b/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaPeer.java
deleted file mode 100644
index 202c7c511999b483e3081d583be0318e245dd089..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaPeer.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package i2p.bote.network.kademlia;
-
-import java.util.concurrent.atomic.AtomicInteger;
-
-import net.i2p.data.Destination;
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-public class KademliaPeer {
-    private static final int STALE_THRESHOLD = 5;
-    
-    private Log log = new Log(KademliaPeer.class);
-    private Destination destination;
-    private Hash destinationHash;
-    private long lastPingSent;
-    private long lastReception;
-    private long activeSince;
-    private AtomicInteger consecutiveTimeouts;
-    
-    public KademliaPeer(Destination destination, long lastReception) {
-        this.destination = destination;
-        destinationHash = destination.calculateHash();
-        if (destinationHash == null)
-            log.error("calculateHash() returned null!");
-        
-        this.lastReception = lastReception;
-        lastPingSent = 0;
-        activeSince = lastReception;
-        consecutiveTimeouts = new AtomicInteger(0);
-    }
-    
-    public Destination getDestination() {
-    	return destination;
-    }
-    
-    public Hash getDestinationHash() {
-    	return destinationHash;
-    }
-    
-    public boolean isStale() {
-        return consecutiveTimeouts.get() >= STALE_THRESHOLD;
-    }
-    
-    public void incrementStaleCounter() {
-        consecutiveTimeouts.incrementAndGet();
-    }
-    
-    public void resetStaleCounter() {
-        consecutiveTimeouts.set(0);
-    }
-    
-    public long getLastPingSent() {
-    	return lastPingSent;
-    }
-    
-    public void setLastReception(long time) {
-    	lastReception = time;
-    }
-    
-    public long getLastReception() {
-    	return lastReception;
-    }
-    
-    public long getActiveSince() {
-    	return activeSince;
-    }
-    
-/*    public BigInteger getDistance(KademliaPeer anotherPeer) {
-        return KademliaUtil.getDistance(getDestinationHash(), anotherPeer.getDestinationHash());
-    }*/
- 
-    @Override
-    public String toString() {
-        return destination.toString();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaUtil.java b/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaUtil.java
deleted file mode 100644
index 59bb5f02f81b87b29a5615ff6941551b2cd2bd2a..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/network/kademlia/KademliaUtil.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package i2p.bote.network.kademlia;
-
-import java.math.BigInteger;
-
-import net.i2p.data.DataHelper;
-import net.i2p.data.Hash;
-
-public class KademliaUtil {
-
-    public static BigInteger getDistance(KademliaPeer node, Hash key) {
-        return getDistance(node.getDestinationHash(), key);
-    }
-
-    public static BigInteger getDistance(Hash key1, Hash key2) {
-        // This shouldn't be a performance bottleneck, so save some mem by not using Hash.cachedXor
-        byte[] xoredData = DataHelper.xor(key1.getData(), key2.getData());
-        return new BigInteger(xoredData);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/CommunicationPacket.java b/apps/i2pbote/src/i2p/bote/packet/CommunicationPacket.java
deleted file mode 100644
index 4eddeb0d2435277b11ea4504ccc135d27cb2b61c..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/CommunicationPacket.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package i2p.bote.packet;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import net.i2p.util.Log;
-
-public abstract class CommunicationPacket extends I2PBotePacket {
-    private static final byte PACKET_VERSION = 1;
-    protected static final int HEADER_LENGTH = 6 + UniqueId.LENGTH;   // length of the common packet header in the byte array representation; this is where subclasses start reading
-    private static final byte[] PACKET_PREFIX = new byte[] {(byte)0x6D, (byte)0x30, (byte)0x52, (byte)0xE9};
-    private static Log static_log = new Log(CommunicationPacket.class);
-    
-    private Log log = new Log(CommunicationPacket.class);
-    private UniqueId packetId;
-    private CountDownLatch sentSignal;
-    private long sentTime;
-
-    protected CommunicationPacket() {
-        this(new UniqueId());
-    }
-    
-    protected CommunicationPacket(UniqueId packetId) {
-        this.packetId = packetId;
-        sentSignal = new CountDownLatch(1);
-        sentTime = -1;
-    }
-    
-    /**
-     * Creates a packet and initializes the header fields shared by all Communication Packets: packet type and packet id.
-     * @param data
-     */
-    protected CommunicationPacket(byte[] data) {
-        verifyHeader(data);
-        checkPacketType(data[4]);
-        packetId = new UniqueId(data, 6);
-    }
-    
-    /**
-     * Creates a packet object from its byte array representation. If there is an error,
-     * <code>null</code> is returned.
-     * @param data
-     * @param log
-     * @return
-     */
-    public static CommunicationPacket createPacket(byte[] data) {
-        char packetTypeCode = (char)data[4];   // byte 4 of a communication packet is the packet type code
-        Class<? extends I2PBotePacket> packetType = decodePacketTypeCode(packetTypeCode);
-        if (packetType==null || !CommunicationPacket.class.isAssignableFrom(packetType)) {
-            static_log.error("Type code is not a CommunicationPacket type code: <" + packetTypeCode + ">");
-            return null;
-        }
-        
-        Class<? extends CommunicationPacket> commPacketType = packetType.asSubclass(CommunicationPacket.class);
-        try {
-            return commPacketType.getConstructor(byte[].class).newInstance(data);
-        }
-        catch (Exception e) {
-            static_log.warn("Can't instantiate packet for type code <" + packetTypeCode + ">", e);
-            return null;
-        }
-    }
-    
-    // check the packet prefix and version number of a packet
-    private void verifyHeader(byte[] packet) {
-        for (int i=0; i<PACKET_PREFIX.length; i++)
-            if (packet[i] != PACKET_PREFIX[i])
-                log.error("Packet prefix invalid at byte " + i + ". Expected = " + PACKET_PREFIX[i] + ", actual = " + packet[i]);
-        if (packet[5] != 1)
-            log.error("Unsupported packet version: " + packet[5]);
-    }
-
-    public void setPacketId(UniqueId packetId) {
-        this.packetId = packetId;
-    }
-    
-    public UniqueId getPacketId() {
-        return packetId;
-    }
-    
-    public synchronized void setSentTime(long sentTime) {
-        this.sentTime = sentTime;
-        sentSignal.countDown();
-    }
-
-    public synchronized long getSentTime() {
-        return sentTime;
-    }
-
-    public boolean hasBeenSent() {
-        return sentTime > 0;
-    }
-    
-    public boolean awaitSending(long timeout, TimeUnit unit) throws InterruptedException {
-        return sentSignal.await(timeout, unit);
-    }
-    
-    /**
-     * Writes the Prefix, Version, Type, and Packet Id fields of a I2PBote packet to
-     * an {@link OutputStream}.
-     * @param outputStream
-     */
-    protected void writeHeader(OutputStream outputStream) throws IOException {
-        outputStream.write(PACKET_PREFIX);
-        outputStream.write((byte)getPacketTypeCode());
-        outputStream.write(PACKET_VERSION);
-        outputStream.write(packetId.toByteArray());
-    }
-    
-    @Override
-    public String toString() {
-        return "Type=" + getClass().getSimpleName() + " Id=" + (packetId==null?"<null>":packetId.toString().substring(0, 8)) + "...";
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/DataPacket.java b/apps/i2pbote/src/i2p/bote/packet/DataPacket.java
deleted file mode 100644
index 3764f728bc0e8e3a091d02c961edd7cdec94ccaa..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/DataPacket.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package i2p.bote.packet;
-
-import i2p.bote.Util;
-import i2p.bote.folder.FolderElement;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import net.i2p.util.Log;
-
-/**
- * The superclass of all "payload" packet types.
- *
- * @author HungryHobo@mail.i2p
- */
-public abstract class DataPacket extends I2PBotePacket implements FolderElement {
-    private static Log log = new Log(DataPacket.class);
-
-    private File file;
-    
-    public DataPacket() {
-    }
-    
-    @Override
-    public void writeTo(OutputStream outputStream) throws Exception {
-        outputStream.write(toByteArray());
-    }
-    
-    public static DataPacket createPacket(File file) {
-        InputStream inputStream = null;
-        try {
-            inputStream = new FileInputStream(file);
-            DataPacket packet = createPacket(Util.readInputStream(inputStream));
-            return packet;
-        }
-        catch (IOException e) {
-            log.error("Can't read packet file: " + file.getAbsolutePath(), e);
-            return null;
-        }
-        finally {
-            try {
-                inputStream.close();
-            }
-            catch (IOException e) {
-                log.error("Can't close stream.", e);
-            }
-        }
-    }
-    
-    /**
-     * Creates a {@link DataPacket} object from its byte array representation.
-     * If there is an error, <code>null</code> is returned.
-     * @param data
-     * @return
-     */
-    public static DataPacket createPacket(byte[] data) {
-        char packetTypeCode = (char)data[0];   // first byte of a data packet is the packet type code
-        Class<? extends I2PBotePacket> packetType = decodePacketTypeCode(packetTypeCode);
-        if (packetType==null || !DataPacket.class.isAssignableFrom(packetType)) {
-            log.error("Type code is not a DataPacket type code: <" + packetTypeCode + ">");
-            return null;
-        }
-        
-        Class<? extends DataPacket> dataPacketType = packetType.asSubclass(DataPacket.class);
-        try {
-            return dataPacketType.getConstructor(byte[].class).newInstance(data);
-        }
-        catch (Exception e) {
-            log.warn("Can't instantiate packet for type code <" + packetTypeCode + ">", e);
-            return null;
-        }
-    }
-
-    // FolderElement implementation
-    @Override
-    public File getFile() {
-        return file;
-    }
-
-    @Override
-    public void setFile(File file) {
-        this.file = file;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/Email.java b/apps/i2pbote/src/i2p/bote/packet/Email.java
deleted file mode 100644
index 905ff891081dc84004dc9fb56a436190cb1a3f72..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/Email.java
+++ /dev/null
@@ -1,166 +0,0 @@
-package i2p.bote.packet;
-
-import i2p.bote.EmailDestination;
-import i2p.bote.folder.FolderElement;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Enumeration;
-import java.util.Properties;
-
-import javax.mail.MessagingException;
-import javax.mail.Session;
-import javax.mail.internet.MimeMessage;
-
-import net.i2p.util.Log;
-
-import com.nettgryppa.security.HashCash;
-
-public class Email extends MimeMessage implements FolderElement {
-    private static final int MAX_BYTES_PER_PACKET = 30 * 1024;
-    private static final String[] HEADER_WHITELIST = new String[] {
-        "From", "Sender", "To", "CC", "BCC", "Reply-To", "Subject", "MIME-Version", "Content-Type", "Content-Transfer-Encoding",
-        "Message-Id", "In-Reply-To"
-    };
-    
-    private Log log = new Log(Email.class);
-    private String filename;
-    private UniqueId messageId;
-
-    public Email() {
-        super(Session.getDefaultInstance(new Properties()));
-        messageId = new UniqueId();
-    }
-
-    /**
-     * Creates an Email object from an InputStream containing a MIME email.
-     * 
-     * @param inputStream
-     * @throws MessagingException
-     * @see MimeMessage(Session, InputStream)
-
-     */
-    public Email(InputStream inputStream) throws MessagingException {
-        super(Session.getDefaultInstance(new Properties()), inputStream);
-        messageId = new UniqueId();
-    }
-
-    /**
-     * Creates an Email object from a byte array containing a MIME email.
-     * 
-     * @param bytes
-     * @throws MessagingException
-     */
-     public Email(byte[] bytes) throws MessagingException {
-         super(Session.getDefaultInstance(new Properties()), new ByteArrayInputStream(bytes));
-         messageId = new UniqueId();
-     }
-
-    // TODO
-    public void setHashCash(HashCash hashCash) {
-        // add hashCash to header
-    }
-
-    /**
-     * Called by <code>saveChanges()</code>, see JavaMail JavaDoc.
-     */
-    @Override
-    protected void updateHeaders() throws MessagingException {
-        setHeader("Message-Id", getMessageID());
-    }
-    
-    /**
-     * Returns a message ID that conforms to RFC822, but doesn't reveal the sender's
-     * domain or user name.
-     */
-    @Override
-    public String getMessageID() {
-        return messageId.toBase64() + "@i2p";
-    }
-
-    /**
-     * Converts the email into one or more email packets.
-     * 
-     * @param destination
-     * @return
-     * @throws IOException
-     * @throws MessagingException
-     */
-    public Collection<EmailPacket> createEmailPackets(EmailDestination destination) throws IOException, MessagingException {
-        ArrayList<EmailPacket> packets = new ArrayList<EmailPacket>();
-        
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        writeTo(outputStream);
-        byte[] emailArray = outputStream.toByteArray();
-        
-        int fragmentIndex = 0;
-        int blockStart = 0;   // the array index where the next block of data starts
-        while (true) {
-            int blockSize = Math.min(emailArray.length-blockStart, MAX_BYTES_PER_PACKET);
-            if (blockSize <= 0)
-                break;
-            else {
-                // make a new array with the right length
-                byte[] block = new byte[blockSize];
-                System.arraycopy(emailArray, blockStart, block, 0, blockSize);
-                UniqueId deletionKeyPlain = new UniqueId();
-                UniqueId deletionKeyEncrypted = new UniqueId(deletionKeyPlain);   // encryption happens later
-                EmailPacket packet = new EmailPacket(block, deletionKeyPlain, deletionKeyEncrypted, messageId, fragmentIndex, 0, destination);   // we'll set the # of fragments in a minute
-                packets.add(packet);
-                fragmentIndex++;
-                blockStart += blockSize;
-            }
-        }
-        
-        // set fragment count
-        int numFragments = fragmentIndex;
-        for (EmailPacket packet: packets)
-            packet.setNumFragments(numFragments);
-        
-        return packets;
-    }
-
-/*    private byte[] toByteArray() throws MessagingException {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        try {
-            writeTo(outputStream);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        return outputStream.toByteArray();
-    }*/
-
-    /**
-     * Removes all mail headers except the ones in <code>HEADER_WHITELIST</code>.
-     * @throws MessagingException 
-     */
-    public void scrubHeaders() throws MessagingException {
-        // TODO does the superclass remove BCC addresses (except the recipient's)? if not, do it in here.
-        @SuppressWarnings("unchecked")
-        Enumeration<String> headersToRemove = getNonMatchingHeaders(HEADER_WHITELIST);
-        while (headersToRemove.hasMoreElements()) {
-            String headerName = headersToRemove.nextElement();
-            removeHeader(headerName);
-        }
-    }
-
-    // FolderElement implementation
-    @Override
-    public File getFile() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    // FolderElement implementation
-    @Override
-    public void setFile(File file) {
-        // TODO Auto-generated method stub
-        
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/EmailPacket.java b/apps/i2pbote/src/i2p/bote/packet/EmailPacket.java
deleted file mode 100644
index cff46ccfa7dc5a5562918f30d5b752ec672e1d9c..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/EmailPacket.java
+++ /dev/null
@@ -1,246 +0,0 @@
-package i2p.bote.packet;
-
-import i2p.bote.EmailDestination;
-import i2p.bote.EmailIdentity;
-import i2p.bote.packet.dht.DhtStorablePacket;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import net.i2p.I2PAppContext;
-import net.i2p.crypto.ElGamalAESEngine;
-import net.i2p.crypto.SessionKeyManager;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Hash;
-import net.i2p.data.PrivateKey;
-import net.i2p.data.PublicKey;
-import net.i2p.data.SessionKey;
-import net.i2p.util.Log;
-import net.i2p.util.RandomSource;
-
-@TypeCode('E')
-public class EmailPacket extends DhtStorablePacket {
-    private static final int PADDED_SIZE = PrivateKey.KEYSIZE_BYTES;   // TODO is this a good choice?
-    
-    private ElGamalAESEngine cryptoEngine = I2PAppContext.getGlobalContext().elGamalAESEngine();
-    private SessionKeyManager sessionKeyManager = I2PAppContext.getGlobalContext().sessionKeyManager();
-    private Log log = new Log(EmailPacket.class);
-    private Hash dhtKey;
-    private UniqueId deletionKeyPlain;
-    // Begin encrypted fields
-    private UniqueId deletionKeyEncrypted;
-    private UniqueId messageId;
-	private int fragmentIndex;
-	private int numFragments;
-    private byte[] content;
-    private byte[] encryptedData;   // all encrypted fields: Encrypted Deletion Key, Message ID, Fragment Index, Number of Fragments, and Content.
-
-	/**
-	 * Creates an <code>EmailPacket</code> from raw datagram data.
-	 * To read the encrypted parts of the packet, <code>decrypt</code> must be called first.
-     * @param data
-	 */
-	public EmailPacket(byte[] data) {
-        ByteBuffer buffer = ByteBuffer.wrap(data);
-        if (buffer.get() != getPacketTypeCode())
-            log.error("Wrong type code for EmailPacket. Expected <" + getPacketTypeCode() + ">, got <" + (char)data[0] + ">");
-        
-        dhtKey = readHash(buffer);
-        deletionKeyPlain = new UniqueId(buffer);
-        
-        int encryptedLength = buffer.getShort();   // length of the encrypted part of the packet
-        encryptedData = new byte[encryptedLength];
-        buffer.get(encryptedData);
-	}
-	
-	/**
-	 * Creates a <code>EmailPacket</code> from a <code>byte</code> array that contains MIME data.
-	 * The public key of <code>emailDestination</code> is used for encryption.
-	 * @param content
-     * @param deletionKeyPlain
-     * @param deletionKeyEncrypted
-	 * @param messageId
-	 * @param fragmentIndex
-	 * @param numFragments
-	 * @param emailDestination
-	 */
-	public EmailPacket(byte[] content, UniqueId deletionKeyPlain, UniqueId deletionKeyEncrypted, UniqueId messageId, int fragmentIndex, int numFragments, EmailDestination emailDestination) {
-        this.deletionKeyPlain = deletionKeyPlain;
-        this.deletionKeyEncrypted = deletionKeyEncrypted;
-        this.messageId = messageId;
-        this.fragmentIndex = fragmentIndex;
-        this.numFragments = numFragments;
-        this.content = content;
-        
-        dhtKey = generateRandomHash();
-        
-        // make an array with all data that gets encrypted
-        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
-        DataOutputStream dataStream = new DataOutputStream(byteStream);
-        try {
-            deletionKeyEncrypted.writeTo(dataStream);
-            messageId.writeTo(dataStream);
-            dataStream.writeShort(fragmentIndex);
-            dataStream.writeShort(numFragments);
-            dataStream.writeShort(content.length);
-            dataStream.write(content);
-            
-            encryptedData = encrypt(byteStream.toByteArray(), emailDestination);
-            verify();
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream/DataOutputStream.", e);
-        }
-	}
-	
-	private Hash generateRandomHash() {
-	    RandomSource randomSource = RandomSource.getInstance();
-	    
-	    byte[] bytes = new byte[Hash.HASH_LENGTH];
-	    for (int i=0; i<bytes.length; i++)
-	        bytes[i] = (byte)randomSource.nextInt(256);
-	    
-	    return new Hash(bytes);
-	}
-    
-	/**
-	 * Decrypts the encrypted part of the packet with the private key of <code>identity</code>.
-	 * @param identity
-	 * @throws DataFormatException 
-	 */
-	public void decrypt(EmailIdentity identity) throws DataFormatException {
-	    byte[] decryptedData = decrypt(encryptedData, identity);
-	    ByteBuffer buffer = ByteBuffer.wrap(decryptedData);
-	    
-        deletionKeyEncrypted = new UniqueId(buffer);
-        messageId = new UniqueId(buffer);
-        fragmentIndex = buffer.getShort();
-        numFragments = buffer.getShort();
-        
-        int contentLength = buffer.getShort();
-        content = new byte[contentLength];
-        buffer.get(content);
-        
-        verify();
-	}
-	
-    /**
-     * Decrypts data with an email identity's private key.
-     * @param data
-     * @param identity
-     * @return The decrypted data
-     */
-	private byte[] decrypt(byte[] data, EmailIdentity identity) throws DataFormatException {
-        PrivateKey privateKey = identity.getPrivateEncryptionKey();
-        return cryptoEngine.decrypt(data, privateKey);
-	}
-	
-    /**
-     * Encrypts data with an email destination's public key.
-     * @param data
-     * @param emailDestination
-     * @return The encrypted data
-     */
-    public byte[] encrypt(byte[] data, EmailDestination emailDestination) {
-        PublicKey publicKey = emailDestination.getPublicEncryptionKey();
-        SessionKey sessionKey = sessionKeyManager.createSession(publicKey);
-        return cryptoEngine.encrypt(data, publicKey, sessionKey, PADDED_SIZE);
-    }
-    
-    @Override
-    public Hash getDhtKey() {
-        return dhtKey;
-    }
-	
-    /**
-     * Returns the value of the "plaintext deletion key" field.
-     * Storage nodes set this to all zero bytes when the packet is retrieved.
-     * @return
-     */
-    public UniqueId getPlaintextDeletionKey() {
-        return deletionKeyPlain;
-    }
-    
-    /**
-     * Returns the value of the "encrypted deletion key" field.
-     * Storage nodes set this to all zero bytes when the packet is retrieved.
-     * @return
-     */
-    public UniqueId getEncryptedDeletionKey() {
-        return deletionKeyEncrypted;
-    }
-    
-	/**
-     * Returns the index of the fragment in the {@link Email} this packet belongs to.
-	 */
-	public int getFragmentIndex() {
-	    return fragmentIndex;
-	}
-	
-	/**
-	 * Returns the number of fragments in the {@link Email} this packet belongs to.
-	 * @return
-	 */
-	public int getNumFragments() {
-	    return numFragments;
-	}
-	
-	public void setContent(byte[] content) {
-		this.content = content;
-	}
-	
-    public byte[] getContent() {
-        return content;
-    }
-    
-	@Override
-	public byte[] toByteArray() {
-	    ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
-	    DataOutputStream dataStream = new DataOutputStream(byteArrayStream);
-
-        try {
-            dataStream.write((byte)getPacketTypeCode());
-            dataStream.write(dhtKey.toByteArray());
-            dataStream.write(deletionKeyPlain.toByteArray());
-            dataStream.writeShort(encryptedData.length);
-            dataStream.write(encryptedData);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-		return byteArrayStream.toByteArray();
-	}
-
-	public void setNumFragments(int numFragments) {
-	    this.numFragments = numFragments;
-	}
-	
-	// Return the number of bytes in the packet.
-	// TODO just return content.length+CONST so we don't call toByteArray every time
-	public int getSize() {
-	    return toByteArray().length;
-	}
-	
-	public UniqueId getMessageId() {
-	    return messageId;
-	}
-    
-    private void verify() {
-        if (fragmentIndex<0 || fragmentIndex>=numFragments || numFragments<1)
-            log.error("Illegal values: fragmentIndex=" + fragmentIndex + " numFragments="+numFragments);
-        // TODO more sanity checks?
-    }
-    
-    @Override
-    public String toString() {
-        // TODO needs improvement
-        String str = super.toString() + " fragIdx=" + fragmentIndex + " numFrags=" + numFragments;
-        if (content == null)
-            str += " content=null";
-        else
-            str += " content length=" + content.length + " bytes";
-        return str;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/EmailSession.java b/apps/i2pbote/src/i2p/bote/packet/EmailSession.java
deleted file mode 100644
index df5be3c53ce2c22038ef0d1b5adf06fd666552d4..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/EmailSession.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package i2p.bote.packet;
-
-import java.nio.ByteBuffer;
-
-import net.i2p.util.RandomSource;
-
-public class EmailSession {
-    private static final byte SESSION_ID_LENGTH = 16;
-    
-    private byte[] sessionId;
-    
-    public EmailSession() {
-        sessionId = generateSessionId();
-    }
-
-    /**
-     * Construct a <CODE>EmailSession</CODE> using data read from a <CODE>ByteBuffer</CODE>.
-     * @param buffer
-     */
-    public EmailSession(ByteBuffer buffer) {
-        sessionId = new byte[SESSION_ID_LENGTH];
-        buffer.get(sessionId);
-    }
-
-    public byte[] getSessionId() {
-        return sessionId;
-    }
-    
-    private byte[] generateSessionId() {
-        RandomSource randomSource = RandomSource.getInstance();
-        byte[] sessionId = new byte[SESSION_ID_LENGTH];
-        for (int i=0; i<sessionId.length; i++)
-            sessionId[i] = (byte)randomSource.nextInt(256);
-        return sessionId;
-    }
-    
-    public String toString() {
-        StringBuffer buffer = new StringBuffer("[");
-        for (int i=0; i<sessionId.length; i++) {
-            if (i > 0)
-                buffer = buffer.append(" ");
-            String hexByte = Integer.toHexString(sessionId[i] & 0xFF);
-            if (hexByte.length() < 2)
-                buffer = buffer.append("0");
-            buffer = buffer.append(hexByte);
-        }
-        return buffer.append("]").toString();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/I2PBotePacket.java b/apps/i2pbote/src/i2p/bote/packet/I2PBotePacket.java
deleted file mode 100644
index 9694c2edcd940792783965c83242120e4cdc4bbb..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/I2PBotePacket.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package i2p.bote.packet;
-
-import java.nio.ByteBuffer;
-
-import i2p.bote.packet.dht.FindClosePeersPacket;
-import i2p.bote.packet.dht.RetrieveRequest;
-import i2p.bote.packet.dht.StoreRequest;
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-public abstract class I2PBotePacket {
-    private static final int MAX_DATAGRAM_SIZE = 31 * 1024;
-    private static final Log log = new Log(I2PBotePacket.class);
-    @SuppressWarnings("unchecked")
-    private static Class<? extends I2PBotePacket>[] ALL_PACKET_TYPES = new Class[] {
-        RelayPacket.class, ResponsePacket.class, RetrieveRequest.class, StoreRequest.class, FindClosePeersPacket.class,
-        PeerList.class, EmailPacket.class, IndexPacket.class
-    };
-    
-	public abstract byte[] toByteArray();
-	
-	/**
-	 * Returns the size of the packet in bytes.
-	 * @return
-	 */
-	public int getSize() {
-	    return toByteArray().length;
-	}
-    
-    /**
-     * Returns <code>false</code> if this packet can't fit into an I2P datagram.
-     * @return
-     */
-    public boolean isTooBig() {
-        return getSize() > MAX_DATAGRAM_SIZE;
-    }
-    
-    protected char getPacketTypeCode(Class<? extends I2PBotePacket> dataType) {
-        return dataType.getAnnotation(TypeCode.class).value();
-    }
-	
-    public char getPacketTypeCode() {
-        return getPacketTypeCode(getClass());
-    }
-
-	/**
-	 * Logs an error if the packet type of the packet instance is not correct
-	 * @param packetTypeCode
-	 */
-	protected void checkPacketType(char packetTypeCode) {
-	    if (getPacketTypeCode() != packetTypeCode)
-	        log.error("Packet type code of class " + getClass().getSimpleName() + " should be " + getPacketTypeCode() + ", is <" + packetTypeCode + ">");
-	}
-	
-    protected void checkPacketType(byte packetTypeCode) {
-        checkPacketType((char)packetTypeCode);
-    }
-
-/*	protected void checkPacketVersion(byte version, byte minVersion, byte maxVersion) {
-	    // TODO
-	}*/
-    
-    /**
-     * Creates a {@link Hash} from bytes read from a {@link ByteBuffer}.
-     * No check is done to make sure the buffer has enough bytes available.
-     */
-    protected Hash readHash(ByteBuffer buffer) {
-        byte[] bytes = new byte[Hash.HASH_LENGTH];
-        buffer.get(bytes);
-        return new Hash(bytes);
-    }
-	
-    protected static Class<? extends I2PBotePacket> decodePacketTypeCode(char packetTypeCode) {
-        for (Class<? extends I2PBotePacket> packetType: ALL_PACKET_TYPES)
-            if (packetType.getAnnotation(TypeCode.class).value() == packetTypeCode)
-                return packetType;
-        
-        log.debug("Invalid type code for I2PBotePacket: <" + packetTypeCode + ">");
-        return null;
-    }
-    
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/IndexPacket.java b/apps/i2pbote/src/i2p/bote/packet/IndexPacket.java
deleted file mode 100644
index 0b09d744512d3ea10c02142e0cf08a081fcde864..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/IndexPacket.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package i2p.bote.packet;
-
-import i2p.bote.EmailDestination;
-import i2p.bote.packet.dht.DhtStorablePacket;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-/**
- * This class is not thread-safe.
- *
- * @author HungryHobo@mail.i2p
- */
-@TypeCode('I')
-public class IndexPacket extends DhtStorablePacket {
-    private Log log = new Log(IndexPacket.class);
-    private Collection<Hash> dhtKeys;   // DHT keys of email packets
-    private Hash destinationHash;   // The DHT key of this packet
-    
-    public IndexPacket(byte[] data) {
-        ByteBuffer dataBuffer = ByteBuffer.wrap(data);
-        if (dataBuffer.get() != getPacketTypeCode())
-            log.error("Wrong type code for IndexPacket. Expected <" + getPacketTypeCode() + ">, got <" + (char)data[0] + ">");
-        
-        destinationHash = readHash(dataBuffer);
-
-        int numKeys = dataBuffer.get();
-        
-        dhtKeys = new ArrayList<Hash>();
-        for (int i=0; i<numKeys; i++) {
-            Hash dhtKey = readHash(dataBuffer);
-            dhtKeys.add(dhtKey);
-        }
-        
-        // TODO catch BufferUnderflowException; warn if extra bytes in the array
-    }
-
-    public IndexPacket(Collection<EmailPacket> emailPackets, EmailDestination emailDestination) {
-        dhtKeys = new ArrayList<Hash>();
-        for (EmailPacket emailPacket: emailPackets)
-            dhtKeys.add(emailPacket.getDhtKey());
-        
-        destinationHash = emailDestination.getHash();
-    }
-    
-    /**
-     * Merges the DHT keys of multiple index packets into one big index packet.
-     * The DHT key of this packet is not initialized.
-     * @param indexPackets
-     */
-    public IndexPacket(Collection<IndexPacket> indexPackets) {
-        dhtKeys = new HashSet<Hash>();
-        for (IndexPacket packet: indexPackets)
-            dhtKeys.addAll(packet.getDhtKeys());
-    }
-    
-    /**
-     * A varargs version of {@link IndexPacket(Collection<IndexPacket>)}.
-     * @param indexPackets
-     */
-    public IndexPacket(IndexPacket... indexPackets) {
-        this(Arrays.asList(indexPackets));
-    }
-    
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        outputStream.write(getPacketTypeCode());
-        try {
-            outputStream.write((byte)getPacketTypeCode());
-            destinationHash.writeBytes(outputStream);
-            outputStream.write((byte)dhtKeys.size());
-            for (Hash dhtKey: dhtKeys)
-                dhtKey.writeBytes(outputStream);
-            // TODO in the unit test, verify that toByteArray().length = Hash.NUM_BYTES + 1 + dhtKeys.size()*Hash.NUM_BYTES
-        } catch (DataFormatException e) {
-            log.error("Invalid format for email destination.", e);
-        } catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        return outputStream.toByteArray();
-    }
-
-    /**
-     * Returns the DHT keys of the {@link EmailPacket}s referenced by this {@link IndexPacket}.
-     * @return
-     */
-    public Collection<Hash> getDhtKeys() {
-        return dhtKeys;
-    }
-    
-    /**
-     * Returns the DHT key of this packet.
-     */
-    @Override
-    public Hash getDhtKey() {
-        return destinationHash;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/PeerList.java b/apps/i2pbote/src/i2p/bote/packet/PeerList.java
deleted file mode 100644
index 41679113b2e2bada6182e22a85214e0cf27881e4..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/PeerList.java
+++ /dev/null
@@ -1,70 +0,0 @@
-package i2p.bote.packet;
-
-import i2p.bote.network.kademlia.KademliaPeer;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collection;
-
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-@TypeCode('L')
-public class PeerList extends DataPacket {
-    private Log log = new Log(PeerList.class);
-    // TODO should be a Collection<Destination> because this class will also be used for relay peer lists
-    private Collection<KademliaPeer> peers;
-
-    public PeerList(Collection<KademliaPeer> peers) {
-        this.peers = peers;
-    }
-
-    public PeerList(byte[] data) throws DataFormatException {
-        ByteBuffer buffer = ByteBuffer.wrap(data);
-        if (buffer.get() != getPacketTypeCode())
-            log.error("Wrong type code for PeerList. Expected <" + getPacketTypeCode() + ">, got <" + (char)data[0] + ">");
-        
-        int numPeers = buffer.getShort();
-
-        peers = new ArrayList<KademliaPeer>();
-        for (int i=0; i<numPeers; i++) {
-            Destination destination = new Destination();
-            byte[] peerData = new byte[388];
-            // read 384 bytes, leave the last 3 bytes zero
-            buffer.get(peerData, 0, 384);
-            
-            destination.readBytes(peerData, 0);
-            KademliaPeer peer = new KademliaPeer(destination, 0);
-            peers.add(peer);
-        }
-        
-        if (buffer.hasRemaining())
-            log.debug("Peer List has " + buffer.remaining() + " extra bytes.");
-    }
-    
-    public Collection<KademliaPeer> getPeers() {
-        return peers;
-    }
-    
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
-        DataOutputStream dataStream = new DataOutputStream(arrayOutputStream);
-        
-        try {
-            dataStream.write((byte)getPacketTypeCode());
-            dataStream.writeShort(peers.size());
-            for (KademliaPeer peer: peers)
-                dataStream.write(peer.getDestination().toByteArray());
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        
-        return arrayOutputStream.toByteArray();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/PeerListRequest.java b/apps/i2pbote/src/i2p/bote/packet/PeerListRequest.java
deleted file mode 100644
index 060fcf7723b85a73f7b5df2e5a3890234098bbf1..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/PeerListRequest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package i2p.bote.packet;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import net.i2p.util.Log;
-
-@TypeCode('A')
-public class PeerListRequest extends CommunicationPacket {
-    private Log log = new Log(PeerListRequest.class);
-
-    public PeerListRequest() {
-    }
-
-    public PeerListRequest(byte[] data) {
-        super(data);
-        
-        int remaining = data.length - CommunicationPacket.HEADER_LENGTH;
-        if (remaining > 0)
-            log.debug("Peer List Request packet has " + remaining + " extra bytes.");
-    }
-
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        
-        try {
-            writeHeader(outputStream);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        
-        return outputStream.toByteArray();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/RelayPacket.java b/apps/i2pbote/src/i2p/bote/packet/RelayPacket.java
deleted file mode 100644
index 736da9e1c51c84b40f1420c68cdaf17118c88537..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/RelayPacket.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package i2p.bote.packet;
-
-import i2p.bote.Util;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.ByteBuffer;
-
-import net.i2p.data.DataFormatException;
-import net.i2p.data.Destination;
-import net.i2p.util.Log;
-
-@TypeCode('R')
-public class RelayPacket extends DataPacket {
-    public static final int XOR_KEY_LENGTH = 32;   // length of the XOR key in bytes
-    
-    private Log log = new Log(RelayPacket.class);
-    private long earliestSendTime;
-    private long latestSendTime;
-    private byte[] xorKey;
-    private Destination nextDestination;   // an I2P node to send the packet to
-    private byte[] payload;   // can contain another Relay Packet, Email Packet, or Retrieve Request
-
-    public RelayPacket(Destination nextDestination, long earliestSendTime, long latestSendTime) {
-        this.nextDestination = nextDestination;
-        this.earliestSendTime = earliestSendTime;
-        this.latestSendTime = latestSendTime;
-    }
-
-    public RelayPacket(byte[] data) throws DataFormatException {
-        ByteBuffer buffer = ByteBuffer.wrap(data);
-        
-        earliestSendTime = buffer.getInt();
-        latestSendTime = buffer.getInt();
-        
-        xorKey = new byte[XOR_KEY_LENGTH];
-        buffer.get(xorKey);
-        
-        nextDestination = new Destination();
-        byte[] destinationData = new byte[384];
-        buffer.get(destinationData);
-        nextDestination.readBytes(destinationData, 0);
-        
-        int payloadLength = buffer.getShort();
-        payload = new byte[payloadLength];
-        buffer.get(payload);
-        
-        if (buffer.hasRemaining())
-            log.debug("Relay Packet has " + buffer.remaining() + " extra bytes.");
-    }
-    
-    public RelayPacket(DataPacket dataPacket, Destination nextDestination, long earliestSendTime, long latestSendTime, byte[] xorKey) throws DataFormatException {
-        // TODO
-    }
-    
-    public RelayPacket(InputStream inputStream) throws IOException, DataFormatException {
-        this(Util.readInputStream(inputStream));
-    }
-    
-    public Destination getNextDestination() {
-        return nextDestination;
-    }
-
-    public long getEarliestSendTime() {
-        return earliestSendTime;
-    }
-
-    public long getLatestSendTime() {
-        return latestSendTime;
-    }
-    
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
-        DataOutputStream dataStream = new DataOutputStream(arrayOutputStream);
- 
-        try {
-            dataStream.writeInt((int)earliestSendTime);
-            dataStream.writeInt((int)latestSendTime);
-            dataStream.write(xorKey);
-            dataStream.write(nextDestination.toByteArray());
-            dataStream.writeShort(payload.length);
-            dataStream.write(payload);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        
-        return arrayOutputStream.toByteArray();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/RelayRequest.java b/apps/i2pbote/src/i2p/bote/packet/RelayRequest.java
deleted file mode 100644
index 405ec6fd6d7d68a9fc638f67e497bd278c8629af..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/RelayRequest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package i2p.bote.packet;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.security.NoSuchAlgorithmException;
-
-import net.i2p.I2PAppContext;
-import net.i2p.crypto.ElGamalAESEngine;
-import net.i2p.data.DataFormatException;
-import net.i2p.data.PrivateKey;
-import net.i2p.util.Log;
-
-import com.nettgryppa.security.HashCash;
-
-@TypeCode('Y')
-public class RelayRequest extends CommunicationPacket {
-    private Log log = new Log(RelayPacket.class);
-    private ElGamalAESEngine encrypter = I2PAppContext.getGlobalContext().elGamalAESEngine();
-    private HashCash hashCash;
-    private byte[] storedData;
-
-    public RelayRequest(HashCash hashCash, DataPacket dataPacket) {
-        this.hashCash = hashCash;
-        this.storedData = dataPacket.toByteArray();
-    }
-    
-    public RelayRequest(byte[] data) throws NoSuchAlgorithmException {
-        super(data);
-        ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
-        
-        int hashCashLength = buffer.getShort();
-        byte[] hashCashData = new byte[hashCashLength];
-        buffer.get(hashCashData);
-        hashCash = new HashCash(new String(hashCashData));
-        
-        int dataLength = buffer.getShort();
-        storedData = new byte[dataLength];
-        buffer.get(storedData);
-        
-        if (buffer.hasRemaining())
-            log.debug("Storage Request Packet has " + buffer.remaining() + " extra bytes.");
-    }
-
-    public HashCash getHashCash() {
-        return hashCash;
-    }
-
-    /**
-     * Returns the payload packet, i.e. the data that is being relayed.
-     * @param localDecryptionKey
-     * @return
-     * @throws DataFormatException
-     */
-    public DataPacket getStoredPacket(PrivateKey localDecryptionKey) throws DataFormatException {
-        byte[] decryptedData = encrypter.decrypt(storedData, localDecryptionKey);
-        return DataPacket.createPacket(decryptedData);
-    }
-
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
-        DataOutputStream dataStream = new DataOutputStream(byteArrayStream);
-
-        try {
-            writeHeader(dataStream);
-            String hashCashString = hashCash.toString();
-            dataStream.writeShort(hashCashString.length());
-            dataStream.write(hashCashString.getBytes());
-            dataStream.writeShort(storedData.length);
-            dataStream.write(storedData);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        return byteArrayStream.toByteArray();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/ResponsePacket.java b/apps/i2pbote/src/i2p/bote/packet/ResponsePacket.java
deleted file mode 100644
index 01d70ef2aee36c1e6b1552f45be49038818bdb54..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/ResponsePacket.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package i2p.bote.packet;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import net.i2p.util.Log;
-
-@TypeCode('N')
-public class ResponsePacket extends CommunicationPacket {
-    private Log log = new Log(EmailPacket.class);
-    private StatusCode statusCode;
-    private DataPacket payload;
-
-    public ResponsePacket(DataPacket payload, StatusCode statusCode, UniqueId packetId) {
-        super(packetId);
-        this.payload = payload;
-        this.statusCode = statusCode;
-    }
-    
-    public ResponsePacket(byte[] data) {
-        super(data);
-        ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
-
-        statusCode = StatusCode.values()[buffer.get()];
-
-        int payloadLength = buffer.getShort();
-        if (payloadLength > 0) {
-            byte[] payloadData = new byte[payloadLength];
-            buffer.get(payloadData);
-            payload = DataPacket.createPacket(payloadData);
-        }
-        
-        if (buffer.hasRemaining())
-            log.debug("Response Packet has " + buffer.remaining() + " extra bytes.");
-    }
-
-    public DataPacket getPayload() {
-        return payload;
-    }
-    
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
-        DataOutputStream dataStream = new DataOutputStream(byteStream);
-        
-        try {
-            writeHeader(dataStream);
-            dataStream.write(statusCode.ordinal());
-            
-            byte[] payloadBytes = payload.toByteArray();
-            dataStream.writeShort(payloadBytes.length);
-            dataStream.write(payloadBytes);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        
-        return byteStream.toByteArray();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/StatusCode.java b/apps/i2pbote/src/i2p/bote/packet/StatusCode.java
deleted file mode 100644
index 9bb44a22ddfd0c91a96fca66169aeea898a2683c..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/StatusCode.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package i2p.bote.packet;
-
-public enum StatusCode {
-    OK, GENERAL_ERROR, NO_DATA_FOUND, INVALID_PACKET, INVALID_HASHCASH, INSUFFICIENT_HASHCASH, NO_DISK_SPACE;
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/TypeCode.java b/apps/i2pbote/src/i2p/bote/packet/TypeCode.java
deleted file mode 100644
index 8006b07dcea731d6b60e8c8b7a278e536993e9f0..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/TypeCode.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package i2p.bote.packet;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface TypeCode {
-    char value();
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/UniqueId.java b/apps/i2pbote/src/i2p/bote/packet/UniqueId.java
deleted file mode 100644
index d72fcc747082fe944af77d4e0cd309da30f8dc9a..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/UniqueId.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package i2p.bote.packet;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.math.BigInteger;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-import net.i2p.data.Base64;
-import net.i2p.util.Log;
-import net.i2p.util.RandomSource;
-
-public class UniqueId implements Comparable<UniqueId> {
-    public static final byte LENGTH = 32;
-    
-    private Log log = new Log(UniqueId.class);
-    private byte[] bytes;
-
-    /**
-     * Create a random <code>UniqueId</code>.
-     */
-    public UniqueId() {
-        bytes = new byte[LENGTH];
-        for (int i=0; i<LENGTH; i++)
-            bytes[i] = (byte)RandomSource.getInstance().nextInt(256);
-    }
-
-    /**
-     * Create a packet id from a 32 bytes of an array, starting at <code>offset</code>.
-     * @param bytes
-     */
-    public UniqueId(byte[] bytes, int offset) {
-        this.bytes = new byte[LENGTH];
-        System.arraycopy(bytes, offset, this.bytes, 0, LENGTH);
-    }
-    
-    /**
-     * A copy constructor.
-     * @param uniqueId
-     */
-    public UniqueId(UniqueId uniqueId) {
-        this.bytes = uniqueId.bytes.clone();
-    }
-    
-    /**
-     * Creates a <code>UniqueId</code> using data read from a {@link ByteBuffer}.
-     * @param buffer
-     */
-    public UniqueId(ByteBuffer buffer) {
-        bytes = new byte[LENGTH];
-        buffer.get(bytes);
-    }
-    
-    public byte[] toByteArray() {
-        return bytes;
-    }
-    
-    public String toBase64() {
-        return Base64.encode(bytes);
-    }
-
-    public void writeTo(OutputStream outputStream) throws IOException {
-        outputStream.write(bytes);
-    }
-    
-    @Override
-    public int compareTo(UniqueId otherPacketId) {
-        return new BigInteger(bytes).compareTo(new BigInteger(otherPacketId.bytes));
-    }
-    
-    @Override
-    public String toString() {
-        return Base64.encode(bytes);
-    }
-    
-    @Override
-    public boolean equals(Object anotherObject) {
-        if (!(anotherObject instanceof UniqueId))
-            return false;
-        UniqueId otherPacketId = (UniqueId)anotherObject;
-        
-        return Arrays.equals(bytes, otherPacketId.bytes);
-    }
-    
-    @Override
-    public int hashCode() {
-        return Arrays.hashCode(bytes);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/dht/DhtStorablePacket.java b/apps/i2pbote/src/i2p/bote/packet/dht/DhtStorablePacket.java
deleted file mode 100644
index f854cc71316b7f7edfff524ceaeefe9ecfcdac06..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/dht/DhtStorablePacket.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package i2p.bote.packet.dht;
-
-import i2p.bote.packet.DataPacket;
-import i2p.bote.packet.I2PBotePacket;
-
-import java.io.File;
-
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-public abstract class DhtStorablePacket extends DataPacket {
-    private static Log log = new Log(DhtStorablePacket.class);
-
-    public abstract Hash getDhtKey();
-
-    /**
-     * Creates a {@link DhtStorablePacket} object from its byte array representation.
-     * The type of packet depends on the packet type field in the byte array.
-     * If there is an error, <code>null</code> is returned.
-     * @param data
-     * @param log
-     * @return
-     */
-    public static DhtStorablePacket createPacket(byte[] data) {
-        DataPacket packet = DataPacket.createPacket(data);
-        if (packet instanceof DhtStorablePacket)
-            return (DhtStorablePacket)packet;
-        else {
-            log.error("Packet is not a DhtStorablePacket: " + packet);
-            return null;
-        }
-    }
-
-    public static Class<? extends DhtStorablePacket> decodePacketTypeCode(char packetTypeCode) {
-        Class<? extends I2PBotePacket> packetType = I2PBotePacket.decodePacketTypeCode(packetTypeCode);
-        if (packetType!=null && DhtStorablePacket.class.isAssignableFrom(packetType))
-            return packetType.asSubclass(DhtStorablePacket.class);
-        else {
-            log.debug("Invalid type code for DhtStorablePacket: <" + packetTypeCode + ">");
-            return null;
-        }
-    }
-    
-    public static DhtStorablePacket createPacket(File file) {
-        DataPacket dataPacket;
-        dataPacket = DataPacket.createPacket(file);
-        if (dataPacket instanceof DhtStorablePacket)
-            return (DhtStorablePacket)dataPacket;
-        else {
-            log.warn("Expected: DhtStorablePacket, got: " + dataPacket.getClass().getSimpleName());
-            return null;
-        }
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/dht/FindClosePeersPacket.java b/apps/i2pbote/src/i2p/bote/packet/dht/FindClosePeersPacket.java
deleted file mode 100644
index 9cd8cd39250c6e4cee555da47cb2bf9786ff68cb..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/dht/FindClosePeersPacket.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package i2p.bote.packet.dht;
-
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.packet.TypeCode;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-@TypeCode('F')
-public class FindClosePeersPacket extends CommunicationPacket {
-    private Log log = new Log(FindClosePeersPacket.class);
-    private Hash key;
-
-    public FindClosePeersPacket(Hash key) {
-        this.key = key;
-    }
-    
-    public FindClosePeersPacket(byte[] data) {
-        super(data);
-        
-        byte[] hashData = new byte[Hash.HASH_LENGTH];
-        System.arraycopy(data, CommunicationPacket.HEADER_LENGTH, hashData, 0, hashData.length);
-        key = new Hash(hashData);
-        
-        int remaining = data.length - (CommunicationPacket.HEADER_LENGTH+hashData.length);
-        if (remaining > 0)
-            log.debug("Find Close Nodes Request packet has " + remaining + " extra bytes.");
-    }
-    public Hash getKey() {
-        return key;
-    }
-
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        
-        try {
-            writeHeader(outputStream);
-            outputStream.write(key.toByteArray());
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        
-        return outputStream.toByteArray();
-    }
-    
-    @Override
-    public String toString() {
-        return super.toString() + " key=" + key.toBase64().substring(0, 8) + "...";
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/dht/RetrieveRequest.java b/apps/i2pbote/src/i2p/bote/packet/dht/RetrieveRequest.java
deleted file mode 100644
index b23d4223b66ae45b107f12b22cb122531287f525..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/dht/RetrieveRequest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package i2p.bote.packet.dht;
-
-import i2p.bote.I2PBote;
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.packet.I2PBotePacket;
-import i2p.bote.packet.TypeCode;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-@TypeCode('Q')
-public class RetrieveRequest extends CommunicationPacket {
-    private Log log = new Log(I2PBote.class);
-    private Hash key;
-    private Class<? extends DhtStorablePacket> dataType;
-
-    public RetrieveRequest(Hash key, Class<? extends DhtStorablePacket> dataType) {
-        this.key = key;
-        this.dataType = dataType;
-    }
-    
-    public RetrieveRequest(byte[] data) {
-        super(data);
-        ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
-        
-        char dataTypeCode = (char)buffer.get();
-        dataType = DhtStorablePacket.decodePacketTypeCode(dataTypeCode);
-        
-        byte[] keyBytes = new byte[Hash.HASH_LENGTH];
-        buffer.get(keyBytes);
-        key = new Hash(keyBytes);
-        
-        if (buffer.hasRemaining())
-            log.debug("Retrieve Request Packet has " + buffer.remaining() + " extra bytes.");
-    }
-
-    public Hash getKey() {
-        return key;
-    }
-    
-    public Class<? extends I2PBotePacket> getDataType() {
-        return dataType;
-    }
-
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
-        
-        try {
-            writeHeader(outputStream);
-            outputStream.write(getPacketTypeCode(dataType));
-            outputStream.write(key.toByteArray());
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        
-        return outputStream.toByteArray();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/packet/dht/StoreRequest.java b/apps/i2pbote/src/i2p/bote/packet/dht/StoreRequest.java
deleted file mode 100644
index b387d388976770ab81568826d905c46013aa5ca0..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/packet/dht/StoreRequest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package i2p.bote.packet.dht;
-
-import i2p.bote.packet.CommunicationPacket;
-import i2p.bote.packet.RelayPacket;
-import i2p.bote.packet.TypeCode;
-
-import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.security.NoSuchAlgorithmException;
-
-import net.i2p.data.Hash;
-import net.i2p.util.Log;
-
-import com.nettgryppa.security.HashCash;
-
-@TypeCode('S')
-public class StoreRequest extends CommunicationPacket {
-    private Log log = new Log(RelayPacket.class);
-    private HashCash hashCash;
-    private DhtStorablePacket packetToStore;
-
-    public StoreRequest(HashCash hashCash, DhtStorablePacket packetToStore) {
-        this.hashCash = hashCash;
-        this.packetToStore = packetToStore;
-    }
-    
-    public StoreRequest(byte[] data) throws NoSuchAlgorithmException {
-        super(data);
-        ByteBuffer buffer = ByteBuffer.wrap(data, HEADER_LENGTH, data.length-HEADER_LENGTH);
-        
-        int hashCashLength = buffer.getShort();
-        byte[] hashCashData = new byte[hashCashLength];
-        buffer.get(hashCashData);
-        hashCash = new HashCash(new String(hashCashData));
-        
-        int dataLength = buffer.getShort();
-        byte[] storedData = new byte[dataLength];
-        buffer.get(storedData);
-        packetToStore = DhtStorablePacket.createPacket(storedData);
-        
-        if (buffer.hasRemaining())
-            log.debug("Storage Request Packet has " + buffer.remaining() + " extra bytes.");
-    }
-
-    public Hash getKey() {
-        return packetToStore.getDhtKey();
-    }
-    
-    public DhtStorablePacket getPacketToStore() {
-        return packetToStore;
-    }
-
-    @Override
-    public byte[] toByteArray() {
-        ByteArrayOutputStream byteArrayStream = new ByteArrayOutputStream();
-        DataOutputStream dataStream = new DataOutputStream(byteArrayStream);
-
-        try {
-            writeHeader(dataStream);
-            String hashCashString = hashCash.toString();
-            dataStream.writeShort(hashCashString.length());
-            dataStream.write(hashCashString.getBytes());
-            byte[] dataToStore = packetToStore.toByteArray();
-            dataStream.writeShort(dataToStore.length);
-            dataStream.write(dataToStore);
-        }
-        catch (IOException e) {
-            log.error("Can't write to ByteArrayOutputStream.", e);
-        }
-        return byteArrayStream.toByteArray();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/service/I2PBoteException.java b/apps/i2pbote/src/i2p/bote/service/I2PBoteException.java
deleted file mode 100644
index 0024dc4d5d54de378c12169b0ac9d284d9103291..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/service/I2PBoteException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package i2p.bote.service;
-
-import net.i2p.I2PException;
-
-public class I2PBoteException extends I2PException {
-    private static final long serialVersionUID = 5002910979704859701L;
-
-    public I2PBoteException(String message) {
-        super(message);
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/service/I2PBoteThread.java b/apps/i2pbote/src/i2p/bote/service/I2PBoteThread.java
deleted file mode 100644
index 12bb918a33142634abaded9edcf1181bcdec1cd5..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/service/I2PBoteThread.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package i2p.bote.service;
-
-import net.i2p.util.I2PAppThread;
-
-public class I2PBoteThread extends I2PAppThread {
-
-    protected I2PBoteThread(String name) {
-        super(name);
-    }
-    
-    private volatile boolean shutdown;
-    
-    public void requestShutdown() {
-        shutdown = true;
-    }
-    
-    protected boolean shutdownRequested() {
-        return shutdown;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/service/OutboxProcessor.java b/apps/i2pbote/src/i2p/bote/service/OutboxProcessor.java
deleted file mode 100644
index 839bfa44c9875c3039b726deea029efe4e994aec..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/service/OutboxProcessor.java
+++ /dev/null
@@ -1,155 +0,0 @@
-package i2p.bote.service;
-
-import i2p.bote.Configuration;
-import i2p.bote.EmailDestination;
-import i2p.bote.folder.Outbox;
-import i2p.bote.network.EmailAddressResolver;
-import i2p.bote.network.PeerManager;
-import i2p.bote.packet.Email;
-
-import java.io.IOException;
-import java.security.NoSuchAlgorithmException;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import javax.mail.Address;
-import javax.mail.MessagingException;
-import javax.mail.internet.InternetAddress;
-
-import net.i2p.client.I2PSession;
-import net.i2p.util.I2PAppThread;
-import net.i2p.util.Log;
-
-import com.nettgryppa.security.HashCash;
-
-/**
- * A background thread that checks the outbox for emails and sends them to the I2P network.
- *
- * @author HungryHobo@mail.i2p
- */
-public class OutboxProcessor extends I2PAppThread {
-	private static final int PAUSE = 10;   // The wait time, in minutes, before processing the folder again. Can be interrupted from the outside.
-	
-	private Log log = new Log(OutboxProcessor.class);
-	private Outbox outbox;
-	private Configuration configuration;
-	private EmailAddressResolver emailAddressResolver;
-	private Map<Address, String> statusMap;
-	private CountDownLatch checkForEmailSignal;
-	
-	public OutboxProcessor(I2PSession i2pSession, Outbox outbox, Configuration configuration, PeerManager peerManager) {
-		super("OutboxProcessor");
-		this.outbox = outbox;
-		this.configuration = configuration;
-		statusMap = new ConcurrentHashMap<Address, String>();
-		emailAddressResolver = new EmailAddressResolver();
-	}
-	
-	@Override
-	public void run() {
-		while (true) {
-            synchronized(this) {
-                checkForEmailSignal = new CountDownLatch(1);
-            }
-            
-			log.info("Processing outgoing emails in directory '" + outbox.getStorageDirectory() + "'.");
-			for (Email email: outbox) {
-			    log.info("Processing outbox file: '" + email.getFile() + "'.");
-				try {
-					sendEmail(email);
-				} catch (Exception e) {
-				    log.error("Error sending email.", e);
-				}
-			}
-			
-			try {
-	            checkForEmailSignal.await(PAUSE, TimeUnit.MINUTES);
-			} catch (InterruptedException e) {
-			    log.error("OutboxProcessor received an InterruptedException.", e);
-			}
-		}
-	}
-	
-	/**
-	 * Tells the <code>OutboxProcessor</code> to check for new outgoing emails immediately.
-	 */
-	public void checkForEmail() {
-	    checkForEmailSignal.countDown();
-	}
-	
-	/**
-	 * Send an {@link Email} to all recipients specified in the header.
-	 * @param email
-	 * @throws MessagingException
-	 * @throws IOException
-	 */
-	private void sendEmail(Email email) throws MessagingException, IOException {
-		email.saveChanges();   // this updates the headers
-		email.scrubHeaders();   // TODO if the MimeMessage implementation doesn't remove BCC fields, move this line to EmailSendTask.sendIndividual(), after addHashCash
-		
-		for (Address recipient: email.getAllRecipients())
-			// only handle email addresses, report an error for news addresses
-			if (recipient instanceof InternetAddress) {
-				String recipientAddress = ((InternetAddress)recipient).getAddress();
-				sendToOne(recipientAddress, email);
-			}
-			else
-			    log.error("Illegal recipient type: " + recipient.getType());
-	}
-
-	/**
-     * Send an {@link Email} to one recipient.
-	 * @param address
-	 * @param email
-	 */
-	private void sendToOne(String address, Email email) {
-		String logSuffix = null;   // only used for logging
-		try {
-			addHashCash(email);
-			logSuffix = "Recipient = '" + address + "' Message ID = '" + email.getMessageID() + "'";
-			EmailDestination emailDestination = emailAddressResolver.getDestination(address);
-		}
-		catch (Exception e) {
-		    log.error("Error trying to send email. " + logSuffix);
-			outbox.updateStatus(email, new int[] { 1 }, "Email sent to recipient: " + address);
-			return;
-		}
-		
-/*		int[] numForwarded = new int[fragments.size()];
-		for (int fragmentIndex=0; fragmentIndex<fragments.size(); fragmentIndex++)
-		    for (int i=0; i<configuration.getRedundancy(); i++) {
-		        Destination peer = peerManager.getRandomPeer();
-			    Authorizer.Response relaySendResult = sendToHost(fragments.get(fragmentIndex), peer);
-				if (Authorizer.Response.ACCEPTED.equals(relaySendResult))
-					numForwarded[fragmentIndex]++;
-				int redundancy = configuration.getRedundancy();
-				
-				boolean done = true;
-				for (int j=0; j<numForwarded.length; j++)
-					if (numForwarded[j] < redundancy)
-						done = false;
-				if (done) {
-					outbox.updateStatus(email, numForwarded, "Email sent to " + redundancy + " relays, plus kept on localhost.");
-					log.info("Email submitted to relays. " + logSuffix);
-					return;
-				}
-			}
-			log.info("Relaying not yet successful. " + logSuffix);
-			outbox.updateStatus(email, numForwarded, "Relaying failed or succeeded partially, will try again soon.");
-		}*/
-	}
-
-	private void addHashCash(Email email) throws NoSuchAlgorithmException, MessagingException {
-		email.setHeader("X-HashCash", HashCash.mintCash("", configuration.getHashCashStrength()).toString());
-	}
-	
-	public Map<Address, String> getStatus() {
-		return statusMap;
-	}
-	
-	public void shutDown() {
-	    // TODO
-	}
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/service/POP3Service.java b/apps/i2pbote/src/i2p/bote/service/POP3Service.java
deleted file mode 100644
index 0073364c1d0c078967db6da8a9dcc888c6e25125..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/service/POP3Service.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package i2p.bote.service;
-
-import net.i2p.util.I2PAppThread;
-
-public class POP3Service extends I2PAppThread {
-
-	public POP3Service() {
-		super("Background thread for delivering email to email clients");
-	}
-	
-	public void shutDown() {
-	}
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/service/RelayPacketSender.java b/apps/i2pbote/src/i2p/bote/service/RelayPacketSender.java
deleted file mode 100644
index 1af71c2b31b086d597e601b92df08050727339a5..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/service/RelayPacketSender.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package i2p.bote.service;
-
-import i2p.bote.Configuration;
-import i2p.bote.folder.PacketFolder;
-import i2p.bote.network.I2PSendQueue;
-import i2p.bote.packet.RelayPacket;
-
-import java.text.ParseException;
-
-import javax.mail.MessagingException;
-
-import com.nettgryppa.security.HashCash;
-
-import net.i2p.I2PAppContext;
-import net.i2p.crypto.ElGamalAESEngine;
-import net.i2p.crypto.SessionKeyManager;
-import net.i2p.util.Log;
-import net.i2p.util.RandomSource;
-
-/**
- * A background thread that sends packets in the relay outbox to the I2P network.
- *
- * @author HungryHobo@mail.i2p
- */
-public class RelayPacketSender extends I2PBoteThread {
-    private static final int PAUSE = 10 * 60 * 1000;   // the wait time, in milliseconds,  before processing the folder again
-    private static final int PADDED_SIZE = 16 * 1024;
-    private static final Log log = new Log(RelayPacketSender.class);
-    
-    private I2PSendQueue sendQueue;
-    private ElGamalAESEngine encrypter = I2PAppContext.getGlobalContext().elGamalAESEngine();
-    private SessionKeyManager sessionKeyManager = I2PAppContext.getGlobalContext().sessionKeyManager();
-    private PacketFolder<RelayPacket> packetStore;
-    private Configuration configuration;
-    
-    public RelayPacketSender(I2PSendQueue sendQueue, PacketFolder<RelayPacket> packetStore) {
-        super("RelayPacketSender");
-        this.sendQueue = sendQueue;
-        this.packetStore = packetStore;
-    }
-    
-    @Override
-    public void run() {
-        while (true) {
-            if (log.shouldLog(Log.DEBUG))
-                log.debug("Deleting expired packets...");
-            try {
-                deleteExpiredPackets();
-            } catch (Exception e) {
-                log.error("Error deleting expired packets", e);
-            }
-            
-            log.info("Processing outgoing packets in directory '" + packetStore.getStorageDirectory().getAbsolutePath() + "'");
-            for (RelayPacket packet: packetStore) {
-                log.info("Processing packet file: <" + packet.getFile() + ">");
-                try {
-                    HashCash hashCash = null;   // TODO
-                    long sendTime = getRandomSendTime(packet);
-                    sendQueue.sendRelayRequest(packet, hashCash, sendTime);
-                } catch (Exception e) {
-                    log.error("Error sending packet. ", e);
-                }
-            }
-            
-            try {
-                Thread.sleep(PAUSE);
-            } catch (InterruptedException e) {
-                log.error("RelayPacketSender received an InterruptedException.");
-            }
-        }
-    }
-    
-    private long getRandomSendTime(RelayPacket packet) {
-        long min = packet.getEarliestSendTime();
-        long max = packet.getLatestSendTime();
-        return min + RandomSource.getInstance().nextLong(max-min);
-    }
-    
-    public void deleteExpiredPackets() throws ParseException, MessagingException {
-        // TODO look at filename which = receive time, delete if too old
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/service/SMTPService.java b/apps/i2pbote/src/i2p/bote/service/SMTPService.java
deleted file mode 100644
index 3bdb8f0e58793990a17936221749705401b1679a..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/service/SMTPService.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package i2p.bote.service;
-
-import net.i2p.util.I2PAppThread;
-
-public class SMTPService extends I2PAppThread {
-
-	public SMTPService() {
-		super("Background thread for receiving email from email clients");
-	}
-	
-	public void shutDown() {
-	}
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/service/StreamHandler.java b/apps/i2pbote/src/i2p/bote/service/StreamHandler.java
deleted file mode 100644
index 7a78bd9818b8a0892c460365f37ebc501eeefef2..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/service/StreamHandler.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package i2p.bote.service;
-
-import java.util.EventListener;
-
-public interface StreamHandler extends EventListener {
-
-    void streamReceived(StreamReceivedEvent event);
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/service/StreamReceivedEvent.java b/apps/i2pbote/src/i2p/bote/service/StreamReceivedEvent.java
deleted file mode 100644
index 4cd93f6d9408d79d03faf1b119d6e58ac9ee1dac..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/service/StreamReceivedEvent.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package i2p.bote.service;
-
-import java.util.EventObject;
-
-import net.i2p.client.streaming.I2PSocket;
-import net.i2p.data.Destination;
-
-public class StreamReceivedEvent extends EventObject {
-    private static final long serialVersionUID = 5231936151819853813L;
-    
-    private I2PSocket socket;
-
-    public StreamReceivedEvent(I2PSocket socket) {
-        super(socket.getPeerDestination());
-        this.socket = socket;
-    }
-
-    public I2PSocket getSocket() {
-        return socket;
-    }
-    
-    public Destination getSource() {
-        return (Destination)super.getSource();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/web/CheckMailTag.java b/apps/i2pbote/src/i2p/bote/web/CheckMailTag.java
deleted file mode 100644
index e58cf995febe34ca526820b0c3133dd79355a046..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/web/CheckMailTag.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package i2p.bote.web;
-
-import javax.servlet.jsp.tagext.SimpleTagSupport;
-
-public class CheckMailTag extends SimpleTagSupport {
-
-    public void doTag() {
-        JSPHelper.checkForMail();
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/web/JSPHelper.java b/apps/i2pbote/src/i2p/bote/web/JSPHelper.java
deleted file mode 100644
index 7100a15c0f6cb22aa9ffae1baf908d924ff140ef..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/web/JSPHelper.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package i2p.bote.web;
-
-import i2p.bote.EmailIdentity;
-import i2p.bote.I2PBote;
-import i2p.bote.Identities;
-import i2p.bote.Util;
-import i2p.bote.folder.EmailFolder;
-
-import java.io.IOException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ThreadFactory;
-import java.util.concurrent.TimeUnit;
-
-import net.i2p.util.Log;
-
-/**
- * Implements JSP functions.
- *
- * @author HungryHobo@mail.i2p
- */
-public class JSPHelper {
-    private static final Log log = new Log(JSPHelper.class);
-    private static final int MAX_THREADS = 10;
-    private static final int THREAD_STACK_SIZE = 64 * 1024;   // TODO find a safe low value (default in 64-bit Java 1.6 = 1MByte)
-    private static final ThreadFactory MAIL_CHECK_THREAD_FACTORY = Util.createThreadFactory("CheckMail", THREAD_STACK_SIZE);
-    private static ExecutorService EXECUTOR;
-
-    private JSPHelper() {
-        throw new UnsupportedOperationException();
-    }
-    
-    public static Identities getIdentities() {
-        return I2PBote.getInstance().getIdentities();
-    }
-    
-    /**
-     * Updates an email identity if <code>key</code> exists, or adds a new identity.
-     * @param key A base64-encoded email identity key
-     * @param description
-     * @param publicName
-     * @param emailAddress
-     * @return null if sucessful, or an error message if an error occured
-     */
-    public static String saveIdentity(String key, String publicName, String description, String emailAddress) {
-        Identities identities = JSPHelper.getIdentities();
-        EmailIdentity identity = identities.get(key);
-        
-        if (identity != null) {
-            identity.setPublicName(publicName);
-            identity.setDescription(description);
-            identity.setEmailAddress(emailAddress);
-        }
-        else {
-            identity = new EmailIdentity();
-            identity.setPublicName(publicName);
-            identity.setDescription(description);
-            identity.setEmailAddress(emailAddress);
-            identities.add(identity);
-        }
-
-        try {
-            identities.save();
-            return null;
-        }
-        catch (IOException e) {
-            return e.getLocalizedMessage();
-        }
-    }
-
-    /**
-     * Deletes an email identity.
-     * @param key A base64-encoded email identity key
-     * @return null if sucessful, or an error message if an error occured
-     */
-    public static String deleteIdentity(String key) {
-        Identities identities = JSPHelper.getIdentities();
-        identities.remove(key);
-
-        try {
-            identities.save();
-            return null;
-        }
-        catch (IOException e) {
-            return e.getLocalizedMessage();
-        }
-    }
-
-    public static void checkForMail() {
-        if (!isCheckingForMail()) {
-            EXECUTOR = Executors.newFixedThreadPool(MAX_THREADS, MAIL_CHECK_THREAD_FACTORY);
-            
-            I2PBote bote = I2PBote.getInstance();
-            for (EmailIdentity identity: bote.getIdentities())
-                EXECUTOR.submit(bote.createCheckMailTask(identity));
-            
-            EXECUTOR.shutdown();
-            try {
-                EXECUTOR.awaitTermination(1, TimeUnit.DAYS);
-            }
-            catch (InterruptedException e) {
-                log.error("Interrupted while checking for mail.", e);
-                EXECUTOR.shutdownNow();
-            }
-        }
-    }
-
-    public static boolean isCheckingForMail() {
-        return (EXECUTOR!=null && !EXECUTOR.isTerminated());
-    }
-    
-    public static EmailFolder getMailFolder(String folderName) {
-        if ("Inbox".equals(folderName))
-            return I2PBote.getInstance().getInbox();
-        else
-            return null;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/web/PrintNumDhtPeersTag.java b/apps/i2pbote/src/i2p/bote/web/PrintNumDhtPeersTag.java
deleted file mode 100644
index aec3a723de08683098d306a1bb2773582ae9e8fc..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/web/PrintNumDhtPeersTag.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package i2p.bote.web;
-
-import i2p.bote.I2PBote;
-
-import java.io.IOException;
-
-import javax.servlet.jsp.JspWriter;
-import javax.servlet.jsp.PageContext;
-import javax.servlet.jsp.tagext.SimpleTagSupport;
-
-import net.i2p.util.Log;
-
-public class PrintNumDhtPeersTag extends SimpleTagSupport {
-    private Log log = new Log(PrintNumDhtPeersTag.class);
-
-    public void doTag() {
-        PageContext pageContext = (PageContext) getJspContext();
-        JspWriter out = pageContext.getOut();
-        
-        try {
-            out.println(I2PBote.getInstance().getNumDhtPeers());
-        } catch (IOException e) {
-            log.error("Can't write output to HTML page", e);
-        }
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/web/PrintNumRelayPeersTag.java b/apps/i2pbote/src/i2p/bote/web/PrintNumRelayPeersTag.java
deleted file mode 100644
index edd48a419bd893da25d68adf22a711db282ce700..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/web/PrintNumRelayPeersTag.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package i2p.bote.web;
-
-import i2p.bote.I2PBote;
-
-import java.io.IOException;
-
-import javax.servlet.jsp.JspWriter;
-import javax.servlet.jsp.PageContext;
-import javax.servlet.jsp.tagext.SimpleTagSupport;
-
-import net.i2p.util.Log;
-
-public class PrintNumRelayPeersTag extends SimpleTagSupport {
-    private Log log = new Log(PrintNumRelayPeersTag.class);
-
-    public void doTag() {
-        PageContext pageContext = (PageContext) getJspContext();
-        JspWriter out = pageContext.getOut();
-        
-        try {
-            out.println(I2PBote.getInstance().getNumRelayPeers());
-        } catch (IOException e) {
-            log.error("Can't write output to HTML page", e);
-        }
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/web/ReturnStatus.java b/apps/i2pbote/src/i2p/bote/web/ReturnStatus.java
deleted file mode 100644
index 23417308cd182ef82554f213130f0fe77d8ac5a7..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/web/ReturnStatus.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package i2p.bote.web;
-
-/**
- * Represents the return status of a JSP function.
- *
- * @author HungryHobo@mail.i2p
- */
-public class ReturnStatus {
-
-    private boolean error;
-    private String message;
-    
-    public ReturnStatus(boolean error, String message) {
-        this.setError(error);
-        this.setMessage(message);
-    }
-
-    public void setError(boolean error) {
-        this.error = error;
-    }
-
-    public boolean isError() {
-        return error;
-    }
-
-    public void setMessage(String message) {
-        this.message = message;
-    }
-
-    public String getMessage() {
-        return message;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/web/SendEmailTag.java b/apps/i2pbote/src/i2p/bote/web/SendEmailTag.java
deleted file mode 100644
index c74d5487db51f705c7f6e5ca0ee65ae06b9e06f2..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/web/SendEmailTag.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package i2p.bote.web;
-
-import i2p.bote.I2PBote;
-import i2p.bote.packet.Email;
-
-import java.io.IOException;
-
-import javax.mail.Message.RecipientType;
-import javax.mail.internet.InternetAddress;
-import javax.servlet.jsp.JspWriter;
-import javax.servlet.jsp.PageContext;
-import javax.servlet.jsp.tagext.SimpleTagSupport;
-
-import net.i2p.util.Log;
-
-public class SendEmailTag extends SimpleTagSupport {
-    // TODO make all Log instances final
-    private Log log = new Log(SendEmailTag.class);
-	private String recipientAddress;
-	private String message;
-
-	public void doTag() {
-		PageContext pageContext = (PageContext) getJspContext();
-		JspWriter out = pageContext.getOut();
-		
-		Email email = new Email();
-		String statusMessage;
-		try {
-			email.addRecipient(RecipientType.TO, new InternetAddress(recipientAddress));
-			email.setContent(message, "text/html");
-			I2PBote.getInstance().sendEmail(email);
-			statusMessage = "Email has been queued for sending.";
-		}
-		catch (Exception e) {
-			statusMessage = "Error sending email: " + e.getLocalizedMessage();
-			log.error("Error sending email", e);
-		}
-
-		try {
-			out.println(statusMessage);
-		} catch (IOException e) {
-			log.error("Can't write output to HTML page", e);
-		}
-	}
-
-	public void setRecipient(String recipient) {
-		this.recipientAddress = recipient;
-	}
-
-	public String getRecipient() {
-		return recipientAddress;
-	}
-
-	public void setMessage(String message) {
-		this.message = message;
-	}
-
-	public String getMessage() {
-		return message;
-	}
-}
\ No newline at end of file
diff --git a/apps/i2pbote/src/i2p/bote/web/ServiceInitializer.java b/apps/i2pbote/src/i2p/bote/web/ServiceInitializer.java
deleted file mode 100644
index 24052823e41a379b5b0bf2d713a8a72426901a82..0000000000000000000000000000000000000000
--- a/apps/i2pbote/src/i2p/bote/web/ServiceInitializer.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package i2p.bote.web;
-
-import i2p.bote.I2PBote;
-
-import javax.servlet.ServletContextEvent;
-import javax.servlet.ServletContextListener;
-
-/**
- * Starts the I2PBote backend when the web app is initialized
- * @author HungryHobo@mail.i2p
- *
- */
-public class ServiceInitializer implements ServletContextListener {
-	@Override
-	public void contextDestroyed(ServletContextEvent event) {
-		I2PBote.shutDown();
-	}
-
-	@Override
-	public void contextInitialized(ServletContextEvent event) {
-		I2PBote.startUp();
-	}
-
-}
diff --git a/apps/i2pbote/test/i2p/bote/AllTests.java b/apps/i2pbote/test/i2p/bote/AllTests.java
deleted file mode 100644
index 42146bba0cb26c12ef826bcc75812ca714757eab..0000000000000000000000000000000000000000
--- a/apps/i2pbote/test/i2p/bote/AllTests.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package i2p.bote;
-
-import i2p.bote.packet.EmailPacketTest;
-import i2p.bote.packet.I2PBotePacketTest;
-import i2p.bote.packet.ResponsePacketTest;
-import i2p.bote.packet.kademlia.FindCloseNodesPacketTest;
-import i2p.bote.packet.kademlia.StorageRequestTest;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses( { I2PBotePacketTest.class, StorageRequestTest.class, EmailPacketTest.class, FindCloseNodesPacketTest.class, ResponsePacketTest.class })
-public class AllTests {
-
-    public static Test suite() {
-        TestSuite suite = new TestSuite("Test for i2p.bote");
-        //$JUnit-BEGIN$
-
-        //$JUnit-END$
-        return suite;
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/test/i2p/bote/network/kademlia/KademliaDHTTest.java b/apps/i2pbote/test/i2p/bote/network/kademlia/KademliaDHTTest.java
deleted file mode 100644
index 3cc8cee641ecb97c383f82f39622fda530517dff..0000000000000000000000000000000000000000
--- a/apps/i2pbote/test/i2p/bote/network/kademlia/KademliaDHTTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package i2p.bote.network.kademlia;
-
-import static org.junit.Assert.fail;
-import i2p.bote.network.I2PPacketDispatcher;
-import i2p.bote.network.I2PSendQueue;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileWriter;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-
-import net.i2p.client.I2PClient;
-import net.i2p.client.I2PClientFactory;
-import net.i2p.client.I2PSession;
-import net.i2p.data.Destination;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class KademliaDHTTest {
-    private static final int NUM_NODES = 100;
-    
-    private Collection<KademliaDHT> nodes;
-
-    @Before
-    public void setUp() throws Exception {
-        File tmpDir = new File(System.getProperty("java.io.tmpdir"));
-        File testDir = new File(tmpDir, "I2PBote-Test_" + System.currentTimeMillis());
-        testDir.mkdir();
-        
-        I2PClient i2pClient = I2PClientFactory.createClient();
-        
-        Destination firstNode = null;
-        
-        nodes = Collections.synchronizedList(new ArrayList<KademliaDHT>());
-        for (int i=0; i<NUM_NODES; i++) {
-            ByteArrayOutputStream arrayStream = new ByteArrayOutputStream();
-            Destination destination = i2pClient.createDestination(arrayStream);
-            byte[] destinationArray = arrayStream.toByteArray();
-            I2PSession i2pSession = i2pClient.createSession(new ByteArrayInputStream(destinationArray), null);
-            
-            I2PPacketDispatcher packetDispatcher = new I2PPacketDispatcher();
-            i2pSession.addSessionListener(packetDispatcher, I2PSession.PROTO_ANY, I2PSession.PORT_ANY);
-            
-            I2PSendQueue sendQueue = new I2PSendQueue(i2pSession, packetDispatcher);
-            
-            File peerFile = new File(testDir, "peers" + i);
-            if (firstNode != null) {
-                FileWriter writer = new FileWriter(peerFile);
-                writer.write(firstNode.toBase64());
-            }
-            else
-                firstNode = destination;
-            
-            nodes.add(new KademliaDHT(destination, sendQueue, packetDispatcher, peerFile));
-        }
-    }
-
-    @After
-    public void tearDown() throws Exception {
-    }
-
-    @Test
-    public void testBootstrap() {
-        fail("Not yet implemented");
-    }
-    
-    @Test
-    public void testFindOne() {
-        fail("Not yet implemented");
-    }
-
-    @Test
-    public void testFindAll() {
-        fail("Not yet implemented");
-    }
-
-    @Test
-    public void testStore() {
-        fail("Not yet implemented");
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/test/i2p/bote/packet/EmailPacketTest.java b/apps/i2pbote/test/i2p/bote/packet/EmailPacketTest.java
deleted file mode 100644
index d5671f7087892d01de4a8475274af60d38d19dbe..0000000000000000000000000000000000000000
--- a/apps/i2pbote/test/i2p/bote/packet/EmailPacketTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package i2p.bote.packet;
-
-import static junit.framework.Assert.assertTrue;
-import i2p.bote.EmailIdentity;
-
-import java.util.Arrays;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class EmailPacketTest {
-    EmailPacket emailPacket;
-    EmailIdentity identity;
-    String message = "This is a test message. Test 1 2 3 Test";
-
-    @Before
-    public void setUp() throws Exception {
-        byte[] content = message.getBytes();
-        UniqueId deletionKeyPlain = new UniqueId(new byte[] {72, -18, -72, -39, 122, 40, -104, -66, -54, -61, -108, 72, 54, 30, 37, 76, 44, 86, 104, -124, -31, 32, -82, -27, 26, 76, 7, 106, -76, 72, 49, -44}, 0);
-        UniqueId deletionKeyEncrypted = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
-        
-        byte[] messageIdBytes = new byte[] {2, -69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123};
-        UniqueId messageId = new UniqueId(messageIdBytes, 0);
-        
-        int fragmentIndex = 0;
-        int numFragments = 1;
-        
-        String base64Identity = "piYT1uJ3O8~bBPZmTvehMbp3-Zksg5enhvIlp2X8txqL25l0WdQMWwyt30UAOVQqxGdnMPTqqjh~-zoa~rCQORo~J1gRxLwCX9LlHQqaIimJilrbN-rhKy4Xlft054wbgQjLSC-WICE4W64KDfitwRzdr7lV6lz~0KFiZ8erZ-~WPMG1CgWEku9lILQUdUHyFBguPcK9oPDq7oGBuFGy8w0CvAq7ex3nmbL7zQVA~VqILtOGeGK2fidCuuofj4AQsTcXmH9O0nxZGCIJBhf~4EWmazvxu8XVB8pabNQvRDbmFu6q85JTwmxC45lCjqNw30hp8q2zoqP-zchjWOrxFUhSumpBdD0xXJR~qmhejh4WnuRnnam9j3fcxH5i~T7xWgmvIbpZEI4kyc9VEbXbLI7k-bU2A6sdP-AGt5~TjGLcxpdsPnOLRXO-Dsi7E9-3Kc84s4TmdpEJdtHn1dxYyeeT-ysVOqXjv5w5Cuk0XJpUIJG8n7aXHpNb-QLxPD3yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWF3qnAX-p41Po~VNmOUzS-Yt~noD8-e~L3P5rZXBWf-XtB4hkloo6m1jwqphEdf1";
-        identity = new EmailIdentity(base64Identity);
-
-        emailPacket = new EmailPacket(content, deletionKeyPlain, deletionKeyEncrypted, messageId, fragmentIndex, numFragments, identity);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-    }
-
-    @Test
-    public void toByteArrayAndBack() throws Exception {
-        byte[] arrayA = emailPacket.toByteArray();
-        byte[] arrayB = new EmailPacket(arrayA).toByteArray();
-        assertTrue("The two arrays differ!", Arrays.equals(arrayA, arrayB));
-    }
-    
-    @Test
-    public void testEncryptionDecryption() throws Exception {
-        emailPacket.setContent(null);
-        emailPacket.decrypt(identity);
-        byte[] arrayA = emailPacket.getContent();
-        byte[] arrayB = message.getBytes();
-        assertTrue("Email message differs after decryption!", Arrays.equals(arrayA, arrayB));
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/test/i2p/bote/packet/I2PBotePacketTest.java b/apps/i2pbote/test/i2p/bote/packet/I2PBotePacketTest.java
deleted file mode 100644
index ad49db64829205781a1e1a9f3e1355dfc1bb2bbf..0000000000000000000000000000000000000000
--- a/apps/i2pbote/test/i2p/bote/packet/I2PBotePacketTest.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package i2p.bote.packet;
-
-import static org.junit.Assert.assertTrue;
-
-import java.lang.reflect.Field;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class I2PBotePacketTest {
-
-    @Before
-    public void setUp() throws Exception {
-    }
-
-    @After
-    public void tearDown() throws Exception {
-    }
-
-    @Test
-    public void testDecodePacketTypeCode() throws Exception {
-        Field allPacketTypesField = I2PBotePacket.class.getDeclaredField("ALL_PACKET_TYPES");
-        allPacketTypesField.setAccessible(true);
-        Class<? extends I2PBotePacket>[] allPacketTypes = (Class<? extends I2PBotePacket>[])allPacketTypesField.get(null);
-        
-        for (Class<? extends I2PBotePacket> packetType: allPacketTypes) {
-            TypeCode typeCode = packetType.getAnnotation(TypeCode.class);
-            System.out.println("Testing decodePacketTypeCode on " + packetType);
-            assertTrue(I2PBotePacket.decodePacketTypeCode(typeCode.value()).equals(packetType));
-        }
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/test/i2p/bote/packet/ResponsePacketTest.java b/apps/i2pbote/test/i2p/bote/packet/ResponsePacketTest.java
deleted file mode 100644
index 9d604ebd6674e5b638eeb09d0aae02d71575a344..0000000000000000000000000000000000000000
--- a/apps/i2pbote/test/i2p/bote/packet/ResponsePacketTest.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package i2p.bote.packet;
-
-import static junit.framework.Assert.assertTrue;
-import i2p.bote.EmailIdentity;
-
-import java.security.NoSuchAlgorithmException;
-import java.util.Arrays;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class ResponsePacketTest {
-    private ResponsePacket responsePacket;
-
-    @Before
-    public void setUp() throws Exception {
-        byte[] packetIdBytes = new byte[] {120, 120, -8, -88, 21, 126, 46, -61, 18, -101, 15, 53, 20, -44, -112, 42, 86, -117, 30, -96, -66, 33, 71, -55, -102, -78, 78, -82, -105, 66, -116, 43};
-        UniqueId packetId = new UniqueId(packetIdBytes, 0);
-        
-        responsePacket = new ResponsePacket(createDataPacket(), StatusCode.OK, packetId);
-    }
-
-    private DataPacket createDataPacket() throws NoSuchAlgorithmException {
-        byte[] dataToStore = new byte[] {2, 109, -80, -37, -106, 83, -33, -39, -94, -76, -112, -98, 99, 25, -61, 44, -92, -85, 1, 10, -128, -2, -27, -86, -126, -33, -11, 42, 56, 3, -97, -101, 111, 7, -96, 25, 121, 74, 89, -40, -33, 82, -50, -18, 49, 106, 13, -121, 53, -83, -2, 35, -7, 71, -71, 26, 90, 1};
-        
-        UniqueId deletionKeyPlain = new UniqueId(new byte[] {72, -18, -72, -39, 122, 40, -104, -66, -54, -61, -108, 72, 54, 30, 37, 76, 44, 86, 104, -124, -31, 32, -82, -27, 26, 76, 7, 106, -76, 72, 49, -44}, 0);
-        UniqueId deletionKeyEncrypted = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
-        byte[] messageIdBytes = new byte[] {2, -69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123};
-        UniqueId messageId = new UniqueId(messageIdBytes, 0);
-        
-        String base64Identity = "piYT1uJ3O8~bBPZmTvehMbp3-Zksg5enhvIlp2X8txqL25l0WdQMWwyt30UAOVQqxGdnMPTqqjh~-zoa~rCQORo~J1gRxLwCX9LlHQqaIimJilrbN-rhKy4Xlft054wbgQjLSC-WICE4W64KDfitwRzdr7lV6lz~0KFiZ8erZ-~WPMG1CgWEku9lILQUdUHyFBguPcK9oPDq7oGBuFGy8w0CvAq7ex3nmbL7zQVA~VqILtOGeGK2fidCuuofj4AQsTcXmH9O0nxZGCIJBhf~4EWmazvxu8XVB8pabNQvRDbmFu6q85JTwmxC45lCjqNw30hp8q2zoqP-zchjWOrxFUhSumpBdD0xXJR~qmhejh4WnuRnnam9j3fcxH5i~T7xWgmvIbpZEI4kyc9VEbXbLI7k-bU2A6sdP-AGt5~TjGLcxpdsPnOLRXO-Dsi7E9-3Kc84s4TmdpEJdtHn1dxYyeeT-ysVOqXjv5w5Cuk0XJpUIJG8n7aXHpNb-QLxPD3yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWF3qnAX-p41Po~VNmOUzS-Yt~noD8-e~L3P5rZXBWf-XtB4hkloo6m1jwqphEdf1";
-        EmailIdentity identity = new EmailIdentity(base64Identity);
-
-        return new EmailPacket(dataToStore, deletionKeyPlain, deletionKeyEncrypted, messageId, 0, 1, identity);
-    }
-    
-    @After
-    public void tearDown() throws Exception {
-    }
-
-    @Test
-    public void toByteArrayAndBack() throws Exception {
-        byte[] arrayA = responsePacket.toByteArray();
-        byte[] arrayB = new ResponsePacket(arrayA).toByteArray();
-        assertTrue(Arrays.equals(arrayA, arrayB));
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/test/i2p/bote/packet/kademlia/FindCloseNodesPacketTest.java b/apps/i2pbote/test/i2p/bote/packet/kademlia/FindCloseNodesPacketTest.java
deleted file mode 100644
index 5773b01d913877d197708dec20ee9fd148cfe4df..0000000000000000000000000000000000000000
--- a/apps/i2pbote/test/i2p/bote/packet/kademlia/FindCloseNodesPacketTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package i2p.bote.packet.kademlia;
-
-
-import static junit.framework.Assert.assertTrue;
-
-import i2p.bote.packet.dht.FindClosePeersPacket;
-
-import java.util.Arrays;
-
-import net.i2p.data.Hash;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class FindCloseNodesPacketTest {
-    FindClosePeersPacket findCloseNodesPacket;
-
-    @Before
-    public void setUp() throws Exception {
-        Hash key = new Hash(new byte[] {-48, 78, 66, 58, -79, 87, 38, -103, -60, -27, 108, 55, 117, 37, -99, 93, -23, -102, -83, 20, 44, -80, 65, 89, -68, -73, 69, 51, 115, 79, 24, 127});
-        
-        findCloseNodesPacket = new FindClosePeersPacket(key);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-    }
-
-    @Test
-    public void toByteArrayAndBack() throws Exception {
-        byte[] arrayA = findCloseNodesPacket.toByteArray();
-        byte[] arrayB = new FindClosePeersPacket(arrayA).toByteArray();
-        assertTrue("The two arrays differ!", Arrays.equals(arrayA, arrayB));
-    }
-}
\ No newline at end of file
diff --git a/apps/i2pbote/test/i2p/bote/packet/kademlia/StorageRequestTest.java b/apps/i2pbote/test/i2p/bote/packet/kademlia/StorageRequestTest.java
deleted file mode 100644
index 172b1c5c3046335c3b9b8d884a7293f6f564e521..0000000000000000000000000000000000000000
--- a/apps/i2pbote/test/i2p/bote/packet/kademlia/StorageRequestTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package i2p.bote.packet.kademlia;
-
-import static junit.framework.Assert.assertTrue;
-import i2p.bote.EmailIdentity;
-import i2p.bote.packet.EmailPacket;
-import i2p.bote.packet.UniqueId;
-import i2p.bote.packet.dht.StoreRequest;
-
-import java.util.Arrays;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import com.nettgryppa.security.HashCash;
-
-public class StorageRequestTest {
-    StoreRequest storageRequest;
-
-    @Before
-    public void setUp() throws Exception {
-        byte[] emailContent = new byte[] {2, 109, -80, -37, -106, 83, -33, -39, -94, -76, -112, -98, 99, 25, -61, 44, -92, -85, 1, 10, -128, -2, -27, -86, -126, -33, -11, 42, 56, 3, -97, -101, 111, 7, -96, 25, 121, 74, 89, -40, -33, 82, -50, -18, 49, 106, 13, -121, 53, -83, -2, 35, -7, 71, -71, 26, 90, 1};
-        UniqueId deletionKeyPlain = new UniqueId(new byte[] {72, -18, -72, -39, 122, 40, -104, -66, -54, -61, -108, 72, 54, 30, 37, 76, 44, 86, 104, -124, -31, 32, -82, -27, 26, 76, 7, 106, -76, 72, 49, -44}, 0);
-        UniqueId deletionKeyEncrypted = new UniqueId(new byte[] {-62, -112, 99, -65, 13, 44, -117, -111, 96, 45, -6, 64, 78, 57, 117, 103, -24, 101, 106, -116, -18, 62, 99, -49, 60, -81, 8, 64, 27, -41, -104, 58}, 0);
-        UniqueId messageId = new UniqueId(new byte[] {2, -69, -24, -109, 1, 69, -122, -69, 113, -68, -90, 55, -28, 105, 97, 125, 70, 51, 58, 14, 2, -13, -53, 90, -29, 36, 67, 36, -94, -108, -125, 11, 123}, 0);
-        String base64Identity = "piYT1uJ3O8~bBPZmTvehMbp3-Zksg5enhvIlp2X8txqL25l0WdQMWwyt30UAOVQqxGdnMPTqqjh~-zoa~rCQORo~J1gRxLwCX9LlHQqaIimJilrbN-rhKy4Xlft054wbgQjLSC-WICE4W64KDfitwRzdr7lV6lz~0KFiZ8erZ-~WPMG1CgWEku9lILQUdUHyFBguPcK9oPDq7oGBuFGy8w0CvAq7ex3nmbL7zQVA~VqILtOGeGK2fidCuuofj4AQsTcXmH9O0nxZGCIJBhf~4EWmazvxu8XVB8pabNQvRDbmFu6q85JTwmxC45lCjqNw30hp8q2zoqP-zchjWOrxFUhSumpBdD0xXJR~qmhejh4WnuRnnam9j3fcxH5i~T7xWgmvIbpZEI4kyc9VEbXbLI7k-bU2A6sdP-AGt5~TjGLcxpdsPnOLRXO-Dsi7E9-3Kc84s4TmdpEJdtHn1dxYyeeT-ysVOqXjv5w5Cuk0XJpUIJG8n7aXHpNb-QLxPD3yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADWF3qnAX-p41Po~VNmOUzS-Yt~noD8-e~L3P5rZXBWf-XtB4hkloo6m1jwqphEdf1";
-        EmailIdentity identity = new EmailIdentity(base64Identity);
-        EmailPacket emailPacket = new EmailPacket(emailContent, deletionKeyPlain, deletionKeyEncrypted, messageId, 0, 1, identity);
-        
-        HashCash hashCash = HashCash.mintCash("1234", 1);
-        
-        storageRequest = new StoreRequest(hashCash, emailPacket);
-    }
-
-    @After
-    public void tearDown() throws Exception {
-    }
-
-    @Test
-    public void toByteArrayAndBack() throws Exception {
-        byte[] arrayA = storageRequest.toByteArray();
-        byte[] arrayB = new StoreRequest(arrayA).toByteArray();
-        assertTrue(Arrays.equals(arrayA, arrayB));
-    }
-}
\ No newline at end of file