forked from I2P_Developers/i2p.i2p
merge of '0738aeef8a1d4e9ca82dc5ba0077d83a57c47f81'
and '9625ea3e96d57df74bc62018bf64230a22c49ce0'
This commit is contained in:
@@ -25,9 +25,9 @@ import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.StringReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -35,6 +35,8 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* Utility class providing methods to parse and write files in config file
|
||||
* format, and subscription file format.
|
||||
@@ -277,7 +279,7 @@ public class ConfigParser {
|
||||
*/
|
||||
public static void write(Map map, File file) throws IOException {
|
||||
ConfigParser
|
||||
.write(map, new BufferedWriter(new FileWriter(file, false)));
|
||||
.write(map, new BufferedWriter(new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -316,7 +318,7 @@ public class ConfigParser {
|
||||
public static void writeSubscriptions(List list, File file)
|
||||
throws IOException {
|
||||
ConfigParser.writeSubscriptions(list, new BufferedWriter(
|
||||
new FileWriter(file, false)));
|
||||
new OutputStreamWriter(new SecureFileOutputStream(file), "UTF-8")));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -29,6 +29,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
|
||||
/**
|
||||
* Main class of addressbook. Performs updates, and runs the main loop.
|
||||
@@ -131,11 +132,11 @@ public class Daemon {
|
||||
String settingsLocation = "config.txt";
|
||||
File homeFile;
|
||||
if (args.length > 0) {
|
||||
homeFile = new File(args[0]);
|
||||
homeFile = new SecureDirectory(args[0]);
|
||||
if (!homeFile.isAbsolute())
|
||||
homeFile = new File(I2PAppContext.getGlobalContext().getRouterDir(), args[0]);
|
||||
homeFile = new SecureDirectory(I2PAppContext.getGlobalContext().getRouterDir(), args[0]);
|
||||
} else {
|
||||
homeFile = new File(System.getProperty("user.dir"));
|
||||
homeFile = new SecureDirectory(System.getProperty("user.dir"));
|
||||
}
|
||||
|
||||
Map defaultSettings = new HashMap();
|
||||
|
@@ -26,6 +26,7 @@ import net.i2p.util.ConcurrentHashSet;
|
||||
import net.i2p.util.EepGet;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.Translate;
|
||||
@@ -77,7 +78,7 @@ public class I2PSnarkUtil {
|
||||
// This is used for both announce replies and .torrent file downloads,
|
||||
// so it must be available even if not connected to I2CP.
|
||||
// so much for multiple instances
|
||||
_tmpDir = new File(ctx.getTempDir(), "i2psnark");
|
||||
_tmpDir = new SecureDirectory(ctx.getTempDir(), "i2psnark");
|
||||
FileUtil.rmdir(_tmpDir, false);
|
||||
_tmpDir.mkdirs();
|
||||
}
|
||||
@@ -196,11 +197,14 @@ public class I2PSnarkUtil {
|
||||
|
||||
/** connect to the given destination */
|
||||
I2PSocket connect(PeerID peer) throws IOException {
|
||||
Hash dest = peer.getAddress().calculateHash();
|
||||
Destination addr = peer.getAddress();
|
||||
if (addr == null)
|
||||
throw new IOException("Null address");
|
||||
Hash dest = addr.calculateHash();
|
||||
if (_shitlist.contains(dest))
|
||||
throw new IOException("Not trying to contact " + dest.toBase64() + ", as they are shitlisted");
|
||||
try {
|
||||
I2PSocket rv = _manager.connect(peer.getAddress());
|
||||
I2PSocket rv = _manager.connect(addr);
|
||||
if (rv != null)
|
||||
_shitlist.remove(dest);
|
||||
return rv;
|
||||
@@ -224,7 +228,8 @@ public class I2PSnarkUtil {
|
||||
public File get(String url, boolean rewrite) { return get(url, rewrite, 0); }
|
||||
public File get(String url, int retries) { return get(url, true, retries); }
|
||||
public File get(String url, boolean rewrite, int retries) {
|
||||
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fetching [" + url + "] proxy=" + _proxyHost + ":" + _proxyPort + ": " + _shouldProxy);
|
||||
File out = null;
|
||||
try {
|
||||
// we could use the system tmp dir but deleteOnExit() doesn't seem to work on all platforms...
|
||||
@@ -248,10 +253,12 @@ public class I2PSnarkUtil {
|
||||
}
|
||||
EepGet get = new I2PSocketEepGet(_context, _manager, retries, out.getAbsolutePath(), fetchURL);
|
||||
if (get.fetch()) {
|
||||
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Fetch successful [" + url + "]: size=" + out.length());
|
||||
return out;
|
||||
} else {
|
||||
_log.warn("Fetch failed [" + url + "]");
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Fetch failed [" + url + "]");
|
||||
out.delete();
|
||||
return null;
|
||||
}
|
||||
|
@@ -109,7 +109,8 @@ public class MetaInfo
|
||||
*/
|
||||
public MetaInfo(Map m) throws InvalidBEncodingException
|
||||
{
|
||||
_log.debug("Creating a metaInfo: " + m, new Exception("source"));
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Creating a metaInfo: " + m, new Exception("source"));
|
||||
BEValue val = (BEValue)m.get("announce");
|
||||
if (val == null)
|
||||
throw new InvalidBEncodingException("Missing announce string");
|
||||
@@ -446,14 +447,16 @@ public class MetaInfo
|
||||
else
|
||||
buf.append(val.toString());
|
||||
}
|
||||
_log.debug(buf.toString());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug(buf.toString());
|
||||
byte[] infoBytes = BEncoder.bencode(info);
|
||||
//_log.debug("info bencoded: [" + Base64.encode(infoBytes, true) + "]");
|
||||
try
|
||||
{
|
||||
MessageDigest digest = MessageDigest.getInstance("SHA");
|
||||
byte hash[] = digest.digest(infoBytes);
|
||||
_log.debug("info hash: [" + net.i2p.data.Base64.encode(hash) + "]");
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("info hash: [" + net.i2p.data.Base64.encode(hash) + "]");
|
||||
return hash;
|
||||
}
|
||||
catch(NoSuchAlgorithmException nsa)
|
||||
|
@@ -92,7 +92,8 @@ public class Peer implements Comparable
|
||||
byte[] id = handshake(in, out);
|
||||
this.peerID = new PeerID(id, sock.getPeerDestination());
|
||||
_id = ++__id;
|
||||
_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id));
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Creating a new peer with " + peerID.getAddress().calculateHash().toBase64(), new Exception("creating " + _id));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -129,7 +130,7 @@ public class Peer implements Comparable
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return peerID.hashCode() ^ (2 << _id);
|
||||
return peerID.hashCode() ^ (7777 * (int)_id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,6 +151,7 @@ public class Peer implements Comparable
|
||||
|
||||
/**
|
||||
* Compares the PeerIDs.
|
||||
* @deprecated unused?
|
||||
*/
|
||||
public int compareTo(Object o)
|
||||
{
|
||||
@@ -181,14 +183,16 @@ public class Peer implements Comparable
|
||||
if (state != null)
|
||||
throw new IllegalStateException("Peer already started");
|
||||
|
||||
_log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting"));
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Running connection to " + peerID.getAddress().calculateHash().toBase64(), new Exception("connecting"));
|
||||
try
|
||||
{
|
||||
// Do we need to handshake?
|
||||
if (din == null)
|
||||
{
|
||||
sock = util.connect(peerID);
|
||||
_log.debug("Connected to " + peerID + ": " + sock);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Connected to " + peerID + ": " + sock);
|
||||
if ((sock == null) || (sock.isClosed())) {
|
||||
throw new IOException("Unable to reach " + peerID);
|
||||
}
|
||||
@@ -207,14 +211,20 @@ public class Peer implements Comparable
|
||||
// = new BufferedOutputStream(sock.getOutputStream());
|
||||
byte [] id = handshake(in, out); //handshake(bis, bos);
|
||||
byte [] expected_id = peerID.getID();
|
||||
if (!Arrays.equals(expected_id, id))
|
||||
throw new IOException("Unexpected peerID '"
|
||||
if (expected_id == null) {
|
||||
peerID.setID(id);
|
||||
} else if (Arrays.equals(expected_id, id)) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Handshake got matching IDs with " + toString());
|
||||
} else {
|
||||
throw new IOException("Unexpected peerID '"
|
||||
+ PeerID.idencode(id)
|
||||
+ "' expected '"
|
||||
+ PeerID.idencode(expected_id) + "'");
|
||||
_log.debug("Handshake got matching IDs with " + toString());
|
||||
}
|
||||
} else {
|
||||
_log.debug("Already have din [" + sock + "] with " + toString());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Already have din [" + sock + "] with " + toString());
|
||||
}
|
||||
|
||||
PeerConnectionIn in = new PeerConnectionIn(this, din);
|
||||
@@ -229,7 +239,8 @@ public class Peer implements Comparable
|
||||
state = s;
|
||||
listener.connected(this);
|
||||
|
||||
_log.debug("Start running the reader with " + toString());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Start running the reader with " + toString());
|
||||
// Use this thread for running the incomming connection.
|
||||
// The outgoing connection creates its own Thread.
|
||||
out.startup();
|
||||
@@ -279,7 +290,8 @@ public class Peer implements Comparable
|
||||
dout.write(my_id);
|
||||
dout.flush();
|
||||
|
||||
_log.debug("Wrote my shared hash and ID to " + toString());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Wrote my shared hash and ID to " + toString());
|
||||
|
||||
// Handshake read - header
|
||||
byte b = din.readByte();
|
||||
@@ -306,7 +318,8 @@ public class Peer implements Comparable
|
||||
|
||||
// Handshake read - peer id
|
||||
din.readFully(bs);
|
||||
_log.debug("Read the remote side's hash and peerID fully from " + toString());
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Read the remote side's hash and peerID fully from " + toString());
|
||||
return bs;
|
||||
}
|
||||
|
||||
|
@@ -76,10 +76,12 @@ public class PeerAcceptor
|
||||
// is this working right?
|
||||
try {
|
||||
peerInfoHash = readHash(in);
|
||||
_log.info("infohash read from " + socket.getPeerDestination().calculateHash().toBase64()
|
||||
+ ": " + Base64.encode(peerInfoHash));
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("infohash read from " + socket.getPeerDestination().calculateHash().toBase64()
|
||||
+ ": " + Base64.encode(peerInfoHash));
|
||||
} catch (IOException ioe) {
|
||||
_log.info("Unable to read the infohash from " + socket.getPeerDestination().calculateHash().toBase64());
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Unable to read the infohash from " + socket.getPeerDestination().calculateHash().toBase64());
|
||||
throw ioe;
|
||||
}
|
||||
in = new SequenceInputStream(new ByteArrayInputStream(peerInfoHash), in);
|
||||
|
@@ -375,7 +375,8 @@ public class PeerCoordinator implements PeerListener
|
||||
|
||||
if (need_more)
|
||||
{
|
||||
_log.debug("Adding a peer " + peer.getPeerID().getAddress().calculateHash().toBase64() + " for " + metainfo.getName(), new Exception("add/run"));
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Adding a peer " + peer.getPeerID().toString() + " for " + metainfo.getName(), new Exception("add/run"));
|
||||
|
||||
// Run the peer with us as listener and the current bitfield.
|
||||
final PeerListener listener = this;
|
||||
|
@@ -24,19 +24,33 @@ import java.io.IOException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
|
||||
import org.klomp.snark.bencode.BDecoder;
|
||||
import org.klomp.snark.bencode.BEValue;
|
||||
import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||
|
||||
/**
|
||||
* Store the address information about a peer.
|
||||
* Prior to 0.8.1, an instantiation required a peer ID, and full Destination address.
|
||||
* Starting with 0.8.1, to support compact tracker responses,
|
||||
* a PeerID can be instantiated with a Destination Hash alone.
|
||||
* The full destination lookup is deferred until getAddress() is called,
|
||||
* and the PeerID is not required.
|
||||
* Equality is now determined solely by the dest hash.
|
||||
*/
|
||||
public class PeerID implements Comparable
|
||||
{
|
||||
private final byte[] id;
|
||||
private final Destination address;
|
||||
private byte[] id;
|
||||
private Destination address;
|
||||
private final int port;
|
||||
|
||||
private byte[] destHash;
|
||||
/** whether we have tried to get the dest from the hash - only do once */
|
||||
private boolean triedDestLookup;
|
||||
private final int hash;
|
||||
|
||||
public PeerID(byte[] id, Destination address)
|
||||
@@ -44,7 +58,7 @@ public class PeerID implements Comparable
|
||||
this.id = id;
|
||||
this.address = address;
|
||||
this.port = 6881;
|
||||
|
||||
this.destHash = address.calculateHash().getData();
|
||||
hash = calculateHash();
|
||||
}
|
||||
|
||||
@@ -77,17 +91,49 @@ public class PeerID implements Comparable
|
||||
throw new InvalidBEncodingException("Invalid destination [" + bevalue.getString() + "]");
|
||||
|
||||
port = 6881;
|
||||
|
||||
this.destHash = address.calculateHash().getData();
|
||||
hash = calculateHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a PeerID from a destHash
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public PeerID(byte[] dest_hash) throws InvalidBEncodingException
|
||||
{
|
||||
// id and address remain null
|
||||
port = 6881;
|
||||
if (dest_hash.length != 32)
|
||||
throw new InvalidBEncodingException("bad hash length");
|
||||
destHash = dest_hash;
|
||||
hash = DataHelper.hashCode(dest_hash);
|
||||
}
|
||||
|
||||
public byte[] getID()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
public Destination getAddress()
|
||||
/** for connecting out to peer based on desthash @since 0.8.1 */
|
||||
public void setID(byte[] xid)
|
||||
{
|
||||
id = xid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the destination.
|
||||
* If this PeerId was instantiated with a destHash,
|
||||
* and we have not yet done so, lookup the full destination, which may take
|
||||
* up to 10 seconds.
|
||||
* @return Dest or null if unknown
|
||||
*/
|
||||
public synchronized Destination getAddress()
|
||||
{
|
||||
if (address == null && destHash != null && !triedDestLookup) {
|
||||
String b32 = Base32.encode(destHash) + ".b32.i2p";
|
||||
address = I2PAppContext.getGlobalContext().namingService().lookup(b32);
|
||||
triedDestLookup = true;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
@@ -96,16 +142,19 @@ public class PeerID implements Comparable
|
||||
return port;
|
||||
}
|
||||
|
||||
/** @since 0.8.1 */
|
||||
public byte[] getDestHash()
|
||||
{
|
||||
return destHash;
|
||||
}
|
||||
|
||||
private int calculateHash()
|
||||
{
|
||||
int b = 0;
|
||||
for (int i = 0; i < id.length; i++)
|
||||
b ^= id[i];
|
||||
return (b ^ address.hashCode()) ^ port;
|
||||
return DataHelper.hashCode(destHash);
|
||||
}
|
||||
|
||||
/**
|
||||
* The hash code of a PeerID is the exclusive or of all id bytes.
|
||||
* The hash code of a PeerID is the hashcode of the desthash
|
||||
*/
|
||||
@Override
|
||||
public int hashCode()
|
||||
@@ -115,18 +164,15 @@ public class PeerID implements Comparable
|
||||
|
||||
/**
|
||||
* Returns true if and only if this peerID and the given peerID have
|
||||
* the same 20 bytes as ID.
|
||||
* the same destination hash
|
||||
*/
|
||||
public boolean sameID(PeerID pid)
|
||||
{
|
||||
boolean equal = true;
|
||||
for (int i = 0; equal && i < id.length; i++)
|
||||
equal = id[i] == pid.id[i];
|
||||
return equal;
|
||||
return DataHelper.eq(destHash, pid.getDestHash());
|
||||
}
|
||||
|
||||
/**
|
||||
* Two PeerIDs are equal when they have the same id, address and port.
|
||||
* Two PeerIDs are equal when they have the same dest hash
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
@@ -135,9 +181,7 @@ public class PeerID implements Comparable
|
||||
{
|
||||
PeerID pid = (PeerID)o;
|
||||
|
||||
return port == pid.port
|
||||
&& address.equals(pid.address)
|
||||
&& sameID(pid);
|
||||
return sameID(pid);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
@@ -145,6 +189,7 @@ public class PeerID implements Comparable
|
||||
|
||||
/**
|
||||
* Compares port, address and id.
|
||||
* @deprecated unused? and will NPE now that address can be null?
|
||||
*/
|
||||
public int compareTo(Object o)
|
||||
{
|
||||
@@ -176,6 +221,8 @@ public class PeerID implements Comparable
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
if (id == null || address == null)
|
||||
return "unkn@" + Base64.encode(destHash).substring(0, 6);
|
||||
int nonZero = 0;
|
||||
for (int i = 0; i < id.length; i++) {
|
||||
if (id[i] != 0) {
|
||||
|
@@ -437,7 +437,7 @@ public class Snark
|
||||
try { storage.close(); } catch (IOException ioee) {
|
||||
ioee.printStackTrace();
|
||||
}
|
||||
fatal("Could not create storage", ioe);
|
||||
fatal("Could not check or create storage", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -357,6 +357,7 @@ public class TrackerClient extends I2PAppThread
|
||||
+ "&uploaded=" + uploaded
|
||||
+ "&downloaded=" + downloaded
|
||||
+ "&left=" + left
|
||||
+ "&compact"
|
||||
+ ((! event.equals(NO_EVENT)) ? ("&event=" + event) : "");
|
||||
if (left <= 0 || event.equals(STOPPED_EVENT) || !coordinator.needPeers())
|
||||
s += "&numwant=0";
|
||||
|
@@ -24,7 +24,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -33,11 +32,17 @@ import org.klomp.snark.bencode.BDecoder;
|
||||
import org.klomp.snark.bencode.BEValue;
|
||||
import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||
|
||||
/**
|
||||
* The data structure for the tracker response.
|
||||
* Handles both traditional and compact formats.
|
||||
* Compact format 1 - a list of hashes - early format for testing
|
||||
* Compact format 2 - One big string of concatenated hashes - official format
|
||||
*/
|
||||
public class TrackerInfo
|
||||
{
|
||||
private final String failure_reason;
|
||||
private final int interval;
|
||||
private final Set peers;
|
||||
private final Set<Peer> peers;
|
||||
private int complete;
|
||||
private int incomplete;
|
||||
|
||||
@@ -73,10 +78,19 @@ public class TrackerInfo
|
||||
interval = beInterval.getInt();
|
||||
|
||||
BEValue bePeers = (BEValue)m.get("peers");
|
||||
if (bePeers == null)
|
||||
if (bePeers == null) {
|
||||
peers = Collections.EMPTY_SET;
|
||||
else
|
||||
peers = getPeers(bePeers.getList(), my_id, metainfo);
|
||||
} else {
|
||||
Set<Peer> p;
|
||||
try {
|
||||
// One big string (the official compact format)
|
||||
p = getPeers(bePeers.getBytes(), my_id, metainfo);
|
||||
} catch (InvalidBEncodingException ibe) {
|
||||
// List of Dictionaries or List of Strings
|
||||
p = getPeers(bePeers.getList(), my_id, metainfo);
|
||||
}
|
||||
peers = p;
|
||||
}
|
||||
|
||||
BEValue bev = (BEValue)m.get("complete");
|
||||
if (bev != null) try {
|
||||
@@ -94,32 +108,68 @@ public class TrackerInfo
|
||||
}
|
||||
}
|
||||
|
||||
public static Set getPeers(InputStream in, byte[] my_id, MetaInfo metainfo)
|
||||
/******
|
||||
public static Set<Peer> getPeers(InputStream in, byte[] my_id, MetaInfo metainfo)
|
||||
throws IOException
|
||||
{
|
||||
return getPeers(new BDecoder(in), my_id, metainfo);
|
||||
}
|
||||
|
||||
public static Set getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo)
|
||||
public static Set<Peer> getPeers(BDecoder be, byte[] my_id, MetaInfo metainfo)
|
||||
throws IOException
|
||||
{
|
||||
return getPeers(be.bdecodeList().getList(), my_id, metainfo);
|
||||
}
|
||||
******/
|
||||
|
||||
public static Set getPeers(List l, byte[] my_id, MetaInfo metainfo)
|
||||
/** List of Dictionaries or List of Strings */
|
||||
private static Set<Peer> getPeers(List<BEValue> l, byte[] my_id, MetaInfo metainfo)
|
||||
throws IOException
|
||||
{
|
||||
Set peers = new HashSet(l.size());
|
||||
Set<Peer> peers = new HashSet(l.size());
|
||||
|
||||
Iterator it = l.iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
for (BEValue bev : l) {
|
||||
PeerID peerID;
|
||||
try {
|
||||
peerID = new PeerID(((BEValue)it.next()).getMap());
|
||||
// Case 1 - non-compact - A list of dictionaries (maps)
|
||||
peerID = new PeerID(bev.getMap());
|
||||
} catch (InvalidBEncodingException ibe) {
|
||||
// don't let one bad entry spoil the whole list
|
||||
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
|
||||
try {
|
||||
// Case 2 - compact - A list of 32-byte binary strings (hashes)
|
||||
// This was just for testing and is not the official format
|
||||
peerID = new PeerID(bev.getBytes());
|
||||
} catch (InvalidBEncodingException ibe2) {
|
||||
// don't let one bad entry spoil the whole list
|
||||
//Snark.debug("Discarding peer from list: " + ibe, Snark.ERROR);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
peers.add(new Peer(peerID, my_id, metainfo));
|
||||
}
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
private static final int HASH_LENGTH = 32;
|
||||
|
||||
/**
|
||||
* One big string of concatenated 32-byte hashes
|
||||
* @since 0.8.1
|
||||
*/
|
||||
private static Set<Peer> getPeers(byte[] l, byte[] my_id, MetaInfo metainfo)
|
||||
throws IOException
|
||||
{
|
||||
int count = l.length / HASH_LENGTH;
|
||||
Set<Peer> peers = new HashSet(count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
PeerID peerID;
|
||||
byte[] hash = new byte[HASH_LENGTH];
|
||||
System.arraycopy(l, i * HASH_LENGTH, hash, 0, HASH_LENGTH);
|
||||
try {
|
||||
peerID = new PeerID(hash);
|
||||
} catch (InvalidBEncodingException ibe) {
|
||||
// won't happen
|
||||
continue;
|
||||
}
|
||||
peers.add(new Peer(peerID, my_id, metainfo));
|
||||
@@ -128,7 +178,7 @@ public class TrackerInfo
|
||||
return peers;
|
||||
}
|
||||
|
||||
public Set getPeers()
|
||||
public Set<Peer> getPeers()
|
||||
{
|
||||
return peers;
|
||||
}
|
||||
|
@@ -140,7 +140,7 @@ public class BEValue
|
||||
* succeeds when the BEValue is actually a List, otherwise it will
|
||||
* throw a InvalidBEncodingException.
|
||||
*/
|
||||
public List getList() throws InvalidBEncodingException
|
||||
public List<BEValue> getList() throws InvalidBEncodingException
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -157,7 +157,7 @@ public class BEValue
|
||||
* values. This operation only succeeds when the BEValue is actually
|
||||
* a Map, otherwise it will throw a InvalidBEncodingException.
|
||||
*/
|
||||
public Map getMap() throws InvalidBEncodingException
|
||||
public Map<BEValue, BEValue> getMap() throws InvalidBEncodingException
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@@ -8,6 +8,7 @@ import java.io.PrintWriter;
|
||||
import java.text.Collator;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
@@ -821,10 +822,10 @@ public class I2PSnarkServlet extends Default {
|
||||
out.write("</td>\n</tr>\n");
|
||||
|
||||
if(showPeers && isRunning && curPeers > 0) {
|
||||
List peers = snark.coordinator.peerList();
|
||||
Iterator it = peers.iterator();
|
||||
while (it.hasNext()) {
|
||||
Peer peer = (Peer)it.next();
|
||||
List<Peer> peers = snark.coordinator.peerList();
|
||||
if (!showDebug)
|
||||
Collections.sort(peers, new PeerComparator());
|
||||
for (Peer peer : peers) {
|
||||
if (!peer.isConnected())
|
||||
continue;
|
||||
out.write("<tr class=\"" + rowClass + "\">");
|
||||
@@ -908,6 +909,19 @@ public class I2PSnarkServlet extends Default {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by completeness (seeds first), then by ID
|
||||
* @since 0.8.1
|
||||
*/
|
||||
private static class PeerComparator implements Comparator<Peer> {
|
||||
public int compare(Peer l, Peer r) {
|
||||
int diff = r.completed() - l.completed(); // reverse
|
||||
if (diff != 0)
|
||||
return diff;
|
||||
return l.toString().substring(5, 9).compareTo(r.toString().substring(5, 9));
|
||||
}
|
||||
}
|
||||
|
||||
private void writeAddForm(PrintWriter out, HttpServletRequest req) throws IOException {
|
||||
String uri = req.getRequestURI();
|
||||
String newURL = req.getParameter("newURL");
|
||||
|
@@ -22,4 +22,68 @@
|
||||
30
|
||||
</session-timeout>
|
||||
</session-config>
|
||||
|
||||
|
||||
<!-- mime types not in mime.properties in the jetty 5.1.15 source -->
|
||||
|
||||
<mime-mapping>
|
||||
<extension>mkv</extension>
|
||||
<mime-type>video/x-matroska</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>wmv</extension>
|
||||
<mime-type>video/x-ms-wmv</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>flv</extension>
|
||||
<mime-type>video/x-flv</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>mp4</extension>
|
||||
<mime-type>video/mp4</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>rar</extension>
|
||||
<mime-type>application/rar</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>7z</extension>
|
||||
<mime-type>application/x-7z-compressed</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>iso</extension>
|
||||
<mime-type>application/x-iso9660-image</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>ico</extension>
|
||||
<mime-type>image/x-icon</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>exe</extension>
|
||||
<mime-type>application/x-msdos-program</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>flac</extension>
|
||||
<mime-type>audio/flac</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>m4a</extension>
|
||||
<mime-type>audio/mpeg</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
<mime-mapping>
|
||||
<extension>wma</extension>
|
||||
<mime-type>audio/x-ms-wma</mime-type>
|
||||
</mime-mapping>
|
||||
|
||||
</web-app>
|
||||
|
@@ -81,7 +81,7 @@
|
||||
|
||||
<target name="war" depends="precompilejsp, bundle">
|
||||
<war destfile="build/i2ptunnel.war" webxml="../jsp/web-out.xml"
|
||||
basedir="../jsp/" excludes="web.xml, **/*.java, *.jsp">
|
||||
basedir="../jsp/" excludes="web.xml, web-fragment.xml, web-out.xml, **/*.java, *.jsp">
|
||||
</war>
|
||||
</target>
|
||||
|
||||
|
@@ -24,6 +24,9 @@ import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* This does the transparent gzip decompression on the client side.
|
||||
* Extended in I2PTunnelHTTPServer to do the compression on the server side.
|
||||
*
|
||||
* Simple stream for delivering an HTTP response to
|
||||
* the client, trivially filtered to make sure "Connection: close"
|
||||
* is always in the response. Perhaps add transparent handling of the
|
||||
@@ -33,29 +36,27 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private ByteCache _cache;
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
protected ByteArray _headerBuffer;
|
||||
private boolean _headerWritten;
|
||||
private byte _buf1[];
|
||||
private final byte _buf1[];
|
||||
protected boolean _gzip;
|
||||
private long _dataWritten;
|
||||
private InternalGZIPInputStream _in;
|
||||
private static final int CACHE_SIZE = 8*1024;
|
||||
private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE);
|
||||
// OOM DOS prevention
|
||||
private static final int MAX_HEADER_SIZE = 64*1024;
|
||||
|
||||
public HTTPResponseOutputStream(OutputStream raw) {
|
||||
super(raw);
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*1000, 30*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressionRatio", "ratio of compressed size to decompressed size after transfer", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpCompressed", "compressed size transferred", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_context.statManager().createRateStat("i2ptunnel.httpExpanded", "size transferred after expansion", "I2PTunnel", new long[] { 60*60*1000 });
|
||||
_log = _context.logManager().getLog(getClass());
|
||||
_cache = ByteCache.getInstance(8, CACHE_SIZE);
|
||||
_headerBuffer = _cache.acquire();
|
||||
_headerWritten = false;
|
||||
_gzip = false;
|
||||
_dataWritten = 0;
|
||||
_buf1 = new byte[1];
|
||||
}
|
||||
|
||||
@@ -96,14 +97,20 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
}
|
||||
}
|
||||
|
||||
/** grow (and free) the buffer as necessary */
|
||||
private void ensureCapacity() {
|
||||
/**
|
||||
* grow (and free) the buffer as necessary
|
||||
* @throws IOException if the headers are too big
|
||||
*/
|
||||
private void ensureCapacity() throws IOException {
|
||||
if (_headerBuffer.getValid() >= MAX_HEADER_SIZE)
|
||||
throw new IOException("Max header size exceeded: " + MAX_HEADER_SIZE);
|
||||
if (_headerBuffer.getValid() + 1 >= _headerBuffer.getData().length) {
|
||||
int newSize = (int)(_headerBuffer.getData().length * 1.5);
|
||||
ByteArray newBuf = new ByteArray(new byte[newSize]);
|
||||
System.arraycopy(_headerBuffer.getData(), 0, newBuf.getData(), 0, _headerBuffer.getValid());
|
||||
newBuf.setValid(_headerBuffer.getValid());
|
||||
newBuf.setOffset(0);
|
||||
// if we changed the ByteArray size, don't put it back in the cache
|
||||
if (_headerBuffer.getData().length == CACHE_SIZE)
|
||||
_cache.release(_headerBuffer);
|
||||
_headerBuffer = newBuf;
|
||||
@@ -219,7 +226,7 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
//out.flush();
|
||||
PipedInputStream pi = new PipedInputStream();
|
||||
PipedOutputStream po = new PipedOutputStream(pi);
|
||||
new I2PAppThread(new Pusher(pi, out), "HTTP decompresser").start();
|
||||
new I2PAppThread(new Pusher(pi, out), "HTTP decompressor").start();
|
||||
out = po;
|
||||
}
|
||||
|
||||
@@ -231,13 +238,13 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
_out = out;
|
||||
}
|
||||
public void run() {
|
||||
OutputStream to = null;
|
||||
_in = null;
|
||||
long start = System.currentTimeMillis();
|
||||
long written = 0;
|
||||
ByteArray ba = null;
|
||||
try {
|
||||
_in = new InternalGZIPInputStream(_inRaw);
|
||||
byte buf[] = new byte[8192];
|
||||
ba = _cache.acquire();
|
||||
byte buf[] = ba.getData();
|
||||
int read = -1;
|
||||
while ( (read = _in.read(buf)) != -1) {
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -251,6 +258,8 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
} catch (IOException ioe) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Error decompressing: " + written + ", " + (_in != null ? _in.getTotalRead() + "/" + _in.getTotalExpanded() : ""), ioe);
|
||||
} catch (OutOfMemoryError oom) {
|
||||
_log.error("OOM in HTTP Decompressor", oom);
|
||||
} finally {
|
||||
if (_log.shouldLog(Log.WARN) && (_in != null))
|
||||
_log.warn("After decompression, written=" + written +
|
||||
@@ -259,23 +268,26 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
+ ", expanded=" + _in.getTotalExpanded() + ", remaining=" + _in.getRemaining()
|
||||
+ ", finished=" + _in.getFinished()
|
||||
: ""));
|
||||
if (ba != null)
|
||||
_cache.release(ba);
|
||||
if (_out != null) try {
|
||||
_out.close();
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
long end = System.currentTimeMillis();
|
||||
|
||||
double compressed = (_in != null ? _in.getTotalRead() : 0);
|
||||
double expanded = (_in != null ? _in.getTotalExpanded() : 0);
|
||||
double ratio = 0;
|
||||
if (expanded > 0)
|
||||
ratio = compressed/expanded;
|
||||
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), end-start);
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, end-start);
|
||||
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, end-start);
|
||||
if (compressed > 0 && expanded > 0) {
|
||||
// only update the stats if we did something
|
||||
double ratio = compressed/expanded;
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressionRatio", (int)(100d*ratio), 0);
|
||||
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed, 0);
|
||||
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** just a wrapper to provide stats for debugging */
|
||||
private static class InternalGZIPInputStream extends GZIPInputStream {
|
||||
public InternalGZIPInputStream(InputStream in) throws IOException {
|
||||
super(in);
|
||||
@@ -294,6 +306,12 @@ class HTTPResponseOutputStream extends FilterOutputStream {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From Inflater javadoc:
|
||||
* Returns the total number of bytes remaining in the input buffer. This can be used to find out
|
||||
* what bytes still remain in the input buffer after decompression has finished.
|
||||
*/
|
||||
public long getRemaining() {
|
||||
try {
|
||||
return super.inf.getRemaining();
|
||||
|
@@ -8,7 +8,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
@@ -110,7 +109,7 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
int index = _context.random().nextInt(size);
|
||||
return dests.get(index);
|
||||
}
|
||||
}
|
||||
|
@@ -146,7 +146,7 @@ public class I2PTunnelConnectClient extends I2PTunnelClientBase implements Runna
|
||||
int size = _proxyList.size();
|
||||
if (size <= 0)
|
||||
return null;
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
int index = _context.random().nextInt(size);
|
||||
return _proxyList.get(index);
|
||||
}
|
||||
}
|
||||
|
@@ -205,7 +205,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
l.log("Proxy list is empty - no outproxy available");
|
||||
return null;
|
||||
}
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
int index = _context.random().nextInt(size);
|
||||
String proxy = (String)proxyList.get(index);
|
||||
return proxy;
|
||||
}
|
||||
@@ -626,6 +626,14 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
//line = "From: i2p";
|
||||
line = null;
|
||||
continue; // completely strip the line
|
||||
} else if (lowercaseLine.startsWith("authorization: ntlm ")) {
|
||||
// Block Windows NTLM after 401
|
||||
line = null;
|
||||
continue;
|
||||
} else if (lowercaseLine.startsWith("proxy-authorization: ntlm ")) {
|
||||
// Block Windows NTLM after 407
|
||||
line = null;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -808,7 +816,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelClientBase implements Runnable
|
||||
* @return non-null
|
||||
*/
|
||||
private byte[] getErrorPage(String base, byte[] backup) {
|
||||
return getErrorPage(getTunnel().getContext(), base, backup);
|
||||
return getErrorPage(_context, base, backup);
|
||||
}
|
||||
|
||||
private static byte[] getErrorPage(I2PAppContext ctx, String base, byte[] backup) {
|
||||
|
@@ -290,6 +290,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
|
||||
}
|
||||
}
|
||||
|
||||
/** just a wrapper to provide stats for debugging */
|
||||
private static class InternalGZIPOutputStream extends GZIPOutputStream {
|
||||
public InternalGZIPOutputStream(OutputStream target) throws IOException {
|
||||
super(target);
|
||||
|
@@ -9,7 +9,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
@@ -124,7 +123,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
if (size == 1) // skip the rand in the most common case
|
||||
return dests.get(0);
|
||||
int index = I2PAppContext.getGlobalContext().random().nextInt(size);
|
||||
int index = _context.random().nextInt(size);
|
||||
return dests.get(index);
|
||||
}
|
||||
|
||||
@@ -182,6 +181,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
outmsg=outmsg+"\r\n"; // rfc1459 sec. 2.3
|
||||
output.write(outmsg.getBytes("ISO-8859-1"));
|
||||
// probably doesn't do much but can't hurt
|
||||
output.flush();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("inbound BLOCKED: "+inmsg);
|
||||
@@ -257,6 +258,8 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase implements Runnable
|
||||
}
|
||||
outmsg=outmsg+"\r\n"; // rfc1459 sec. 2.3
|
||||
output.write(outmsg.getBytes("ISO-8859-1"));
|
||||
// save 250 ms in streaming
|
||||
output.flush();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("outbound BLOCKED: "+"\""+inmsg+"\"");
|
||||
|
@@ -129,7 +129,14 @@ public class I2PTunnelRunner extends I2PAppThread implements I2PSocket.SocketErr
|
||||
// do NOT flush here, it will block and then onTimeout.run() won't happen on fail.
|
||||
// But if we don't flush, then we have to wait for the connectDelay timer to fire
|
||||
// in i2p socket? To be researched and/or fixed.
|
||||
//i2pout.flush();
|
||||
//
|
||||
// AS OF 0.8.1, MessageOutputStream.flush() is fixed to only wait for accept,
|
||||
// not for "completion" (i.e. an ACK from the far end).
|
||||
// So we now get a fast return from flush(), and can do it here to save 250 ms.
|
||||
// To make sure we are under the initial window size and don't hang waiting for accept,
|
||||
// only flush if it fits in one message.
|
||||
if (initialI2PData.length <= 1730) // ConnectionOptions.DEFAULT_MAX_MESSAGE_SIZE
|
||||
i2pout.flush();
|
||||
}
|
||||
}
|
||||
if (initialSocketData != null) {
|
||||
|
@@ -18,6 +18,7 @@ import net.i2p.data.Base32;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* Coordinate the runtime operation and configuration of a tunnel.
|
||||
@@ -89,7 +90,7 @@ public class TunnelController implements Logging {
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(keyFile);
|
||||
fos = new SecureFileOutputStream(keyFile);
|
||||
Destination dest = client.createDestination(fos);
|
||||
String destStr = dest.toBase64();
|
||||
log("Private key created and saved in " + keyFile.getAbsolutePath());
|
||||
|
@@ -20,6 +20,7 @@ import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
/**
|
||||
* Coordinate a set of tunnels within the JVM, loading and storing their config
|
||||
@@ -255,7 +256,7 @@ public class TunnelControllerGroup {
|
||||
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
fos = new FileOutputStream(cfgFile);
|
||||
fos = new SecureFileOutputStream(cfgFile);
|
||||
fos.write(buf.toString().getBytes("UTF-8"));
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Config written to " + cfgFile.getPath());
|
||||
|
@@ -13,6 +13,11 @@ import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.PrivateKeyFile;
|
||||
import net.i2p.data.Signature;
|
||||
import net.i2p.data.SigningPrivateKey;
|
||||
import net.i2p.i2ptunnel.TunnelController;
|
||||
import net.i2p.i2ptunnel.TunnelControllerGroup;
|
||||
|
||||
@@ -68,6 +73,31 @@ public class EditBean extends IndexBean {
|
||||
return "i2ptunnel" + tunnel + "-privKeys.dat";
|
||||
}
|
||||
|
||||
public String getNameSignature(int tunnel) {
|
||||
String spoof = getSpoofedHost(tunnel);
|
||||
if (spoof.length() <= 0)
|
||||
return "";
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun == null)
|
||||
return "";
|
||||
String keyFile = tun.getPrivKeyFile();
|
||||
if (keyFile != null && keyFile.trim().length() > 0) {
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(keyFile);
|
||||
try {
|
||||
Destination d = pkf.getDestination();
|
||||
if (d == null)
|
||||
return "";
|
||||
SigningPrivateKey privKey = pkf.getSigningPrivKey();
|
||||
if (privKey == null)
|
||||
return "";
|
||||
//System.err.println("Signing " + spoof + " with " + Base64.encode(privKey.getData()));
|
||||
Signature sig = _context.dsa().sign(spoof.getBytes("UTF-8"), privKey);
|
||||
return Base64.encode(sig.getData());
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public boolean startAutomatically(int tunnel) {
|
||||
TunnelController tun = getController(tunnel);
|
||||
if (tun != null)
|
||||
|
@@ -9,6 +9,7 @@ package net.i2p.i2ptunnel.web;
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -41,10 +42,10 @@ public class IndexBean {
|
||||
protected TunnelControllerGroup _group;
|
||||
private String _action;
|
||||
private int _tunnel;
|
||||
private long _prevNonce;
|
||||
private long _prevNonce2;
|
||||
private long _curNonce;
|
||||
private long _nextNonce;
|
||||
//private long _prevNonce;
|
||||
//private long _prevNonce2;
|
||||
private String _curNonce;
|
||||
//private long _nextNonce;
|
||||
|
||||
private String _type;
|
||||
private String _name;
|
||||
@@ -85,10 +86,14 @@ public class IndexBean {
|
||||
/** deprecated unimplemented, now using routerconsole realm */
|
||||
//public static final String PROP_TUNNEL_PASSPHRASE = "i2ptunnel.passphrase";
|
||||
public static final String PROP_TUNNEL_PASSPHRASE = "consolePassword";
|
||||
static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
|
||||
static final String PROP_NONCE_OLD = PROP_NONCE + '2';
|
||||
//static final String PROP_NONCE = IndexBean.class.getName() + ".nonce";
|
||||
//static final String PROP_NONCE_OLD = PROP_NONCE + '2';
|
||||
/** 3 wasn't enough for some browsers. They are reloading the page for some reason - maybe HEAD? @since 0.8.1 */
|
||||
private static final int MAX_NONCES = 5;
|
||||
/** store nonces in a static FIFO instead of in System Properties @since 0.8.1 */
|
||||
private static final List<String> _nonces = new ArrayList(MAX_NONCES + 1);
|
||||
|
||||
static final String CLIENT_NICKNAME = "shared clients";
|
||||
|
||||
public static final String PROP_THEME_NAME = "routerconsole.theme";
|
||||
public static final String DEFAULT_THEME = "light";
|
||||
public static final String PROP_CSS_DISABLED = "routerconsole.css.disabled";
|
||||
@@ -98,34 +103,39 @@ public class IndexBean {
|
||||
_context = I2PAppContext.getGlobalContext();
|
||||
_log = _context.logManager().getLog(IndexBean.class);
|
||||
_group = TunnelControllerGroup.getInstance();
|
||||
_action = null;
|
||||
_tunnel = -1;
|
||||
_curNonce = -1;
|
||||
_prevNonce = -1;
|
||||
_prevNonce2 = -1;
|
||||
try {
|
||||
String nonce2 = System.getProperty(PROP_NONCE_OLD);
|
||||
if (nonce2 != null)
|
||||
_prevNonce2 = Long.parseLong(nonce2);
|
||||
String nonce = System.getProperty(PROP_NONCE);
|
||||
if (nonce != null) {
|
||||
_prevNonce = Long.parseLong(nonce);
|
||||
System.setProperty(PROP_NONCE_OLD, nonce);
|
||||
}
|
||||
} catch (NumberFormatException nfe) {}
|
||||
_nextNonce = _context.random().nextLong();
|
||||
System.setProperty(PROP_NONCE, Long.toString(_nextNonce));
|
||||
_curNonce = "-1";
|
||||
addNonce();
|
||||
_booleanOptions = new ConcurrentHashSet(4);
|
||||
_otherOptions = new ConcurrentHashMap(4);
|
||||
}
|
||||
|
||||
public long getNextNonce() { return _nextNonce; }
|
||||
public static String getNextNonce() {
|
||||
synchronized (_nonces) {
|
||||
return _nonces.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNonce(String nonce) {
|
||||
if ( (nonce == null) || (nonce.trim().length() <= 0) ) return;
|
||||
try {
|
||||
_curNonce = Long.parseLong(nonce);
|
||||
} catch (NumberFormatException nfe) {
|
||||
_curNonce = -1;
|
||||
_curNonce = nonce;
|
||||
}
|
||||
|
||||
/** add a random nonce to the head of the queue @since 0.8.1 */
|
||||
private void addNonce() {
|
||||
String nextNonce = Long.toString(_context.random().nextLong());
|
||||
synchronized (_nonces) {
|
||||
_nonces.add(0, nextNonce);
|
||||
int sz = _nonces.size();
|
||||
if (sz > MAX_NONCES)
|
||||
_nonces.remove(sz - 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** do we know this nonce? @since 0.8.1 */
|
||||
private static boolean haveNonce(String nonce) {
|
||||
synchronized (_nonces) {
|
||||
return _nonces.contains(nonce);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,7 +165,7 @@ public class IndexBean {
|
||||
private String processAction() {
|
||||
if ( (_action == null) || (_action.trim().length() <= 0) || ("Cancel".equals(_action)))
|
||||
return "";
|
||||
if ( (_prevNonce != _curNonce) && (_prevNonce2 != _curNonce) && (!validPassphrase()) )
|
||||
if ( (!haveNonce(_curNonce)) && (!validPassphrase()) )
|
||||
return "Invalid form submission, probably because you used the 'back' or 'reload' button on your browser. Please resubmit.";
|
||||
if ("Stop all".equals(_action))
|
||||
return stopAll();
|
||||
|
@@ -196,7 +196,16 @@
|
||||
<a href="/susidns/addressbook.jsp?book=private&hostname=<%=editBean.getTunnelName(curTunnel)%>&destination=<%=editBean.getDestinationBase64(curTunnel)%>#add"><%=intl._("Add to local addressbook")%></a>
|
||||
<% } %>
|
||||
</div>
|
||||
|
||||
|
||||
<% if (("httpserver".equals(tunnelType)) || ("httpbidirserver".equals(tunnelType))) {
|
||||
%><div id="sigField" class="rowItem">
|
||||
<label for="signature">
|
||||
<%=intl._("Hostname Signature")%>
|
||||
</label>
|
||||
<input type="text" size="30" readonly="readonly" title="Use to prove that the website name is for this destination" value="<%=editBean.getNameSignature(curTunnel)%>" wrap="off" class="freetext" />
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="footer">
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -130,16 +130,19 @@ public class I2PSocketManagerFactory {
|
||||
if (!opts.containsKey(name))
|
||||
opts.setProperty(name, System.getProperty(name));
|
||||
}
|
||||
boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
|
||||
if (oldLib && false) {
|
||||
//boolean oldLib = DEFAULT_MANAGER.equals(opts.getProperty(PROP_MANAGER, DEFAULT_MANAGER));
|
||||
//if (oldLib && false) {
|
||||
// for the old streaming lib
|
||||
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
|
||||
// opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_GUARANTEED);
|
||||
//opts.setProperty("tunnels.depthInbound", "0");
|
||||
} else {
|
||||
//} else {
|
||||
// for new streaming lib:
|
||||
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
|
||||
//opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_BEST_EFFORT);
|
||||
// as of 0.8.1 (I2CP default is BestEffort)
|
||||
if (!opts.containsKey(I2PClient.PROP_RELIABILITY))
|
||||
opts.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
|
||||
//p.setProperty("tunnels.depthInbound", "0");
|
||||
}
|
||||
//}
|
||||
|
||||
if (i2cpHost != null)
|
||||
opts.setProperty(I2PClient.PROP_TCP_HOST, i2cpHost);
|
||||
|
@@ -113,7 +113,7 @@
|
||||
<target name="war" depends="precompilejsp">
|
||||
<!-- Don't include the css in the war, the main build.xml will copy it to docs/themes/console/ -->
|
||||
<war destfile="build/routerconsole.war" webxml="../jsp/web-out.xml"
|
||||
basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, *.jsi, web-fragment.xml">
|
||||
basedir="../jsp/" excludes="web.xml, *.css, **/*.java, *.jsp, *.jsi, web-fragment.xml, web-out.xml">
|
||||
</war>
|
||||
</target>
|
||||
<target name="precompilejsp" unless="precompilejsp.uptodate">
|
||||
|
@@ -17,6 +17,8 @@ public class ConfigLoggingHandler extends FormHandler {
|
||||
private String _recordFormat;
|
||||
private String _dateFormat;
|
||||
private String _fileSize;
|
||||
private String _newLogClass;
|
||||
private String _newLogLevel = "WARN";
|
||||
|
||||
@Override
|
||||
protected void processForm() {
|
||||
@@ -26,7 +28,7 @@ public class ConfigLoggingHandler extends FormHandler {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setShouldsave(String moo) { _shouldSave = true; }
|
||||
|
||||
public void setLevels(String levels) {
|
||||
@@ -47,6 +49,18 @@ public class ConfigLoggingHandler extends FormHandler {
|
||||
public void setLogfilesize(String size) {
|
||||
_fileSize = (size != null ? size.trim() : null);
|
||||
}
|
||||
|
||||
/** @since 0.8.1 */
|
||||
public void setNewlogclass(String s) {
|
||||
if (s != null && s.length() > 0)
|
||||
_newLogClass = s;
|
||||
}
|
||||
|
||||
/** @since 0.8.1 */
|
||||
public void setNewloglevel(String s) {
|
||||
if (s != null)
|
||||
_newLogLevel = s;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user made changes to the config and wants to save them, so
|
||||
@@ -56,14 +70,18 @@ public class ConfigLoggingHandler extends FormHandler {
|
||||
private void saveChanges() {
|
||||
boolean shouldSave = false;
|
||||
|
||||
if (_levels != null) {
|
||||
if (_levels != null || _newLogClass != null) {
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
props.load(new ByteArrayInputStream(_levels.getBytes()));
|
||||
if (_levels != null)
|
||||
props.load(new ByteArrayInputStream(_levels.getBytes()));
|
||||
if (_newLogClass != null)
|
||||
props.setProperty(_newLogClass, _newLogLevel);
|
||||
_context.logManager().setLimits(props);
|
||||
shouldSave = true;
|
||||
addFormNotice("Log limits updated");
|
||||
addFormNotice(_("Log overrides updated"));
|
||||
} catch (IOException ioe) {
|
||||
// shouldn't ever happen (BAIS shouldnt cause an IOE)
|
||||
_context.logManager().getLog(ConfigLoggingHandler.class).error("Error reading from the props?", ioe);
|
||||
addFormError("Error updating the log limits - levels not valid");
|
||||
}
|
||||
@@ -83,7 +101,7 @@ public class ConfigLoggingHandler extends FormHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (_dateFormat != null) {
|
||||
if (_dateFormat != null && !_dateFormat.equals(_context.logManager().getDateFormatPattern())) {
|
||||
boolean valid = _context.logManager().setDateFormat(_dateFormat);
|
||||
if (valid) {
|
||||
shouldSave = true;
|
||||
@@ -139,7 +157,7 @@ public class ConfigLoggingHandler extends FormHandler {
|
||||
boolean saved = _context.logManager().saveConfig();
|
||||
|
||||
if (saved)
|
||||
addFormNotice("Log configuration saved and applied successfully");
|
||||
addFormNotice(_("Log configuration saved"));
|
||||
else
|
||||
addFormNotice("Error saving the configuration (applied but not saved) - please see the error logs");
|
||||
}
|
||||
|
@@ -1,9 +1,13 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class ConfigLoggingHelper extends HelperBase {
|
||||
public ConfigLoggingHelper() {}
|
||||
@@ -19,18 +23,13 @@ public class ConfigLoggingHelper extends HelperBase {
|
||||
}
|
||||
public String getMaxFileSize() {
|
||||
int bytes = _context.logManager().getFileSize();
|
||||
if (bytes == 0) return "1m";
|
||||
if (bytes > 1024*1024*1024)
|
||||
return (bytes/(1024*1024*1024)) + "g";
|
||||
else if (bytes > 1024*1024)
|
||||
return (bytes/(1024*1024)) + "m";
|
||||
else
|
||||
return (bytes/(1024)) + "k";
|
||||
if (bytes <= 0) return "1.00 MB";
|
||||
return DataHelper.formatSize2(bytes) + 'B';
|
||||
}
|
||||
public String getLogLevelTable() {
|
||||
StringBuilder buf = new StringBuilder(32*1024);
|
||||
Properties limits = _context.logManager().getLimits();
|
||||
TreeSet sortedLogs = new TreeSet();
|
||||
TreeSet<String> sortedLogs = new TreeSet();
|
||||
for (Iterator iter = limits.keySet().iterator(); iter.hasNext(); ) {
|
||||
String prefix = (String)iter.next();
|
||||
sortedLogs.add(prefix);
|
||||
@@ -46,6 +45,20 @@ public class ConfigLoggingHelper extends HelperBase {
|
||||
buf.append("<i>" + _("Add additional logging statements above. Example: net.i2p.router.tunnel=WARN") + "</i><br>");
|
||||
buf.append("<i>" + _("Or put entries in the logger.config file. Example: logger.record.net.i2p.router.tunnel=WARN") + "</i><br>");
|
||||
buf.append("<i>" + _("Valid levels are DEBUG, INFO, WARN, ERROR, CRIT") + "</i>\n");
|
||||
|
||||
/****
|
||||
// this is too big and ugly
|
||||
if (limits.size() <= 0)
|
||||
return "";
|
||||
buf.append("<table>");
|
||||
for (String prefix : sortedLogs) {
|
||||
buf.append("<tr><td>").append(prefix).append("</td><td>");
|
||||
String level = limits.getProperty(prefix);
|
||||
buf.append(getLogLevelBox("level-" + prefix, level, true)).append("</td></tr>");
|
||||
}
|
||||
buf.append("</table>");
|
||||
****/
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@@ -53,18 +66,69 @@ public class ConfigLoggingHelper extends HelperBase {
|
||||
|
||||
public String getDefaultLogLevelBox() {
|
||||
String cur = _context.logManager().getDefaultLimit();
|
||||
return getLogLevelBox("defaultloglevel", cur, false);
|
||||
}
|
||||
|
||||
private String getLogLevelBox(String name, String cur, boolean showRemove) {
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
buf.append("<select name=\"defaultloglevel\">\n");
|
||||
buf.append("<select name=\"").append(name).append("\">\n");
|
||||
|
||||
for (int i = 0; i < levels.length; i++) {
|
||||
String l = levels[i];
|
||||
buf.append("<option value=\"").append(l).append("\" ");
|
||||
buf.append("<option value=\"").append(l).append('\"');
|
||||
if (l.equals(cur))
|
||||
buf.append(" selected=\"true\" ");
|
||||
buf.append(" selected=\"true\"");
|
||||
buf.append('>').append(_(l)).append("</option>\n");
|
||||
}
|
||||
|
||||
if (showRemove)
|
||||
buf.append("<option value=\"remove\">").append(_("Remove")).append("</option>");
|
||||
buf.append("</select>\n");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* All the classes the log manager knows about, except ones that
|
||||
* already have overrides
|
||||
* @since 0.8.1
|
||||
*/
|
||||
public String getNewClassBox() {
|
||||
List<Log> logs = _context.logManager().getLogs();
|
||||
Set limits = _context.logManager().getLimits().keySet();
|
||||
TreeSet<String> sortedLogs = new TreeSet();
|
||||
|
||||
for (Log log : logs) {
|
||||
String name = log.getName();
|
||||
if (!limits.contains(name))
|
||||
sortedLogs.add(name);
|
||||
|
||||
// add higher classes of length 3 or more
|
||||
int dots = 0;
|
||||
int lastdot = -1;
|
||||
int nextdot = 0;
|
||||
while ((nextdot = name.indexOf('.', lastdot + 1)) > 0) {
|
||||
if (++dots >= 3) {
|
||||
String subst = name.substring(0, nextdot);
|
||||
if (!limits.contains(subst))
|
||||
sortedLogs.add(subst);
|
||||
}
|
||||
lastdot = nextdot;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder(65536);
|
||||
buf.append("<select name=\"newlogclass\">\n" +
|
||||
"<option value=\"\" selected=\"true\">")
|
||||
.append(_("Select a class to add"))
|
||||
.append("</option>\n");
|
||||
|
||||
for (String l : sortedLogs) {
|
||||
buf.append("<option value=\"").append(l).append("\">")
|
||||
.append(l).append("</option>\n");
|
||||
}
|
||||
|
||||
buf.append("</select>\n");
|
||||
buf.append(getLogLevelBox("newloglevel", "WARN", false));
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
||||
|
@@ -44,13 +44,16 @@ public class LogsHelper extends HelperBase {
|
||||
}
|
||||
******/
|
||||
|
||||
private String formatMessages(List msgs) {
|
||||
/** formats in reverse order */
|
||||
private String formatMessages(List<String> msgs) {
|
||||
if (msgs.isEmpty())
|
||||
return "<p><i>" + _("No log messages") + "</i></p>";
|
||||
boolean colorize = Boolean.valueOf(_context.getProperty("routerconsole.logs.color")).booleanValue();
|
||||
StringBuilder buf = new StringBuilder(16*1024);
|
||||
buf.append("<ul>");
|
||||
buf.append("<code>\n");
|
||||
for (int i = msgs.size(); i > 0; i--) {
|
||||
String msg = (String)msgs.get(i - 1);
|
||||
String msg = msgs.get(i - 1);
|
||||
buf.append("<li>");
|
||||
if (colorize) {
|
||||
String color;
|
||||
|
@@ -19,6 +19,8 @@ import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
@@ -34,6 +36,7 @@ import net.i2p.router.TunnelPoolSettings;
|
||||
import net.i2p.router.networkdb.kademlia.HashDistance; // debug
|
||||
import net.i2p.util.HexDump; // debug
|
||||
import net.i2p.util.ObjectCounter;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
import net.i2p.util.VersionComparator;
|
||||
|
||||
public class NetDbRenderer {
|
||||
@@ -371,9 +374,11 @@ public class NetDbRenderer {
|
||||
int cost = addr.getCost();
|
||||
if (!((style.equals("SSU") && cost == 5) || (style.equals("NTCP") && cost == 10)))
|
||||
buf.append('[').append(_("cost")).append('=').append("" + cost).append("] ");
|
||||
for (Iterator optIter = addr.getOptions().keySet().iterator(); optIter.hasNext(); ) {
|
||||
String name = (String)optIter.next();
|
||||
String val = addr.getOptions().getProperty(name);
|
||||
Properties p = new OrderedProperties();
|
||||
p.putAll(addr.getOptions());
|
||||
for (Map.Entry e : p.entrySet()) {
|
||||
String name = (String) e.getKey();
|
||||
String val = (String) e.getValue();
|
||||
buf.append('[').append(_(DataHelper.stripHTML(name))).append('=').append(DataHelper.stripHTML(val)).append("] ");
|
||||
}
|
||||
}
|
||||
|
@@ -563,7 +563,7 @@ public class PluginStarter implements Runnable {
|
||||
/**
|
||||
* http://jimlife.wordpress.com/2007/12/19/java-adding-new-classpath-at-runtime/
|
||||
*/
|
||||
public static void addPath(URL u) throws Exception {
|
||||
private static void addPath(URL u) throws Exception {
|
||||
URLClassLoader urlClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
|
||||
Class urlClass = URLClassLoader.class;
|
||||
Method method = urlClass.getDeclaredMethod("addURL", new Class[]{URL.class});
|
||||
|
@@ -16,6 +16,7 @@ import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.OrderedProperties;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.VersionComparator;
|
||||
@@ -150,7 +151,7 @@ public class PluginUpdateHandler extends UpdateHandler {
|
||||
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {
|
||||
updateStatus("<b>" + _("Plugin downloaded") + "</b>");
|
||||
File f = new File(_updateFile);
|
||||
File appDir = new File(_context.getConfigDir(), PLUGIN_DIR);
|
||||
File appDir = new SecureDirectory(_context.getConfigDir(), PLUGIN_DIR);
|
||||
if ((!appDir.exists()) && (!appDir.mkdir())) {
|
||||
f.delete();
|
||||
statusDone("<b>" + _("Cannot create plugin directory {0}", appDir.getAbsolutePath()) + "</b>");
|
||||
@@ -273,7 +274,7 @@ public class PluginUpdateHandler extends UpdateHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
File destDir = new File(appDir, appName);
|
||||
File destDir = new SecureDirectory(appDir, appName);
|
||||
if (destDir.exists()) {
|
||||
if (Boolean.valueOf(props.getProperty("install-only")).booleanValue()) {
|
||||
to.delete();
|
||||
|
@@ -14,6 +14,7 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
|
||||
import org.mortbay.http.DigestAuthenticator;
|
||||
import org.mortbay.http.HashUserRealm;
|
||||
@@ -62,7 +63,7 @@ public class RouterConsoleRunner {
|
||||
}
|
||||
|
||||
public void startConsole() {
|
||||
File workDir = new File(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work");
|
||||
File workDir = new SecureDirectory(I2PAppContext.getGlobalContext().getTempDir(), "jetty-work");
|
||||
boolean workDirRemoved = FileUtil.rmdir(workDir, false);
|
||||
if (!workDirRemoved)
|
||||
System.err.println("ERROR: Unable to remove Jetty temporary work directory");
|
||||
@@ -115,7 +116,7 @@ public class RouterConsoleRunner {
|
||||
}
|
||||
_server.setRootWebApp(ROUTERCONSOLE);
|
||||
WebApplicationContext wac = _server.addWebApplication("/", _webAppsDir + ROUTERCONSOLE + ".war");
|
||||
File tmpdir = new File(workDir, ROUTERCONSOLE + "-" + _listenPort);
|
||||
File tmpdir = new SecureDirectory(workDir, ROUTERCONSOLE + "-" + _listenPort);
|
||||
tmpdir.mkdir();
|
||||
wac.setTempDirectory(tmpdir);
|
||||
baseHandler = new LocaleWebAppHandler(I2PAppContext.getGlobalContext());
|
||||
@@ -130,7 +131,7 @@ public class RouterConsoleRunner {
|
||||
String enabled = props.getProperty(PREFIX + appName + ENABLED);
|
||||
if (! "false".equals(enabled)) {
|
||||
String path = new File(dir, fileNames[i]).getCanonicalPath();
|
||||
tmpdir = new File(workDir, appName + "-" + _listenPort);
|
||||
tmpdir = new SecureDirectory(workDir, appName + "-" + _listenPort);
|
||||
WebAppStarter.addWebApp(I2PAppContext.getGlobalContext(), _server, appName, path, tmpdir);
|
||||
|
||||
if (enabled == null) {
|
||||
|
@@ -10,6 +10,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
|
||||
import org.mortbay.http.HttpContext;
|
||||
import org.mortbay.http.HttpListener;
|
||||
@@ -41,7 +42,7 @@ public class WebAppStarter {
|
||||
* @throws just about anything, caller would be wise to catch Throwable
|
||||
*/
|
||||
static void startWebApp(I2PAppContext ctx, Server server, String appName, String warPath) throws Exception {
|
||||
File tmpdir = new File(ctx.getTempDir(), "jetty-work-" + appName + ctx.random().nextInt());
|
||||
File tmpdir = new SecureDirectory(ctx.getTempDir(), "jetty-work-" + appName + ctx.random().nextInt());
|
||||
WebApplicationContext wac = addWebApp(ctx, server, appName, warPath, tmpdir);
|
||||
wac.start();
|
||||
}
|
||||
@@ -73,7 +74,7 @@ public class WebAppStarter {
|
||||
warModTimes.put(warPath, new Long(newmod));
|
||||
} else if (oldmod.longValue() < newmod) {
|
||||
// copy war to temporary directory
|
||||
File warTmpDir = new File(ctx.getTempDir(), "war-copy-" + appName + ctx.random().nextInt());
|
||||
File warTmpDir = new SecureDirectory(ctx.getTempDir(), "war-copy-" + appName + ctx.random().nextInt());
|
||||
warTmpDir.mkdir();
|
||||
String tmpPath = (new File(warTmpDir, appName + ".war")).getAbsolutePath();
|
||||
if (!FileUtil.copy(warPath, tmpPath, true))
|
||||
|
@@ -46,6 +46,8 @@
|
||||
</i></td>
|
||||
</tr><tr><td class="mediumtags" align="right"><b><%=intl._("Log level overrides")%>:</b></td>
|
||||
<td><jsp:getProperty name="logginghelper" property="logLevelTable" /></td>
|
||||
</tr><tr><td class="mediumtags" align="right"><b><%=intl._("New override")%>:</b></td>
|
||||
<td><jsp:getProperty name="logginghelper" property="newClassBox" /></td>
|
||||
</tr><tr><td colspan="2"><hr></td>
|
||||
</tr><tr class="tablefooter"><td colspan="2"> <div class="formaction">
|
||||
<input type="reset" value="<%=intl._("Cancel")%>" >
|
||||
|
@@ -148,16 +148,21 @@ public class Connection {
|
||||
}
|
||||
|
||||
/**
|
||||
* This doesn't "send a choke". Rather, it blocks if the outbound window is full,
|
||||
* thus choking the sender that calls this.
|
||||
*
|
||||
* Block until there is an open outbound packet slot or the write timeout
|
||||
* expires.
|
||||
* PacketLocal is the only caller, generally with -1.
|
||||
*
|
||||
* @param timeoutMs PacketLocal is the only caller, often with -1??????
|
||||
* @return true if the packet should be sent
|
||||
* @param timeoutMs 0 or negative means wait forever, 5 minutes max
|
||||
* @return true if the packet should be sent, false for a fatal error
|
||||
* will return false after 5 minutes even if timeoutMs is <= 0.
|
||||
*/
|
||||
boolean packetSendChoke(long timeoutMs) {
|
||||
// if (false) return true; // <--- what the fuck??
|
||||
long start = _context.clock().now();
|
||||
long writeExpire = start + timeoutMs;
|
||||
long writeExpire = start + timeoutMs; // only used if timeoutMs > 0
|
||||
boolean started = false;
|
||||
while (true) {
|
||||
long timeLeft = writeExpire - _context.clock().now();
|
||||
|
@@ -646,6 +646,7 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
return Boolean.valueOf(val).booleanValue();
|
||||
}
|
||||
|
||||
/****
|
||||
public static void main(String args[]) {
|
||||
Properties p = new Properties();
|
||||
|
||||
@@ -656,4 +657,5 @@ public class ConnectionOptions extends I2PSocketOptionsImpl {
|
||||
c = new ConnectionOptions(new I2PSocketOptionsImpl(p));
|
||||
System.out.println("opts: " + c);
|
||||
}
|
||||
****/
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ import java.util.Map;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.ByteArray;
|
||||
import net.i2p.util.ByteCache;
|
||||
//import net.i2p.util.ByteCache;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
@@ -20,8 +20,8 @@ import net.i2p.util.Log;
|
||||
*
|
||||
*/
|
||||
public class MessageInputStream extends InputStream {
|
||||
private I2PAppContext _context;
|
||||
private Log _log;
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
/**
|
||||
* List of ByteArray objects of data ready to be read,
|
||||
* with the first ByteArray at index 0, and the next
|
||||
@@ -29,7 +29,7 @@ public class MessageInputStream extends InputStream {
|
||||
* that array.
|
||||
*
|
||||
*/
|
||||
private List _readyDataBlocks;
|
||||
private final List<ByteArray> _readyDataBlocks;
|
||||
private int _readyDataBlockIndex;
|
||||
/** highest message ID used in the readyDataBlocks */
|
||||
private volatile long _highestReadyBlockId;
|
||||
@@ -40,7 +40,7 @@ public class MessageInputStream extends InputStream {
|
||||
* out of order when there are lower IDs not yet
|
||||
* received
|
||||
*/
|
||||
private Map _notYetReadyBlocks;
|
||||
private final Map<Long, ByteArray> _notYetReadyBlocks;
|
||||
/**
|
||||
* if we have received a flag saying there won't be later messages, EOF
|
||||
* after we have cleared what we have received.
|
||||
@@ -51,9 +51,9 @@ public class MessageInputStream extends InputStream {
|
||||
private int _readTimeout;
|
||||
private IOException _streamError;
|
||||
private long _readTotal;
|
||||
private ByteCache _cache;
|
||||
//private ByteCache _cache;
|
||||
|
||||
private byte[] _oneByte = new byte[1];
|
||||
private final byte[] _oneByte = new byte[1];
|
||||
|
||||
private final Object _dataLock;
|
||||
|
||||
@@ -61,16 +61,12 @@ public class MessageInputStream extends InputStream {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(MessageInputStream.class);
|
||||
_readyDataBlocks = new ArrayList(4);
|
||||
_readyDataBlockIndex = 0;
|
||||
_highestReadyBlockId = -1;
|
||||
_highestBlockId = -1;
|
||||
_readTimeout = -1;
|
||||
_readTotal = 0;
|
||||
_notYetReadyBlocks = new HashMap(4);
|
||||
_dataLock = new Object();
|
||||
_closeReceived = false;
|
||||
_locallyClosed = false;
|
||||
_cache = ByteCache.getInstance(128, Packet.MAX_PAYLOAD_SIZE);
|
||||
//_cache = ByteCache.getInstance(128, Packet.MAX_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
/** What is the highest block ID we've completely received through?
|
||||
@@ -140,10 +136,8 @@ public class MessageInputStream extends InputStream {
|
||||
if (num <= 0) return null;
|
||||
blocks = new long[num];
|
||||
int i = 0;
|
||||
for (Iterator iter = _notYetReadyBlocks.keySet().iterator(); iter.hasNext(); ) {
|
||||
Long id = (Long)iter.next();
|
||||
blocks[i] = id.longValue();
|
||||
i++;
|
||||
for (Long id : _notYetReadyBlocks.keySet()) {
|
||||
blocks[i++] = id.longValue();
|
||||
}
|
||||
}
|
||||
Arrays.sort(blocks);
|
||||
@@ -178,16 +172,15 @@ public class MessageInputStream extends InputStream {
|
||||
buf.append("Close received, ready bytes: ");
|
||||
long available = 0;
|
||||
for (int i = 0; i < _readyDataBlocks.size(); i++)
|
||||
available += ((ByteArray)_readyDataBlocks.get(i)).getValid();
|
||||
available += _readyDataBlocks.get(i).getValid();
|
||||
available -= _readyDataBlockIndex;
|
||||
buf.append(available);
|
||||
buf.append(" blocks: ").append(_readyDataBlocks.size());
|
||||
|
||||
buf.append(" not ready blocks: ");
|
||||
long notAvailable = 0;
|
||||
for (Iterator iter = _notYetReadyBlocks.keySet().iterator(); iter.hasNext(); ) {
|
||||
Long id = (Long)iter.next();
|
||||
ByteArray ba = (ByteArray)_notYetReadyBlocks.get(id);
|
||||
for (Long id : _notYetReadyBlocks.keySet()) {
|
||||
ByteArray ba = _notYetReadyBlocks.get(id);
|
||||
buf.append(id).append(" ");
|
||||
|
||||
if (ba != null)
|
||||
@@ -237,7 +230,7 @@ public class MessageInputStream extends InputStream {
|
||||
long cur = _highestReadyBlockId + 1;
|
||||
// now pull in any previously pending blocks
|
||||
while (_notYetReadyBlocks.containsKey(new Long(cur))) {
|
||||
ByteArray ba = (ByteArray)_notYetReadyBlocks.remove(new Long(cur));
|
||||
ByteArray ba = _notYetReadyBlocks.remove(new Long(cur));
|
||||
if ( (ba != null) && (ba.getData() != null) && (ba.getValid() > 0) ) {
|
||||
_readyDataBlocks.add(ba);
|
||||
}
|
||||
@@ -341,7 +334,7 @@ public class MessageInputStream extends InputStream {
|
||||
return i;
|
||||
} else {
|
||||
// either was already ready, or we wait()ed and it arrived
|
||||
ByteArray cur = (ByteArray)_readyDataBlocks.get(0);
|
||||
ByteArray cur = _readyDataBlocks.get(0);
|
||||
byte rv = cur.getData()[cur.getOffset()+_readyDataBlockIndex];
|
||||
_readyDataBlockIndex++;
|
||||
boolean removed = false;
|
||||
@@ -378,7 +371,7 @@ public class MessageInputStream extends InputStream {
|
||||
int numBytes = 0;
|
||||
synchronized (_dataLock) {
|
||||
for (int i = 0; i < _readyDataBlocks.size(); i++) {
|
||||
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
|
||||
ByteArray cur = _readyDataBlocks.get(i);
|
||||
if (i == 0)
|
||||
numBytes += cur.getValid() - _readyDataBlockIndex;
|
||||
else
|
||||
@@ -402,14 +395,13 @@ public class MessageInputStream extends InputStream {
|
||||
if (_locallyClosed) return 0;
|
||||
int numBytes = 0;
|
||||
for (int i = 0; i < _readyDataBlocks.size(); i++) {
|
||||
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
|
||||
ByteArray cur = _readyDataBlocks.get(i);
|
||||
if (i == 0)
|
||||
numBytes += cur.getValid() - _readyDataBlockIndex;
|
||||
else
|
||||
numBytes += cur.getValid();
|
||||
}
|
||||
for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) {
|
||||
ByteArray cur = (ByteArray)iter.next();
|
||||
for (ByteArray cur : _notYetReadyBlocks.values()) {
|
||||
numBytes += cur.getValid();
|
||||
}
|
||||
return numBytes;
|
||||
@@ -421,7 +413,7 @@ public class MessageInputStream extends InputStream {
|
||||
if (_locallyClosed) return 0;
|
||||
int numBytes = 0;
|
||||
for (int i = 0; i < _readyDataBlocks.size(); i++) {
|
||||
ByteArray cur = (ByteArray)_readyDataBlocks.get(i);
|
||||
ByteArray cur = _readyDataBlocks.get(i);
|
||||
if (i == 0)
|
||||
numBytes += cur.getValid() - _readyDataBlockIndex;
|
||||
else
|
||||
@@ -440,8 +432,7 @@ public class MessageInputStream extends InputStream {
|
||||
|
||||
// we don't need the data, but we do need to keep track of the messageIds
|
||||
// received, so we can ACK accordingly
|
||||
for (Iterator iter = _notYetReadyBlocks.values().iterator(); iter.hasNext(); ) {
|
||||
ByteArray ba = (ByteArray)iter.next();
|
||||
for (ByteArray ba : _notYetReadyBlocks.values()) {
|
||||
ba.setData(null);
|
||||
//_cache.release(ba);
|
||||
}
|
||||
|
@@ -43,6 +43,10 @@ public class MessageOutputStream extends OutputStream {
|
||||
private long _sendPeriodBytes;
|
||||
private int _sendBps;
|
||||
|
||||
/**
|
||||
* Since this is less than i2ptunnel's i2p.streaming.connectDelay default of 1000,
|
||||
* we only wait 250 at the start. Guess that's ok, 1000 is too long anyway.
|
||||
*/
|
||||
private static final int DEFAULT_PASSIVE_FLUSH_DELAY = 250;
|
||||
|
||||
public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver) {
|
||||
@@ -273,8 +277,18 @@ public class MessageOutputStream extends OutputStream {
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush the data already queued up, blocking until it has been
|
||||
* delivered.
|
||||
* Flush the data already queued up, blocking only if the outbound
|
||||
* window is full.
|
||||
*
|
||||
* Prior to 0.8.1, this blocked until "delivered".
|
||||
* "Delivered" meant "received an ACK from the far end",
|
||||
* which is not the commom implementation of flush(), and really hurt the
|
||||
* performance of i2psnark, which flush()ed frequently.
|
||||
* Calling flush() would cause a complete window stall.
|
||||
*
|
||||
* As of 0.8.1, only wait for accept into the streaming output queue.
|
||||
* This will speed up snark significantly, and allow us to flush()
|
||||
* the initial data in I2PTunnelRunner, saving 250 ms.
|
||||
*
|
||||
* @throws IOException if the write fails
|
||||
*/
|
||||
@@ -283,6 +297,14 @@ public class MessageOutputStream extends OutputStream {
|
||||
/* @throws InterruptedIOException if the write times out
|
||||
* Documented here, but doesn't belong in the javadoc.
|
||||
*/
|
||||
flush(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param wait_for_accept_only see discussion in close() code
|
||||
* @@since 0.8.1
|
||||
*/
|
||||
private void flush(boolean wait_for_accept_only) throws IOException {
|
||||
long begin = _context.clock().now();
|
||||
WriteStatus ws = null;
|
||||
if (_log.shouldLog(Log.INFO) && _valid > 0)
|
||||
@@ -297,14 +319,28 @@ public class MessageOutputStream extends OutputStream {
|
||||
throwAnyError();
|
||||
return;
|
||||
}
|
||||
ws = _dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
locked_updateBufferSize();
|
||||
_lastFlushed = _context.clock().now();
|
||||
_dataLock.notifyAll();
|
||||
// if valid == 0 return ??? - no, this could flush a CLOSE packet too.
|
||||
|
||||
// Yes, flush here, inside the data lock, and do all the waitForCompletion() stuff below
|
||||
// (disabled)
|
||||
if (!wait_for_accept_only) {
|
||||
ws = _dataReceiver.writeData(_buf, 0, _valid);
|
||||
_written += _valid;
|
||||
_valid = 0;
|
||||
locked_updateBufferSize();
|
||||
_lastFlushed = _context.clock().now();
|
||||
_dataLock.notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
// Skip all the waitForCompletion() stuff below, which is insanity, as of 0.8.1
|
||||
// must do this outside the data lock
|
||||
if (wait_for_accept_only) {
|
||||
flushAvailable(_dataReceiver, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait a loooooong time, until we have the ACK
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("before waiting " + _writeTimeout + "ms for completion of " + ws);
|
||||
if (_closed &&
|
||||
@@ -328,14 +364,28 @@ public class MessageOutputStream extends OutputStream {
|
||||
throwAnyError();
|
||||
}
|
||||
|
||||
/**
|
||||
* This does a flush, and BLOCKS until
|
||||
* the CLOSE packet is acked.
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (_closed) {
|
||||
synchronized (_dataLock) { _dataLock.notifyAll(); }
|
||||
return;
|
||||
}
|
||||
// setting _closed before flush() will force flush() to send a CLOSE packet
|
||||
_closed = true;
|
||||
flush();
|
||||
|
||||
// In 0.8.1 we rewrote flush() to only wait for accept into the window,
|
||||
// not "completion" (i.e. ack from the far end).
|
||||
// Unfortunately, that broke close(), at least in i2ptunnel HTTPClient.
|
||||
// Symptom was premature close, i.e. incomplete pages and images.
|
||||
// Possible cause - I2PTunnelRunner code? or the code here that follows flush()?
|
||||
// It seems like we shouldn't have to wait for the far-end ACK for a close packet,
|
||||
// should we? To be researched further.
|
||||
// false -> wait for completion, not just accept.
|
||||
flush(false);
|
||||
_log.debug("Output stream closed after writing " + _written);
|
||||
ByteArray ba = null;
|
||||
synchronized (_dataLock) {
|
||||
@@ -351,7 +401,11 @@ public class MessageOutputStream extends OutputStream {
|
||||
_dataCache.release(ba);
|
||||
}
|
||||
}
|
||||
/** nonblocking close */
|
||||
|
||||
/**
|
||||
* nonblocking close -
|
||||
* Use outside of this package is deprecated, should be made package local
|
||||
*/
|
||||
public void closeInternal() {
|
||||
_closed = true;
|
||||
if (_streamError == null)
|
||||
@@ -412,6 +466,8 @@ public class MessageOutputStream extends OutputStream {
|
||||
if (_log.shouldLog(Log.INFO) && _valid > 0)
|
||||
_log.info("flushAvailable() valid = " + _valid);
|
||||
synchronized (_dataLock) {
|
||||
// if valid == 0 return ??? - no, this could flush a CLOSE packet too.
|
||||
|
||||
// _buf may be null, but the data receiver can handle that just fine,
|
||||
// deciding whether or not to send a packet
|
||||
ws = target.writeData(_buf, 0, _valid);
|
||||
@@ -457,14 +513,21 @@ public class MessageOutputStream extends OutputStream {
|
||||
|
||||
/** Define a way to detect the status of a write */
|
||||
public interface WriteStatus {
|
||||
/** wait until the data written either fails or succeeds */
|
||||
/**
|
||||
* Wait until the data written either fails or succeeds.
|
||||
* Success means an ACK FROM THE FAR END.
|
||||
* @param maxWaitMs -1 = forever
|
||||
*/
|
||||
public void waitForCompletion(int maxWaitMs);
|
||||
|
||||
/**
|
||||
* wait until the data written is accepted into the outbound pool,
|
||||
* Wait until the data written is accepted into the outbound pool,
|
||||
* (i.e. the outbound window is not full)
|
||||
* which we throttle rather than accept arbitrary data and queue
|
||||
* @param maxWaitMs -1 = forever ?
|
||||
* @param maxWaitMs -1 = forever
|
||||
*/
|
||||
public void waitForAccept(int maxWaitMs);
|
||||
|
||||
/** the write was accepted. aka did the socket not close? */
|
||||
public boolean writeAccepted();
|
||||
/** did the write fail? */
|
||||
|
@@ -194,7 +194,8 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
}
|
||||
|
||||
/**
|
||||
* @param maxWaitMs MessageOutputStream is the only caller, often with -1 ??????
|
||||
* Blocks until outbound window is not full. See Connection.packetSendChoke().
|
||||
* @param maxWaitMs MessageOutputStream is the only caller, generally with -1
|
||||
*/
|
||||
public void waitForAccept(int maxWaitMs) {
|
||||
if (_connection == null)
|
||||
@@ -220,6 +221,7 @@ public class PacketLocal extends Packet implements MessageOutputStream.WriteStat
|
||||
+ toString());
|
||||
}
|
||||
|
||||
/** block until the packet is acked from the far end */
|
||||
public void waitForCompletion(int maxWaitMs) {
|
||||
long expiration = _context.clock().now()+maxWaitMs;
|
||||
while (true) {
|
||||
|
@@ -72,7 +72,6 @@
|
||||
<include name="images/*.png"/>
|
||||
<include name="css.css"/>
|
||||
<include name="index.html"/>
|
||||
<include name="WEB-INF/web-out.xml"/>
|
||||
<include name="WEB-INF/classes/${project}.properties"/>
|
||||
</fileset>
|
||||
</war>
|
||||
|
@@ -38,6 +38,7 @@ import java.util.Properties;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
public class AddressbookBean
|
||||
{
|
||||
@@ -330,7 +331,7 @@ public class AddressbookBean
|
||||
{
|
||||
String filename = properties.getProperty( getBook() + "_addressbook" );
|
||||
|
||||
FileOutputStream fos = new FileOutputStream( ConfigBean.addressbookPrefix + filename );
|
||||
FileOutputStream fos = new SecureFileOutputStream( ConfigBean.addressbookPrefix + filename );
|
||||
addressbook.store( fos, null );
|
||||
try {
|
||||
fos.close();
|
||||
|
@@ -27,13 +27,13 @@ package i2p.susi.dns;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Serializable;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
public class ConfigBean implements Serializable {
|
||||
|
||||
@@ -111,7 +111,7 @@ public class ConfigBean implements Serializable {
|
||||
{
|
||||
File file = new File( configFileName );
|
||||
try {
|
||||
PrintWriter out = new PrintWriter( new FileOutputStream( file ) );
|
||||
PrintWriter out = new PrintWriter( new SecureFileOutputStream( file ) );
|
||||
out.print( config );
|
||||
out.flush();
|
||||
out.close();
|
||||
|
@@ -28,12 +28,13 @@ import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
|
||||
public class SubscriptionsBean
|
||||
{
|
||||
private String action, fileName, content, serial, lastSerial;
|
||||
@@ -113,7 +114,7 @@ public class SubscriptionsBean
|
||||
{
|
||||
File file = new File( getFileName() );
|
||||
try {
|
||||
PrintWriter out = new PrintWriter( new FileOutputStream( file ) );
|
||||
PrintWriter out = new PrintWriter( new SecureFileOutputStream( file ) );
|
||||
out.print( content );
|
||||
out.flush();
|
||||
out.close();
|
||||
|
Reference in New Issue
Block a user