Steal zzz's zzzot tracker. Start migration to I2PControl
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
Copyright 2010 zzz (zzz@mail.i2p)
|
||||
Copyright 2011 hottuna (dev@robertfoss.se)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
44
README.txt
44
README.txt
@ -1,42 +1,2 @@
|
||||
This is a very simple in-memory open tracker, wrapped into an I2P plugin.
|
||||
|
||||
The plugin starts a new http serer tunnel, eepsite, and Jetty server running at port 7662.
|
||||
The tracker status is available at http://127.0.0.1:7661/tracker/ .
|
||||
If other files are desired on the eepsite, they can be added at eepsite/docroot .
|
||||
|
||||
The open tracker code and jsps were written from scratch, but depend on some code
|
||||
in i2psnark.jar from the I2P installation for bencoding, and of course
|
||||
on other i2p libraries.
|
||||
See the license files in I2P for i2p and i2psnark licenses.
|
||||
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.
|
||||
|
||||
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 JVM and Jetty, running this in the same JVM probably doesn't hog to much more memory.
|
||||
|
||||
Valid announce URLs:
|
||||
/a
|
||||
/announce
|
||||
/announce.jsp
|
||||
/announce.php
|
||||
/tracker/a
|
||||
/tracker/announce
|
||||
/tracker/announce.jsp
|
||||
/tracker/announce.php
|
||||
|
||||
Valid scrape URLs:
|
||||
/scrape
|
||||
/scrape.jsp
|
||||
/scrape.php
|
||||
/tracker/scrape
|
||||
/tracker/scrape.jsp
|
||||
/tracker/scrape.php
|
||||
|
||||
The tracker also responds to seedless queries at
|
||||
/Seedless/index.jsp
|
||||
|
||||
You may use the rest of the eepsite for other purposes, for example you
|
||||
may place torrent files in eepsite/docroot/torrents.
|
||||
I2PControl
|
||||
API for remote control of I2P via a JSON-RPC library.
|
||||
|
20
TODO.txt
20
TODO.txt
@ -1,19 +1 @@
|
||||
Configuration file:
|
||||
- interval
|
||||
- clean time
|
||||
- max peers in response
|
||||
- disable full scrapes
|
||||
- disable all scrapes
|
||||
- disable seedless
|
||||
|
||||
Stop the cleaner
|
||||
|
||||
Throttles:
|
||||
- full scrapes
|
||||
- per-requestor
|
||||
|
||||
Bans:
|
||||
- refuse non-GETs
|
||||
|
||||
Verifier:
|
||||
- Check dest vs. b32 in header
|
||||
http://zzz.i2p/topics/888?page=1#p3440
|
||||
|
16
build.xml
16
build.xml
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project basedir="." default="all" name="zzzot">
|
||||
<project basedir="." default="all" name="I2PControl">
|
||||
|
||||
<target name="all" depends="clean,plugin" />
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<delete file="plugin/i2ptunnel.config" />
|
||||
<!-- get version number -->
|
||||
<buildnumber file="scripts/build.number" />
|
||||
<property name="release.number" value="0.5" />
|
||||
<property name="release.number" value="0.0" />
|
||||
|
||||
<!-- make the update xpi2p -->
|
||||
<!-- this contains everything except i2ptunnel.config -->
|
||||
@ -26,8 +26,8 @@
|
||||
</exec>
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="-g" />
|
||||
<arg value="plugin/lib/zzzot.jar.pack" />
|
||||
<arg value="src/build/zzzot.jar" />
|
||||
<arg value="plugin/lib/I2PControl.jar.pack" />
|
||||
<arg value="src/build/I2PControl.jar" />
|
||||
</exec>
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="-g" />
|
||||
@ -37,7 +37,7 @@
|
||||
<exec executable="scripts/makeplugin.sh" failonerror="true" >
|
||||
<arg value="plugin" />
|
||||
</exec>
|
||||
<move file="zzzot.xpi2p" tofile="zzzot-update.xpi2p" overwrite="true" />
|
||||
<move file="I2PControl.xpi2p" tofile="I2PControl-update.xpi2p" overwrite="true" />
|
||||
|
||||
<!-- make the install xpi2p -->
|
||||
<copy file="scripts/i2ptunnel.config" todir="plugin/" overwrite="true" />
|
||||
@ -56,12 +56,12 @@
|
||||
<ant dir="src" target="clean" />
|
||||
<delete file="plugin/i2ptunnel.config" />
|
||||
<delete file="plugin/plugin.config" />
|
||||
<delete file="plugin/lib/zzzot.jar.pack" />
|
||||
<delete file="plugin/lib/I2PControl.jar.pack" />
|
||||
<delete file="plugin/eepsite/webapps/tracker.war.pack" />
|
||||
<delete file="plugin/LICENSE.txt" />
|
||||
<delete file="plugin/README.txt" />
|
||||
<delete file="zzzot.xpi2p" />
|
||||
<delete file="zzzot-update.xpi2p" />
|
||||
<delete file="I2PControl.xpi2p" />
|
||||
<delete file="I2PControl-update.xpi2p" />
|
||||
</target>
|
||||
|
||||
</project>
|
||||
|
@ -1,8 +1,7 @@
|
||||
name=zzzot
|
||||
signer=zzz-plugin@mail.i2p
|
||||
consoleLinkName=ZzzOT
|
||||
consoleLinkURL=http://127.0.0.1:7662/tracker/index.jsp
|
||||
description=Open tracker
|
||||
author=zzz
|
||||
updateURL=http://stats.i2p/i2p/plugins/zzzot-update.xpi2p
|
||||
name=I2PControl
|
||||
signer=dev@robertfoss.se
|
||||
consoleLinkName=I2PControl
|
||||
consoleLinkURL=http://127.0.0.1:7662/I2PControl/index.jsp
|
||||
description=Remote
|
||||
author=hottuna
|
||||
license=Apache 2.0
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project basedir="." default="all" name="i2psnark">
|
||||
<project basedir="." default="all" name="I2PControl">
|
||||
<property name="i2pbase" value="../../i2p.i2p"/>
|
||||
<property name="i2plib" value="${i2pbase}/build"/>
|
||||
<property name="jettylib" value="${i2pbase}/apps/jetty/jettylib"/>
|
||||
@ -39,7 +39,7 @@
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="builddep, compile">
|
||||
<jar destfile="build/zzzot.jar" basedir="./build/obj" includes="**/*.class" >
|
||||
<jar destfile="build/I2PControl.jar" basedir="./build/obj" includes="**/*.class" >
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
<arg value="build/jspjava" />
|
||||
<arg value="-v" />
|
||||
<arg value="-p" />
|
||||
<arg value="net.i2p.zzzot" />
|
||||
<arg value="net.i2p.I2PControl" />
|
||||
<arg value="-webinc" />
|
||||
<arg value="build/web-fragment.xml" />
|
||||
<arg value="-webapp" />
|
||||
|
@ -1,37 +0,0 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2010 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.io.UnsupportedEncodingException;
|
||||
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
/**
|
||||
* A 20-byte SHA1 info hash
|
||||
*/
|
||||
public class InfoHash extends ByteArray {
|
||||
|
||||
public InfoHash(String data) throws UnsupportedEncodingException {
|
||||
this(data.getBytes("ISO-8859-1"));
|
||||
}
|
||||
|
||||
public InfoHash(byte[] data) {
|
||||
super(data);
|
||||
if (data.length != 20)
|
||||
throw new IllegalArgumentException("Bad infohash length: " + data.length);
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2010 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.io.UnsupportedEncodingException;
|
||||
|
||||
import net.i2p.data.ByteArray;
|
||||
|
||||
/**
|
||||
* A 20-byte peer ID
|
||||
*/
|
||||
public class PID extends ByteArray {
|
||||
|
||||
public PID(String data) throws UnsupportedEncodingException {
|
||||
this(data.getBytes("ISO-8859-1"));
|
||||
}
|
||||
|
||||
public PID(byte[] data) {
|
||||
super(data);
|
||||
if (data.length != 20)
|
||||
throw new IllegalArgumentException("Bad peer ID length: " + data.length);
|
||||
}
|
||||
}
|
@ -1,92 +0,0 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2010 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.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/*
|
||||
* A single peer for a single torrent.
|
||||
* Save a couple stats, and implements
|
||||
* a Map so we can BEncode it
|
||||
* So it's like PeerID but in reverse - we make a Map from the
|
||||
* data. PeerID makes the data from a Map.
|
||||
*/
|
||||
public class Peer extends HashMap<String, Object> {
|
||||
|
||||
private long lastSeen;
|
||||
private long bytesLeft;
|
||||
private static final ConcurrentHashMap<String, String> destCache = new ConcurrentHashMap();
|
||||
private static final Integer PORT = Integer.valueOf(6881);
|
||||
private static final long CLEAN_TIME = 3*60*60*1000;
|
||||
|
||||
static {
|
||||
SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
|
||||
}
|
||||
|
||||
public Peer(byte[] id, Destination address) {
|
||||
super(3);
|
||||
if (id.length != 20)
|
||||
throw new IllegalArgumentException("Bad peer ID length: " + id.length);
|
||||
put("peer id", id);
|
||||
put("port", PORT);
|
||||
// cache the 520-byte address strings
|
||||
String dest = address.toBase64() + ".i2p";
|
||||
String oldDest = destCache.putIfAbsent(dest, dest);
|
||||
if (oldDest != null)
|
||||
dest = oldDest;
|
||||
put("ip", dest);
|
||||
}
|
||||
|
||||
public void setLeft(long l) {
|
||||
bytesLeft = l;
|
||||
lastSeen = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean isSeed() {
|
||||
return bytesLeft <= 0;
|
||||
}
|
||||
|
||||
public long lastSeen() {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
/** 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");
|
||||
} catch (UnsupportedEncodingException uee) { return null; }
|
||||
}
|
||||
|
||||
private static class Cleaner implements SimpleTimer.TimedEvent {
|
||||
public void timeReached() {
|
||||
destCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2010 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.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* All the peers for a single torrent
|
||||
*/
|
||||
public class Peers extends ConcurrentHashMap<PID, Peer> {
|
||||
|
||||
public Peers() {
|
||||
super();
|
||||
}
|
||||
|
||||
public int countSeeds() {
|
||||
int rv = 0;
|
||||
for (Peer p : values()) {
|
||||
if (p.isSeed())
|
||||
rv++;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
public int countLeeches() {
|
||||
return size() - countSeeds();
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
// ========================================================================
|
||||
// $Id: ForwardHandler.java,v 1.16 2005/08/13 00:01:26 gregwilkins Exp $
|
||||
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.
|
||||
// ========================================================================
|
||||
|
||||
package net.i2p.zzzot;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
import org.mortbay.http.HttpException;
|
||||
import org.mortbay.http.HttpMessage;
|
||||
import org.mortbay.http.HttpRequest;
|
||||
import org.mortbay.http.HttpResponse;
|
||||
import org.mortbay.http.PathMap;
|
||||
import org.mortbay.http.handler.AbstractHttpHandler;
|
||||
import org.mortbay.util.URI;
|
||||
import org.mortbay.util.UrlEncoded;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Forward Request Handler.
|
||||
* Forwards a request to a new URI. Experimental - use with caution.
|
||||
* @version $Revision: 1.16 $
|
||||
* @author Greg Wilkins (gregw)
|
||||
*
|
||||
* Just like ForwardHandler but forwards query parameters too
|
||||
* And took out the dependency on apache logging
|
||||
* @author zzz
|
||||
*/
|
||||
public class QForwardHandler extends AbstractHttpHandler
|
||||
{
|
||||
PathMap _forward = new PathMap();
|
||||
String _root;
|
||||
boolean _handleQueries = false;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constructor.
|
||||
*/
|
||||
public QForwardHandler()
|
||||
{}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Constructor.
|
||||
* @param rootForward
|
||||
*/
|
||||
public QForwardHandler(String rootForward)
|
||||
{
|
||||
_root=rootForward;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add a forward mapping.
|
||||
* @param pathSpecInContext The path to forward from
|
||||
* @param newPath The path to forward to.
|
||||
*/
|
||||
public void addForward(String pathSpecInContext,
|
||||
String newPath)
|
||||
{
|
||||
_forward.put(pathSpecInContext,newPath);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Add a forward mapping for root path.
|
||||
* This allows a forward for exactly / which is the default
|
||||
* path in a pathSpec.
|
||||
* @param newPath The path to forward to.
|
||||
*/
|
||||
public void setRootForward(String newPath)
|
||||
{
|
||||
_root=newPath;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Set the Handler up to cope with forwards to paths that contain query
|
||||
* elements (e.g. "/blah"->"/foo?a=b").
|
||||
* AND (I2P) pass params through (e.g. "/blah?c=d? -> "/foo?c=d? ).
|
||||
* @param b
|
||||
*/
|
||||
public void setHandleQueries(boolean b)
|
||||
{
|
||||
_handleQueries = b;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void handle(String pathInContext,
|
||||
String pathParams,
|
||||
HttpRequest request,
|
||||
HttpResponse response)
|
||||
throws HttpException, IOException
|
||||
{
|
||||
String newPath=null;
|
||||
String query=null;
|
||||
if (_root!=null && ("/".equals(pathInContext) || pathInContext.startsWith("/;")))
|
||||
newPath=_root;
|
||||
else
|
||||
{
|
||||
Map.Entry entry = _forward.getMatch(pathInContext);
|
||||
if (entry!=null)
|
||||
{
|
||||
String match = (String)entry.getValue();
|
||||
if (_handleQueries)
|
||||
{
|
||||
int hook = match.indexOf('?');
|
||||
if (hook != -1){
|
||||
query = match.substring(hook+1);
|
||||
match = match.substring(0, hook);
|
||||
}
|
||||
}
|
||||
String info=PathMap.pathInfo((String)entry.getKey(),pathInContext);
|
||||
newPath=info==null?match:(URI.addPaths(match,info));
|
||||
}
|
||||
}
|
||||
|
||||
if (newPath!=null)
|
||||
{
|
||||
// this is the new part for i2p
|
||||
// setPath() changes the request URI and loses the parameters
|
||||
// so save them and add them back
|
||||
Map saved = null;
|
||||
if (_handleQueries){
|
||||
request.setCharacterEncoding("ISO-8859-1", false);
|
||||
saved = request.getParameters();
|
||||
}
|
||||
|
||||
int last=request.setState(HttpMessage.__MSG_EDITABLE);
|
||||
String context=getHttpContext().getContextPath();
|
||||
if (context.length()==1)
|
||||
request.setPath(newPath);
|
||||
else
|
||||
request.setPath(URI.addPaths(context,newPath));
|
||||
if (_handleQueries && query != null){
|
||||
// add forwarded to query string to parameters
|
||||
UrlEncoded.decodeTo(query, request.getParameters());
|
||||
}
|
||||
|
||||
// this is the new part for i2p
|
||||
if (_handleQueries){
|
||||
// add them back
|
||||
request.getParameters().putAll(saved);
|
||||
}
|
||||
|
||||
request.setState(last);
|
||||
getHttpContext().getHttpServer().service(request,response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2010 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.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* All the torrents
|
||||
*/
|
||||
public class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
|
||||
|
||||
public Torrents() {
|
||||
super();
|
||||
}
|
||||
|
||||
public int countPeers() {
|
||||
int rv = 0;
|
||||
for (Peers p : values()) {
|
||||
rv += p.size();
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2010 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.Iterator;
|
||||
|
||||
import net.i2p.util.SimpleScheduler;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
|
||||
/**
|
||||
* Instantiate this to fire it up
|
||||
*/
|
||||
class ZzzOT {
|
||||
|
||||
private Torrents _torrents;
|
||||
private static final long CLEAN_TIME = 4*60*1000;
|
||||
private static final long EXPIRE_TIME = 60*60*1000;
|
||||
|
||||
ZzzOT() {
|
||||
_torrents = new Torrents();
|
||||
SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
|
||||
}
|
||||
|
||||
Torrents getTorrents() {
|
||||
return _torrents;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_torrents.clear();
|
||||
// no way to stop the cleaner
|
||||
}
|
||||
|
||||
private class Cleaner implements SimpleTimer.TimedEvent {
|
||||
|
||||
public void timeReached() {
|
||||
long now = System.currentTimeMillis();
|
||||
for (Iterator<Peers> iter = _torrents.values().iterator(); iter.hasNext(); ) {
|
||||
Peers p = iter.next();
|
||||
int recent = 0;
|
||||
for (Iterator<Peer> iterp = p.values().iterator(); iterp.hasNext(); ) {
|
||||
Peer peer = iterp.next();
|
||||
if (peer.lastSeen() < now - EXPIRE_TIME)
|
||||
iterp.remove();
|
||||
else
|
||||
recent++;
|
||||
}
|
||||
if (recent <= 0)
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,229 +0,0 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2010 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.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.PrivateKeyFile;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.i2ptunnel.TunnelController;
|
||||
import net.i2p.apps.systray.UrlLauncher;
|
||||
|
||||
import org.mortbay.http.HttpContext;
|
||||
import org.mortbay.jetty.Server;
|
||||
|
||||
/**
|
||||
* This handles the starting and stopping of an eepsite tunnel and jetty
|
||||
* from a single static class so it can be called via clients.config.
|
||||
*
|
||||
* This makes installation of a new eepsite a turnkey operation -
|
||||
* the user is not required to configure a new tunnel in i2ptunnel manually.
|
||||
*
|
||||
* Usage: ZzzOTController -d $PLUGIN [start|stop]
|
||||
*
|
||||
* @author zzz
|
||||
*/
|
||||
public class ZzzOTController {
|
||||
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ZzzOTController.class);
|
||||
private static Server _server;
|
||||
private static TunnelController _tunnel;
|
||||
private static ZzzOT _zzzot;
|
||||
private static Object _lock = new Object();
|
||||
|
||||
public static void main(String args[]) {
|
||||
if (args.length != 3 || (!"-d".equals(args[0])))
|
||||
throw new IllegalArgumentException("Usage: PluginController -d $PLUGIN [start|stop]");
|
||||
if ("start".equals(args[2]))
|
||||
start(args);
|
||||
else if ("stop".equals(args[2]))
|
||||
stop();
|
||||
else
|
||||
throw new IllegalArgumentException("Usage: PluginController -d $PLUGIN [start|stop]");
|
||||
}
|
||||
|
||||
public static Torrents getTorrents() {
|
||||
synchronized(_lock) {
|
||||
if (_zzzot == null)
|
||||
_zzzot = new ZzzOT();
|
||||
}
|
||||
return _zzzot.getTorrents();
|
||||
}
|
||||
|
||||
private static void start(String args[]) {
|
||||
File pluginDir = new File(args[1]);
|
||||
if (!pluginDir.exists())
|
||||
throw new IllegalArgumentException("Plugin directory " + pluginDir.getAbsolutePath() + " does not exist");
|
||||
|
||||
// We create the private key file in advance, so that we can
|
||||
// create the help.html file from the templates
|
||||
// without waiting for i2ptunnel to create it AND build the tunnels before returning.
|
||||
Destination dest = null;
|
||||
File key = new File(pluginDir, "eepPriv.dat");
|
||||
if (!key.exists()) {
|
||||
PrivateKeyFile pkf = new PrivateKeyFile(new File(pluginDir, "eepPriv.dat"));
|
||||
try {
|
||||
dest = pkf.createIfAbsent();
|
||||
} catch (Exception e) {
|
||||
_log.error("Unable to create " + key.getAbsolutePath() + ' ' + e);
|
||||
throw new IllegalArgumentException("Unable to create " + key.getAbsolutePath() + ' ' + e);
|
||||
}
|
||||
_log.error("NOTICE: ZzzOT: New eepsite keys created in " + key.getAbsolutePath());
|
||||
_log.error("NOTICE: ZzzOT: You should back up this file!");
|
||||
String b32 = Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
|
||||
String b64 = dest.toBase64();
|
||||
_log.error("NOTICE: ZzzOT: Your base 32 address is " + b32);
|
||||
_log.error("NOTICE: ZzzOT: Your base 64 address is " + b64);
|
||||
}
|
||||
startJetty(pluginDir, dest);
|
||||
startI2PTunnel(pluginDir, dest);
|
||||
}
|
||||
|
||||
|
||||
private static void startI2PTunnel(File pluginDir, Destination dest) {
|
||||
File i2ptunnelConfig = new File(pluginDir, "i2ptunnel.config");
|
||||
Properties i2ptunnelProps = new Properties();
|
||||
try {
|
||||
DataHelper.loadProps(i2ptunnelProps, i2ptunnelConfig);
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Cannot open " + i2ptunnelConfig.getAbsolutePath() + ' ' + ioe);
|
||||
throw new IllegalArgumentException("Cannot open " + i2ptunnelConfig.getAbsolutePath() + ' ' + ioe);
|
||||
}
|
||||
TunnelController tun = new TunnelController(i2ptunnelProps, "tunnel.0.");
|
||||
// start in foreground so we can get the destination
|
||||
//tun.startTunnelBackground();
|
||||
tun.startTunnel();
|
||||
if (dest != null) {
|
||||
List msgs = tun.clearMessages();
|
||||
for (Object s : msgs) {
|
||||
_log.error("NOTICE: ZzzOT Tunnel message: " + s);
|
||||
}
|
||||
}
|
||||
_tunnel = tun;
|
||||
}
|
||||
|
||||
private static void startJetty(File pluginDir, Destination dest) {
|
||||
if (_server != null)
|
||||
throw new IllegalArgumentException("Jetty already running!");
|
||||
migrateJettyXML(pluginDir);
|
||||
I2PAppContext context = I2PAppContext.getGlobalContext();
|
||||
File tmpdir = new File(context.getTempDir().getAbsolutePath(), "/zzzot-work");
|
||||
tmpdir.mkdir();
|
||||
File jettyXml = new File(pluginDir, "jetty.xml");
|
||||
try {
|
||||
Server serv = new Server(jettyXml.getAbsolutePath());
|
||||
HttpContext[] hcs = serv.getContexts();
|
||||
for (int i = 0; i < hcs.length; i++)
|
||||
hcs[i].setTempDirectory(tmpdir);
|
||||
serv.start();
|
||||
_server = serv;
|
||||
} catch (Throwable t) {
|
||||
_log.error("ZzzOT jetty start failed", t);
|
||||
throw new IllegalArgumentException("Jetty start failed " + t);
|
||||
}
|
||||
if (dest != null)
|
||||
launchHelp(pluginDir, dest);
|
||||
}
|
||||
|
||||
private static void stop() {
|
||||
stopI2PTunnel();
|
||||
stopJetty();
|
||||
if (_zzzot != null)
|
||||
_zzzot.stop();
|
||||
}
|
||||
|
||||
private static void stopI2PTunnel() {
|
||||
if (_tunnel == null)
|
||||
return;
|
||||
try {
|
||||
_tunnel.stopTunnel();
|
||||
} catch (Throwable t) {
|
||||
_log.error("ZzzOT tunnel stop failed", t);
|
||||
throw new IllegalArgumentException("Tunnel stop failed " + t);
|
||||
}
|
||||
_tunnel = null;
|
||||
}
|
||||
|
||||
private static void stopJetty() {
|
||||
if (_server == null)
|
||||
return;
|
||||
try {
|
||||
_server.stop();
|
||||
} catch (Throwable t) {
|
||||
_log.error("ZzzOT jetty stop failed", t);
|
||||
throw new IllegalArgumentException("Jetty stop failed " + t);
|
||||
}
|
||||
_server = null;
|
||||
}
|
||||
|
||||
/** put the directory in the jetty.xml file */
|
||||
private static void migrateJettyXML(File pluginDir) {
|
||||
File outFile = new File(pluginDir, "jetty.xml");
|
||||
if (outFile.exists())
|
||||
return;
|
||||
File fileTmpl = new File(pluginDir, "templates/jetty.xml");
|
||||
try {
|
||||
String props = FileUtil.readTextFile(fileTmpl.getAbsolutePath(), 250, true);
|
||||
if (props == null)
|
||||
throw new IOException(fileTmpl.getAbsolutePath() + " open failed");
|
||||
props = props.replace("$PLUGIN", pluginDir.getAbsolutePath());
|
||||
FileOutputStream os = new FileOutputStream(outFile);
|
||||
os.write(props.getBytes("UTF-8"));
|
||||
os.close();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("jetty.xml migrate failed", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
/** put the directory, base32, and base64 info in the help.html file and launch a browser window to display it */
|
||||
private static void launchHelp(File pluginDir, Destination dest) {
|
||||
File fileTmpl = new File(pluginDir, "templates/help.html");
|
||||
File outFile = new File(pluginDir, "eepsite/docroot/help.html");
|
||||
String b32 = Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
|
||||
String b64 = dest.toBase64();
|
||||
try {
|
||||
String html = FileUtil.readTextFile(fileTmpl.getAbsolutePath(), 100, true);
|
||||
if (html == null)
|
||||
throw new IOException(fileTmpl.getAbsolutePath() + " open failed");
|
||||
html = html.replace("$PLUGIN", pluginDir.getAbsolutePath());
|
||||
html = html.replace("$B32", b32);
|
||||
html = html.replace("$B64", b64);
|
||||
FileOutputStream os = new FileOutputStream(outFile);
|
||||
os.write(html.getBytes("UTF-8"));
|
||||
os.close();
|
||||
Thread t = new I2PAppThread(new Launcher(), "ZzzOTHelp", true);
|
||||
t.start();
|
||||
} catch (IOException ioe) {
|
||||
_log.error("ZzzOT help launch failed", ioe);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Launcher implements Runnable {
|
||||
public void run() {
|
||||
UrlLauncher.main(new String[] { "http://127.0.0.1:7662/help.html" } );
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user