propagate from branch 'i2p.i2p' (head 833ef88c125ba48423bc704701303ba55858336f)

to branch 'i2p.i2p.zzz.sam' (head 7814184e3e7cb4b819a0d7b4ceeda5befbe536c3)
This commit is contained in:
zzz
2016-01-03 13:51:03 +00:00
915 changed files with 81925 additions and 56871 deletions

View File

@ -270,6 +270,8 @@ source_lang = en
;;trans.ca = installer/resources/locale/po/messages_ca.po
trans.de = installer/resources/locale/po/messages_de.po
trans.es = installer/resources/locale/po/messages_es.po
;; currently fails check
;;trans.fi = installer/resources/locale/po/messages_fi.po
trans.fr = installer/resources/locale/po/messages_fr.po
trans.id = installer/resources/locale/po/messages_id.po
trans.it = installer/resources/locale/po/messages_it.po
@ -297,6 +299,7 @@ type = PROPERTIES
trans.cs = core/java/src/gnu/getopt/MessagesBundle_cs.properties
trans.de = core/java/src/gnu/getopt/MessagesBundle_de.properties
trans.es = core/java/src/gnu/getopt/MessagesBundle_es.properties
trans.fi = core/java/src/gnu/getopt/MessagesBundle_fi.properties
trans.fr = core/java/src/gnu/getopt/MessagesBundle_fr.properties
trans.hu = core/java/src/gnu/getopt/MessagesBundle_hu.properties
;; Java converts id to in
@ -322,6 +325,7 @@ trans.zh_CN = core/java/src/gnu/getopt/MessagesBundle_zh.properties
[I2P.streaming]
source_file = apps/ministreaming/locale/messages_en.po
source_lang = en
trans.ca = apps/ministreaming/locale/messages_ca.po
trans.de = apps/ministreaming/locale/messages_de.po
trans.es = apps/ministreaming/locale/messages_es.po
trans.fr = apps/ministreaming/locale/messages_fr.po
@ -332,6 +336,7 @@ trans.nb = apps/ministreaming/locale/messages_nb.po
trans.pl = apps/ministreaming/locale/messages_pl.po
trans.ro = apps/ministreaming/locale/messages_ro.po
trans.ru_RU = apps/ministreaming/locale/messages_ru.po
trans.sv_SE = apps/ministreaming/locale/messages_sv.po
trans.uk_UA = apps/ministreaming/locale/messages_uk.po
trans.zh_CN = apps/ministreaming/locale/messages_zh.po

View File

@ -25,21 +25,22 @@ where there are comments labeled "PORTABLE". Do this before you
run I2P for the first time.
To start I2P:
(*nix): sh i2prouter start
(*nix, BSD, Mac): sh i2prouter start
(win*): I2P.exe
(non-x86 platforms PPC, ARM, etc): sh runplain.sh
(platforms without wrapper support): sh runplain.sh
To stop I2P (gracefully):
lynx http://localhost:7657/summaryframe (click "Shutdown")
or (*nix, BSD, Mac) sh i2prouter graceful
To stop I2P immediately:
sh i2prouter stop
(*nix, BSD, Mac) sh i2prouter stop
To uninstall I2P:
rm -rf $I2PInstallDir ~/.i2p
Supported JVMs:
All platforms: Java 1.6 or higher required; 1.7 or higher recommended
All platforms: Java 1.7 or higher required
Windows: OpenJDK or Oracle from http://java.com/download
Linux: OpenJDK or Oracle from http://java.com/download
FreeBSD: OpenJDK or Oracle from http://java.com/download

View File

@ -1,8 +1,9 @@
I2P source installation instructions
Prerequisites to build from source:
Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
Java SDK (preferably Oracle/Sun or OpenJDK) 1.7.0 or higher
Non-linux operating systems and JVMs: See https://trac.i2p2.de/wiki/java
Certain subsystems for embedded (core, router, mstreaming, streaming, i2ptunnel) require only Java 1.6
Apache Ant 1.7.0 or higher
The xgettext, msgfmt, and msgmerge tools installed
from the GNU gettext package http://www.gnu.org/software/gettext/
@ -40,29 +41,30 @@ or on Windows, just double-click on i2pinstall.exe.
Or move the i2pupdate.zip file into an existing installation directory and restart.
To start I2P:
(*nix): sh i2prouter start
(*nix, BSD, Mac): sh i2prouter start
(win*): I2P.exe or i2prouter.bat
(non-x86 platforms PPC, ARM, etc): sh runplain.sh
(platforms without wrapper support): sh runplain.sh
To install I2P as a system service:
(*nix) sh i2prouter install
(*nix, BSD, Mac) sh i2prouter install
(win*) install_i2p_service_winnt.bat
To uninstall I2P as a system service:
(*nix) sh i2prouter remove
(*nix, BSD, Mac) sh i2prouter remove
(win*) uninstall_i2p-service_winnt.bat
To stop I2P (gracefully):
lynx http://localhost:7657/summaryframe (click "Shutdown")
or (*nix, BSD, Mac) sh i2prouter graceful
To stop I2P immediately:
sh i2prouter stop
(*nix, BSD, Mac) sh i2prouter stop
To uninstall I2P:
rm -rf $I2PInstallDir ~/.i2p
Supported JVMs:
Windows: Latest available from http://java.com/download (1.5+ supported)
Linux: Latest available from http://java.com/download (1.5+ supported)
FreeBSD: 1.5-compatible (NIO required)
Windows: Latest available from http://java.com/download (1.7+ supported)
Linux: Latest available from http://java.com/download (1.7+ supported)
FreeBSD: 1.7-compatible (NIO required)
Other operating systems and JVMs: See http://trac.i2p2.de/wiki/java

View File

@ -252,8 +252,8 @@ Applications:
Bundles systray4j-2.4.1:
See licenses/LICENSE-LGPLv2.1.txt
Tomcat 6.0.43:
Copyright 1999-2014 The Apache Software Foundation
Tomcat 6.0.44:
Copyright 1999-2015 The Apache Software Foundation
See licenses/LICENSE-Apache2.0.txt
See licenses/NOTICE-Tomcat.txt

View File

@ -1,6 +1,7 @@
Prerequisites to build from source:
Java SDK (preferably Oracle/Sun or OpenJDK) 1.6.0 or higher
Java SDK (preferably Oracle/Sun or OpenJDK) 1.7.0 or higher
Non-linux operating systems and JVMs: See https://trac.i2p2.de/wiki/java
Certain subsystems for embedded (core, router, mstreaming, streaming, i2ptunnel) require only Java 1.6
Apache Ant 1.7.0 or higher
The xgettext, msgfmt, and msgmerge tools installed
from the GNU gettext package http://www.gnu.org/software/gettext/
@ -19,7 +20,8 @@ To build:
Documentation:
https://geti2p.net/how
API: run 'ant javadoc' then start at build/javadoc/index.html
API: http://docs.i2p-projekt.de/javadoc/
or run 'ant javadoc' then start at build/javadoc/index.html
Latest release:
https://geti2p.net/download
@ -34,6 +36,15 @@ Need help?
IRC irc.freenode.net #i2p
http://forum.i2p/
Bug reports:
https://trac.i2p2.de/report/1
Contact information, security issues, press inquiries:
https://geti2p.net/en/contact
Twitter:
@i2p, @geti2p
Licenses:
See LICENSE.txt

View File

@ -66,7 +66,7 @@ public class Main {
}
static void wrtxt(OutputStream CMDout, String s) throws IOException {
CMDout.write(s.getBytes());
CMDout.write(DataHelper.getUTF8(s));
CMDout.write('\n');
CMDout.flush();
}

View File

@ -63,7 +63,7 @@ public class Main {
}
static void wrtxt(OutputStream CMDout, String s) throws IOException {
CMDout.write(s.getBytes());
CMDout.write(DataHelper.getUTF8(s));
CMDout.write('\n');
CMDout.flush();
}

View File

@ -247,11 +247,11 @@ public class BOB implements Runnable, ClientApp {
save = true;
}
if (!props.containsKey("inbound.length")) {
props.setProperty("inbound.length", "1");
props.setProperty("inbound.length", "3");
save = true;
}
if (!props.containsKey("outbound.length")) {
props.setProperty("outbound.length", "1");
props.setProperty("outbound.length", "3");
save = true;
}
if (!props.containsKey("inbound.lengthVariance")) {
@ -338,7 +338,7 @@ public class BOB implements Runnable, ClientApp {
if (g) {
DoCMDS conn_c = new DoCMDS(spin, lock, server, props, database, _log);
Thread t = new Thread(conn_c);
Thread t = new I2PAppThread(conn_c);
t.setName("BOB.DoCMDS " + i);
t.start();
i++;

View File

@ -25,12 +25,13 @@ import java.util.Locale;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClientFactory;
//import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
//import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.util.I2PAppThread;
// needed only for debugging.
// import java.util.logging.Level;
// import java.util.logging.Logger;
@ -1307,7 +1308,7 @@ public class DoCMDS implements Runnable {
// wait
}
tunnel = new MUXlisten(lock, database, nickinfo, _log);
Thread t = new Thread(tunnel);
Thread t = new I2PAppThread(tunnel);
t.start();
// try {
// Thread.sleep(1000 * 10); // Slow down the startup.

View File

@ -18,10 +18,12 @@ package net.i2p.BOB;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.util.I2PAppThread;
/**
* Listen on I2P and connect to TCP
@ -30,16 +32,15 @@ import net.i2p.client.streaming.I2PSocketManager;
*/
public class I2Plistener implements Runnable {
private NamedDB info, database;
private Logger _log;
public I2PSocketManager socketManager;
public I2PServerSocket serverSocket;
private AtomicBoolean lives;
private final NamedDB info, database;
private final Logger _log;
private final I2PServerSocket serverSocket;
private final AtomicBoolean lives;
/**
* Constructor
* @param SS
* @param S
* @param S unused
* @param info
* @param database
* @param _log
@ -48,7 +49,6 @@ public class I2Plistener implements Runnable {
this.database = database;
this.info = info;
this._log = _log;
this.socketManager = S;
this.serverSocket = SS;
this.lives = lives;
}
@ -79,7 +79,7 @@ public class I2Plistener implements Runnable {
conn++;
// toss the connection to a new thread.
I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database, lives);
Thread t = new Thread(conn_c, Thread.currentThread().getName() + " I2PtoTCP " + conn);
Thread t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " I2PtoTCP " + conn);
t.start();
}

View File

@ -19,7 +19,10 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.data.DataHelper;
import net.i2p.util.I2PAppThread;
/**
* Process I2P->TCP
@ -104,15 +107,15 @@ public class I2PtoTCP implements Runnable {
if (tell) {
// tell who is connecting
out.write(I2P.getPeerDestination().toBase64().getBytes());
out.write(DataHelper.getASCII(I2P.getPeerDestination().toBase64()));
out.write(10); // nl
out.flush(); // not really needed, but...
}
// setup to cross the streams
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA");
q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB");
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
// Fire!
t.start();
q.start();

View File

@ -21,11 +21,13 @@ import java.net.InetAddress;
import java.net.ServerSocket;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
/**
@ -201,14 +203,14 @@ public class MUXlisten implements Runnable {
// I2P -> TCP
SS = socketManager.getServerSocket();
I2Plistener conn = new I2Plistener(SS, socketManager, info, database, _log, lives);
t = new Thread(tg, conn, "BOBI2Plistener " + N);
t = new I2PAppThread(tg, conn, "BOBI2Plistener " + N);
t.start();
}
if (come_in) {
// TCP -> I2P
TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log, lives);
q = new Thread(tg, conn, "BOBTCPlistener " + N);
q = new I2PAppThread(tg, conn, "BOBTCPlistener " + N);
q.start();
}

View File

@ -64,7 +64,7 @@ public class NamedDB {
}
/**
* Find objects in the array, returns it's index or throws exception
* Find objects in the array, returns its index or throws exception
* @param key
* @return an objects index
* @throws ArrayIndexOutOfBoundsException when key does not exist

View File

@ -20,8 +20,10 @@ import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.client.streaming.I2PServerSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.util.I2PAppThread;
/**
* Listen on TCP port and connect to I2P
@ -30,12 +32,11 @@ import net.i2p.client.streaming.I2PSocketManager;
*/
public class TCPlistener implements Runnable {
private NamedDB info, database;
private Logger _log;
public I2PSocketManager socketManager;
public I2PServerSocket serverSocket;
private ServerSocket listener;
private AtomicBoolean lives;
private final NamedDB info, database;
private final Logger _log;
private final I2PSocketManager socketManager;
private final ServerSocket listener;
private final AtomicBoolean lives;
/**
* Constructor
@ -76,7 +77,7 @@ public class TCPlistener implements Runnable {
conn++;
// toss the connection to a new thread.
TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info, database, lives);
Thread t = new Thread(conn_c, Thread.currentThread().getName() + " TCPtoI2P " + conn);
Thread t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPtoI2P " + conn);
t.start();
g = false;
}

View File

@ -24,13 +24,14 @@ import java.net.NoRouteToHostException;
import java.net.Socket;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.data.DataFormatException;
import net.i2p.data.Destination;
//import net.i2p.i2ptunnel.I2PTunnel;
import net.i2p.I2PAppContext;
import net.i2p.util.I2PAppThread;
/**
*
@ -158,8 +159,8 @@ public class TCPtoI2P implements Runnable {
// setup to cross the streams
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
t = new Thread(conn_c, Thread.currentThread().getName() + " TCPioA");
q = new Thread(conn_a, Thread.currentThread().getName() + " TCPioB");
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
// Fire!
t.start();
q.start();

View File

@ -34,15 +34,17 @@ import net.i2p.util.Log;
* The skeletal frame is here, just needs to be finished.
*
* @author sponge
* @deprecated incomplete, unused
*/
public class UDPIOthread implements I2PSessionListener, Runnable {
private NamedDB info;
private Log _log;
private Socket socket;
private final NamedDB info;
private final Log _log;
private final Socket socket;
private DataInputStream in;
private DataOutputStream out;
private I2PSession _session;
private final I2PSession _session;
// FIXME never set
private Destination _peerDestination;
private boolean up;
@ -58,7 +60,6 @@ public class UDPIOthread implements I2PSessionListener, Runnable {
this._log = _log;
this.socket = socket;
this._session = _session;
}
/**

View File

@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Pattern;
import net.i2p.I2PAppContext;
import net.i2p.util.EepGet;
@ -49,6 +50,26 @@ class AddressBook {
private boolean modified;
private static final boolean DEBUG = false;
private static final int MIN_DEST_LENGTH = 516;
private static final int MAX_DEST_LENGTH = MIN_DEST_LENGTH + 100; // longer than any known cert type for now
/**
* 5-67 chars lower/upper case
*/
private static final Pattern HOST_PATTERN =
Pattern.compile("^[0-9a-zA-Z\\.-]{5,67}$");
/**
* 52 chars lower/upper case
* Always ends in 'a' or 'q'
*/
private static final Pattern B32_PATTERN =
Pattern.compile("^[2-7a-zA-Z]{51}[aAqQ]$");
/** not a complete qualification, just a quick check */
private static final Pattern B64_PATTERN =
Pattern.compile("^[0-9a-zA-Z~-]{" + MIN_DEST_LENGTH + ',' + MAX_DEST_LENGTH + "}={0,2}$");
/**
* Construct an AddressBook from the contents of the Map addresses.
*
@ -159,8 +180,13 @@ class AddressBook {
* @since 0.8.7
*/
public Iterator<Map.Entry<String, String>> iterator() {
if (this.subFile != null)
return new ConfigIterator(this.subFile);
if (this.subFile != null) {
try {
return new ConfigIterator(this.subFile);
} catch (IOException ioe) {
return new ConfigIterator();
}
}
return this.addresses.entrySet().iterator();
}
@ -201,9 +227,6 @@ class AddressBook {
return "Map containing " + this.addresses.size() + " entries";
}
private static final int MIN_DEST_LENGTH = 516;
private static final int MAX_DEST_LENGTH = MIN_DEST_LENGTH + 100; // longer than any known cert type for now
/**
* Do basic validation of the hostname
* hostname was already converted to lower case by ConfigParser.parse()
@ -220,9 +243,10 @@ class AddressBook {
host.indexOf("..") < 0 &&
// IDN - basic check, not complete validation
(host.indexOf("--") < 0 || host.startsWith("xn--") || host.indexOf(".xn--") > 0) &&
host.replaceAll("[a-z0-9.-]", "").length() == 0 &&
HOST_PATTERN.matcher(host).matches() &&
// Base32 spoofing (52chars.i2p)
(! (host.length() == 56 && host.substring(0,52).replaceAll("[a-z2-7]", "").length() == 0)) &&
// We didn't do it this way, we use a .b32.i2p suffix, but let's prohibit it anyway
(! (host.length() == 56 && B32_PATTERN.matcher(host.substring(0,52)).matches())) &&
// ... or maybe we do Base32 this way ...
(! host.equals("b32.i2p")) &&
(! host.endsWith(".b32.i2p")) &&
@ -246,7 +270,7 @@ class AddressBook {
(dest.length() > MIN_DEST_LENGTH && dest.length() <= MAX_DEST_LENGTH)) &&
// B64 comes in groups of 2, 3, or 4 chars, but never 1
((dest.length() % 4) != 1) &&
dest.replaceAll("[a-zA-Z0-9~-]", "").length() == 0
B64_PATTERN.matcher(dest).matches()
;
}
@ -332,4 +356,27 @@ class AddressBook {
protected void finalize() {
delete();
}
/****
public static void main(String[] args) {
String[] tests = { "foo.i2p",
"3bnipzzu67cdq2rcygyxz52xhvy6ylokn4zfrk36ywn6pixmaoza.b32.i2p",
"9rhEy4dT9fMlcSOhDzfWRxCV2aen4Zp4eSthOf5f9gVKMa4PtQJ-wEzm2KEYeDXkbM6wEDvMQ6ou4LIniSE6bSAwy7fokiXk5oabels-sJmftnQWRbZyyXEAsLc3gpJJvp9km7kDyZ0z0YGL5tf3S~OaWdptB5tSBOAOjm6ramcYZMWhyUqm~xSL1JyXUqWEHRYwhoDJNL6-L516VpDYVigMBpIwskjeFGcqK8BqWAe0bRwxIiFTPN6Ck8SDzQvS1l1Yj-zfzg3X3gOknzwR8nrHUkjsWtEB6nhbOr8AR21C9Hs0a7MUJvSe2NOuBoNTrtxT76jDruI78JcG5r~WKl6M12yM-SqeBNE9hQn2QCHeHAKju7FdRCbqaZ99IwyjfwvZbkiYYQVN1xlUuGaXrj98XDzK7GORYdH-PrVGfEbMXQ40KLHUWHz8w4tQXAOQrCHEichod0RIzuuxo3XltCWKrf1xGZhkAo9bk2qXi6digCijvYNaKmQdXZYWW~RtAAAA",
"6IZTYacjlXjSAxu-uXEO5oGsj-f4tfePHEvGjs5pu-AMXMwD7-xFdi8kdobDMJp9yRAl96U7yLl~0t9zHeqqYmNeZnDSkTmAcSC2PT45ZJDXBobKi1~a77zuqfPwnzEatYfW3GL1JQAEkAmiwNJoG7ThTZ3zT7W9ekVJpHi9mivpTbaI~rALLfuAg~Mvr60nntZHjqhEZuiU4dTXrmc5nykl~UaMnBdwHL4jKmoN5CotqHyLYZfp74fdD-Oq4SkhuBhU8wkBIM3lz3Ul1o6-s0lNUMdYJq1CyxnyP7jeekdfAlSx4P4sU4M0dPaYvPdOFWPWwBuEh0pCs5Mj01B2xeEBhpV~xSLn6ru5Vq98TrmaR33KHxd76OYYFsWwzVbBuMVSd800XpBghGFucGw01YHYsPh3Afb01sXbf8Nb1bkxCy~DsrmoH4Ww3bpx66JhRTWvg5al3oWlCX51CnJUqaaK~dPL-pBvAyLKIA5aYvl8ca66jtA7AFDxsOb2texBBQAEAAcAAA==",
"te9Ky7XvVcLLr5vQqvfmOasg915P3-ddP3iDqpMMk7v5ufFKobLAX~1k-E4WVsJVlkYvkHVOjxix-uT1IdewKmLd81s5wZtz0GQ3ZC6p0C3S2cOxz7kQqf7QYSR0BrhZC~2du3-GdQO9TqNmsnHrah5lOZf0LN2JFEFPqg8ZB5JNm3JjJeSqePBRk3zAUogNaNK3voB1MVI0ZROKopXAJM4XMERNqI8tIH4ngGtV41SEJJ5pUFrrTx~EiUPqmSEaEA6UDYZiqd23ZlewZ31ExXQj97zvkuhKCoS9A9MNkzZejJhP-TEXWF8~KHur9f51H--EhwZ42Aj69-3GuNjsMdTwglG5zyIfhd2OspxJrXzCPqIV2sXn80IbPgwxHu0CKIJ6X43B5vTyVu87QDI13MIRNGWNZY5KmM5pilGP7jPkOs4xQDo4NHzpuJR5igjWgJIBPU6fI9Pzq~BMzjLiZOMp8xNWey1zKC96L0eX4of1MG~oUvq0qmIHGNa1TlUwBQAEAAEAAA==",
"(*&(*&(*&(*",
"9rhEy4dT9fMlcSOhDzfWRxCV2aen4Zp4eSthOf5f9gVKMa4PtQJ-wEzm2KEYeDXkbM6wEDvMQ6ou4LIniSE6bSAwy7fokiXk5oabels-sJmftnQWRbZyyXEAsLc3gpJJvp9km7kDyZ0z0YGL5tf3S~OaWdptB5tSBOAOjm6ramcYZMWhyUqm~xSL1JyXUqWEHRYwhoDJNL6-L516VpDYVigMBpIwskjeFGcqK8BqWAe0bRwxIiFTPN6Ck8SDzQvS1l1Yj-zfzg3X3gOknzwR8nrHUkjsWtEB6nhbOr8AR21C9Hs0a7MUJvSe2NOuBoNTrtxT76jDruI78JcG5r~WKl6M12yM-SqeBNE9hQn2QCHeHAKju7FdRCbqaZ99IwyjfwvZbkiYYQVN1xlUuGaXrj98XDzK7GORYdH-PrVGfEbMXQ40KLHUWHz8w4tQXAOQrCHEichod0RIzuuxo3XltCWKrf1xGZhkAo9bk2qXi6digCijvYNaKmQdXZYWW~RtAAA",
"6IZTYacjlXjSAxu-uXEO5oGsj-f4tfePHEvGjs5pu-AMXMwD7-xFdi8kdobDMJp9yRAl96U7yLl~0t9zHeqqYmNeZnDSkTmAcSC2PT45ZJDXBobKi1~a77zuqfPwnzEatYfW3GL1JQAEkAmiwNJoG7ThTZ3zT7W9ekVJpHi9mivpTbaI~rALLfuAg~Mvr60nntZHjqhEZuiU4dTXrmc5nykl~UaMnBdwHL4jKmoN5CotqHyLYZfp74fdD-Oq4SkhuBhU8wkBIM3lz3Ul1o6-s0lNUMdYJq1CyxnyP7jeekdfAlSx4P4sU4M0dPaYvPdOFWPWwBuEh0pCs5Mj01B2xeEBhpV~xSLn6ru5Vq98TrmaR33KHxd76OYYFsWwzVbBuMVSd800XpBghGFucGw01YHYsPh3Afb01sXbf8Nb1bkxCy~DsrmoH4Ww3bpx66JhRTWvg5al3oWlCX51CnJUqaaK~dPL-pBvAyLKIA5aYvl8ca66jtA7AFDxsOb2texBBQAEAAcAAA===",
"!e9Ky7XvVcLLr5vQqvfmOasg915P3-ddP3iDqpMMk7v5ufFKobLAX~1k-E4WVsJVlkYvkHVOjxix-uT1IdewKmLd81s5wZtz0GQ3ZC6p0C3S2cOxz7kQqf7QYSR0BrhZC~2du3-GdQO9TqNmsnHrah5lOZf0LN2JFEFPqg8ZB5JNm3JjJeSqePBRk3zAUogNaNK3voB1MVI0ZROKopXAJM4XMERNqI8tIH4ngGtV41SEJJ5pUFrrTx~EiUPqmSEaEA6UDYZiqd23ZlewZ31ExXQj97zvkuhKCoS9A9MNkzZejJhP-TEXWF8~KHur9f51H--EhwZ42Aj69-3GuNjsMdTwglG5zyIfhd2OspxJrXzCPqIV2sXn80IbPgwxHu0CKIJ6X43B5vTyVu87QDI13MIRNGWNZY5KmM5pilGP7jPkOs4xQDo4NHzpuJR5igjWgJIBPU6fI9Pzq~BMzjLiZOMp8xNWey1zKC96L0eX4of1MG~oUvq0qmIHGNa1TlUwBQAEAAEAAA==",
"x"
};
for (String s : tests) {
test(s);
}
}
public static void test(String s) {
System.out.println(s + " valid host? " + isValidKey(s) + " valid dest? " + isValidDest(s));
}
****/
}

View File

@ -22,6 +22,7 @@
package net.i2p.addressbook;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@ -31,6 +32,8 @@ import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import net.i2p.data.DataHelper;
/**
* A class to iterate through a hosts.txt or config file without
* reading the whole thing into memory.
@ -41,7 +44,7 @@ import java.util.NoSuchElementException;
*
* @since 0.8.7
*/
class ConfigIterator implements Iterator<Map.Entry<String, String>> {
class ConfigIterator implements Iterator<Map.Entry<String, String>>, Closeable {
private BufferedReader input;
private ConfigEntry next;
@ -54,11 +57,9 @@ class ConfigIterator implements Iterator<Map.Entry<String, String>> {
/**
* An iterator over the key/value pairs in the file.
*/
public ConfigIterator(File file) {
try {
public ConfigIterator(File file) throws IOException {
FileInputStream fileStream = new FileInputStream(file);
input = new BufferedReader(new InputStreamReader(fileStream));
} catch (IOException ioe) {}
input = new BufferedReader(new InputStreamReader(fileStream, "UTF-8"));
}
public boolean hasNext() {
@ -70,7 +71,7 @@ class ConfigIterator implements Iterator<Map.Entry<String, String>> {
String inputLine = input.readLine();
while (inputLine != null) {
inputLine = ConfigParser.stripComments(inputLine);
String[] splitLine = inputLine.split("=");
String[] splitLine = DataHelper.split(inputLine, "=");
if (splitLine.length == 2) {
next = new ConfigEntry(splitLine[0].trim().toLowerCase(Locale.US), splitLine[1].trim());
return true;

View File

@ -35,6 +35,7 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import net.i2p.data.DataHelper;
import net.i2p.util.SecureFile;
import net.i2p.util.SecureFileOutputStream;
import net.i2p.util.SystemVersion;
@ -93,7 +94,7 @@ class ConfigParser {
inputLine = input.readLine();
while (inputLine != null) {
inputLine = stripComments(inputLine);
String[] splitLine = inputLine.split("=");
String[] splitLine = DataHelper.split(inputLine, "=");
if (splitLine.length == 2) {
result.put(splitLine[0].trim().toLowerCase(Locale.US), splitLine[1].trim());
}
@ -116,7 +117,7 @@ class ConfigParser {
public static Map<String, String> parse(File file) throws IOException {
FileInputStream fileStream = new FileInputStream(file);
BufferedReader input = new BufferedReader(new InputStreamReader(
fileStream));
fileStream, "UTF-8"));
Map<String, String> rv = parse(input);
try {
fileStream.close();
@ -205,7 +206,7 @@ class ConfigParser {
public static List<String> parseSubscriptions(File file) throws IOException {
FileInputStream fileStream = new FileInputStream(file);
BufferedReader input = new BufferedReader(new InputStreamReader(
fileStream));
fileStream, "UTF-8"));
List<String> rv = parseSubscriptions(input);
try {
fileStream.close();

View File

@ -25,6 +25,7 @@ import java.util.Properties;
import net.i2p.I2PAppContext;
import net.i2p.client.naming.NamingServiceUpdater;
import net.i2p.util.I2PAppThread;
/**
* A thread that waits five minutes, then runs the addressbook daemon.
@ -32,7 +33,7 @@ import net.i2p.client.naming.NamingServiceUpdater;
* @author Ragnarok
*
*/
public class DaemonThread extends Thread implements NamingServiceUpdater {
public class DaemonThread extends I2PAppThread implements NamingServiceUpdater {
private String[] args;

View File

@ -23,8 +23,9 @@ package net.i2p.addressbook;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.Date;
/**
@ -56,8 +57,8 @@ class Log {
public void append(String entry) {
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(this.file,
true));
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(this.file,
true), "UTF-8"));
String timestamp = new Date().toString();
bw.write(timestamp + " -- " + entry);
bw.newLine();

View File

@ -26,6 +26,7 @@ import java.util.Iterator;
import java.util.List;
import net.i2p.I2PAppContext;
import net.i2p.util.PortMapper;
/**
* An iterator over the subscriptions in a SubscriptionList. Note that this iterator
@ -69,11 +70,14 @@ class SubscriptionIterator implements Iterator<AddressBook> {
* Yes, the EepGet fetch() is done in here in next().
*
* see java.util.Iterator#next()
* @return an AddressBook (empty if the minimum delay has not been met)
* @return non-null AddressBook (empty if the minimum delay has not been met,
* or there is no proxy tunnel, or the fetch otherwise fails)
*/
public AddressBook next() {
Subscription sub = this.subIterator.next();
if (sub.getLastFetched() + this.delay < I2PAppContext.getGlobalContext().clock().now()) {
if (sub.getLastFetched() + this.delay < I2PAppContext.getGlobalContext().clock().now() &&
I2PAppContext.getGlobalContext().portMapper().getPort(PortMapper.SVC_HTTP_PROXY) >= 0 &&
!I2PAppContext.getGlobalContext().getBooleanProperty("i2p.vmCommSystem")) {
//System.err.println("Fetching addressbook from " + sub.getLocation());
return new AddressBook(sub, this.proxyHost, this.proxyPort);
} else {

View File

@ -11,6 +11,7 @@ import java.util.Iterator;
import java.util.Set;
import net.i2p.data.Hash;
import net.i2p.data.DataHelper
import net.i2p.router.Router;
import net.i2p.router.RouterContext;
import net.i2p.util.I2PThread;
@ -47,7 +48,7 @@ class AdminRunner implements Runnable {
reply(out, "this is not a website");
} else if ( (command.indexOf("routerStats.html") >= 0) || (command.indexOf("oldstats.jsp") >= 0) ) {
try {
out.write("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n".getBytes());
out.write(DataHelper.getASCII("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n"));
_generator.generateStatsPage(new OutputStreamWriter(out));
out.close();
} catch (IOException ioe) {
@ -61,7 +62,7 @@ class AdminRunner implements Runnable {
reply(out, shutdown(command));
} else if (true || command.indexOf("routerConsole.html") > 0) {
try {
out.write("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n".getBytes());
out.write(DataHelper.getASCII("HTTP/1.1 200 OK\nConnection: close\nCache-control: no-cache\nContent-type: text/html\n\n"));
_context.router().renderStatusHTML(new OutputStreamWriter(out));
out.close();
} catch (IOException ioe) {
@ -80,7 +81,7 @@ class AdminRunner implements Runnable {
reply.append("Content-type: text/html\n\n");
reply.append(content);
try {
out.write(reply.toString().getBytes());
out.write(DataHelper.getASCII(reply.toString()));
out.close();
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
@ -97,7 +98,7 @@ class AdminRunner implements Runnable {
reply.append("Content-type: text/plain\n\n");
reply.append(content);
try {
out.write(reply.toString().getBytes());
out.write(DataHelper.getASCII(reply.toString()));
out.close();
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))

View File

@ -1,4 +1,4 @@
# Last Modified: Sun Apr 12 22:08:32 2015
#Last Modified: Sun Dec 06 12:30:32 2015
# vim:syntax=apparmor et ts=8 sw=4
#include <tunables/global>
@ -15,7 +15,7 @@ $INSTALL_PATH/{i2prouter,runplain.sh} flags=(complain) {
$INSTALL_PATH/ r,
$INSTALL_PATH/{i2psvc,wrapper} rmix,
owner $INSTALL_PATH/** rwklm,
owner $INSTALL_PATH/** rwkm,
# Needed for Java
owner @{PROC} r,
@ -57,7 +57,7 @@ $INSTALL_PATH/{i2prouter,runplain.sh} flags=(complain) {
/usr/share/java/eclipse-ecj-*.jar r,
/{,var/}tmp/ rwm,
owner /{,var/}tmp/** rwklm,
owner /{,var/}tmp/** rwkm,
/{,usr/}bin/{,b,d}ash rix,
/{,usr/}bin/cat rix,

View File

@ -26,12 +26,12 @@ then
fi
# on windows, one must specify the path of commnad find
# since windows has its own retarded version of find.
# since windows has its own version of find.
if which find|grep -q -i windows ; then
export PATH=.:/bin:/usr/local/bin:$PATH
fi
# Fast mode - update ondemond
# set LG2 to the language you need in envrionment varibales to enable this
# set LG2 to the language you need in environment variables to enable this
# add ../java/ so the refs will work in the po file
JPATHS="src"
@ -64,19 +64,19 @@ do
echo "Updating the $i file from the tags..."
# extract strings from java and jsp files, and update messages.po files
# translate calls must be one of the forms:
# _("foo")
# _t("foo")
# _x("foo")
# intl._("foo")
# intl._t("foo")
# intl.title("foo")
# handler._("foo")
# formhandler._("foo")
# handler._t("foo")
# formhandler._t("foo")
# net.i2p.router.web.Messages.getString("foo")
# In a jsp, you must use a helper or handler that has the context set.
# To start a new translation, copy the header from an old translation to the new .po file,
# then ant distclean updater.
find $JPATHS -name *.java > $TMPFILE
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
--keyword=_ --keyword=_x --keyword=intl._ --keyword=intl.title \
--keyword=_t --keyword=_x --keyword=intl._ --keyword=intl.title \
--keyword=handler._ --keyword=formhandler._ \
--keyword=net.i2p.router.web.Messages.getString \
-o ${i}t

View File

@ -4,7 +4,7 @@
# To contribute translations, see http://www.i2p2.de/newdevelopers
#
# Translators:
# Denis Blank <gribua@gmail.com>, 2011
# Denis Lysenko <gribua@gmail.com>, 2011
# LinuxChata, 2014
# madjong <madjong@i2pmail.org>, 2014
msgid ""
@ -12,9 +12,9 @@ msgstr ""
"Project-Id-Version: I2P\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-01-09 19:27+0000\n"
"PO-Revision-Date: 2014-12-17 17:00+0000\n"
"Last-Translator: madjong <madjong@i2pmail.org>\n"
"Language-Team: Ukrainian (Ukraine) (http://www.transifex.com/projects/p/I2P/language/uk_UA/)\n"
"PO-Revision-Date: 2015-08-07 16:31+0000\n"
"Last-Translator: Denis Lysenko <gribua@gmail.com>\n"
"Language-Team: Ukrainian (Ukraine) (http://www.transifex.com/otf/I2P/language/uk_UA/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@ -20,7 +20,7 @@ public class ExternalTrayManager extends TrayManager {
@Override
public PopupMenu getMainMenu() {
PopupMenu popup = new PopupMenu();
MenuItem startItem = new MenuItem(_("Start I2P"));
MenuItem startItem = new MenuItem(_t("Start I2P"));
startItem.addActionListener(new ActionListener() {
@Override
@ -35,7 +35,7 @@ public class ExternalTrayManager extends TrayManager {
@Override
protected void done() {
trayIcon.displayMessage(_("Starting"), _("I2P is starting!"), TrayIcon.MessageType.INFO);
trayIcon.displayMessage(_t("Starting"), _t("I2P is starting!"), TrayIcon.MessageType.INFO);
//Hide the tray icon.
//We cannot stop the desktopgui program entirely,
//since that risks killing the I2P process as well.

View File

@ -23,7 +23,7 @@ public class InternalTrayManager extends TrayManager {
public PopupMenu getMainMenu() {
PopupMenu popup = new PopupMenu();
MenuItem browserLauncher = new MenuItem(_("Launch I2P Browser"));
MenuItem browserLauncher = new MenuItem(_t("Launch I2P Browser"));
browserLauncher.addActionListener(new ActionListener() {
@Override
@ -47,7 +47,7 @@ public class InternalTrayManager extends TrayManager {
}.execute();
}
});
MenuItem desktopguiConfigurationLauncher = new MenuItem(_("Configure desktopgui"));
MenuItem desktopguiConfigurationLauncher = new MenuItem(_t("Configure desktopgui"));
desktopguiConfigurationLauncher.addActionListener(new ActionListener() {
@Override
@ -64,7 +64,7 @@ public class InternalTrayManager extends TrayManager {
}
});
MenuItem restartItem = new MenuItem(_("Restart I2P"));
MenuItem restartItem = new MenuItem(_t("Restart I2P"));
restartItem.addActionListener(new ActionListener() {
@Override
@ -82,7 +82,7 @@ public class InternalTrayManager extends TrayManager {
}
});
MenuItem stopItem = new MenuItem(_("Stop I2P"));
MenuItem stopItem = new MenuItem(_t("Stop I2P"));
stopItem.addActionListener(new ActionListener() {
@Override

View File

@ -78,7 +78,7 @@ public abstract class TrayManager {
return image;
}
protected static String _(String s) {
return DesktopguiTranslator._(s);
protected static String _t(String s) {
return DesktopguiTranslator._t(s);
}
}

View File

@ -40,10 +40,10 @@ public class DesktopguiConfigurationFrame extends javax.swing.JFrame {
cancelButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
setTitle(_("Tray icon configuration"));
setTitle(_t("Tray icon configuration"));
desktopguiEnabled.setSelected(true);
desktopguiEnabled.setText(_("Should tray icon be enabled?"));
desktopguiEnabled.setText(_t("Should tray icon be enabled?"));
desktopguiEnabled.setActionCommand("shouldDesktopguiBeEnabled");
okButton.setText("OK");
@ -98,8 +98,8 @@ public class DesktopguiConfigurationFrame extends javax.swing.JFrame {
configureDesktopgui();
}//GEN-LAST:event_okButtonMouseReleased
protected static String _(String s) {
return DesktopguiTranslator._(s);
protected static String _t(String s) {
return DesktopguiTranslator._t(s);
}
private void configureDesktopgui() {

View File

@ -16,11 +16,11 @@ public class DesktopguiTranslator {
return ctx;
}
public static String _(String s) {
public static String _t(String s) {
return Translate.getString(s, getRouterContext(), BUNDLE_NAME);
}
public static String _(String s, Object o) {
public static String _t(String s, Object o) {
return Translate.getString(s, o, getRouterContext(), BUNDLE_NAME);
}
}

View File

@ -1,340 +1 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
See ../../licenses/LICENSE-GPLv2.txt

View File

@ -1,24 +0,0 @@
- I2PSnark:
- add multitorrent support by checking the metainfo hash in the
PeerAcceptor and feeding it off to the appropriate coordinator
- add a web interface
- BEncode
- Byte array length indicator can overflow.
- Support really big BigNums (only 256 chars allowed now)
- Better BEValue toString(). Uses stupid heuristic now for debugging.
- Implemented bencoding.
- Remove application level hack to calculate sha1 hash for metainfo
(But can it be done as efficiently?)
- Storage
- Check file name filter.
- TrackerClient
- Support undocumented &numwant= request.
- PeerCoordinator
- Disconnect from other seeds as soon as you are a seed yourself.
- Text UI
- Make it completely silent.

View File

@ -25,12 +25,12 @@ then
fi
# on windows, one must specify the path of commnad find
# since windows has its own retarded version of find.
# since windows has its own version of find.
if which find|grep -q -i windows ; then
export PATH=.:/bin:/usr/local/bin:$PATH
fi
# Fast mode - update ondemond
# set LG2 to the language you need in envrionment varibales to enable this
# set LG2 to the language you need in environment variables to enable this
# add ../java/ so the refs will work in the po file
JPATHS="../java/src"
@ -63,13 +63,13 @@ do
echo "Updating the $i file from the tags..."
# extract strings from java and jsp files, and update messages.po files
# translate calls must be one of the forms:
# _("foo")
# _t("foo")
# _x("foo")
# To start a new translation, copy the header from an old translation to the new .po file,
# then ant distclean poupdate.
find $JPATHS -name *.java > $TMPFILE
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
--keyword=_ --keyword=_x \
--keyword=_t --keyword=_x \
-o ${i}t
if [ $? -ne 0 ]
then

View File

@ -20,6 +20,8 @@
package org.klomp.snark;
import java.util.Arrays;
/**
* Container of a byte array representing set and unset bits.
@ -66,7 +68,7 @@ public class BitField
/**
* This returns the actual byte array used. Changes to this array
* effect this BitField. Note that some bits at the end of the byte
* affect this BitField. Note that some bits at the end of the byte
* array are supposed to be always unset if they represent bits
* bigger then the size of the bitfield.
*/
@ -105,6 +107,37 @@ public class BitField
}
}
/**
* Sets the given bit to false.
*
* @exception IndexOutOfBoundsException if bit is smaller then zero
* bigger then size (inclusive).
* @since 0.9.22
*/
public void clear(int bit)
{
if (bit < 0 || bit >= size)
throw new IndexOutOfBoundsException(Integer.toString(bit));
int index = bit/8;
int mask = 128 >> (bit % 8);
synchronized(this) {
if ((bitfield[index] & mask) != 0) {
count--;
bitfield[index] &= ~mask;
}
}
}
/**
* Sets all bits to true.
*
* @since 0.9.21
*/
public void setAll() {
Arrays.fill(bitfield, (byte) 0xff);
count = size;
}
/**
* Return true if the bit is set or false if it is not.
*

View File

@ -24,6 +24,7 @@ import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
@ -213,6 +214,20 @@ class ConnectionAcceptor implements Runnable
}
}
}
catch (ConnectException ioe)
{
// This is presumed to be due to socket closing by I2PSnarkUtil.disconnect(),
// which does not currently call our halt(), although it should
if (_log.shouldWarn())
_log.warn("Error while accepting", ioe);
synchronized(this) {
if (!stop) {
locked_halt();
thread = null;
stop = true;
}
}
}
catch (IOException ioe)
{
int level = stop ? Log.WARN : Log.ERROR;

View File

@ -42,7 +42,7 @@ abstract class ExtensionHandler {
* @param dht advertise DHT capability
* @return bencoded outgoing handshake message
*/
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht) {
public static byte[] getHandshake(int metasize, boolean pexAndMetadata, boolean dht, boolean uploadOnly) {
Map<String, Object> handshake = new HashMap<String, Object>();
Map<String, Integer> m = new HashMap<String, Integer>();
if (pexAndMetadata) {
@ -59,6 +59,9 @@ abstract class ExtensionHandler {
handshake.put("p", Integer.valueOf(TrackerClient.PORT));
handshake.put("v", "I2PSnark");
handshake.put("reqq", Integer.valueOf(5));
// BEP 21
if (uploadOnly)
handshake.put("upload_only", Integer.valueOf(1));
return BEncoder.bencode(handshake);
}
@ -90,17 +93,20 @@ abstract class ExtensionHandler {
peer.setHandshakeMap(map);
Map<String, BEValue> msgmap = map.get("m").getMap();
if (msgmap.get(TYPE_PEX) != null) {
if (log.shouldLog(Log.DEBUG))
log.debug("Peer supports PEX extension: " + peer);
// peer state calls peer listener calls sendPEX()
}
if (log.shouldLog(Log.DEBUG))
log.debug("Peer " + peer + " supports extensions: " + msgmap.keySet());
if (msgmap.get(TYPE_DHT) != null) {
if (log.shouldLog(Log.DEBUG))
log.debug("Peer supports DHT extension: " + peer);
// peer state calls peer listener calls sendDHT()
}
//if (msgmap.get(TYPE_PEX) != null) {
// if (log.shouldLog(Log.DEBUG))
// log.debug("Peer supports PEX extension: " + peer);
// // peer state calls peer listener calls sendPEX()
//}
//if (msgmap.get(TYPE_DHT) != null) {
// if (log.shouldLog(Log.DEBUG))
// log.debug("Peer supports DHT extension: " + peer);
// // peer state calls peer listener calls sendDHT()
//}
MagnetState state = peer.getMagnetState();
@ -204,30 +210,31 @@ abstract class ExtensionHandler {
if (log.shouldLog(Log.DEBUG))
log.debug("Got request for " + piece + " from: " + peer);
byte[] pc;
int totalSize;
synchronized(state) {
pc = state.getChunk(piece);
totalSize = state.getSize();
}
sendPiece(peer, piece, pc);
sendPiece(peer, piece, pc, totalSize);
// Do this here because PeerConnectionOut only reports for PIECE messages
peer.uploaded(pc.length);
listener.uploaded(peer, pc.length);
} else if (type == TYPE_DATA) {
int size = map.get("total_size").getInt();
if (log.shouldLog(Log.DEBUG))
log.debug("Got data for " + piece + " length " + size + " from: " + peer);
// On close reading of BEP 9, this is the total metadata size.
// Prior to 0.9.21, we sent the piece size, so we can't count on it.
// just ignore it. The actual length will be verified in saveChunk()
//int size = map.get("total_size").getInt();
//if (log.shouldLog(Log.DEBUG))
// log.debug("Got data for " + piece + " length " + size + " from: " + peer);
boolean done;
int chk = -1;
synchronized(state) {
if (state.isComplete())
return;
int len = is.available();
if (len != size) {
// probably fatal
if (log.shouldLog(Log.WARN))
log.warn("total_size " + size + " but avail data " + len);
}
peer.downloaded(len);
listener.downloaded(peer, len);
// this checks the size
done = state.saveChunk(piece, bs, bs.length - len, len);
if (log.shouldLog(Log.INFO))
log.info("Got chunk " + piece + " from " + peer);
@ -290,11 +297,15 @@ abstract class ExtensionHandler {
}
}
private static void sendPiece(Peer peer, int piece, byte[] data) {
private static void sendPiece(Peer peer, int piece, byte[] data, int totalSize) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("msg_type", Integer.valueOf(TYPE_DATA));
map.put("piece", Integer.valueOf(piece));
map.put("total_size", Integer.valueOf(data.length));
// BEP 9
// "This key has the same semantics as the 'metadata_size' in the extension header"
// which apparently means the same value. Fixed in 0.9.21.
//map.put("total_size", Integer.valueOf(data.length));
map.put("total_size", Integer.valueOf(totalSize));
byte[] dict = BEncoder.bencode(map);
byte[] payload = new byte[dict.length + data.length];
System.arraycopy(dict, 0, payload, 0, dict.length);

View File

@ -3,8 +3,8 @@ package org.klomp.snark;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@ -14,6 +14,7 @@ import java.util.Set;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PServerSocket;
@ -74,7 +75,7 @@ public class I2PSnarkUtil {
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
public static final int DEFAULT_STARTUP_DELAY = 3;
public static final boolean DEFAULT_USE_OPENTRACKERS = true;
public static final int MAX_CONNECTIONS = 16; // per torrent
public static final int MAX_CONNECTIONS = 24; // per torrent
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
public static final boolean DEFAULT_USE_DHT = true;
public static final String EEPGET_USER_AGENT = "I2PSnark";
@ -135,6 +136,7 @@ public class I2PSnarkUtil {
public boolean configured() { return _configured; }
@SuppressWarnings({"unchecked", "rawtypes"})
public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) {
if (i2cpHost != null)
_i2cpHost = i2cpHost;
@ -255,6 +257,8 @@ public class I2PSnarkUtil {
opts.setProperty("i2p.streaming.disableRejectLogging", "true");
if (opts.getProperty("i2p.streaming.answerPings") == null)
opts.setProperty("i2p.streaming.answerPings", "false");
if (opts.getProperty(I2PClient.PROP_SIGTYPE) == null)
opts.setProperty(I2PClient.PROP_SIGTYPE, "EdDSA_SHA512_Ed25519");
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
_connecting = false;
}
@ -456,7 +460,7 @@ public class I2PSnarkUtil {
return null;
}
String getOurIPString() {
public String getOurIPString() {
Destination dest = getMyDestination();
if (dest != null)
return dest.toBase64();
@ -586,10 +590,10 @@ public class I2PSnarkUtil {
*/
public boolean isKnownOpenTracker(String url) {
try {
URL u = new URL(url);
URI u = new URI(url);
String host = u.getHost();
return host != null && SnarkManager.KNOWN_OPENTRACKERS.contains(host);
} catch (MalformedURLException mue) {
} catch (URISyntaxException use) {
return false;
}
}
@ -657,7 +661,7 @@ public class I2PSnarkUtil {
* The {0} will be replaced by the parameter.
* Single quotes must be doubled, i.e. ' -> '' in the string.
* @param o parameter, not translated.
* To tranlslate parameter also, use _("foo {0} bar", _("baz"))
* To translate parameter also, use _t("foo {0} bar", _t("baz"))
* Do not double the single quotes in the parameter.
* Use autoboxing to call with ints, longs, floats, etc.
*/

View File

@ -120,10 +120,10 @@ class IdleChecker extends SimpleTimer2.TimedEvent {
Map<String, String> opts = _util.getI2CPOptions();
String i = opts.get("inbound.quantity");
if (i == null)
i = "3";
i = Integer.toString(SnarkManager.DEFAULT_TUNNEL_QUANTITY);
String o = opts.get("outbound.quantity");
if (o == null)
o = "3";
o = Integer.toString(SnarkManager.DEFAULT_TUNNEL_QUANTITY);
String ib = opts.get("inbound.backupQuantity");
if (ib == null)
ib = "0";

View File

@ -55,11 +55,13 @@ class Message
byte type;
// Used for HAVE, REQUEST, PIECE and CANCEL messages.
// Also SUGGEST, REJECT, ALLOWED_FAST
// low byte used for EXTENSION message
// low two bytes used for PORT message
int piece;
// Used for REQUEST, PIECE and CANCEL messages.
// Also REJECT
int begin;
int length;
@ -104,15 +106,18 @@ class Message
int datalen = 1;
// piece is 4 bytes.
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL)
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL ||
type == SUGGEST || type == REJECT || type == ALLOWED_FAST)
datalen += 4;
// begin/offset is 4 bytes
if (type == REQUEST || type == PIECE || type == CANCEL)
if (type == REQUEST || type == PIECE || type == CANCEL ||
type == REJECT)
datalen += 4;
// length is 4 bytes
if (type == REQUEST || type == CANCEL)
if (type == REQUEST || type == CANCEL ||
type == REJECT)
datalen += 4;
// msg type is 1 byte
@ -131,15 +136,18 @@ class Message
dos.writeByte(type & 0xFF);
// Send additional info (piece number)
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL)
if (type == HAVE || type == REQUEST || type == PIECE || type == CANCEL ||
type == SUGGEST || type == REJECT || type == ALLOWED_FAST)
dos.writeInt(piece);
// Send additional info (begin/offset)
if (type == REQUEST || type == PIECE || type == CANCEL)
if (type == REQUEST || type == PIECE || type == CANCEL ||
type == REJECT)
dos.writeInt(begin);
// Send additional info (length); for PIECE this is implicit.
if (type == REQUEST || type == CANCEL)
if (type == REQUEST || type == CANCEL ||
type == REJECT)
dos.writeInt(length);
if (type == EXTENSION)
@ -173,21 +181,32 @@ class Message
case UNINTERESTED:
return "UNINTERESTED";
case HAVE:
return "HAVE(" + piece + ")";
return "HAVE(" + piece + ')';
case BITFIELD:
return "BITFIELD";
case REQUEST:
return "REQUEST(" + piece + "," + begin + "," + length + ")";
return "REQUEST(" + piece + ',' + begin + ',' + length + ')';
case PIECE:
return "PIECE(" + piece + "," + begin + "," + length + ")";
return "PIECE(" + piece + ',' + begin + ',' + length + ')';
case CANCEL:
return "CANCEL(" + piece + "," + begin + "," + length + ")";
return "CANCEL(" + piece + ',' + begin + ',' + length + ')';
case PORT:
return "PORT(" + piece + ")";
return "PORT(" + piece + ')';
case EXTENSION:
return "EXTENSION(" + piece + ',' + data.length + ')';
// fast extensions below here
case SUGGEST:
return "SUGGEST(" + piece + ')';
case HAVE_ALL:
return "HAVE_ALL";
case HAVE_NONE:
return "HAVE_NONE";
case REJECT:
return "REJECT(" + piece + ',' + begin + ',' + length + ')';
case ALLOWED_FAST:
return "ALLOWED_FAST(" + piece + ')';
default:
return "<UNKNOWN>";
return "UNKNOWN (" + type + ')';
}
}
}

View File

@ -74,10 +74,11 @@ public class MetaInfo
* @param files null for single-file torrent
* @param lengths null for single-file torrent
* @param announce_list may be null
* @param created_by may be null
*/
MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
int piece_length, byte[] piece_hashes, long length, boolean privateTorrent,
List<List<String>> announce_list)
List<List<String>> announce_list, String created_by)
{
this.announce = announce;
this.name = name;
@ -91,7 +92,7 @@ public class MetaInfo
this.privateTorrent = privateTorrent;
this.announce_list = announce_list;
this.comment = null;
this.created_by = null;
this.created_by = created_by;
this.creation_date = I2PAppContext.getGlobalContext().clock().now();
// TODO if we add a parameter for other keys

View File

@ -108,7 +108,8 @@ class PartialPiece implements Comparable<PartialPiece> {
/**
* Convert this PartialPiece to a request for the next chunk.
* Used by PeerState only.
* Used by PeerState only. This depends on the downloaded value
* as set by setDownloaded() or read().
*/
public Request getRequest() {
@ -128,14 +129,16 @@ class PartialPiece implements Comparable<PartialPiece> {
}
/**
* How many bytes are good - only valid by setDownloaded()
* How many bytes are good - as set by setDownloaded() or read()
*/
public int getDownloaded() {
return this.off;
}
/**
* Call this before returning a PartialPiece to the PeerCoordinator
* Call this if necessary before returning a PartialPiece to the PeerCoordinator.
* We do not use a bitmap to track individual chunks received.
* Any chunks after a 'hole' will be lost.
* @since 0.9.1
*/
public void setDownloaded(int offset) {
@ -191,11 +194,20 @@ class PartialPiece implements Comparable<PartialPiece> {
/**
* Blocking.
* If offset matches the previous downloaded amount
* (as set by a previous call to read() or setDownlaoded()),
* the downloaded amount will be incremented by len.
*
* @since 0.9.1
*/
public void read(DataInputStream din, int off, int len) throws IOException {
public void read(DataInputStream din, int offset, int len) throws IOException {
if (bs != null) {
din.readFully(bs, off, len);
din.readFully(bs, offset, len);
synchronized (this) {
// only works for in-order chunks
if (this.off == offset)
this.off += len;
}
} else {
// read in fully before synching on raf
ByteArray ba;
@ -211,8 +223,11 @@ class PartialPiece implements Comparable<PartialPiece> {
synchronized (this) {
if (raf == null)
createTemp();
raf.seek(off);
raf.seek(offset);
raf.write(tmp);
// only works for in-order chunks
if (this.off == offset)
this.off += len;
}
if (ba != null)
_cache.release(ba, false);

View File

@ -62,7 +62,7 @@ public class Peer implements Comparable<Peer>
// Keeps state for in/out connections. Non-null when the handshake
// was successful, the connection setup and runs
PeerState state;
volatile PeerState state;
/** shared across all peers on this torrent */
MagnetState magnetState;
@ -79,15 +79,15 @@ public class Peer implements Comparable<Peer>
private long uploaded_old[] = {-1,-1,-1};
private long downloaded_old[] = {-1,-1,-1};
// bytes per bt spec: 0011223344556677
static final long OPTION_EXTENSION = 0x0000000000100000l;
static final long OPTION_FAST = 0x0000000000000004l;
static final long OPTION_DHT = 0x0000000000000001l;
// bytes per bt spec: 0011223344556677
private static final long OPTION_EXTENSION = 0x0000000000100000l;
private static final long OPTION_FAST = 0x0000000000000004l;
//private static final long OPTION_DHT = 0x0000000000000001l;
/** we use a different bit since the compact format is different */
/* no, let's use an extension message
static final long OPTION_I2P_DHT = 0x0000000040000000l;
*/
static final long OPTION_AZMP = 0x1000000000000000l;
//private static final long OPTION_AZMP = 0x1000000000000000l;
private long options;
/**
@ -217,9 +217,11 @@ public class Peer implements Comparable<Peer>
*
* If the given BitField is non-null it is send to the peer as first
* message.
*
* @param uploadOnly if we are complete with skipped files, i.e. a partial seed
*/
public void runConnection(I2PSnarkUtil util, PeerListener listener, BitField bitfield, MagnetState mState)
{
public void runConnection(I2PSnarkUtil util, PeerListener listener, BitField bitfield,
MagnetState mState, boolean uploadOnly) {
if (state != null)
throw new IllegalStateException("Peer already started");
@ -275,17 +277,9 @@ public class Peer implements Comparable<Peer>
int metasize = metainfo != null ? metainfo.getInfoBytes().length : -1;
boolean pexAndMetadata = metainfo == null || !metainfo.isPrivate();
boolean dht = util.getDHT() != null;
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata, dht));
out.sendExtension(0, ExtensionHandler.getHandshake(metasize, pexAndMetadata, dht, uploadOnly));
}
// Old DHT PORT message
//if ((options & OPTION_I2P_DHT) != 0 && util.getDHT() != null) {
// if (_log.shouldLog(Log.DEBUG))
// _log.debug("Peer supports DHT, sending PORT message");
// int port = util.getDHT().getPort();
// out.sendPort(port);
//}
// Send our bitmap
if (bitfield != null)
s.out.sendBitfield(bitfield);
@ -297,7 +291,7 @@ public class Peer implements Comparable<Peer>
if (_log.shouldLog(Log.DEBUG))
_log.debug("Start running the reader with " + toString());
// Use this thread for running the incomming connection.
// Use this thread for running the incoming connection.
// The outgoing connection creates its own Thread.
out.startup();
Thread.currentThread().setName("Snark reader from " + peerID);
@ -338,6 +332,9 @@ public class Peer implements Comparable<Peer>
dout.write("BitTorrent protocol".getBytes("UTF-8"));
// Handshake write - options
long myOptions = OPTION_EXTENSION;
// we can't handle HAVE_ALL or HAVE_NONE if we don't know the number of pieces
if (metainfo != null)
myOptions |= OPTION_FAST;
// FIXME get util here somehow
//if (util.getDHT() != null)
// myOptions |= OPTION_I2P_DHT;
@ -385,15 +382,15 @@ public class Peer implements Comparable<Peer>
if (options != 0) {
// send them something in runConnection() above
if (_log.shouldLog(Log.DEBUG))
_log.debug("Peer supports options 0x" + Long.toString(options, 16) + ": " + toString());
_log.debug("Peer supports options 0x" + Long.toHexString(options) + ": " + toString());
}
return bs;
}
/** @since 0.8.4 */
public long getOptions() {
return options;
/** @since 0.9.21 */
public boolean supportsFast() {
return (options & OPTION_FAST) != 0;
}
/** @since 0.8.4 */

View File

@ -267,7 +267,23 @@ class PeerCheckerTask implements Runnable
// close out unused files, but we don't need to do it every time
Storage storage = coordinator.getStorage();
if (storage != null && (_runCount % 4) == 0) {
if (storage != null) {
// The more files a torrent has, the more often we call the cleaner,
// to keep from running out of FDs
int files = storage.getFileCount();
int skip;
if (files == 1)
skip = 6;
else if (files <= 4)
skip = 4;
else if (files <= 20)
skip = 3;
else if (files <= 50)
skip = 2;
else
skip = 1;
if ((_runCount % skip) == 0)
storage.cleanRAFs();
}

View File

@ -39,7 +39,7 @@ class PeerConnectionIn implements Runnable
private static final int MAX_MSG_SIZE = Math.max(PeerState.PARTSIZE + 9,
MagnetState.CHUNK_SIZE + 100); // 100 for the ext msg dictionary
private Thread thread;
private volatile Thread thread;
private volatile boolean quit;
long lastRcvd;
@ -75,9 +75,12 @@ class PeerConnectionIn implements Runnable
thread = Thread.currentThread();
try
{
PeerState ps = peer.state;
while (!quit && ps != null)
while (!quit)
{
final PeerState ps = peer.state;
if (ps == null)
break;
// Common variables used for some messages.
int piece;
int begin;
@ -91,59 +94,64 @@ class PeerConnectionIn implements Runnable
if (i == 0)
{
ps.keepAliveMessage();
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received keepalive from " + peer);
ps.keepAliveMessage();
continue;
}
byte b = din.readByte();
Message m = new Message();
m.type = b;
switch (b)
{
case 0:
ps.chokeMessage(true);
case Message.CHOKE:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received choke from " + peer);
ps.chokeMessage(true);
break;
case 1:
ps.chokeMessage(false);
case Message.UNCHOKE:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received unchoke from " + peer);
ps.chokeMessage(false);
break;
case 2:
ps.interestedMessage(true);
case Message.INTERESTED:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received interested from " + peer);
ps.interestedMessage(true);
break;
case 3:
ps.interestedMessage(false);
case Message.UNINTERESTED:
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received not interested from " + peer);
ps.interestedMessage(false);
break;
case 4:
case Message.HAVE:
piece = din.readInt();
ps.haveMessage(piece);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received havePiece(" + piece + ") from " + peer);
ps.haveMessage(piece);
break;
case 5:
case Message.BITFIELD:
byte[] bitmap = new byte[i-1];
din.readFully(bitmap);
ps.bitfieldMessage(bitmap);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received bitmap from " + peer + ": size=" + (i-1) /* + ": " + ps.bitfield */ );
ps.bitfieldMessage(bitmap);
break;
case 6:
case Message.REQUEST:
piece = din.readInt();
begin = din.readInt();
len = din.readInt();
ps.requestMessage(piece, begin, len);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received request(" + piece + "," + begin + ") from " + peer);
ps.requestMessage(piece, begin, len);
break;
case 7:
case Message.PIECE:
piece = din.readInt();
begin = din.readInt();
len = i-9;
@ -151,9 +159,9 @@ class PeerConnectionIn implements Runnable
if (req != null)
{
req.read(din);
ps.pieceMessage(req);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received data(" + piece + "," + begin + ") from " + peer);
ps.pieceMessage(req);
}
else
{
@ -165,21 +173,24 @@ class PeerConnectionIn implements Runnable
_log.debug("Received UNWANTED data(" + piece + "," + begin + ") from " + peer);
}
break;
case 8:
case Message.CANCEL:
piece = din.readInt();
begin = din.readInt();
len = din.readInt();
ps.cancelMessage(piece, begin, len);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received cancel(" + piece + "," + begin + ") from " + peer);
ps.cancelMessage(piece, begin, len);
break;
case 9: // PORT message
case Message.PORT:
int port = din.readUnsignedShort();
ps.portMessage(port);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received port message from " + peer);
ps.portMessage(port);
break;
case 20: // Extension message
case Message.EXTENSION:
int id = din.readUnsignedByte();
byte[] payload = new byte[i-2];
din.readFully(payload);
@ -187,6 +198,43 @@ class PeerConnectionIn implements Runnable
_log.debug("Received extension message from " + peer);
ps.extensionMessage(id, payload);
break;
// fast extensions below here
case Message.SUGGEST:
piece = din.readInt();
ps.suggestMessage(piece);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received suggest(" + piece + ") from " + peer);
break;
case Message.HAVE_ALL:
ps.haveMessage(true);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received have_all from " + peer);
break;
case Message.HAVE_NONE:
ps.haveMessage(false);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received have_none from " + peer);
break;
case Message.REJECT:
piece = din.readInt();
begin = din.readInt();
len = din.readInt();
ps.rejectMessage(piece, begin, len);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received reject(" + piece + ',' + begin + ',' + len + ") from " + peer);
break;
case Message.ALLOWED_FAST:
piece = din.readInt();
ps.allowedFastMessage(piece);
if (_log.shouldLog(Log.DEBUG))
_log.debug("Received allowed_fast(" + piece + ") from " + peer);
break;
default:
byte[] bs = new byte[i-1];
din.readFully(bs);
@ -202,11 +250,9 @@ class PeerConnectionIn implements Runnable
if (_log.shouldLog(Log.INFO))
_log.info("IOError talking with " + peer, ioe);
}
catch (Throwable t)
catch (RuntimeException t)
{
_log.error("Error talking with " + peer, t);
if (t instanceof OutOfMemoryError)
throw (OutOfMemoryError)t;
}
finally
{

View File

@ -22,9 +22,10 @@ package org.klomp.snark;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicLong;
import net.i2p.I2PAppContext;
@ -42,7 +43,7 @@ class PeerConnectionOut implements Runnable
private boolean quit;
// Contains Messages.
private final List<Message> sendQueue = new ArrayList<Message>();
private final BlockingQueue<Message> sendQueue = new LinkedBlockingQueue<Message>();
private static final AtomicLong __id = new AtomicLong();
private final long _id;
@ -124,6 +125,16 @@ class PeerConnectionOut implements Runnable
if (state.choking) {
it.remove();
//SimpleTimer.getInstance().removeEvent(nm.expireEvent);
if (peer.supportsFast()) {
Message r = new Message();
r.type = Message.REJECT;
r.piece = nm.piece;
r.begin = nm.begin;
r.length = nm.length;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send " + peer + ": " + r);
r.sendMessage(dout);
}
}
nm = null;
}
@ -141,8 +152,8 @@ class PeerConnectionOut implements Runnable
it.remove();
}
}
if (m == null && !sendQueue.isEmpty()) {
m = sendQueue.remove(0);
if (m == null) {
m = sendQueue.poll();
//SimpleTimer.getInstance().removeEvent(m.expireEvent);
}
}
@ -159,6 +170,8 @@ class PeerConnectionOut implements Runnable
lastSent = System.currentTimeMillis();
// Remove all piece messages after sending a choke message.
// FiXME this causes REJECT messages to be sent before sending the CHOKE;
// BEP 6 recommends sending them after.
if (m.type == Message.CHOKE)
removeMessage(Message.PIECE);
@ -233,7 +246,7 @@ class PeerConnectionOut implements Runnable
{
synchronized(sendQueue)
{
sendQueue.add(m);
sendQueue.offer(m);
sendQueue.notifyAll();
}
}
@ -277,11 +290,22 @@ class PeerConnectionOut implements Runnable
while (it.hasNext())
{
Message m = it.next();
if (m.type == type)
{
if (m.type == type) {
it.remove();
removed = true;
}
if (type == Message.PIECE && peer.supportsFast()) {
Message r = new Message();
r.type = Message.REJECT;
r.piece = m.piece;
r.begin = m.begin;
r.length = m.length;
if (_log.shouldLog(Log.DEBUG))
_log.debug("Send " + peer + ": " + r);
try {
r.sendMessage(dout);
} catch (IOException ioe) {}
}
}
}
sendQueue.notifyAll();
}
@ -296,7 +320,7 @@ class PeerConnectionOut implements Runnable
synchronized(sendQueue)
{
if(sendQueue.isEmpty())
sendQueue.add(m);
sendQueue.offer(m);
sendQueue.notifyAll();
}
}
@ -349,12 +373,19 @@ class PeerConnectionOut implements Runnable
void sendBitfield(BitField bitfield)
{
Message m = new Message();
m.type = Message.BITFIELD;
m.data = bitfield.getFieldBytes();
m.off = 0;
m.len = m.data.length;
addMessage(m);
boolean fast = peer.supportsFast();
if (fast && bitfield.complete()) {
sendHaveAll();
} else if (fast && bitfield.count() <= 0) {
sendHaveNone();
} else {
Message m = new Message();
m.type = Message.BITFIELD;
m.data = bitfield.getFieldBytes();
m.off = 0;
m.len = m.data.length;
addMessage(m);
}
}
/** reransmit requests not received in 7m */
@ -509,7 +540,8 @@ class PeerConnectionOut implements Runnable
}
/**
* Remove all Request messages from the queue
* Remove all Request messages from the queue.
* Does not send a cancel message.
* @since 0.8.2
*/
void cancelRequestMessages() {
@ -521,9 +553,12 @@ class PeerConnectionOut implements Runnable
}
}
// Called by the PeerState when the other side doesn't want this
// request to be handled anymore. Removes any pending Piece Message
// from out send queue.
/**
* Called by the PeerState when the other side doesn't want this
* request to be handled anymore. Removes any pending Piece Message
* from out send queue.
* Does not send a cancel message.
*/
void cancelRequest(int piece, int begin, int length)
{
synchronized (sendQueue)
@ -559,4 +594,50 @@ class PeerConnectionOut implements Runnable
m.piece = port;
addMessage(m);
}
/**
* Unused
* @since 0.9.21
*/
void sendSuggest(int piece) {
Message m = new Message();
m.type = Message.SUGGEST;
m.piece = piece;
addMessage(m);
}
/** @since 0.9.21 */
private void sendHaveAll() {
Message m = new Message();
m.type = Message.HAVE_ALL;
addMessage(m);
}
/** @since 0.9.21 */
private void sendHaveNone() {
Message m = new Message();
m.type = Message.HAVE_NONE;
addMessage(m);
}
/** @since 0.9.21 */
void sendReject(int piece, int begin, int length) {
Message m = new Message();
m.type = Message.REJECT;
m.piece = piece;
m.begin = begin;
m.length = length;
addMessage(m);
}
/**
* Unused
* @since 0.9.21
*/
void sendAllowedFast(int piece) {
Message m = new Message();
m.type = Message.ALLOWED_FAST;
m.piece = piece;
addMessage(m);
}
}

View File

@ -33,6 +33,7 @@ import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.atomic.AtomicLong;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
@ -69,7 +70,7 @@ class PeerCoordinator implements PeerListener
// package local for access by CheckDownLoadersTask
final static long CHECK_PERIOD = 40*1000; // 40 seconds
final static int MAX_UPLOADERS = 6;
final static int MAX_UPLOADERS = 8;
public static final long MAX_INACTIVE = 8*60*1000;
/**
@ -87,8 +88,8 @@ class PeerCoordinator implements PeerListener
// final static int MAX_DOWNLOADERS = MAX_CONNECTIONS;
// int downloaders = 0;
private long uploaded;
private long downloaded;
private final AtomicLong uploaded = new AtomicLong();
private final AtomicLong downloaded = new AtomicLong();
final static int RATE_DEPTH = 3; // make following arrays RATE_DEPTH long
private final long uploaded_old[] = {-1,-1,-1};
private final long downloaded_old[] = {-1,-1,-1};
@ -279,7 +280,7 @@ class PeerCoordinator implements PeerListener
*/
public long getUploaded()
{
return uploaded;
return uploaded.get();
}
/**
@ -287,7 +288,7 @@ class PeerCoordinator implements PeerListener
* @since 0.9.15
*/
public void setUploaded(long up) {
uploaded = up;
uploaded.set(up);
}
/**
@ -295,7 +296,7 @@ class PeerCoordinator implements PeerListener
*/
public long getDownloaded()
{
return downloaded;
return downloaded.get();
}
/**
@ -321,16 +322,22 @@ class PeerCoordinator implements PeerListener
*/
public long getDownloadRate()
{
if (halted)
return 0;
return getRate(downloaded_old);
}
public long getUploadRate()
{
if (halted)
return 0;
return getRate(uploaded_old);
}
public long getCurrentUploadRate()
{
if (halted)
return 0;
// no need to synchronize, only one value
long r = uploaded_old[0];
if (r <= 0)
@ -396,7 +403,7 @@ class PeerCoordinator implements PeerListener
* Formerly used to
* reduce max if huge pieces to keep from ooming when leeching
* but now we don't
* @return usually 16
* @return usually I2PSnarkUtil.MAX_CONNECTIONS
*/
private int getMaxConnections() {
if (metainfo == null)
@ -597,11 +604,13 @@ class PeerCoordinator implements PeerListener
bitfield = storage.getBitField();
else
bitfield = null;
// if we aren't a seed but we don't want any more
final boolean partialComplete = wantedBytes == 0 && bitfield != null && !bitfield.complete();
Runnable r = new Runnable()
{
public void run()
{
peer.runConnection(_util, listener, bitfield, magnetState);
peer.runConnection(_util, listener, bitfield, magnetState, partialComplete);
}
};
String threadName = "Snark peer " + peer.toString();
@ -913,6 +922,7 @@ class PeerCoordinator implements PeerListener
* Returns a byte array containing the requested piece or null of
* the piece is unknown.
*
* @return bytes or null for errors such as not having the piece yet
* @throws RuntimeException on IOE getting the data
*/
public ByteArray gotRequest(Peer peer, int piece, int off, int len)
@ -944,7 +954,7 @@ class PeerCoordinator implements PeerListener
*/
public void uploaded(Peer peer, int size)
{
uploaded += size;
uploaded.addAndGet(size);
//if (listener != null)
// listener.peerChange(this, peer);
@ -955,7 +965,7 @@ class PeerCoordinator implements PeerListener
*/
public void downloaded(Peer peer, int size)
{
downloaded += size;
downloaded.addAndGet(size);
//if (listener != null)
// listener.peerChange(this, peer);
@ -976,8 +986,9 @@ class PeerCoordinator implements PeerListener
}
int piece = pp.getPiece();
synchronized(wantedPieces)
{
// try/catch outside the synch to avoid deadlock in the catch
try {
synchronized(wantedPieces) {
Piece p = new Piece(piece);
if (!wantedPieces.contains(p))
{
@ -993,8 +1004,7 @@ class PeerCoordinator implements PeerListener
}
}
try
{
// try/catch moved outside of synch
// this takes forever if complete, as it rechecks
if (storage.putPiece(pp))
{
@ -1003,26 +1013,38 @@ class PeerCoordinator implements PeerListener
}
else
{
// so we will try again
markUnrequested(peer, piece);
// just in case
removePartialPiece(piece);
// Oops. We didn't actually download this then... :(
downloaded -= metainfo.getPieceLength(piece);
_log.warn("Got BAD piece " + piece + "/" + metainfo.getPieces() + " from " + peer + " for " + metainfo.getName());
downloaded.addAndGet(0 - metainfo.getPieceLength(piece));
// Mark this peer as not having the piece. PeerState will update its bitfield.
for (Piece pc : wantedPieces) {
if (pc.getId() == piece) {
pc.removePeer(peer);
break;
}
}
if (_log.shouldWarn())
_log.warn("Got BAD piece " + piece + "/" + metainfo.getPieces() + " from " + peer + " for " + metainfo.getName());
return false; // No need to announce BAD piece to peers.
}
}
catch (IOException ioe)
{
wantedPieces.remove(p);
wantedBytes -= metainfo.getPieceLength(p.getId());
} // synch
} catch (IOException ioe) {
String msg = "Error writing storage (piece " + piece + ") for " + metainfo.getName() + ": " + ioe;
_log.error(msg, ioe);
if (listener != null) {
listener.addMessage(msg);
listener.addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
}
// deadlock was here
snark.stopTorrent();
throw new RuntimeException(msg, ioe);
}
wantedPieces.remove(p);
wantedBytes -= metainfo.getPieceLength(p.getId());
}
}
// just in case
removePartialPiece(piece);
@ -1134,8 +1156,9 @@ class PeerCoordinator implements PeerListener
*
* Also mark the piece unrequested if this peer was the only one.
*
* @param peer partials, must include the zero-offset (empty) ones too
* No dup pieces, piece.setDownloaded() must be set
* @param peer partials, must include the zero-offset (empty) ones too.
* No dup pieces, piece.setDownloaded() must be set.
* len field in Requests is ignored.
* @since 0.8.2
*/
public void savePartialPieces(Peer peer, List<Request> partials)

View File

@ -21,6 +21,7 @@
package org.klomp.snark;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@ -155,12 +156,25 @@ class PeerState implements DataLoader
setInteresting(true);
}
void bitfieldMessage(byte[] bitmap)
{
synchronized(this)
{
if (_log.shouldLog(Log.DEBUG))
_log.debug(peer + " rcv bitfield");
void bitfieldMessage(byte[] bitmap) {
bitfieldMessage(bitmap, false);
}
/**
* @param bitmap null to use the isAll param
* @param isAll only if bitmap == null: true for have_all, false for have_none
* @since 0.9.21
*/
private void bitfieldMessage(byte[] bitmap, boolean isAll) {
if (_log.shouldLog(Log.DEBUG)) {
if (bitmap != null)
_log.debug(peer + " rcv bitfield bytes: " + bitmap.length);
else if (isAll)
_log.debug(peer + " rcv bitfield HAVE_ALL");
else
_log.debug(peer + " rcv bitfield HAVE_NONE");
}
synchronized(this) {
if (bitfield != null)
{
// XXX - Be liberal in what you accept?
@ -172,10 +186,24 @@ class PeerState implements DataLoader
// XXX - Check for weird bitfield and disconnect?
// FIXME will have to regenerate the bitfield after we know exactly
// how many pieces there are, as we don't know how many spare bits there are.
if (metainfo == null)
bitfield = new BitField(bitmap, bitmap.length * 8);
else
bitfield = new BitField(bitmap, metainfo.getPieces());
if (metainfo == null) {
if (bitmap != null) {
bitfield = new BitField(bitmap, bitmap.length * 8);
} else {
// we can't handle this situation
if (_log.shouldLog(Log.WARN))
_log.warn("have_x w/o metainfo: " + isAll);
return;
}
} else {
if (bitmap != null) {
bitfield = new BitField(bitmap, metainfo.getPieces());
} else {
bitfield = new BitField(metainfo.getPieces());
if (isAll)
bitfield.setAll();
}
}
}
if (metainfo == null)
return;
@ -198,14 +226,21 @@ class PeerState implements DataLoader
+ piece + ", " + begin + ", " + length + ") ");
if (metainfo == null)
return;
if (choking)
{
if (_log.shouldLog(Log.INFO))
_log.info("Request received, but choking " + peer);
if (choking) {
if (peer.supportsFast()) {
if (_log.shouldInfo())
_log.info("Request received, sending reject to choked " + peer);
out.sendReject(piece, begin, length);
} else {
if (_log.shouldInfo())
_log.info("Request received, but choking " + peer);
}
return;
}
}
// Sanity check
// There is no check here that we actually have the piece;
// this will be caught in loadData() below
if (piece < 0
|| piece >= metainfo.getPieces()
|| begin < 0
@ -219,6 +254,8 @@ class PeerState implements DataLoader
+ ", " + begin
+ ", " + length
+ "' message from " + peer);
if (peer.supportsFast())
out.sendReject(piece, begin, length);
return;
}
@ -227,8 +264,14 @@ class PeerState implements DataLoader
// Todo: limit number of requests also? (robert 64 x 4KB)
if (out.queuedBytes() + length > MAX_PIPELINE_BYTES)
{
if (_log.shouldLog(Log.WARN))
_log.warn("Discarding request over pipeline limit from " + peer);
if (peer.supportsFast()) {
if (_log.shouldWarn())
_log.warn("Rejecting request over pipeline limit from " + peer);
out.sendReject(piece, begin, length);
} else {
if (_log.shouldWarn())
_log.warn("Discarding request over pipeline limit from " + peer);
}
return;
}
@ -243,7 +286,8 @@ class PeerState implements DataLoader
/**
* This is the callback that PeerConnectionOut calls
*
* @return bytes or null for errors
* @return bytes or null for errors such as not having the piece yet
* @throws RuntimeException on IOE getting the data
* @since 0.8.2
*/
public ByteArray loadData(int piece, int begin, int length) {
@ -253,6 +297,8 @@ class PeerState implements DataLoader
// XXX - Protocol error-> diconnect?
if (_log.shouldLog(Log.WARN))
_log.warn("Got request for unknown piece: " + piece);
if (peer.supportsFast())
out.sendReject(piece, begin, length);
return null;
}
@ -265,6 +311,8 @@ class PeerState implements DataLoader
+ ", " + begin
+ ", " + length
+ "' message from " + peer);
if (peer.supportsFast())
out.sendReject(piece, begin, length);
return null;
}
@ -322,6 +370,11 @@ class PeerState implements DataLoader
{
if (_log.shouldLog(Log.WARN))
_log.warn("Got BAD " + req.getPiece() + " from " + peer);
synchronized(this) {
// so we don't ask again
if (bitfield != null)
bitfield.clear(req.getPiece());
}
}
}
@ -455,7 +508,12 @@ class PeerState implements DataLoader
for (Integer p : pcs) {
Request req = getLowestOutstandingRequest(p.intValue());
if (req != null) {
req.getPartialPiece().setDownloaded(req.off);
PartialPiece pp = req.getPartialPiece();
synchronized(pp) {
int dl = pp.getDownloaded();
if (req.off != dl)
req = new Request(pp, dl, 1);
}
rv.add(req);
}
}
@ -536,6 +594,89 @@ class PeerState implements DataLoader
listener.gotPort(peer, port, port + 1);
}
/////////// fast message handlers /////////
/**
* BEP 6
* Treated as "have" for now
* @since 0.9.21
*/
void suggestMessage(int piece) {
if (_log.shouldInfo())
_log.info("Handling suggest as have(" + piece + ") from " + peer);
haveMessage(piece);
}
/**
* BEP 6
* @param isAll true for have_all, false for have_none
* @since 0.9.21
*/
void haveMessage(boolean isAll) {
bitfieldMessage(null, isAll);
}
/**
* BEP 6
* If the peer rejects lower chunks but not higher ones, thus creating holes,
* we won't figure it out and the piece will fail, since we don't currently
* keep a chunk bitmap in PartialPiece.
* As long as the peer rejects all the chunks, or rejects only the last chunks,
* no holes are created and we will be fine. The reject messages may be in any order,
* just don't make a hole when it's over.
*
* @since 0.9.21
*/
void rejectMessage(int piece, int begin, int length) {
if (_log.shouldInfo())
_log.info("Got reject(" + piece + ',' + begin + ',' + length + ") from " + peer);
out.cancelRequest(piece, begin, length);
synchronized(this) {
Request deletedRequest = null;
// for this piece only
boolean haveMoreRequests = false;
for (Iterator<Request> iter = outstandingRequests.iterator(); iter.hasNext(); ) {
Request req = iter.next();
if (req.getPiece() == piece) {
if (req.off == begin && req.len == length) {
iter.remove();
deletedRequest = req;
} else {
haveMoreRequests = true;
}
}
}
if (deletedRequest != null && !haveMoreRequests) {
// We must return the piece to the coordinator
// Create a new fake request so we can set the offset correctly
PartialPiece pp = deletedRequest.getPartialPiece();
int downloaded = pp.getDownloaded();
Request req;
if (deletedRequest.off == downloaded)
req = deletedRequest;
else
req = new Request(pp, downloaded, 1);
List<Request> pcs = Collections.singletonList(req);
listener.savePartialPieces(this.peer, pcs);
if (_log.shouldWarn())
_log.warn("Returned to coord. w/ offset " + pp.getDownloaded() + " due to reject(" + piece + ',' + begin + ',' + length + ") from " + peer);
}
if (lastRequest != null && lastRequest.getPiece() == piece &&
lastRequest.off == begin && lastRequest.len == length)
lastRequest = null;
}
}
/**
* BEP 6
* Ignored for now
* @since 0.9.21
*/
void allowedFastMessage(int piece) {
if (_log.shouldInfo())
_log.info("Ignoring allowed_fast(" + piece + ") from " + peer);
}
void unknownMessage(int type, byte[] bs)
{
if (_log.shouldLog(Log.WARN))
@ -543,6 +684,8 @@ class PeerState implements DataLoader
+ " length: " + bs.length);
}
/////////// end message handlers /////////
/**
* We now have this piece.
* Tell the peer and cancel any requests for the piece.

View File

@ -43,13 +43,13 @@ class Request
*/
Request(PartialPiece piece, int off, int len)
{
// Sanity check
if (off < 0 || len <= 0 || off + len > piece.getLength())
throw new IndexOutOfBoundsException("Illegal Request " + toString());
this.piece = piece;
this.off = off;
this.len = len;
// Sanity check
if (off < 0 || len <= 0 || off + len > piece.getLength())
throw new IndexOutOfBoundsException("Illegal Request " + toString());
}
/**

View File

@ -290,6 +290,7 @@ public class Snark
/**
* multitorrent
* @throws RuntimeException via fatal()
*/
public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
StorageListener slistener, CoordinatorListener clistener,
@ -304,6 +305,7 @@ public class Snark
* multitorrent
*
* @param baseFile if null, use rootDir/torrentName; if non-null, use it instead
* @throws RuntimeException via fatal()
* @since 0.9.11
*/
public Snark(I2PSnarkUtil util, String torrent, String ip, int user_port,
@ -478,6 +480,7 @@ public class Snark
* @param torrent a fake name for now (not a file name)
* @param ih 20-byte info hash
* @param trackerURL may be null
* @throws RuntimeException via fatal()
* @since 0.8.4
*/
public Snark(I2PSnarkUtil util, String torrent, byte[] ih, String trackerURL,
@ -531,6 +534,8 @@ public class Snark
/**
* Start up contacting peers and querying the tracker.
* Blocks if tunnel is not yet open.
*
* @throws RuntimeException via fatal()
*/
public synchronized void startTorrent() {
starting = true;
@ -612,7 +617,6 @@ public class Snark
* @since 0.9.1
*/
public synchronized void stopTorrent(boolean fast) {
stopped = true;
TrackerClient tc = trackerclient;
if (tc != null)
tc.halt(fast);
@ -620,17 +624,28 @@ public class Snark
if (pc != null)
pc.halt();
Storage st = storage;
if (!fast)
// HACK: Needed a way to distinguish between user-stop and
// shutdown-stop. stopTorrent(true) is in stopAllTorrents().
// (#766)
stopped = true;
if (st != null) {
boolean changed = storage.isChanged() || getUploaded() != savedUploaded;
// TODO: Cache the config-in-mem to compare vs config-on-disk
// (needed for auto-save to not double-save in some cases)
//boolean changed = storage.isChanged() || getUploaded() != savedUploaded;
boolean changed = true;
if (changed && completeListener != null)
completeListener.updateStatus(this);
try {
storage.close();
} catch (IOException ioe) {
System.out.println("Error closing " + torrent);
ioe.printStackTrace();
}
if (changed && completeListener != null)
completeListener.updateStatus(this);
}
if (fast)
// HACK: See above if(!fast)
stopped = true;
if (pc != null && _peerCoordinatorSet != null)
_peerCoordinatorSet.remove(pc);
if (_peerCoordinatorSet == null)
@ -730,6 +745,18 @@ public class Snark
return storage != null && storage.isChecking();
}
/**
* If checking is in progress, return completion 0.0 ... 1.0,
* else return 1.0.
* @since 0.9.23
*/
public double getCheckingProgress() {
if (storage != null && storage.isChecking())
return storage.getCheckingProgress();
else
return 1.0d;
}
/**
* Disk allocation (ballooning) in progress.
* @since 0.9.3
@ -884,6 +911,30 @@ public class Snark
return -1;
}
/**
* Bytes not received and set to skipped.
* This is not the same as the total of all skipped files,
* since pieces may span multiple files.
*
* @return exact value. or 0 if no storage yet.
* @since 0.9.24
*/
public long getSkippedLength() {
PeerCoordinator coord = coordinator;
if (coord != null) {
// fast way
long r = getRemainingLength();
if (r <= 0)
return 0;
long n = coord.getNeededLength();
return r - n;
} else if (storage != null) {
// slow way
return storage.getSkippedLength();
}
return 0;
}
/**
* Does not account (i.e. includes) for skipped files.
* @return number of pieces still needed (magnet mode or not), or -1 if unknown
@ -1249,7 +1300,8 @@ public class Snark
public void setWantedPieces(Storage storage)
{
coordinator.setWantedPieces();
if (coordinator != null)
coordinator.setWantedPieces();
}
///////////// End StorageListener methods
@ -1258,7 +1310,7 @@ public class Snark
/** SnarkSnutdown callback unused */
public void shutdown()
{
// Should not be necessary since all non-deamon threads should
// Should not be necessary since all non-daemon threads should
// have died. But in reality this does not always happen.
//System.exit(0);
}
@ -1277,7 +1329,7 @@ public class Snark
* coordinatorListener
*/
final static int MIN_TOTAL_UPLOADERS = 4;
final static int MAX_TOTAL_UPLOADERS = 10;
final static int MAX_TOTAL_UPLOADERS = 20;
public boolean overUploadLimit(int uploaders) {
if (_peerCoordinatorSet == null || uploaders <= 0)

File diff suppressed because it is too large Load Diff

View File

@ -20,6 +20,7 @@
package org.klomp.snark;
import java.io.Closeable;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@ -38,6 +39,8 @@ import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import gnu.getopt.Getopt;
import net.i2p.I2PAppContext;
import net.i2p.crypto.SHA1;
import net.i2p.data.ByteArray;
@ -50,7 +53,7 @@ import net.i2p.util.SystemVersion;
/**
* Maintains pieces on disk. Can be used to store and retrieve pieces.
*/
public class Storage
public class Storage implements Closeable
{
private final MetaInfo metainfo;
private final List<TorrentFile> _torrentFiles;
@ -70,18 +73,20 @@ public class Storage
private boolean changed;
private volatile boolean _isChecking;
private final AtomicInteger _allocateCount = new AtomicInteger();
private final AtomicInteger _checkProgress = new AtomicInteger();
/** The default piece size. */
private static final int DEFAULT_PIECE_SIZE = 256*1024;
/** bigger than this will be rejected */
public static final int MAX_PIECE_SIZE = 8*1024*1024;
public static final int MAX_PIECE_SIZE = 16*1024*1024;
/** The maximum number of pieces in a torrent. */
public static final int MAX_PIECES = 10*1024;
public static final int MAX_PIECES = 32*1024;
public static final long MAX_TOTAL_SIZE = MAX_PIECE_SIZE * (long) MAX_PIECES;
private static final Map<String, String> _filterNameCache = new ConcurrentHashMap<String, String>();
private static final boolean _isWindows = SystemVersion.isWindows();
private static final boolean _isARM = SystemVersion.isARM();
private static final int BUFSIZE = PeerState.PARTSIZE;
private static final ByteCache _cache = ByteCache.getInstance(16, BUFSIZE);
@ -122,10 +127,12 @@ public class Storage
*
* @param announce may be null
* @param listener may be null
* @param created_by may be null
* @throws IOException when creating and/or checking files fails.
*/
public Storage(I2PSnarkUtil util, File baseFile, String announce,
List<List<String>> announce_list,
String created_by,
boolean privateTorrent, StorageListener listener)
throws IOException
{
@ -159,7 +166,7 @@ public class Storage
else
pc_size = DEFAULT_PIECE_SIZE;
int pcs = (int) ((total - 1)/pc_size) + 1;
while (pcs > (MAX_PIECES * 2 / 3) && pc_size < MAX_PIECE_SIZE)
while (pcs > (MAX_PIECES / 3) && pc_size < MAX_PIECE_SIZE)
{
pc_size *= 2;
pcs = (int) ((total - 1)/pc_size) +1;
@ -194,7 +201,7 @@ public class Storage
byte[] piece_hashes = fast_digestCreate();
metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
lengthsList, piece_size, piece_hashes, total, privateTorrent,
announce_list);
announce_list, created_by);
}
@ -306,6 +313,18 @@ public class Storage
return _isChecking;
}
/**
* If checking is in progress, return completion 0.0 ... 1.0,
* else return 1.0.
* @since 0.9.23
*/
public double getCheckingProgress() {
if (_isChecking)
return _checkProgress.get() / (double) pieces;
else
return 1.0d;
}
/**
* Disk allocation (ballooning) in progress.
* Always false on Windows.
@ -336,29 +355,28 @@ public class Storage
* @return number of bytes remaining; -1 if unknown file
* @since 0.7.14
*/
/****
public long remaining(int fileIndex) {
if (fileIndex < 0 || fileIndex >= _torrentFiles.size())
return -1;
if (complete())
return 0;
long bytes = 0;
for (int i = 0; i < _torrentFiles.size(); i++) {
TorrentFile tf = _torrentFiles.get(i);
if (i == fileIndex) {
File f = tf.RAFfile;
if (complete())
return 0;
int psz = piece_size;
long start = bytes;
long end = start + tf.length;
int pc = (int) (bytes / psz);
int pc = (int) (bytes / piece_size);
long rv = 0;
if (!bitfield.get(pc))
rv = Math.min(psz - (start % psz), tf.length);
for (int j = pc + 1; (((long)j) * psz) < end && j < pieces; j++) {
rv = Math.min(piece_size - (start % piece_size), tf.length);
for (int j = pc + 1; (((long)j) * piece_size) < end && j < pieces; j++) {
if (!bitfield.get(j)) {
if (((long)(j+1))*psz < end)
rv += psz;
if (((long)(j+1))*piece_size < end)
rv += piece_size;
else
rv += end - (((long)j) * psz);
rv += end - (((long)j) * piece_size);
}
}
return rv;
@ -367,6 +385,40 @@ public class Storage
}
return -1;
}
****/
/**
* For efficiency, calculate remaining bytes for all files at once
*
* @return number of bytes remaining for each file, use indexOf() to get index for a file
* @since 0.9.23
*/
public long[] remaining() {
long[] rv = new long[_torrentFiles.size()];
if (complete())
return rv;
long bytes = 0;
for (int i = 0; i < _torrentFiles.size(); i++) {
TorrentFile tf = _torrentFiles.get(i);
long start = bytes;
long end = start + tf.length;
int pc = (int) (bytes / piece_size);
long rvi = 0;
if (!bitfield.get(pc))
rvi = Math.min(piece_size - (start % piece_size), tf.length);
for (int j = pc + 1; (((long)j) * piece_size) < end && j < pieces; j++) {
if (!bitfield.get(j)) {
if (((long)(j+1))*piece_size < end)
rvi += piece_size;
else
rvi += end - (((long)j) * piece_size);
}
}
rv[i] = rvi;
bytes += tf.length;
}
return rv;
}
/**
* @param fileIndex as obtained from indexOf
@ -450,9 +502,8 @@ public class Storage
int file = 0;
long pcEnd = -1;
long fileEnd = _torrentFiles.get(0).length - 1;
int psz = piece_size;
for (int i = 0; i < rv.length; i++) {
pcEnd += psz;
pcEnd += piece_size;
int pri = _torrentFiles.get(file).priority;
while (fileEnd <= pcEnd && file < _torrentFiles.size() - 1) {
file++;
@ -467,6 +518,31 @@ public class Storage
return rv;
}
/**
* Call setPriority() for all changed files first,
* then call this.
* The length of all the pieces that are not yet downloaded,
* and are set to skipped.
* This is not the same as the total of all skipped files,
* since pieces may span multiple files.
*
* @return 0 on error, if complete, or if only one file
* @since 0.9.24
*/
public long getSkippedLength() {
int[] pri = getPiecePriorities();
if (pri == null)
return 0;
long rv = 0;
final int end = pri.length - 1;
for (int i = 0; i <= end; i++) {
if (pri[i] <= -9 && !bitfield.get(i)) {
rv += (i != end) ? piece_size : metainfo.getPieceLength(i);
}
}
return rv;
}
/**
* The BitField that tells which pieces this storage contains.
* Do not change this since this is the current state of the storage.
@ -496,6 +572,9 @@ public class Storage
/**
* Creates (and/or checks) all files from the metainfo file list.
* Only call this once, and only after the constructor with the metainfo.
* Use recheck() to check again later.
*
* @throws IllegalStateException if called more than once
*/
public void check() throws IOException
{
@ -506,6 +585,9 @@ public class Storage
* Creates (and/or checks) all files from the metainfo file list.
* Use a saved bitfield and timestamp from a config file.
* Only call this once, and only after the constructor with the metainfo.
* Use recheck() to check again later.
*
* @throws IllegalStateException if called more than once
*/
public void check(long savedTime, BitField savedBitField) throws IOException
{
@ -691,7 +773,7 @@ public class Storage
}
rv = repl;
}
} catch (Exception ex) {
} catch (RuntimeException ex) {
ex.printStackTrace();
}
}
@ -763,6 +845,14 @@ public class Storage
return rv;
}
/**
* Does not include directories.
* @since 0.9.23
*/
public int getFileCount() {
return _torrentFiles.size();
}
/**
* Includes the base for a multi-file torrent.
* Sorted bottom-up for easy deletion.
@ -784,6 +874,24 @@ public class Storage
return rv;
}
/**
* Blocking. Holds lock.
* Recommend running only when stopped.
* Caller should thread.
* Calls listener.setWantedPieces() on completion if anything changed.
*
* @return true if anything changed, false otherwise
* @since 0.9.23
*/
public boolean recheck() throws IOException {
int previousNeeded = needed;
checkCreateFiles(true);
boolean changed = previousNeeded != needed;
if (listener != null && changed)
listener.setWantedPieces(this);
return changed;
}
/**
* This is called at the beginning, and at presumed completion,
* so we have to be careful about locking.
@ -808,6 +916,7 @@ public class Storage
private void locked_checkCreateFiles(boolean recheck) throws IOException
{
_checkProgress.set(0);
// Whether we are resuming or not,
// if any of the files already exists we assume we are resuming.
boolean resume = false;
@ -824,13 +933,16 @@ public class Storage
// Make sure all files are available and of correct length
// The files should all exist as they have been created with zero length by createFilesFromNames()
long lengthProgress = 0;
for (TorrentFile tf : _torrentFiles)
{
long length = tf.RAFfile.length();
lengthProgress += tf.length;
if(tf.RAFfile.exists() && length == tf.length)
{
if (listener != null)
listener.storageAllocated(this, length);
_checkProgress.set(0);
resume = true; // XXX Could dynamicly check
}
else if (length == 0) {
@ -842,6 +954,8 @@ public class Storage
tf.closeRAF();
} catch (IOException ioe) {}
}
if (!resume)
_checkProgress.set((int) (pieces * lengthProgress / total_length));
} else {
String msg = "File '" + tf.name + "' exists, but has wrong length (expected " +
tf.length + " but found " + length + ") - repairing corruption";
@ -850,6 +964,7 @@ public class Storage
_log.error(msg);
changed = true;
resume = true;
_checkProgress.set(0);
_probablyComplete = false; // to force RW
synchronized(tf) {
RandomAccessFile raf = tf.checkRAF();
@ -870,17 +985,16 @@ public class Storage
long pieceEnd = 0;
for (int i = 0; i < pieces; i++)
{
_checkProgress.set(i);
int length = getUncheckedPiece(i, piece);
boolean correctHash = metainfo.checkPiece(i, piece, 0, length);
// close as we go so we don't run out of file descriptors
pieceEnd += length;
while (fileEnd <= pieceEnd) {
TorrentFile tf = _torrentFiles.get(file);
synchronized(tf) {
try {
tf.closeRAF();
} catch (IOException ioe) {}
}
try {
tf.closeRAF();
} catch (IOException ioe) {}
if (++file >= _torrentFiles.size())
break;
fileEnd += _torrentFiles.get(file).length;
@ -896,6 +1010,7 @@ public class Storage
}
}
_checkProgress.set(pieces);
_probablyComplete = complete();
// close all the files so we don't end up with a zillion open ones;
// we will reopen as needed
@ -952,9 +1067,7 @@ public class Storage
for (TorrentFile tf : _torrentFiles)
{
try {
synchronized(tf) {
tf.closeRAF();
}
} catch (IOException ioe) {
_log.error("Error closing " + tf, ioe);
// gobble gobble
@ -1179,17 +1292,15 @@ public class Storage
return length;
}
private static final long RAFCloseDelay = 4*60*1000;
private static final long RAF_CLOSE_DELAY = 4*60*1000;
/**
* Close unused RAFs - call periodically
*/
public void cleanRAFs() {
long cutoff = System.currentTimeMillis() - RAFCloseDelay;
long cutoff = System.currentTimeMillis() - RAF_CLOSE_DELAY;
for (TorrentFile tf : _torrentFiles) {
synchronized(tf) {
tf.closeRAF(cutoff);
}
}
}
@ -1315,7 +1426,9 @@ public class Storage
// Windows will zero-fill up to the point of the write, which
// will make the file fairly unfragmented, on average, at least until
// near the end where it will get exponentially more fragmented.
if (!_isWindows)
// Also don't ballon on ARM, as a proxy for solid state disk, where fragmentation doesn't matter too much.
// Actual detection of SSD is almost impossible.
if (!_isWindows && !_isARM)
isSparse = true;
}
@ -1372,18 +1485,44 @@ public class Storage
* @since 0.9.4
*/
public static void main(String[] args) {
if (args.length < 1 || args.length > 2) {
System.err.println("Usage: Storage file-or-dir [announceURL]");
boolean error = false;
String created_by = null;
String announce = null;
Getopt g = new Getopt("Storage", args, "a:c:");
try {
int c;
while ((c = g.getopt()) != -1) {
switch (c) {
case 'a':
announce = g.getOptarg();
break;
case 'c':
created_by = g.getOptarg();
break;
case '?':
case ':':
default:
error = true;
break;
} // switch
} // while
} catch (RuntimeException e) {
e.printStackTrace();
error = true;
}
if (error || args.length - g.getOptind() != 1) {
System.err.println("Usage: Storage [-a announceURL] [-c created-by] file-or-dir");
System.exit(1);
}
File base = new File(args[0]);
String announce = args.length == 2 ? args[1] : null;
File base = new File(args[g.getOptind()]);
I2PAppContext ctx = I2PAppContext.getGlobalContext();
I2PSnarkUtil util = new I2PSnarkUtil(ctx);
File file = null;
FileOutputStream out = null;
try {
Storage storage = new Storage(util, base, announce, null, false, null);
Storage storage = new Storage(util, base, announce, null, created_by, false, null);
MetaInfo meta = storage.getMetaInfo();
file = new File(storage.getBaseName() + ".torrent");
out = new FileOutputStream(file);

View File

@ -23,8 +23,8 @@ package org.klomp.snark;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -875,18 +875,20 @@ public class TrackerClient implements Runnable {
}
/**
* @param ann an announce URL
* @param ann an announce URL, may be null, returns false if null
* @return true for i2p hosts only
* @since 0.7.12
*/
public static boolean isValidAnnounce(String ann) {
URL url;
if (ann == null)
return false;
URI url;
try {
url = new URL(ann);
} catch (MalformedURLException mue) {
return false;
url = new URI(ann);
} catch (URISyntaxException use) {
return false;
}
return url.getProtocol().equals("http") &&
return "http".equals(url.getScheme()) && url.getHost() != null &&
(url.getHost().endsWith(".i2p") || url.getHost().equals("i2p"));
}
@ -896,15 +898,17 @@ public class TrackerClient implements Runnable {
* @since 0.9.5
*/
private static Hash getHostHash(String ann) {
URL url;
URI url;
try {
url = new URL(ann);
} catch (MalformedURLException mue) {
url = new URI(ann);
} catch (URISyntaxException use) {
return null;
}
if (!url.getProtocol().equals("http"))
if (!"http".equals(url.getScheme()))
return null;
String host = url.getHost();
if (host == null)
return null;
if (host.endsWith(".i2p"))
return ConvertToHash.getHash(host);
if (host.equals("i2p")) {
@ -912,7 +916,7 @@ public class TrackerClient implements Runnable {
if (path == null || path.length() < 517 ||
!path.startsWith("/"))
return null;
String[] parts = path.substring(1).split("[/\\?&;]", 2);
String[] parts = DataHelper.split(path.substring(1), "[/\\?&;]", 2);
return ConvertToHash.getHash(parts[0]);
}
return null;

View File

@ -25,6 +25,7 @@ import java.util.List;
import java.util.Map;
import net.i2p.data.Base64;
import net.i2p.data.DataHelper;
/**
* Holds different types that a bencoded byte array can represent.
@ -208,7 +209,7 @@ public class BEValue
} else if (bin) {
buf.append(bs.length).append(" bytes: ").append(Base64.encode(bs));
} else {
buf.append('"').append(new String(bs)).append('"');
buf.append('"').append(DataHelper.getUTF8(bs)).append('"');
}
valueString = buf.toString();
} else

View File

@ -106,6 +106,7 @@ class DHTTracker {
* @param noSeeds true if we do not want seeds in the result
* @return list or empty list (never null)
*/
@SuppressWarnings({"unchecked", "rawtypes"})
List<Hash> getPeers(InfoHash ih, int max, boolean noSeeds) {
Peers peers = _torrents.get(ih);
if (peers == null || max <= 0)

View File

@ -40,6 +40,7 @@ import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
import org.klomp.snark.I2PSnarkUtil;
import org.klomp.snark.SnarkManager;
import org.klomp.snark.TrackerClient;
import org.klomp.snark.bencode.BDecoder;
@ -128,8 +129,10 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
/** Max number of nodes to return. BEP 5 says 8 */
private static final int K = 8;
/** Max number of peers to return. BEP 5 doesn't say. We'll use the same as I2PSnarkUtil.MAX_CONNECTIONS */
private static final int MAX_WANT = 16;
/** Max number of peers to return. BEP 5 doesn't say.
* We'll use more than I2PSnarkUtil.MAX_CONNECTIONS since lots could be old.
*/
private static final int MAX_WANT = I2PSnarkUtil.MAX_CONNECTIONS * 3 / 2;
/** overloads error codes which start with 201 */
private static final int REPLY_NONE = 0;
@ -243,6 +246,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
* @param maxWait how long to wait for each to reply (not total) must be > 0
* @param parallel how many outstanding at once (unimplemented, always 1)
*/
@SuppressWarnings("unchecked")
private void explore(NID target, int maxNodes, long maxWait, int parallel) {
List<NodeInfo> nodes = _knownNodes.findClosest(target, maxNodes);
if (nodes.isEmpty()) {
@ -327,6 +331,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
* @param noSeeds true if we do not want seeds in the result
* @return possibly empty (never null)
*/
@SuppressWarnings("unchecked")
public Collection<Hash> getPeersAndAnnounce(byte[] ih, int max, long maxWait,
int annMax, long annMaxWait,
boolean isSeed, boolean noSeeds) {
@ -858,6 +863,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
* @param repliable true for all but announce
* @return null on error
*/
@SuppressWarnings("unchecked")
private ReplyWaiter sendQuery(NodeInfo nInfo, Map<String, Object> map, boolean repliable) {
if (nInfo.equals(_myNodeInfo))
throw new IllegalArgumentException("wtf don't send to ourselves");
@ -907,6 +913,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
* @param toPort the query port, we will increment here
* @return success
*/
@SuppressWarnings("unchecked")
private boolean sendResponse(NodeInfo nInfo, MsgID msgID, Map<String, Object> map) {
if (nInfo.equals(_myNodeInfo))
throw new IllegalArgumentException("wtf don't send to ourselves");
@ -1408,7 +1415,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
private List<Hash> receivePeers(NodeInfo nInfo, List<BEValue> peers) throws InvalidBEncodingException {
if (_log.shouldLog(Log.INFO))
_log.info("Rcvd peers from: " + nInfo);
int max = Math.min(MAX_WANT, peers.size());
int max = Math.min(MAX_WANT * 2, peers.size());
List<Hash> rv = new ArrayList<Hash>(max);
for (BEValue bev : peers) {
byte[] b = bev.getBytes();

View File

@ -102,7 +102,7 @@ class NodeInfo extends SimpleDataStructure {
*/
public NodeInfo(String s) throws DataFormatException {
super();
String[] parts = s.split(":", 4);
String[] parts = DataHelper.split(s, ":", 4);
if (parts.length != 4)
throw new DataFormatException("Bad format");
byte[] nid = Base64.decode(parts[0]);
@ -225,7 +225,7 @@ class NodeInfo extends SimpleDataStructure {
NodeInfo ni = (NodeInfo) o;
// assume dest matches, ignore it
return this.hash.equals(ni.hash) && nID.equals(ni.nID) && port == ni.port;
} catch (Exception e) {
} catch (RuntimeException e) {
return false;
}
}

View File

@ -77,7 +77,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
_log = ctx.logManager().getLog(FetchAndAdd.class);
_mgr = mgr;
_url = url;
_name = _("Download torrent file from {0}", url);
_name = _t("Download torrent file from {0}", url);
_dataDir = dataDir;
byte[] fake = null;
try {
@ -90,7 +90,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
* Set off by startTorrent()
*/
public void run() {
_mgr.addMessageNoEscape(_("Fetching {0}", urlify(_url)));
_mgr.addMessageNoEscape(_t("Fetching {0}", urlify(_url)));
File file = get();
if (!_isRunning) // stopped?
return;
@ -100,7 +100,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
_mgr.deleteMagnet(this);
add(file);
} else {
_mgr.addMessageNoEscape(_("Torrent was not retrieved from {0}", urlify(_url)) +
_mgr.addMessageNoEscape(_t("Torrent was not retrieved from {0}", urlify(_url)) +
((_failCause != null) ? (": " + DataHelper.stripHTML(_failCause)) : ""));
}
if (file != null)
@ -127,7 +127,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
out.deleteOnExit();
if (!_mgr.util().connected()) {
_mgr.addMessage(_("Opening the I2P tunnel"));
_mgr.addMessage(_t("Opening the I2P tunnel"));
if (!_mgr.util().connect())
return null;
}
@ -154,7 +154,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
* This Snark may then be deleted.
*/
private void add(File file) {
_mgr.addMessageNoEscape(_("Torrent fetched from {0}", urlify(_url)));
_mgr.addMessageNoEscape(_t("Torrent fetched from {0}", urlify(_url)));
FileInputStream in = null;
try {
in = new FileInputStream(file);
@ -163,7 +163,7 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
try { in.close(); } catch (IOException ioe) {}
Snark snark = _mgr.getTorrentByInfoHash(fileInfoHash);
if (snark != null) {
_mgr.addMessage(_("Torrent with this info hash is already running: {0}", snark.getBaseName()));
_mgr.addMessage(_t("Torrent with this info hash is already running: {0}", snark.getBaseName()));
return;
}
@ -175,9 +175,9 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
if (torrentFile.exists()) {
if (_mgr.getTorrent(canonical) != null)
_mgr.addMessage(_("Torrent already running: {0}", name));
_mgr.addMessage(_t("Torrent already running: {0}", name));
else
_mgr.addMessage(_("Torrent already in the queue: {0}", name));
_mgr.addMessage(_t("Torrent already in the queue: {0}", name));
} else {
// This may take a LONG time to create the storage.
_mgr.copyAndAddTorrent(file, canonical, _dataDir);
@ -188,9 +188,9 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
throw new IOException("Unknown error - check logs");
}
} catch (IOException ioe) {
_mgr.addMessageNoEscape(_("Torrent at {0} was not valid", urlify(_url)) + ": " + DataHelper.stripHTML(ioe.getMessage()));
_mgr.addMessageNoEscape(_t("Torrent at {0} was not valid", urlify(_url)) + ": " + DataHelper.stripHTML(ioe.getMessage()));
} catch (OutOfMemoryError oom) {
_mgr.addMessageNoEscape(_("ERROR - Out of memory, cannot create torrent from {0}", urlify(_url)) + ": " + DataHelper.stripHTML(oom.getMessage()));
_mgr.addMessageNoEscape(_t("ERROR - Out of memory, cannot create torrent from {0}", urlify(_url)) + ": " + DataHelper.stripHTML(oom.getMessage()));
} finally {
try { if (in != null) in.close(); } catch (IOException ioe) {}
}
@ -345,11 +345,11 @@ public class FetchAndAdd extends Snark implements EepGet.StatusListener, Runnabl
// End of EepGet status listeners
private String _(String s) {
private String _t(String s) {
return _mgr.util().getString(s);
}
private String _(String s, String o) {
private String _t(String s, String o) {
return _mgr.util().getString(s, o);
}

View File

@ -6,6 +6,8 @@ import java.text.Collator;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.klomp.snark.MetaInfo;
import org.klomp.snark.Snark;
@ -18,6 +20,13 @@ import org.klomp.snark.Storage;
*/
class Sorters {
/**
* See below
*/
private static final Pattern PATTERN_DE, PATTERN_EN, PATTERN_ES, PATTERN_FR,
PATTERN_IT, PATTERN_NL, PATTERN_PT;
private static Pattern _pattern;
/**
* Negative is reverse
*
@ -113,8 +122,8 @@ class Sorters {
/**
* Sort alphabetically in current locale, ignore case, ignore leading "the "
* (I guess this is worth it, a lot of torrents start with "The "
* Sort alphabetically in current locale, ignore case, ignore leading
* articles such as "the" if the pattern is set by setPattern()
* @since 0.7.14
*/
private static class TorrentNameComparator implements Comparator<Snark>, Serializable {
@ -130,13 +139,16 @@ class Sorters {
if (l.getStorage() != null && r.getStorage() == null)
return 1;
String ls = l.getBaseName();
String llc = ls.toLowerCase(Locale.US);
if (llc.startsWith("the ") || llc.startsWith("the.") || llc.startsWith("the_"))
ls = ls.substring(4);
String rs = r.getBaseName();
String rlc = rs.toLowerCase(Locale.US);
if (rlc.startsWith("the ") || rlc.startsWith("the.") || rlc.startsWith("the_"))
rs = rs.substring(4);
Pattern p = _pattern;
if (p != null) {
Matcher m = p.matcher(ls);
if (m.matches())
ls = ls.substring(m.group(1).length());
m = p.matcher(rs);
if (m.matches())
rs = rs.substring(m.group(1).length());
}
return Collator.getInstance().compare(ls, rs);
}
}
@ -356,13 +368,14 @@ class Sorters {
/**
* @param storage may be null
* @param remainingArray precomputed, non-null iff storage is non-null
*/
public FileAndIndex(File file, Storage storage) {
public FileAndIndex(File file, Storage storage, long[] remainingArray) {
this.file = file;
index = storage != null ? storage.indexOf(file) : -1;
if (index >= 0) {
isDirectory = false;
remaining = storage.remaining(index);
remaining = remainingArray[index];
priority = storage.getPriority(index);
} else {
isDirectory = file.isDirectory();
@ -527,4 +540,104 @@ class Sorters {
return r.priority - l.priority;
}
}
/*
* Match an indefinite or definite article in the language,
* followed by one or more whitespace, '.', or '_'.
* Does not match "partitive" articles.
*
* https://en.wikipedia.org/wiki/Article_%28grammar%29
* http://www.loc.gov/marc/bibliographic/bdapndxf.html
*/
static {
PATTERN_DE = Pattern.compile(
// can't make the non-capturing innner group work
//"^((?:" +
"^((" +
"der|die|das|des|dem|den|ein|eine|einer|eines|einem|einen" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
PATTERN_EN = Pattern.compile(
"^((" +
"a|an|the" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
PATTERN_ES = Pattern.compile(
"^((" +
"el|la|lo|los|las|un|una|unos|unas" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
PATTERN_FR = Pattern.compile(
// note l' doesn't require whitespace after
"^(l'|((" +
"le|la|les|un|une|des" +
")[\\s\\._]+)).*",
Pattern.CASE_INSENSITIVE);
PATTERN_IT = Pattern.compile(
// note l' and un' don't require whitespace after
"^(l'|un'|((" +
"il|lo|la|i|gli|le|uno|una|un" +
")[\\s\\._]+)).*",
Pattern.CASE_INSENSITIVE);
PATTERN_NL = Pattern.compile(
"^((" +
"de|het|het'n|een|een'n" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
PATTERN_PT = Pattern.compile(
"^((" +
"o|a|os|as|um|uma|uns|umas" +
")[\\s\\._]+).*",
Pattern.CASE_INSENSITIVE);
}
/**
* Sets static field, oh well
* @param lang null for none
* @since 0.9.23
*/
public static void setPattern(String lang) {
Pattern p;
if (lang == null)
p = null;
else if (lang.equals("de"))
p = PATTERN_DE;
else if (lang.equals("en"))
p = PATTERN_EN;
else if (lang.equals("es"))
p = PATTERN_ES;
else if (lang.equals("fr"))
p = PATTERN_FR;
else if (lang.equals("it"))
p = PATTERN_IT;
else if (lang.equals("nl"))
p = PATTERN_NL;
else if (lang.equals("pt"))
p = PATTERN_PT;
else
p = null;
_pattern = p;
}
/****
public static final void main(String[] args) {
if (args.length != 2) {
System.out.println("Usage: Sorters lang 'string'");
System.exit(1);
}
String lang = args[0];
setPattern(lang);
if (_pattern == null) {
System.out.println("Unsupported " + lang);
System.exit(1);
}
String s = args[1];
Matcher m = _pattern.matcher(s);
if (m.matches()) {
System.out.println("Match is \"" + m.group(1) + '"');
} else {
System.out.println("No match for \"" + s + '"');
}
}
****/
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@
7z = application/x-7z-compressed
ape = audio/x-monkeys-audio
bz2 = application/x-bzip2
cue = application/x-cue
dmg = application/apple-diskimage
epub = application/epub+zip
flac = audio/flac
@ -11,6 +12,7 @@ iso = application/x-iso9660-image
m4a = audio/mp4a-latm
m4b = audio/mp4a-latm
m4v = video/x-m4v
mka = audio/x-matroska
mkv = video/x-matroska
mobi = application/x-mobipocket-ebook
mp4 = video/mp4
@ -31,3 +33,4 @@ war = application/java-archive
webm = video/webm
wma = audio/x-ms-wma
wmv = video/x-ms-wmv
xz = application/x-xz

View File

@ -1,6 +1,4 @@
To run I2PSnark from the command line, run "java -jar lib/i2psnark.jar", but
to run it with the web UI, run "launch-i2psnark". I2PSnark is
GPL'ed software, based on Snark (http://www.klomp.org/) to run on top of I2P
(http://www.i2p2.de/) within a webserver (such as the bundled Jetty from
http://jetty.mortbay.org/). For more information about I2PSnark, get in touch
with the folks at http://forum.i2p2.de/
i2psnark is packaged as a webapp running in the router console.
Command line and standalone operation of i2psnark are not currently supported.
See http://trac.i2p2.i2p/ticket/1191 or http://trac.i2p2.de/ticket/1191
for the status of restoring standalone support.

View File

@ -1,11 +1,9 @@
This is an I2P port of snark [http://klomp.org/snark], a GPL'ed bittorrent client
This is i2psnark, an I2P port of snark http://klomp.org/snark/ , a GPLv2 bittorrent client.
It contains significant enhancements including a web UI and support for
multitorrent, magnet, PEX and DHT.
The build in tracker has been removed for simplicity.
i2psnark is packaged as a webapp running in the router console.
Example usage:
java -jar lib/i2psnark.jar myFile.torrent
or, a more verbose setting:
java -jar lib/i2psnark.jar --eepproxy 127.0.0.1 4444 \
--i2cp 127.0.0.1 7654 "inbound.length=2 outbound.length=2" \
--debug 6 myFile.torrent
See http://i2p-projekt.i2p/en/docs/applications/bittorrent
or https://geti2p.net/en/docs/applications/bittorrent
for the specification of the protocols for bittorrent over I2P.

View File

@ -25,12 +25,12 @@ then
fi
# on windows, one must specify the path of commnad find
# since windows has its own retarded version of find.
# since windows has its own version of find.
if which find|grep -q -i windows ; then
export PATH=.:/bin:/usr/local/bin:$PATH
fi
# Fast mode - update ondemond
# set LG2 to the language you need in envrionment varibales to enable this
# set LG2 to the language you need in environment variables to enable this
# add ../java/ so the refs will work in the po file
JPATHS="../java/build/Proxy.java ../java/src/net/i2p/i2ptunnel/I2PTunnelHTTPClient.java ../java/src/net/i2p/i2ptunnel/localServer/LocalHTTPServer.java"
@ -62,16 +62,16 @@ do
echo "Updating the $i file from the tags..."
# extract strings from java and jsp files, and update messages.po files
# translate calls must be one of the forms:
# _("foo")
# _t("foo")
# _x("foo")
# intl._("foo")
# intl._t("foo")
# intl.title("foo")
# In a jsp, you must use a helper or handler that has the context set.
# To start a new translation, copy the header from an old translation to the new .po file,
# then ant distclean updater.
find $JPATHS -name *.java > $TMPFILE
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
--keyword=_ \
--keyword=_t \
-o ${i}t
if [ $? -ne 0 ]
then

View File

@ -24,12 +24,12 @@ then
fi
# on windows, one must specify the path of commnad find
# since windows has its own retarded version of find.
# since windows has its own version of find.
if which find|grep -q -i windows ; then
export PATH=.:/bin:/usr/local/bin:$PATH
fi
# Fast mode - update ondemond
# set LG2 to the language you need in envrionment varibales to enable this
# set LG2 to the language you need in environment variables to enable this
# add ../java/ so the refs will work in the po file
JPATHS="../java/src/net/i2p/i2ptunnel/web ../jsp/WEB-INF"
@ -61,16 +61,16 @@ do
echo "Updating the $i file from the tags..."
# extract strings from java and jsp files, and update messages.po files
# translate calls must be one of the forms:
# _("foo")
# _t("foo")
# _x("foo")
# intl._("foo")
# intl._t("foo")
# intl.title("foo")
# In a jsp, you must use a helper or handler that has the context set.
# To start a new translation, copy the header from an old translation to the new .po file,
# then ant distclean updater.
find $JPATHS -name *.java > $TMPFILE
xgettext -f $TMPFILE -F -L java --from-code=UTF-8 --add-comments\
--keyword=_ --keyword=_x --keyword=intl._ --keyword=intl.title \
--keyword=_t --keyword=_x --keyword=intl._ --keyword=intl.title \
-o ${i}t
if [ $? -ne 0 ]
then

View File

@ -7,14 +7,13 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import net.i2p.I2PAppContext;
import net.i2p.data.DataHelper;
import net.i2p.data.Hash;
import net.i2p.util.Clock;
import net.i2p.util.Log;
import net.i2p.util.SimpleTimer2;
import net.i2p.util.SystemVersion;
/**
* Count how often something happens with a particular peer and all peers.
@ -57,9 +56,7 @@ class ConnThrottler {
_log = log;
// for logging
_fmt = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
String systemTimeZone = I2PAppContext.getGlobalContext().getProperty("i2p.systemTimeZone");
if (systemTimeZone != null)
_fmt.setTimeZone(TimeZone.getTimeZone(systemTimeZone));
_fmt.setTimeZone(SystemVersion.getSystemTimeZone());
new Cleaner();
}

View File

@ -0,0 +1,372 @@
package net.i2p.i2ptunnel;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.CRC32;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;
import net.i2p.data.DataHelper;
/**
* Gunzip implementation per
* <a href="http://www.faqs.org/rfcs/rfc1952.html">RFC 1952</a>, reusing
* java's standard CRC32 and Inflater and InflaterOutputStream implementations.
*
* Note that the underlying InflaterOutputStream cannot be reused after close(),
* so we don't have a Reusable version of this.
*
* Modified from net.i2p.util.ResettableGZIPInputStream to use Java 6 InflaterOutputstream
* @since 0.9.21
*/
class GunzipOutputStream extends InflaterOutputStream {
private static final int FOOTER_SIZE = 8; // CRC32 + ISIZE
private final CRC32 _crc32;
private final byte _buf1[] = new byte[1];
private boolean _complete;
private final byte _footer[] = new byte[FOOTER_SIZE];
private long _bytesReceived;
private long _bytesReceivedAtCompletion;
private enum HeaderState { MB1, MB2, CF, MT0, MT1, MT2, MT3, EF, OS, FLAGS,
EH1, EH2, EHDATA, NAME, COMMENT, CRC1, CRC2, DONE }
private HeaderState _state = HeaderState.MB1;
private int _flags;
private int _extHdrToRead;
/**
* Build a new Gunzip stream
*/
public GunzipOutputStream(OutputStream uncompressedStream) throws IOException {
super(uncompressedStream, new Inflater(true));
_crc32 = new CRC32();
}
@Override
public void write(int b) throws IOException {
_buf1[0] = (byte) b;
write(_buf1, 0, 1);
}
@Override
public void write(byte buf[]) throws IOException {
write(buf, 0, buf.length);
}
@Override
public void write(byte buf[], int off, int len) throws IOException {
if (_complete) {
// shortcircuit so the inflater doesn't try to refill
// with the footer's data (which would fail, causing ZLIB err)
return;
}
boolean isFinished = inf.finished();
for (int i = off; i < off + len; i++) {
if (!isFinished) {
if (_state != HeaderState.DONE) {
verifyHeader(buf[i]);
continue;
}
// ensure we call the same method variant so we don't depend on underlying implementation
super.write(buf, i, 1);
if (inf.finished()) {
isFinished = true;
_bytesReceivedAtCompletion = _bytesReceived;
}
}
_footer[(int) (_bytesReceived++ % FOOTER_SIZE)] = buf[i];
if (isFinished) {
long footerSize = _bytesReceivedAtCompletion - _bytesReceived;
// could be at 7 or 8...
// we write the first byte of the footer to the Inflater if necessary...
// see comments in ResettableGZIPInputStream for details
if (footerSize >= FOOTER_SIZE - 1) {
try {
verifyFooter();
inf.reset(); // so it doesn't bitch about missing data...
_complete = true;
return;
} catch (IOException ioe) {
// failed at 7, retry at 8
if (footerSize == FOOTER_SIZE - 1 && i < off + len - 1)
continue;
_complete = true;
throw ioe;
}
}
}
}
}
/**
* Inflater statistic
*/
public long getTotalRead() {
try {
return inf.getBytesRead();
} catch (RuntimeException e) {
return 0;
}
}
/**
* Inflater statistic
*/
public long getTotalExpanded() {
try {
return inf.getBytesWritten();
} catch (RuntimeException e) {
// possible NPE in some implementations
return 0;
}
}
/**
* Inflater statistic
*/
public long getRemaining() {
try {
return inf.getRemaining();
} catch (RuntimeException e) {
// possible NPE in some implementations
return 0;
}
}
/**
* Inflater statistic
*/
public boolean getFinished() {
try {
return inf.finished();
} catch (RuntimeException e) {
// possible NPE in some implementations
return true;
}
}
@Override
public void close() throws IOException {
_complete = true;
_state = HeaderState.DONE;
super.close();
}
@Override
public String toString() {
return "GOS read: " + getTotalRead() + " expanded: " + getTotalExpanded() + " remaining: " + getRemaining() + " finished: " + getFinished();
}
/**
* @throws IOException on CRC or length check fail
*/
private void verifyFooter() throws IOException {
int idx = (int) (_bytesReceivedAtCompletion % FOOTER_SIZE);
byte[] footer;
if (idx == 0) {
footer = _footer;
} else {
footer = new byte[FOOTER_SIZE];
for (int i = 0; i < FOOTER_SIZE; i++) {
footer[i] = _footer[(int) ((_bytesReceivedAtCompletion + i) % FOOTER_SIZE)];
}
}
long actualSize = inf.getTotalOut();
long expectedSize = DataHelper.fromLongLE(footer, 4, 4);
if (expectedSize != actualSize)
throw new IOException("gunzip expected " + expectedSize + " bytes, got " + actualSize);
long actualCRC = _crc32.getValue();
long expectedCRC = DataHelper.fromLongLE(footer, 0, 4);
if (expectedCRC != actualCRC)
throw new IOException("gunzip CRC fail expected 0x" + Long.toHexString(expectedCRC) +
" bytes, got 0x" + Long.toHexString(actualCRC));
}
/**
* Make sure the header is valid, throwing an IOException if it is bad.
* Pushes through the state machine, checking as we go.
* Call for each byte until HeaderState is DONE.
*/
private void verifyHeader(byte b) throws IOException {
int c = b & 0xff;
switch (_state) {
case MB1:
if (c != 0x1F) throw new IOException("First magic byte was wrong [" + c + "]");
_state = HeaderState.MB2;
break;
case MB2:
if (c != 0x8B) throw new IOException("Second magic byte was wrong [" + c + "]");
_state = HeaderState.CF;
break;
case CF:
if (c != 0x08) throw new IOException("Compression format is invalid [" + c + "]");
_state = HeaderState.FLAGS;
break;
case FLAGS:
_flags = c;
_state = HeaderState.MT0;
break;
case MT0:
// ignore
_state = HeaderState.MT1;
break;
case MT1:
// ignore
_state = HeaderState.MT2;
break;
case MT2:
// ignore
_state = HeaderState.MT3;
break;
case MT3:
// ignore
_state = HeaderState.EF;
break;
case EF:
if ( (c != 0x00) && (c != 0x02) && (c != 0x04) )
throw new IOException("Invalid extended flags [" + c + "]");
_state = HeaderState.OS;
break;
case OS:
// ignore
if (0 != (_flags & (1<<5)))
_state = HeaderState.EH1;
else if (0 != (_flags & (1<<4)))
_state = HeaderState.NAME;
else if (0 != (_flags & (1<<3)))
_state = HeaderState.COMMENT;
else if (0 != (_flags & (1<<6)))
_state = HeaderState.CRC1;
else
_state = HeaderState.DONE;
break;
case EH1:
_extHdrToRead = c;
_state = HeaderState.EH2;
break;
case EH2:
_extHdrToRead += (c << 8);
if (_extHdrToRead > 0)
_state = HeaderState.EHDATA;
else if (0 != (_flags & (1<<4)))
_state = HeaderState.NAME;
if (0 != (_flags & (1<<3)))
_state = HeaderState.COMMENT;
else if (0 != (_flags & (1<<6)))
_state = HeaderState.CRC1;
else
_state = HeaderState.DONE;
break;
case EHDATA:
// ignore
if (--_extHdrToRead <= 0) {
if (0 != (_flags & (1<<4)))
_state = HeaderState.NAME;
if (0 != (_flags & (1<<3)))
_state = HeaderState.COMMENT;
else if (0 != (_flags & (1<<6)))
_state = HeaderState.CRC1;
else
_state = HeaderState.DONE;
}
break;
case NAME:
// ignore
if (c == 0) {
if (0 != (_flags & (1<<3)))
_state = HeaderState.COMMENT;
else if (0 != (_flags & (1<<6)))
_state = HeaderState.CRC1;
else
_state = HeaderState.DONE;
}
break;
case COMMENT:
// ignore
if (c == 0) {
if (0 != (_flags & (1<<6)))
_state = HeaderState.CRC1;
else
_state = HeaderState.DONE;
}
break;
case CRC1:
// ignore
_state = HeaderState.CRC2;
break;
case CRC2:
// ignore
_state = HeaderState.DONE;
break;
case DONE:
default:
break;
}
}
/****
public static void main(String args[]) {
java.util.Random r = new java.util.Random();
for (int i = 0; i < 1050; i++) {
byte[] b = new byte[i];
r.nextBytes(b);
if (!test(b)) return;
}
for (int i = 1; i < 64*1024; i+= 29) {
byte[] b = new byte[i];
r.nextBytes(b);
if (!test(b)) return;
}
}
private static boolean test(byte[] b) {
int size = b.length;
try {
java.io.ByteArrayOutputStream baos = new java.io.ByteArrayOutputStream(size);
java.util.zip.GZIPOutputStream o = new java.util.zip.GZIPOutputStream(baos);
o.write(b);
o.finish();
o.flush();
byte compressed[] = baos.toByteArray();
java.io.ByteArrayOutputStream baos2 = new java.io.ByteArrayOutputStream(size);
GunzipOutputStream out = new GunzipOutputStream(baos2);
out.write(compressed);
byte rv[] = baos2.toByteArray();
if (rv.length != b.length)
throw new RuntimeException("read length: " + rv.length + " expected: " + b.length);
if (!net.i2p.data.DataHelper.eq(rv, 0, b, 0, b.length)) {
throw new RuntimeException("foo, read=" + rv.length);
} else {
System.out.println("match, w00t @ " + size);
return true;
}
} catch (Exception e) {
System.out.println("Error dealing with size=" + size + ": " + e.getMessage());
e.printStackTrace();
return false;
}
}
****/
}

View File

@ -10,20 +10,14 @@ package net.i2p.i2ptunnel;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.Locale;
import java.util.concurrent.RejectedExecutionException;
import net.i2p.I2PAppContext;
import net.i2p.data.ByteArray;
import net.i2p.util.BigPipedInputStream;
import net.i2p.data.DataHelper;
import net.i2p.util.ByteCache;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.util.ReusableGZIPInputStream;
/**
* This does the transparent gzip decompression on the client side.
@ -45,7 +39,10 @@ class HTTPResponseOutputStream extends FilterOutputStream {
private final byte _buf1[];
protected boolean _gzip;
protected long _dataExpected;
/** lower-case, trimmed */
protected String _contentType;
/** lower-case, trimmed */
protected String _contentEncoding;
private static final int CACHE_SIZE = 8*1024;
private static final ByteCache _cache = ByteCache.getInstance(8, CACHE_SIZE);
@ -152,10 +149,12 @@ class HTTPResponseOutputStream extends FilterOutputStream {
for (int i = 0; i < _headerBuffer.getValid(); i++) {
if (isNL(_headerBuffer.getData()[i])) {
if (lastEnd == -1) {
responseLine = new String(_headerBuffer.getData(), 0, i+1); // includes NL
responseLine = DataHelper.getUTF8(_headerBuffer.getData(), 0, i+1); // includes NL
responseLine = filterResponseLine(responseLine);
responseLine = (responseLine.trim() + "\r\n");
out.write(responseLine.getBytes());
if (_log.shouldLog(Log.INFO))
_log.info("Response: " + responseLine.trim());
out.write(DataHelper.getUTF8(responseLine));
} else {
for (int j = lastEnd+1; j < i; j++) {
if (_headerBuffer.getData()[j] == ':') {
@ -163,22 +162,22 @@ class HTTPResponseOutputStream extends FilterOutputStream {
int valLen = i-(j+1);
if ( (keyLen <= 0) || (valLen < 0) )
throw new IOException("Invalid header @ " + j);
String key = new String(_headerBuffer.getData(), lastEnd+1, keyLen);
String val = null;
String key = DataHelper.getUTF8(_headerBuffer.getData(), lastEnd+1, keyLen);
String val;
if (valLen == 0)
val = "";
else
val = new String(_headerBuffer.getData(), j+2, valLen).trim();
val = DataHelper.getUTF8(_headerBuffer.getData(), j+2, valLen).trim();
if (_log.shouldLog(Log.INFO))
_log.info("Response header [" + key + "] = [" + val + "]");
String lcKey = key.toLowerCase(Locale.US);
if ("connection".equals(lcKey)) {
out.write("Connection: close\r\n".getBytes());
out.write(DataHelper.getASCII("Connection: close\r\n"));
connectionSent = true;
} else if ("proxy-connection".equals(lcKey)) {
out.write("Proxy-Connection: close\r\n".getBytes());
out.write(DataHelper.getASCII("Proxy-Connection: close\r\n"));
proxyConnectionSent = true;
} else if ("content-encoding".equals(lcKey) && "x-i2p-gzip".equals(val.toLowerCase(Locale.US))) {
_gzip = true;
@ -193,20 +192,24 @@ class HTTPResponseOutputStream extends FilterOutputStream {
} catch (NumberFormatException nfe) {}
} else if ("content-type".equals(lcKey)) {
// save for compress decision on server side
_contentType = val;
_contentType = val.toLowerCase(Locale.US);
} else if ("content-encoding".equals(lcKey)) {
// save for compress decision on server side
_contentEncoding = val.toLowerCase(Locale.US);
} else if ("set-cookie".equals(lcKey)) {
String lcVal = val.toLowerCase(Locale.US);
if (lcVal.contains("domain=b32.i2p") ||
lcVal.contains("domain=.b32.i2p")) {
// Strip privacy-damaging "supercookie" for b32.i2p
// Let's presume the user agent ignores a cookie for "i2p"
lcVal.contains("domain=.b32.i2p") ||
lcVal.contains("domain=i2p") ||
lcVal.contains("domain=.i2p")) {
// Strip privacy-damaging "supercookies" for i2p and b32.i2p
// See RFC 6265 and http://publicsuffix.org/
if (_log.shouldLog(Log.INFO))
_log.info("Stripping \"" + key + ": " + val + "\" from response ");
break;
}
}
out.write((key.trim() + ": " + val.trim() + "\r\n").getBytes());
out.write(DataHelper.getUTF8(key.trim() + ": " + val + "\r\n"));
}
break;
}
@ -217,9 +220,9 @@ class HTTPResponseOutputStream extends FilterOutputStream {
}
if (!connectionSent)
out.write("Connection: close\r\n".getBytes());
out.write(DataHelper.getASCII("Connection: close\r\n"));
if (!proxyConnectionSent)
out.write("Proxy-Connection: close\r\n".getBytes());
out.write(DataHelper.getASCII("Proxy-Connection: close\r\n"));
finishHeaders();
@ -240,100 +243,24 @@ class HTTPResponseOutputStream extends FilterOutputStream {
protected boolean shouldCompress() { return _gzip; }
protected void finishHeaders() throws IOException {
out.write("\r\n".getBytes()); // end of the headers
out.write(DataHelper.getASCII("\r\n")); // end of the headers
}
@Override
public void close() throws IOException {
out.close();
if (_log.shouldLog(Log.INFO))
_log.info("Closing " + out + " threaded?? " + shouldCompress(), new Exception("I did it"));
synchronized(this) {
// synch with changing out field below
super.close();
}
}
protected void beginProcessing() throws IOException {
//out.flush();
PipedInputStream pi = BigPipedInputStream.getInstance();
PipedOutputStream po = new PipedOutputStream(pi);
Runnable r = new Pusher(pi, out);
out = po;
// TODO we should be able to do this inline somehow
TunnelControllerGroup tcg = TunnelControllerGroup.getInstance();
if (tcg != null) {
// Run in the client thread pool, as there should be an unused thread
// there after the accept().
// Overridden in I2PTunnelHTTPServer, where it does not use the client pool.
try {
tcg.getClientExecutor().execute(r);
} catch (RejectedExecutionException ree) {
// shouldn't happen
throw ree;
}
} else {
// Fallback in case TCG.getInstance() is null, never instantiated
// and we were not started by TCG.
// Maybe a plugin loaded before TCG? Should be rare.
Thread t = new I2PAppThread(r, "Pusher");
t.start();
}
}
private class Pusher implements Runnable {
private final InputStream _inRaw;
private final OutputStream _out;
public Pusher(InputStream in, OutputStream out) {
_inRaw = in;
_out = out;
}
public void run() {
ReusableGZIPInputStream _in = ReusableGZIPInputStream.acquire();
long written = 0;
ByteArray ba = null;
try {
// blocking
_in.initialize(_inRaw);
ba = _cache.acquire();
byte buf[] = ba.getData();
int read = -1;
while ( (read = _in.read(buf)) != -1) {
if (_log.shouldLog(Log.DEBUG))
_log.debug("Read " + read + " and writing it to the browser/streams");
_out.write(buf, 0, read);
_out.flush();
written += read;
}
if (_log.shouldLog(Log.INFO))
_log.info("Decompressed: " + written + ", " + _in.getTotalRead() + "/" + _in.getTotalExpanded());
} catch (IOException ioe) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error decompressing: " + written + ", " + _in.getTotalRead() + "/" + _in.getTotalExpanded(), ioe);
} catch (OutOfMemoryError oom) {
_log.error("OOM in HTTP Decompressor", oom);
} finally {
if (_log.shouldInfo())
_log.info("After decompression, written=" + written +
" read=" + _in.getTotalRead()
+ ", expanded=" + _in.getTotalExpanded() + ", remaining=" + _in.getRemaining()
+ ", finished=" + _in.getFinished());
if (ba != null)
_cache.release(ba);
if (_out != null) try {
_out.close();
} catch (IOException ioe) {}
try {
_in.close();
} catch (IOException ioe) {}
}
double compressed = _in.getTotalRead();
double expanded = _in.getTotalExpanded();
ReusableGZIPInputStream.release(_in);
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));
_context.statManager().addRateData("i2ptunnel.httpCompressed", (long)compressed);
_context.statManager().addRateData("i2ptunnel.httpExpanded", (long)expanded);
}
OutputStream po = new GunzipOutputStream(out);
synchronized(this) {
out = po;
}
}

View File

@ -35,7 +35,6 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@ -1605,7 +1604,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
private void runRun(String args[], Logging l) {
if (args.length == 1) {
try {
BufferedReader br = new BufferedReader(new FileReader(args[0]));
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(args[0]), "UTF-8"));
String line;
while ((line = br.readLine()) != null) {
runCommand(line, l);
@ -1874,7 +1873,7 @@ public class I2PTunnel extends EventDispatcherImpl implements Logging {
try {
result.fromByteArray(content);
return result;
} catch (Exception ex) {
} catch (RuntimeException ex) {
if (log.shouldLog(Log.INFO))
log.info("File is not a binary destination - trying base64");
try {

View File

@ -3,6 +3,7 @@
*/
package net.i2p.i2ptunnel;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
@ -10,6 +11,7 @@ import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.data.Destination;
@ -122,7 +124,17 @@ public class I2PTunnelClient extends I2PTunnelClientBase {
// we are called from an unlimited thread pool, so run inline
//t.start();
t.run();
} catch (Exception ex) {
} catch (IOException ex) {
if (_log.shouldLog(Log.INFO))
_log.info("Error connecting", ex);
//l.log("Error connecting: " + ex.getMessage());
closeSocket(s);
if (i2ps != null) {
synchronized (sockLock) {
mySockets.remove(sockLock);
}
}
} catch (I2PException ex) {
if (_log.shouldLog(Log.INFO))
_log.info("Error connecting", ex);
//l.log("Error connecting: " + ex.getMessage());

View File

@ -25,12 +25,14 @@ import javax.net.ssl.SSLServerSocketFactory;
import net.i2p.I2PAppContext;
import net.i2p.I2PException;
import net.i2p.client.I2PClient;
import net.i2p.client.I2PSession;
import net.i2p.client.I2PSessionException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketManager;
import net.i2p.client.streaming.I2PSocketManagerFactory;
import net.i2p.client.streaming.I2PSocketOptions;
import net.i2p.crypto.SigType;
import net.i2p.data.Destination;
import net.i2p.util.EventDispatcher;
import net.i2p.util.I2PAppThread;
@ -77,11 +79,24 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
private volatile ThreadPoolExecutor _executor;
/** this is ONLY for shared clients */
private static I2PSocketManager socketManager;
/**
* Only destroy and replace a static shared client socket manager if it's been connected before
* @since 0.9.20
*/
private enum SocketManagerState { INIT, CONNECTED }
private static SocketManagerState _socketManagerState = SocketManagerState.INIT;
public static final String PROP_USE_SSL = I2PTunnelServer.PROP_USE_SSL;
/**
* This constructor always starts the tunnel (ignoring the i2cp.delayOpen option).
* It is used to add a client to an existing socket manager.
* This constructor is used to add a client to an existing socket manager.
* <p/>
* As of 0.9.21 this does NOT open the local socket. You MUST call
* {@link #startRunning()} for that. The local socket will be opened
* immediately (ignoring the <code>i2cp.delayOpen</code> option).
*
* @param localPort if 0, use any port, get actual port selected with getLocalPort()
* @param sktMgr the existing socket manager
@ -98,20 +113,19 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
this.l = l;
_ownDest = true; // == ! shared client
_context = tunnel.getContext();
_context.statManager().createRateStat("i2ptunnel.client.closeBacklog", "How many pending sockets remain when we close one due to backlog?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.closeNoBacklog", "How many pending sockets remain when it was removed prior to backlog timeout?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.manageTime", "How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
initStats();
_log = _context.logManager().getLog(getClass());
startup();
}
/**
* The main constructor.
*
* As of 0.9.20 this is fast, and does NOT connect the manager to the router,
* <p/>
* As of 0.9.21 this is fast, and does NOT connect the manager to the router,
* or open the local socket. You MUST call startRunning() for that.
* <p/>
* (0.9.20 claimed to be fast, but due to a bug it DID connect the manager
* to the router. It did NOT open the local socket however, so it was still
* necessary to call startRunning() for that.)
*
* @param localPort if 0, use any port, get actual port selected with getLocalPort()
* @throws IllegalArgumentException if the I2CP configuration is b0rked so
@ -125,9 +139,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
/**
* Use this to build a client with a persistent private key.
*
* As of 0.9.20 this is fast, and does NOT connect the manager to the router,
* <p/>
* As of 0.9.21 this is fast, and does NOT connect the manager to the router,
* or open the local socket. You MUST call startRunning() for that.
* <p/>
* (0.9.20 claimed to be fast, but due to a bug it DID connect the manager
* to the router. It did NOT open the local socket however, so it was still
* necessary to call startRunning() for that.)
*
* @param localPort if 0, use any port, get actual port selected with getLocalPort()
* @param pkf Path to the private key file, or null to generate a transient key
@ -146,10 +164,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
_handlerName = handlerName;
_context = tunnel.getContext();
_context.statManager().createRateStat("i2ptunnel.client.closeBacklog", "How many pending sockets remain when we close one due to backlog?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.closeNoBacklog", "How many pending sockets remain when it was removed prior to backlog timeout?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.manageTime", "How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
initStats();
_log = _context.logManager().getLog(getClass());
// normalize path so we can find it
@ -168,27 +183,13 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
tunnel.getClientOptions().setProperty("i2cp.dontPublishLeaseSet", "true");
if (tunnel.getClientOptions().getProperty("i2p.streaming.answerPings") == null)
tunnel.getClientOptions().setProperty("i2p.streaming.answerPings", "false");
}
boolean openNow = !Boolean.parseBoolean(tunnel.getClientOptions().getProperty("i2cp.delayOpen"));
if (openNow) {
while (sockMgr == null) {
verifySocketManager();
if (sockMgr == null) {
_log.error("Unable to connect to router and build tunnels for " + handlerName);
// FIXME there is a loop in buildSocketManager(), do we really need another one here?
// no matter, buildSocketManager() now throws an IllegalArgumentException
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
}
}
// can't be null unless we limit the loop above
//if (sockMgr == null) {
// l.log("Invalid I2CP configuration");
// throw new IllegalArgumentException("Socket manager could not be created");
//}
l.log("Tunnels ready for client: " + handlerName);
} // else delay creating session until createI2PSocket() is called
private void initStats() {
_context.statManager().createRateStat("i2ptunnel.client.closeBacklog", "How many pending sockets remain when we close one due to backlog?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.closeNoBacklog", "How many pending sockets remain when it was removed prior to backlog timeout?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.manageTime", "How long it takes to accept a socket and fire it into an i2ptunnel runner (or queue it for the pool)?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
_context.statManager().createRateStat("i2ptunnel.client.buildRunTime", "How long it takes to run a queued socket into an i2ptunnel runner?", "I2PTunnel", new long[] { 60*1000, 10*60*1000, 60*60*1000 });
}
/**
@ -239,10 +240,6 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
connectManager();
}
/** this is ONLY for shared clients */
private static I2PSocketManager socketManager;
/**
* This is ONLY for shared clients.
* As of 0.9.20 this is fast, and does NOT connect the manager to the router.
@ -283,14 +280,19 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
Log _log = tunnel.getContext().logManager().getLog(I2PTunnelClientBase.class);
if (socketManager != null && !socketManager.isDestroyed()) {
I2PSession s = socketManager.getSession();
if (s.isClosed()) {
if (s.isClosed() && _socketManagerState != SocketManagerState.INIT) {
if (_log.shouldLog(Log.INFO))
_log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Building a new socket manager since the old one closed [s=" + s + "]");
tunnel.removeSession(s);
// make sure the old one is closed
socketManager.destroySocketManager();
_socketManagerState = SocketManagerState.INIT;
// We could be here a LONG time, holding the lock
socketManager = buildSocketManager(tunnel, pkf);
// FIXME may not be the right place for this
I2PSession sub = addSubsession(tunnel);
if (sub != null && _log.shouldLog(Log.WARN))
_log.warn("Added subsession " + sub);
} else {
if (_log.shouldLog(Log.INFO))
_log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Not building a new socket manager since the old one is open [s=" + s + "]");
@ -303,10 +305,41 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
if (_log.shouldLog(Log.INFO))
_log.info(tunnel.getClientOptions().getProperty("inbound.nickname") + ": Building a new socket manager since there is no other one");
socketManager = buildSocketManager(tunnel, pkf);
I2PSession sub = addSubsession(tunnel);
if (sub != null && _log.shouldLog(Log.WARN))
_log.warn("Added subsession " + sub);
}
return socketManager;
}
/**
* Add a subsession to a shared client if necessary.
*
* @since 0.9.20
*/
protected static synchronized I2PSession addSubsession(I2PTunnel tunnel) {
I2PSession sess = socketManager.getSession();
if (sess.getMyDestination().getSigType() == SigType.DSA_SHA1)
return null;
Properties props = new Properties();
props.putAll(tunnel.getClientOptions());
String name = props.getProperty("inbound.nickname");
if (name != null)
props.setProperty("inbound.nickname", name + " (DSA)");
name = props.getProperty("outbound.nickname");
if (name != null)
props.setProperty("outbound.nickname", name + " (DSA)");
props.setProperty(I2PClient.PROP_SIGTYPE, "DSA_SHA1");
try {
return socketManager.addSubsession(null, props);
} catch (I2PSessionException ise) {
Log log = tunnel.getContext().logManager().getLog(I2PTunnelClientBase.class);
if (log.shouldLog(Log.WARN))
log.warn("Failed to add subssession", ise);
return null;
}
}
/**
* Kill the shared client, so that on restart in android
* we won't latch onto the old one
@ -424,6 +457,10 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
while (sockMgr.getSession().isClosed()) {
try {
sockMgr.getSession().connect();
synchronized(I2PTunnelClientBase.class) {
if (sockMgr == socketManager)
_socketManagerState = SocketManagerState.CONNECTED;
}
} catch (I2PSessionException ise) {
// shadows instance _log
Log _log = getTunnel().getContext().logManager().getLog(I2PTunnelClientBase.class);
@ -525,7 +562,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
if (open && listenerReady) {
boolean openNow = !Boolean.parseBoolean(getTunnel().getClientOptions().getProperty("i2cp.delayOpen"));
if (openNow)
if (openNow || chained)
l.log("Client ready, listening on " + getTunnel().listenHost + ':' + localPort);
else
l.log("Client ready, listening on " + getTunnel().listenHost + ':' + localPort + ", delaying tunnel open until required");

View File

@ -240,7 +240,7 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
}
if (user != null && pw != null) {
newRequest.append("Proxy-Authorization: Basic ")
.append(Base64.encode((user + ':' + pw).getBytes(), true)) // true = use standard alphabet
.append(Base64.encode(DataHelper.getUTF8(user + ':' + pw), true)) // true = use standard alphabet
.append("\r\n");
}
}
@ -265,7 +265,7 @@ public class I2PTunnelConnectClient extends I2PTunnelHTTPClientBase implements R
else
_log.warn(getPrefix(requestId) + "Auth required, sending 407");
}
out.write(getAuthError(result == AuthResult.AUTH_STALE).getBytes());
out.write(DataHelper.getASCII(getAuthError(result == AuthResult.AUTH_STALE)));
s.close();
return;
}

View File

@ -414,7 +414,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
_log.debug(getPrefix(requestId) + "First line [" + line + "]");
}
String[] params = line.split(" ", 3);
String[] params = DataHelper.split(line, " ", 3);
if(params.length != 3) {
break;
}
@ -631,8 +631,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
String header = getErrorPage("ahelper-notfound", ERR_AHELPER_NOTFOUND);
try {
out.write(header.getBytes("UTF-8"));
out.write(("<p>" + _("This seems to be a bad destination:") + " " + ahelperKey + " " +
_("i2paddresshelper cannot help you with a destination like that!") +
out.write(("<p>" + _t("This seems to be a bad destination:") + " " + ahelperKey + " " +
_t("i2paddresshelper cannot help you with a destination like that!") +
"</p>").getBytes("UTF-8"));
writeFooter(out);
reader.drain();
@ -706,7 +706,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
String conflictURL = conflictURI.toASCIIString();
String header = getErrorPage("ahelper-conflict", ERR_AHELPER_CONFLICT);
out.write(header.getBytes("UTF-8"));
out.write(_("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.",
out.write(_t("To visit the destination in your host database, click <a href=\"{0}\">here</a>. To visit the conflicting addresshelper destination, click <a href=\"{1}\">here</a>.",
trustedURL, conflictURL).getBytes("UTF-8"));
out.write("</p></div>".getBytes("UTF-8"));
writeFooter(out);
@ -881,7 +881,9 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
} else if(lowercaseLine.startsWith("accept")) {
// strip the accept-blah headers, as they vary dramatically from
// browser to browser
if(!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) {
// But allow Accept-Encoding: gzip, deflate
if(!lowercaseLine.startsWith("accept-encoding: ") &&
!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT))) {
line = null;
continue;
}
@ -933,8 +935,8 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
// according to rfc2616 s14.3, this *should* force identity, even if
// an explicit q=0 for gzip doesn't. tested against orion.i2p, and it
// seems to work.
if(!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT)))
newRequest.append("Accept-Encoding: \r\n");
//if (!Boolean.parseBoolean(getTunnel().getClientOptions().getProperty(PROP_ACCEPT)))
// newRequest.append("Accept-Encoding: \r\n");
if (!usingInternalOutproxy)
newRequest.append("X-Accept-Encoding: x-i2p-gzip;q=1.0, identity;q=0.5, deflate;q=0, gzip;q=0, *;q=0\r\n");
}
@ -1116,7 +1118,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
header = getErrorPage("dnfb", ERR_DESTINATION_UNKNOWN);
} else if(destination.length() == 60 && destination.toLowerCase(Locale.US).endsWith(".b32.i2p")) {
header = getErrorPage("nols", ERR_DESTINATION_UNKNOWN);
extraMessage = _("Destination lease set not found");
extraMessage = _t("Destination lease set not found");
} else {
header = getErrorPage("dnfh", ERR_DESTINATION_UNKNOWN);
jumpServers = getTunnel().getClientOptions().getProperty(PROP_JUMP_SERVERS);
@ -1250,7 +1252,7 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
String s = getTunnel().getClientOptions().getProperty(PROP_SSL_OUTPROXIES);
if (s == null)
return null;
String[] p = s.split("[,; \r\n\t]");
String[] p = DataHelper.split(s, "[,; \r\n\t]");
if (p.length == 0)
return null;
// todo doesn't check for ""
@ -1268,31 +1270,31 @@ public class I2PTunnelHTTPClient extends I2PTunnelHTTPClientBase implements Runn
Writer out = new BufferedWriter(new OutputStreamWriter(outs, "UTF-8"));
String header = getErrorPage("ahelper-new", ERR_AHELPER_NEW);
out.write(header);
out.write("<table><tr><td class=\"mediumtags\" align=\"right\">" + _("Host") +
out.write("<table><tr><td class=\"mediumtags\" align=\"right\">" + _t("Host") +
"</td><td class=\"mediumtags\">" + destination + "</td></tr>\n");
try {
String b32 = Base32.encode(SHA256Generator.getInstance().calculateHash(Base64.decode(ahelperKey)).getData());
out.write("<tr><td class=\"mediumtags\" align=\"right\">" + _("Base 32") + "</td>" +
out.write("<tr><td class=\"mediumtags\" align=\"right\">" + _t("Base 32") + "</td>" +
"<td><a href=\"http://" + b32 + ".b32.i2p/\">" + b32 + ".b32.i2p</a></td></tr>");
} catch(Exception e) {
}
out.write("<tr><td class=\"mediumtags\" align=\"right\">" + _("Destination") + "</td><td>" +
out.write("<tr><td class=\"mediumtags\" align=\"right\">" + _t("Destination") + "</td><td>" +
"<textarea rows=\"1\" style=\"height: 4em; min-width: 0; min-height: 0;\" cols=\"70\" wrap=\"off\" readonly=\"readonly\" >" +
ahelperKey + "</textarea></td></tr></table>\n" +
"<hr><div class=\"formaction\">" +
// FIXME if there is a query remaining it is lost
"<form method=\"GET\" action=\"" + targetRequest + "\">" +
"<button type=\"submit\" class=\"go\">" + _("Continue to {0} without saving", destination) + "</button>" +
"<button type=\"submit\" class=\"go\">" + _t("Continue to {0} without saving", destination) + "</button>" +
"</form>\n<form method=\"GET\" action=\"http://" + LOCAL_SERVER + "/add\">" +
"<input type=\"hidden\" name=\"host\" value=\"" + destination + "\">\n" +
"<input type=\"hidden\" name=\"dest\" value=\"" + ahelperKey + "\">\n" +
"<input type=\"hidden\" name=\"nonce\" value=\"" + _proxyNonce + "\">\n" +
"<button type=\"submit\" class=\"accept\" name=\"router\" value=\"router\">" +
_("Save {0} to router address book and continue to website", destination) + "</button><br>\n");
_t("Save {0} to router address book and continue to website", destination) + "</button><br>\n");
if(_context.namingService().getName().equals("BlockfileNamingService")) {
// only blockfile supports multiple books
out.write("<br><button type=\"submit\" name=\"master\" value=\"master\">" + _("Save {0} to master address book and continue to website", destination) + "</button><br>\n");
out.write("<button type=\"submit\" name=\"private\" value=\"private\">" + _("Save {0} to private address book and continue to website", destination) + "</button>\n");
out.write("<br><button type=\"submit\" name=\"master\" value=\"master\">" + _t("Save {0} to master address book and continue to website", destination) + "</button><br>\n");
out.write("<button type=\"submit\" name=\"private\" value=\"private\">" + _t("Save {0} to private address book and continue to website", destination) + "</button>\n");
}
// Firefox (and others?) don't send referer to meta refresh target, which is
// what the jump servers use, so this isn't that useful.

View File

@ -285,7 +285,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
// We send Accept-Charset: UTF-8 in the 407 so hopefully it comes back that way inside the B64 ?
try {
String dec = new String(decoded, "UTF-8");
String[] parts = dec.split(":");
String[] parts = DataHelper.split(dec, ":");
String user = parts[0];
String pw = parts[1];
// first try pw for that user
@ -684,7 +684,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
out.write("</a>");
if (usingWWWProxy) {
out.write("<br><br><b>");
out.write(_("HTTP Outproxy"));
out.write(_t("HTTP Outproxy"));
out.write(":</b> " + wwwProxy);
}
if (extraMessage != null) {
@ -723,7 +723,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
if (first) {
first = false;
out.write("<br><br><h3>");
out.write(_("Click a link below for an address helper from a jump service"));
out.write(_t("Click a link below for an address helper from a jump service"));
out.write("</h3>\n");
} else {
out.write("<br>");
@ -733,7 +733,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
out.write(uri);
out.write("\">");
// Translators: parameter is a host name
out.write(_("{0} jump service", jumphost));
out.write(_t("{0} jump service", jumphost));
out.write("</a>\n");
}
}
@ -778,7 +778,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* Translate
* @since 0.9.14 moved from I2PTunnelHTTPClient
*/
protected String _(String key) {
protected String _t(String key) {
return Translate.getString(key, _context, BUNDLE_NAME);
}
@ -787,7 +787,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* {0}
* @since 0.9.14 moved from I2PTunnelHTTPClient
*/
protected String _(String key, Object o) {
protected String _t(String key, Object o) {
return Translate.getString(key, o, _context, BUNDLE_NAME);
}
@ -796,7 +796,7 @@ public abstract class I2PTunnelHTTPClientBase extends I2PTunnelClientBase implem
* {0} and {1}
* @since 0.9.14 moved from I2PTunnelHTTPClient
*/
protected String _(String key, Object o, Object o2) {
protected String _t(String key, Object o, Object o2) {
return Translate.getString(key, o, o2, _context, BUNDLE_NAME);
}
}

View File

@ -33,6 +33,9 @@ public class I2PTunnelHTTPClientRunner extends I2PTunnelRunner {
super(s, i2ps, slock, initialI2PData, null, sockList, onFail);
}
/**
* Only call once!
*/
@Override
protected OutputStream getSocketOut() throws IOException {
OutputStream raw = super.getSocketOut();

View File

@ -419,12 +419,14 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
setEntry(headers, "Connection", "close");
// we keep the enc sent by the browser before clobbering it, since it may have
// been x-i2p-gzip
String enc = getEntryOrNull(headers, "Accept-encoding");
String altEnc = getEntryOrNull(headers, "X-Accept-encoding");
String enc = getEntryOrNull(headers, "Accept-Encoding");
String altEnc = getEntryOrNull(headers, "X-Accept-Encoding");
// according to rfc2616 s14.3, this *should* force identity, even if
// "identity;q=1, *;q=0" didn't.
setEntry(headers, "Accept-encoding", "");
// as of 0.9.23, the client passes this header through, and we do the same,
// so if the server and browser can do the compression/decompression, we don't have to
//setEntry(headers, "Accept-Encoding", "");
socket.setReadTimeout(readTimeout);
Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
@ -432,7 +434,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
// instead of i2ptunnelrunner, use something that reads the HTTP
// request from the socket, modifies the headers, sends the request to the
// server, reads the response headers, rewriting to include Content-encoding: x-i2p-gzip
// if it was one of the Accept-encoding: values, and gzip the payload
// if it was one of the Accept-Encoding: values, and gzip the payload
boolean allowGZIP = true;
String val = opts.getProperty("i2ptunnel.gzip");
if ( (val != null) && (!Boolean.parseBoolean(val)) )
@ -443,7 +445,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
boolean useGZIP = alt || ( (enc != null) && (enc.indexOf("x-i2p-gzip") >= 0) );
// Don't pass this on, outproxies should strip so I2P traffic isn't so obvious but they probably don't
if (alt)
headers.remove("X-Accept-encoding");
headers.remove("X-Accept-Encoding");
String modifiedHeader = formatHeaders(headers, command);
if (_log.shouldLog(Log.DEBUG))
@ -453,7 +455,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
if (allowGZIP && useGZIP) {
t = new CompressedRequestor(s, socket, modifiedHeader, getTunnel().getContext(), _log);
} else {
t = new I2PTunnelRunner(s, socket, slock, null, modifiedHeader.getBytes(),
t = new I2PTunnelRunner(s, socket, slock, null, DataHelper.getUTF8(modifiedHeader),
null, (I2PTunnelRunner.FailCallback) null);
}
// run in the unlimited client pool
@ -535,7 +537,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
if (_log.shouldLog(Log.INFO))
_log.info("request headers: " + _headers);
serverout.write(_headers.getBytes());
serverout.write(DataHelper.getUTF8(_headers));
browserin = _browser.getInputStream();
// Don't spin off a thread for this except for POSTs
// beware interference with Shoutcast, etc.?
@ -573,7 +575,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
Map<String, List<String>> headers = readHeaders(null, serverin, command,
SERVER_SKIPHEADERS, _ctx);
String modifiedHeaders = formatHeaders(headers, command);
compressedOut.write(modifiedHeaders.getBytes());
compressedOut.write(DataHelper.getUTF8(modifiedHeaders));
Sender s = new Sender(compressedOut, serverin, "server: server to browser", _log);
if (_log.shouldLog(Log.INFO))
@ -662,7 +664,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
*/
@Override
protected String filterResponseLine(String line) {
String[] s = line.split(" ", 3);
String[] s = DataHelper.split(line, " ", 3);
if (s.length > 1 &&
(s[1].startsWith("3") || s[1].startsWith("5")))
_dataExpected = 0;
@ -671,6 +673,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
/**
* Don't compress small responses or images.
* Don't compress things that are already compressed.
* Compression is inline but decompression on the client side
* creates a new thread.
*/
@ -687,7 +690,11 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
(!_contentType.equals("application/x-bzip")) &&
(!_contentType.equals("application/x-bzip2")) &&
(!_contentType.equals("application/x-gzip")) &&
(!_contentType.equals("application/zip"))));
(!_contentType.equals("application/zip")))) &&
(_contentEncoding == null ||
((!_contentEncoding.equals("gzip")) &&
(!_contentEncoding.equals("compress")) &&
(!_contentEncoding.equals("deflate"))));
}
@Override
@ -695,7 +702,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
//if (_log.shouldLog(Log.INFO))
// _log.info("Including x-i2p-gzip as the content encoding in the response");
if (shouldCompress())
out.write("Content-encoding: x-i2p-gzip\r\n".getBytes());
out.write(DataHelper.getASCII("Content-encoding: x-i2p-gzip\r\n"));
super.finishHeaders();
}
@ -735,7 +742,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
public long getTotalRead() {
try {
return def.getTotalIn();
} catch (Exception e) {
} catch (RuntimeException e) {
// j2se 1.4.2_08 on linux is sometimes throwing an NPE in the getTotalIn() implementation
return 0;
}
@ -743,7 +750,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
public long getTotalCompressed() {
try {
return def.getTotalOut();
} catch (Exception e) {
} catch (RuntimeException e) {
// j2se 1.4.2_08 on linux is sometimes throwing an NPE in the getTotalOut() implementation
return 0;
}
@ -817,7 +824,7 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
* @throws BadRequestException on bad headers
* @throws IOException on other errors in the underlying stream
*/
private static Map<String, List<String>> readHeaders(I2PSocket socket, InputStream in, StringBuilder command,
static Map<String, List<String>> readHeaders(I2PSocket socket, InputStream in, StringBuilder command,
String[] skipHeaders, I2PAppContext ctx) throws IOException {
HashMap<String, List<String>> headers = new HashMap<String, List<String>>();
StringBuilder buf = new StringBuilder(128);
@ -877,9 +884,9 @@ public class I2PTunnelHTTPServer extends I2PTunnelServer {
String lcName = name.toLowerCase(Locale.US);
if ("accept-encoding".equals(lcName))
name = "Accept-encoding";
name = "Accept-Encoding";
else if ("x-accept-encoding".equals(lcName))
name = "X-Accept-encoding";
name = "X-Accept-Encoding";
else if ("x-forwarded-for".equals(lcName))
name = "X-Forwarded-For";
else if ("x-forwarded-server".equals(lcName))

View File

@ -8,6 +8,7 @@ import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;
import net.i2p.I2PException;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.client.streaming.I2PSocketAddress;
import net.i2p.data.DataHelper;
@ -142,7 +143,7 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase {
// we are called from an unlimited thread pool, so run inline
//out.start();
out.run();
} catch (Exception ex) {
} catch (IOException ex) {
// generally NoRouteToHostException
if (_log.shouldLog(Log.WARN))
_log.warn("Error connecting", ex);
@ -160,6 +161,23 @@ public class I2PTunnelIRCClient extends I2PTunnelClientBase {
mySockets.remove(sockLock);
}
}
} catch (I2PException ex) {
if (_log.shouldLog(Log.WARN))
_log.warn("Error connecting", ex);
//l.log("Error connecting: " + ex.getMessage());
try {
// Send a response so the user doesn't just see a disconnect
// and blame his router or the network.
String name = addr != null ? addr.getHostName() : "undefined";
String msg = ":" + name + " 499 you :" + ex + "\r\n";
s.getOutputStream().write(DataHelper.getUTF8(msg));
} catch (IOException ioe) {}
closeSocket(s);
if (i2ps != null) {
synchronized (sockLock) {
mySockets.remove(sockLock);
}
}
}
}

View File

@ -13,6 +13,7 @@ import java.util.Properties;
import net.i2p.client.streaming.I2PSocket;
import net.i2p.crypto.SHA256Generator;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.Base32;
@ -128,7 +129,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
this.cloakKey = new byte[Hash.HASH_LENGTH];
tunnel.getContext().random().nextBytes(this.cloakKey);
} else {
this.cloakKey = SHA256Generator.getInstance().calculateHash(passphrase.trim().getBytes()).getData();
this.cloakKey = SHA256Generator.getInstance().calculateHash(DataHelper.getUTF8(passphrase.trim())).getData();
}
// get the fake hostmask to use
@ -158,7 +159,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
modifiedRegistration = buf.toString();
}
Socket s = getSocket(socket.getPeerDestination().calculateHash(), socket.getLocalPort());
Thread t = new I2PTunnelRunner(s, socket, slock, null, modifiedRegistration.getBytes(),
Thread t = new I2PTunnelRunner(s, socket, slock, null, DataHelper.getUTF8(modifiedRegistration),
null, (I2PTunnelRunner.FailCallback) null);
// run in the unlimited client pool
//t.start();
@ -277,7 +278,7 @@ public class I2PTunnelIRCServer extends I2PTunnelServer implements Runnable {
//if (_log.shouldLog(Log.DEBUG))
// _log.debug("Got line: " + s);
String field[]=s.split(" ",5);
String field[] = DataHelper.split(s, " ", 5);
String command;
int idx=0;

Some files were not shown because too many files have changed in this diff Show More