20 Commits

Author SHA1 Message Date
zzz
e1e29a953b 0.19.0 2024-04-07 15:56:21 -04:00
zzz
059f1cf6e7 Fix version in comment 2024-03-27 15:41:33 -04:00
zzz
32062dff20 Handle /a/scrape and /tracker/a/scrape
for BiglyBT
ref: BiglyBT issue 3211
2024-03-27 15:33:55 -04:00
zzz
7a6c0bcccd CSS update
courtesy drzed
2024-03-27 15:32:14 -04:00
zzz
1853bbfd0e Disable UDP support for now 2024-03-27 15:31:12 -04:00
zzz
5828c358f6 Build: Fail on jsp error 2023-11-28 13:05:57 -05:00
zzz
017d8a61c3 Fix missing eepsite/logs dir on install
after migration from mtn to git
as reported by drzed
2023-11-28 10:04:49 -05:00
zzz
cc640088c4 Add UDP announce URL to stats page 2022-01-15 12:10:39 -05:00
zzz
2016546fc1 UDP AIOOBE fix 2022-01-15 11:45:51 -05:00
zzz
9ed1a1ce0c Refactor / fix boolean showfooter config
Add full scrape config, default false
2022-01-15 11:38:07 -05:00
zzz
8fd17dd938 Use local RandomIterator class 2022-01-15 09:37:43 -05:00
zzz
51e4184ea8 Add note about build requirements 2022-01-14 08:00:22 -05:00
zzz
df683fcf5c Preliminary UDP support
WIP, untested
Requires router 1.6.1-8 or higher
ref: Proposal 160
2022-01-14 07:30:29 -05:00
zzz
ca66c075bf Build: Change target to java 8 2021-12-30 14:34:57 -05:00
zzz
f35827abcb Build: Create missing directories (since git doesn't) 2021-12-30 10:42:58 -05:00
zzz
290b7e9ba8 Change footer to point to git.idk.i2p 2020-11-21 09:55:29 -05:00
9a988e5c6d make it so you can set $I2P prior to running makeplugin.sh. 2020-10-15 01:11:59 +00:00
zzz
5d2343fe71 0.18.0
Enable ratchet by default
Disable pack200
2020-08-30 16:51:18 +00:00
zzz
94ccfa25e6 enable ratchet 2020-08-23 14:19:14 +00:00
zzz
36cee7777a changelog fix 2019-11-19 19:21:17 +00:00
15 changed files with 456 additions and 26 deletions

View File

@ -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)

View File

@ -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

View File

@ -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" />

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View 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);
}
}
}

View File

@ -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;

View File

@ -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>

View File

@ -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&hellip;</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>
<%

View File

@ -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";