Steal zzz's zzzot tracker. Start migration to I2PControl

This commit is contained in:
dev
2011-06-10 06:40:36 +00:00
parent 6e3b85ac97
commit c636af7ead
14 changed files with 21 additions and 778 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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