Author: toad
Date: 2007-06-28 17:07:40 +0000 (Thu, 28 Jun 2007)
New Revision: 13809

Added:
   trunk/freenet/src/freenet/clients/http/ConnectionsToadlet.java
   trunk/freenet/src/freenet/node/DarknetPeerNode.java
   trunk/freenet/src/freenet/node/DarknetPeerNodeStatus.java
Modified:
   trunk/freenet/src/freenet/clients/http/DarknetConnectionsToadlet.java
   trunk/freenet/src/freenet/clients/http/N2NTMToadlet.java
   trunk/freenet/src/freenet/clients/http/StatisticsToadlet.java
   trunk/freenet/src/freenet/node/FNPPacketMangler.java
   trunk/freenet/src/freenet/node/Node.java
   trunk/freenet/src/freenet/node/NodeIPDetector.java
   trunk/freenet/src/freenet/node/NodeStats.java
   trunk/freenet/src/freenet/node/PacketSender.java
   trunk/freenet/src/freenet/node/PeerManager.java
   trunk/freenet/src/freenet/node/PeerNode.java
   trunk/freenet/src/freenet/node/PeerNodeStatus.java
   trunk/freenet/src/freenet/node/TextModeClientInterface.java
   trunk/freenet/src/freenet/node/fcp/ListPeerNotesMessage.java
   trunk/freenet/src/freenet/node/fcp/ModifyPeer.java
   trunk/freenet/src/freenet/node/fcp/ModifyPeerNote.java
   trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
   trunk/freenet/src/freenet/node/useralerts/N2NTMUserAlert.java
Log:
PeerNode -> PeerNode + DarknetPeerNode
DarknetConnectionsToadlet -> ConnectionsToadlet + DarknetConnectionsToadlet

Added: trunk/freenet/src/freenet/clients/http/ConnectionsToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/ConnectionsToadlet.java              
                (rev 0)
+++ trunk/freenet/src/freenet/clients/http/ConnectionsToadlet.java      
2007-06-28 17:07:40 UTC (rev 13809)
@@ -0,0 +1,647 @@
+package freenet.clients.http;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.net.URI;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import freenet.client.HighLevelSimpleClient;
+import freenet.io.xfer.PacketThrottle;
+import freenet.l10n.L10n;
+import freenet.node.DarknetPeerNodeStatus;
+import freenet.node.FSParseException;
+import freenet.node.Node;
+import freenet.node.NodeClientCore;
+import freenet.node.NodeStats;
+import freenet.node.PeerManager;
+import freenet.node.PeerNodeStatus;
+import freenet.node.Version;
+import freenet.support.HTMLNode;
+import freenet.support.MultiValueTable;
+import freenet.support.SimpleFieldSet;
+import freenet.support.SizeUtil;
+import freenet.support.TimeUtil;
+import freenet.support.api.HTTPRequest;
+
+public abstract class ConnectionsToadlet extends Toadlet {
+
+       protected final Node node;
+       protected final NodeClientCore core;
+       protected final NodeStats stats;
+       protected final PeerManager peers;
+       protected boolean isReversed = false;
+       protected final DecimalFormat fix1 = new DecimalFormat("##0.0%");
+       
+       public String supportedMethods() {
+               return "GET";
+       }
+
+       protected ConnectionsToadlet(Node n, NodeClientCore core, 
HighLevelSimpleClient client) {
+               super(client);
+               this.node = n;
+               this.core = core;
+               this.stats = n.nodeStats;
+               this.peers = n.peers;
+       }
+
+       public void handleGet(URI uri, final HTTPRequest request, 
ToadletContext ctx) throws ToadletContextClosedException, IOException, 
RedirectException {
+               String path = uri.getPath();
+               if(path.endsWith("myref.fref")) {
+                       SimpleFieldSet fs = node.exportPublicFieldSet();
+                       StringWriter sw = new StringWriter();
+                       fs.writeTo(sw);
+                       MultiValueTable extraHeaders = new MultiValueTable();
+                       // Force download to disk
+                       extraHeaders.put("Content-Disposition", "attachment; 
filename=myref.fref");
+                       this.writeReply(ctx, 200, 
"application/x-freenet-reference", "OK", extraHeaders, sw.toString());
+                       return;
+               }
+
+               if(path.endsWith("myref.txt")) {
+                       SimpleFieldSet fs = node.exportPublicFieldSet();
+                       StringWriter sw = new StringWriter();
+                       fs.writeTo(sw);
+                       this.writeReply(ctx, 200, "text/plain", "OK", 
sw.toString());
+                       return;
+               }
+               
+               if(!ctx.isAllowedFullAccess()) {
+                       super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
+                       return;
+               }
+               
+               final boolean advancedModeEnabled = 
node.isAdvancedModeEnabled();
+               final boolean fProxyJavascriptEnabled = 
node.isFProxyJavascriptEnabled();
+               
+               /* gather connection statistics */
+               DarknetPeerNodeStatus[] peerNodeStatuses = 
peers.getDarknetPeerNodeStatuses();
+               Arrays.sort(peerNodeStatuses, new Comparator() {
+                       public int compare(Object first, Object second) {
+                               int result = 0;
+                               boolean isSet = true;
+                               DarknetPeerNodeStatus firstNode = 
(DarknetPeerNodeStatus) first;
+                               DarknetPeerNodeStatus secondNode = 
(DarknetPeerNodeStatus) second;
+                               
+                               if(request.isParameterSet("sortBy")){
+                                       final String sortBy = 
request.getParam("sortBy"); 
+
+                                       if(sortBy.equals("name")){
+                                               result = 
firstNode.getName().compareToIgnoreCase(secondNode.getName());
+                                       }else if(sortBy.equals("address")){
+                                               result = 
firstNode.getPeerAddress().compareToIgnoreCase(secondNode.getPeerAddress());
+                                       }else if(sortBy.equals("location")){
+                                               result = 
(firstNode.getLocation() - secondNode.getLocation()) < 0 ? -1 : 1; // Shouldn't 
be equal anyway
+                                       }else if(sortBy.equals("version")){
+                                               result = 
Version.getArbitraryBuildNumber(firstNode.getVersion()) - 
Version.getArbitraryBuildNumber(secondNode.getVersion());
+                                       }else if(sortBy.equals("privnote")){
+                                               result = 
firstNode.getPrivateDarknetCommentNote().compareToIgnoreCase(secondNode.getPrivateDarknetCommentNote());
+                                       }else
+                                               isSet=false;
+                               }else
+                                       isSet=false;
+                               
+                               if(!isSet){
+                                       int statusDifference = 
firstNode.getStatusValue() - secondNode.getStatusValue();
+                                       if (statusDifference != 0) 
+                                               result = (statusDifference < 0 
? -1 : 1);
+                                       else
+                                               result = 
firstNode.getName().compareToIgnoreCase(secondNode.getName());
+                               }
+
+                               if(result == 0){
+                                       return 0;
+                               }else if(request.isParameterSet("reversed")){
+                                       isReversed = true;
+                                       return result > 0 ? -1 : 1;
+                               }else{
+                                       isReversed = false;
+                                       return result < 0 ? -1 : 1;
+                               }
+                       }
+               });
+               
+               int numberOfConnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_CONNECTED);
+               int numberOfRoutingBackedOff = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF);
+               int numberOfTooNew = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_TOO_NEW);
+               int numberOfTooOld = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_TOO_OLD);
+               int numberOfDisconnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISCONNECTED);
+               int numberOfNeverConnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED);
+               int numberOfDisabled = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISABLED);
+               int numberOfBursting = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_BURSTING);
+               int numberOfListening = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_LISTENING);
+               int numberOfListenOnly = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_LISTEN_ONLY);
+               int numberOfClockProblem = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_CLOCK_PROBLEM);
+               
+               int numberOfSimpleConnected = numberOfConnected + 
numberOfRoutingBackedOff;
+               int numberOfNotConnected = numberOfTooNew + numberOfTooOld + 
numberOfDisconnected + numberOfNeverConnected + numberOfDisabled + 
numberOfBursting + numberOfListening + numberOfListenOnly;
+               String titleCountString = null;
+               if(advancedModeEnabled) {
+                       titleCountString = "(" + numberOfConnected + '/' + 
numberOfRoutingBackedOff + '/' + numberOfTooNew + '/' + numberOfTooOld + '/' + 
numberOfNotConnected + ')';
+               } else {
+                       titleCountString = (numberOfNotConnected + 
numberOfSimpleConnected)>0 ? String.valueOf(numberOfSimpleConnected) : "";
+               }
+               
+               HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(L10n.getString("DarknetConnectionsToadlet.fullTitle",
 new String[] { "counts", "name" }, new String[] { titleCountString, 
node.getMyName() } ), ctx);
+               HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
+               
+               // FIXME! We need some nice images
+               long now = System.currentTimeMillis();
+       
+               if(ctx.isAllowedFullAccess())
+                       contentNode.addChild(core.alerts.createSummary());
+               
+               if(peerNodeStatuses.length>0){
+
+                       /* node status values */
+                       long nodeUptimeSeconds = (now - node.startupTime) / 
1000;
+                       int bwlimitDelayTime = (int) 
stats.getBwlimitDelayTime();
+                       int nodeAveragePingTime = (int) 
stats.getNodeAveragePingTime();
+                       int networkSizeEstimateSession = 
stats.getNetworkSizeEstimate(-1);
+                       int networkSizeEstimateRecent = 0;
+                       if(nodeUptimeSeconds > (48*60*60)) {  // 48 hours
+                               networkSizeEstimateRecent = 
stats.getNetworkSizeEstimate(now - (48*60*60*1000));  // 48 hours
+                       }
+                       DecimalFormat fix4 = new DecimalFormat("0.0000");
+                       double routingMissDistance =  
stats.routingMissDistance.currentValue();
+                       double backedOffPercent =  
stats.backedOffPercent.currentValue();
+                       String nodeUptimeString = 
TimeUtil.formatTime(nodeUptimeSeconds * 1000);  // *1000 to convert to 
milliseconds
+
+                       // BEGIN OVERVIEW TABLE
+                       HTMLNode overviewTable = contentNode.addChild("table", 
"class", "column");
+                       HTMLNode overviewTableRow = 
overviewTable.addChild("tr");
+                       HTMLNode nextTableCell = 
overviewTableRow.addChild("td", "class", "first");
+
+                       /* node status overview box */
+                       if(advancedModeEnabled) {
+                               HTMLNode overviewInfobox = 
nextTableCell.addChild("div", "class", "infobox");
+                               overviewInfobox.addChild("div", "class", 
"infobox-header", "Node status overview");
+                               HTMLNode overviewInfoboxContent = 
overviewInfobox.addChild("div", "class", "infobox-content");
+                               HTMLNode overviewList = 
overviewInfoboxContent.addChild("ul");
+                               overviewList.addChild("li", 
"bwlimitDelayTime:\u00a0" + bwlimitDelayTime + "ms");
+                               overviewList.addChild("li", 
"nodeAveragePingTime:\u00a0" + nodeAveragePingTime + "ms");
+                               overviewList.addChild("li", 
"networkSizeEstimateSession:\u00a0" + networkSizeEstimateSession + 
"\u00a0nodes");
+                               if(nodeUptimeSeconds > (48*60*60)) {  // 48 
hours
+                                       overviewList.addChild("li", 
"networkSizeEstimateRecent:\u00a0" + networkSizeEstimateRecent + "\u00a0nodes");
+                               }
+                               overviewList.addChild("li", "nodeUptime:\u00a0" 
+ nodeUptimeString);
+                               overviewList.addChild("li", 
"routingMissDistance:\u00a0" + fix4.format(routingMissDistance));
+                               overviewList.addChild("li", 
"backedOffPercent:\u00a0" + fix1.format(backedOffPercent));
+                               overviewList.addChild("li", 
"pInstantReject:\u00a0" + fix1.format(stats.pRejectIncomingInstantly()));
+                               nextTableCell = overviewTableRow.addChild("td");
+                       }
+
+                       // Activity box
+                       int numARKFetchers = node.getNumARKFetchers();
+
+                       HTMLNode activityInfobox = 
nextTableCell.addChild("div", "class", "infobox");
+                       activityInfobox.addChild("div", "class", 
"infobox-header", l10n("activityTitle"));
+                       HTMLNode activityInfoboxContent = 
activityInfobox.addChild("div", "class", "infobox-content");
+                       HTMLNode activityList = 
StatisticsToadlet.drawActivity(activityInfoboxContent, node);
+                       if (advancedModeEnabled && activityList != null) {
+                               if (numARKFetchers > 0) {
+                                       activityList.addChild("li", 
"ARK\u00a0Fetch\u00a0Requests:\u00a0" + numARKFetchers);
+                               }
+                               StatisticsToadlet.drawBandwidth(activityList, 
node, nodeUptimeSeconds);
+                       }
+
+                       nextTableCell = advancedModeEnabled ? 
overviewTableRow.addChild("td") : overviewTableRow.addChild("td", "class", 
"last");
+
+                       // Peer statistics box
+                       HTMLNode peerStatsInfobox = 
nextTableCell.addChild("div", "class", "infobox");
+                       peerStatsInfobox.addChild("div", "class", 
"infobox-header", l10nStats("peerStatsTitle"));
+                       HTMLNode peerStatsContent = 
peerStatsInfobox.addChild("div", "class", "infobox-content");
+                       HTMLNode peerStatsList = 
peerStatsContent.addChild("ul");
+                       if (numberOfConnected > 0) {
+                               HTMLNode peerStatsConnectedListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsConnectedListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_connected", 
l10n("connected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("connectedShort"));
+                               peerStatsConnectedListItem.addChild("span", 
":\u00a0" + numberOfConnected);
+                       }
+                       if (numberOfRoutingBackedOff > 0) {
+                               HTMLNode peerStatsRoutingBackedOffListItem = 
peerStatsList.addChild("li").addChild("span");
+                               
peerStatsRoutingBackedOffListItem.addChild("span", new String[] { "class", 
"title", "style" }, new String[] { "peer_backed_off", (advancedModeEnabled ? 
l10n("backedOff") : l10n("busy")), "border-bottom: 1px dotted; cursor: help;" 
}, advancedModeEnabled ? l10n("backedOffShort") : l10n("busyShort"));
+                               
peerStatsRoutingBackedOffListItem.addChild("span", ":\u00a0" + 
numberOfRoutingBackedOff);
+                       }
+                       if (numberOfTooNew > 0) {
+                               HTMLNode peerStatsTooNewListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsTooNewListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_too_new", 
l10n("tooNew"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("tooNewShort"));
+                               peerStatsTooNewListItem.addChild("span", 
":\u00a0" + numberOfTooNew);
+                       }
+                       if (numberOfTooOld > 0) {
+                               HTMLNode peerStatsTooOldListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsTooOldListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_too_old", 
l10n("tooOld"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("tooOldShort"));
+                               peerStatsTooOldListItem.addChild("span", 
":\u00a0" + numberOfTooOld);
+                       }
+                       if (numberOfDisconnected > 0) {
+                               HTMLNode peerStatsDisconnectedListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsDisconnectedListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { "peer_disconnected", 
l10n("notConnected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("notConnectedShort"));
+                               peerStatsDisconnectedListItem.addChild("span", 
":\u00a0" + numberOfDisconnected);
+                       }
+                       if (numberOfNeverConnected > 0) {
+                               HTMLNode peerStatsNeverConnectedListItem = 
peerStatsList.addChild("li").addChild("span");
+                               
peerStatsNeverConnectedListItem.addChild("span", new String[] { "class", 
"title", "style" }, new String[] { "peer_never_connected", 
l10n("neverConnected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("neverConnectedShort"));
+                               
peerStatsNeverConnectedListItem.addChild("span", ":\u00a0" + 
numberOfNeverConnected);
+                       }
+                       if (numberOfDisabled > 0) {
+                               HTMLNode peerStatsDisabledListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsDisabledListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_disabled", 
l10n("disabled"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("disabledShort"));
+                               peerStatsDisabledListItem.addChild("span", 
":\u00a0" + numberOfDisabled);
+                       }
+                       if (numberOfBursting > 0) {
+                               HTMLNode peerStatsBurstingListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsBurstingListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_bursting", 
l10n("bursting"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("burstingShort"));
+                               peerStatsBurstingListItem.addChild("span", 
":\u00a0" + numberOfBursting);
+                       }
+                       if (numberOfListening > 0) {
+                               HTMLNode peerStatsListeningListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsListeningListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_listening", 
l10n("listening"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("listeningShort"));
+                               peerStatsListeningListItem.addChild("span", 
":\u00a0" + numberOfListening);
+                       }
+                       if (numberOfListenOnly > 0) {
+                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { "peer_listen_only", 
l10n("listenOnly"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("listenOnlyShort"));
+                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfListenOnly);
+                       }
+                       if (numberOfClockProblem > 0) {
+                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
+                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { 
"peer_clock_problem", l10n("clockProblem"), "border-bottom: 1px dotted; cursor: 
help;" }, l10n("clockProblemShort"));
+                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfClockProblem);
+                       }
+
+                       // Peer routing backoff reason box
+                       if(advancedModeEnabled) {
+                               nextTableCell = overviewTableRow.addChild("td", 
"class", "last");
+                               HTMLNode backoffReasonInfobox = 
nextTableCell.addChild("div", "class", "infobox");
+                               backoffReasonInfobox.addChild("div", "class", 
"infobox-header", "Peer backoff reasons");
+                               HTMLNode backoffReasonContent = 
backoffReasonInfobox.addChild("div", "class", "infobox-content");
+                               String [] routingBackoffReasons = 
peers.getPeerNodeRoutingBackoffReasons();
+                               if(routingBackoffReasons.length == 0) {
+                                       backoffReasonContent.addChild("#", 
"Good, your node is not backed off from any peers!");
+                               } else {
+                                       HTMLNode reasonList = 
backoffReasonContent.addChild("ul");
+                                       for(int 
i=0;i<routingBackoffReasons.length;i++) {
+                                               int reasonCount = 
peers.getPeerNodeRoutingBackoffReasonSize(routingBackoffReasons[i]);
+                                               if(reasonCount > 0) {
+                                                       
reasonList.addChild("li", routingBackoffReasons[i] + '\u00a0' + reasonCount);
+                                               }
+                                       }
+                               }
+                       }
+                       // END OVERVIEW TABLE
+
+                       // BEGIN PEER TABLE
+                       if(fProxyJavascriptEnabled) {
+                               StringBuffer jsBuf = new StringBuffer();
+                               // FIXME: There's probably some icky Javascript 
in here (this is the first thing that worked for me); feel free to fix up to 
Javascript guru standards
+                               jsBuf.append( "  function peerNoteChange() {\n" 
);
+                               jsBuf.append( "    var theobj = 
document.getElementById( \"action\" );\n" );
+                               jsBuf.append( "    var length = 
theobj.options.length;\n" );
+                               jsBuf.append( "    for (var i = 0; i < length; 
i++) {\n" );
+                               jsBuf.append( "      if(theobj.options[i] == 
\"update_notes\") {\n" );
+                               jsBuf.append( "        theobj.options[i].select 
= true;\n" );
+                               jsBuf.append( "      } else {\n" );
+                               jsBuf.append( "        theobj.options[i].select 
= false;\n" );
+                               jsBuf.append( "      }\n" );
+                               jsBuf.append( "    }\n" );
+                               jsBuf.append( "    
theobj.value=\"update_notes\";\n" );
+                               //jsBuf.append( "    document.getElementById( 
\"peersForm\" ).submit();\n" );
+                               jsBuf.append( "    document.getElementById( 
\"peersForm\" ).doAction.click();\n" );
+                               jsBuf.append( "  }\n" );
+                               jsBuf.append( "  function peerNoteBlur() {\n" );
+                               jsBuf.append( "    var theobj = 
document.getElementById( \"action\" );\n" );
+                               jsBuf.append( "    var length = 
theobj.options.length;\n" );
+                               jsBuf.append( "    for (var i = 0; i < length; 
i++) {\n" );
+                               jsBuf.append( "      if(theobj.options[i] == 
\"update_notes\") {\n" );
+                               jsBuf.append( "        theobj.options[i].select 
= true;\n" );
+                               jsBuf.append( "      } else {\n" );
+                               jsBuf.append( "        theobj.options[i].select 
= false;\n" );
+                               jsBuf.append( "      }\n" );
+                               jsBuf.append( "    }\n" );
+                               jsBuf.append( "    
theobj.value=\"update_notes\";\n" );
+                               jsBuf.append( "  }\n" );
+                               contentNode.addChild("script", "type", 
"text/javascript").addChild("%", jsBuf.toString());
+                       }
+                       HTMLNode peerTableInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
+                       HTMLNode peerTableInfoboxHeader = 
peerTableInfobox.addChild("div", "class", "infobox-header");
+                       peerTableInfoboxHeader.addChild("#", l10n("myFriends"));
+                       if (advancedModeEnabled) {
+                               if (!path.endsWith("displaymessagetypes.html")) 
{
+                                       peerTableInfoboxHeader.addChild("#", " 
");
+                                       peerTableInfoboxHeader.addChild("a", 
"href", "displaymessagetypes.html", "(more detailed)");
+                               }
+                       }
+                       HTMLNode peerTableInfoboxContent = 
peerTableInfobox.addChild("div", "class", "infobox-content");
+
+                       if (peerNodeStatuses.length == 0) {
+                               
L10n.addL10nSubstitution(peerTableInfoboxContent, 
"DarknetConnectionsToadlet.noPeersWithHomepageLink", 
+                                               new String[] { "link", "/link" 
}, new String[] { "<a href=\"/\">", "</a>" });
+                       } else {
+                               HTMLNode peerForm = 
ctx.addFormChild(peerTableInfoboxContent, ".", "peersForm");
+                               HTMLNode peerTable = peerForm.addChild("table", 
"class", "darknet_connections");
+                               HTMLNode peerTableHeaderRow = 
peerTable.addChild("tr");
+                               peerTableHeaderRow.addChild("th");
+                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "status")).addChild("#", l10n("statusTitle"));
+                               if(hasNameColumn())
+                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"name")).addChild("span", new String[] { "title", "style" }, new String[] { 
l10n("nameClickToMessage"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("nameTitle"));
+                               if (advancedModeEnabled) {
+                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"address")).addChild("span", new String[] { "title", "style" }, new String[] { 
l10n("ipAddress"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("ipAddressTitle"));
+                               }
+                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "version")).addChild("#", l10n("versionTitle"));
+                               if (advancedModeEnabled) {
+                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"location")).addChild("#", "Location");
+                                       
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { "Other node busy? Display: Percentage of time the 
node is overloaded, Current wait time remaining (0=not overloaded)/total/last 
overload reason", "border-bottom: 1px dotted; cursor: help;" }, "Backoff");
+
+                                       
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { "Probability of the node rejecting a request due to 
overload or causing a timeout.", "border-bottom: 1px dotted; cursor: help;" }, 
"Overload Probability");
+                               }
+                               
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { l10n("idleTime"), "border-bottom: 1px dotted; cursor: 
help;" }, l10n("idleTimeTitle"));
+                               if(hasPrivateNoteColumn())
+                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"privnote")).addChild("span", new String[] { "title", "style" }, new String[] { 
l10n("privateNote"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("privateNoteTitle"));
+
+                               if(advancedModeEnabled) {
+                                       peerTableHeaderRow.addChild("th", 
"%\u00a0Time Routable");
+                                       peerTableHeaderRow.addChild("th", 
"Total\u00a0Traffic\u00a0(in/out)");
+                                       peerTableHeaderRow.addChild("th", 
"Congestion\u00a0Control");
+                                       peerTableHeaderRow.addChild("th", 
"Time\u00a0Delta");
+                               }
+                               
+                               for (int peerIndex = 0, peerCount = 
peerNodeStatuses.length; peerIndex < peerCount; peerIndex++) {
+                                       
+                                       PeerNodeStatus peerNodeStatus = 
peerNodeStatuses[peerIndex];
+                                       drawRow(peerTable, peerNodeStatus, 
advancedModeEnabled, fProxyJavascriptEnabled, now, path);
+                                       
+                               }
+
+                               HTMLNode actionSelect = 
peerForm.addChild("select", new String[] { "id", "name" }, new String[] { 
"action", "action" });
+                               actionSelect.addChild("option", "value", "", 
l10n("selectAction"));
+                               actionSelect.addChild("option", "value", 
"send_n2ntm", l10n("sendMessageToPeers"));
+                               actionSelect.addChild("option", "value", 
"update_notes", l10n("updateChangedPrivnotes"));
+                               if(advancedModeEnabled) {
+                                       actionSelect.addChild("option", 
"value", "enable", "Enable selected peers");
+                                       actionSelect.addChild("option", 
"value", "disable", "Disable selected peers");
+                                       actionSelect.addChild("option", 
"value", "set_burst_only", "On selected peers, set BurstOnly (only set this if 
you have a static IP and are not NATed and neither is the peer)");
+                                       actionSelect.addChild("option", 
"value", "clear_burst_only", "On selected peers, clear BurstOnly");
+                                       actionSelect.addChild("option", 
"value", "set_listen_only", "On selected peers, set ListenOnly (not 
recommended)");
+                                       actionSelect.addChild("option", 
"value", "clear_listen_only", "On selected peers, clear ListenOnly");
+                                       actionSelect.addChild("option", 
"value", "set_allow_local", "On selected peers, set allowLocalAddresses (useful 
if you are connecting to another node on the same LAN)");
+                                       actionSelect.addChild("option", 
"value", "clear_allow_local", "On selected peers, clear allowLocalAddresses");
+                                       actionSelect.addChild("option", 
"value", "set_ignore_source_port", "On selected peers, set ignoreSourcePort 
(try this if behind an evil corporate firewall; otherwise not recommended)");
+                                       actionSelect.addChild("option", 
"value", "clear_ignore_source_port", "On selected peers, clear 
ignoreSourcePort");
+                               }
+                               actionSelect.addChild("option", "value", "", 
l10n("separator"));
+                               actionSelect.addChild("option", "value", 
"remove", l10n("removePeers"));
+                               peerForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "doAction", l10n("go") });
+                               
+                       }
+                       // END PEER TABLE
+               }
+
+               // BEGIN PEER ADDITION BOX
+               HTMLNode peerAdditionInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
+               peerAdditionInfobox.addChild("div", "class", "infobox-header", 
l10n("addPeerTitle"));
+               HTMLNode peerAdditionContent = 
peerAdditionInfobox.addChild("div", "class", "infobox-content");
+               HTMLNode peerAdditionForm = 
ctx.addFormChild(peerAdditionContent, ".", "addPeerForm");
+               peerAdditionForm.addChild("#", l10n("pasteReference"));
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("textarea", new String[] { "id", 
"name", "rows", "cols" }, new String[] { "reftext", "ref", "8", "74" });
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("#", (l10n("urlReference") + ' '));
+               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "refurl", "text", "url" });
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("#", (l10n("fileReference") + ' '));
+               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "reffile", "file", "reffile" });
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("#", (l10n("enterDescription") + ' 
'));
+               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name", "size", "maxlength", "value" }, new String[] { "peerPrivateNote", 
"text", "peerPrivateNote", "16", "250", "" });
+               peerAdditionForm.addChild("br");
+               peerAdditionForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "add", l10n("add") });
+               
+               // our reference
+               HTMLNode referenceInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
+               HTMLNode headerReferenceInfobox = 
referenceInfobox.addChild("div", "class", "infobox-header");
+               // FIXME better way to deal with this sort of thing???
+               L10n.addL10nSubstitution(headerReferenceInfobox, 
"DarknetConnectionsToadlet.myReferenceHeader",
+                               new String[] { "linkref", "/linkref", 
"linktext", "/linktext" },
+                               new String[] { "<a href=\"myref.fref\">", 
"</a>", "<a href=\"myref.txt\">", "</a>" });
+               HTMLNode warningSentence = 
headerReferenceInfobox.addChild("pre");
+               L10n.addL10nSubstitution(warningSentence, 
"DarknetConnectionsToadlet.referenceCopyWarning",
+                               new String[] { "bold", "/bold" },
+                               new String[] { "<b>", "</b>" });
+               referenceInfobox.addChild("div", "class", 
"infobox-content").addChild("pre", "id", "reference", 
node.exportPublicFieldSet().toString() + '\n');
+               
+               // our ports
+               HTMLNode portInfobox = contentNode.addChild("div", "class", 
"infobox infobox-normal");
+               portInfobox.addChild("div", "class", "infobox-header", 
l10n("nodePortsTitle"));
+               HTMLNode portInfoboxContent = portInfobox.addChild("div", 
"class", "infobox-content");
+               HTMLNode portInfoList = portInfoboxContent.addChild("ul");
+               SimpleFieldSet fproxyConfig = 
node.config.get("fproxy").exportFieldSet(true);
+               SimpleFieldSet fcpConfig = 
node.config.get("fcp").exportFieldSet(true);
+               SimpleFieldSet tmciConfig = 
node.config.get("console").exportFieldSet(true);
+               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fnpPort", new String[] { "port" }, 
new String[] { Integer.toString(node.getFNPPort()) }));
+               try {
+                       if(fproxyConfig.getBoolean("enabled", false)) {
+                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fproxyPort", new String[] { "port" }, 
new String[] { Integer.toString(fproxyConfig.getInt("port")) }));
+                       } else {
+                               portInfoList.addChild("li", 
l10n("fproxyDisabled"));
+                       }
+                       if(fcpConfig.getBoolean("enabled", false)) {
+                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fcpPort", new String[] { "port" }, 
new String[] { Integer.toString(fcpConfig.getInt("port")) }));
+                       } else {
+                               portInfoList.addChild("li", 
l10n("fcpDisabled"));
+                       }
+                       if(tmciConfig.getBoolean("enabled", false)) {
+                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.tmciPort", new String[] { "port" }, 
new String[] { Integer.toString(tmciConfig.getInt("port")) }));
+                       } else {
+                               portInfoList.addChild("li", 
l10n("tmciDisabled"));
+                       }
+               } catch (FSParseException e) {
+                       // ignore
+               }
+               
+               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
+       }
+       
+       
+       private void drawRow(HTMLNode peerTable, PeerNodeStatus peerNodeStatus, 
boolean advancedModeEnabled, boolean fProxyJavascriptEnabled, long now, String 
path) {
+               HTMLNode peerRow = peerTable.addChild("tr");
+
+               // check box column
+               peerRow.addChild("td", "class", 
"peer-marker").addChild("input", new String[] { "type", "name" }, new String[] 
{ "checkbox", "node_" + peerNodeStatus.hashCode() });
+
+               // status column
+               String statusString = peerNodeStatus.getStatusName();
+               if (!advancedModeEnabled && (peerNodeStatus.getStatusValue() == 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF)) {
+                       statusString = "BUSY";
+               }
+               peerRow.addChild("td", "class", "peer-status").addChild("span", 
"class", peerNodeStatus.getStatusCSSName(), statusString + 
(peerNodeStatus.isFetchingARK() ? "*" : ""));
+
+               drawNameColumn(peerRow, peerNodeStatus);
+               
+               // address column
+               if (advancedModeEnabled) {
+                       String pingTime = "";
+                       if (peerNodeStatus.isConnected()) {
+                               pingTime = " (" + (int) 
peerNodeStatus.getAveragePingTime() + "ms)";
+                       }
+                       peerRow.addChild("td", "class", 
"peer-address").addChild("#", ((peerNodeStatus.getPeerAddress() != null) ? 
(peerNodeStatus.getPeerAddress() + ':' + peerNodeStatus.getPeerPort()) : 
(l10n("unknownAddress"))) + pingTime);
+               }
+
+               // version column
+               if (peerNodeStatus.getStatusValue() != 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED && 
(peerNodeStatus.isPublicInvalidVersion() || 
peerNodeStatus.isPublicReverseInvalidVersion())) {  // Don't draw attention to 
a version problem if NEVER CONNECTED
+                       peerRow.addChild("td", "class", 
"peer-version").addChild("span", "class", "peer_version_problem", 
peerNodeStatus.getSimpleVersion());
+               } else {
+                       peerRow.addChild("td", "class", 
"peer-version").addChild("#", peerNodeStatus.getSimpleVersion());
+               }
+
+               // location column
+               if (advancedModeEnabled) {
+                       peerRow.addChild("td", "class", "peer-location", 
String.valueOf(peerNodeStatus.getLocation()));
+               }
+
+               if (advancedModeEnabled) {
+                       // backoff column
+                       HTMLNode backoffCell = peerRow.addChild("td", "class", 
"peer-backoff");
+                       backoffCell.addChild("#", 
fix1.format(peerNodeStatus.getBackedOffPercent()));
+                       int backoff = (int) 
(Math.max(peerNodeStatus.getRoutingBackedOffUntil() - now, 0));
+                       // Don't list the backoff as zero before it's actually 
zero
+                       if ((backoff > 0) && (backoff < 1000)) {
+                               backoff = 1000;
+                       }
+                       backoffCell.addChild("#", ' ' + String.valueOf(backoff 
/ 1000) + '/' + String.valueOf(peerNodeStatus.getRoutingBackoffLength() / 
1000));
+                       backoffCell.addChild("#", 
(peerNodeStatus.getLastBackoffReason() == null) ? "" : ('/' + 
(peerNodeStatus.getLastBackoffReason())));
+
+                       // overload probability column
+                       HTMLNode pRejectCell = peerRow.addChild("td", "class", 
"peer-backoff"); // FIXME
+                       pRejectCell.addChild("#", 
fix1.format(peerNodeStatus.getPReject()));
+               }
+
+               // idle column
+               long idle = peerNodeStatus.getTimeLastRoutable();
+               if (peerNodeStatus.isRoutable()) {
+                       idle = peerNodeStatus.getTimeLastConnectionCompleted();
+               } else if (peerNodeStatus.getStatusValue() == 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED) {
+                       idle = peerNodeStatus.getPeerAddedTime();
+               }
+               if(!peerNodeStatus.isConnected() && (now - idle) > (2 * 7 * 24 
* 60 * 60 * (long) 1000)) { // 2 weeks
+                       peerRow.addChild("td", "class", 
"peer-idle").addChild("span", "class", "peer_idle_old", idleToString(now, 
idle));
+               } else {
+                       peerRow.addChild("td", "class", "peer-idle", 
idleToString(now, idle));
+               }
+
+               if(hasPrivateNoteColumn())
+                       drawPrivateNoteColumn(peerRow, peerNodeStatus, 
fProxyJavascriptEnabled);
+
+               if(advancedModeEnabled) {
+                       // percent of time connected column
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", 
fix1.format(peerNodeStatus.getPercentTimeRoutableConnection()));
+                       // total traffic column
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", SizeUtil.formatSize(peerNodeStatus.getTotalInputBytes())+" / 
"+SizeUtil.formatSize(peerNodeStatus.getTotalOutputBytes()));
+                       // congestion control
+                       PacketThrottle t = peerNodeStatus.getThrottle();
+                       String val;
+                       if(t == null)
+                               val = "none";
+                       else
+                               val = (int)((1000.0 / t.getDelay()) * 
1024.0)+"B/sec delay "+
+                                       t.getDelay()+"ms (RTT 
"+t.getRoundTripTime()+"ms window "+t.getWindowSize();
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", val);
+                       // time delta
+                       peerRow.addChild("td", "class", "peer-idle" /* FIXME 
*/).addChild("#", TimeUtil.formatTime(peerNodeStatus.getClockDelta()));
+               }
+               
+               if (path.endsWith("displaymessagetypes.html")) {
+                       drawMessageTypes(peerTable, peerNodeStatus);
+               }
+       }
+
+       /** Is there a name column? */
+       abstract protected boolean hasNameColumn();
+       
+       /**
+        * Draw the name column, if there is one. This will be directly after 
the status column.
+        */
+       abstract protected void drawNameColumn(HTMLNode peerRow, PeerNodeStatus 
peerNodeStatus);
+
+       /**
+        * Is there a private note column?
+        */
+       abstract protected boolean hasPrivateNoteColumn();
+
+       /**
+        * Draw the private note column.
+        */
+       abstract protected void drawPrivateNoteColumn(HTMLNode peerRow, 
PeerNodeStatus peerNodeStatus, boolean fProxyJavascriptEnabled);
+       
+       private void drawMessageTypes(HTMLNode peerTable, PeerNodeStatus 
peerNodeStatus) {
+               HTMLNode messageCountRow = peerTable.addChild("tr", "class", 
"message-status");
+               messageCountRow.addChild("td", "colspan", "2");
+               HTMLNode messageCountCell = messageCountRow.addChild("td", 
"colspan", "9");  // = total table row width - 2 from above colspan
+               HTMLNode messageCountTable = messageCountCell.addChild("table", 
"class", "message-count");
+               HTMLNode countHeaderRow = messageCountTable.addChild("tr");
+               countHeaderRow.addChild("th", "Message");
+               countHeaderRow.addChild("th", "Incoming");
+               countHeaderRow.addChild("th", "Outgoing");
+               List messageNames = new ArrayList();
+               Map messageCounts = new HashMap();
+               for (Iterator incomingMessages = 
peerNodeStatus.getLocalMessagesReceived().keySet().iterator(); 
incomingMessages.hasNext(); ) {
+                       String messageName = (String) incomingMessages.next();
+                       messageNames.add(messageName);
+                       Long messageCount = (Long) 
peerNodeStatus.getLocalMessagesReceived().get(messageName);
+                       messageCounts.put(messageName, new Long[] { 
messageCount, new Long(0) });
+               }
+               for (Iterator outgoingMessages = 
peerNodeStatus.getLocalMessagesSent().keySet().iterator(); 
outgoingMessages.hasNext(); ) {
+                       String messageName = (String) outgoingMessages.next();
+                       if (!messageNames.contains(messageName)) {
+                               messageNames.add(messageName);
+                       }
+                       Long messageCount = (Long) 
peerNodeStatus.getLocalMessagesSent().get(messageName);
+                       Long[] existingCounts = (Long[]) 
messageCounts.get(messageName);
+                       if (existingCounts == null) {
+                               messageCounts.put(messageName, new Long[] { new 
Long(0), messageCount });
+                       } else {
+                               existingCounts[1] = messageCount;
+                       }
+               }
+               Collections.sort(messageNames, new Comparator() {
+                       public int compare(Object first, Object second) {
+                               return ((String) 
first).compareToIgnoreCase((String) second);
+                       }
+               });
+               for (Iterator messageNamesIterator = messageNames.iterator(); 
messageNamesIterator.hasNext(); ) {
+                       String messageName = (String) 
messageNamesIterator.next();
+                       Long[] messageCount = (Long[]) 
messageCounts.get(messageName);
+                       HTMLNode messageRow = messageCountTable.addChild("tr");
+                       messageRow.addChild("td", messageName);
+                       messageRow.addChild("td", "class", "right-align", 
String.valueOf(messageCount[0]));
+                       messageRow.addChild("td", "class", "right-align", 
String.valueOf(messageCount[1]));
+               }
+       }
+
+       private String idleToString(long now, long idle) {
+               if (idle <= 0) {
+                       return " ";
+               }
+               long idleMilliseconds = now - idle;
+               return TimeUtil.formatTime(idleMilliseconds);
+       }
+       
+       private static String l10n(String string) {
+               return L10n.getString("DarknetConnectionsToadlet."+string);
+       }
+       
+       private static String l10nStats(String string) {
+               return L10n.getString("StatisticsToadlet."+string);
+       }
+
+       private String sortString(boolean isReversed, String type) {
+               return (isReversed ? ("?sortBy="+type) : 
("?sortBy="+type+"&reversed"));
+       }
+
+
+}

Modified: trunk/freenet/src/freenet/clients/http/DarknetConnectionsToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/DarknetConnectionsToadlet.java       
2007-06-28 15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/clients/http/DarknetConnectionsToadlet.java       
2007-06-28 17:07:40 UTC (rev 13809)
@@ -3,619 +3,42 @@
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.io.StringWriter;
 import java.net.URI;
 import java.net.URL;
 import java.net.URLConnection;
-import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;

 import freenet.client.HighLevelSimpleClient;
 import freenet.io.comm.PeerParseException;
 import freenet.io.comm.ReferenceSignatureVerificationException;
-import freenet.io.xfer.PacketThrottle;
 import freenet.l10n.L10n;
+import freenet.node.DarknetPeerNode;
+import freenet.node.DarknetPeerNodeStatus;
 import freenet.node.FSParseException;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
-import freenet.node.NodeStats;
 import freenet.node.PeerManager;
-import freenet.node.PeerNode;
 import freenet.node.PeerNodeStatus;
-import freenet.node.Version;
 import freenet.support.HTMLNode;
 import freenet.support.Logger;
 import freenet.support.MultiValueTable;
 import freenet.support.SimpleFieldSet;
-import freenet.support.SizeUtil;
-import freenet.support.TimeUtil;
 import freenet.support.api.HTTPRequest;

-public class DarknetConnectionsToadlet extends Toadlet {
+public class DarknetConnectionsToadlet extends ConnectionsToadlet {

-       private final Node node;
-       private final NodeClientCore core;
-       private final NodeStats stats;
-       private final PeerManager peers;
-       private boolean isReversed = false;
-       
-       protected DarknetConnectionsToadlet(Node n, NodeClientCore core, 
HighLevelSimpleClient client) {
-               super(client);
-               this.node = n;
-               this.core = core;
-               this.stats = n.nodeStats;
-               this.peers = n.peers;
+       DarknetConnectionsToadlet(Node n, NodeClientCore core, 
HighLevelSimpleClient client) {
+               super(n, core, client);
        }
-
+       
        public String supportedMethods() {
                return "GET, POST";
        }

-       public void handleGet(URI uri, final HTTPRequest request, 
ToadletContext ctx) throws ToadletContextClosedException, IOException, 
RedirectException {
-               String path = uri.getPath();
-               if(path.endsWith("myref.fref")) {
-                       SimpleFieldSet fs = node.exportPublicFieldSet();
-                       StringWriter sw = new StringWriter();
-                       fs.writeTo(sw);
-                       MultiValueTable extraHeaders = new MultiValueTable();
-                       // Force download to disk
-                       extraHeaders.put("Content-Disposition", "attachment; 
filename=myref.fref");
-                       this.writeReply(ctx, 200, 
"application/x-freenet-reference", "OK", extraHeaders, sw.toString());
-                       return;
-               }
-
-               if(path.endsWith("myref.txt")) {
-                       SimpleFieldSet fs = node.exportPublicFieldSet();
-                       StringWriter sw = new StringWriter();
-                       fs.writeTo(sw);
-                       this.writeReply(ctx, 200, "text/plain", "OK", 
sw.toString());
-                       return;
-               }
-               
-               if(!ctx.isAllowedFullAccess()) {
-                       super.sendErrorPage(ctx, 403, "Unauthorized", 
L10n.getString("Toadlet.unauthorized"));
-                       return;
-               }
-               
-               final boolean advancedModeEnabled = 
node.isAdvancedModeEnabled();
-               final boolean fProxyJavascriptEnabled = 
node.isFProxyJavascriptEnabled();
-               
-               /* gather connection statistics */
-               PeerNodeStatus[] peerNodeStatuses = peers.getPeerNodeStatuses();
-               Arrays.sort(peerNodeStatuses, new Comparator() {
-                       public int compare(Object first, Object second) {
-                               int result = 0;
-                               boolean isSet = true;
-                               PeerNodeStatus firstNode = (PeerNodeStatus) 
first;
-                               PeerNodeStatus secondNode = (PeerNodeStatus) 
second;
-                               
-                               if(request.isParameterSet("sortBy")){
-                                       final String sortBy = 
request.getParam("sortBy"); 
-
-                                       if(sortBy.equals("name")){
-                                               result = 
firstNode.getName().compareToIgnoreCase(secondNode.getName());
-                                       }else if(sortBy.equals("address")){
-                                               result = 
firstNode.getPeerAddress().compareToIgnoreCase(secondNode.getPeerAddress());
-                                       }else if(sortBy.equals("location")){
-                                               result = 
(firstNode.getLocation() - secondNode.getLocation()) < 0 ? -1 : 1; // Shouldn't 
be equal anyway
-                                       }else if(sortBy.equals("version")){
-                                               result = 
Version.getArbitraryBuildNumber(firstNode.getVersion()) - 
Version.getArbitraryBuildNumber(secondNode.getVersion());
-                                       }else if(sortBy.equals("privnote")){
-                                               result = 
firstNode.getPrivateDarknetCommentNote().compareToIgnoreCase(secondNode.getPrivateDarknetCommentNote());
-                                       }else
-                                               isSet=false;
-                               }else
-                                       isSet=false;
-                               
-                               if(!isSet){
-                                       int statusDifference = 
firstNode.getStatusValue() - secondNode.getStatusValue();
-                                       if (statusDifference != 0) 
-                                               result = (statusDifference < 0 
? -1 : 1);
-                                       else
-                                               result = 
firstNode.getName().compareToIgnoreCase(secondNode.getName());
-                               }
-
-                               if(result == 0){
-                                       return 0;
-                               }else if(request.isParameterSet("reversed")){
-                                       isReversed = true;
-                                       return result > 0 ? -1 : 1;
-                               }else{
-                                       isReversed = false;
-                                       return result < 0 ? -1 : 1;
-                               }
-                       }
-               });
-               
-               int numberOfConnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_CONNECTED);
-               int numberOfRoutingBackedOff = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF);
-               int numberOfTooNew = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_TOO_NEW);
-               int numberOfTooOld = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_TOO_OLD);
-               int numberOfDisconnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISCONNECTED);
-               int numberOfNeverConnected = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED);
-               int numberOfDisabled = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_DISABLED);
-               int numberOfBursting = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_BURSTING);
-               int numberOfListening = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_LISTENING);
-               int numberOfListenOnly = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_LISTEN_ONLY);
-               int numberOfClockProblem = 
PeerNodeStatus.getPeerStatusCount(peerNodeStatuses, 
PeerManager.PEER_NODE_STATUS_CLOCK_PROBLEM);
-               
-               int numberOfSimpleConnected = numberOfConnected + 
numberOfRoutingBackedOff;
-               int numberOfNotConnected = numberOfTooNew + numberOfTooOld + 
numberOfDisconnected + numberOfNeverConnected + numberOfDisabled + 
numberOfBursting + numberOfListening + numberOfListenOnly;
-               String titleCountString = null;
-               if(advancedModeEnabled) {
-                       titleCountString = "(" + numberOfConnected + '/' + 
numberOfRoutingBackedOff + '/' + numberOfTooNew + '/' + numberOfTooOld + '/' + 
numberOfNotConnected + ')';
-               } else {
-                       titleCountString = (numberOfNotConnected + 
numberOfSimpleConnected)>0 ? String.valueOf(numberOfSimpleConnected) : "";
-               }
-               
-               HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(L10n.getString("DarknetConnectionsToadlet.fullTitle",
 new String[] { "counts", "name" }, new String[] { titleCountString, 
node.getMyName() } ), ctx);
-               HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
-               
-               // FIXME! We need some nice images
-               long now = System.currentTimeMillis();
-       
-               if(ctx.isAllowedFullAccess())
-                       contentNode.addChild(core.alerts.createSummary());
-               
-               if(peerNodeStatuses.length>0){
-
-                       /* node status values */
-                       long nodeUptimeSeconds = (now - node.startupTime) / 
1000;
-                       int bwlimitDelayTime = (int) 
stats.getBwlimitDelayTime();
-                       int nodeAveragePingTime = (int) 
stats.getNodeAveragePingTime();
-                       int networkSizeEstimateSession = 
stats.getNetworkSizeEstimate(-1);
-                       int networkSizeEstimateRecent = 0;
-                       if(nodeUptimeSeconds > (48*60*60)) {  // 48 hours
-                               networkSizeEstimateRecent = 
stats.getNetworkSizeEstimate(now - (48*60*60*1000));  // 48 hours
-                       }
-                       DecimalFormat fix4 = new DecimalFormat("0.0000");
-                       double routingMissDistance =  
stats.routingMissDistance.currentValue();
-                       DecimalFormat fix1 = new DecimalFormat("##0.0%");
-                       double backedOffPercent =  
stats.backedOffPercent.currentValue();
-                       String nodeUptimeString = 
TimeUtil.formatTime(nodeUptimeSeconds * 1000);  // *1000 to convert to 
milliseconds
-
-                       // BEGIN OVERVIEW TABLE
-                       HTMLNode overviewTable = contentNode.addChild("table", 
"class", "column");
-                       HTMLNode overviewTableRow = 
overviewTable.addChild("tr");
-                       HTMLNode nextTableCell = 
overviewTableRow.addChild("td", "class", "first");
-
-                       /* node status overview box */
-                       if(advancedModeEnabled) {
-                               HTMLNode overviewInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                               overviewInfobox.addChild("div", "class", 
"infobox-header", "Node status overview");
-                               HTMLNode overviewInfoboxContent = 
overviewInfobox.addChild("div", "class", "infobox-content");
-                               HTMLNode overviewList = 
overviewInfoboxContent.addChild("ul");
-                               overviewList.addChild("li", 
"bwlimitDelayTime:\u00a0" + bwlimitDelayTime + "ms");
-                               overviewList.addChild("li", 
"nodeAveragePingTime:\u00a0" + nodeAveragePingTime + "ms");
-                               overviewList.addChild("li", 
"networkSizeEstimateSession:\u00a0" + networkSizeEstimateSession + 
"\u00a0nodes");
-                               if(nodeUptimeSeconds > (48*60*60)) {  // 48 
hours
-                                       overviewList.addChild("li", 
"networkSizeEstimateRecent:\u00a0" + networkSizeEstimateRecent + "\u00a0nodes");
-                               }
-                               overviewList.addChild("li", "nodeUptime:\u00a0" 
+ nodeUptimeString);
-                               overviewList.addChild("li", 
"routingMissDistance:\u00a0" + fix4.format(routingMissDistance));
-                               overviewList.addChild("li", 
"backedOffPercent:\u00a0" + fix1.format(backedOffPercent));
-                               overviewList.addChild("li", 
"pInstantReject:\u00a0" + fix1.format(stats.pRejectIncomingInstantly()));
-                               nextTableCell = overviewTableRow.addChild("td");
-                       }
-
-                       // Activity box
-                       int numARKFetchers = node.getNumARKFetchers();
-
-                       HTMLNode activityInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                       activityInfobox.addChild("div", "class", 
"infobox-header", l10n("activityTitle"));
-                       HTMLNode activityInfoboxContent = 
activityInfobox.addChild("div", "class", "infobox-content");
-                       HTMLNode activityList = 
StatisticsToadlet.drawActivity(activityInfoboxContent, node);
-                       if (advancedModeEnabled && activityList != null) {
-                               if (numARKFetchers > 0) {
-                                       activityList.addChild("li", 
"ARK\u00a0Fetch\u00a0Requests:\u00a0" + numARKFetchers);
-                               }
-                               StatisticsToadlet.drawBandwidth(activityList, 
node, nodeUptimeSeconds);
-                       }
-
-                       nextTableCell = advancedModeEnabled ? 
overviewTableRow.addChild("td") : overviewTableRow.addChild("td", "class", 
"last");
-
-                       // Peer statistics box
-                       HTMLNode peerStatsInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                       peerStatsInfobox.addChild("div", "class", 
"infobox-header", l10nStats("peerStatsTitle"));
-                       HTMLNode peerStatsContent = 
peerStatsInfobox.addChild("div", "class", "infobox-content");
-                       HTMLNode peerStatsList = 
peerStatsContent.addChild("ul");
-                       if (numberOfConnected > 0) {
-                               HTMLNode peerStatsConnectedListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsConnectedListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_connected", 
l10n("connected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("connectedShort"));
-                               peerStatsConnectedListItem.addChild("span", 
":\u00a0" + numberOfConnected);
-                       }
-                       if (numberOfRoutingBackedOff > 0) {
-                               HTMLNode peerStatsRoutingBackedOffListItem = 
peerStatsList.addChild("li").addChild("span");
-                               
peerStatsRoutingBackedOffListItem.addChild("span", new String[] { "class", 
"title", "style" }, new String[] { "peer_backed_off", (advancedModeEnabled ? 
l10n("backedOff") : l10n("busy")), "border-bottom: 1px dotted; cursor: help;" 
}, advancedModeEnabled ? l10n("backedOffShort") : l10n("busyShort"));
-                               
peerStatsRoutingBackedOffListItem.addChild("span", ":\u00a0" + 
numberOfRoutingBackedOff);
-                       }
-                       if (numberOfTooNew > 0) {
-                               HTMLNode peerStatsTooNewListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsTooNewListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_too_new", 
l10n("tooNew"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("tooNewShort"));
-                               peerStatsTooNewListItem.addChild("span", 
":\u00a0" + numberOfTooNew);
-                       }
-                       if (numberOfTooOld > 0) {
-                               HTMLNode peerStatsTooOldListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsTooOldListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_too_old", 
l10n("tooOld"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("tooOldShort"));
-                               peerStatsTooOldListItem.addChild("span", 
":\u00a0" + numberOfTooOld);
-                       }
-                       if (numberOfDisconnected > 0) {
-                               HTMLNode peerStatsDisconnectedListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsDisconnectedListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { "peer_disconnected", 
l10n("notConnected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("notConnectedShort"));
-                               peerStatsDisconnectedListItem.addChild("span", 
":\u00a0" + numberOfDisconnected);
-                       }
-                       if (numberOfNeverConnected > 0) {
-                               HTMLNode peerStatsNeverConnectedListItem = 
peerStatsList.addChild("li").addChild("span");
-                               
peerStatsNeverConnectedListItem.addChild("span", new String[] { "class", 
"title", "style" }, new String[] { "peer_never_connected", 
l10n("neverConnected"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("neverConnectedShort"));
-                               
peerStatsNeverConnectedListItem.addChild("span", ":\u00a0" + 
numberOfNeverConnected);
-                       }
-                       if (numberOfDisabled > 0) {
-                               HTMLNode peerStatsDisabledListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsDisabledListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_disabled", 
l10n("disabled"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("disabledShort"));
-                               peerStatsDisabledListItem.addChild("span", 
":\u00a0" + numberOfDisabled);
-                       }
-                       if (numberOfBursting > 0) {
-                               HTMLNode peerStatsBurstingListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsBurstingListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_bursting", 
l10n("bursting"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("burstingShort"));
-                               peerStatsBurstingListItem.addChild("span", 
":\u00a0" + numberOfBursting);
-                       }
-                       if (numberOfListening > 0) {
-                               HTMLNode peerStatsListeningListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsListeningListItem.addChild("span", new 
String[] { "class", "title", "style" }, new String[] { "peer_listening", 
l10n("listening"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("listeningShort"));
-                               peerStatsListeningListItem.addChild("span", 
":\u00a0" + numberOfListening);
-                       }
-                       if (numberOfListenOnly > 0) {
-                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { "peer_listen_only", 
l10n("listenOnly"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("listenOnlyShort"));
-                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfListenOnly);
-                       }
-                       if (numberOfClockProblem > 0) {
-                               HTMLNode peerStatsListenOnlyListItem = 
peerStatsList.addChild("li").addChild("span");
-                               peerStatsListenOnlyListItem.addChild("span", 
new String[] { "class", "title", "style" }, new String[] { 
"peer_clock_problem", l10n("clockProblem"), "border-bottom: 1px dotted; cursor: 
help;" }, l10n("clockProblemShort"));
-                               peerStatsListenOnlyListItem.addChild("span", 
":\u00a0" + numberOfClockProblem);
-                       }
-
-                       // Peer routing backoff reason box
-                       if(advancedModeEnabled) {
-                               nextTableCell = overviewTableRow.addChild("td", 
"class", "last");
-                               HTMLNode backoffReasonInfobox = 
nextTableCell.addChild("div", "class", "infobox");
-                               backoffReasonInfobox.addChild("div", "class", 
"infobox-header", "Peer backoff reasons");
-                               HTMLNode backoffReasonContent = 
backoffReasonInfobox.addChild("div", "class", "infobox-content");
-                               String [] routingBackoffReasons = 
peers.getPeerNodeRoutingBackoffReasons();
-                               if(routingBackoffReasons.length == 0) {
-                                       backoffReasonContent.addChild("#", 
"Good, your node is not backed off from any peers!");
-                               } else {
-                                       HTMLNode reasonList = 
backoffReasonContent.addChild("ul");
-                                       for(int 
i=0;i<routingBackoffReasons.length;i++) {
-                                               int reasonCount = 
peers.getPeerNodeRoutingBackoffReasonSize(routingBackoffReasons[i]);
-                                               if(reasonCount > 0) {
-                                                       
reasonList.addChild("li", routingBackoffReasons[i] + '\u00a0' + reasonCount);
-                                               }
-                                       }
-                               }
-                       }
-                       // END OVERVIEW TABLE
-
-                       // BEGIN PEER TABLE
-                       if(fProxyJavascriptEnabled) {
-                               StringBuffer jsBuf = new StringBuffer();
-                               // FIXME: There's probably some icky Javascript 
in here (this is the first thing that worked for me); feel free to fix up to 
Javascript guru standards
-                               jsBuf.append( "  function peerNoteChange() {\n" 
);
-                               jsBuf.append( "    var theobj = 
document.getElementById( \"action\" );\n" );
-                               jsBuf.append( "    var length = 
theobj.options.length;\n" );
-                               jsBuf.append( "    for (var i = 0; i < length; 
i++) {\n" );
-                               jsBuf.append( "      if(theobj.options[i] == 
\"update_notes\") {\n" );
-                               jsBuf.append( "        theobj.options[i].select 
= true;\n" );
-                               jsBuf.append( "      } else {\n" );
-                               jsBuf.append( "        theobj.options[i].select 
= false;\n" );
-                               jsBuf.append( "      }\n" );
-                               jsBuf.append( "    }\n" );
-                               jsBuf.append( "    
theobj.value=\"update_notes\";\n" );
-                               //jsBuf.append( "    document.getElementById( 
\"peersForm\" ).submit();\n" );
-                               jsBuf.append( "    document.getElementById( 
\"peersForm\" ).doAction.click();\n" );
-                               jsBuf.append( "  }\n" );
-                               jsBuf.append( "  function peerNoteBlur() {\n" );
-                               jsBuf.append( "    var theobj = 
document.getElementById( \"action\" );\n" );
-                               jsBuf.append( "    var length = 
theobj.options.length;\n" );
-                               jsBuf.append( "    for (var i = 0; i < length; 
i++) {\n" );
-                               jsBuf.append( "      if(theobj.options[i] == 
\"update_notes\") {\n" );
-                               jsBuf.append( "        theobj.options[i].select 
= true;\n" );
-                               jsBuf.append( "      } else {\n" );
-                               jsBuf.append( "        theobj.options[i].select 
= false;\n" );
-                               jsBuf.append( "      }\n" );
-                               jsBuf.append( "    }\n" );
-                               jsBuf.append( "    
theobj.value=\"update_notes\";\n" );
-                               jsBuf.append( "  }\n" );
-                               contentNode.addChild("script", "type", 
"text/javascript").addChild("%", jsBuf.toString());
-                       }
-                       HTMLNode peerTableInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
-                       HTMLNode peerTableInfoboxHeader = 
peerTableInfobox.addChild("div", "class", "infobox-header");
-                       peerTableInfoboxHeader.addChild("#", l10n("myFriends"));
-                       if (advancedModeEnabled) {
-                               if (!path.endsWith("displaymessagetypes.html")) 
{
-                                       peerTableInfoboxHeader.addChild("#", " 
");
-                                       peerTableInfoboxHeader.addChild("a", 
"href", "displaymessagetypes.html", "(more detailed)");
-                               }
-                       }
-                       HTMLNode peerTableInfoboxContent = 
peerTableInfobox.addChild("div", "class", "infobox-content");
-
-                       if (peerNodeStatuses.length == 0) {
-                               
L10n.addL10nSubstitution(peerTableInfoboxContent, 
"DarknetConnectionsToadlet.noPeersWithHomepageLink", 
-                                               new String[] { "link", "/link" 
}, new String[] { "<a href=\"/\">", "</a>" });
-                       } else {
-                               HTMLNode peerForm = 
ctx.addFormChild(peerTableInfoboxContent, ".", "peersForm");
-                               HTMLNode peerTable = peerForm.addChild("table", 
"class", "darknet_connections");
-                               HTMLNode peerTableHeaderRow = 
peerTable.addChild("tr");
-                               peerTableHeaderRow.addChild("th");
-                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "status")).addChild("#", l10n("statusTitle"));
-                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "name")).addChild("span", new String[] { 
"title", "style" }, new String[] { l10n("nameClickToMessage"), "border-bottom: 
1px dotted; cursor: help;" }, l10n("nameTitle"));
-                               if (advancedModeEnabled) {
-                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"address")).addChild("span", new String[] { "title", "style" }, new String[] { 
l10n("ipAddress"), "border-bottom: 1px dotted; cursor: help;" }, 
l10n("ipAddressTitle"));
-                               }
-                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "version")).addChild("#", l10n("versionTitle"));
-                               if (advancedModeEnabled) {
-                                       
peerTableHeaderRow.addChild("th").addChild("a", "href", sortString(isReversed, 
"location")).addChild("#", "Location");
-                                       
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { "Other node busy? Display: Percentage of time the 
node is overloaded, Current wait time remaining (0=not overloaded)/total/last 
overload reason", "border-bottom: 1px dotted; cursor: help;" }, "Backoff");
-
-                                       
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { "Probability of the node rejecting a request due to 
overload or causing a timeout.", "border-bottom: 1px dotted; cursor: help;" }, 
"Overload Probability");
-                               }
-                               
peerTableHeaderRow.addChild("th").addChild("span", new String[] { "title", 
"style" }, new String[] { l10n("idleTime"), "border-bottom: 1px dotted; cursor: 
help;" }, l10n("idleTimeTitle"));
-                               peerTableHeaderRow.addChild("th").addChild("a", 
"href", sortString(isReversed, "privnote")).addChild("span", new String[] { 
"title", "style" }, new String[] { l10n("privateNote"), "border-bottom: 1px 
dotted; cursor: help;" }, l10n("privateNoteTitle"));
-
-                               if(advancedModeEnabled) {
-                                       peerTableHeaderRow.addChild("th", 
"%\u00a0Time Routable");
-                                       peerTableHeaderRow.addChild("th", 
"Total\u00a0Traffic\u00a0(in/out)");
-                                       peerTableHeaderRow.addChild("th", 
"Congestion\u00a0Control");
-                                       peerTableHeaderRow.addChild("th", 
"Time\u00a0Delta");
-                               }
-                               
-                               for (int peerIndex = 0, peerCount = 
peerNodeStatuses.length; peerIndex < peerCount; peerIndex++) {
-                                       PeerNodeStatus peerNodeStatus = 
peerNodeStatuses[peerIndex];
-                                       HTMLNode peerRow = 
peerTable.addChild("tr");
-
-                                       // check box column
-                                       peerRow.addChild("td", "class", 
"peer-marker").addChild("input", new String[] { "type", "name" }, new String[] 
{ "checkbox", "node_" + peerNodeStatus.hashCode() });
-
-                                       // status column
-                                       String statusString = 
peerNodeStatus.getStatusName();
-                                       if (!advancedModeEnabled && 
(peerNodeStatus.getStatusValue() == 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF)) {
-                                               statusString = "BUSY";
-                                       }
-                                       peerRow.addChild("td", "class", 
"peer-status").addChild("span", "class", peerNodeStatus.getStatusCSSName(), 
statusString + (peerNodeStatus.isFetchingARK() ? "*" : ""));
-
-                                       // name column
-                                       peerRow.addChild("td", "class", 
"peer-name").addChild("a", "href", "/send_n2ntm/?peernode_hashcode=" + 
peerNodeStatus.hashCode(), peerNodeStatus.getName());
-
-                                       // address column
-                                       if (advancedModeEnabled) {
-                                               String pingTime = "";
-                                               if 
(peerNodeStatus.isConnected()) {
-                                                       pingTime = " (" + (int) 
peerNodeStatus.getAveragePingTime() + "ms)";
-                                               }
-                                               peerRow.addChild("td", "class", 
"peer-address").addChild("#", ((peerNodeStatus.getPeerAddress() != null) ? 
(peerNodeStatus.getPeerAddress() + ':' + peerNodeStatus.getPeerPort()) : 
(l10n("unknownAddress"))) + pingTime);
-                                       }
-
-                                       // version column
-                                       if (peerNodeStatus.getStatusValue() != 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED && 
(peerNodeStatus.isPublicInvalidVersion() || 
peerNodeStatus.isPublicReverseInvalidVersion())) {  // Don't draw attention to 
a version problem if NEVER CONNECTED
-                                               peerRow.addChild("td", "class", 
"peer-version").addChild("span", "class", "peer_version_problem", 
peerNodeStatus.getSimpleVersion());
-                                       } else {
-                                               peerRow.addChild("td", "class", 
"peer-version").addChild("#", peerNodeStatus.getSimpleVersion());
-                                       }
-
-                                       // location column
-                                       if (advancedModeEnabled) {
-                                               peerRow.addChild("td", "class", 
"peer-location", String.valueOf(peerNodeStatus.getLocation()));
-                                       }
-
-                                       if (advancedModeEnabled) {
-                                               // backoff column
-                                               HTMLNode backoffCell = 
peerRow.addChild("td", "class", "peer-backoff");
-                                               backoffCell.addChild("#", 
fix1.format(peerNodeStatus.getBackedOffPercent()));
-                                               int backoff = (int) 
(Math.max(peerNodeStatus.getRoutingBackedOffUntil() - now, 0));
-                                               // Don't list the backoff as 
zero before it's actually zero
-                                               if ((backoff > 0) && (backoff < 
1000)) {
-                                                       backoff = 1000;
-                                               }
-                                               backoffCell.addChild("#", ' ' + 
String.valueOf(backoff / 1000) + '/' + 
String.valueOf(peerNodeStatus.getRoutingBackoffLength() / 1000));
-                                               backoffCell.addChild("#", 
(peerNodeStatus.getLastBackoffReason() == null) ? "" : ('/' + 
(peerNodeStatus.getLastBackoffReason())));
-
-                                               // overload probability column
-                                               HTMLNode pRejectCell = 
peerRow.addChild("td", "class", "peer-backoff"); // FIXME
-                                               pRejectCell.addChild("#", 
fix1.format(peerNodeStatus.getPReject()));
-                                       }
-
-                                       // idle column
-                                       long idle = 
peerNodeStatus.getTimeLastRoutable();
-                                       if (peerNodeStatus.isRoutable()) {
-                                               idle = 
peerNodeStatus.getTimeLastConnectionCompleted();
-                                       } else if 
(peerNodeStatus.getStatusValue() == 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED) {
-                                               idle = 
peerNodeStatus.getPeerAddedTime();
-                                       }
-                                       if(!peerNodeStatus.isConnected() && 
(now - idle) > (2 * 7 * 24 * 60 * 60 * (long) 1000)) { // 2 weeks
-                                               peerRow.addChild("td", "class", 
"peer-idle").addChild("span", "class", "peer_idle_old", idleToString(now, 
idle));
-                                       } else {
-                                               peerRow.addChild("td", "class", 
"peer-idle", idleToString(now, idle));
-                                       }
-
-                                       // private darknet node comment note 
column
-                                       if(fProxyJavascriptEnabled) {
-                                               peerRow.addChild("td", "class", 
"peer-private-darknet-comment-note").addChild("input", new String[] { "type", 
"name", "size", "maxlength", "onBlur", "onChange", "value" }, new String[] { 
"text", "peerPrivateNote_" + peerNodeStatus.hashCode(), "16", "250", 
"peerNoteBlur();", "peerNoteChange();", 
peerNodeStatus.getPrivateDarknetCommentNote() });
-                                       } else {
-                                               peerRow.addChild("td", "class", 
"peer-private-darknet-comment-note").addChild("input", new String[] { "type", 
"name", "size", "maxlength", "value" }, new String[] { "text", 
"peerPrivateNote_" + peerNodeStatus.hashCode(), "16", "250", 
peerNodeStatus.getPrivateDarknetCommentNote() });
-                                       }
-
-                                       if(advancedModeEnabled) {
-                                               // percent of time connected 
column
-                                               peerRow.addChild("td", "class", 
"peer-idle" /* FIXME */).addChild("#", 
fix1.format(peerNodeStatus.getPercentTimeRoutableConnection()));
-                                               // total traffic column
-                                               peerRow.addChild("td", "class", 
"peer-idle" /* FIXME */).addChild("#", 
SizeUtil.formatSize(peerNodeStatus.getTotalInputBytes())+" / 
"+SizeUtil.formatSize(peerNodeStatus.getTotalOutputBytes()));
-                                               // congestion control
-                                               PacketThrottle t = 
peerNodeStatus.getThrottle();
-                                               String val;
-                                               if(t == null)
-                                                       val = "none";
-                                               else
-                                                       val = (int)((1000.0 / 
t.getDelay()) * 1024.0)+"B/sec delay "+
-                                                               
t.getDelay()+"ms (RTT "+t.getRoundTripTime()+"ms window "+t.getWindowSize();
-                                               peerRow.addChild("td", "class", 
"peer-idle" /* FIXME */).addChild("#", val);
-                                               // time delta
-                                               peerRow.addChild("td", "class", 
"peer-idle" /* FIXME */).addChild("#", 
TimeUtil.formatTime(peerNodeStatus.getClockDelta()));
-                                       }
-                                       
-                                       if 
(path.endsWith("displaymessagetypes.html")) {
-                                               HTMLNode messageCountRow = 
peerTable.addChild("tr", "class", "message-status");
-                                               messageCountRow.addChild("td", 
"colspan", "2");
-                                               HTMLNode messageCountCell = 
messageCountRow.addChild("td", "colspan", String.valueOf(advancedModeEnabled ? 
9 : 5));  // = total table row width - 2 from above colspan
-                                               HTMLNode messageCountTable = 
messageCountCell.addChild("table", "class", "message-count");
-                                               HTMLNode countHeaderRow = 
messageCountTable.addChild("tr");
-                                               countHeaderRow.addChild("th", 
"Message");
-                                               countHeaderRow.addChild("th", 
"Incoming");
-                                               countHeaderRow.addChild("th", 
"Outgoing");
-                                               List messageNames = new 
ArrayList();
-                                               Map messageCounts = new 
HashMap();
-                                               for (Iterator incomingMessages 
= peerNodeStatus.getLocalMessagesReceived().keySet().iterator(); 
incomingMessages.hasNext(); ) {
-                                                       String messageName = 
(String) incomingMessages.next();
-                                                       
messageNames.add(messageName);
-                                                       Long messageCount = 
(Long) peerNodeStatus.getLocalMessagesReceived().get(messageName);
-                                                       
messageCounts.put(messageName, new Long[] { messageCount, new Long(0) });
-                                               }
-                                               for (Iterator outgoingMessages 
= peerNodeStatus.getLocalMessagesSent().keySet().iterator(); 
outgoingMessages.hasNext(); ) {
-                                                       String messageName = 
(String) outgoingMessages.next();
-                                                       if 
(!messageNames.contains(messageName)) {
-                                                               
messageNames.add(messageName);
-                                                       }
-                                                       Long messageCount = 
(Long) peerNodeStatus.getLocalMessagesSent().get(messageName);
-                                                       Long[] existingCounts = 
(Long[]) messageCounts.get(messageName);
-                                                       if (existingCounts == 
null) {
-                                                               
messageCounts.put(messageName, new Long[] { new Long(0), messageCount });
-                                                       } else {
-                                                               
existingCounts[1] = messageCount;
-                                                       }
-                                               }
-                                               Collections.sort(messageNames, 
new Comparator() {
-                                                       public int 
compare(Object first, Object second) {
-                                                               return 
((String) first).compareToIgnoreCase((String) second);
-                                                       }
-                                               });
-                                               for (Iterator 
messageNamesIterator = messageNames.iterator(); messageNamesIterator.hasNext(); 
) {
-                                                       String messageName = 
(String) messageNamesIterator.next();
-                                                       Long[] messageCount = 
(Long[]) messageCounts.get(messageName);
-                                                       HTMLNode messageRow = 
messageCountTable.addChild("tr");
-                                                       
messageRow.addChild("td", messageName);
-                                                       
messageRow.addChild("td", "class", "right-align", 
String.valueOf(messageCount[0]));
-                                                       
messageRow.addChild("td", "class", "right-align", 
String.valueOf(messageCount[1]));
-                                               }
-                                       }
-                               }
-
-                               HTMLNode actionSelect = 
peerForm.addChild("select", new String[] { "id", "name" }, new String[] { 
"action", "action" });
-                               actionSelect.addChild("option", "value", "", 
l10n("selectAction"));
-                               actionSelect.addChild("option", "value", 
"send_n2ntm", l10n("sendMessageToPeers"));
-                               actionSelect.addChild("option", "value", 
"update_notes", l10n("updateChangedPrivnotes"));
-                               if(advancedModeEnabled) {
-                                       actionSelect.addChild("option", 
"value", "enable", "Enable selected peers");
-                                       actionSelect.addChild("option", 
"value", "disable", "Disable selected peers");
-                                       actionSelect.addChild("option", 
"value", "set_burst_only", "On selected peers, set BurstOnly (only set this if 
you have a static IP and are not NATed and neither is the peer)");
-                                       actionSelect.addChild("option", 
"value", "clear_burst_only", "On selected peers, clear BurstOnly");
-                                       actionSelect.addChild("option", 
"value", "set_listen_only", "On selected peers, set ListenOnly (not 
recommended)");
-                                       actionSelect.addChild("option", 
"value", "clear_listen_only", "On selected peers, clear ListenOnly");
-                                       actionSelect.addChild("option", 
"value", "set_allow_local", "On selected peers, set allowLocalAddresses (useful 
if you are connecting to another node on the same LAN)");
-                                       actionSelect.addChild("option", 
"value", "clear_allow_local", "On selected peers, clear allowLocalAddresses");
-                                       actionSelect.addChild("option", 
"value", "set_ignore_source_port", "On selected peers, set ignoreSourcePort 
(try this if behind an evil corporate firewall; otherwise not recommended)");
-                                       actionSelect.addChild("option", 
"value", "clear_ignore_source_port", "On selected peers, clear 
ignoreSourcePort");
-                               }
-                               actionSelect.addChild("option", "value", "", 
l10n("separator"));
-                               actionSelect.addChild("option", "value", 
"remove", l10n("removePeers"));
-                               peerForm.addChild("input", new String[] { 
"type", "name", "value" }, new String[] { "submit", "doAction", l10n("go") });
-                               
-                       }
-                       // END PEER TABLE
-               }
-
-               // BEGIN PEER ADDITION BOX
-               HTMLNode peerAdditionInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
-               peerAdditionInfobox.addChild("div", "class", "infobox-header", 
l10n("addPeerTitle"));
-               HTMLNode peerAdditionContent = 
peerAdditionInfobox.addChild("div", "class", "infobox-content");
-               HTMLNode peerAdditionForm = 
ctx.addFormChild(peerAdditionContent, ".", "addPeerForm");
-               peerAdditionForm.addChild("#", l10n("pasteReference"));
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("textarea", new String[] { "id", 
"name", "rows", "cols" }, new String[] { "reftext", "ref", "8", "74" });
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("#", (l10n("urlReference") + ' '));
-               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "refurl", "text", "url" });
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("#", (l10n("fileReference") + ' '));
-               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name" }, new String[] { "reffile", "file", "reffile" });
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("#", (l10n("enterDescription") + ' 
'));
-               peerAdditionForm.addChild("input", new String[] { "id", "type", 
"name", "size", "maxlength", "value" }, new String[] { "peerPrivateNote", 
"text", "peerPrivateNote", "16", "250", "" });
-               peerAdditionForm.addChild("br");
-               peerAdditionForm.addChild("input", new String[] { "type", 
"name", "value" }, new String[] { "submit", "add", l10n("add") });
-               
-               // our reference
-               HTMLNode referenceInfobox = contentNode.addChild("div", 
"class", "infobox infobox-normal");
-               HTMLNode headerReferenceInfobox = 
referenceInfobox.addChild("div", "class", "infobox-header");
-               // FIXME better way to deal with this sort of thing???
-               L10n.addL10nSubstitution(headerReferenceInfobox, 
"DarknetConnectionsToadlet.myReferenceHeader",
-                               new String[] { "linkref", "/linkref", 
"linktext", "/linktext" },
-                               new String[] { "<a href=\"myref.fref\">", 
"</a>", "<a href=\"myref.txt\">", "</a>" });
-               HTMLNode warningSentence = 
headerReferenceInfobox.addChild("pre");
-               L10n.addL10nSubstitution(warningSentence, 
"DarknetConnectionsToadlet.referenceCopyWarning",
-                               new String[] { "bold", "/bold" },
-                               new String[] { "<b>", "</b>" });
-               referenceInfobox.addChild("div", "class", 
"infobox-content").addChild("pre", "id", "reference", 
node.exportPublicFieldSet().toString() + '\n');
-               
-               // our ports
-               HTMLNode portInfobox = contentNode.addChild("div", "class", 
"infobox infobox-normal");
-               portInfobox.addChild("div", "class", "infobox-header", 
l10n("nodePortsTitle"));
-               HTMLNode portInfoboxContent = portInfobox.addChild("div", 
"class", "infobox-content");
-               HTMLNode portInfoList = portInfoboxContent.addChild("ul");
-               SimpleFieldSet fproxyConfig = 
node.config.get("fproxy").exportFieldSet(true);
-               SimpleFieldSet fcpConfig = 
node.config.get("fcp").exportFieldSet(true);
-               SimpleFieldSet tmciConfig = 
node.config.get("console").exportFieldSet(true);
-               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fnpPort", new String[] { "port" }, 
new String[] { Integer.toString(node.getFNPPort()) }));
-               try {
-                       if(fproxyConfig.getBoolean("enabled", false)) {
-                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fproxyPort", new String[] { "port" }, 
new String[] { Integer.toString(fproxyConfig.getInt("port")) }));
-                       } else {
-                               portInfoList.addChild("li", 
l10n("fproxyDisabled"));
-                       }
-                       if(fcpConfig.getBoolean("enabled", false)) {
-                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.fcpPort", new String[] { "port" }, 
new String[] { Integer.toString(fcpConfig.getInt("port")) }));
-                       } else {
-                               portInfoList.addChild("li", 
l10n("fcpDisabled"));
-                       }
-                       if(tmciConfig.getBoolean("enabled", false)) {
-                               portInfoList.addChild("li", 
L10n.getString("DarknetConnectionsToadlet.tmciPort", new String[] { "port" }, 
new String[] { Integer.toString(tmciConfig.getInt("port")) }));
-                       } else {
-                               portInfoList.addChild("li", 
l10n("tmciDisabled"));
-                       }
-               } catch (FSParseException e) {
-                       // ignore
-               }
-               
-               this.writeReply(ctx, 200, "text/html", "OK", 
pageNode.generate());
-       }
-       
        private static String l10n(String string) {
                return L10n.getString("DarknetConnectionsToadlet."+string);
        }

-       private static String l10nStats(String string) {
-               return L10n.getString("StatisticsToadlet."+string);
-       }
-
-       private String sortString(boolean isReversed, String type) {
-               return (isReversed ? ("?sortBy="+type) : 
("?sortBy="+type+"&reversed"));
-       }
-
        public void handlePost(URI uri, final HTTPRequest request, 
ToadletContext ctx) throws ToadletContextClosedException, IOException, 
RedirectException {
                boolean logMINOR = Logger.shouldLog(Logger.MINOR, this);

@@ -697,9 +120,9 @@
                                this.sendErrorPage(ctx, 
l10n("failedToAddNodeInternalErrorTitle"), 
l10n("failedToAddNodeInternalError"), t);
                                return;
                        }
-                       PeerNode pn;
+                       DarknetPeerNode pn;
                        try {
-                               pn = new PeerNode(fs, node, node.peers, false, 
node.packetMangler);
+                               pn = new DarknetPeerNode(fs, node, node.peers, 
false, node.packetMangler);
                                pn.setPrivateDarknetCommentNote(privateComment);
                        } catch (FSParseException e1) {
                                this.sendErrorPage(ctx, 200, 
l10n("failedToAddNodeTitle"),
@@ -735,11 +158,11 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("send_n2ntm")) {
                        HTMLNode pageNode = 
ctx.getPageMaker().getPageNode(l10n("sendMessageTitle"), ctx);
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        HashMap peers = new HashMap();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
-                                       PeerNode pn = peerNodes[i];
+                                       DarknetPeerNode pn = peerNodes[i];
                                        String peer_name = pn.getName();
                                        String peer_hash = "" + pn.hashCode();
                                        if(!peers.containsKey(peer_hash)) {
@@ -753,7 +176,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("update_notes")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("peerPrivateNote_"+peerNodes[i].hashCode())) {
                                        
if(!request.getPartAsString("peerPrivateNote_"+peerNodes[i].hashCode(),250).equals(peerNodes[i].getPrivateDarknetCommentNote()))
 {
@@ -768,7 +191,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("enable")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].enablePeer();
@@ -781,7 +204,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("disable")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].disablePeer();
@@ -794,7 +217,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("set_burst_only")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setBurstOnly(true);
@@ -807,7 +230,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("clear_burst_only")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setBurstOnly(false);
@@ -820,7 +243,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("set_ignore_source_port")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setIgnoreSourcePort(true);
@@ -833,7 +256,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("clear_ignore_source_port")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setIgnoreSourcePort(false);
@@ -846,7 +269,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("set_listen_only")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setListenOnly(true);
@@ -859,7 +282,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("clear_listen_only")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        peerNodes[i].setListenOnly(false);
@@ -872,7 +295,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("set_allow_local")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        
peerNodes[i].setAllowLocalAddresses(true);
@@ -885,7 +308,7 @@
                } else if (request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("clear_allow_local")) {
                        //int hashcode = 
Integer.decode(request.getParam("node")).intValue();

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        
peerNodes[i].setAllowLocalAddresses(false);
@@ -898,7 +321,7 @@
                } else if (request.isPartSet("remove") || 
(request.isPartSet("doAction") && 
request.getPartAsString("action",25).equals("remove"))) {                   
                        if(logMINOR) Logger.minor(this, "Remove node");

-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {       
                                        
if((peerNodes[i].timeLastConnectionCompleted() < (System.currentTimeMillis() - 
1000*60*60*24*7) /* one week */) ||  (peerNodes[i].peerNodeStatus == 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED) || request.isPartSet("forceit")){
@@ -931,7 +354,7 @@
                        return;
                } else if (request.isPartSet("acceptTransfer")) {
                        // FIXME this is ugly, should probably move both this 
code and the PeerNode code somewhere.
-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        long id = 
Long.parseLong(request.getPartAsString("id", 32)); // FIXME handle 
NumberFormatException
@@ -945,7 +368,7 @@
                        return;
                } else if (request.isPartSet("rejectTransfer")) {
                        // FIXME this is ugly, should probably move both this 
code and the PeerNode code somewhere.
-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        for(int i = 0; i < peerNodes.length; i++) {
                                if 
(request.isPartSet("node_"+peerNodes[i].hashCode())) {
                                        long id = 
Long.parseLong(request.getPartAsString("id", 32)); // FIXME handle 
NumberFormatException
@@ -962,11 +385,28 @@
                }
        }

-       private String idleToString(long now, long idle) {
-               if (idle <= 0) {
-                       return " ";
+       protected boolean hasNameColumn() {
+               return true;
+       }
+       
+       protected void drawNameColumn(HTMLNode peerRow, PeerNodeStatus 
peerNodeStatus) {
+               // name column
+               peerRow.addChild("td", "class", "peer-name").addChild("a", 
"href", "/send_n2ntm/?peernode_hashcode=" + peerNodeStatus.hashCode(), 
((DarknetPeerNodeStatus)peerNodeStatus).getName());
+       }
+
+       protected boolean hasPrivateNoteColumn() {
+               return true;
+       }
+
+       protected void drawPrivateNoteColumn(HTMLNode peerRow, PeerNodeStatus 
peerNodeStatus, boolean fProxyJavascriptEnabled) {
+               // private darknet node comment note column
+               DarknetPeerNodeStatus status = (DarknetPeerNodeStatus) 
peerNodeStatus;
+               if(fProxyJavascriptEnabled) {
+                       peerRow.addChild("td", "class", 
"peer-private-darknet-comment-note").addChild("input", new String[] { "type", 
"name", "size", "maxlength", "onBlur", "onChange", "value" }, new String[] { 
"text", "peerPrivateNote_" + peerNodeStatus.hashCode(), "16", "250", 
"peerNoteBlur();", "peerNoteChange();", status.getPrivateDarknetCommentNote() 
});
+               } else {
+                       peerRow.addChild("td", "class", 
"peer-private-darknet-comment-note").addChild("input", new String[] { "type", 
"name", "size", "maxlength", "value" }, new String[] { "text", 
"peerPrivateNote_" + peerNodeStatus.hashCode(), "16", "250", 
status.getPrivateDarknetCommentNote() });
                }
-               long idleMilliseconds = now - idle;
-               return TimeUtil.formatTime(idleMilliseconds);
        }
+
+
 }

Modified: trunk/freenet/src/freenet/clients/http/N2NTMToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/N2NTMToadlet.java    2007-06-28 
15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/clients/http/N2NTMToadlet.java    2007-06-28 
17:07:40 UTC (rev 13809)
@@ -12,6 +12,7 @@
 import freenet.client.HighLevelSimpleClient;
 import freenet.io.comm.MessageCore;
 import freenet.l10n.L10n;
+import freenet.node.DarknetPeerNode;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
 import freenet.node.PeerManager;
@@ -67,7 +68,7 @@
                                // ignore here, handle below
                        }
                        if (input_hashcode != -1) {
-                               PeerNode[] peerNodes = 
node.getDarknetConnections();
+                               DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                                for (int i = 0; i < peerNodes.length; i++) {
                                        int peer_hashcode = 
peerNodes[i].hashCode();
                                        if (peer_hashcode == input_hashcode) {
@@ -150,7 +151,7 @@
                        HTMLNode contentNode = 
ctx.getPageMaker().getContentNode(pageNode);
                        HTMLNode peerTableInfobox = contentNode.addChild("div", 
"class",
                                        "infobox infobox-normal");
-                       PeerNode[] peerNodes = node.getDarknetConnections();
+                       DarknetPeerNode[] peerNodes = 
node.getDarknetConnections();
                        String fnam = request.getPartAsString("filename", 1024);
                        File filename = null;
                        if(fnam != null && fnam.length() > 0) {
@@ -169,7 +170,7 @@
                        peerTableHeaderRow.addChild("th", l10n("sendStatus"));
                        for (int i = 0; i < peerNodes.length; i++) {
                                if (request.isPartSet("node_" + 
peerNodes[i].hashCode())) {
-                                       PeerNode pn = peerNodes[i];
+                                       DarknetPeerNode pn = peerNodes[i];

                                        int status;


Modified: trunk/freenet/src/freenet/clients/http/StatisticsToadlet.java
===================================================================
--- trunk/freenet/src/freenet/clients/http/StatisticsToadlet.java       
2007-06-28 15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/clients/http/StatisticsToadlet.java       
2007-06-28 17:07:40 UTC (rev 13809)
@@ -16,6 +16,7 @@
 import freenet.config.SubConfig;
 import freenet.io.comm.IOStatisticCollector;
 import freenet.l10n.L10n;
+import freenet.node.DarknetPeerNodeStatus;
 import freenet.node.Node;
 import freenet.node.NodeClientCore;
 import freenet.node.NodeStarter;
@@ -115,8 +116,8 @@
                PeerNodeStatus[] peerNodeStatuses = peers.getPeerNodeStatuses();
                Arrays.sort(peerNodeStatuses, new Comparator() {
                        public int compare(Object first, Object second) {
-                               PeerNodeStatus firstNode = (PeerNodeStatus) 
first;
-                               PeerNodeStatus secondNode = (PeerNodeStatus) 
second;
+                               DarknetPeerNodeStatus firstNode = 
(DarknetPeerNodeStatus) first;
+                               DarknetPeerNodeStatus secondNode = 
(DarknetPeerNodeStatus) second;
                                int statusDifference = 
firstNode.getStatusValue() - secondNode.getStatusValue();
                                if (statusDifference != 0) {
                                        return statusDifference;

Added: trunk/freenet/src/freenet/node/DarknetPeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/DarknetPeerNode.java                         
(rev 0)
+++ trunk/freenet/src/freenet/node/DarknetPeerNode.java 2007-06-28 17:07:40 UTC 
(rev 13809)
@@ -0,0 +1,1480 @@
+package freenet.node;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+
+import freenet.client.DefaultMIMETypes;
+import freenet.io.comm.DMT;
+import freenet.io.comm.DisconnectedException;
+import freenet.io.comm.FreenetInetAddress;
+import freenet.io.comm.Message;
+import freenet.io.comm.NotConnectedException;
+import freenet.io.comm.Peer;
+import freenet.io.comm.PeerParseException;
+import freenet.io.comm.ReferenceSignatureVerificationException;
+import freenet.io.comm.RetrievalException;
+import freenet.io.xfer.BulkReceiver;
+import freenet.io.xfer.BulkTransmitter;
+import freenet.io.xfer.PartiallyReceivedBulk;
+import freenet.l10n.L10n;
+import freenet.node.useralerts.N2NTMUserAlert;
+import freenet.node.useralerts.UserAlert;
+import freenet.support.Base64;
+import freenet.support.Fields;
+import freenet.support.HTMLNode;
+import freenet.support.IllegalBase64Exception;
+import freenet.support.Logger;
+import freenet.support.SimpleFieldSet;
+import freenet.support.SizeUtil;
+import freenet.support.io.FileUtil;
+import freenet.support.io.RandomAccessFileWrapper;
+import freenet.support.io.RandomAccessThing;
+
+public class DarknetPeerNode extends PeerNode {
+
+    /** Name of this node */
+    String myName;
+    
+    /** True if this peer is not to be connected with */
+    private boolean isDisabled;
+    
+    /** True if we don't send handshake requests to this peer, but will 
connect if we receive one */
+    private boolean isListenOnly;
+    
+    /** True if we send handshake requests to this peer in infrequent bursts */
+    private boolean isBurstOnly;
+    
+    /** True if we are currently sending this peer a burst of handshake 
requests */
+    private boolean isBursting;
+
+    /** True if we want to ignore the source port of the node's sent packets.
+     * This is normally set when dealing with an Evil Corporate Firewall which 
rewrites the port on outgoing
+     * packets but does not redirect incoming packets destined to the 
rewritten port.
+     * What it does is this: If we have an address with the same IP but a 
different port, to the detectedPeer,
+     * we use that instead. */
+    private boolean ignoreSourcePort;
+    
+    /** True if we want to allow LAN/localhost addresses. */
+    private boolean allowLocalAddresses;
+    
+    /** Extra peer data file numbers */
+    private LinkedHashSet extraPeerDataFileNumbers;
+
+    /** Private comment on the peer for /friends/ page */
+    private String privateDarknetComment;
+    
+    /** Private comment on the peer for /friends/ page's extra peer data file 
number */
+    private int privateDarknetCommentFileNumber;
+    
+    /** Queued-to-send N2NTM extra peer data file numbers */
+    private LinkedHashSet queuedToSendN2NTMExtraPeerDataFileNumbers;
+
+    /** Number of handshake attempts (while in ListenOnly mode) since the 
beginning of this burst */
+    private int listeningHandshakeBurstCount;
+    
+    /** Total number of handshake attempts (while in ListenOnly mode) to be in 
this burst */
+    private int listeningHandshakeBurstSize;
+    
+    private static boolean logMINOR;
+    
+    /**
+     * Create a darknet PeerNode from a SimpleFieldSet
+     * @param fs The SimpleFieldSet to parse
+     * @param node2 The running Node we are part of.
+     */
+    public DarknetPeerNode(SimpleFieldSet fs, Node node2, PeerManager peers, 
boolean fromLocal, OutgoingPacketMangler mangler) throws FSParseException, 
PeerParseException, ReferenceSignatureVerificationException {
+       super(fs, node2, peers, fromLocal, mangler);
+       
+       logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       
+       long now = System.currentTimeMillis();
+       
+        String name = fs.get("myName");
+        if(name == null) throw new FSParseException("No name");
+        myName = name;
+
+        if(fromLocal) {
+               SimpleFieldSet metadata = fs.subset("metadata");
+               
+               isDisabled = Fields.stringToBool(metadata.get("isDisabled"), 
false);
+               isListenOnly = 
Fields.stringToBool(metadata.get("isListenOnly"), false);
+               isBurstOnly = Fields.stringToBool(metadata.get("isBurstOnly"), 
false);
+               ignoreSourcePort = 
Fields.stringToBool(metadata.get("ignoreSourcePort"), false);
+               allowLocalAddresses = 
Fields.stringToBool(metadata.get("allowLocalAddresses"), false);
+        }
+       
+        listeningHandshakeBurstCount = 0;
+        listeningHandshakeBurstSize = Node.MIN_BURSTING_HANDSHAKE_BURST_SIZE
+               + 
node.random.nextInt(Node.RANDOMIZED_BURSTING_HANDSHAKE_BURST_SIZE);
+        if(isBurstOnly) {
+               Logger.minor(this, "First BurstOnly mode handshake in 
"+(sendHandshakeTime - now)+"ms for "+getName()+" (count: 
"+listeningHandshakeBurstCount+", size: "+listeningHandshakeBurstSize+ ')');
+        }
+
+               // Setup the private darknet comment note
+        privateDarknetComment = "";
+        privateDarknetCommentFileNumber = -1;
+
+               // Setup the extraPeerDataFileNumbers
+               extraPeerDataFileNumbers = new LinkedHashSet();
+               
+               // Setup the queuedToSendN2NTMExtraPeerDataFileNumbers
+               queuedToSendN2NTMExtraPeerDataFileNumbers = new LinkedHashSet();
+        
+        setPeerNodeStatus(now);
+
+    }
+
+    /**
+     * 
+     * Normally this is the address that packets have been received from from 
this node.
+     * However, if ignoreSourcePort is set, we will search for a similar 
address with a different port 
+     * number in the node reference.
+     */
+    public synchronized Peer getPeer(){
+       Peer detectedPeer = super.getPeer();
+       if(ignoreSourcePort) {
+               FreenetInetAddress addr = detectedPeer == null ? null : 
detectedPeer.getFreenetAddress();
+               int port = detectedPeer == null ? -1 : detectedPeer.getPort();
+               if(nominalPeer == null) return detectedPeer;
+               for(int i=0;i<nominalPeer.size();i++) {
+                       Peer p = (Peer) nominalPeer.get(i);
+                       if(p.getPort() != port && 
p.getFreenetAddress().equals(addr)) {
+                               return p;
+                       }
+               }
+       }
+       return detectedPeer;
+    }
+
+    /**
+     * @return True, if we are disconnected and it has been a
+     * sufficient time period since we last sent a handshake
+     * attempt.
+     */
+    public boolean shouldSendHandshake() {
+       synchronized(this) {
+               if(isDisabled) return false;
+               if(isListenOnly) return false;
+               if(!shouldSendHandshake()) return false;
+               if(isBurstOnly())
+                       isBursting = true;
+               else
+                       return true;
+       }
+               setPeerNodeStatus(System.currentTimeMillis());
+               return true;
+    }
+    
+    protected synchronized boolean innerProcessNewNoderef(SimpleFieldSet fs, 
boolean forARK) throws FSParseException {
+       boolean changedAnything = super.innerProcessNewNoderef(fs, forARK);
+        String name = fs.get("myName");
+        if(name != null && !name.equals(myName)) {
+               changedAnything = true;
+            myName = name;
+        }
+        return changedAnything;
+    }
+    
+    public synchronized SimpleFieldSet exportFieldSet() {
+       SimpleFieldSet fs = super.exportMetadataFieldSet();
+       fs.putSingle("myName", getName());
+       return fs;
+    }
+       
+    public synchronized SimpleFieldSet exportMetadataFieldSet() {
+       SimpleFieldSet fs = super.exportMetadataFieldSet();
+       if(isDisabled)
+               fs.putSingle("isDisabled", "true");
+       if(isListenOnly)
+               fs.putSingle("isListenOnly", "true");
+       if(isBurstOnly)
+               fs.putSingle("isBurstOnly", "true");
+       if(ignoreSourcePort)
+               fs.putSingle("ignoreSourcePort", "true");
+       if(allowLocalAddresses)
+               fs.putSingle("allowLocalAddresses", "true");
+       return fs;
+    }
+
+    
+       public synchronized String getName() {
+               return myName;
+       }
+
+       protected synchronized int getPeerNodeStatus(long now, long 
backedOffUntil) {
+               if(isDisabled) {
+                       return PeerManager.PEER_NODE_STATUS_DISABLED;
+               }
+               int status = super.getPeerNodeStatus(now, backedOffUntil);
+               if(status == PeerManager.PEER_NODE_STATUS_CONNECTED || 
+                               status == 
PeerManager.PEER_NODE_STATUS_CLOCK_PROBLEM ||
+                               status == 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF ||
+                               status == PeerManager.PEER_NODE_STATUS_TOO_NEW 
||
+                               status == PeerManager.PEER_NODE_STATUS_TOO_OLD)
+                       return status;
+               if(isListenOnly)
+                       return PeerManager.PEER_NODE_STATUS_LISTEN_ONLY;
+               if(isBursting)
+                       return PeerManager.PEER_NODE_STATUS_BURSTING;
+               if(isBurstOnly)
+                       return PeerManager.PEER_NODE_STATUS_LISTENING;
+               return status;
+       }
+       
+       public void enablePeer() {
+               synchronized(this) {
+                       isDisabled = false;
+               }
+               setPeerNodeStatus(System.currentTimeMillis());
+        node.peers.writePeers();
+       }
+       
+       public void disablePeer() {
+               synchronized(this) {
+                       isDisabled = true;
+               }
+               if(isConnected()) {
+                       forceDisconnect();
+               }
+               stopARKFetcher();
+               setPeerNodeStatus(System.currentTimeMillis());
+        node.peers.writePeers();
+       }
+
+       public synchronized boolean isDisabled() {
+               return isDisabled;
+       }
+       
+       public void setListenOnly(boolean setting) {
+               synchronized(this) {
+                       isListenOnly = setting;
+               }
+               if(setting && isBurstOnly()) {
+                       setBurstOnly(false);
+               }
+               if(setting) {
+                       stopARKFetcher();
+               }
+               setPeerNodeStatus(System.currentTimeMillis());
+        node.peers.writePeers();
+       }
+
+       public synchronized boolean isListenOnly() {
+               return isListenOnly;
+       }
+       
+       public void setBurstOnly(boolean setting) {
+               synchronized(this) {
+                       isBurstOnly = setting;
+               }
+               if(setting && isListenOnly()) {
+                       setListenOnly(false);
+               }
+               long now = System.currentTimeMillis();
+               if(!setting) {
+                       synchronized(this) {
+                               sendHandshakeTime = now;  // don't keep any 
long handshake delays we might have had under BurstOnly
+                       }
+               }
+               setPeerNodeStatus(now);
+               node.peers.writePeers();
+       }
+
+       public void setIgnoreSourcePort(boolean setting) {
+               synchronized(this) {
+                       ignoreSourcePort = setting;
+               }
+       }
+       
+
+       public boolean isIgnoreSourcePort() {
+               return ignoreSourcePort;
+       }
+       
+       public boolean isIgnoreSource() {
+               return ignoreSourcePort;
+       }
+       
+       public synchronized boolean isBurstOnly() {
+               return isBurstOnly;
+       }
+
+       public synchronized boolean allowLocalAddresses() {
+               return allowLocalAddresses;
+       }
+
+       public void setAllowLocalAddresses(boolean setting) {
+               synchronized(this) {
+                       allowLocalAddresses = setting;
+               }
+        node.peers.writePeers();
+       }
+       
+       public boolean readExtraPeerData() {
+               String extraPeerDataDirPath = node.getExtraPeerDataDir();
+               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
+               if(!extraPeerDataPeerDir.exists()) {
+                       return false;
+               }
+               if(!extraPeerDataPeerDir.isDirectory()) {
+                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
+                       return false;
+               }
+               File[] extraPeerDataFiles = extraPeerDataPeerDir.listFiles();
+               if(extraPeerDataFiles == null) {
+                       return false;
+               }
+               boolean gotError = false;
+               boolean readResult = false;
+               for (int i = 0; i < extraPeerDataFiles.length; i++) {
+                       Integer fileNumber;
+                       try {
+                               fileNumber = new 
Integer(extraPeerDataFiles[i].getName());
+                       } catch (NumberFormatException e) {
+                               gotError = true;
+                               continue;
+                       }
+                       synchronized(extraPeerDataFileNumbers) {
+                               extraPeerDataFileNumbers.add(fileNumber);
+                       }
+                       readResult = 
readExtraPeerDataFile(extraPeerDataFiles[i], fileNumber.intValue());
+                       if(!readResult) {
+                               gotError = true;
+                       }
+               }
+               return !gotError;
+       }
+
+       public boolean rereadExtraPeerDataFile(int fileNumber) {
+               if(logMINOR)
+                       Logger.minor(this, "Rereading peer data file 
"+fileNumber+" for "+shortToString());
+               String extraPeerDataDirPath = node.getExtraPeerDataDir();
+               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
+               if(!extraPeerDataPeerDir.exists()) {
+                       Logger.error(this, "Extra peer data directory for peer 
does not exist: "+extraPeerDataPeerDir.getPath());
+                       return false;
+               }
+               if(!extraPeerDataPeerDir.isDirectory()) {
+                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
+                       return false;
+               }
+               File extraPeerDataFile = new 
File(extraPeerDataDirPath+File.separator+getIdentityString()+File.separator+fileNumber);
+               if(!extraPeerDataFile.exists()) {
+                       Logger.error(this, "Extra peer data file for peer does 
not exist: "+extraPeerDataFile.getPath());
+                       return false;
+               }
+               return readExtraPeerDataFile(extraPeerDataFile, fileNumber);
+       }
+
+       public boolean readExtraPeerDataFile(File extraPeerDataFile, int 
fileNumber) {
+               if(logMINOR) Logger.minor(this, "Reading "+extraPeerDataFile+" 
: "+fileNumber+" for "+shortToString());
+               boolean gotError = false;
+               if(!extraPeerDataFile.exists()) {
+                       if(logMINOR)
+                               Logger.minor(this, "Does not exist");
+                       return false;
+               }
+               Logger.normal(this, "extraPeerDataFile: 
"+extraPeerDataFile.getPath());
+               FileInputStream fis;
+               try {
+                       fis = new FileInputStream(extraPeerDataFile);
+               } catch (FileNotFoundException e1) {
+                       Logger.normal(this, "Extra peer data file not found: 
"+extraPeerDataFile.getPath());
+                       return false;
+               }
+               InputStreamReader isr;
+               try {
+                       isr = new InputStreamReader(fis, "UTF-8");
+               } catch (UnsupportedEncodingException e) {
+                       throw new Error("Impossible: JVM doesn't support UTF-8: 
"+e, e);
+               }
+               BufferedReader br = new BufferedReader(isr);
+               SimpleFieldSet fs = null;
+               try {
+                       // Read in the single SimpleFieldSet
+                       fs = new SimpleFieldSet(br, false, true);
+               } catch (EOFException e3) {
+                       // End of file, fine
+               } catch (IOException e4) {
+                       Logger.error(this, "Could not read extra peer data 
file: "+e4, e4);
+               } finally {
+                       try {
+                               br.close();
+                       } catch (IOException e5) {
+                               Logger.error(this, "Ignoring "+e5+" caught 
reading "+extraPeerDataFile.getPath(), e5);
+                       }
+               }
+               if(fs == null) {
+                       Logger.normal(this, "Deleting corrupt (too short?) 
file: "+extraPeerDataFile);
+                       deleteExtraPeerDataFile(fileNumber);
+                       return true;
+               }
+               boolean parseResult = false;
+               try {
+                       parseResult = parseExtraPeerData(fs, extraPeerDataFile, 
fileNumber);
+                       if(!parseResult) {
+                               gotError = true;
+                       }
+               } catch (FSParseException e2) {
+                       Logger.error(this, "Could not parse extra peer data: 
"+e2+ '\n' +fs.toString(),e2);
+                       gotError = true;
+               }
+               return !gotError;
+       }
+
+       private boolean parseExtraPeerData(SimpleFieldSet fs, File 
extraPeerDataFile, int fileNumber) throws FSParseException {
+               String extraPeerDataTypeString = fs.get("extraPeerDataType");
+               int extraPeerDataType = -1;
+               try {
+                       extraPeerDataType = 
Integer.parseInt(extraPeerDataTypeString);
+               } catch (NumberFormatException e) {
+                       Logger.error(this, "NumberFormatException parsing 
extraPeerDataType ("+extraPeerDataTypeString+") in file 
"+extraPeerDataFile.getPath());
+                       return false;
+               }
+               if(extraPeerDataType == Node.EXTRA_PEER_DATA_TYPE_N2NTM) {
+                       node.handleNodeToNodeTextMessageSimpleFieldSet(fs, 
this, fileNumber);
+                       return true;
+               } else if(extraPeerDataType == 
Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE) {
+                       String peerNoteTypeString = fs.get("peerNoteType");
+                       int peerNoteType = -1;
+                       try {
+                               peerNoteType = 
Integer.parseInt(peerNoteTypeString);
+                       } catch (NumberFormatException e) {
+                               Logger.error(this, "NumberFormatException 
parsing peerNoteType ("+peerNoteTypeString+") in file 
"+extraPeerDataFile.getPath());
+                               return false;
+                       }
+                       if(peerNoteType == 
Node.PEER_NOTE_TYPE_PRIVATE_DARKNET_COMMENT) {
+                               synchronized(privateDarknetComment) {
+                                       try {
+                                               privateDarknetComment = new 
String(Base64.decode(fs.get("privateDarknetComment")));
+                                       } catch (IllegalBase64Exception e) {
+                                               Logger.error(this, "Bad Base64 
encoding when decoding a private darknet comment SimpleFieldSet", e);
+                                               return false;
+                                       }
+                                       privateDarknetCommentFileNumber = 
fileNumber;
+                               }
+                               return true;
+                       }
+                       Logger.error(this, "Read unknown peer note type 
'"+peerNoteType+"' from file "+extraPeerDataFile.getPath());
+                       return false;
+               } else if(extraPeerDataType == 
Node.EXTRA_PEER_DATA_TYPE_QUEUED_TO_SEND_N2NTM) {
+                       boolean sendSuccess = false;
+                       int type = fs.getInt("n2nType", 1); // FIXME remove 
default
+                       fs.putOverwrite("n2nType", Integer.toString(type));
+                       if(isConnected()) {
+                               Message n2ntm;
+                               if(fs.get("extraPeerDataType") != null) {
+                                       fs.removeValue("extraPeerDataType");
+                               }
+                               if(fs.get("senderFileNumber") != null) {
+                                       fs.removeValue("senderFileNumber");
+                               }
+                               fs.putOverwrite("senderFileNumber", 
String.valueOf(fileNumber));
+                               if(fs.get("sentTime") != null) {
+                                       fs.removeValue("sentTime");
+                               }
+                               fs.putOverwrite("sentTime", 
Long.toString(System.currentTimeMillis()));
+                               
+                               try {
+                                       n2ntm = 
DMT.createNodeToNodeMessage(type, fs.toString().getBytes("UTF-8"));
+                               } catch (UnsupportedEncodingException e) {
+                                       Logger.error(this, 
"UnsupportedEncodingException processing extraPeerDataType 
("+extraPeerDataTypeString+") in file "+extraPeerDataFile.getPath(), e);
+                                       return false;
+                               }
+
+                               try {
+                                       
synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
+                                               node.usm.send(this, n2ntm, 
null);
+                                               Logger.normal(this, "Sent 
queued ("+fileNumber+") N2NTM to '"+getName()+"': "+n2ntm);
+                                               sendSuccess = true;
+                                               
queuedToSendN2NTMExtraPeerDataFileNumbers.remove(new Integer(fileNumber));
+                                       }
+                                       deleteExtraPeerDataFile(fileNumber);
+                               } catch (NotConnectedException e) {
+                                       sendSuccess = false;  // redundant, but 
clear
+                               }
+                       }
+                       if(!sendSuccess) {
+                               
synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
+                                       fs.putOverwrite("extraPeerDataType", 
Integer.toString(extraPeerDataType));
+                                       fs.removeValue("sentTime");
+                                       
queuedToSendN2NTMExtraPeerDataFileNumbers.add(new Integer(fileNumber));
+                               }
+                       }
+                       return true;
+               }
+               Logger.error(this, "Read unknown extra peer data type 
'"+extraPeerDataType+"' from file "+extraPeerDataFile.getPath());
+               return false;
+       }
+
+       public int writeNewExtraPeerDataFile(SimpleFieldSet fs, int 
extraPeerDataType) {
+               String extraPeerDataDirPath = node.getExtraPeerDataDir();
+               if(extraPeerDataType > 0)
+                       fs.putOverwrite("extraPeerDataType", 
Integer.toString(extraPeerDataType));
+               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
+               if(!extraPeerDataPeerDir.exists()) {
+                       if(!extraPeerDataPeerDir.mkdir()) {
+                               Logger.error(this, "Extra peer data directory 
for peer could not be created: "+extraPeerDataPeerDir.getPath());
+                               return -1;
+                       }
+               }
+               if(!extraPeerDataPeerDir.isDirectory()) {
+                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
+                       return -1;
+               }
+               Integer[] localFileNumbers = null;
+               int nextFileNumber = 0;
+               synchronized(extraPeerDataFileNumbers) {
+                       // Find the first free slot
+                       localFileNumbers = (Integer[]) 
extraPeerDataFileNumbers.toArray(new Integer[extraPeerDataFileNumbers.size()]);
+                       Arrays.sort(localFileNumbers);
+                       for (int i = 0; i < localFileNumbers.length; i++) {
+                               if(localFileNumbers[i].intValue() > 
nextFileNumber) {
+                                       break;
+                               }
+                               nextFileNumber = localFileNumbers[i].intValue() 
+ 1;
+                       }
+                       extraPeerDataFileNumbers.add(new 
Integer(nextFileNumber));
+               }
+               FileOutputStream fos;
+               File extraPeerDataFile = new 
File(extraPeerDataPeerDir.getPath()+File.separator+nextFileNumber);
+               if(extraPeerDataFile.exists()) {
+                       Logger.error(this, "Extra peer data file already 
exists: "+extraPeerDataFile.getPath());
+                       return -1;
+               }
+               String f = extraPeerDataFile.getPath();
+               try {
+                       fos = new FileOutputStream(f);
+               } catch (FileNotFoundException e2) {
+                       Logger.error(this, "Cannot write extra peer data file 
to disk: Cannot create "
+                                       + f + " - " + e2, e2);
+                       return -1;
+               }
+               OutputStreamWriter w;
+               try {
+                       w = new OutputStreamWriter(fos, "UTF-8");
+               } catch (UnsupportedEncodingException e2) {
+                       throw new Error("UTF-8 unsupported!: "+e2, e2);
+               }
+               BufferedWriter bw = new BufferedWriter(w);
+               try {
+                       fs.writeTo(bw);
+                       bw.close();
+               } catch (IOException e) {
+                       try {
+                               fos.close();
+                       } catch (IOException e1) {
+                               Logger.error(this, "Cannot close extra peer 
data file: "+e, e);
+                       }
+                       Logger.error(this, "Cannot write file: " + e, e);
+                       return -1;
+               }
+               return nextFileNumber;
+       }
+
+       public void deleteExtraPeerDataFile(int fileNumber) {
+               String extraPeerDataDirPath = node.getExtraPeerDataDir();
+               File extraPeerDataPeerDir = new File(extraPeerDataDirPath, 
getIdentityString());
+               if(!extraPeerDataPeerDir.exists()) {
+                       Logger.error(this, "Extra peer data directory for peer 
does not exist: "+extraPeerDataPeerDir.getPath());
+                       return;
+               }
+               if(!extraPeerDataPeerDir.isDirectory()) {
+                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
+                       return;
+               }
+               File extraPeerDataFile = new File(extraPeerDataPeerDir, 
Integer.toString(fileNumber));
+               if(!extraPeerDataFile.exists()) {
+                       Logger.error(this, "Extra peer data file for peer does 
not exist: "+extraPeerDataFile.getPath());
+                       return;
+               }
+               synchronized(extraPeerDataFileNumbers) {
+                       extraPeerDataFileNumbers.remove(new 
Integer(fileNumber));
+               }
+               if(!extraPeerDataFile.delete()) {
+                       if(extraPeerDataFile.exists()) {
+                               Logger.error(this, "Cannot delete file 
"+extraPeerDataFile+" after sending message to "+getPeer()+" - it may be resent 
on resting the node");
+                       } else {
+                               Logger.normal(this, "File does not exist when 
deleting: "+extraPeerDataFile+" after sending message to "+getPeer());
+                       }
+               }
+       }
+
+       public void removeExtraPeerDataDir() {
+               String extraPeerDataDirPath = node.getExtraPeerDataDir();
+               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
+               if(!extraPeerDataPeerDir.exists()) {
+                       Logger.error(this, "Extra peer data directory for peer 
does not exist: "+extraPeerDataPeerDir.getPath());
+                       return;
+               }
+               if(!extraPeerDataPeerDir.isDirectory()) {
+                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
+                       return;
+               }
+               Integer[] localFileNumbers = null;
+               synchronized(extraPeerDataFileNumbers) {
+                       localFileNumbers = (Integer[]) 
extraPeerDataFileNumbers.toArray(new Integer[extraPeerDataFileNumbers.size()]);
+               }
+               for (int i = 0; i < localFileNumbers.length; i++) {
+                       deleteExtraPeerDataFile(localFileNumbers[i].intValue());
+               }
+               extraPeerDataPeerDir.delete();
+       }
+
+       public boolean rewriteExtraPeerDataFile(SimpleFieldSet fs, int 
extraPeerDataType, int fileNumber) {
+               String extraPeerDataDirPath = node.getExtraPeerDataDir();
+               if(extraPeerDataType > 0)
+                       fs.putOverwrite("extraPeerDataType", 
Integer.toString(extraPeerDataType));
+               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
+               if(!extraPeerDataPeerDir.exists()) {
+                       Logger.error(this, "Extra peer data directory for peer 
does not exist: "+extraPeerDataPeerDir.getPath());
+                       return false;
+               }
+               if(!extraPeerDataPeerDir.isDirectory()) {
+                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
+                       return false;
+               }
+               File extraPeerDataFile = new 
File(extraPeerDataDirPath+File.separator+getIdentityString()+File.separator+fileNumber);
+               if(!extraPeerDataFile.exists()) {
+                       Logger.error(this, "Extra peer data file for peer does 
not exist: "+extraPeerDataFile.getPath());
+                       return false;
+               }
+               String f = extraPeerDataFile.getPath();
+               FileOutputStream fos;
+               try {
+                       fos = new FileOutputStream(f);
+               } catch (FileNotFoundException e2) {
+                       Logger.error(this, "Cannot write extra peer data file 
to disk: Cannot open "
+                                       + f + " - " + e2, e2);
+                       return false;
+               }
+               OutputStreamWriter w;
+               try {
+                       w = new OutputStreamWriter(fos, "UTF-8");
+               } catch (UnsupportedEncodingException e2) {
+                       throw new Error("JVM doesn't support UTF-8 charset!: 
"+e2, e2);
+               }
+               BufferedWriter bw = new BufferedWriter(w);
+               try {
+                       fs.writeTo(bw);
+                       bw.close();
+               } catch (IOException e) {
+                       try {
+                               fos.close();
+                       } catch (IOException e1) {
+                               Logger.error(this, "Cannot close extra peer 
data file: "+e, e);
+                       }
+                       Logger.error(this, "Cannot write file: " + e, e);
+                       return false;
+               }
+               return true;
+       }
+       
+       public synchronized String getPrivateDarknetCommentNote() {
+               return privateDarknetComment;
+       }
+       
+       public synchronized void setPrivateDarknetCommentNote(String comment) {
+               int localFileNumber;
+               synchronized(privateDarknetComment) {
+                       privateDarknetComment = comment;
+                       localFileNumber = privateDarknetCommentFileNumber;
+               }
+               SimpleFieldSet fs = new SimpleFieldSet(true);
+               fs.put("peerNoteType", 
Node.PEER_NOTE_TYPE_PRIVATE_DARKNET_COMMENT);
+               fs.putSingle("privateDarknetComment", 
Base64.encode(comment.getBytes()));
+               if(localFileNumber == -1) {
+                       localFileNumber = writeNewExtraPeerDataFile(fs, 
Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE);
+                       synchronized(privateDarknetComment) {
+                               privateDarknetCommentFileNumber = 
localFileNumber;
+                       }
+               } else {
+                       rewriteExtraPeerDataFile(fs, 
Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE, localFileNumber);
+               }
+       }
+
+       public void queueN2NTM(SimpleFieldSet fs) {
+               int fileNumber = writeNewExtraPeerDataFile( fs, 
Node.EXTRA_PEER_DATA_TYPE_QUEUED_TO_SEND_N2NTM);
+               synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
+                       queuedToSendN2NTMExtraPeerDataFileNumbers.add(new 
Integer(fileNumber));
+               }
+       }
+
+       public void sendQueuedN2NTMs() {
+               if(logMINOR)
+                       Logger.minor(this, "Sending queued N2NTMs for 
"+shortToString());
+               Integer[] localFileNumbers = null;
+               synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
+                       localFileNumbers = (Integer[]) 
queuedToSendN2NTMExtraPeerDataFileNumbers.toArray(new 
Integer[queuedToSendN2NTMExtraPeerDataFileNumbers.size()]);
+               }
+               Arrays.sort(localFileNumbers);
+               for (int i = 0; i < localFileNumbers.length; i++) {
+                       rereadExtraPeerDataFile(localFileNumbers[i].intValue());
+               }
+       }
+
+       void startARKFetcher() {
+               synchronized(this) {
+                       if(isListenOnly) {
+                               Logger.minor(this, "Not starting ark fetcher 
for "+this+" as it's in listen-only mode.");
+                               return;
+                       }
+               }
+               super.startARKFetcher();
+       }
+       
+       public String getTMCIPeerInfo() {
+               return getName()+'\t'+super.getTMCIPeerInfo();
+       }
+       
+       /**
+        * A method to be called once at the beginning of every time 
isConnected() is true
+        */
+       protected void onConnect() {
+               sendQueuedN2NTMs();
+       }
+
+       // File transfer offers
+       // FIXME this should probably be somewhere else, along with the N2NTM 
stuff... but where?
+       // FIXME this should be persistent across node restarts
+
+       /** Files I have offered to this peer */
+       private final HashMap myFileOffersByUID = new HashMap();
+       /** Files this peer has offered to me */
+       private final HashMap hisFileOffersByUID = new HashMap();
+       
+       private void storeOffers() {
+               // FIXME do something
+       }
+       
+       class FileOffer {
+               final long uid;
+               final String filename;
+               final String mimeType;
+               final String comment;
+               private RandomAccessThing data;
+               final long size;
+               /** Who is offering it? True = I am offering it, False = I am 
being offered it */
+               final boolean amIOffering;
+               private PartiallyReceivedBulk prb;
+               private BulkTransmitter transmitter;
+               private BulkReceiver receiver;
+               /** True if the offer has either been accepted or rejected */
+               private boolean acceptedOrRejected;
+               
+               FileOffer(long uid, RandomAccessThing data, String filename, 
String mimeType, String comment) throws IOException {
+                       this.uid = uid;
+                       this.data = data;
+                       this.filename = filename;
+                       this.mimeType = mimeType;
+                       this.comment = comment;
+                       size = data.size();
+                       amIOffering = true;
+               }
+
+               public FileOffer(SimpleFieldSet fs, boolean amIOffering) throws 
FSParseException {
+                       uid = fs.getLong("uid");
+                       size = fs.getLong("size");
+                       mimeType = fs.get("metadata.contentType");
+                       filename = FileUtil.sanitize(fs.get("filename"), 
mimeType);
+                       comment = fs.get("comment");
+                       this.amIOffering = amIOffering;
+               }
+
+               public void toFieldSet(SimpleFieldSet fs) {
+                       fs.put("uid", uid);
+                       fs.putSingle("filename", filename);
+                       fs.putSingle("metadata.contentType", mimeType);
+                       fs.putSingle("comment", comment);
+                       fs.put("size", size);
+               }
+
+               public void accept() {
+                       acceptedOrRejected = true;
+                       File dest = new File(node.clientCore.downloadDir, 
"direct-"+FileUtil.sanitize(getName())+"-"+filename);
+                       try {
+                               data = new RandomAccessFileWrapper(dest, "rw");
+                       } catch (FileNotFoundException e) {
+                               // Impossible
+                               throw new Error("Impossible: 
FileNotFoundException opening with RAF with rw! "+e, e);
+                       }
+                       prb = new PartiallyReceivedBulk(node.usm, size, 
Node.PACKET_SIZE, data, false);
+                       receiver = new BulkReceiver(prb, DarknetPeerNode.this, 
uid);
+                       // FIXME make this persistent
+                       Thread t = new Thread(new Runnable() {
+                               public void run() {
+                                       if(logMINOR)
+                                               Logger.minor(this, "Received 
file");
+                                       try {
+                                               if(!receiver.receive()) {
+                                                       String err = "Failed to 
receive "+this;
+                                                       Logger.error(this, err);
+                                                       System.err.println(err);
+                                                       onReceiveFailure();
+                                               } else {
+                                                       onReceiveSuccess();
+                                               }
+                                       } catch (Throwable t) {
+                                               Logger.error(this, "Caught 
"+t+" receiving file", t);
+                                               onReceiveFailure();
+                                       }
+                                       if(logMINOR)
+                                               Logger.minor(this, "Received 
file");
+                               }
+                       }, "Receiver for bulk transfer "+uid+":"+filename);
+                       t.setDaemon(true);
+                       t.start();
+                       if(logMINOR) Logger.minor(this, "Receiving on "+t);
+                       sendFileOfferAccepted(uid);
+               }
+
+               public void send() throws DisconnectedException {
+                       prb = new PartiallyReceivedBulk(node.usm, size, 
Node.PACKET_SIZE, data, true);
+                       transmitter = new BulkTransmitter(prb, 
DarknetPeerNode.this, uid, node.outputThrottle);
+                       if(logMINOR)
+                               Logger.minor(this, "Sending "+uid);
+                       Thread t = new Thread(new Runnable() {
+                               public void run() {
+                                       if(logMINOR)
+                                               Logger.minor(this, "Sending 
file");
+                                       try {
+                                               if(!transmitter.send()) {
+                                                       String err = "Failed to 
send "+uid+" for "+FileOffer.this;
+                                                       Logger.error(this, err);
+                                                       System.err.println(err);
+                                               }
+                                       } catch (Throwable t) {
+                                               Logger.error(this, "Caught 
"+t+" sending file", t);
+                                       }
+                                       if(logMINOR)
+                                               Logger.minor(this, "Sent file");
+                               }
+
+                       }, "Sender for bulk transfer "+uid+":"+filename);
+                       t.setDaemon(true);
+                       t.start();
+               }
+
+               public void reject() {
+                       acceptedOrRejected = true;
+                       sendFileOfferRejected(uid);
+               }
+
+               public void onRejected() {
+                       transmitter.cancel();
+                       // FIXME prb's can't be shared, right? Well they aren't 
here...
+                       prb.abort(RetrievalException.CANCELLED_BY_RECEIVER, 
"Cancelled by receiver");
+               }
+
+               protected void onReceiveFailure() {
+                       UserAlert alert = new UserAlert() {
+                               public String dismissButtonText() {
+                                       return L10n.getString("UserAlert.hide");
+                               }
+                               public HTMLNode getHTMLText() {
+                                       HTMLNode div = new HTMLNode("div");
+                                       
+                                       // FIXME localise!!!
+                                       
+                                       div.addChild("p", 
l10n("failedReceiveHeader", new String[] { "filename", "node" },
+                                                       new String[] { 
filename, getName() }));
+                                       
+                                       // Descriptive table
+                                       
+                                       HTMLNode table = div.addChild("table", 
"border", "0");
+                                       HTMLNode row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("fileLabel"));
+                                       row.addChild("td").addChild("#", 
filename);
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("sizeLabel"));
+                                       row.addChild("td").addChild("#", 
SizeUtil.formatSize(size));
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("mimeLabel"));
+                                       row.addChild("td").addChild("#", 
mimeType);
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("senderLabel"));
+                                       row.addChild("td").addChild("#", 
getName());
+                                       row = table.addChild("tr");
+                                       if(comment != null && comment.length() 
> 0) {
+                                               
row.addChild("td").addChild("#", l10n("commentLabel"));
+                                               
row.addChild("td").addChild("#", comment);
+                                       }
+                                       
+                                       return div;
+                               }
+
+                               public short getPriorityClass() {
+                                       return UserAlert.MINOR;
+                               }
+
+                               public String getText() {
+                                       StringBuffer sb = new StringBuffer();
+                                       sb.append(l10n("failedReceiveHeader", 
new String[] { "filename", "node" },
+                                                       new String[] { 
filename, getName() }));
+                                       sb.append('\n');
+                                       sb.append(l10n("fileLabel"));
+                                       sb.append(' ');
+                                       sb.append(filename);
+                                       sb.append('\n');
+                                       sb.append(l10n("sizeLabel"));
+                                       sb.append(' ');
+                                       sb.append(SizeUtil.formatSize(size));
+                                       sb.append('\n');
+                                       sb.append(l10n("mimeLabel"));
+                                       sb.append(' ');
+                                       sb.append(mimeType);
+                                       sb.append('\n');
+                                       sb.append(l10n("senderLabel"));
+                                       sb.append(' ');
+                                       sb.append(getName());
+                                       sb.append('\n');
+                                       if(comment != null && comment.length() 
> 0) {
+                                               sb.append(l10n("commentLabel"));
+                                               sb.append(' ');
+                                               sb.append(comment);
+                                       }
+                                       return sb.toString();
+                               }
+
+                               public String getTitle() {
+                                       return l10n("failedReceiveTitle");
+                               }
+
+                               public boolean isValid() {
+                                       return true;
+                               }
+
+                               public void isValid(boolean validity) {
+                                       // Ignore
+                               }
+
+                               public void onDismiss() {
+                                       // Ignore
+                               }
+
+                               public boolean shouldUnregisterOnDismiss() {
+                                       return true;
+                               }
+
+                               public boolean userCanDismiss() {
+                                       return true;
+                               }
+                               
+                       };
+                       node.clientCore.alerts.register(alert);
+               }
+
+               private void onReceiveSuccess() {
+                       UserAlert alert = new UserAlert() {
+                               public String dismissButtonText() {
+                                       return L10n.getString("UserAlert.hide");
+                               }
+                               public HTMLNode getHTMLText() {
+                                       HTMLNode div = new HTMLNode("div");
+                                       
+                                       // FIXME localise!!!
+                                       
+                                       div.addChild("p", 
l10n("succeededReceiveHeader", new String[] { "filename", "node" },
+                                                       new String[] { 
filename, getName() }));
+                                       
+                                       // Descriptive table
+                                       
+                                       HTMLNode table = div.addChild("table", 
"border", "0");
+                                       HTMLNode row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("fileLabel"));
+                                       row.addChild("td").addChild("#", 
filename);
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("sizeLabel"));
+                                       row.addChild("td").addChild("#", 
SizeUtil.formatSize(size));
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("mimeLabel"));
+                                       row.addChild("td").addChild("#", 
mimeType);
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("senderLabel"));
+                                       row.addChild("td").addChild("#", 
getName());
+                                       row = table.addChild("tr");
+                                       if(comment != null && comment.length() 
> 0) {
+                                               
row.addChild("td").addChild("#", l10n("commentLabel"));
+                                               
row.addChild("td").addChild("#", comment);
+                                       }
+                                       
+                                       return div;
+                               }
+
+                               public short getPriorityClass() {
+                                       return UserAlert.MINOR;
+                               }
+
+                               public String getText() {
+                                       StringBuffer sb = new StringBuffer();
+                                       
sb.append(l10n("succeededReceiveHeader", new String[] { "filename", "node" },
+                                                       new String[] { 
filename, getName() }));
+                                       sb.append('\n');
+                                       sb.append(l10n("fileLabel"));
+                                       sb.append(' ');
+                                       sb.append(filename);
+                                       sb.append('\n');
+                                       sb.append(l10n("sizeLabel"));
+                                       sb.append(' ');
+                                       sb.append(SizeUtil.formatSize(size));
+                                       sb.append('\n');
+                                       sb.append(l10n("mimeLabel"));
+                                       sb.append(' ');
+                                       sb.append(mimeType);
+                                       sb.append('\n');
+                                       sb.append(l10n("senderLabel"));
+                                       sb.append(' ');
+                                       sb.append(userToString());
+                                       sb.append('\n');
+                                       if(comment != null && comment.length() 
> 0) {
+                                               sb.append(l10n("commentLabel"));
+                                               sb.append(' ');
+                                               sb.append(comment);
+                                       }
+                                       return sb.toString();
+                               }
+
+                               public String getTitle() {
+                                       return l10n("succeededReceiveTitle");
+                               }
+
+                               public boolean isValid() {
+                                       return true;
+                               }
+
+                               public void isValid(boolean validity) {
+                                       // Ignore
+                               }
+
+                               public void onDismiss() {
+                                       // Ignore
+                               }
+
+                               public boolean shouldUnregisterOnDismiss() {
+                                       return true;
+                               }
+
+                               public boolean userCanDismiss() {
+                                       return true;
+                               }
+                               
+                       };
+                       node.clientCore.alerts.register(alert);
+               }
+
+               
+               /** Ask the user whether (s)he wants to download a file from a 
direct peer */
+               public UserAlert askUserUserAlert() {
+                       return new UserAlert() {
+                               public String dismissButtonText() {
+                                       return null; // Cannot hide, but can 
reject
+                               }
+                               public HTMLNode getHTMLText() {
+                                       HTMLNode div = new HTMLNode("div");
+                                       
+                                       div.addChild("p", 
l10n("offeredFileHeader", "name", getName()));
+                                       
+                                       // Descriptive table
+                                       
+                                       HTMLNode table = div.addChild("table", 
"border", "0");
+                                       HTMLNode row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("fileLabel"));
+                                       row.addChild("td").addChild("#", 
filename);
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("sizeLabel"));
+                                       row.addChild("td").addChild("#", 
SizeUtil.formatSize(size));
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("mimeLabel"));
+                                       row.addChild("td").addChild("#", 
mimeType);
+                                       row = table.addChild("tr");
+                                       row.addChild("td").addChild("#", 
l10n("senderLabel"));
+                                       row.addChild("td").addChild("#", 
getName());
+                                       row = table.addChild("tr");
+                                       if(comment != null && comment.length() 
> 0) {
+                                               
row.addChild("td").addChild("#", l10n("commentLabel"));
+                                               
row.addChild("td").addChild("#", comment);
+                                       }
+                                       
+                                       // Accept/reject form
+                                       
+                                       // Hopefully we will have a container 
when this function is called!
+                                       HTMLNode form = 
node.clientCore.getToadletContainer().addFormChild(div, "/friends/", 
"f2fFileOfferAcceptForm");
+                                       
+                                       // FIXME node_ is inefficient
+                                       form.addChild("input", new String[] { 
"type", "name" },
+                                                       new String[] { 
"hidden", "node_"+DarknetPeerNode.this.hashCode() });
+
+                                       form.addChild("input", new String[] { 
"type", "name", "value" },
+                                                       new String[] { 
"hidden", "id", Long.toString(uid) });
+                                       
+                                       form.addChild("input", new String[] { 
"type", "name", "value" }, 
+                                                       new String[] { 
"submit", "acceptTransfer", l10n("acceptTransferButton") });
+
+                                       form.addChild("input", new String[] { 
"type", "name", "value" }, 
+                                                       new String[] { 
"submit", "rejectTransfer", l10n("rejectTransferButton") });
+                                       
+                                       return div;
+                               }
+                               public short getPriorityClass() {
+                                       return UserAlert.MINOR;
+                               }
+                               public String getText() {
+                                       StringBuffer sb = new StringBuffer();
+                                       sb.append(l10n("offeredFileHeader", 
"name", getName()));
+                                       sb.append('\n');
+                                       sb.append(l10n("fileLabel"));
+                                       sb.append(' ');
+                                       sb.append(filename);
+                                       sb.append('\n');
+                                       sb.append(l10n("sizeLabel"));
+                                       sb.append(' ');
+                                       sb.append(SizeUtil.formatSize(size));
+                                       sb.append('\n');
+                                       sb.append(l10n("mimeLabel"));
+                                       sb.append(' ');
+                                       sb.append(mimeType);
+                                       sb.append('\n');
+                                       sb.append(l10n("senderLabel"));
+                                       sb.append(' ');
+                                       sb.append(userToString());
+                                       sb.append('\n');
+                                       if(comment != null && comment.length() 
> 0) {
+                                               sb.append(l10n("commentLabel"));
+                                               sb.append(' ');
+                                               sb.append(comment);
+                                       }
+                                       return sb.toString();
+                               }
+                               public String getTitle() {
+                                       return l10n("askUserTitle");
+                               }
+
+                               public boolean isValid() {
+                                       if(acceptedOrRejected) {
+                                               
node.clientCore.alerts.unregister(this);
+                                               return false;
+                                       }
+                                       return true;
+                               }
+                               public void isValid(boolean validity) {
+                                       // Ignore
+                               }
+                               public void onDismiss() {
+                                       // Ignore
+                               }
+                               public boolean shouldUnregisterOnDismiss() {
+                                       return false;
+                               }
+
+                               public boolean userCanDismiss() {
+                                       return false; // should accept or reject
+                               }
+                       };
+                       
+               }
+               private String l10n(String key) {
+                       return L10n.getString("FileOffer."+key);
+               }
+               private String l10n(String key, String pattern, String value) {
+                       return L10n.getString("FileOffer."+key, pattern, value);
+               }
+               private String l10n(String key, String[] pattern, String[] 
value) {
+                       return L10n.getString("FileOffer."+key, pattern, value);
+               }
+       }
+
+       public int sendTextMessage(String message) {
+               long now = System.currentTimeMillis();
+               SimpleFieldSet fs = new SimpleFieldSet(true);
+               fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
+               fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_USERALERT);
+               try {
+                       fs.putSingle("source_nodename", 
Base64.encode(node.getMyName().getBytes("UTF-8")));
+                       fs.putSingle("target_nodename", 
Base64.encode(getName().getBytes("UTF-8")));
+                       fs.putSingle("text", 
Base64.encode(message.getBytes("UTF-8")));
+                       fs.put("composedTime", now);
+                       fs.put("sentTime", now);
+                       Message n2ntm;
+                       n2ntm = DMT.createNodeToNodeMessage(
+                                       Node.N2N_MESSAGE_TYPE_FPROXY, fs
+                                                       
.toString().getBytes("UTF-8"));
+                       try {
+                               sendAsync(n2ntm, null, 0, null);
+                       } catch (NotConnectedException e) {
+                               fs.removeValue("sentTime");
+                               queueN2NTM(fs);
+                               setPeerNodeStatus(System.currentTimeMillis());
+                               return getPeerNodeStatus();
+                       }
+                       this.setPeerNodeStatus(System.currentTimeMillis());
+                       return getPeerNodeStatus();
+               } catch (UnsupportedEncodingException e) {
+                       throw new Error("Impossible: "+e, e);
+               }
+       }
+
+       public int sendFileOfferAccepted(long uid) {
+               storeOffers();
+               long now = System.currentTimeMillis();
+               SimpleFieldSet fs = new SimpleFieldSet(true);
+               fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
+               fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER_ACCEPTED);
+               try {
+                       fs.putSingle("source_nodename", 
Base64.encode(node.getMyName().getBytes("UTF-8")));
+                       fs.putSingle("target_nodename", 
Base64.encode(getName().getBytes("UTF-8")));
+                       fs.put("composedTime", now);
+                       fs.put("sentTime", now);
+                       fs.put("uid", uid);
+                       if(logMINOR)
+                               Logger.minor(this, "Sending node to node 
message (file offer accepted):\n"+fs);
+                       Message n2ntm;
+                       n2ntm = DMT.createNodeToNodeMessage(
+                                       Node.N2N_MESSAGE_TYPE_FPROXY, fs
+                                                       
.toString().getBytes("UTF-8"));
+                       try {
+                               sendAsync(n2ntm, null, 0, null);
+                       } catch (NotConnectedException e) {
+                               fs.removeValue("sentTime");
+                               queueN2NTM(fs);
+                               setPeerNodeStatus(System.currentTimeMillis());
+                               return getPeerNodeStatus();
+                       }
+                       this.setPeerNodeStatus(System.currentTimeMillis());
+                       return getPeerNodeStatus();
+               } catch (UnsupportedEncodingException e) {
+                       throw new Error("Impossible: "+e, e);
+               }
+       }
+
+       public int sendFileOfferRejected(long uid) {
+               storeOffers();
+               long now = System.currentTimeMillis();
+               SimpleFieldSet fs = new SimpleFieldSet(true);
+               fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
+               fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER_REJECTED);
+               try {
+                       fs.putSingle("source_nodename", 
Base64.encode(node.getMyName().getBytes("UTF-8")));
+                       fs.putSingle("target_nodename", 
Base64.encode(getName().getBytes("UTF-8")));
+                       fs.put("composedTime", now);
+                       fs.put("sentTime", now);
+                       fs.put("uid", uid);
+                       if(logMINOR)
+                               Logger.minor(this, "Sending node to node 
message (file offer rejected):\n"+fs);
+                       Message n2ntm;
+                       n2ntm = DMT.createNodeToNodeMessage(
+                                       Node.N2N_MESSAGE_TYPE_FPROXY, fs
+                                                       
.toString().getBytes("UTF-8"));
+                       try {
+                               sendAsync(n2ntm, null, 0, null);
+                       } catch (NotConnectedException e) {
+                               fs.removeValue("sentTime");
+                               queueN2NTM(fs);
+                               setPeerNodeStatus(System.currentTimeMillis());
+                               return getPeerNodeStatus();
+                       }
+                       this.setPeerNodeStatus(System.currentTimeMillis());
+                       return getPeerNodeStatus();
+               } catch (UnsupportedEncodingException e) {
+                       throw new Error("Impossible: "+e, e);
+               }
+       }
+
+       public int sendFileOffer(File filename, String message) throws 
IOException {
+               String fnam = filename.getName();
+               String mime = DefaultMIMETypes.guessMIMEType(fnam, false);
+               long uid = node.random.nextLong();
+               RandomAccessThing data = new RandomAccessFileWrapper(filename, 
"r");
+               FileOffer fo = new FileOffer(uid, data, fnam, mime, message);
+               synchronized(this) {
+                       myFileOffersByUID.put(new Long(uid), fo);
+               }
+               storeOffers();
+               long now = System.currentTimeMillis();
+               SimpleFieldSet fs = new SimpleFieldSet(true);
+               fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
+               fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER);
+               try {
+                       fs.putSingle("source_nodename", 
Base64.encode(node.getMyName().getBytes("UTF-8")));
+                       fs.putSingle("target_nodename", 
Base64.encode(getName().getBytes("UTF-8")));
+                       fs.put("composedTime", now);
+                       fs.put("sentTime", now);
+                       fo.toFieldSet(fs);
+                       if(logMINOR)
+                               Logger.minor(this, "Sending node to node 
message (file offer):\n"+fs);
+                       Message n2ntm;
+                       int status = getPeerNodeStatus();
+                       n2ntm = DMT.createNodeToNodeMessage(
+                                       Node.N2N_MESSAGE_TYPE_FPROXY, fs
+                                                       
.toString().getBytes("UTF-8"));
+                       try {
+                               sendAsync(n2ntm, null, 0, null);
+                       } catch (NotConnectedException e) {
+                               fs.removeValue("sentTime");
+                               queueN2NTM(fs);
+                               setPeerNodeStatus(System.currentTimeMillis());
+                               return getPeerNodeStatus();
+                       }
+                       return status;
+               } catch (UnsupportedEncodingException e) {
+                       throw new Error("Impossible: "+e, e);
+               }
+       }
+
+       public void handleFproxyN2NTM(SimpleFieldSet fs, int fileNumber) {
+               String source_nodename = null;
+               String target_nodename = null;
+               String text = null;
+               long composedTime;
+               long sentTime;
+               long receivedTime;
+               try {
+                       source_nodename = new 
String(Base64.decode(fs.get("source_nodename")));
+                       target_nodename = new 
String(Base64.decode(fs.get("target_nodename")));
+                       text = new String(Base64.decode(fs.get("text")));
+                       composedTime = fs.getLong("composedTime", -1);
+                       sentTime = fs.getLong("sentTime", -1);
+                       receivedTime = fs.getLong("receivedTime", -1);
+               } catch (IllegalBase64Exception e) {
+                       Logger.error(this, "Bad Base64 encoding when decoding a 
N2NTM SimpleFieldSet", e);
+                       return;
+               }
+               N2NTMUserAlert userAlert = new N2NTMUserAlert(this, 
source_nodename, target_nodename, text, fileNumber, composedTime, sentTime, 
receivedTime);
+               node.clientCore.alerts.register(userAlert);
+       }
+
+       public void handleFproxyFileOffer(SimpleFieldSet fs, int fileNumber) {
+               final FileOffer offer;
+               try {
+                       offer = new FileOffer(fs, false);
+               } catch (FSParseException e) {
+                       Logger.error(this, "Could not parse offer: "+e+" on 
"+this+" :\n"+fs, e);
+                       return;
+               }
+               Long u = new Long(offer.uid);
+               synchronized(this) {
+                       if(hisFileOffersByUID.containsKey(u)) return; // Ignore 
re-advertisement
+                       hisFileOffersByUID.put(u, offer);
+               }
+               
+               // Don't persist for now - FIXME
+               this.deleteExtraPeerDataFile(fileNumber);
+               
+               UserAlert alert = offer.askUserUserAlert();
+                       
+               node.clientCore.alerts.register(alert);
+       }
+
+       public void acceptTransfer(long id) {
+               if(logMINOR)
+                       Logger.minor(this, "Accepting transfer "+id+" on 
"+this);
+               FileOffer fo;
+               synchronized(this) {
+                       fo = (FileOffer) hisFileOffersByUID.get(new Long(id));
+               }
+               fo.accept();
+       }
+       
+       public void rejectTransfer(long id) {
+               FileOffer fo;
+               synchronized(this) {
+                       fo = (FileOffer) hisFileOffersByUID.remove(new 
Long(id));
+               }
+               fo.reject();
+       }
+       
+       public void handleFproxyFileOfferAccepted(SimpleFieldSet fs, int 
fileNumber) {
+               // Don't persist for now - FIXME
+               this.deleteExtraPeerDataFile(fileNumber);
+               
+               long uid;
+               try {
+                       uid = fs.getLong("uid");
+               } catch (FSParseException e) {
+                       Logger.error(this, "Could not parse offer accepted: 
"+e+" on "+this+" :\n"+fs, e);
+                       return;
+               }
+               if(logMINOR)
+                       Logger.minor(this, "Offer accepted for "+uid);
+               Long u = new Long(uid);
+               FileOffer fo;
+               synchronized(this) {
+                       fo = (FileOffer) (myFileOffersByUID.get(u));
+               }
+               if(fo == null) {
+                       Logger.error(this, "No such offer: "+uid);
+                       try {
+                               sendAsync(DMT.createFNPBulkSendAborted(uid), 
null, fileNumber, null);
+                       } catch (NotConnectedException e) {
+                               // Fine by me!
+                       }
+                       return;
+               }
+               try {
+                       fo.send();
+               } catch (DisconnectedException e) {
+                       Logger.error(this, "Cannot send because node 
disconnected: "+e+" for "+uid+":"+fo.filename, e);
+               }
+       }
+
+       public void handleFproxyFileOfferRejected(SimpleFieldSet fs, int 
fileNumber) {
+               // Don't persist for now - FIXME
+               this.deleteExtraPeerDataFile(fileNumber);
+               
+               long uid;
+               try {
+                       uid = fs.getLong("uid");
+               } catch (FSParseException e) {
+                       Logger.error(this, "Could not parse offer rejected: 
"+e+" on "+this+" :\n"+fs, e);
+                       return;
+               }
+               
+               FileOffer fo;
+               synchronized(this) {
+                       fo = (FileOffer) myFileOffersByUID.remove(new 
Long(uid));
+               }
+               fo.onRejected();
+       }
+
+       public String userToString() {
+               return ""+getPeer()+" : "+getName();
+       }
+
+       protected synchronized boolean innerCalcNextHandshake(boolean 
successfulHandshakeSend, boolean dontFetchARK, long now) {
+               boolean fetchARKFlag = false;
+               if(isBurstOnly) {
+                       listeningHandshakeBurstCount++;
+                       if(verifiedIncompatibleOlderVersion || 
verifiedIncompatibleNewerVersion) { 
+                               // Let them know we're here, but have no hope 
of connecting
+                               // Send one packet only.
+                               listeningHandshakeBurstCount = 0;
+                       } else if(listeningHandshakeBurstCount >= 
listeningHandshakeBurstSize) {
+                               listeningHandshakeBurstCount = 0;
+                               fetchARKFlag = true;
+                       }
+                       if(listeningHandshakeBurstCount == 0) {  // 0 only if 
we just reset it above
+                               sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_BURSTING_HANDSHAKE_BURSTS
+                                       + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_BURSTING_HANDSHAKE_BURSTS);
+                               listeningHandshakeBurstSize = 
Node.MIN_BURSTING_HANDSHAKE_BURST_SIZE
+                                               + 
node.random.nextInt(Node.RANDOMIZED_BURSTING_HANDSHAKE_BURST_SIZE);
+                               isBursting = false;
+                       } else {
+                               sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_HANDSHAKE_SENDS
+                                       + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_HANDSHAKE_SENDS);
+                       }
+                       if(logMINOR) Logger.minor(this, "Next BurstOnly mode 
handshake in "+(sendHandshakeTime - now)+"ms for "+getName()+" (count: 
"+listeningHandshakeBurstCount+", size: "+listeningHandshakeBurstSize+ ')', new 
Exception("double-called debug"));
+               }
+               return fetchARKFlag;
+       }
+}

Added: trunk/freenet/src/freenet/node/DarknetPeerNodeStatus.java
===================================================================
--- trunk/freenet/src/freenet/node/DarknetPeerNodeStatus.java                   
        (rev 0)
+++ trunk/freenet/src/freenet/node/DarknetPeerNodeStatus.java   2007-06-28 
17:07:40 UTC (rev 13809)
@@ -0,0 +1,62 @@
+package freenet.node;
+
+public class DarknetPeerNodeStatus extends PeerNodeStatus {
+
+       private final String name;
+
+       private final boolean burstOnly;
+
+       private final boolean listening;
+
+       private final boolean disabled;
+
+       private final String privateDarknetCommentNote;
+       
+       public DarknetPeerNodeStatus(DarknetPeerNode peerNode) {
+               super(peerNode);
+               this.name = peerNode.getName();
+               this.burstOnly = peerNode.isBurstOnly();
+               this.listening = peerNode.isListenOnly();
+               this.disabled = peerNode.isDisabled();
+               this.privateDarknetCommentNote = 
peerNode.getPrivateDarknetCommentNote();
+       }
+       
+       /**
+        * @return the name
+        */
+       public String getName() {
+               return name;
+       }
+
+       /**
+        * @return the burstOnly
+        */
+       public boolean isBurstOnly() {
+               return burstOnly;
+       }
+
+       /**
+        * @return the disabled
+        */
+       public boolean isDisabled() {
+               return disabled;
+       }
+
+       /**
+        * @return the listening
+        */
+       public boolean isListening() {
+               return listening;
+       }
+
+       /**
+        * @return the privateDarknetCommentNote
+        */
+       public String getPrivateDarknetCommentNote() {
+               return privateDarknetCommentNote;
+       }
+
+       public String toString() {
+               return name + ' ' + super.toString();
+       }
+}

Modified: trunk/freenet/src/freenet/node/FNPPacketMangler.java
===================================================================
--- trunk/freenet/src/freenet/node/FNPPacketMangler.java        2007-06-28 
15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/node/FNPPacketMangler.java        2007-06-28 
17:07:40 UTC (rev 13809)
@@ -402,15 +402,15 @@
         if(logMINOR) Logger.minor(this, "Processed: 
"+HexUtil.bytesToHex(data));
         long time2 = System.currentTimeMillis();
         if((time2 - time1) > 200) {
-          Logger.error(this, "sendFirstHalfDHPacket: time2 is more than 200ms 
after time1 ("+(time2 - time1)+") working on "+replyTo+" of "+pn.getName());
+          Logger.error(this, "sendFirstHalfDHPacket: time2 is more than 200ms 
after time1 ("+(time2 - time1)+") working on "+replyTo+" of 
"+pn.userToString());
         }
         sendAuthPacket(1, negType, phase, data, pn, replyTo);
         long time3 = System.currentTimeMillis();
         if((time3 - time2) > 500) {
-          Logger.error(this, "sendFirstHalfDHPacket:sendAuthPacket() time3 is 
more than half a second after time2 ("+(time3 - time2)+") working on 
"+replyTo+" of "+pn.getName());
+          Logger.error(this, "sendFirstHalfDHPacket:sendAuthPacket() time3 is 
more than half a second after time2 ("+(time3 - time2)+") working on 
"+replyTo+" of "+pn.userToString());
         }
         if((time3 - time1) > 500) {
-          Logger.error(this, "sendFirstHalfDHPacket: time3 is more than half a 
second after time1 ("+(time3 - time1)+") working on "+replyTo+" of 
"+pn.getName());
+          Logger.error(this, "sendFirstHalfDHPacket: time3 is more than half a 
second after time1 ("+(time3 - time1)+") working on "+replyTo+" of 
"+pn.userToString());
         }
     }

@@ -467,7 +467,7 @@
      }

     private void sendPacket(byte[] data, Peer replyTo, PeerNode pn, int 
alreadyReportedBytes) throws LocalAddressException {
-       if(pn.isIgnoreSourcePort()) {
+       if(pn.isIgnoreSource()) {
                Peer p = pn.getPeer();
                if(p != null) replyTo = p;
        }
@@ -1484,23 +1484,23 @@
         handshakeIPs = pn.getHandshakeIPs();
         long secondTime = System.currentTimeMillis();
         if((secondTime - firstTime) > 1000)
-            Logger.error(this, "getHandshakeIPs() took more than a second to 
execute ("+(secondTime - firstTime)+") working on "+pn.getName());
+            Logger.error(this, "getHandshakeIPs() took more than a second to 
execute ("+(secondTime - firstTime)+") working on "+pn.userToString());
         if(handshakeIPs.length == 0) {
             pn.couldNotSendHandshake();
             long thirdTime = System.currentTimeMillis();
             if((thirdTime - secondTime) > 1000)
-                Logger.error(this, "couldNotSendHandshake() (after 
getHandshakeIPs()) took more than a second to execute ("+(thirdTime - 
secondTime)+") working on "+pn.getName());
+                Logger.error(this, "couldNotSendHandshake() (after 
getHandshakeIPs()) took more than a second to execute ("+(thirdTime - 
secondTime)+") working on "+pn.userToString());
             return;
         } else {
             long DHTime1 = System.currentTimeMillis();
             ctx = DiffieHellman.generateContext();
             long DHTime2 = System.currentTimeMillis();
             if((DHTime2 - DHTime1) > 1000)
-                Logger.error(this, "DHTime2 is more than a second after 
DHTime1 ("+(DHTime2 - DHTime1)+") working on "+pn.getName());
+                Logger.error(this, "DHTime2 is more than a second after 
DHTime1 ("+(DHTime2 - DHTime1)+") working on "+pn.userToString());
             pn.setKeyAgreementSchemeContext(ctx);
             long DHTime3 = System.currentTimeMillis();
             if((DHTime3 - DHTime2) > 1000)
-                Logger.error(this, "DHTime3 is more than a second after 
DHTime2 ("+(DHTime3 - DHTime2)+") working on "+pn.getName());
+                Logger.error(this, "DHTime3 is more than a second after 
DHTime2 ("+(DHTime3 - DHTime2)+") working on "+pn.userToString());
         }
         int sentCount = 0;
         long loopTime1 = System.currentTimeMillis();
@@ -1516,20 +1516,20 @@
                }
                long innerLoopTime2 = System.currentTimeMillis();
                if((innerLoopTime2 - innerLoopTime1) > 500)
-                       Logger.normal(this, "innerLoopTime2 is more than half a 
second after innerLoopTime1 ("+(innerLoopTime2 - innerLoopTime1)+") working on 
"+handshakeIPs[i]+" of "+pn.getName());
+                       Logger.normal(this, "innerLoopTime2 is more than half a 
second after innerLoopTime1 ("+(innerLoopTime2 - innerLoopTime1)+") working on 
"+handshakeIPs[i]+" of "+pn.userToString());
                sendFirstHalfDHPacket(0, negType, ctx.getOurExponential(), pn, 
handshakeIPs[i]);
                long innerLoopTime3 = System.currentTimeMillis();
                if((innerLoopTime3 - innerLoopTime2) > 500)
-                       Logger.normal(this, "innerLoopTime3 is more than half a 
second after innerLoopTime2 ("+(innerLoopTime3 - innerLoopTime2)+") working on 
"+handshakeIPs[i]+" of "+pn.getName());
+                       Logger.normal(this, "innerLoopTime3 is more than half a 
second after innerLoopTime2 ("+(innerLoopTime3 - innerLoopTime2)+") working on 
"+handshakeIPs[i]+" of "+pn.userToString());
                pn.sentHandshake();
                long innerLoopTime4 = System.currentTimeMillis();
                if((innerLoopTime4 - innerLoopTime3) > 500)
-                       Logger.normal(this, "innerLoopTime4 is more than half a 
second after innerLoopTime3 ("+(innerLoopTime4 - innerLoopTime3)+") working on 
"+handshakeIPs[i]+" of "+pn.getName());
+                       Logger.normal(this, "innerLoopTime4 is more than half a 
second after innerLoopTime3 ("+(innerLoopTime4 - innerLoopTime3)+") working on 
"+handshakeIPs[i]+" of "+pn.userToString());
                sentCount += 1;
         }
         long loopTime2 = System.currentTimeMillis();
         if((loopTime2 - loopTime1) > 1000)
-               Logger.normal(this, "loopTime2 is more than a second after 
loopTime1 ("+(loopTime2 - loopTime1)+") working on "+pn.getName());
+               Logger.normal(this, "loopTime2 is more than a second after 
loopTime1 ("+(loopTime2 - loopTime1)+") working on "+pn.userToString());
         if(sentCount==0) {
             pn.couldNotSendHandshake();
         }

Modified: trunk/freenet/src/freenet/node/Node.java
===================================================================
--- trunk/freenet/src/freenet/node/Node.java    2007-06-28 15:02:56 UTC (rev 
13808)
+++ trunk/freenet/src/freenet/node/Node.java    2007-06-28 17:07:40 UTC (rev 
13809)
@@ -2550,8 +2550,8 @@
                return nodeUpdater;
        }

-       public PeerNode[] getDarknetConnections() {
-               return peers.myPeers;
+       public DarknetPeerNode[] getDarknetConnections() {
+               return peers.getDarknetPeers();
        }

        public boolean addDarknetConnection(PeerNode pn) {
@@ -2596,7 +2596,12 @@
         * Handle a received node to node message
         */
        public void receivedNodeToNodeMessage(Message m) {
-         PeerNode source = (PeerNode)m.getSource();
+         PeerNode src = (PeerNode) m.getSource();
+         if(!(src instanceof DarknetPeerNode)) {
+               Logger.error(this, "Got N2NTM from opennet node ?!?!?!: "+m+" 
from "+src);
+               return;
+         }
+         DarknetPeerNode source = (DarknetPeerNode)m.getSource();
          int type = ((Integer) 
m.getObject(DMT.NODE_TO_NODE_MESSAGE_TYPE)).intValue();
          if(type == Node.N2N_MESSAGE_TYPE_FPROXY) {
                ShortBuffer messageData = (ShortBuffer) 
m.getObject(DMT.NODE_TO_NODE_MESSAGE_DATA);
@@ -2640,7 +2645,7 @@
         * Handle a node to node text message SimpleFieldSet
         * @throws FSParseException 
         */
-       public void handleNodeToNodeTextMessageSimpleFieldSet(SimpleFieldSet 
fs, PeerNode source, int fileNumber) throws FSParseException {
+       public void handleNodeToNodeTextMessageSimpleFieldSet(SimpleFieldSet 
fs, DarknetPeerNode source, int fileNumber) throws FSParseException {
          if(logMINOR)
                  Logger.minor(this, "Got node to node message: \n"+fs);
          int overallType = fs.getInt("n2nType", 1); // FIXME remove default
@@ -2652,7 +2657,7 @@
          }
        }

-       private void 
handleFproxyNodeToNodeTextMessageSimpleFieldSet(SimpleFieldSet fs, PeerNode 
source, int fileNumber) throws FSParseException {
+       private void 
handleFproxyNodeToNodeTextMessageSimpleFieldSet(SimpleFieldSet fs, 
DarknetPeerNode source, int fileNumber) throws FSParseException {
                int type = fs.getInt("type");
                if(type == Node.N2N_TEXT_MESSAGE_TYPE_USERALERT) {
                        source.handleFproxyN2NTM(fs, fileNumber);
@@ -2718,8 +2723,8 @@
        /**
         * Return a peer of the node given its ip and port, name or identity, 
as a String
         */
-       public PeerNode getPeerNode(String nodeIdentifier) {
-               PeerNode[] pn = peers.myPeers;
+       public DarknetPeerNode getPeerNode(String nodeIdentifier) {
+               DarknetPeerNode[] pn = peers.getDarknetPeers();
                for(int i=0;i<pn.length;i++)
                {
                        Peer peer = pn[i].getPeer();

Modified: trunk/freenet/src/freenet/node/NodeIPDetector.java
===================================================================
--- trunk/freenet/src/freenet/node/NodeIPDetector.java  2007-06-28 15:02:56 UTC 
(rev 13808)
+++ trunk/freenet/src/freenet/node/NodeIPDetector.java  2007-06-28 17:07:40 UTC 
(rev 13809)
@@ -11,7 +11,6 @@
 import freenet.config.SubConfig;
 import freenet.io.comm.FreenetInetAddress;
 import freenet.io.comm.Peer;
-import freenet.io.comm.MessageCore;
 import freenet.io.comm.UdpSocketHandler;
 import freenet.l10n.L10n;
 import freenet.node.useralerts.IPUndetectedUserAlert;

Modified: trunk/freenet/src/freenet/node/NodeStats.java
===================================================================
--- trunk/freenet/src/freenet/node/NodeStats.java       2007-06-28 15:02:56 UTC 
(rev 13808)
+++ trunk/freenet/src/freenet/node/NodeStats.java       2007-06-28 17:07:40 UTC 
(rev 13809)
@@ -695,11 +695,11 @@
                fs.put("unclaimedFIFOSize", node.usm.getUnclaimedFIFOSize());

                /* gather connection statistics */
-               PeerNodeStatus[] peerNodeStatuses = peers.getPeerNodeStatuses();
+               DarknetPeerNodeStatus[] peerNodeStatuses = 
peers.getDarknetPeerNodeStatuses();
                Arrays.sort(peerNodeStatuses, new Comparator() {
                        public int compare(Object first, Object second) {
-                               PeerNodeStatus firstNode = (PeerNodeStatus) 
first;
-                               PeerNodeStatus secondNode = (PeerNodeStatus) 
second;
+                               DarknetPeerNodeStatus firstNode = 
(DarknetPeerNodeStatus) first;
+                               DarknetPeerNodeStatus secondNode = 
(DarknetPeerNodeStatus) second;
                                int statusDifference = 
firstNode.getStatusValue() - secondNode.getStatusValue();
                                if (statusDifference != 0) {
                                        return statusDifference;

Modified: trunk/freenet/src/freenet/node/PacketSender.java
===================================================================
--- trunk/freenet/src/freenet/node/PacketSender.java    2007-06-28 15:02:56 UTC 
(rev 13808)
+++ trunk/freenet/src/freenet/node/PacketSender.java    2007-06-28 17:07:40 UTC 
(rev 13809)
@@ -300,11 +300,11 @@
                        pn.startARKFetcher();
                 long afterHandshakeTime = System.currentTimeMillis();
                 if((afterHandshakeTime - beforeHandshakeTime) > (2*1000))
-                    Logger.error(this, "afterHandshakeTime is more than 2 
seconds past beforeHandshakeTime ("+(afterHandshakeTime - 
beforeHandshakeTime)+") in PacketSender working with "+pn.getPeer()+" named 
"+pn.getName());
+                    Logger.error(this, "afterHandshakeTime is more than 2 
seconds past beforeHandshakeTime ("+(afterHandshakeTime - 
beforeHandshakeTime)+") in PacketSender working with "+pn.userToString());
             }
                long tempNow = System.currentTimeMillis();
                if((tempNow - oldTempNow) > (5*1000))
-                       Logger.error(this, "tempNow is more than 5 seconds past 
oldTempNow ("+(tempNow - oldTempNow)+") in PacketSender working with 
"+pn.getPeer()+" named "+pn.getName());
+                       Logger.error(this, "tempNow is more than 5 seconds past 
oldTempNow ("+(tempNow - oldTempNow)+") in PacketSender working with 
"+pn.userToString());
                oldTempNow = tempNow;
        }


Modified: trunk/freenet/src/freenet/node/PeerManager.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerManager.java     2007-06-28 15:02:56 UTC 
(rev 13808)
+++ trunk/freenet/src/freenet/node/PeerManager.java     2007-06-28 17:07:40 UTC 
(rev 13809)
@@ -206,7 +206,8 @@
         if(peerNodePreviousRoutingBackoffReason != null) {
                
removePeerNodeRoutingBackoffReason(peerNodePreviousRoutingBackoffReason, pn);
         }
-        pn.removeExtraPeerDataDir();
+        if(pn instanceof DarknetPeerNode)
+               ((DarknetPeerNode)pn).removeExtraPeerDataDir();
         if(!isInPeers) return false;

         // removing from connectedPeers
@@ -829,10 +830,7 @@
         * Ask each PeerNode to read in it's extra peer data
         */
        public void readExtraPeerData() {
-               PeerNode[] peers;
-               synchronized (this) {
-                       peers = myPeers;
-               }
+               DarknetPeerNode[] peers = getDarknetPeers();
                for (int i = 0; i < peers.length; i++) {
                        try {
                                peers[i].readExtraPeerData();
@@ -1107,6 +1105,15 @@
                return peerNodeStatuses;
        }

+       public DarknetPeerNodeStatus[] getDarknetPeerNodeStatuses() {
+        DarknetPeerNode[] peers = getDarknetPeers();
+               DarknetPeerNodeStatus[] peerNodeStatuses = new 
DarknetPeerNodeStatus[peers.length];
+               for (int peerIndex = 0, peerCount = peers.length; peerIndex < 
peerCount; peerIndex++) {
+                       peerNodeStatuses[peerIndex] = (DarknetPeerNodeStatus) 
peers[peerIndex].getStatus();
+               }
+               return peerNodeStatuses;
+       }
+
        /**
         * Update hadRoutableConnectionCount/routableConnectionCheckCount on 
peers if the timer has expired
         */
@@ -1124,4 +1131,21 @@
                }
        }

+       /**
+        * Get the darknet peers list.
+        */
+       public DarknetPeerNode[] getDarknetPeers() {
+               PeerNode[] peers;
+               synchronized(this) {
+                       peers = myPeers;
+               }
+               // FIXME optimise! Maybe maintain as a separate list?
+               Vector v = new Vector(myPeers.length);
+               for(int i=0;i<peers.length;i++) {
+                       if(peers[i] instanceof DarknetPeerNode)
+                               v.add(peers[i]);
+               }
+               return (DarknetPeerNode[])v.toArray(new 
DarknetPeerNode[v.size()]);
+       }
+
 }

Modified: trunk/freenet/src/freenet/node/PeerNode.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNode.java        2007-06-28 15:02:56 UTC 
(rev 13808)
+++ trunk/freenet/src/freenet/node/PeerNode.java        2007-06-28 17:07:40 UTC 
(rev 13809)
@@ -1,16 +1,9 @@
 package freenet.node;

 import java.io.BufferedReader;
-import java.io.BufferedWriter;
 import java.io.ByteArrayInputStream;
-import java.io.EOFException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
 import java.lang.ref.WeakReference;
@@ -18,18 +11,14 @@
 import java.net.MalformedURLException;
 import java.net.UnknownHostException;
 import java.util.Arrays;
-import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.Iterator;
-import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.Vector;
 import java.util.zip.DataFormatException;
 import java.util.zip.Inflater;

 import net.i2p.util.NativeBigInteger;
-
-import freenet.client.DefaultMIMETypes;
 import freenet.client.FetchResult;
 import freenet.client.async.USKRetriever;
 import freenet.client.async.USKRetrieverCallback;
@@ -54,32 +43,20 @@
 import freenet.io.comm.PeerContext;
 import freenet.io.comm.PeerParseException;
 import freenet.io.comm.ReferenceSignatureVerificationException;
-import freenet.io.comm.RetrievalException;
 import freenet.io.comm.SocketHandler;
-import freenet.io.xfer.BulkReceiver;
-import freenet.io.xfer.BulkTransmitter;
 import freenet.io.xfer.PacketThrottle;
-import freenet.io.xfer.PartiallyReceivedBulk;
 import freenet.keys.ClientSSK;
 import freenet.keys.FreenetURI;
 import freenet.keys.Key;
 import freenet.keys.USK;
-import freenet.l10n.L10n;
-import freenet.node.useralerts.N2NTMUserAlert;
-import freenet.node.useralerts.UserAlert;
 import freenet.support.Base64;
 import freenet.support.Fields;
-import freenet.support.HTMLNode;
 import freenet.support.HexUtil;
 import freenet.support.IllegalBase64Exception;
 import freenet.support.LRUHashtable;
 import freenet.support.Logger;
 import freenet.support.SimpleFieldSet;
-import freenet.support.SizeUtil;
 import freenet.support.WouldBlockException;
-import freenet.support.io.FileUtil;
-import freenet.support.io.RandomAccessFileWrapper;
-import freenet.support.io.RandomAccessThing;
 import freenet.support.math.RunningAverage;
 import freenet.support.math.SimpleRunningAverage;
 import freenet.support.math.TimeDecayingRunningAverage;
@@ -104,12 +81,12 @@
     /** Set to true based on a relevant incoming handshake from this peer
      *  Set true if this peer has a incompatible older build than we are
      */
-    private boolean verifiedIncompatibleOlderVersion;
+    protected boolean verifiedIncompatibleOlderVersion;

     /** Set to true based on a relevant incoming handshake from this peer
      *  Set true if this peer has a incompatible newer build than we are
      */
-    private boolean verifiedIncompatibleNewerVersion;
+    protected boolean verifiedIncompatibleNewerVersion;

     /** My low-level address for SocketManager purposes */
     private Peer detectedPeer;
@@ -118,7 +95,7 @@
     private final OutgoingPacketMangler outgoingMangler;

     /** Advertised addresses */
-    private Vector nominalPeer;
+    protected Vector nominalPeer;

     /** The PeerNode's report of our IP address */
     private Peer remoteDetectedPeer;
@@ -126,9 +103,6 @@
     /** Is this a testnet node? */
     public final boolean testnetEnabled;

-    /** Name of this node */
-    String myName;
-    
     /** Packets sent/received on the current preferred key */
     private KeyTracker currentTracker;

@@ -176,12 +150,6 @@
     /** After this many failed handshakes, we start the ARK fetcher. */
     private static final int MAX_HANDSHAKE_COUNT = 2;

-    /** Number of handshake attempts (while in ListenOnly mode) since the 
beginning of this burst */
-    private int listeningHandshakeBurstCount;
-    
-    /** Total number of handshake attempts (while in ListenOnly mode) to be in 
this burst */
-    private int listeningHandshakeBurstSize;
-    
     /** Current location in the keyspace */
     private Location currentLocation;

@@ -232,7 +200,7 @@
     final boolean decrementHTLAtMinimum;

     /** Time at which we should send the next handshake request */
-    private long sendHandshakeTime;
+    protected long sendHandshakeTime;

     /** Time after which we log message requeues while rate limiting */
     private long nextMessageRequeueLogTime;
@@ -305,43 +273,9 @@
     /** When this peer was added to this node */
     private long peerAddedTime = 1;

-    /** True if this peer is not to be connected with */
-    private boolean isDisabled;
-    
-    /** True if we don't send handshake requests to this peer, but will 
connect if we receive one */
-    private boolean isListenOnly;
-    
-    /** True if we send handshake requests to this peer in infrequent bursts */
-    private boolean isBurstOnly;
-    
-    /** True if we are currently sending this peer a burst of handshake 
requests */
-    private boolean isBursting;
-
-    /** True if we want to ignore the source port of the node's sent packets.
-     * This is normally set when dealing with an Evil Corporate Firewall which 
rewrites the port on outgoing
-     * packets but does not redirect incoming packets destined to the 
rewritten port.
-     * What it does is this: If we have an address with the same IP but a 
different port, to the detectedPeer,
-     * we use that instead. */
-    private boolean ignoreSourcePort;
-    
-    /** True if we want to allow LAN/localhost addresses. */
-    private boolean allowLocalAddresses;
-    
-    /** Extra peer data file numbers */
-    private LinkedHashSet extraPeerDataFileNumbers;
-
     /** Average proportion of requests which are rejected or timed out */
     private TimeDecayingRunningAverage pRejected;

-    /** Private comment on the peer for /friends/ page */
-    private String privateDarknetComment;
-    
-    /** Private comment on the peer for /friends/ page's extra peer data file 
number */
-    private int privateDarknetCommentFileNumber;
-    
-    /** Queued-to-send N2NTM extra peer data file numbers */
-    private LinkedHashSet queuedToSendN2NTMExtraPeerDataFileNumbers;
-
     /** Total low-level input bytes */
     private long totalBytesIn;

@@ -381,7 +315,7 @@
      * @param node2 The running Node we are part of.
      */
     public PeerNode(SimpleFieldSet fs, Node node2, PeerManager peers, boolean 
fromLocal, OutgoingPacketMangler mangler) throws FSParseException, 
PeerParseException, ReferenceSignatureVerificationException {
-       logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       logMINOR = Logger.shouldLog(Logger.MINOR, PeerNode.class);
        myRef = new WeakReference(this);
        this.outgoingMangler = mangler;
         this.node = node2;
@@ -414,9 +348,6 @@

         updateShouldDisconnectNow();

-        String name = fs.get("myName");
-        if(name == null) throw new FSParseException("No name");
-        myName = name;
         String testnet = fs.get("testnet");
         testnetEnabled = Fields.stringToBool(fs.get("testnet"), true);
         if(node.testnetEnabled != testnetEnabled) {
@@ -444,7 +375,7 @@
                 throw new FSParseException(e1);
         }
         if(nominalPeer.isEmpty()) {
-               Logger.normal(this, "No IP addresses found for identity 
'"+Base64.encode(identity)+"', possibly at location 
'"+Double.toString(currentLocation.getValue())+"' with name '"+getName()+ '\'');
+               Logger.normal(this, "No IP addresses found for identity 
'"+Base64.encode(identity)+"', possibly at location 
'"+Double.toString(currentLocation.getValue())+": "+userToString());
                detectedPeer = null;
         } else {
                detectedPeer = (Peer) nominalPeer.firstElement();
@@ -633,11 +564,6 @@
                if(!neverConnected) {
                        peerAddedTime = 0;  // don't store anymore
                }
-               isDisabled = Fields.stringToBool(metadata.get("isDisabled"), 
false);
-               isListenOnly = 
Fields.stringToBool(metadata.get("isListenOnly"), false);
-               isBurstOnly = Fields.stringToBool(metadata.get("isBurstOnly"), 
false);
-               ignoreSourcePort = 
Fields.stringToBool(metadata.get("ignoreSourcePort"), false);
-               allowLocalAddresses = 
Fields.stringToBool(metadata.get("allowLocalAddresses"), false);
                String tempHadRoutableConnectionCountString = 
metadata.get("hadRoutableConnectionCount");
                if(tempHadRoutableConnectionCountString != null) {
                        long tempHadRoutableConnectionCount = 
Long.parseLong(tempHadRoutableConnectionCountString);
@@ -663,25 +589,9 @@

         sendHandshakeTime = now;  // Be sure we're ready to handshake right 
away

-               listeningHandshakeBurstCount = 0;
-               listeningHandshakeBurstSize = 
Node.MIN_BURSTING_HANDSHAKE_BURST_SIZE
-                               + 
node.random.nextInt(Node.RANDOMIZED_BURSTING_HANDSHAKE_BURST_SIZE);
-               if(isBurstOnly) {
-                       Logger.minor(this, "First BurstOnly mode handshake in 
"+(sendHandshakeTime - now)+"ms for "+getName()+" (count: 
"+listeningHandshakeBurstCount+", size: "+listeningHandshakeBurstSize+ ')');
-               }
-
         // status may have changed from PEER_NODE_STATUS_DISCONNECTED to 
PEER_NODE_STATUS_NEVER_CONNECTED
         setPeerNodeStatus(now);

-               // Setup the private darknet comment note
-        privateDarknetComment = "";
-        privateDarknetCommentFileNumber = -1;
-
-               // Setup the extraPeerDataFileNumbers
-               extraPeerDataFileNumbers = new LinkedHashSet();
-               
-               // Setup the queuedToSendN2NTMExtraPeerDataFileNumbers
-               queuedToSendN2NTMExtraPeerDataFileNumbers = new LinkedHashSet();
     }

     private boolean parseARK(SimpleFieldSet fs, boolean onStartup) {
@@ -725,24 +635,13 @@
     }

     /**
-     * Get my low-level address.
+     * Get my low-level address. This is the address that packets have been 
received from from this node.
      * 
      * Normally this is the address that packets have been received from from 
this node.
      * However, if ignoreSourcePort is set, we will search for a similar 
address with a different port 
      * number in the node reference.
      */
     public synchronized Peer getPeer(){
-       if(ignoreSourcePort) {
-               FreenetInetAddress addr = detectedPeer == null ? null : 
detectedPeer.getFreenetAddress();
-               int port = detectedPeer == null ? -1 : detectedPeer.getPort();
-               if(nominalPeer == null) return detectedPeer;
-               for(int i=0;i<nominalPeer.size();i++) {
-                       Peer p = (Peer) nominalPeer.get(i);
-                       if(p.getPort() != port && 
p.getFreenetAddress().equals(addr)) {
-                               return p;
-                       }
-               }
-       }
        return detectedPeer;
     }

@@ -791,12 +690,12 @@
             // Don't do a DNS request on the first cycle through PeerNodes by 
DNSRequest
             // upon startup (I suspect the following won't do anything, but 
just in case)
                if(logMINOR)
-                       Logger.debug(this, "updateHandshakeIPs: calling 
getAddress(false) on Peer '"+localHandshakeIPs[i]+"' for PeerNode 
'"+getPeer()+"' named '"+getName()+"' ("+ignoreHostnames+ ')');
+                       Logger.debug(this, "updateHandshakeIPs: calling 
getAddress(false) on Peer '"+localHandshakeIPs[i]+"' for "+shortToString()+" 
("+ignoreHostnames+ ')');
             localHandshakeIPs[i].getAddress(false);
           } else {
             // Actually do the DNS request for the member Peer of 
localHandshakeIPs
                if(logMINOR)
-                       Logger.debug(this, "updateHandshakeIPs: calling 
getHandshakeAddress() on Peer '"+localHandshakeIPs[i]+"' for PeerNode 
'"+getPeer()+"' named '"+getName()+"' ("+ignoreHostnames+ ')');
+                       Logger.debug(this, "updateHandshakeIPs: calling 
getHandshakeAddress() on Peer '"+localHandshakeIPs[i]+"' for 
"+shortToString()+" ("+ignoreHostnames+ ')');
             localHandshakeIPs[i].getHandshakeAddress();
           }
         }
@@ -821,7 +720,7 @@
                                lastAttemptedHandshakeIPUpdateTime = now;
                        }
            }
-       if(logMINOR) Logger.minor(this, "Updating handshake IPs for peer 
'"+getPeer()+"' named '"+getName()+"' ("+ignoreHostnames+ ')');
+       if(logMINOR) Logger.minor(this, "Updating handshake IPs for peer 
'"+shortToString()+"' ("+ignoreHostnames+ ')');
        Peer[] localHandshakeIPs;
        Peer[] myNominalPeer;

@@ -1132,20 +1031,12 @@
         boolean tempShouldSendHandshake = false;
         synchronized(this) {
                tempShouldSendHandshake = (!isConnected()) &&
-                               (!isDisabled) &&  // don't connect to disabled 
peers
-                               (!isListenOnly) &&  // don't send handshake 
requests to isListenOnly peers
                 (handshakeIPs != null) &&
                 (now > sendHandshakeTime);
                }
                if(tempShouldSendHandshake && (hasLiveHandshake(now))) {
                        tempShouldSendHandshake = false;
                }
-               if(tempShouldSendHandshake && isBurstOnly()) {
-                       synchronized(this) {
-                               isBursting = true;
-                       }
-                       setPeerNodeStatus(now);
-               }
                return tempShouldSendHandshake;
     }

@@ -1165,50 +1056,32 @@

     boolean firstHandshake = true;

-    private void calcNextHandshake(boolean successfulHandshakeSend, boolean 
dontFetchARK) {
+    /**
+     * Set sendHandshakeTime, and return whether to fetch the ARK.
+     */
+    protected synchronized boolean innerCalcNextHandshake(boolean 
successfulHandshakeSend, boolean dontFetchARK, long now) {
+               if(verifiedIncompatibleOlderVersion || 
verifiedIncompatibleNewerVersion) { 
+                       // Let them know we're here, but have no hope of 
connecting
+                       sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_VERSION_SENDS
+                               + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_VERSION_SENDS);
+               } else if(invalidVersion() && !firstHandshake) {
+                       sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_VERSION_PROBES
+                               + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_VERSION_PROBES);
+               } else {
+                       sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_HANDSHAKE_SENDS
+                               + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_HANDSHAKE_SENDS);
+               }
+               if(successfulHandshakeSend) {
+                       firstHandshake = false;
+               }
+               handshakeCount++;
+               return ((handshakeCount == MAX_HANDSHAKE_COUNT) && 
!(verifiedIncompatibleOlderVersion || verifiedIncompatibleNewerVersion));
+    }
+    
+    protected void calcNextHandshake(boolean successfulHandshakeSend, boolean 
dontFetchARK) {
         long now = System.currentTimeMillis();
         boolean fetchARKFlag = false;
-        synchronized(this) {
-                       if(!isBurstOnly) {
-                               if(verifiedIncompatibleOlderVersion || 
verifiedIncompatibleNewerVersion) { 
-                                       // Let them know we're here, but have 
no hope of connecting
-                                       sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_VERSION_SENDS
-                                               + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_VERSION_SENDS);
-                               } else if(invalidVersion() && !firstHandshake) {
-                                       sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_VERSION_PROBES
-                                               + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_VERSION_PROBES);
-                               } else {
-                                       sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_HANDSHAKE_SENDS
-                                               + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_HANDSHAKE_SENDS);
-                               }
-                               if(successfulHandshakeSend) {
-                                       firstHandshake = false;
-                               }
-                               handshakeCount++;
-                               fetchARKFlag = ((handshakeCount == 
MAX_HANDSHAKE_COUNT) && !(verifiedIncompatibleOlderVersion || 
verifiedIncompatibleNewerVersion));
-                       } else {
-                               listeningHandshakeBurstCount++;
-                               if(verifiedIncompatibleOlderVersion || 
verifiedIncompatibleNewerVersion) { 
-                                       // Let them know we're here, but have 
no hope of connecting
-                                       // Send one packet only.
-                                       listeningHandshakeBurstCount = 0;
-                               } else if(listeningHandshakeBurstCount >= 
listeningHandshakeBurstSize) {
-                                       listeningHandshakeBurstCount = 0;
-                                       fetchARKFlag = true;
-                               }
-                               if(listeningHandshakeBurstCount == 0) {  // 0 
only if we just reset it above
-                                       sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_BURSTING_HANDSHAKE_BURSTS
-                                               + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_BURSTING_HANDSHAKE_BURSTS);
-                                       listeningHandshakeBurstSize = 
Node.MIN_BURSTING_HANDSHAKE_BURST_SIZE
-                                                       + 
node.random.nextInt(Node.RANDOMIZED_BURSTING_HANDSHAKE_BURST_SIZE);
-                                       isBursting = false;
-                               } else {
-                                       sendHandshakeTime = now + 
Node.MIN_TIME_BETWEEN_HANDSHAKE_SENDS
-                                               + 
node.random.nextInt(Node.RANDOMIZED_TIME_BETWEEN_HANDSHAKE_SENDS);
-                               }
-                               if(logMINOR) Logger.minor(this, "Next BurstOnly 
mode handshake in "+(sendHandshakeTime - now)+"ms for "+getName()+" (count: 
"+listeningHandshakeBurstCount+", size: "+listeningHandshakeBurstSize+ ')', new 
Exception("double-called debug"));
-                       }
-        }
+        fetchARKFlag = innerCalcNextHandshake(successfulHandshakeSend, 
dontFetchARK, now);
                setPeerNodeStatus(now);  // Because of isBursting being set 
above and it can't hurt others
         // Don't fetch ARKs for peers we have verified (through handshake) to 
be incompatible with us
         if(fetchARKFlag && !dontFetchARK) {
@@ -1216,7 +1089,7 @@
                        startARKFetcher();
                        long arkFetcherStartTime2 = System.currentTimeMillis();
                        if((arkFetcherStartTime2 - arkFetcherStartTime1) > 500) 
{
-                               Logger.normal(this, "arkFetcherStartTime2 is 
more than half a second after arkFetcherStartTime1 ("+(arkFetcherStartTime2 - 
arkFetcherStartTime1)+") working on "+getName());
+                               Logger.normal(this, "arkFetcherStartTime2 is 
more than half a second after arkFetcherStartTime1 ("+(arkFetcherStartTime2 - 
arkFetcherStartTime1)+") working on "+shortToString());
                        }
         }
     }
@@ -1356,7 +1229,7 @@
      * Update the Location to a new value.
      */
     public void updateLocation(double newLoc) {
-       logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       logMINOR = Logger.shouldLog(Logger.MINOR, PeerNode.class);
        synchronized(this) {
                        currentLocation.setValue(newLoc);
                }
@@ -1499,7 +1372,7 @@
      * @return True unless we rejected the handshake, or it failed to parse.
      */
     public boolean completedHandshake(long thisBootID, byte[] data, int 
offset, int length, BlockCipher encCipher, byte[] encKey, Peer replyTo, boolean 
unverified) {
-       logMINOR = Logger.shouldLog(Logger.MINOR, this);
+       logMINOR = Logger.shouldLog(Logger.MINOR, PeerNode.class);
        long now = System.currentTimeMillis();

        // Update sendHandshakeTime; don't send another handshake for a while.
@@ -1593,7 +1466,7 @@
                if(oldCur != null) oldCur.completelyDeprecated(newTracker);
                if(prev != null) prev.deprecated();
                Logger.normal(this, "Completed handshake with "+this+" on 
"+replyTo+" - current: "+currentTracker+
-                               " old: "+previousTracker+" unverified: 
"+unverifiedTracker+" bootID: "+thisBootID+" getName(): "+getName());
+                               " old: "+previousTracker+" unverified: 
"+unverifiedTracker+" bootID: "+thisBootID+" for "+shortToString());

                // Received a packet
                receivedPacket(unverified);
@@ -1622,17 +1495,13 @@
                                Logger.minor(this, "No ARK for "+this+" !!!!");
                                return;
                        }
-                       if(isListenOnly){
-                               Logger.minor(this, "Not starting ark fetcher 
for "+this+" as it's in listen-only mode.");
-                       }else{
-                               Logger.minor(this, "Starting ARK fetcher for 
"+this+" : "+myARK);
-                               if(arkFetcher == null)
-                                       arkFetcher = 
node.clientCore.uskManager.subscribeContent(myARK, this, true, 
node.arkFetcherContext, RequestStarter.IMMEDIATE_SPLITFILE_PRIORITY_CLASS, 
node);
-                       }
+                       Logger.minor(this, "Starting ARK fetcher for "+this+" : 
"+myARK);
+                       if(arkFetcher == null)
+                               arkFetcher = 
node.clientCore.uskManager.subscribeContent(myARK, this, true, 
node.arkFetcherContext, RequestStarter.IMMEDIATE_SPLITFILE_PRIORITY_CLASS, 
node);
                }
     }

-    private void stopARKFetcher() {
+    protected void stopARKFetcher() {
        Logger.minor(this, "Stopping ARK fetcher for "+this+" : "+myARK);
        // FIXME any way to reduce locking here?
        synchronized(arkFetcherSync) {
@@ -1797,7 +1666,7 @@

     /** The synchronized part of processNewNoderef 
      * @throws FSParseException */
-    private synchronized boolean innerProcessNewNoderef(SimpleFieldSet fs, 
boolean forARK) throws FSParseException {
+    protected synchronized boolean innerProcessNewNoderef(SimpleFieldSet fs, 
boolean forARK) throws FSParseException {
         boolean changedAnything = false;
         String identityString = fs.get("identity");
         if(identityString != null) {
@@ -1884,8 +1753,6 @@
         // DO NOT change detectedPeer !!!
         // The given physical.udp may be WRONG!!!

-        String name = fs.get("myName");
-        
         // In future, ARKs may support automatic transition when the ARK key 
is changed.
         // So parse it anyway. If it fails, no big loss; it won't even log an 
error.

@@ -1901,11 +1768,6 @@

         if(parseARK(fs, false))
                changedAnything = true;
-        if(name != null && !name.equals(myName)) {
-               changedAnything = true;
-            myName = name;
-        }
-        
                return changedAnything;
        }

@@ -1966,7 +1828,7 @@
         }
         if((getPeerNodeStatus() == 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED) && (getPeerAddedTime() > 1))
             idle = (int) ((now - getPeerAddedTime()) / 1000);
-        return getName()+ '\t' +getPeer()+ '\t' +getIdentityString()+ '\t' 
+getLocation().getValue()+ '\t' +getPeerNodeStatusString()+ '\t' +idle;
+        return String.valueOf(getPeer())+ '\t' +getIdentityString()+ '\t' 
+getLocation().getValue()+ '\t' +getPeerNodeStatusString()+ '\t' +idle;
     }

     public String getFreevizOutput() {
@@ -2013,16 +1875,6 @@
                fs.putSingle("peerAddedTime", Long.toString(peerAddedTime));
        if(neverConnected)
                fs.putSingle("neverConnected", "true");
-       if(isDisabled)
-               fs.putSingle("isDisabled", "true");
-       if(isListenOnly)
-               fs.putSingle("isListenOnly", "true");
-       if(isBurstOnly)
-               fs.putSingle("isBurstOnly", "true");
-       if(ignoreSourcePort)
-               fs.putSingle("ignoreSourcePort", "true");
-       if(allowLocalAddresses)
-               fs.putSingle("allowLocalAddresses", "true");
        if(hadRoutableConnectionCount > 0)
                fs.putSingle("hadRoutableConnectionCount", 
Long.toString(hadRoutableConnectionCount));
        if(routableConnectionCheckCount > 0)
@@ -2073,7 +1925,6 @@
         fs.putSingle("location", Double.toString(currentLocation.getValue()));
         fs.putSingle("testnet", Boolean.toString(testnetEnabled));
         fs.putSingle("version", version);
-        fs.putSingle("myName", getName());
         if(peerCryptoGroup != null)
                fs.put("dsaGroup", peerCryptoGroup.asFieldSet());
         if(peerPubKey != null)
@@ -2343,10 +2194,6 @@
                return remoteDetectedPeer;
        }

-       public synchronized String getName() {
-               return myName;
-       }
-
        public synchronized int getRoutingBackoffLength() {
                return routingBackoffLength;
        }
@@ -2496,56 +2343,54 @@
        return "peer_unknown_status";
   }

-       public void setPeerNodeStatus(long now) {
-               long localRoutingBackedOffUntil = getRoutingBackedOffUntil();
-               synchronized(this) {
-                       checkConnectionsAndTrackers();
-                       int oldPeerNodeStatus = peerNodeStatus;
-                       if(isRoutable()) {  // Function use also updates 
timeLastConnected and timeLastRoutable
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_CONNECTED;
-                               if(now < localRoutingBackedOffUntil ) {
-                                       peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF;
-                                       
if(!lastRoutingBackoffReason.equals(previousRoutingBackoffReason) || 
(previousRoutingBackoffReason == null)) {
-                                               if(previousRoutingBackoffReason 
!= null) {
-                                                       
peers.removePeerNodeRoutingBackoffReason(previousRoutingBackoffReason, this);
-                                               }
-                                               
peers.addPeerNodeRoutingBackoffReason(lastRoutingBackoffReason, this);
-                                               previousRoutingBackoffReason = 
lastRoutingBackoffReason;
-                                       }
-                               } else {
+    protected synchronized int getPeerNodeStatus(long now, long 
routingBackedOffUntil) {
+               checkConnectionsAndTrackers();
+               int oldPeerNodeStatus = peerNodeStatus;
+               if(isRoutable()) {  // Function use also updates 
timeLastConnected and timeLastRoutable
+                       peerNodeStatus = PeerManager.PEER_NODE_STATUS_CONNECTED;
+                       if(now < routingBackedOffUntil) {
+                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_ROUTING_BACKED_OFF;
+                               
if(!lastRoutingBackoffReason.equals(previousRoutingBackoffReason) || 
(previousRoutingBackoffReason == null)) {
                                        if(previousRoutingBackoffReason != 
null) {
                                                
peers.removePeerNodeRoutingBackoffReason(previousRoutingBackoffReason, this);
-                                               previousRoutingBackoffReason = 
null;
                                        }
+                                       
peers.addPeerNodeRoutingBackoffReason(lastRoutingBackoffReason, this);
+                                       previousRoutingBackoffReason = 
lastRoutingBackoffReason;
                                }
-                       } else if(isDisabled) {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_DISABLED;
-                       } else if(isConnected() && 
verifiedIncompatibleNewerVersion) {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_TOO_NEW;
-                       } else if(isConnected && 
verifiedIncompatibleOlderVersion) {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_TOO_OLD;
-                       } else if(isConnected && Math.abs(clockDelta) > 
MAX_CLOCK_DELTA) {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_CLOCK_PROBLEM;
-                       } else if(neverConnected) {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED;
-                       } else if(isListenOnly) {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_LISTEN_ONLY;
-                       } else if(isBursting) {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_BURSTING;
-                       } else if(isBurstOnly) {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_LISTENING;
                        } else {
-                               peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_DISCONNECTED;
+                               if(previousRoutingBackoffReason != null) {
+                                       
peers.removePeerNodeRoutingBackoffReason(previousRoutingBackoffReason, this);
+                                       previousRoutingBackoffReason = null;
+                               }
                        }
-                       if(!isConnected && (previousRoutingBackoffReason != 
null)) {
-                               
peers.removePeerNodeRoutingBackoffReason(previousRoutingBackoffReason, this);
-                               previousRoutingBackoffReason = null;
-                       }
-                       if(peerNodeStatus != oldPeerNodeStatus) {
-                               peers.removePeerNodeStatus( oldPeerNodeStatus, 
this );
-                         peers.addPeerNodeStatus( peerNodeStatus, this );
-                       }
+               } else if(isConnected() && verifiedIncompatibleNewerVersion) {
+                       peerNodeStatus = PeerManager.PEER_NODE_STATUS_TOO_NEW;
+               } else if(isConnected && verifiedIncompatibleOlderVersion) {
+                       peerNodeStatus = PeerManager.PEER_NODE_STATUS_TOO_OLD;
+               } else if(isConnected && Math.abs(clockDelta) > 
MAX_CLOCK_DELTA) {
+                       peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_CLOCK_PROBLEM;
+               } else if(neverConnected) {
+                       peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_NEVER_CONNECTED;
+               } else {
+                       peerNodeStatus = 
PeerManager.PEER_NODE_STATUS_DISCONNECTED;
                }
+               if(!isConnected && (previousRoutingBackoffReason != null)) {
+                       
peers.removePeerNodeRoutingBackoffReason(previousRoutingBackoffReason, this);
+                       previousRoutingBackoffReason = null;
+               }
+               if(peerNodeStatus != oldPeerNodeStatus) {
+                       peers.removePeerNodeStatus( oldPeerNodeStatus, this );
+                 peers.addPeerNodeStatus( peerNodeStatus, this );
+               }
+               return peerNodeStatus;
+    }
+  
+       public int setPeerNodeStatus(long now) {
+               long routingBackedOffUntil = getRoutingBackedOffUntil();
+               synchronized(this) {
+                       peerNodeStatus = getPeerNodeStatus(now, 
routingBackedOffUntil);
+               }
+               return peerNodeStatus;
        }

        private synchronized void checkConnectionsAndTrackers() {
@@ -2585,80 +2430,6 @@
                return handshakeCount;
        }

-       public void enablePeer() {
-               synchronized(this) {
-                       isDisabled = false;
-               }
-               setPeerNodeStatus(System.currentTimeMillis());
-        node.peers.writePeers();
-       }
-       
-       public void disablePeer() {
-               synchronized(this) {
-                       isDisabled = true;
-               }
-               if(isConnected()) {
-                       forceDisconnect();
-               }
-               stopARKFetcher();
-               setPeerNodeStatus(System.currentTimeMillis());
-        node.peers.writePeers();
-       }
-
-       public synchronized boolean isDisabled() {
-               return isDisabled;
-       }
-       
-       public void setListenOnly(boolean setting) {
-               synchronized(this) {
-                       isListenOnly = setting;
-               }
-               if(setting && isBurstOnly()) {
-                       setBurstOnly(false);
-               }
-               if(setting) {
-                       stopARKFetcher();
-               }
-               setPeerNodeStatus(System.currentTimeMillis());
-        node.peers.writePeers();
-       }
-
-       public synchronized boolean isListenOnly() {
-               return isListenOnly;
-       }
-       
-       public void setBurstOnly(boolean setting) {
-               synchronized(this) {
-                       isBurstOnly = setting;
-               }
-               if(setting && isListenOnly()) {
-                       setListenOnly(false);
-               }
-               long now = System.currentTimeMillis();
-               if(!setting) {
-                       synchronized(this) {
-                               sendHandshakeTime = now;  // don't keep any 
long handshake delays we might have had under BurstOnly
-                       }
-               }
-               setPeerNodeStatus(now);
-               node.peers.writePeers();
-       }
-
-       public void setIgnoreSourcePort(boolean setting) {
-               synchronized(this) {
-                       ignoreSourcePort = setting;
-               }
-       }
-       
-
-       public boolean isIgnoreSourcePort() {
-               return ignoreSourcePort;
-       }
-       
-       public synchronized boolean isBurstOnly() {
-               return isBurstOnly;
-       }
-
        synchronized void updateShouldDisconnectNow() {
                verifiedIncompatibleOlderVersion = invalidVersion();
                verifiedIncompatibleNewerVersion = reverseInvalidVersion();
@@ -2680,413 +2451,6 @@
                Logger.normal(this, "Invalidated "+this);
        }

-       public synchronized boolean allowLocalAddresses() {
-               return allowLocalAddresses;
-       }
-
-       public boolean readExtraPeerData() {
-               String extraPeerDataDirPath = node.getExtraPeerDataDir();
-               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
-               if(!extraPeerDataPeerDir.exists()) {
-                       return false;
-               }
-               if(!extraPeerDataPeerDir.isDirectory()) {
-                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
-                       return false;
-               }
-               File[] extraPeerDataFiles = extraPeerDataPeerDir.listFiles();
-               if(extraPeerDataFiles == null) {
-                       return false;
-               }
-               boolean gotError = false;
-               boolean readResult = false;
-               for (int i = 0; i < extraPeerDataFiles.length; i++) {
-                       Integer fileNumber;
-                       try {
-                               fileNumber = new 
Integer(extraPeerDataFiles[i].getName());
-                       } catch (NumberFormatException e) {
-                               gotError = true;
-                               continue;
-                       }
-                       synchronized(extraPeerDataFileNumbers) {
-                               extraPeerDataFileNumbers.add(fileNumber);
-                       }
-                       readResult = 
readExtraPeerDataFile(extraPeerDataFiles[i], fileNumber.intValue());
-                       if(!readResult) {
-                               gotError = true;
-                       }
-               }
-               return !gotError;
-       }
-
-       public boolean rereadExtraPeerDataFile(int fileNumber) {
-               if(logMINOR)
-                       Logger.minor(this, "Rereading peer data file 
"+fileNumber+" for "+shortToString());
-               String extraPeerDataDirPath = node.getExtraPeerDataDir();
-               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
-               if(!extraPeerDataPeerDir.exists()) {
-                       Logger.error(this, "Extra peer data directory for peer 
does not exist: "+extraPeerDataPeerDir.getPath());
-                       return false;
-               }
-               if(!extraPeerDataPeerDir.isDirectory()) {
-                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
-                       return false;
-               }
-               File extraPeerDataFile = new 
File(extraPeerDataDirPath+File.separator+getIdentityString()+File.separator+fileNumber);
-               if(!extraPeerDataFile.exists()) {
-                       Logger.error(this, "Extra peer data file for peer does 
not exist: "+extraPeerDataFile.getPath());
-                       return false;
-               }
-               return readExtraPeerDataFile(extraPeerDataFile, fileNumber);
-       }
-
-       public boolean readExtraPeerDataFile(File extraPeerDataFile, int 
fileNumber) {
-               if(logMINOR) Logger.minor(this, "Reading "+extraPeerDataFile+" 
: "+fileNumber+" for "+shortToString());
-               boolean gotError = false;
-               if(!extraPeerDataFile.exists()) {
-                       if(logMINOR)
-                               Logger.minor(this, "Does not exist");
-                       return false;
-               }
-               Logger.normal(this, "extraPeerDataFile: 
"+extraPeerDataFile.getPath());
-               FileInputStream fis;
-               try {
-                       fis = new FileInputStream(extraPeerDataFile);
-               } catch (FileNotFoundException e1) {
-                       Logger.normal(this, "Extra peer data file not found: 
"+extraPeerDataFile.getPath());
-                       return false;
-               }
-               InputStreamReader isr;
-               try {
-                       isr = new InputStreamReader(fis, "UTF-8");
-               } catch (UnsupportedEncodingException e) {
-                       throw new Error("Impossible: JVM doesn't support UTF-8: 
"+e, e);
-               }
-               BufferedReader br = new BufferedReader(isr);
-               SimpleFieldSet fs = null;
-               try {
-                       // Read in the single SimpleFieldSet
-                       fs = new SimpleFieldSet(br, false, true);
-               } catch (EOFException e3) {
-                       // End of file, fine
-               } catch (IOException e4) {
-                       Logger.error(this, "Could not read extra peer data 
file: "+e4, e4);
-               } finally {
-                       try {
-                               br.close();
-                       } catch (IOException e5) {
-                               Logger.error(this, "Ignoring "+e5+" caught 
reading "+extraPeerDataFile.getPath(), e5);
-                       }
-               }
-               if(fs == null) {
-                       Logger.normal(this, "Deleting corrupt (too short?) 
file: "+extraPeerDataFile);
-                       deleteExtraPeerDataFile(fileNumber);
-                       return true;
-               }
-               boolean parseResult = false;
-               try {
-                       parseResult = parseExtraPeerData(fs, extraPeerDataFile, 
fileNumber);
-                       if(!parseResult) {
-                               gotError = true;
-                       }
-               } catch (FSParseException e2) {
-                       Logger.error(this, "Could not parse extra peer data: 
"+e2+ '\n' +fs.toString(),e2);
-                       gotError = true;
-               }
-               return !gotError;
-       }
-
-       private boolean parseExtraPeerData(SimpleFieldSet fs, File 
extraPeerDataFile, int fileNumber) throws FSParseException {
-               String extraPeerDataTypeString = fs.get("extraPeerDataType");
-               int extraPeerDataType = -1;
-               try {
-                       extraPeerDataType = 
Integer.parseInt(extraPeerDataTypeString);
-               } catch (NumberFormatException e) {
-                       Logger.error(this, "NumberFormatException parsing 
extraPeerDataType ("+extraPeerDataTypeString+") in file 
"+extraPeerDataFile.getPath());
-                       return false;
-               }
-               if(extraPeerDataType == Node.EXTRA_PEER_DATA_TYPE_N2NTM) {
-                       node.handleNodeToNodeTextMessageSimpleFieldSet(fs, 
this, fileNumber);
-                       return true;
-               } else if(extraPeerDataType == 
Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE) {
-                       String peerNoteTypeString = fs.get("peerNoteType");
-                       int peerNoteType = -1;
-                       try {
-                               peerNoteType = 
Integer.parseInt(peerNoteTypeString);
-                       } catch (NumberFormatException e) {
-                               Logger.error(this, "NumberFormatException 
parsing peerNoteType ("+peerNoteTypeString+") in file 
"+extraPeerDataFile.getPath());
-                               return false;
-                       }
-                       if(peerNoteType == 
Node.PEER_NOTE_TYPE_PRIVATE_DARKNET_COMMENT) {
-                               synchronized(privateDarknetComment) {
-                                       try {
-                                               privateDarknetComment = new 
String(Base64.decode(fs.get("privateDarknetComment")));
-                                       } catch (IllegalBase64Exception e) {
-                                               Logger.error(this, "Bad Base64 
encoding when decoding a private darknet comment SimpleFieldSet", e);
-                                               return false;
-                                       }
-                                       privateDarknetCommentFileNumber = 
fileNumber;
-                               }
-                               return true;
-                       }
-                       Logger.error(this, "Read unknown peer note type 
'"+peerNoteType+"' from file "+extraPeerDataFile.getPath());
-                       return false;
-               } else if(extraPeerDataType == 
Node.EXTRA_PEER_DATA_TYPE_QUEUED_TO_SEND_N2NTM) {
-                       boolean sendSuccess = false;
-                       int type = fs.getInt("n2nType", 1); // FIXME remove 
default
-                       fs.putOverwrite("n2nType", Integer.toString(type));
-                       if(isConnected()) {
-                               Message n2ntm;
-                               if(fs.get("extraPeerDataType") != null) {
-                                       fs.removeValue("extraPeerDataType");
-                               }
-                               if(fs.get("senderFileNumber") != null) {
-                                       fs.removeValue("senderFileNumber");
-                               }
-                               fs.putOverwrite("senderFileNumber", 
String.valueOf(fileNumber));
-                               if(fs.get("sentTime") != null) {
-                                       fs.removeValue("sentTime");
-                               }
-                               fs.putOverwrite("sentTime", 
Long.toString(System.currentTimeMillis()));
-                               
-                               try {
-                                       n2ntm = 
DMT.createNodeToNodeMessage(type, fs.toString().getBytes("UTF-8"));
-                               } catch (UnsupportedEncodingException e) {
-                                       Logger.error(this, 
"UnsupportedEncodingException processing extraPeerDataType 
("+extraPeerDataTypeString+") in file "+extraPeerDataFile.getPath(), e);
-                                       return false;
-                               }
-
-                               try {
-                                       
synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
-                                               node.usm.send(this, n2ntm, 
null);
-                                               Logger.normal(this, "Sent 
queued ("+fileNumber+") N2NTM to '"+getName()+"': "+n2ntm);
-                                               sendSuccess = true;
-                                               
queuedToSendN2NTMExtraPeerDataFileNumbers.remove(new Integer(fileNumber));
-                                       }
-                                       deleteExtraPeerDataFile(fileNumber);
-                               } catch (NotConnectedException e) {
-                                       sendSuccess = false;  // redundant, but 
clear
-                               }
-                       }
-                       if(!sendSuccess) {
-                               
synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
-                                       fs.putOverwrite("extraPeerDataType", 
Integer.toString(extraPeerDataType));
-                                       fs.removeValue("sentTime");
-                                       
queuedToSendN2NTMExtraPeerDataFileNumbers.add(new Integer(fileNumber));
-                               }
-                       }
-                       return true;
-               }
-               Logger.error(this, "Read unknown extra peer data type 
'"+extraPeerDataType+"' from file "+extraPeerDataFile.getPath());
-               return false;
-       }
-
-       public int writeNewExtraPeerDataFile(SimpleFieldSet fs, int 
extraPeerDataType) {
-               String extraPeerDataDirPath = node.getExtraPeerDataDir();
-               if(extraPeerDataType > 0)
-                       fs.putOverwrite("extraPeerDataType", 
Integer.toString(extraPeerDataType));
-               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
-               if(!extraPeerDataPeerDir.exists()) {
-                       if(!extraPeerDataPeerDir.mkdir()) {
-                               Logger.error(this, "Extra peer data directory 
for peer could not be created: "+extraPeerDataPeerDir.getPath());
-                               return -1;
-                       }
-               }
-               if(!extraPeerDataPeerDir.isDirectory()) {
-                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
-                       return -1;
-               }
-               Integer[] localFileNumbers = null;
-               int nextFileNumber = 0;
-               synchronized(extraPeerDataFileNumbers) {
-                       // Find the first free slot
-                       localFileNumbers = (Integer[]) 
extraPeerDataFileNumbers.toArray(new Integer[extraPeerDataFileNumbers.size()]);
-                       Arrays.sort(localFileNumbers);
-                       for (int i = 0; i < localFileNumbers.length; i++) {
-                               if(localFileNumbers[i].intValue() > 
nextFileNumber) {
-                                       break;
-                               }
-                               nextFileNumber = localFileNumbers[i].intValue() 
+ 1;
-                       }
-                       extraPeerDataFileNumbers.add(new 
Integer(nextFileNumber));
-               }
-               FileOutputStream fos;
-               File extraPeerDataFile = new 
File(extraPeerDataPeerDir.getPath()+File.separator+nextFileNumber);
-               if(extraPeerDataFile.exists()) {
-                       Logger.error(this, "Extra peer data file already 
exists: "+extraPeerDataFile.getPath());
-                       return -1;
-               }
-               String f = extraPeerDataFile.getPath();
-               try {
-                       fos = new FileOutputStream(f);
-               } catch (FileNotFoundException e2) {
-                       Logger.error(this, "Cannot write extra peer data file 
to disk: Cannot create "
-                                       + f + " - " + e2, e2);
-                       return -1;
-               }
-               OutputStreamWriter w;
-               try {
-                       w = new OutputStreamWriter(fos, "UTF-8");
-               } catch (UnsupportedEncodingException e2) {
-                       throw new Error("UTF-8 unsupported!: "+e2, e2);
-               }
-               BufferedWriter bw = new BufferedWriter(w);
-               try {
-                       fs.writeTo(bw);
-                       bw.close();
-               } catch (IOException e) {
-                       try {
-                               fos.close();
-                       } catch (IOException e1) {
-                               Logger.error(this, "Cannot close extra peer 
data file: "+e, e);
-                       }
-                       Logger.error(this, "Cannot write file: " + e, e);
-                       return -1;
-               }
-               return nextFileNumber;
-       }
-
-       public void deleteExtraPeerDataFile(int fileNumber) {
-               String extraPeerDataDirPath = node.getExtraPeerDataDir();
-               File extraPeerDataPeerDir = new File(extraPeerDataDirPath, 
getIdentityString());
-               if(!extraPeerDataPeerDir.exists()) {
-                       Logger.error(this, "Extra peer data directory for peer 
does not exist: "+extraPeerDataPeerDir.getPath());
-                       return;
-               }
-               if(!extraPeerDataPeerDir.isDirectory()) {
-                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
-                       return;
-               }
-               File extraPeerDataFile = new File(extraPeerDataPeerDir, 
Integer.toString(fileNumber));
-               if(!extraPeerDataFile.exists()) {
-                       Logger.error(this, "Extra peer data file for peer does 
not exist: "+extraPeerDataFile.getPath());
-                       return;
-               }
-               synchronized(extraPeerDataFileNumbers) {
-                       extraPeerDataFileNumbers.remove(new 
Integer(fileNumber));
-               }
-               if(!extraPeerDataFile.delete()) {
-                       if(extraPeerDataFile.exists()) {
-                               Logger.error(this, "Cannot delete file 
"+extraPeerDataFile+" after sending message to "+getPeer()+" - it may be resent 
on resting the node");
-                       } else {
-                               Logger.normal(this, "File does not exist when 
deleting: "+extraPeerDataFile+" after sending message to "+getPeer());
-                       }
-               }
-       }
-
-       public void removeExtraPeerDataDir() {
-               String extraPeerDataDirPath = node.getExtraPeerDataDir();
-               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
-               if(!extraPeerDataPeerDir.exists()) {
-                       Logger.error(this, "Extra peer data directory for peer 
does not exist: "+extraPeerDataPeerDir.getPath());
-                       return;
-               }
-               if(!extraPeerDataPeerDir.isDirectory()) {
-                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
-                       return;
-               }
-               Integer[] localFileNumbers = null;
-               synchronized(extraPeerDataFileNumbers) {
-                       localFileNumbers = (Integer[]) 
extraPeerDataFileNumbers.toArray(new Integer[extraPeerDataFileNumbers.size()]);
-               }
-               for (int i = 0; i < localFileNumbers.length; i++) {
-                       deleteExtraPeerDataFile(localFileNumbers[i].intValue());
-               }
-               extraPeerDataPeerDir.delete();
-       }
-
-       public boolean rewriteExtraPeerDataFile(SimpleFieldSet fs, int 
extraPeerDataType, int fileNumber) {
-               String extraPeerDataDirPath = node.getExtraPeerDataDir();
-               if(extraPeerDataType > 0)
-                       fs.putOverwrite("extraPeerDataType", 
Integer.toString(extraPeerDataType));
-               File extraPeerDataPeerDir = new 
File(extraPeerDataDirPath+File.separator+getIdentityString());
-               if(!extraPeerDataPeerDir.exists()) {
-                       Logger.error(this, "Extra peer data directory for peer 
does not exist: "+extraPeerDataPeerDir.getPath());
-                       return false;
-               }
-               if(!extraPeerDataPeerDir.isDirectory()) {
-                       Logger.error(this, "Extra peer data directory for peer 
not a directory: "+extraPeerDataPeerDir.getPath());
-                       return false;
-               }
-               File extraPeerDataFile = new 
File(extraPeerDataDirPath+File.separator+getIdentityString()+File.separator+fileNumber);
-               if(!extraPeerDataFile.exists()) {
-                       Logger.error(this, "Extra peer data file for peer does 
not exist: "+extraPeerDataFile.getPath());
-                       return false;
-               }
-               String f = extraPeerDataFile.getPath();
-               FileOutputStream fos;
-               try {
-                       fos = new FileOutputStream(f);
-               } catch (FileNotFoundException e2) {
-                       Logger.error(this, "Cannot write extra peer data file 
to disk: Cannot open "
-                                       + f + " - " + e2, e2);
-                       return false;
-               }
-               OutputStreamWriter w;
-               try {
-                       w = new OutputStreamWriter(fos, "UTF-8");
-               } catch (UnsupportedEncodingException e2) {
-                       throw new Error("JVM doesn't support UTF-8 charset!: 
"+e2, e2);
-               }
-               BufferedWriter bw = new BufferedWriter(w);
-               try {
-                       fs.writeTo(bw);
-                       bw.close();
-               } catch (IOException e) {
-                       try {
-                               fos.close();
-                       } catch (IOException e1) {
-                               Logger.error(this, "Cannot close extra peer 
data file: "+e, e);
-                       }
-                       Logger.error(this, "Cannot write file: " + e, e);
-                       return false;
-               }
-               return true;
-       }
-       
-       public synchronized String getPrivateDarknetCommentNote() {
-               return privateDarknetComment;
-       }
-       
-       public synchronized void setPrivateDarknetCommentNote(String comment) {
-               int localFileNumber;
-               synchronized(privateDarknetComment) {
-                       privateDarknetComment = comment;
-                       localFileNumber = privateDarknetCommentFileNumber;
-               }
-               SimpleFieldSet fs = new SimpleFieldSet(true);
-               fs.put("peerNoteType", 
Node.PEER_NOTE_TYPE_PRIVATE_DARKNET_COMMENT);
-               fs.putSingle("privateDarknetComment", 
Base64.encode(comment.getBytes()));
-               if(localFileNumber == -1) {
-                       localFileNumber = writeNewExtraPeerDataFile(fs, 
Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE);
-                       synchronized(privateDarknetComment) {
-                               privateDarknetCommentFileNumber = 
localFileNumber;
-                       }
-               } else {
-                       rewriteExtraPeerDataFile(fs, 
Node.EXTRA_PEER_DATA_TYPE_PEER_NOTE, localFileNumber);
-               }
-       }
-
-       public void queueN2NTM(SimpleFieldSet fs) {
-               int fileNumber = writeNewExtraPeerDataFile( fs, 
Node.EXTRA_PEER_DATA_TYPE_QUEUED_TO_SEND_N2NTM);
-               synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
-                       queuedToSendN2NTMExtraPeerDataFileNumbers.add(new 
Integer(fileNumber));
-               }
-       }
-
-       public void sendQueuedN2NTMs() {
-               if(logMINOR)
-                       Logger.minor(this, "Sending queued N2NTMs for 
"+shortToString());
-               Integer[] localFileNumbers = null;
-               synchronized(queuedToSendN2NTMExtraPeerDataFileNumbers) {
-                       localFileNumbers = (Integer[]) 
queuedToSendN2NTMExtraPeerDataFileNumbers.toArray(new 
Integer[queuedToSendN2NTMExtraPeerDataFileNumbers.size()]);
-               }
-               Arrays.sort(localFileNumbers);
-               for (int i = 0; i < localFileNumbers.length; i++) {
-                       rereadExtraPeerDataFile(localFileNumbers[i].intValue());
-               }
-       }
-
        public void maybeOnConnect() {
                if(wasDisconnected && isConnected()) {
                        synchronized(this) {
@@ -3103,8 +2467,8 @@
        /**
         * A method to be called once at the beginning of every time 
isConnected() is true
         */
-       private void onConnect() {
-               sendQueuedN2NTMs();
+       protected void onConnect() {
+               // Do nothing in the default impl
        }

        public void onFound(long edition, FetchResult result) {
@@ -3165,13 +2529,6 @@
                return isSignatureVerificationSuccessfull;
        }

-       public void setAllowLocalAddresses(boolean setting) {
-               synchronized(this) {
-                       allowLocalAddresses = setting;
-               }
-        node.peers.writePeers();
-       }
-       
        public void checkRoutableConnectionStatus() {
                synchronized(this) {
                        if(isRoutable()) {
@@ -3235,709 +2592,8 @@
                return DSA.verify(peerPubKey, sig, new NativeBigInteger(1, 
hash), false);
        }

-       // File transfer offers
-       // FIXME this should probably be somewhere else, along with the N2NTM 
stuff... but where?
-       // FIXME this should be persistent across node restarts
-
-       /** Files I have offered to this peer */
-       private final HashMap myFileOffersByUID = new HashMap();
-       /** Files this peer has offered to me */
-       private final HashMap hisFileOffersByUID = new HashMap();
-       
-       private void storeOffers() {
-               // FIXME do something
-       }
-       
-       class FileOffer {
-               final long uid;
-               final String filename;
-               final String mimeType;
-               final String comment;
-               private RandomAccessThing data;
-               final long size;
-               /** Who is offering it? True = I am offering it, False = I am 
being offered it */
-               final boolean amIOffering;
-               private PartiallyReceivedBulk prb;
-               private BulkTransmitter transmitter;
-               private BulkReceiver receiver;
-               /** True if the offer has either been accepted or rejected */
-               private boolean acceptedOrRejected;
-               
-               FileOffer(long uid, RandomAccessThing data, String filename, 
String mimeType, String comment) throws IOException {
-                       this.uid = uid;
-                       this.data = data;
-                       this.filename = filename;
-                       this.mimeType = mimeType;
-                       this.comment = comment;
-                       size = data.size();
-                       amIOffering = true;
-               }
-
-               public FileOffer(SimpleFieldSet fs, boolean amIOffering) throws 
FSParseException {
-                       uid = fs.getLong("uid");
-                       size = fs.getLong("size");
-                       mimeType = fs.get("metadata.contentType");
-                       filename = FileUtil.sanitize(fs.get("filename"), 
mimeType);
-                       comment = fs.get("comment");
-                       this.amIOffering = amIOffering;
-               }
-
-               public void toFieldSet(SimpleFieldSet fs) {
-                       fs.put("uid", uid);
-                       fs.putSingle("filename", filename);
-                       fs.putSingle("metadata.contentType", mimeType);
-                       fs.putSingle("comment", comment);
-                       fs.put("size", size);
-               }
-
-               public void accept() {
-                       acceptedOrRejected = true;
-                       File dest = new File(node.clientCore.downloadDir, 
"direct-"+FileUtil.sanitize(getName())+"-"+filename);
-                       try {
-                               data = new RandomAccessFileWrapper(dest, "rw");
-                       } catch (FileNotFoundException e) {
-                               // Impossible
-                               throw new Error("Impossible: 
FileNotFoundException opening with RAF with rw! "+e, e);
-                       }
-                       prb = new PartiallyReceivedBulk(node.usm, size, 
Node.PACKET_SIZE, data, false);
-                       receiver = new BulkReceiver(prb, PeerNode.this, uid);
-                       // FIXME make this persistent
-                       Thread t = new Thread(new Runnable() {
-                               public void run() {
-                                       if(logMINOR)
-                                               Logger.minor(this, "Received 
file");
-                                       try {
-                                               if(!receiver.receive()) {
-                                                       String err = "Failed to 
receive "+this;
-                                                       Logger.error(this, err);
-                                                       System.err.println(err);
-                                                       onReceiveFailure();
-                                               } else {
-                                                       onReceiveSuccess();
-                                               }
-                                       } catch (Throwable t) {
-                                               Logger.error(this, "Caught 
"+t+" receiving file", t);
-                                               onReceiveFailure();
-                                       }
-                                       if(logMINOR)
-                                               Logger.minor(this, "Received 
file");
-                               }
-                       }, "Receiver for bulk transfer "+uid+":"+filename);
-                       t.setDaemon(true);
-                       t.start();
-                       if(logMINOR) Logger.minor(this, "Receiving on "+t);
-                       sendFileOfferAccepted(uid);
-               }
-
-               public void send() throws DisconnectedException {
-                       prb = new PartiallyReceivedBulk(node.usm, size, 
Node.PACKET_SIZE, data, true);
-                       transmitter = new BulkTransmitter(prb, PeerNode.this, 
uid, node.outputThrottle);
-                       if(logMINOR)
-                               Logger.minor(this, "Sending "+uid);
-                       Thread t = new Thread(new Runnable() {
-                               public void run() {
-                                       if(logMINOR)
-                                               Logger.minor(this, "Sending 
file");
-                                       try {
-                                               if(!transmitter.send()) {
-                                                       String err = "Failed to 
send "+uid+" for "+FileOffer.this;
-                                                       Logger.error(this, err);
-                                                       System.err.println(err);
-                                               }
-                                       } catch (Throwable t) {
-                                               Logger.error(this, "Caught 
"+t+" sending file", t);
-                                       }
-                                       if(logMINOR)
-                                               Logger.minor(this, "Sent file");
-                               }
-
-                       }, "Sender for bulk transfer "+uid+":"+filename);
-                       t.setDaemon(true);
-                       t.start();
-               }
-
-               public void reject() {
-                       acceptedOrRejected = true;
-                       sendFileOfferRejected(uid);
-               }
-
-               public void onRejected() {
-                       transmitter.cancel();
-                       // FIXME prb's can't be shared, right? Well they aren't 
here...
-                       prb.abort(RetrievalException.CANCELLED_BY_RECEIVER, 
"Cancelled by receiver");
-               }
-
-               protected void onReceiveFailure() {
-                       UserAlert alert = new UserAlert() {
-                               public String dismissButtonText() {
-                                       return L10n.getString("UserAlert.hide");
-                               }
-                               public HTMLNode getHTMLText() {
-                                       HTMLNode div = new HTMLNode("div");
-                                       
-                                       // FIXME localise!!!
-                                       
-                                       div.addChild("p", 
l10n("failedReceiveHeader", new String[] { "filename", "node" },
-                                                       new String[] { 
filename, getName() }));
-                                       
-                                       // Descriptive table
-                                       
-                                       HTMLNode table = div.addChild("table", 
"border", "0");
-                                       HTMLNode row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("fileLabel"));
-                                       row.addChild("td").addChild("#", 
filename);
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("sizeLabel"));
-                                       row.addChild("td").addChild("#", 
SizeUtil.formatSize(size));
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("mimeLabel"));
-                                       row.addChild("td").addChild("#", 
mimeType);
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("senderLabel"));
-                                       row.addChild("td").addChild("#", 
getName());
-                                       row = table.addChild("tr");
-                                       if(comment != null && comment.length() 
> 0) {
-                                               
row.addChild("td").addChild("#", l10n("commentLabel"));
-                                               
row.addChild("td").addChild("#", comment);
-                                       }
-                                       
-                                       return div;
-                               }
-
-                               public short getPriorityClass() {
-                                       return UserAlert.MINOR;
-                               }
-
-                               public String getText() {
-                                       StringBuffer sb = new StringBuffer();
-                                       sb.append(l10n("failedReceiveHeader", 
new String[] { "filename", "node" },
-                                                       new String[] { 
filename, getName() }));
-                                       sb.append('\n');
-                                       sb.append(l10n("fileLabel"));
-                                       sb.append(' ');
-                                       sb.append(filename);
-                                       sb.append('\n');
-                                       sb.append(l10n("sizeLabel"));
-                                       sb.append(' ');
-                                       sb.append(SizeUtil.formatSize(size));
-                                       sb.append('\n');
-                                       sb.append(l10n("mimeLabel"));
-                                       sb.append(' ');
-                                       sb.append(mimeType);
-                                       sb.append('\n');
-                                       sb.append(l10n("senderLabel"));
-                                       sb.append(' ');
-                                       sb.append(getName());
-                                       sb.append('\n');
-                                       if(comment != null && comment.length() 
> 0) {
-                                               sb.append(l10n("commentLabel"));
-                                               sb.append(' ');
-                                               sb.append(comment);
-                                       }
-                                       return sb.toString();
-                               }
-
-                               public String getTitle() {
-                                       return l10n("failedReceiveTitle");
-                               }
-
-                               public boolean isValid() {
-                                       return true;
-                               }
-
-                               public void isValid(boolean validity) {
-                                       // Ignore
-                               }
-
-                               public void onDismiss() {
-                                       // Ignore
-                               }
-
-                               public boolean shouldUnregisterOnDismiss() {
-                                       return true;
-                               }
-
-                               public boolean userCanDismiss() {
-                                       return true;
-                               }
-                               
-                       };
-                       node.clientCore.alerts.register(alert);
-               }
-
-               private void onReceiveSuccess() {
-                       UserAlert alert = new UserAlert() {
-                               public String dismissButtonText() {
-                                       return L10n.getString("UserAlert.hide");
-                               }
-                               public HTMLNode getHTMLText() {
-                                       HTMLNode div = new HTMLNode("div");
-                                       
-                                       // FIXME localise!!!
-                                       
-                                       div.addChild("p", 
l10n("succeededReceiveHeader", new String[] { "filename", "node" },
-                                                       new String[] { 
filename, getName() }));
-                                       
-                                       // Descriptive table
-                                       
-                                       HTMLNode table = div.addChild("table", 
"border", "0");
-                                       HTMLNode row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("fileLabel"));
-                                       row.addChild("td").addChild("#", 
filename);
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("sizeLabel"));
-                                       row.addChild("td").addChild("#", 
SizeUtil.formatSize(size));
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("mimeLabel"));
-                                       row.addChild("td").addChild("#", 
mimeType);
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("senderLabel"));
-                                       row.addChild("td").addChild("#", 
getName());
-                                       row = table.addChild("tr");
-                                       if(comment != null && comment.length() 
> 0) {
-                                               
row.addChild("td").addChild("#", l10n("commentLabel"));
-                                               
row.addChild("td").addChild("#", comment);
-                                       }
-                                       
-                                       return div;
-                               }
-
-                               public short getPriorityClass() {
-                                       return UserAlert.MINOR;
-                               }
-
-                               public String getText() {
-                                       StringBuffer sb = new StringBuffer();
-                                       
sb.append(l10n("succeededReceiveHeader", new String[] { "filename", "node" },
-                                                       new String[] { 
filename, getName() }));
-                                       sb.append('\n');
-                                       sb.append(l10n("fileLabel"));
-                                       sb.append(' ');
-                                       sb.append(filename);
-                                       sb.append('\n');
-                                       sb.append(l10n("sizeLabel"));
-                                       sb.append(' ');
-                                       sb.append(SizeUtil.formatSize(size));
-                                       sb.append('\n');
-                                       sb.append(l10n("mimeLabel"));
-                                       sb.append(' ');
-                                       sb.append(mimeType);
-                                       sb.append('\n');
-                                       sb.append(l10n("senderLabel"));
-                                       sb.append(' ');
-                                       sb.append(userToString());
-                                       sb.append('\n');
-                                       if(comment != null && comment.length() 
> 0) {
-                                               sb.append(l10n("commentLabel"));
-                                               sb.append(' ');
-                                               sb.append(comment);
-                                       }
-                                       return sb.toString();
-                               }
-
-                               public String getTitle() {
-                                       return l10n("succeededReceiveTitle");
-                               }
-
-                               public boolean isValid() {
-                                       return true;
-                               }
-
-                               public void isValid(boolean validity) {
-                                       // Ignore
-                               }
-
-                               public void onDismiss() {
-                                       // Ignore
-                               }
-
-                               public boolean shouldUnregisterOnDismiss() {
-                                       return true;
-                               }
-
-                               public boolean userCanDismiss() {
-                                       return true;
-                               }
-                               
-                       };
-                       node.clientCore.alerts.register(alert);
-               }
-
-               
-               /** Ask the user whether (s)he wants to download a file from a 
direct peer */
-               public UserAlert askUserUserAlert() {
-                       return new UserAlert() {
-                               public String dismissButtonText() {
-                                       return null; // Cannot hide, but can 
reject
-                               }
-                               public HTMLNode getHTMLText() {
-                                       HTMLNode div = new HTMLNode("div");
-                                       
-                                       div.addChild("p", 
l10n("offeredFileHeader", "name", getName()));
-                                       
-                                       // Descriptive table
-                                       
-                                       HTMLNode table = div.addChild("table", 
"border", "0");
-                                       HTMLNode row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("fileLabel"));
-                                       row.addChild("td").addChild("#", 
filename);
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("sizeLabel"));
-                                       row.addChild("td").addChild("#", 
SizeUtil.formatSize(size));
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("mimeLabel"));
-                                       row.addChild("td").addChild("#", 
mimeType);
-                                       row = table.addChild("tr");
-                                       row.addChild("td").addChild("#", 
l10n("senderLabel"));
-                                       row.addChild("td").addChild("#", 
getName());
-                                       row = table.addChild("tr");
-                                       if(comment != null && comment.length() 
> 0) {
-                                               
row.addChild("td").addChild("#", l10n("commentLabel"));
-                                               
row.addChild("td").addChild("#", comment);
-                                       }
-                                       
-                                       // Accept/reject form
-                                       
-                                       // Hopefully we will have a container 
when this function is called!
-                                       HTMLNode form = 
node.clientCore.getToadletContainer().addFormChild(div, "/friends/", 
"f2fFileOfferAcceptForm");
-                                       
-                                       // FIXME node_ is inefficient
-                                       form.addChild("input", new String[] { 
"type", "name" },
-                                                       new String[] { 
"hidden", "node_"+PeerNode.this.hashCode() });
-
-                                       form.addChild("input", new String[] { 
"type", "name", "value" },
-                                                       new String[] { 
"hidden", "id", Long.toString(uid) });
-                                       
-                                       form.addChild("input", new String[] { 
"type", "name", "value" }, 
-                                                       new String[] { 
"submit", "acceptTransfer", l10n("acceptTransferButton") });
-
-                                       form.addChild("input", new String[] { 
"type", "name", "value" }, 
-                                                       new String[] { 
"submit", "rejectTransfer", l10n("rejectTransferButton") });
-                                       
-                                       return div;
-                               }
-                               public short getPriorityClass() {
-                                       return UserAlert.MINOR;
-                               }
-                               public String getText() {
-                                       StringBuffer sb = new StringBuffer();
-                                       sb.append(l10n("offeredFileHeader", 
"name", getName()));
-                                       sb.append('\n');
-                                       sb.append(l10n("fileLabel"));
-                                       sb.append(' ');
-                                       sb.append(filename);
-                                       sb.append('\n');
-                                       sb.append(l10n("sizeLabel"));
-                                       sb.append(' ');
-                                       sb.append(SizeUtil.formatSize(size));
-                                       sb.append('\n');
-                                       sb.append(l10n("mimeLabel"));
-                                       sb.append(' ');
-                                       sb.append(mimeType);
-                                       sb.append('\n');
-                                       sb.append(l10n("senderLabel"));
-                                       sb.append(' ');
-                                       sb.append(userToString());
-                                       sb.append('\n');
-                                       if(comment != null && comment.length() 
> 0) {
-                                               sb.append(l10n("commentLabel"));
-                                               sb.append(' ');
-                                               sb.append(comment);
-                                       }
-                                       return sb.toString();
-                               }
-                               public String getTitle() {
-                                       return l10n("askUserTitle");
-                               }
-
-                               public boolean isValid() {
-                                       if(acceptedOrRejected) {
-                                               
node.clientCore.alerts.unregister(this);
-                                               return false;
-                                       }
-                                       return true;
-                               }
-                               public void isValid(boolean validity) {
-                                       // Ignore
-                               }
-                               public void onDismiss() {
-                                       // Ignore
-                               }
-                               public boolean shouldUnregisterOnDismiss() {
-                                       return false;
-                               }
-
-                               public boolean userCanDismiss() {
-                                       return false; // should accept or reject
-                               }
-                       };
-                       
-               }
-               private String l10n(String key) {
-                       return L10n.getString("FileOffer."+key);
-               }
-               private String l10n(String key, String pattern, String value) {
-                       return L10n.getString("FileOffer."+key, pattern, value);
-               }
-               private String l10n(String key, String[] pattern, String[] 
value) {
-                       return L10n.getString("FileOffer."+key, pattern, value);
-               }
-       }
-
-       public int sendTextMessage(String message) {
-               long now = System.currentTimeMillis();
-               SimpleFieldSet fs = new SimpleFieldSet(true);
-               fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
-               fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_USERALERT);
-               try {
-                       fs.putSingle("source_nodename", 
Base64.encode(node.getMyName().getBytes("UTF-8")));
-                       fs.putSingle("target_nodename", 
Base64.encode(getName().getBytes("UTF-8")));
-                       fs.putSingle("text", 
Base64.encode(message.getBytes("UTF-8")));
-                       fs.put("composedTime", now);
-                       fs.put("sentTime", now);
-                       Message n2ntm;
-                       n2ntm = DMT.createNodeToNodeMessage(
-                                       Node.N2N_MESSAGE_TYPE_FPROXY, fs
-                                                       
.toString().getBytes("UTF-8"));
-                       try {
-                               sendAsync(n2ntm, null, 0, null);
-                       } catch (NotConnectedException e) {
-                               fs.removeValue("sentTime");
-                               queueN2NTM(fs);
-                               setPeerNodeStatus(System.currentTimeMillis());
-                               return getPeerNodeStatus();
-                       }
-                       this.setPeerNodeStatus(System.currentTimeMillis());
-                       return getPeerNodeStatus();
-               } catch (UnsupportedEncodingException e) {
-                       throw new Error("Impossible: "+e, e);
-               }
-       }
-
-       public int sendFileOfferAccepted(long uid) {
-               storeOffers();
-               long now = System.currentTimeMillis();
-               SimpleFieldSet fs = new SimpleFieldSet(true);
-               fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
-               fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER_ACCEPTED);
-               try {
-                       fs.putSingle("source_nodename", 
Base64.encode(node.getMyName().getBytes("UTF-8")));
-                       fs.putSingle("target_nodename", 
Base64.encode(getName().getBytes("UTF-8")));
-                       fs.put("composedTime", now);
-                       fs.put("sentTime", now);
-                       fs.put("uid", uid);
-                       if(logMINOR)
-                               Logger.minor(this, "Sending node to node 
message (file offer accepted):\n"+fs);
-                       Message n2ntm;
-                       n2ntm = DMT.createNodeToNodeMessage(
-                                       Node.N2N_MESSAGE_TYPE_FPROXY, fs
-                                                       
.toString().getBytes("UTF-8"));
-                       try {
-                               sendAsync(n2ntm, null, 0, null);
-                       } catch (NotConnectedException e) {
-                               fs.removeValue("sentTime");
-                               queueN2NTM(fs);
-                               setPeerNodeStatus(System.currentTimeMillis());
-                               return getPeerNodeStatus();
-                       }
-                       this.setPeerNodeStatus(System.currentTimeMillis());
-                       return getPeerNodeStatus();
-               } catch (UnsupportedEncodingException e) {
-                       throw new Error("Impossible: "+e, e);
-               }
-       }
-
-       public int sendFileOfferRejected(long uid) {
-               storeOffers();
-               long now = System.currentTimeMillis();
-               SimpleFieldSet fs = new SimpleFieldSet(true);
-               fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
-               fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER_REJECTED);
-               try {
-                       fs.putSingle("source_nodename", 
Base64.encode(node.getMyName().getBytes("UTF-8")));
-                       fs.putSingle("target_nodename", 
Base64.encode(getName().getBytes("UTF-8")));
-                       fs.put("composedTime", now);
-                       fs.put("sentTime", now);
-                       fs.put("uid", uid);
-                       if(logMINOR)
-                               Logger.minor(this, "Sending node to node 
message (file offer rejected):\n"+fs);
-                       Message n2ntm;
-                       n2ntm = DMT.createNodeToNodeMessage(
-                                       Node.N2N_MESSAGE_TYPE_FPROXY, fs
-                                                       
.toString().getBytes("UTF-8"));
-                       try {
-                               sendAsync(n2ntm, null, 0, null);
-                       } catch (NotConnectedException e) {
-                               fs.removeValue("sentTime");
-                               queueN2NTM(fs);
-                               setPeerNodeStatus(System.currentTimeMillis());
-                               return getPeerNodeStatus();
-                       }
-                       this.setPeerNodeStatus(System.currentTimeMillis());
-                       return getPeerNodeStatus();
-               } catch (UnsupportedEncodingException e) {
-                       throw new Error("Impossible: "+e, e);
-               }
-       }
-
-       public int sendFileOffer(File filename, String message) throws 
IOException {
-               String fnam = filename.getName();
-               String mime = DefaultMIMETypes.guessMIMEType(fnam, false);
-               long uid = node.random.nextLong();
-               RandomAccessThing data = new RandomAccessFileWrapper(filename, 
"r");
-               FileOffer fo = new FileOffer(uid, data, fnam, mime, message);
-               synchronized(this) {
-                       myFileOffersByUID.put(new Long(uid), fo);
-               }
-               storeOffers();
-               long now = System.currentTimeMillis();
-               SimpleFieldSet fs = new SimpleFieldSet(true);
-               fs.put("n2nType", Node.N2N_MESSAGE_TYPE_FPROXY);
-               fs.put("type", Node.N2N_TEXT_MESSAGE_TYPE_FILE_OFFER);
-               try {
-                       fs.putSingle("source_nodename", 
Base64.encode(node.getMyName().getBytes("UTF-8")));
-                       fs.putSingle("target_nodename", 
Base64.encode(getName().getBytes("UTF-8")));
-                       fs.put("composedTime", now);
-                       fs.put("sentTime", now);
-                       fo.toFieldSet(fs);
-                       if(logMINOR)
-                               Logger.minor(this, "Sending node to node 
message (file offer):\n"+fs);
-                       Message n2ntm;
-                       int status = getPeerNodeStatus();
-                       n2ntm = DMT.createNodeToNodeMessage(
-                                       Node.N2N_MESSAGE_TYPE_FPROXY, fs
-                                                       
.toString().getBytes("UTF-8"));
-                       try {
-                               sendAsync(n2ntm, null, 0, null);
-                       } catch (NotConnectedException e) {
-                               fs.removeValue("sentTime");
-                               queueN2NTM(fs);
-                               setPeerNodeStatus(System.currentTimeMillis());
-                               return getPeerNodeStatus();
-                       }
-                       return status;
-               } catch (UnsupportedEncodingException e) {
-                       throw new Error("Impossible: "+e, e);
-               }
-       }
-
-       public void handleFproxyN2NTM(SimpleFieldSet fs, int fileNumber) {
-               String source_nodename = null;
-               String target_nodename = null;
-               String text = null;
-               long composedTime;
-               long sentTime;
-               long receivedTime;
-               try {
-                       source_nodename = new 
String(Base64.decode(fs.get("source_nodename")));
-                       target_nodename = new 
String(Base64.decode(fs.get("target_nodename")));
-                       text = new String(Base64.decode(fs.get("text")));
-                       composedTime = fs.getLong("composedTime", -1);
-                       sentTime = fs.getLong("sentTime", -1);
-                       receivedTime = fs.getLong("receivedTime", -1);
-               } catch (IllegalBase64Exception e) {
-                       Logger.error(this, "Bad Base64 encoding when decoding a 
N2NTM SimpleFieldSet", e);
-                       return;
-               }
-               N2NTMUserAlert userAlert = new N2NTMUserAlert(this, 
source_nodename, target_nodename, text, fileNumber, composedTime, sentTime, 
receivedTime);
-               node.clientCore.alerts.register(userAlert);
-       }
-
-       public void handleFproxyFileOffer(SimpleFieldSet fs, int fileNumber) {
-               final FileOffer offer;
-               try {
-                       offer = new FileOffer(fs, false);
-               } catch (FSParseException e) {
-                       Logger.error(this, "Could not parse offer: "+e+" on 
"+this+" :\n"+fs, e);
-                       return;
-               }
-               Long u = new Long(offer.uid);
-               synchronized(this) {
-                       if(hisFileOffersByUID.containsKey(u)) return; // Ignore 
re-advertisement
-                       hisFileOffersByUID.put(u, offer);
-               }
-               
-               // Don't persist for now - FIXME
-               this.deleteExtraPeerDataFile(fileNumber);
-               
-               UserAlert alert = offer.askUserUserAlert();
-                       
-               node.clientCore.alerts.register(alert);
-       }
-
-       public void acceptTransfer(long id) {
-               if(logMINOR)
-                       Logger.minor(this, "Accepting transfer "+id+" on 
"+this);
-               FileOffer fo;
-               synchronized(this) {
-                       fo = (FileOffer) hisFileOffersByUID.get(new Long(id));
-               }
-               fo.accept();
-       }
-       
-       public void rejectTransfer(long id) {
-               FileOffer fo;
-               synchronized(this) {
-                       fo = (FileOffer) hisFileOffersByUID.remove(new 
Long(id));
-               }
-               fo.reject();
-       }
-       
-       public void handleFproxyFileOfferAccepted(SimpleFieldSet fs, int 
fileNumber) {
-               // Don't persist for now - FIXME
-               this.deleteExtraPeerDataFile(fileNumber);
-               
-               long uid;
-               try {
-                       uid = fs.getLong("uid");
-               } catch (FSParseException e) {
-                       Logger.error(this, "Could not parse offer accepted: 
"+e+" on "+this+" :\n"+fs, e);
-                       return;
-               }
-               if(logMINOR)
-                       Logger.minor(this, "Offer accepted for "+uid);
-               Long u = new Long(uid);
-               FileOffer fo;
-               synchronized(this) {
-                       fo = (FileOffer) (myFileOffersByUID.get(u));
-               }
-               if(fo == null) {
-                       Logger.error(this, "No such offer: "+uid);
-                       try {
-                               sendAsync(DMT.createFNPBulkSendAborted(uid), 
null, fileNumber, null);
-                       } catch (NotConnectedException e) {
-                               // Fine by me!
-                       }
-                       return;
-               }
-               try {
-                       fo.send();
-               } catch (DisconnectedException e) {
-                       Logger.error(this, "Cannot send because node 
disconnected: "+e+" for "+uid+":"+fo.filename, e);
-               }
-       }
-
-       public void handleFproxyFileOfferRejected(SimpleFieldSet fs, int 
fileNumber) {
-               // Don't persist for now - FIXME
-               this.deleteExtraPeerDataFile(fileNumber);
-               
-               long uid;
-               try {
-                       uid = fs.getLong("uid");
-               } catch (FSParseException e) {
-                       Logger.error(this, "Could not parse offer rejected: 
"+e+" on "+this+" :\n"+fs, e);
-                       return;
-               }
-               
-               FileOffer fo;
-               synchronized(this) {
-                       fo = (FileOffer) myFileOffersByUID.remove(new 
Long(uid));
-               }
-               fo.onRejected();
-       }
-
        public String userToString() {
-               return ""+getPeer()+" : "+getName();
+               return ""+getPeer();
        }

        public void setTimeDelta(long delta) {
@@ -3970,4 +2626,23 @@
        public SocketHandler getSocketHandler() {
                return outgoingMangler.getSocketHandler();
        }
+
+       /** Is this peer disabled? I.e. has the user explicitly disabled it? */
+       public boolean isDisabled() {
+               return false;
+       }
+
+       /** Is this peer allowed local addresses? If false, we will never 
connect to this peer via
+        * a local address even if it advertises them.
+        */
+       public boolean allowLocalAddresses() {
+               return false;
+       }
+
+       /** Is this peer set to ignore source address? If so, we will always 
reply to the peer's official
+        * address, even if we get packets from somewhere else. @see 
DarknetPeerNode.isIgnoreSourcePort().
+        */
+       public boolean isIgnoreSource() {
+               return false;
+       }
 }

Modified: trunk/freenet/src/freenet/node/PeerNodeStatus.java
===================================================================
--- trunk/freenet/src/freenet/node/PeerNodeStatus.java  2007-06-28 15:02:56 UTC 
(rev 13808)
+++ trunk/freenet/src/freenet/node/PeerNodeStatus.java  2007-06-28 17:07:40 UTC 
(rev 13809)
@@ -20,8 +20,6 @@
  */
 public class PeerNodeStatus {

-       private final String name;
-
        private final String peerAddress;

        private final int peerPort;
@@ -42,12 +40,6 @@

        private final long routingBackedOffUntil;

-       private final boolean burstOnly;
-
-       private final boolean listening;
-
-       private final boolean disabled;
-
        private final boolean connected;

        private final boolean routable;
@@ -78,8 +70,6 @@

        private final double pReject;

-       private final String privateDarknetCommentNote;
-       
        private long totalBytesIn;

        private long totalBytesOut;
@@ -91,7 +81,6 @@
        private long clockDelta;

        public PeerNodeStatus(PeerNode peerNode) {
-               this.name = peerNode.getName();
                Peer p = peerNode.getPeer();
                if(p == null) {
                        peerAddress = null;
@@ -108,9 +97,6 @@
                this.simpleVersion = peerNode.getSimpleVersion();
                this.routingBackoffLength = peerNode.getRoutingBackoffLength();
                this.routingBackedOffUntil = 
peerNode.getRoutingBackedOffUntil();
-               this.burstOnly = peerNode.isBurstOnly();
-               this.listening = peerNode.isListenOnly();
-               this.disabled = peerNode.isDisabled();
                this.connected = peerNode.isConnected();
                this.routable = peerNode.isRoutable();
                this.isFetchingARK = peerNode.isFetchingARK();
@@ -126,7 +112,6 @@
                this.localMessagesSent = new 
Hashtable(peerNode.getLocalNodeSentMessagesToStatistic());
                this.hashCode = peerNode.hashCode;
                this.pReject = peerNode.getPRejected();
-               this.privateDarknetCommentNote = 
peerNode.getPrivateDarknetCommentNote();
                this.totalBytesIn = peerNode.getTotalInputBytes();
                this.totalBytesOut = peerNode.getTotalOutputBytes();
                this.percentTimeRoutableConnection = 
peerNode.getPercentTimeRoutableConnection();
@@ -236,13 +221,6 @@
        }

        /**
-        * @return the name
-        */
-       public String getName() {
-               return name;
-       }
-
-       /**
         * @return the peerAddress
         */
        public String getPeerAddress() {
@@ -292,13 +270,6 @@
        }

        /**
-        * @return the burstOnly
-        */
-       public boolean isBurstOnly() {
-               return burstOnly;
-       }
-
-       /**
         * @return the connected
         */
        public boolean isConnected() {
@@ -313,13 +284,6 @@
        }

        /**
-        * @return the disabled
-        */
-       public boolean isDisabled() {
-               return disabled;
-       }
-
-       /**
         * @return the isFetchingARK
         */
        public boolean isFetchingARK() {
@@ -327,13 +291,6 @@
        }

        /**
-        * @return the listening
-        */
-       public boolean isListening() {
-               return listening;
-       }
-
-       /**
         * @return the simpleVersion
         */
        public String getSimpleVersion() {
@@ -341,7 +298,7 @@
        }

        public String toString() {
-               return statusName + ' ' + peerAddress + ':' + peerPort + ' ' + 
name + ' ' + location + ' ' + version + " backoff: " + routingBackoffLength + " 
(" + (Math.max(routingBackedOffUntil - System.currentTimeMillis(), 0)) + ')';
+               return statusName + ' ' + peerAddress + ':' + peerPort + ' ' + 
location + ' ' + version + " backoff: " + routingBackoffLength + " (" + 
(Math.max(routingBackedOffUntil - System.currentTimeMillis(), 0)) + ')';
        }

        public int hashCode() {
@@ -352,13 +309,6 @@
                return pReject;
        }

-       /**
-        * @return the privateDarknetCommentNote
-        */
-       public String getPrivateDarknetCommentNote() {
-               return privateDarknetCommentNote;
-       }
-
        public long getTotalInputBytes() {
                return totalBytesIn;
        }

Modified: trunk/freenet/src/freenet/node/TextModeClientInterface.java
===================================================================
--- trunk/freenet/src/freenet/node/TextModeClientInterface.java 2007-06-28 
15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/node/TextModeClientInterface.java 2007-06-28 
17:07:40 UTC (rev 13809)
@@ -733,7 +733,7 @@
                        out.flush();
                        return false;
                }
-                       PeerNode pn = n.getPeerNode(nodeIdentifier);
+                       DarknetPeerNode pn = n.getPeerNode(nodeIdentifier);
                if(pn == null) {
                        out.write(("n.getPeerNode() failed to get peer details 
for "+nodeIdentifier+"\r\n\r\n").getBytes());
                        out.flush();
@@ -748,7 +748,7 @@
                        out.flush();
                        return false;
                }
-                       PeerNode pn = n.getPeerNode(nodeIdentifier);
+                       DarknetPeerNode pn = n.getPeerNode(nodeIdentifier);
                if(pn == null) {
                        out.write(("n.getPeerNode() failed to get peer details 
for "+nodeIdentifier+"\r\n\r\n").getBytes());
                        out.flush();
@@ -1040,7 +1040,7 @@
         * Report peer success as boolean
         */
        private boolean disablePeer(String nodeIdentifier) {
-               PeerNode[] pn = n.peers.myPeers;
+               DarknetPeerNode[] pn = n.peers.getDarknetPeers();
                for(int i=0;i<pn.length;i++)
                {
                        Peer peer = pn[i].getPeer();
@@ -1063,7 +1063,7 @@
         * Report peer success as boolean
         */
        private boolean enablePeer(String nodeIdentifier) {
-               PeerNode[] pn = n.peers.myPeers;
+               DarknetPeerNode[] pn = n.peers.getDarknetPeers();
                for(int i=0;i<pn.length;i++)
                {
                        Peer peer = pn[i].getPeer();
@@ -1086,7 +1086,7 @@
      * Report peer existence as boolean
      */
     private boolean havePeer(String nodeIdentifier) {
-       PeerNode[] pn = n.peers.myPeers;
+       DarknetPeerNode[] pn = n.peers.getDarknetPeers();
        for(int i=0;i<pn.length;i++)
        {
                Peer peer = pn[i].getPeer();
@@ -1110,7 +1110,7 @@
      */
     private boolean removePeer(String nodeIdentifier) {
        System.out.println("Removing peer from node for: "+nodeIdentifier);
-       PeerNode[] pn = n.peers.myPeers;
+       DarknetPeerNode[] pn = n.peers.getDarknetPeers();
        for(int i=0;i<pn.length;i++)
        {
                Peer peer = pn[i].getPeer();

Modified: trunk/freenet/src/freenet/node/fcp/ListPeerNotesMessage.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ListPeerNotesMessage.java        
2007-06-28 15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/node/fcp/ListPeerNotesMessage.java        
2007-06-28 17:07:40 UTC (rev 13809)
@@ -3,8 +3,8 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.node.fcp;

+import freenet.node.DarknetPeerNode;
 import freenet.node.Node;
-import freenet.node.PeerNode;
 import freenet.support.SimpleFieldSet;

 public class ListPeerNotesMessage extends FCPMessage {
@@ -30,7 +30,7 @@
                        throw new 
MessageInvalidException(ProtocolErrorMessage.ACCESS_DENIED, "ListPeerNotes 
requires full access", fs.get("Identifier"), false);
                }
                String nodeIdentifier = fs.get("NodeIdentifier");
-               PeerNode pn = node.getPeerNode(nodeIdentifier);
+               DarknetPeerNode pn = node.getPeerNode(nodeIdentifier);
                if(pn == null) {
                        FCPMessage msg = new 
UnknownNodeIdentifierMessage(nodeIdentifier);
                        handler.outputHandler.queue(msg);

Modified: trunk/freenet/src/freenet/node/fcp/ModifyPeer.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ModifyPeer.java  2007-06-28 15:02:56 UTC 
(rev 13808)
+++ trunk/freenet/src/freenet/node/fcp/ModifyPeer.java  2007-06-28 17:07:40 UTC 
(rev 13809)
@@ -3,8 +3,8 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.node.fcp;

+import freenet.node.DarknetPeerNode;
 import freenet.node.Node;
-import freenet.node.PeerNode;
 import freenet.support.Fields;
 import freenet.support.SimpleFieldSet;

@@ -31,7 +31,7 @@
                        throw new 
MessageInvalidException(ProtocolErrorMessage.ACCESS_DENIED, "ModifyPeer 
requires full access", fs.get("Identifier"), false);
                }
                String nodeIdentifier = fs.get("NodeIdentifier");
-               PeerNode pn = node.getPeerNode(nodeIdentifier);
+               DarknetPeerNode pn = node.getPeerNode(nodeIdentifier);
                if(pn == null) {
                        FCPMessage msg = new 
UnknownNodeIdentifierMessage(nodeIdentifier);
                        handler.outputHandler.queue(msg);

Modified: trunk/freenet/src/freenet/node/fcp/ModifyPeerNote.java
===================================================================
--- trunk/freenet/src/freenet/node/fcp/ModifyPeerNote.java      2007-06-28 
15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/node/fcp/ModifyPeerNote.java      2007-06-28 
17:07:40 UTC (rev 13809)
@@ -3,9 +3,9 @@
  * http://www.gnu.org/ for further details of the GPL. */
 package freenet.node.fcp;

+import freenet.node.DarknetPeerNode;
 import freenet.node.FSParseException;
 import freenet.node.Node;
-import freenet.node.PeerNode;
 import freenet.support.Base64;
 import freenet.support.IllegalBase64Exception;
 import freenet.support.Logger;
@@ -37,7 +37,7 @@
                if( nodeIdentifier == null ) {
                        throw new 
MessageInvalidException(ProtocolErrorMessage.MISSING_FIELD, "Error: 
NodeIdentifier field missing", null, false);
                }
-               PeerNode pn = node.getPeerNode(nodeIdentifier);
+               DarknetPeerNode pn = node.getPeerNode(nodeIdentifier);
                if(pn == null) {
                        FCPMessage msg = new 
UnknownNodeIdentifierMessage(nodeIdentifier);
                        handler.outputHandler.queue(msg);

Modified: trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java
===================================================================
--- trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java      
2007-06-28 15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/node/updater/UpdateOverMandatoryManager.java      
2007-06-28 17:07:40 UTC (rev 13809)
@@ -112,7 +112,7 @@

                logMINOR = Logger.shouldLog(Logger.MINOR, this);
                if(logMINOR) {
-                       Logger.minor(this, "Update Over Mandatory offer from 
node "+source.getPeer()+" : "+source.getName()+":");
+                       Logger.minor(this, "Update Over Mandatory offer from 
node "+source.getPeer()+" : "+source.userToString()+":");
                        Logger.minor(this, "Main jar key: "+jarKey+" 
version="+mainJarVersion+" length="+mainJarFileLength);
                        Logger.minor(this, "Extra jar key: "+extraJarKey+" 
version="+extraJarVersion+" length="+extraJarFileLength);
                        Logger.minor(this, "Revocation key: "+revocationKey+" 
found="+haveRevocationKey+" length="+revocationKeyFileLength+" last had 3 DNFs 
"+revocationKeyLastTried+" ms ago, "+revocationKeyDNFs+" DNFs so far");
@@ -147,7 +147,7 @@
                                        // Tell the user
                                        alertUser();

-                                       System.err.println("Your peer 
"+source.getPeer()+" : "+source.getName()+" says that the auto-update key is 
blown!");
+                                       System.err.println("Your peer 
"+source.userToString()+" says that the auto-update key is blown!");
                                        System.err.println("Attempting to fetch 
it...");

                                        // Try to transfer it.
@@ -159,14 +159,14 @@
                                                }
                                                public void disconnected() {
                                                        // :(
-                                                       
System.err.println("Failed to send request for revocation key to 
"+source.getPeer()+" : "+source.getName()+" because it disconnected!");
+                                                       
System.err.println("Failed to send request for revocation key to 
"+source.userToString()+" because it disconnected!");
                                                        
synchronized(UpdateOverMandatoryManager.this) {
                                                                
nodesSayKeyRevokedFailedTransfer.add(source);
                                                        }
                                                }
                                                public void fatalError() {
                                                        // Not good!
-                                                       
System.err.println("Failed to send request for revocation key to 
"+source.getPeer()+" : "+source.getName()+" because of a fatal error.");
+                                                       
System.err.println("Failed to send request for revocation key to 
"+source.userToString()+" because of a fatal error.");
                                                }
                                                public void sent() {
                                                        // Cool
@@ -179,12 +179,12 @@
                                } else {
                                        // Should probably also be a useralert?
                                        Logger.normal(this, "Node "+source+" 
sent us a UOM claiming that the auto-update key was blown, but it used a 
different key to us: \nour key="+updateManager.revocationURI+"\nhis 
key="+revocationURI);
-                                       System.err.println("Node 
"+source.getPeer()+" : "+source.getName()+" sent us a UOM claiming that the 
revocation key was blown, but it used a different key to us: \nour 
key="+updateManager.revocationURI+"\nhis key="+revocationURI);
+                                       System.err.println("Node 
"+source.userToString()+" sent us a UOM claiming that the revocation key was 
blown, but it used a different key to us: \nour 
key="+updateManager.revocationURI+"\nhis key="+revocationURI);
                                }
                        } catch (MalformedURLException e) {
                                // Should maybe be a useralert?
                                Logger.error(this, "Node "+source+" sent us a 
UOMAnnounce claiming that the auto-update key was blown, but it had an invalid 
revocation URI: "+revocationKey+" : "+e, e);
-                               System.err.println("Node "+source.getPeer()+" : 
"+source.getName()+" sent us a UOMAnnounce claiming that the revocation key was 
blown, but it had an invalid revocation URI: "+revocationKey+" : "+e);
+                               System.err.println("Node 
"+source.userToString()+" sent us a UOMAnnounce claiming that the revocation 
key was blown, but it had an invalid revocation URI: "+revocationKey+" : "+e);
                        } catch (NotConnectedException e) {
                                System.err.println("Node "+source+" says that 
the auto-update key was blown, but has now gone offline! Something bad may be 
happening!");
                                Logger.error(this, "Node "+source+" says that 
the auto-update key was blown, but has now gone offline! Something bad may be 
happening!");
@@ -213,7 +213,7 @@
                        } catch (MalformedURLException e) {
                                // Should maybe be a useralert?
                                Logger.error(this, "Node "+source+" sent us a 
UOMAnnounce claiming to have a new jar, but it had an invalid URI: 
"+revocationKey+" : "+e, e);
-                               System.err.println("Node "+source.getPeer()+" : 
"+source.getName()+" sent us a UOMAnnounce claiming to have a new jar, but it 
had an invalid URI: "+revocationKey+" : "+e);
+                               System.err.println("Node 
"+source.userToString()+" sent us a UOMAnnounce claiming to have a new jar, but 
it had an invalid URI: "+revocationKey+" : "+e);
                        }
                }

@@ -329,7 +329,7 @@
                                div.addChild("p").addChild("#", 
l10n("connectedSayBlownLabel"));
                                HTMLNode list = div.addChild("ul");
                                for(int 
i=0;i<nodesSayBlownConnected.length;i++) {
-                                       list.addChild("li", 
nodesSayBlownConnected[i].getName()+" 
("+nodesSayBlownConnected[i].getPeer()+")");
+                                       list.addChild("li", 
nodesSayBlownConnected[i].userToString()+" 
("+nodesSayBlownConnected[i].getPeer()+")");
                                }
                        }

@@ -337,7 +337,7 @@
                                div.addChild("p").addChild("#", 
l10n("disconnectedSayBlownLabel"));
                                HTMLNode list = div.addChild("ul");
                                for(int 
i=0;i<nodesSayBlownDisconnected.length;i++) {
-                                       list.addChild("li", 
nodesSayBlownDisconnected[i].getName()+" 
("+nodesSayBlownDisconnected[i].getPeer()+")");
+                                       list.addChild("li", 
nodesSayBlownDisconnected[i].userToString()+" 
("+nodesSayBlownDisconnected[i].getPeer()+")");
                                }
                        }

@@ -345,7 +345,7 @@
                                div.addChild("p").addChild("#", 
l10n("failedTransferSayBlownLabel"));
                                HTMLNode list = div.addChild("ul");
                                for(int 
i=0;i<nodesSayBlownFailedTransfer.length;i++) {
-                                       list.addChild("li", 
nodesSayBlownFailedTransfer[i].getName()+" 
("+nodesSayBlownFailedTransfer[i].getPeer()+")");
+                                       list.addChild("li", 
nodesSayBlownFailedTransfer[i].userToString()+" 
("+nodesSayBlownFailedTransfer[i].getPeer()+")");
                                }
                        }

@@ -381,7 +381,7 @@
                        if(nodesSayBlownConnected.length > 0) {
                                
sb.append(l10n("connectedSayBlownLabel")).append("\n\n");
                                for(int 
i=0;i<nodesSayBlownConnected.length;i++) {
-                                       
sb.append(nodesSayBlownConnected[i].getName()+" 
("+nodesSayBlownConnected[i].getPeer()+")").append("\n");
+                                       
sb.append(nodesSayBlownConnected[i].userToString()+" 
("+nodesSayBlownConnected[i].getPeer()+")").append("\n");
                                }
                                sb.append("\n");
                        }
@@ -390,7 +390,7 @@
                                sb.append(l10n("disconnectedSayBlownLabel"));

                                for(int 
i=0;i<nodesSayBlownDisconnected.length;i++) {
-                                       
sb.append(nodesSayBlownDisconnected[i].getName()+" 
("+nodesSayBlownDisconnected[i].getPeer()+")").append("\n");
+                                       
sb.append(nodesSayBlownDisconnected[i].userToString()+" 
("+nodesSayBlownDisconnected[i].getPeer()+")").append("\n");
                                }
                                sb.append("\n");
                        }
@@ -399,7 +399,7 @@
                                sb.append(l10n("failedTransferSayBlownLabel"));

                                for(int 
i=0;i<nodesSayBlownFailedTransfer.length;i++) {
-                                       
sb.append(nodesSayBlownFailedTransfer[i].getName()+" 
("+nodesSayBlownFailedTransfer[i].getPeer()+")").append('\n');
+                                       
sb.append(nodesSayBlownFailedTransfer[i].userToString()+" 
("+nodesSayBlownFailedTransfer[i].getPeer()+")").append('\n');
                                }
                                sb.append("\n");
                        }
@@ -514,9 +514,9 @@
                final Runnable r = new Runnable() {
                        public void run() {
                                if(!bt.send()) {
-                                       Logger.error(this, "Failed to send 
revocation key blob to "+source.getPeer()+" : "+source.getName());
+                                       Logger.error(this, "Failed to send 
revocation key blob to "+source.userToString());
                                } else {
-                                       Logger.normal(this, "Sent revocation 
key blob to "+source.getPeer()+" : "+source.getName());
+                                       Logger.normal(this, "Sent revocation 
key blob to "+source.userToString());
                                }
                        }

@@ -530,7 +530,7 @@
                                        if(logMINOR)
                                                Logger.minor(this, "Sending 
data...");
                                        // Send the data
-                                       Thread t = new Thread(r, "Revocation 
key send for "+uid+" to "+source.getPeer()+" : "+source.getName());
+                                       Thread t = new Thread(r, "Revocation 
key send for "+uid+" to "+source.userToString());
                                        t.setDaemon(true);
                                        t.start();
                                }
@@ -583,7 +583,7 @@

                if(!revocationURI.equals(updateManager.revocationURI)) {
                        System.err.println("Node sending us a revocation 
certificate from the wrong URI:\n"+
-                                       "Node: "+source.getPeer()+" : 
"+source.getName()+"\n"+
+                                       "Node: "+source.userToString()+"\n"+
                                        "Our   URI: 
"+updateManager.revocationURI+"\n"+
                                        "Their URI: "+revocationURI);
                        synchronized(this) {
@@ -602,8 +602,8 @@
                }

                if(length > NodeUpdateManager.MAX_REVOCATION_KEY_LENGTH) {
-                       System.err.println("Node "+source.getPeer()+" : 
"+source.getName()+" offered us a revocation certificate 
"+SizeUtil.formatSize(length)+" long. This is unacceptably long so we have 
refused the transfer.");
-                       Logger.error(this, "Node "+source.getPeer()+" : 
"+source.getName()+" offered us a revocation certificate 
"+SizeUtil.formatSize(length)+" long. This is unacceptably long so we have 
refused the transfer.");
+                       System.err.println("Node "+source.userToString()+" 
offered us a revocation certificate "+SizeUtil.formatSize(length)+" long. This 
is unacceptably long so we have refused the transfer.");
+                       Logger.error(this, "Node "+source.userToString()+" 
offered us a revocation certificate "+SizeUtil.formatSize(length)+" long. This 
is unacceptably long so we have refused the transfer.");
                        synchronized(UpdateOverMandatoryManager.this) {
                                nodesSayKeyRevokedFailedTransfer.add(source);
                        }
@@ -654,7 +654,7 @@
                                }
                        }

-               }, "Revocation key receive for "+uid+" from 
"+source.getPeer()+" : "+source.getName());
+               }, "Revocation key receive for "+uid+" from 
"+source.userToString());

                t.setDaemon(true);
                t.start();
@@ -892,9 +892,9 @@
                final Runnable r = new Runnable() {
                        public void run() {
                                if(!bt.send()) {
-                                       Logger.error(this, "Failed to send main 
jar blob to "+source.getPeer()+" : "+source.getName());
+                                       Logger.error(this, "Failed to send main 
jar blob to "+source.userToString());
                                } else {
-                                       Logger.normal(this, "Sent main jar blob 
to "+source.getPeer()+" : "+source.getName());
+                                       Logger.normal(this, "Sent main jar blob 
to "+source.userToString());
                                }
                        }

@@ -908,7 +908,7 @@
                                        if(logMINOR)
                                                Logger.minor(this, "Sending 
data...");
                                        // Send the data
-                                       Thread t = new Thread(r, "Main jar send 
for "+uid+" to "+source.getPeer()+" : "+source.getName());
+                                       Thread t = new Thread(r, "Main jar send 
for "+uid+" to "+source.userToString());
                                        t.setDaemon(true);
                                        t.start();
                                }
@@ -961,7 +961,7 @@

                
if(!jarURI.equals(updateManager.updateURI.setSuggestedEdition(version))) {
                        System.err.println("Node sending us a main jar update 
("+version+") from the wrong URI:\n"+
-                                       "Node: "+source.getPeer()+" : 
"+source.getName()+"\n"+
+                                       "Node: "+source.userToString()+"\n"+
                                        "Our   URI: 
"+updateManager.updateURI+"\n"+
                                        "Their URI: "+jarURI);
                        cancelSend(source, uid);
@@ -982,8 +982,8 @@
                }

                if(length > NodeUpdateManager.MAX_MAIN_JAR_LENGTH) {
-                       System.err.println("Node "+source.getPeer()+" : 
"+source.getName()+" offered us a main jar ("+version+") 
"+SizeUtil.formatSize(length)+" long. This is unacceptably long so we have 
refused the transfer.");
-                       Logger.error(this, "Node "+source.getPeer()+" : 
"+source.getName()+" offered us a main jar ("+version+") 
"+SizeUtil.formatSize(length)+" long. This is unacceptably long so we have 
refused the transfer.");
+                       System.err.println("Node "+source.userToString()+" 
offered us a main jar ("+version+") "+SizeUtil.formatSize(length)+" long. This 
is unacceptably long so we have refused the transfer.");
+                       Logger.error(this, "Node "+source.userToString()+" 
offered us a main jar ("+version+") "+SizeUtil.formatSize(length)+" long. This 
is unacceptably long so we have refused the transfer.");
                        // If the transfer fails, we don't try again.
                        cancelSend(source, uid);
                        synchronized(this) {
@@ -1047,7 +1047,7 @@
                                }
                        }

-               }, "Main jar ("+version+") receive for "+uid+" from 
"+source.getPeer()+" : "+source.getName());
+               }, "Main jar ("+version+") receive for "+uid+" from 
"+source.userToString());

                t.setDaemon(true);
                t.start();

Modified: trunk/freenet/src/freenet/node/useralerts/N2NTMUserAlert.java
===================================================================
--- trunk/freenet/src/freenet/node/useralerts/N2NTMUserAlert.java       
2007-06-28 15:02:56 UTC (rev 13808)
+++ trunk/freenet/src/freenet/node/useralerts/N2NTMUserAlert.java       
2007-06-28 17:07:40 UTC (rev 13809)
@@ -7,13 +7,13 @@
 import java.util.Date;

 import freenet.l10n.L10n;
-import freenet.node.PeerNode;
+import freenet.node.DarknetPeerNode;
 import freenet.support.HTMLNode;

 // Node To Node Text Message User Alert
 public class N2NTMUserAlert implements UserAlert {
        private boolean isValid=true;
-       private PeerNode sourcePeerNode;
+       private DarknetPeerNode sourcePeerNode;
        private String sourceNodename;
        private String targetNodename;
        private String messageText;
@@ -22,7 +22,7 @@
        private long sentTime;
        private long receivedTime;

-       public N2NTMUserAlert(PeerNode sourcePeerNode, String source, String 
target, String message, int fileNumber, long composedTime, long  sentTime, long 
receivedTime) {
+       public N2NTMUserAlert(DarknetPeerNode sourcePeerNode, String source, 
String target, String message, int fileNumber, long composedTime, long  
sentTime, long receivedTime) {
                this.sourcePeerNode = sourcePeerNode;
                this.sourceNodename = source;
                this.targetNodename = target;


Reply via email to