Compare commits
20 Commits
zzzot-0.17
...
zzzot-0.19
Author | SHA1 | Date | |
---|---|---|---|
e1e29a953b | |||
059f1cf6e7 | |||
32062dff20 | |||
7a6c0bcccd | |||
1853bbfd0e | |||
5828c358f6 | |||
017d8a61c3 | |||
cc640088c4 | |||
2016546fc1 | |||
9ed1a1ce0c | |||
8fd17dd938 | |||
51e4184ea8 | |||
df683fcf5c | |||
ca66c075bf | |||
f35827abcb | |||
290b7e9ba8 | |||
9a988e5c6d | |||
5d2343fe71 | |||
94ccfa25e6 | |||
36cee7777a |
12
CHANGES.txt
12
CHANGES.txt
@ -1,3 +1,11 @@
|
||||
2024-04-07 [0.19.0]
|
||||
- Disable full scrape by default
|
||||
- Handle BiglyBT scrape URLs
|
||||
|
||||
2020-08-30 [0.18.0]
|
||||
- Enable both encryption types
|
||||
- Disable pack200
|
||||
|
||||
2019-11-19 [0.17.0]
|
||||
- Add more configuration, customization, and registration info to help page
|
||||
- Add variables for $VERSION and $SITENAME
|
||||
@ -15,8 +23,8 @@
|
||||
- Enhance presentation of help page
|
||||
- Modify docroot/index.html post-install to contain b32 footer
|
||||
- Only display warning about moving help.html if necessary
|
||||
- Add rewrite rules for /tracker, /tracker/, /tracker/index.html,
|
||||
/help and /help/ to jetty.xml
|
||||
- Add rewrite rules for /tracker/ and /tracker/index.html to web.xml
|
||||
- Add rewrite rules for /help and /help/ to jetty.xml
|
||||
- Replace jetty.servlet.DefaultServlet with I2PDefaultServlet in BaseContext.xml
|
||||
(requires I2P 0.9.31 or newer)
|
||||
|
||||
|
@ -15,6 +15,7 @@ There is also some code modified from Jetty 5.1.15. See LICENSES.txt for the
|
||||
zzzot and Jetty licenses.
|
||||
|
||||
I2P source must be installed and built in ../i2p.i2p to compile this package.
|
||||
I2P 1.7.0 or higher required to build and run.
|
||||
|
||||
Sure, as a standalone program in its own JVM with Jetty, this would be a pig -
|
||||
you should use the C opentracker instead. But since you're already running the
|
||||
@ -31,6 +32,7 @@ Valid announce URLs:
|
||||
/tracker/announce.jsp
|
||||
/tracker/announce.php
|
||||
|
||||
|
||||
Valid scrape URLs:
|
||||
/scrape
|
||||
/scrape.jsp
|
||||
@ -39,6 +41,8 @@ Valid scrape URLs:
|
||||
/tracker/scrape.jsp
|
||||
/tracker/scrape.php
|
||||
|
||||
|
||||
|
||||
The tracker also responds to seedless queries at:
|
||||
/Seedless/index.jsp
|
||||
|
||||
|
17
build.xml
17
build.xml
@ -16,7 +16,7 @@
|
||||
<delete dir="plugin/eepsite/docroot/torrents/" />
|
||||
<!-- get version number -->
|
||||
<buildnumber file="scripts/build.number" />
|
||||
<property name="release.number" value="0.17.0" />
|
||||
<property name="release.number" value="0.19.0" />
|
||||
|
||||
<!-- make the update xpi2p -->
|
||||
<!-- this contains everything except i2ptunnel.config -->
|
||||
@ -33,14 +33,17 @@
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
||||
<arg value="version=${release.number}-b${build.number}" />
|
||||
</exec>
|
||||
<mkdir dir="plugin/lib/" />
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="-g" />
|
||||
<arg value="plugin/lib/zzzot.jar.pack" />
|
||||
<arg value="-r" />
|
||||
<arg value="plugin/lib/zzzot.jar" />
|
||||
<arg value="src/build/zzzot.jar" />
|
||||
</exec>
|
||||
<mkdir dir="plugin/eepsite/webapps/" />
|
||||
<mkdir dir="plugin/eepsite/logs/" />
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="-g" />
|
||||
<arg value="plugin/eepsite/webapps/tracker.war.pack" />
|
||||
<arg value="-r" />
|
||||
<arg value="plugin/eepsite/webapps/tracker.war" />
|
||||
<arg value="src/build/tracker.war.jar" />
|
||||
</exec>
|
||||
<input message="Enter su3 signing key password:" addproperty="release.password.su3" />
|
||||
@ -87,8 +90,8 @@
|
||||
<delete file="plugin/eepsite/docroot/tracker.css" />
|
||||
<delete file="plugin/eepsite/docroot/tracker-purple.css" />
|
||||
<delete file="plugin/eepsite/docroot/favicon.png" />
|
||||
<delete file="plugin/lib/zzzot.jar.pack" />
|
||||
<delete file="plugin/eepsite/webapps/tracker.war.pack" />
|
||||
<delete file="plugin/lib/zzzot.jar" />
|
||||
<delete file="plugin/eepsite/webapps/tracker.war" />
|
||||
<delete file="plugin/CHANGES.txt" />
|
||||
<delete file="plugin/LICENSE.txt" />
|
||||
<delete file="plugin/README.txt" />
|
||||
|
@ -189,6 +189,13 @@
|
||||
<Set name="replacement">/tracker/scrape.jsp</Set>
|
||||
</New>
|
||||
</Item>
|
||||
<!-- BiglyBT -->
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
||||
<Set name="pattern">/a/scrape</Set>
|
||||
<Set name="replacement">/tracker/scrape.jsp</Set>
|
||||
</New>
|
||||
</Item>
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
||||
<Set name="pattern">/Seedless</Set>
|
||||
|
@ -7,6 +7,7 @@ tunnel.0.option.crypto.tagsToSend=10
|
||||
tunnel.0.option.i2cp.destination.sigType=7
|
||||
tunnel.0.option.i2cp.enableAccessList=false
|
||||
tunnel.0.option.i2cp.encryptLeaseSet=false
|
||||
tunnel.0.option.i2cp.leaseSetEncType=4,0
|
||||
tunnel.0.option.i2cp.reduceIdleTime=1200000
|
||||
tunnel.0.option.i2cp.reduceOnIdle=true
|
||||
tunnel.0.option.i2cp.reduceQuantity=1
|
||||
|
@ -7,6 +7,17 @@
|
||||
# zzz 2010-02
|
||||
# zzz 2014-08 added support for su3 files
|
||||
#
|
||||
|
||||
if [ -z "$I2P" -a -d "$PWD/../i2p.i2p/pkg-temp" ]; then
|
||||
export I2P=../i2p.i2p/pkg-temp
|
||||
fi
|
||||
|
||||
if [ ! -d "$I2P" ]; then
|
||||
echo "Can't locate your I2P installation. Please add a environment variable named I2P with the path to the folder as value"
|
||||
echo "On OSX this solved with running: export I2P=/Applications/i2p if default install directory is used."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PUBKEYDIR=$HOME/.i2p-plugin-keys
|
||||
PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key
|
||||
PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key
|
||||
@ -15,8 +26,6 @@ PUBKEYSTORE=$PUBKEYDIR/plugin-su3-public-signing.crt
|
||||
PRIVKEYSTORE=$PUBKEYDIR/plugin-su3-keystore.ks
|
||||
KEYTYPE=RSA_SHA512_4096
|
||||
|
||||
export I2P=../i2p.i2p/pkg-temp
|
||||
|
||||
PLUGINDIR=${1:-plugin}
|
||||
|
||||
PC=plugin.config
|
||||
|
@ -42,8 +42,8 @@ html, body {
|
||||
border: 1px solid #555;
|
||||
box-shadow: inset 0 0 0 1px #111, inset 0 0 2px 1px #444, 0 0 2px 2px #000;
|
||||
background: #181818;
|
||||
background: repeating-linear-gradient(to right, rgba(255, 255, 255, .05), rgba(0, 0, 0, .08) 2px),
|
||||
repeating-linear-gradient(to bottom, #222, #111 2px);
|
||||
background: repeating-linear-gradient(to right, rgba(255, 255, 255, .05), rgba(0, 0, 0, .08) 2px) center center / 2px 100%,
|
||||
repeating-linear-gradient(to bottom, #222, #111 2px) center center / 100% 2px;
|
||||
background-blend-mode: overlay;
|
||||
will-change: transform;
|
||||
}
|
||||
|
@ -1,3 +1,12 @@
|
||||
#
|
||||
# All changes require plugin restart
|
||||
#
|
||||
# announce interval in seconds
|
||||
# minimum 900 (15 minutes), maximum 21600 (6 hours)
|
||||
interval=1620
|
||||
#
|
||||
showfoooter=true
|
||||
#footerText=your html text here
|
||||
#
|
||||
# default false as of 0.19.0
|
||||
#allowFullScrape=false
|
||||
|
@ -25,7 +25,7 @@
|
||||
</target>
|
||||
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.7" />
|
||||
<property name="javac.version" value="1.8" />
|
||||
|
||||
<target name="compile">
|
||||
<mkdir dir="./build" />
|
||||
@ -62,6 +62,7 @@
|
||||
<arg value="build/web-fragment.xml" />
|
||||
<arg value="-webapp" />
|
||||
<arg value="jsp/" />
|
||||
<arg value="-die" />
|
||||
</java>
|
||||
|
||||
<javac
|
||||
|
@ -68,11 +68,17 @@ public class Peer extends HashMap<String, Object> {
|
||||
/** convert b64.i2p to a Hash, then to a binary string */
|
||||
/* or should we just store it in the constructor? cache it? */
|
||||
public String getHash() {
|
||||
String ip = (String) get("ip");
|
||||
byte[] b = Base64.decode(ip.substring(0, ip.length() - 4));
|
||||
Hash h = SHA256Generator.getInstance().calculateHash(b);
|
||||
try {
|
||||
return new String(h.getData(), "ISO-8859-1");
|
||||
return new String(getHashObject().getData(), "ISO-8859-1");
|
||||
} catch (UnsupportedEncodingException uee) { return null; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.19
|
||||
*/
|
||||
public Hash getHashObject() {
|
||||
String ip = (String) get("ip");
|
||||
byte[] b = Base64.decode(ip.substring(0, ip.length() - 4));
|
||||
return SHA256Generator.getInstance().calculateHash(b);
|
||||
}
|
||||
}
|
||||
|
346
src/java/net/i2p/zzzot/UDPHandler.java
Normal file
346
src/java/net/i2p/zzzot/UDPHandler.java
Normal file
@ -0,0 +1,346 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2022 zzz (zzz@mail.i2p)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.I2PSessionMuxedListener;
|
||||
import net.i2p.client.datagram.I2PDatagramDissector;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.i2ptunnel.I2PTunnel;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* Hook into the session and handle UDP announces
|
||||
* Ref: Proposal 160, BEP 15
|
||||
*
|
||||
* @since 0.19.0
|
||||
*/
|
||||
public class UDPHandler implements I2PSessionMuxedListener {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final I2PTunnel _tunnel;
|
||||
private final ZzzOT _zzzot;
|
||||
private final I2PDatagramDissector _diss;
|
||||
// conn ID to dest and time added
|
||||
private final Map<Long, DestAndTime> _connectCache;
|
||||
private final Cleaner _cleaner;
|
||||
|
||||
// The listen port.
|
||||
// We listen on all ports, so the announce URL
|
||||
// doesn't need a port.
|
||||
public static final int PORT = I2PSession.PORT_ANY;
|
||||
private static final long MAGIC = 0x41727101980L;
|
||||
private static final int ACTION_CONNECT = 0;
|
||||
private static final int ACTION_ANNOUNCE = 1;
|
||||
private static final int ACTION_SCRAPE = 2;
|
||||
private static final int ACTION_ERROR = 3;
|
||||
private static final int MAX_RESPONSES = 25;
|
||||
private static final int EVENT_NONE = 0;
|
||||
private static final int EVENT_COMPLETED = 1;
|
||||
private static final int EVENT_STARTED = 2;
|
||||
private static final int EVENT_STOPPED = 3;
|
||||
private static final long CLEAN_TIME = 2*60*1000;
|
||||
|
||||
|
||||
public UDPHandler(I2PAppContext ctx, I2PTunnel tunnel, ZzzOT zzzot) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(UDPHandler.class);
|
||||
_tunnel = tunnel;
|
||||
_zzzot = zzzot;
|
||||
_diss = new I2PDatagramDissector();
|
||||
_connectCache = new ConcurrentHashMap<Long, DestAndTime>();
|
||||
_cleaner = new Cleaner();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
(new I2PAppThread(new Waiter(), "ZzzOT UDP startup", true)).start();
|
||||
}
|
||||
|
||||
private class Waiter implements Runnable {
|
||||
public void run() {
|
||||
while (true) {
|
||||
// requires I2P 0.9.53 (1.7.0)
|
||||
List<I2PSession> sessions = _tunnel.getSessions();
|
||||
if (sessions.isEmpty()) {
|
||||
try { Thread.sleep(1000); } catch (InterruptedException ie) { break; }
|
||||
continue;
|
||||
}
|
||||
I2PSession session = sessions.get(0);
|
||||
session.addMuxedSessionListener(UDPHandler.this, I2PSession.PROTO_DATAGRAM, PORT);
|
||||
session.addMuxedSessionListener(UDPHandler.this, I2PSession.PROTO_DATAGRAM_RAW, PORT);
|
||||
_cleaner.schedule(CLEAN_TIME);
|
||||
if (_log.shouldInfo())
|
||||
_log.info("got session");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// begin listener methods ///
|
||||
|
||||
public void messageAvailable(I2PSession sess, int id, long size) {
|
||||
throw new IllegalStateException("muxed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void messageAvailable(I2PSession session, int id, long size, int proto, int fromPort, int toPort) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Got " + size + " bytes, proto: " + proto + " from port: " + fromPort + " to port: " + toPort);
|
||||
try {
|
||||
// receive message
|
||||
byte[] msg = session.receiveMessage(id);
|
||||
if (proto == I2PSession.PROTO_DATAGRAM) {
|
||||
// load datagram into it
|
||||
_diss.loadI2PDatagram(msg);
|
||||
handle(session, _diss.getSender(), fromPort, _diss.getPayload());
|
||||
} else if (proto == I2PSession.PROTO_DATAGRAM_RAW) {
|
||||
handle(session, null, fromPort, _diss.getPayload());
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping message with unknown protocol " + proto);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error receiving datagram", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void reportAbuse(I2PSession arg0, int arg1) {}
|
||||
|
||||
public void disconnected(I2PSession arg0) {
|
||||
_cleaner.cancel();
|
||||
_connectCache.clear();
|
||||
}
|
||||
|
||||
public void errorOccurred(I2PSession arg0, String arg1, Throwable arg2) {
|
||||
_log.error(arg1, arg2);
|
||||
}
|
||||
|
||||
/// end listener methods ///
|
||||
|
||||
private void handle(I2PSession session, Destination from, int fromPort, byte[] data) {
|
||||
int sz = data.length;
|
||||
if (sz < 16) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping short msg length " + sz);
|
||||
return;
|
||||
}
|
||||
long connID = DataHelper.fromLong8(data, 0);
|
||||
int action = (int) DataHelper.fromLong(data, 8, 4);
|
||||
if (action == ACTION_CONNECT) {
|
||||
if (connID != MAGIC) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping bad connect magic " + connID);
|
||||
return;
|
||||
}
|
||||
if (from == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping raw connect");
|
||||
return;
|
||||
}
|
||||
handleConnect(session, from, fromPort, data);
|
||||
} else if (action == ACTION_ANNOUNCE) {
|
||||
handleAnnounce(session, connID, from, fromPort, data);
|
||||
} else if (action == ACTION_SCRAPE) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("got unsupported scrape");
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping bad action " + action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from non-null
|
||||
*/
|
||||
private void handleConnect(I2PSession session, Destination from, int fromPort, byte[] data) {
|
||||
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||
long connID = _context.random().nextLong();
|
||||
byte[] resp = new byte[16];
|
||||
DataHelper.toLong(resp, 4, 4, transID);
|
||||
DataHelper.toLong8(resp, 8, connID);
|
||||
try {
|
||||
session.sendMessage(from, resp, I2PSession.PROTO_DATAGRAM_RAW, PORT, fromPort);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("sent connect reply to " + from);
|
||||
_connectCache.put(Long.valueOf(connID), new DestAndTime(from, _context.clock().now()));
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error sending connect reply", ise);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from may be null
|
||||
*/
|
||||
private void handleAnnounce(I2PSession session, long connID, Destination from, int fromPort, byte[] data) {
|
||||
int sz = data.length;
|
||||
if (sz < 96) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping short announce length " + sz);
|
||||
return;
|
||||
}
|
||||
if (from == null) {
|
||||
DestAndTime dat = _connectCache.get(Long.valueOf(connID));
|
||||
if (dat == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("no connID found " + connID);
|
||||
return;
|
||||
}
|
||||
from = dat.dest;
|
||||
}
|
||||
|
||||
// parse packet
|
||||
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||
byte[] bih = new byte[InfoHash.LENGTH];
|
||||
System.arraycopy(data, 16, bih, 0, InfoHash.LENGTH);
|
||||
InfoHash ih = new InfoHash(bih);
|
||||
byte[] bpid = new byte[PID.LENGTH];
|
||||
System.arraycopy(data, 36, bpid, 0, PID.LENGTH);
|
||||
PID pid = new PID(bpid);
|
||||
// ignored
|
||||
//long dl = DataHelper.fromLong8(data, 56);
|
||||
//long ul = DataHelper.fromLong8(data, 72);
|
||||
int event = (int) DataHelper.fromLong(data, 80, 4);
|
||||
long left = event == EVENT_COMPLETED ? 0 : DataHelper.fromLong8(data, 64);
|
||||
// ignored
|
||||
//long ip = DataHelper.fromLong(data, 84, 4);
|
||||
//long key = DataHelper.fromLong(data, 88, 4);
|
||||
long want = DataHelper.fromLong(data, 92, 4);
|
||||
if (want > MAX_RESPONSES)
|
||||
want = MAX_RESPONSES;
|
||||
// ignored
|
||||
//int port = (int) DataHelper.fromLong(data, 96, 2);
|
||||
|
||||
Torrents torrents = _zzzot.getTorrents();
|
||||
Peers peers = torrents.get(ih);
|
||||
if (peers == null && event != EVENT_STOPPED) {
|
||||
peers = new Peers();
|
||||
Peers p2 = torrents.putIfAbsent(ih, peers);
|
||||
if (p2 != null)
|
||||
peers = p2;
|
||||
}
|
||||
int size;
|
||||
int seeds;
|
||||
List<Peer> peerlist;
|
||||
if (event == EVENT_STOPPED) {
|
||||
if (peers != null)
|
||||
peers.remove(pid);
|
||||
peerlist = null;
|
||||
size = 0;
|
||||
seeds = 0;
|
||||
} else {
|
||||
Peer p = peers.get(pid);
|
||||
if (p == null) {
|
||||
ConcurrentMap<String, String> destCache = _zzzot.getDestCache();
|
||||
p = new Peer(pid.getData(), from, destCache);
|
||||
Peer p2 = peers.putIfAbsent(pid, p);
|
||||
if (p2 != null)
|
||||
p = p2;
|
||||
}
|
||||
p.setLeft(left);
|
||||
|
||||
size = peers.size();
|
||||
seeds = peers.countSeeds();
|
||||
if (want <= 0 || event == EVENT_STOPPED) {
|
||||
peerlist = null;
|
||||
} else {
|
||||
peerlist = new ArrayList<Peer>(peers.values());
|
||||
peerlist.remove(p); // them
|
||||
if (want < size - 1) {
|
||||
if (size > 150) {
|
||||
// If size is huge, use random iterator for efficiency
|
||||
List<Peer> rv = new ArrayList<Peer>(size);
|
||||
for (RandomIterator<Peer> iter = new RandomIterator<Peer>(peerlist); iter.hasNext(); ) {
|
||||
rv.add(iter.next());
|
||||
}
|
||||
peerlist = rv;
|
||||
} else {
|
||||
Collections.shuffle(peerlist, _context.random());
|
||||
peerlist = peerlist.subList(0, (int) want);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int count = peerlist != null ? peerlist.size() : 0;
|
||||
byte[] resp = new byte[22 + (32 * count)];
|
||||
resp[3] = (byte) ACTION_ANNOUNCE;
|
||||
DataHelper.toLong(resp, 4, 4, transID);
|
||||
DataHelper.toLong(resp, 8, 4, torrents.getInterval());
|
||||
DataHelper.toLong(resp, 12, 4, size - seeds);
|
||||
DataHelper.toLong(resp, 16, 4, seeds);
|
||||
DataHelper.toLong(resp, 20, 2, count);
|
||||
if (peerlist != null) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
System.arraycopy(peerlist.get(i).getHashObject().getData(), 0, resp, 22 + (i * 32), 32);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
session.sendMessage(from, resp, I2PSession.PROTO_DATAGRAM_RAW, PORT, fromPort);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("sent announce reply to " + from);
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error sending announce reply", ise);
|
||||
}
|
||||
}
|
||||
|
||||
private static class DestAndTime {
|
||||
public final Destination dest;
|
||||
public final long time;
|
||||
|
||||
public DestAndTime(Destination d, long t) {
|
||||
dest = d;
|
||||
time = t;
|
||||
}
|
||||
}
|
||||
|
||||
private class Cleaner extends SimpleTimer2.TimedEvent {
|
||||
|
||||
public Cleaner() { super(_context.simpleTimer2()); }
|
||||
|
||||
public void timeReached() {
|
||||
if (!_connectCache.isEmpty()) {
|
||||
long exp = _context.clock().now() - CLEAN_TIME;
|
||||
for (Iterator<DestAndTime> iter = _connectCache.values().iterator(); iter.hasNext(); ) {
|
||||
DestAndTime dat = iter.next();
|
||||
if (dat.time < exp)
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
schedule(CLEAN_TIME);
|
||||
}
|
||||
}
|
||||
}
|
@ -67,19 +67,22 @@ public class ZzzOTController implements ClientApp {
|
||||
private static volatile ZzzOTController _controller;
|
||||
// you wouldn't run two instances in the same JVM, would you?
|
||||
private static String _sitename;
|
||||
private static String _showfooter;
|
||||
private static boolean _showfooter;
|
||||
private static String _footertext;
|
||||
private static boolean _fullScrape;
|
||||
|
||||
private ClientAppState _state = UNINITIALIZED;
|
||||
|
||||
private static final String NAME = "ZzzOT";
|
||||
private static final String DEFAULT_SITENAME = "ZZZOT";
|
||||
private static final String PROP_SITENAME = "sitename";
|
||||
private static final String VERSION = "0.17.0";
|
||||
private static final String VERSION = "0.18.0";
|
||||
private static final String DEFAULT_SHOWFOOTER = "true";
|
||||
private static final String PROP_SHOWFOOTER = "showfooter";
|
||||
private static final String DEFAULT_FOOTERTEXT = "Running <a href=\"https://github.com/i2p/i2p.plugins.zzzot\" target=\"_blank\">ZZZOT</a> " + VERSION;
|
||||
private static final String DEFAULT_FOOTERTEXT = "Running <a href=\"http://git.idk.i2p/i2p-hackers/i2p.plugins.zzzot\" target=\"_blank\">ZZZOT</a> " + VERSION;
|
||||
private static final String PROP_FOOTERTEXT = "footertext";
|
||||
private static final String PROP_FULLSCRAPE = "allowFullScrape";
|
||||
private static final String DEFAULT_FULLSCRAPE = "false";
|
||||
private static final String CONFIG_FILE = "zzzot.config";
|
||||
private static final String BACKUP_SUFFIX = ".jetty8";
|
||||
private static final String[] xmlFiles = {
|
||||
@ -108,8 +111,9 @@ public class ZzzOTController implements ClientApp {
|
||||
}
|
||||
_zzzot = new ZzzOT(ctx, props);
|
||||
_sitename = props.getProperty(PROP_SITENAME, DEFAULT_SITENAME);
|
||||
_showfooter = props.getProperty(PROP_SHOWFOOTER, DEFAULT_SHOWFOOTER);
|
||||
_showfooter = Boolean.parseBoolean(props.getProperty(PROP_SHOWFOOTER, DEFAULT_SHOWFOOTER));
|
||||
_footertext = props.getProperty(PROP_FOOTERTEXT, DEFAULT_FOOTERTEXT);
|
||||
_fullScrape = Boolean.parseBoolean(props.getProperty(PROP_FULLSCRAPE, DEFAULT_FULLSCRAPE));
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
@ -182,6 +186,11 @@ public class ZzzOTController implements ClientApp {
|
||||
startJetty(pluginDir, dest);
|
||||
startI2PTunnel(pluginDir, dest);
|
||||
_zzzot.start();
|
||||
/*
|
||||
// requires I2P 0.9.53 (1.7.0)
|
||||
UDPHandler udp = new UDPHandler(_context, _tunnel.getTunnel(), _zzzot);
|
||||
udp.start();
|
||||
*/
|
||||
// SeedlessAnnouncer.announce(_tunnel);
|
||||
}
|
||||
|
||||
@ -195,6 +204,8 @@ public class ZzzOTController implements ClientApp {
|
||||
_log.error("Cannot open " + i2ptunnelConfig.getAbsolutePath() + ' ' + ioe);
|
||||
throw new IllegalArgumentException("Cannot open " + i2ptunnelConfig.getAbsolutePath() + ' ' + ioe);
|
||||
}
|
||||
if (i2ptunnelProps.getProperty("tunnel.0.option.i2cp.leaseSetEncType") == null)
|
||||
i2ptunnelProps.setProperty("tunnel.0.option.i2cp.leaseSetEncType", "4,0");
|
||||
TunnelController tun = new TunnelController(i2ptunnelProps, "tunnel.0.");
|
||||
if (dest != null) {
|
||||
// start in foreground so we can get the destination
|
||||
@ -487,7 +498,7 @@ public class ZzzOTController implements ClientApp {
|
||||
}
|
||||
|
||||
/** @since 0.17.0 */
|
||||
public static String shouldShowFooter() {
|
||||
public static boolean shouldShowFooter() {
|
||||
return _showfooter;
|
||||
}
|
||||
|
||||
@ -496,6 +507,11 @@ public class ZzzOTController implements ClientApp {
|
||||
return _footertext;
|
||||
}
|
||||
|
||||
/** @since 0.19.0 */
|
||||
public static boolean allowFullScrape() {
|
||||
return _fullScrape;
|
||||
}
|
||||
|
||||
/** @since 0.12.0 */
|
||||
private synchronized void changeState(ClientAppState state) {
|
||||
_state = state;
|
||||
|
@ -47,4 +47,10 @@
|
||||
<url-pattern>/scrape.php</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- BiglyBT -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>net.i2p.zzzot.scrape_jsp</servlet-name>
|
||||
<url-pattern>/a/scrape</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
|
@ -22,6 +22,16 @@
|
||||
<b>Peers:</b> <%=torrents.countPeers()%><br>
|
||||
</p>
|
||||
<%
|
||||
/*
|
||||
String host = request.getHeader("Host");
|
||||
if (host != null) {
|
||||
int colon = host.indexOf(":");
|
||||
if (colon > 0)
|
||||
host = host.substring(0, colon);
|
||||
host = net.i2p.data.DataHelper.escapeHTML(host);
|
||||
%><p><b>Now with UDP announce support!</b><br>udp://<%=host%>/</p><%
|
||||
}
|
||||
*/
|
||||
} else {
|
||||
%>
|
||||
<p id="initializing"><b><i>Initializing OpenTracker…</i></b></p>
|
||||
@ -29,8 +39,8 @@
|
||||
}
|
||||
%>
|
||||
<%
|
||||
String showfooter = ZzzOTController.shouldShowFooter();
|
||||
if (showfooter == "true") {
|
||||
boolean showfooter = ZzzOTController.shouldShowFooter();
|
||||
if (showfooter) {
|
||||
%>
|
||||
<span id="footer" class="version"><%=ZzzOTController.footerText()%></span>
|
||||
<%
|
||||
|
@ -50,8 +50,12 @@
|
||||
}
|
||||
|
||||
boolean all = info_hash == null;
|
||||
if (all && !ZzzOTController.allowFullScrape()) {
|
||||
fail = true;
|
||||
msg = "unsupported";
|
||||
}
|
||||
|
||||
Torrents torrents = ZzzOTController.getTorrents();
|
||||
Torrents torrents = fail ? null : ZzzOTController.getTorrents();
|
||||
if (torrents == null && !fail) {
|
||||
fail = true;
|
||||
msg = "tracker is down";
|
||||
|
Reference in New Issue
Block a user