40 Commits

Author SHA1 Message Date
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
zzz
2db3525fbb 0.17.0
css tweak
2019-11-19 19:03:02 +00:00
zzz
ea3fa57b04 Moved new webapp mappings from jetty.xml to web.xml
so they work for existing installs
2019-11-19 16:47:06 +00:00
zzz
66ba3f3e76 make me the signer again 2019-11-19 12:17:42 +00:00
zzz
66900c2fbc Start tunnel in background after first install 2019-11-19 12:14:36 +00:00
zzz
96fa6adfad Patch from dr|z3d
- Include alternative tracker-purple.css
- Add favicon.png to docroot
- Add custom icon for homepage
- Add auto page reload for tracker stats (ajax with meta refresh fallback)
- Add footer to /tracker with ZzzOT version and github link
- Add config options to hide version footer on /tracker or change footer text
images by dr|z3d, same license as the code
2019-11-19 12:11:13 +00:00
zzz
1227c727d0 Modified UI patch from dr|z3d
license for mods to zzzot by dr|z3d as per the overall Apache 2 license.
  - Add variables for $VERSION and $SITENAME
  - Enhance presentation of help page
  - Modify docroot/index.html post-install to contain b32
  - Add custom CSS to docroot/tracker.css to allow customization
  - Only display warning about moving help.html if necessary
  - Add rewrite rules for /tracker, /tracker/, /tracker/index.html,
    /help and /help/ to jetty.xml
  - Replace jetty.servlet.DeaultServlet with I2PDefaultServlet in BaseContext.xml
  - Tidy CHANGES.txt and add to plugin/
Bump min I2P version to 0.9.31 for I2PDefaultServlet
2019-11-13 12:35:29 +00:00
zzz
d29211301e fix I2P location in makeplugin.sh 2019-10-12 16:08:24 +00:00
zzz
1b1c5a1c6b Change default sig type to EdDSA 2018-02-21 12:34:14 +00:00
zzz
d72c0c6d61 Hide home dir on help page 2017-12-12 13:06:26 +00:00
zzz
ae250314b0 Add more configuration and customization to help page 2017-12-03 20:47:34 +00:00
zzz
175515f629 Add registration authentication info to help 2017-12-03 20:34:32 +00:00
zzz
c2482d68c8 Fix bencoded scrape response for zzzot (ticket #1994)
requires I2P 0.9.30-8
2017-05-21 18:45:24 +00:00
zzz
a39186cfff typo 2017-04-17 01:10:28 +00:00
zzz
22cf9e06ff 0.15.0 2017-03-11 18:23:14 +00:00
zzz
758c0865dc clean up warnings 2017-03-07 15:33:33 +00:00
zzz
ab14737fd1 For webapp also:
Pull in override.properties for bootclasspath
Bump to java 1.6
2016-08-14 15:38:58 +00:00
zzz
dd0d7ba6c9 Pull in override.properties for bootclasspath
Bump to java 1.6
2016-08-14 15:33:23 +00:00
zzz
d3ccdb5970 increase limits, set tag options 2016-06-13 16:34:35 +00:00
zzz
404ec7d720 update for 0.9.17 2016-06-13 16:34:18 +00:00
zzz
fa37064834 0.14.0 2015-05-28 13:00:07 +00:00
zzz
7cc2d97825 Remove use of deprecated SimpleScheduler
Remove static structures
Use RandomIterator for efficiency
untested
2015-04-16 22:33:50 +00:00
zzz
9ffef703b5 Increase startup delay, so it's after the eepsite Jetty,
so it doesn't create the static RolloverFileOutputStream Timer thread,
so PluginStarter doesn't think it's running after it stops.
2014-11-29 14:05:07 +00:00
zzz
5239dfb768 fix NPE on missing announce parameters 2014-11-26 20:52:58 +00:00
zzz
953533c6e3 0.12.0 2014-11-13 20:15:02 +00:00
zzz
2e0f7ae4bf Release all resources when shut down (requires 0.9.16-6 or higher)
log tweaks
2014-11-13 16:01:15 +00:00
zzz
17e5595dd0 Don't overwrite index.html and robots.txt in the update
More build and clean fixes
2014-11-12 23:10:19 +00:00
zzz
8350d009d4 Add zzzot.config file to set interval 2014-11-12 15:19:13 +00:00
zzz
a7225a2379 Fix parameter decoding for scrape also
Add caching for info hashes and peer ids; move from ByteArray to SDS
Stop cleaner when plugin stops
Move to the ClientApp interface, remove all static refs
Attempt to fix crash after update
2014-11-12 14:48:51 +00:00
zzz
8a9094cd8e Fix critical bug preventing announces from working
0.11.0
2014-11-11 14:08:43 +00:00
zzz
7f4394aa0f add max-jetty-version 2014-10-24 14:43:51 +00:00
zzz
38f38398ef switch Jetty to QTP 2014-10-24 13:01:28 +00:00
zzz
8317e3ce9a Change new install logging from ERROR to INFO
Move up warning about removing help file
2014-08-30 14:03:56 +00:00
zzz
c1f6a14367 add su3 url 2014-08-09 18:47:16 +00:00
zzz
7e36455b05 add support for building su3 plugins 2014-08-09 18:00:17 +00:00
zzz
a85aa6af31 more import cleanup 2014-08-09 16:25:44 +00:00
zzz
f33303f394 Use cached Destinations
Specify min I2P version of 0.9.9 for Destination.create()
2014-08-09 16:21:02 +00:00
zzz
ab882f8582 0.10.0:
Updates and migration for Jetty 7 (I2P 0.9.6)
2013-04-15 14:09:10 +00:00
34 changed files with 2198 additions and 474 deletions

View File

@ -1,45 +1,97 @@
0.9.0 2020-08-30 [0.18.0]
2013-01-25 - Enable both encryption types
Add cache-control directives - Disable pack200
Set max Jetty to 6.99999
0.8 2019-11-19 [0.17.0]
2012-03-10 - Add more configuration, customization, and registration info to help page
fix comment in jetty.xml - Add variables for $VERSION and $SITENAME
- Add custom CSS to docroot/tracker.css to allow customization and include
alternative tracker-purple.css
- Add favicon.png to docroot
- Add custom icon for homepage
- Add auto page reload for tracker stats (ajax with meta refresh fallback)
- Add footer to /tracker with ZzzOT version and github link
- Add config options to hide version footer on /tracker or change footer text
- Tidy CHANGES.txt and add to plugin/
- Start tunnel in background after first install
- Changes for new installs only:
- Change default sig type to EdDSA
- 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/ 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)
0.7 2017-05-21 [0.16.0]
2012-03-10 - Fix scrape response (requires I2P 0.9.30-8) (ticket #1994)
Port to Jetty 6
Replace QForwardHandler with RewriteHandler
Use ${ant.home}/lib/ant.jar instead of pulling ant.jar from Jetty
0.6
2011-12-31
Set max jetty version 5.99999
Add throttle options
Stub out announce-to-seedless
Seedless fixes, untested
0.5 2017-03-11 [0.15.0]
2010-07-11 - Increase default limits, set I2CP tag options
Final compact response format - Update eepsite configuration for Jetty 9 (I2P 0.9.30)
0.4 2015-05-28 [0.14.0]
2010-07-09 - Remove use of deprecated SimpleScheduler (ticket #1523)
Compact request/response support - may not be final format - Remove static structures
Fix NPE if no ip parameter - Use RandomIterator for efficiency
0.3 2014-11-29 [0.13.0]
2010-04-13 - Fix NPE on missing announce parameters
Verify dest - Increase startup delay
Add xfs check
0.2 2014-11-13 [0.12.0]
2010-03-23 - Fix parameter decoding for scrape also
Cache b64 dest strings - Add caching for info hashes and peer ids
Help typo fix (thx duck) - Stop cleaner when plugin stops
Lots of seedless fixes - Move to the ClientApp interface, remove all static refs
Build cleanups - Attempt to fix crash after update
- Add zzzot.config file to set interval
- Don't overwrite index.html and robots.txt in the update
- Release all resources when shut down (requires 0.9.16-6 or higher)
0.1 2014-11-11 [0.11.0]
2010-03-23 - Critical fix for announce parameter decoding, triggered by recent Jetty versions
- Change request queueing in jetty.xml (new installs only)
- SU3 plugin file format
2013-04-14 [0.10.0]
- Updates and migration for Jetty 7 (I2P 0.9.6)
2013-01-25 [0.9.0]
- Add cache-control directives
- Set max Jetty to 6.99999
2012-03-10 [0.8]
- Fix comment in jetty.xml
2012-03-10 [0.7]
- Port to Jetty 6
- Replace QForwardHandler with RewriteHandler
- Use ${ant.home}/lib/ant.jar instead of pulling ant.jar from Jetty
2011-12-31 [0.6]
- Set max jetty version 5.99999
- Add throttle options
- Stub out announce-to-seedless
- Seedless fixes, untested
2010-07-11 [0.5]
- Final compact response format
2010-07-09 [0.4]
- Compact request/response support - may not be final format
- Fix NPE if no ip parameter
2010-04-13 [0.3]
- Verify dest
- Add xfs check
2010-03-23 [0.2]
- Cache b64 dest strings
- Help typo fix (thx duck)
- Lots of seedless fixes
- Build cleanups
2010-03-23 [0.1]
- Initial release

View File

@ -1,42 +1,46 @@
ZzzOT I2P Open Tracker Plugin
-----------------------------
This is a very simple in-memory open tracker, wrapped into an I2P plugin. This is a very simple in-memory open tracker, wrapped into an I2P plugin.
A compiled version of the plugin is available at http://stats.i2p/i2p/plugins/
The plugin starts a new http serer tunnel, eepsite, and Jetty server running at port 7662. The plugin starts a new http server tunnel, eepsite, and Jetty server running at
The tracker status is available at http://127.0.0.1:7662/tracker/ . port 7662. The tracker status is available at http://127.0.0.1:7662/tracker/
If other files are desired on the eepsite, they can be added at eepsite/docroot . 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 The open tracker code and jsps were written from scratch, but depend on some
in i2psnark.jar from the I2P installation for bencoding, and of course code in i2psnark.jar from the I2P installation for bencoding, and of course on
on other i2p libraries. other i2p libraries. See the license files in I2P for i2p and i2psnark licenses.
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
There is also some code modified from Jetty 5.1.15. zzzot and Jetty licenses.
See LICENSES.txt for the zzzot and Jetty licenses.
I2P source must be installed and built in ../i2p.i2p to compile this package. 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 - 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 you should use the C opentracker instead. But since you're already running the
the JVM and Jetty, running this in the same JVM probably doesn't hog to much more memory. JVM and Jetty, running this in the same JVM probably doesn't hog to much more
memory.
Valid announce URLs: Valid announce URLs:
/a /a
/announce /announce
/announce.jsp /announce.jsp
/announce.php /announce.php
/tracker/a /tracker/a
/tracker/announce /tracker/announce
/tracker/announce.jsp /tracker/announce.jsp
/tracker/announce.php /tracker/announce.php
Valid scrape URLs: Valid scrape URLs:
/scrape /scrape
/scrape.jsp /scrape.jsp
/scrape.php /scrape.php
/tracker/scrape /tracker/scrape
/tracker/scrape.jsp /tracker/scrape.jsp
/tracker/scrape.php /tracker/scrape.php
The tracker also responds to seedless queries at The tracker also responds to seedless queries at:
/Seedless/index.jsp /Seedless/index.jsp
You may use the rest of the eepsite for other purposes, for example you You may use the rest of the eepsite for other purposes; for example you
may place torrent files in eepsite/docroot/torrents. may place torrent files in: eepsite/docroot/torrents/

View File

@ -1,5 +1,4 @@
Configuration file: Configuration file:
- interval
- clean time - clean time
- max peers in response - max peers in response
- disable full scrapes - disable full scrapes
@ -17,3 +16,11 @@ Bans:
Verifier: Verifier:
- Check dest vs. b32 in header - Check dest vs. b32 in header
Feature requests:
- display infohashes and status for individual torrents i.e. number of active peers/seeds, total number of downloads: http://bittorrent.org/beps/bep_0048.html
- optional mapping of hashes to torrent names
- optional clustering of open trackers to permit syncing
- blacklists for hashes/torrents
- optional password protection for site and/or admin section

View File

@ -1,22 +1,31 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml version="1.0" encoding="ISO-8859-1"?>
<project basedir="." default="all" name="zzzot"> <project basedir="." default="all" name="zzzot">
<property file="override.properties"/>
<target name="all" depends="clean,plugin" /> <target name="all" depends="clean,plugin" />
<target name="war" > <target name="war" >
<ant dir="src" target="build" /> <ant dir="src" target="build" />
</target> </target>
<target name="plugin" depends="war"> <target name="plugin" depends="war">
<delete file="plugin/i2ptunnel.config" /> <delete>
<!-- in installer but not update -->
<fileset dir="plugin/" includes="i2ptunnel.config zzzot.config eepsite/docroot/index.html eepsite/docroot/robots.txt" />
</delete>
<delete dir="plugin/eepsite/docroot/torrents/" />
<!-- get version number --> <!-- get version number -->
<buildnumber file="scripts/build.number" /> <buildnumber file="scripts/build.number" />
<property name="release.number" value="0.9.0" /> <property name="release.number" value="0.18.0" />
<!-- make the update xpi2p --> <!-- make the update xpi2p -->
<!-- this contains everything except i2ptunnel.config --> <!-- this contains everything except i2ptunnel.config -->
<copy file="LICENSE.txt" todir="plugin/" overwrite="true" /> <copy file="LICENSE.txt" todir="plugin/" overwrite="true" />
<copy file="README.txt" todir="plugin/" overwrite="true" /> <copy file="README.txt" todir="plugin/" overwrite="true" />
<copy file="CHANGES.txt" todir="plugin/" overwrite="true" />
<copy file="scripts/tracker.css" todir="plugin/eepsite/docroot/" overwrite="true" />
<copy file="scripts/tracker-purple.css" todir="plugin/eepsite/docroot/" overwrite="true" />
<copy file="scripts/favicon.png" todir="plugin/eepsite/docroot/" overwrite="true" />
<copy file="scripts/plugin.config" todir="plugin/" overwrite="true" /> <copy file="scripts/plugin.config" todir="plugin/" overwrite="true" />
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true"> <exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
<arg value="update-only=true" /> <arg value="update-only=true" />
@ -25,27 +34,39 @@
<arg value="version=${release.number}-b${build.number}" /> <arg value="version=${release.number}-b${build.number}" />
</exec> </exec>
<exec executable="pack200" failonerror="true"> <exec executable="pack200" failonerror="true">
<arg value="-g" /> <arg value="-r" />
<arg value="plugin/lib/zzzot.jar.pack" /> <arg value="plugin/lib/zzzot.jar" />
<arg value="src/build/zzzot.jar" /> <arg value="src/build/zzzot.jar" />
</exec> </exec>
<exec executable="pack200" failonerror="true"> <exec executable="pack200" failonerror="true">
<arg value="-g" /> <arg value="-r" />
<arg value="plugin/eepsite/webapps/tracker.war.pack" /> <arg value="plugin/eepsite/webapps/tracker.war" />
<arg value="src/build/tracker.war.jar" /> <arg value="src/build/tracker.war.jar" />
</exec> </exec>
<exec executable="scripts/makeplugin.sh" failonerror="true" > <input message="Enter su3 signing key password:" addproperty="release.password.su3" />
<fail message="You must enter a password." >
<condition>
<equals arg1="${release.password.su3}" arg2=""/>
</condition>
</fail>
<!-- this will fail if no su3 keys exist, as it needs the password twice -->
<exec executable="scripts/makeplugin.sh" inputstring="${release.password.su3}" failonerror="true" >
<arg value="plugin" /> <arg value="plugin" />
</exec> </exec>
<move file="zzzot.xpi2p" tofile="zzzot-update.xpi2p" overwrite="true" /> <move file="zzzot.xpi2p" tofile="zzzot-update.xpi2p" overwrite="true" />
<move file="zzzot.su3" tofile="zzzot-update.su3" overwrite="true" />
<!-- make the install xpi2p --> <!-- make the install xpi2p -->
<copy file="scripts/i2ptunnel.config" todir="plugin/" overwrite="true" />
<copy file="scripts/plugin.config" todir="plugin/" overwrite="true" /> <copy file="scripts/plugin.config" todir="plugin/" overwrite="true" />
<!-- Files in installer but not update. Be sure to Add to delete fileset above and clean target below -->
<copy file="scripts/i2ptunnel.config" todir="plugin/" overwrite="true" />
<copy file="scripts/zzzot.config" todir="plugin/" overwrite="true" />
<copy file="scripts/robots.txt" todir="plugin/eepsite/docroot/" overwrite="true" />
<mkdir dir="plugin/eepsite/docroot/torrents/" />
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true"> <exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
<arg value="version=${release.number}-b${build.number}" /> <arg value="version=${release.number}-b${build.number}" />
</exec> </exec>
<exec executable="scripts/makeplugin.sh" failonerror="true" > <exec executable="scripts/makeplugin.sh" inputstring="${release.password.su3}" failonerror="true" >
<arg value="plugin" /> <arg value="plugin" />
</exec> </exec>
</target> </target>
@ -54,14 +75,29 @@
<target name="clean" > <target name="clean" >
<ant dir="src" target="clean" /> <ant dir="src" target="clean" />
<defaultexcludes remove="**/*~"/>
<delete>
<fileset dir="." includes="*/*.~ **/*.*~ */**/*.*~ *.*~" />
</delete>
<delete file="plugin/i2ptunnel.config" /> <delete file="plugin/i2ptunnel.config" />
<delete file="plugin/plugin.config" /> <delete file="plugin/plugin.config" />
<delete file="plugin/lib/zzzot.jar.pack" /> <delete file="plugin/zzzot.config" />
<delete file="plugin/eepsite/webapps/tracker.war.pack" /> <delete file="plugin/eepsite/docroot/index.html" />
<delete file="plugin/eepsite/docroot/robots.txt" />
<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" />
<delete file="plugin/eepsite/webapps/tracker.war" />
<delete file="plugin/CHANGES.txt" />
<delete file="plugin/LICENSE.txt" /> <delete file="plugin/LICENSE.txt" />
<delete file="plugin/README.txt" /> <delete file="plugin/README.txt" />
<delete file="zzzot.xpi2p" /> <delete file="zzzot.xpi2p" />
<delete file="zzzot-update.xpi2p" /> <delete file="zzzot-update.xpi2p" />
<delete file="zzzot.su3" />
<delete file="zzzot-update.su3" />
<delete file="plugin.zip" />
<delete dir="plugin/eepsite/docroot/torrents/" />
</target> </target>
</project> </project>

View File

@ -2,7 +2,7 @@ clientApp.0.main=net.i2p.zzzot.ZzzOTController
clientApp.0.name=ZzzOT clientApp.0.name=ZzzOT
clientApp.0.args=-d $PLUGIN start clientApp.0.args=-d $PLUGIN start
clientApp.0.stopargs=-d $PLUGIN stop clientApp.0.stopargs=-d $PLUGIN stop
clientApp.0.delay=15 clientApp.0.delay=0
clientApp.0.startOnLoad=true clientApp.0.startOnLoad=true
# we also use i2p.jar and i2ptunnel.jar, they are in the standard router classpath # we also use i2p.jar and i2ptunnel.jar, they are in the standard router classpath
clientApp.0.classpath=$PLUGIN/lib/zzzot.jar,$I2P/lib/i2psnark.jar clientApp.0.classpath=$PLUGIN/lib/zzzot.jar,$I2P/lib/i2psnark.jar

View File

@ -1,6 +0,0 @@
<html><head>
<!-- edit this file if you want to change your home page -->
<title>zzzot</title>
</head><body style="background-color: #000; color: #c30; font-size: 2000%;">
<center><b>zzzot</b></center>
</body></html>

View File

@ -1,28 +1,22 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!-- <!--
Configure a custom context for the eepsite. Configure a custom context for the eepsite.
This context contains only a Context with a default servlet This context contains only a ServletContextHandler with a default servlet
to serve static html files and images. to serve static html files and images.
--> -->
<Configure class="org.mortbay.jetty.servlet.Context"> <Configure class="org.eclipse.jetty.servlet.ServletContextHandler">
<Set name="contextPath">/</Set> <Set name="contextPath">/</Set>
<Set name="resourceBase">$PLUGIN/eepsite/docroot/</Set> <Set name="resourceBase">$PLUGIN/eepsite/docroot/</Set>
<Call name="setInitParams"> <Call name="setInitParameter">
<Arg> <Arg>cacheControl</Arg>
<Map> <Arg>max-age=3600,public</Arg>
<Entry>
<Item>org.mortbay.jetty.servlet.Default.cacheControl</Item>
<Item>max-age=3600,public</Item>
</Entry>
</Map>
</Arg>
</Call> </Call>
<Call name="addServlet"> <Call name="addServlet">
<Arg>org.mortbay.jetty.servlet.DefaultServlet</Arg> <Arg>net.i2p.servlet.I2PDefaultServlet</Arg>
<Arg>/</Arg> <Arg>/</Arg>
</Call> </Call>
</Configure> </Configure>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="ISO-8859-1"?> <?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!-- <!--
Configure a custom context for the eepsite. Configure a custom context for the eepsite.
@ -22,21 +22,15 @@ Configure a custom context for the eepsite.
* parameter value. * parameter value.
--> -->
<Configure class="org.mortbay.jetty.servlet.Context"> <Configure class="org.eclipse.jetty.servlet.ServletContextHandler">
<Set name="contextPath">/cgi-bin</Set> <Set name="contextPath">/cgi-bin</Set>
<Set name="resourceBase">$PLUGIN/eepsite/cgi-bin/</Set> <Set name="resourceBase">$PLUGIN/eepsite/cgi-bin/</Set>
<Call name="setInitParams"> <Call name="setInitParameter">
<Arg> <Arg>Path</Arg>
<Map> <Arg>/usr/local/bin:/bin:/usr/bin</Arg>
<Entry>
<Item>Path</Item>
<Item>/usr/local/bin:/bin:/usr/bin</Item>
</Entry>
</Map>
</Arg>
</Call> </Call>
<Call name="addServlet"> <Call name="addServlet">
<Arg>org.mortbay.servlet.CGI</Arg> <Arg>org.eclipse.jetty.servlets.CGI</Arg>
<Arg>/</Arg> <Arg>/</Arg>
</Call> </Call>
</Configure> </Configure>

View File

@ -35,23 +35,23 @@
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- UNCOMMENT TO ACTIVATE <!-- UNCOMMENT TO ACTIVATE
<context-param> <context-param>
<param-name>org.mortbay.jetty.servlet.SessionDomain</param-name> <param-name>org.eclipse.jetty.servlet.SessionDomain</param-name>
<param-value>127.0.0.1</param-value> <param-value>127.0.0.1</param-value>
</context-param> </context-param>
<context-param> <context-param>
<param-name>org.mortbay.jetty.servlet.SessionPath</param-name> <param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
<param-value>/</param-value> <param-value>/</param-value>
</context-param> </context-param>
<context-param> <context-param>
<param-name>org.mortbay.jetty.servlet.MaxAge</param-name> <param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
<param-value>-1</param-value> <param-value>-1</param-value>
</context-param> </context-param>
--> -->
<context-param> <context-param>
<param-name>org.mortbay.jetty.webapp.NoTLDJarPattern</param-name> <param-name>org.eclipse.jetty.webapp.NoTLDJarPattern</param-name>
<param-value>start.jar|ant-.*\.jar|dojo-.*\.jar|jetty-.*\.jar|jsp-api-.*\.jar|junit-.*\.jar|servlet-api-.*\.jar|dnsns\.jar|rt\.jar|jsse\.jar|tools\.jar|sunpkcs11\.jar|sunjce_provider\.jar|xerces.*\.jar</param-value> <param-value>start.jar|ant-.*\.jar|dojo-.*\.jar|jetty-.*\.jar|jsp-api-.*\.jar|junit-.*\.jar|servlet-api-.*\.jar|dnsns\.jar|rt\.jar|jsse\.jar|tools\.jar|sunpkcs11\.jar|sunjce_provider\.jar|xerces.*\.jar</param-value>
</context-param> </context-param>
@ -112,7 +112,7 @@
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<servlet> <servlet>
<servlet-name>default</servlet-name> <servlet-name>default</servlet-name>
<servlet-class>org.mortbay.jetty.servlet.DefaultServlet</servlet-class> <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
<init-param> <init-param>
<param-name>acceptRanges</param-name> <param-name>acceptRanges</param-name>
<param-value>true</param-value> <param-value>true</param-value>
@ -306,7 +306,7 @@
<!-- Uncomment for dynamic invocation <!-- Uncomment for dynamic invocation
<servlet> <servlet>
<servlet-name>invoker</servlet-name> <servlet-name>invoker</servlet-name>
<servlet-class>org.mortbay.jetty.servlet.Invoker</servlet-class> <servlet-class>org.eclipse.jetty.servlet.Invoker</servlet-class>
<init-param> <init-param>
<param-name>verbose</param-name> <param-name>verbose</param-name>
<param-value>false</param-value> <param-value>false</param-value>

View File

@ -1,56 +1,420 @@
<html><head><title>ZzzOT Plugin Help</title></head> <!DOCTYPE HTML>
<body style="background-color: #ddd; color: #a30;"> <html>
<h2>Welcome to the ZzzOT I2P Plugin!</h2>
A new eepsite tunnel and Jetty server have been started for your open tracker. <head>
<meta charset="UTF-8">
<title>ZZZOT OPENTRACKER | HELP</title>
<link rel="icon" type="image/png" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABwlBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADzlhbzeBbzfRbzihbzkxbzdRbzbBbzhhbzjxbzgxYDAQDzbxbzehbzgBbzchbziBbzfxYHAwEJBAHzjBYVCwIFAwAxGQQZDgIVCQINBwFuOQoeEAMcDgIQCQEpFgTwjBbphBXubBW/ZBGkTQ9yPwpVMAhWKAglEgPxgRbwkxXfdxTdaxTKbBLKZBLAahGiYw6IPQxkMQlDJwbqehXocRXnaBXdghTgZxTWfhPWdxPLcRK7XhHCWBG6WBG3URCZWw6KRw2YRQ2IUgyBQQtIJAdJLAY3HAUkFQPxiBbvdhbpfRXscRXcfhTWZRPKexLCeBK5cBG7bBG7ZxG8WBG0XBCsUQ+hWw6hTA6ITwx7OQt0OAtxRApuPQprMwpgNwhbNQhPKQdEHwY6IAUsGQTrjRXpjBXeiRThgRTgfRTibhS0aRCsZxCYTQ6TTQ2RTQ19SwtPLge07EnRAAAAJHRSTlMABK+M1/Xz7YlpJRoMBtzNyIRWT+T7tJBwbl86NRAO5cTCtYFtLGI6AAAEcklEQVRYw5XXd0PTQBQA8LSltUCVlqGIuB9gWScFUxtKF6MLREBkyFC2gHvvvff6vt57ubYkTWr8/ZGmJXfk3ch7kcxU1rjt3loAqKutsvuqK6X/0ugoA50yxwGrrT31e8BQWf1BC80PuV2Qx/o4KHD5PFJptvJdoIpnntydHziNbtx98joEql3ltlLtK7xAwi8m2nUm0qKPoxXm7RucgHrXhtoMDKX6ATmrzW7fASj26ISZK49igPYZhrHbTsOWHuzp6fmS2U5f6TEw+IIBZ99t0H4vDd0y3uzssAzyWpuhZZqVvUU92OwU/Wg7eiXjebux0V66B30UFP/mIIW6SLcZPCEMftaOxOAUjYO2fTVwb4dOkrcM0Cx9GcoyiF06udPQZeAaNPPvxHu+1kkWQTWGX0Yu83BYolPjGkbhrNgxAEdw8VzoUL1WAMnf8AsNByQ6tC6E+Y/ewjCU4/wtdau+ykCCa/zLU4X6etmts4RRlue3H67/aEBYDYoONgKBnwwQGwvoRXFfHBIdHMf199EvbIOQ8X9nQPr9RT7gmnRL5CDu31SrMM5AGL6fC2a1tVgKd7e6uffjxS05UWpFIuKTjbYYwJnYLyF8ep1rEs6EIIcp4nOzycg5/qc92P4AP+m72ixcZKCnPGw2chWnslEs4mddOeeLO2Cs//yP8eYuPZwIh4jgdj6CBBhhQZA3P+liuK3GUMk/Qi05GIEJ5U2LTpz/WinV8GM2HxhGYEaeadbK8h9rJB8/rvsFjMCUvOLXWqe1dIwfk6eEJIMSek9pJfHBIlXx49lW4aUMpdxs1bjJf6qSDvNjR04YSpGfd2gxgMNSHZ+ETmGMQUnsza+xkc4CXLUSrsOTwlMZSmNBJm89ezgnru8THYROC9tghSzDHzVphnIdtKsWwCp2jxrwDupwEFmbajIIFsnL1ICf1dI0LpwgU2DZIl5/g6YRM9JEDwmDZQt4/T1+ckxy8+NkG5oF6+YoZH7io82UUUsIBpZRgwxuJtrOcRqCFctjKPJmiLazhCUZ5T7cyhYl8PpLWLiJR1q0k9sCixRlFa9Pi0daI+bFAb41ImANm5rv4AYwAiw+KYbH3d1zFtdQX6qbpDCCfGJ5Hwg8UKyMHotOB8jIcD6xeFyYWfzrwX82Vt79vuUXMK+4ROnrxpm8ni3dWH63sXS9NW8Gk+vxXHlM6b2/xLBtbSS7WjQovXs0BUa/Yvrvnzfp3REFhmDz4lPBdBrlB806Z3EKj9h0RdYwM1s4F7u0ZjRFFmkAnEuzHm41aZyhB4eu5N5HpYxJD+Oa8ZumMtFhWOrGmHEHmvhFqWtcbCciMhSb9xfciYti26zcjxnM5uipnOm0cblPbPsA9RXdRCF3Po4Bcpi9N1U7AYWDui7UInhgpReQs6HES9dRIJEw7NxamA+T0TAQb4W1175InHcRREy5P5mNW3ntIx6fCwoikUiCQZ7LfUj6t4P1ZWBoT71HsuiA0ct3o/RfKqt99qraOsyfXru7xvT1/y8HBiJNY8d/vwAAAABJRU5ErkJggg==">
<style type="text/css">
html,
body {
min-width: 800px;
min-height: 100%;
line-height: 1.4;
font-size: 14pt;
font-family: "Droid Sans", "Open Sans", "Noto Sans", Ubuntu, "Segoe UI", "Lucida Grande", "DejaVu Sans", Helvetica, sans-serif;
color: #ccc;
background: #111;
overflow-x: hidden;
scrollbar-color: #222 #111;
}
<p><a href="/tracker/index.jsp">Click here to see the current stats</a>. @supports (background-blend-mode: overlay) {
This link is also at the top of your router console when ZzzOT is running. html, body {
background: repeating-linear-gradient(45deg, #333, #111 2px, #111 3px),
repeating-linear-gradient(135deg, #444, #333 2px, #222 3px) #111;
background-blend-mode: overlay, normal;
background-size: 100% 100%, 100% 100%;
background-attachment: fixed;
}
}
<p>Report bugs or add comments on ::selection,
<a href="http://zzz.i2p//forums/16">the plugin forum on zzz.i2p</a>. ::-moz-selection {
text-shadow: none;
background: #431;
color: #fff;
}
<h3>Eepsite Key and Helpful Hints for I2P</h3> code::-moz-selection,
#b64::-moz-selection,
code::selection,
#b64::selection {
background: #150;
color: #fff;
}
<p>Your Base 32 address is <a href="http://$B32/">$B32</a>. p,
Others may access your eepsite using this address, even if you do not publish a hostname. li,
<p>Once you decide on a host name, you may code {
<a href="http://127.0.0.1:7657/susidns/addressbook.jsp?book=private&destination=$B64">add the key to your local addressbook here</a>. text-shadow: 0 1px 1px #000;
<p>Your Base 64 key is: &nbsp;&nbsp;&nbsp;<textarea rows="1" style="height: 3em;" cols="40" readonly="readonly" wrap="off">$B64</textarea> }
<br>You will need this key to register a hostname at <a href="http://stats.i2p/i2p/addkey.html">stats.i2p</a>.
<p>Your private key file is $PLUGIN/eepPriv.dat - back it up!!!
<p>Your eepsite document root is $PLUGIN/eepsite/docroot,
you may put other files there if you wish to have additional content on your eepsite.
<p>The supported announce URLs are:
<ul>
<li><a href="http://$B32/a">http://$B32/a</a>
<li><a href="http://$B32/announce">http://$B32/announce</a>
<li><a href="http://$B32/announce.jsp">http://$B32/announce.jsp</a>
<li><a href="http://$B32/announce.php">http://$B32/announce.php</a>
<li><a href="http://$B32/tracker/a">http://$B32/tracker/a</a>
<li><a href="http://$B32/tracker/announce">http://$B32/tracker/announce</a>
<li><a href="http://$B32/tracker/announce.jsp">http://$B32/tracker/announce.jsp</a>
<li><a href="http://$B32/tracker/announce.php">http://$B32/tracker/announce.php</a>
</ul>
<p>The supported scrape URLs are:
<ul>
<li><a href="http://$B32/scrape">http://$B32/scrape</a>
<li><a href="http://$B32/scrape.jsp">http://$B32/scrape.jsp</a>
<li><a href="http://$B32/scrape.php">http://$B32/scrape.php</a>
<li><a href="http://$B32/tracker/scrape">http://$B32/tracker/scrape</a>
<li><a href="http://$B32/tracker/scrape.jsp">http://$B32/tracker/scrape.jsp</a>
<li><a href="http://$B32/tracker/scrape.php">http://$B32/tracker/scrape.php</a>
</ul>
<p>Your eepsite tunnel is configured for 2 inbound and 2 outbound tunnels, 3 hops each.
You may change tunnel settings by editing $PLUGIN/i2ptunnel.config and restarting the plugin.
The tunnel will not appear in <a href="http://127.0.0.1:7657/i2ptunnel/index.jsp">i2ptunnel</a>.
If your tracker gets over 1000 peers, you will probably want to increase the number of tunnels.
<p>The Jetty webserver port is 7662. If you must change it, edit jetty.xml, i2ptunnel.config, and plugins.config
in the directory $PLUGIN. Then stop and restart the plugin.
<p>This help file is $PLUGIN/eepsite/docroot/help.html, you should probably move it
outside of the document root before you announce your eepsite as it may contain your user name.
<p>As you probably know, an open tracker does not require torrents to be registered,
and it does not host torrent files. You can, however, host torrent files elsewhere on
the eepsite, for example at <a href="http://$B32/torrents/">/torrents</a>.
</body></html> #container {
padding: 5% 15%;
}
#panel {
padding: 20px 40px 15px;
font-size: 85%;
text-align: justify;
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-blend-mode: overlay;
will-change: transform;
}
@supports not (-moz-appearance: none) {
#panel {
background: repeating-linear-gradient(to bottom, #222, #111 2px);
}
}
#sitename,
#sitename:hover,
#sitename:focus {
margin: -8px 0 -14px;
display: inline-block;
font-size: 500%;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
#sitename::before {
margin-top: 4px;
display: inline-block;
content: "";
width: 68px;
height: 56px;
background: url(data:image/png;base64,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) center center no-repeat;
background-size: 56px 56px;
opacity: 0.7;
filter: drop-shadow(0 0 3px rgba(0,0,0,.75));
}
hr.heading {
margin: -10px 0 15px;
height: 1px;
color: transparent;
border: none;
background: repeating-linear-gradient(to right, rgba(255, 255, 255, .05), rgba(0, 0, 0, .08) 2px),
linear-gradient(to right, #888, #666 20%, rgba(0, 0, 0, 0) 90%);
filter: drop-shadow(0 1px 1px #000);
animation: ease-out underline 1s forwards;
will-change: transform;
}
@keyframes underline {
from {
width: 0;
}
to {
width: 100%;
}
}
#v {
margin-left: -10px;
}
h2,
h3 {
color: #ccc;
text-shadow: 0 0 2px #181818, 0 3px 1px #000;
}
h2 {
margin-top: 10px;
}
h3 {
padding: 5px 0 0;
}
h2::first-letter,
h3::first-letter {
font-size: 114%;
}
a:link,
.urls li {
font-weight: bold;
text-decoration: none;
color: #c4ad9d;
outline: none;
}
#local {
font-weight: normal;
color: #bbb;
}
a:visited {
color: #a98770;
}
a:hover,
a:focus {
color: #e88b44;
}
code,
#b64 {
white-space: nowrap;
font-weight: bold;
font-family: "Droid Sans Mono", "Noto Mono", "DejaVu Sans Mono", "Lucida Console", monospace;
font-size: 90%;
color: #292;
-moz-user-select: all;
-webkit-user-select: all;
user-select: all;
}
#b64 {
margin-bottom: -5px;
width: 100%;
display: inline-block;
white-space: no-wrap;
overflow: hidden;
text-overflow: ellipsis;
}
#b64:focus {
white-space: normal;
word-break: break-all;
overflow: normal;
outline: none;
}
.warn {
margin-top: 18px;
padding: 15px 18px 15px 54px;
line-height: 1.2;
border: 1px solid #900;
border-radius: 4px;
box-shadow: inset 0 0 0 1px #000, inset 0 0 2px 1px #900, 0 0 2px 1px rgba(0, 0, 0, .2);
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACcAAAAhCAMAAAEaI3KMAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACbVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQECAgIDAAADAgIEBAQGBAQGBQUGBgYHAQAHBwcICAgJCQkKAQAKCgoLAQALCwsMCwsODg4PDw8QAQAQAgASAAASAwAVCwsaGhobDw8bExMbGxskHx8mJiYpAwAqKiotKysuAQAwLi4yMjI8HR08JiY9CAA/CAA/Pz9BQUFDQ0NKBwBLPDxLS0tTAwBhBABmZmZpAABpPT1tEABvEAB4RUV4WFh4a2uCBwCECACGhoaHeHiUAQCWFgCaFgCclZWkDgClVFSlb2+0lZW0tLS2FQC4BQC9vb3GGQDGxsbIVlbKWFjMFwDOCgDPfn7RFADUrKnV1dXWCgDWEgDWLR7YBATYEADYg4PZ2dnaAwPakozbEADbp6fbysrb29vezc3e1NTe3t7hDQDhIxjk5OTlb2nlcWvmCwDqwL7qysnq6urrCQDrDAPrDgbtAADtgIDv7+/wQkLwS0bxBgDxPTjzBADzhITzq6vz8/P1oJ/1qKf19fX2BAD209P51tb7AgD7HBv77+/7+/v8/Pz/AAD/DAz/amr/bm7/cnL/enr/goL/ior/k5P/lJT/mJj/m5v/o6P/q6v/s7P/t7f/uLj/u7v/vr7/w8P/xcX/yMj/y8v/09P/2Nj/29v/4+P/5OT/6en/6ur/6+v/8fH/8vL/9PT//Pz//f3////ZtbwoAAAAJXRSTlMAAwYMEhUYGyEkMDxCS1ppeISHkJacpairtMPP2Nvk5+3w8/n8UHR7egAAAp9JREFUGBmFwYlDi2EcB/BvB6lRjoxOS/1ykzPkFrmv3GdCyZEzLMRcrZdhjjG3MV4du/S2aU1puXn+Js87G/WWfD4I8W4FkMpaARgEwYAexAHQgFM1gbOPtAPThPUCQNxQyOb5w/Db2aYsKR4y62txgWglILosaDVkQxAS5p+HoK1NXhUC4qVxjccRYG/NYvZUcGQVF4uvreAMgrBeEAyTgNVlQb2QRkE9EaK+fFENhbQWToMOyO3++dPtJrQz2+lkmdnM6ZyJkJgN9+oYmzuZsbp7G2IQ0P+A+b7JZCooMJlMD8wH+oNLOaM7X8GtWlXBndedSQGSSztJRoiKekNpypcxUEh66X+RhA6i1rS0tKzpgfbI6/3q9RLaidvrbmTv3Hvj8NcoSbpEJyVpBP5IfORsfkIHm513ExEUucRuZ4xuMrt9SQR+yzhd+5kxYuxz7bkMBMQWWl+JokiiKL6yFsZCRmaziSMTZzYTuIRThmsCRwJ31XAsAQjP1ekqZFQh0+lyw5FeppSO5aVKy6AmpUFQitIs2rMwLQr/oaKdbW1t20iFbsVRyQdZCcWhG+rRF3w+33ufz3dhtBr/EpY057HH4/Ezv8fjeTwnKQxditQsbeA+5VP+xwZuqSYSXYjJ2OJyueq/HxpP2fu+1btcro0ZMegkloodDsfbH4wNo4kn2Y+3DoejmGKhED/8iM1mkxg3legNY0yy2WyHhw9AB4k5d6qrayQmm0+zmEyqqa6+k5OIvyJS8x5aLJZnzwNW0ornAU8tFsvDvNQIBEWnrzMajbdv3FS6fstoNK5Nj0ZAH9qh1+urrlQGZVJmZdCVKr1ev536gOtHRVqttvxE18q1Wm0R9QUGTth/9H/2j43HoBmbd+3u3q5N0wf/ApzdYLKMtOjLAAAAAElFTkSuQmCC) 12px center no-repeat, repeating-linear-gradient(to bottom, rgba(0, 0, 0, .5), rgba(64, 0, 0, .2) 2px), linear-gradient(to bottom, #222, #111);
background-size: 32px auto, 100% 100%, 100% 100%;
will-change: transform;
}
.emphasis {
margin-top: 4px;
display: inline-block;
line-height: 110%;
color: #900;
font-weight: bold;
}
ul {
margin-top: -10px;
}
ul#config {
list-style-type: none;
}
#config li {
padding: 10px 15px 5px 0;
position: relative;
}
#config li::before {
content: "";
display: inline-block;
width: 25px;
height: 20px;
position: absolute;
top: 12px;
left: -22px;
background: url(data:image/png;base64,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) left top no-repeat;
background-size: 14px 14px;
opacity: .85;
filter: saturate(0) brightness(1.6) drop-shadow(0 1px 1px #000);
}
#config li br {
display: none;
}
.urls li {
margin-left: -15px;
list-style-type: none;
}
.urls li::before {
margin: 2px 0 -3px;
width: 22px;
height: 20px;
display: inline-block;
content: "";
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABEVBMVEUAAAAAAAAAAAAAAAAAAABDREYAAAAAAAAAAACDhYgAAABmaGuPkZRZWlyFh4oAAABkZWcAAAAAAABRUlRiY2ZPUVN4eXtydHdcXV9YWVtCQ0QxMjM2Nzh6fH6DhIcAAAAAAAB3eXt3enxoaWtlZ2p2d3pwcnWCg4Z7fH5OTlBnaWtdXmAxMTJXWFoyMjRAQEIAAABeYGJmZ2qIio2jpKfx8vOKjZDLzM7v7+6wsrWdnqCAgYSbnJ6Nj5KMjY58foFxcnXp6urh4uTOz9KztreYmp2QkJOFh4uSlJaPkJF/f4NVVljR0tS/vsC8vsCsrrKgoqSXl5uQkpN6e31ub3JsbXBdYGJaW12/v8G+vb+SlJjRk2qVAAAAMnRSTlMABAwWJ4UHQiBhEWDohWEw7ToU8e3r5dKRg3tpXz05Nw/v7ejEvrGtfHRtaGZXV0w5MLROkgwAAAE3SURBVDjLndHXcoJAFIBhlyWEGA2WJJY003tjAQ2hKQIGe4tJ3v9BsjDOeLEcLzy335nZPfOnNpzTjEnILYIY7SnNVm/g2QLgO4ra/jPHw8kNgvyDlJ9KvuJwoGcl/sUezQtJ78fO17i7X0/G7MJJ5BWeQ7thPcixC0Km2Sb3kX/WNfk4zSykzVZn6lwKsW+J7J1Yc21dXTzHziccgYlvNKzu0lHCwnnkPy7k6FBvWN+6Eazxr6Izk/ePqDP9NHJB/WCbzplInenn+gb1vIgxTgsJ/To2/V8xjxHUZxr9zxFBrzqq1dVnGOpXecst5L4B9svyolQNXagfdU54XNOvHPUF+5Ge+VqjDvcbjEvvDyHYr3DtDX27D3qKk+yJMvICyFMI56+c+aofOwKWVv0SB3GFuN9m8w950jtLubKVyAAAAABJRU5ErkJggg==) left center no-repeat;
background-size: 16px 16px;
filter: sepia(1) saturate(.4) drop-shadow(0 1px 1px #000);
}
.external::before {
content: "";
display: inline-block;
width: 16px;
height: 14px;
vertical-align: middle;
background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAC61BMVEUAAAAAAAAAAAAAAAAAAAAAAAAYf7wbfa8AAAACDBESfbwZbpwVd64dgLITfrUVfLcadqkVX4YXZ5AKLT8MNUkEExsAAAAAAAAWcqQMOlIUaJQeeqoXerMXeq0WfrgVfrMUYYsUYYgypuIintwso+AmoN6f6QAYmNk5q+U1qOUem9oVgcoZgrec5gCa5QCN2wC/6P0yp+6h6wGd6ACY4wCQ3gBRufApot4bmtwSldcLktYDjdUbhL4fhrsehbmu7Rqq8QCl7QCV4gA6qv6m3Pmg2vlJs/RkwPI5q+xGsucZmOc/ruYHjt7i/Jmx7iyV3wCw4v+L0P/G6v665f2s4PzQ7fu04/uX1/iHz/V7y/U4qfROt+xHsuwRlN0NfMHj/3iK2hx2zhmS3wGe2v8vpP+l3/6X1P1Wuf2X2fyQ1PkooPUamfJcve8rouQRlNY/m84fjsUSiMVVu7RjxKdJtZM/rYbS9oPb+oLT+GnF81eD10W48Dm38iGf5x2R3xKw8wWc4QKG1wKe6AC15P9gvv8dmv/a8f56yf6Gzvo8rfduyPV0x/JVvPKv4+4Oku0noes+relxveQmn+S95+MRlOOn399ruN9Xu9NAr9Akls8Zks8Ih85isszr/sEOfcHJ8btOt7eb3rQnobKHy7BAr7BntKpOuaVNuJ5xzZlkw5fl/o+q34+g145dwIbG7oV20YGBzXpYvm+w5m1vym1uymTO+FyS3FZ1z0zB9EWP2kOc5kKx6UF2zjrB9DlqyDh40TSE1y6l5Rao7BGp7w990wuq7Adtxf+w3+kwn+eY2ONiv9qIz9ktndVUqtRjwdMnlNPF7NENgtG96c4Li8tvyMcwpcZOtr/h+76157wUjLwdm7tEnbbl/LFTqazV9Kq97qUnn6Momp9Xu54fnJ6Pzpt6wJmH1YJiw3k6rHbh/3V/13SJ22V70l2b4Fy27Fi68FZTuVNcv0ij4TprySaF1xSU4RCc6Aie4Qa4/QHF9Rd9AAAAInRSTlMABhYPNh3q3EIv07J37OfC0ZmNWUxLOikiGQ7NnZ2ZmSkpxqDDBgAAAzRJREFUeNplklNgJEsAAHeWcc56DNa2bdsb27Zztm3bNp9t2zY+r2f2nPqtajfqPjMy0rYUArakZcxAjWVammL3oc4iQOeh3YrsaY9o6CnG1z8ccB7o6v71zIXL3V07FU9CD/mspsXvbXY6Vz63utJbtUd3vedIEx560K/hbP7Y+aknYAjXzJtbf1Yn/PPIJlDc9/45QWfXSo/BWOl9aecs/boRofvEvQJ6oqliTtDH2fpmoKzSe8KlPrPWUn58hH3wo8eTRQ7DyAH+9c7T75e9ELn4ywDp2rV1e0uEuw7Sn0YmyF5eDY/vPt3T81tfv/rbFweL9Vxu7Tsl17/JhqeYrqo2+jj+mTM/O3euz6V+5hhrP+tdS24D8dbIRfp0MEHGKwv8/lDI41l8GfZvHx1t08coufkr6q4Mf5IBoSDC8kWcUCBQUbG636W+MVC1jzm4fz41N59o62AfS4NQGEZUFDIYjGXV2/vVN3gkUi9LwDzFBYFkCfs8HYNCMxaIDMZwuMa7fegmj8Rn/iMQFH/JzRXnSZaU/EdHg8AsioZrIvPmujRaOBCwdCdj1HwQrPp9GAkWicyRSDT67M1kwGKe0sNbyJMU7EgGpn0m88KF5lk/JQMmSW+hghVoZKnDoYAD6/9rTfXm+qpXh7Q8sEnS2Rg8AQgaZXIVCAjPX/jRFI/HRaIBjVbbe3JwGzgCMoFM/hYBjcJMeK3taCJhTcRNbec1P5eXbztcu3TFnQlaJmBQUKrCYllmt1qtifmtQ/+2Fu/5rvRSnc22qsAhlytTwU3iCC9TKBQ7YFmstZevOTx7dnv78R0djs+/3/gYDrwWZpIql0qlwFDBI+4d3VXMEujcbrfgK8UkDPzcuPTm3Ls0NCyta+frSoVsNvuvN9LBBABMaub6/CRiMTHPdpUFe+GtD/CpmOSXQ2Mz1xNh8gAS29VRMIO7ZCMeiwYTJIspmc3A0QBkckHBpb8FrD9a8FMQf3eOdOUGCRkglTY6PuRf+YKejoy/X6ROHKds3iCVymQyeUcLfdzE1Ac9KDA47OTxBJVKqVQpCeMnY3EY2D+STMWmIGCnjtVIAmHQCBjoAX0bgdwQZFvzARQAAAAASUVORK5CYII=) center top no-repeat;
background-size: 12px 12px;
opacity: .7;
}
.external:hover::before {
opacity: 1;
}
@supports (-webkit-text-stroke-width: 1px) {
#sitename,
#sitename:hover,
#sitename:focus {
background: #731;
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, .5) 2px),
linear-gradient(to bottom, rgba(255, 96, 0, .2), rgba(0, 0, 0, .9) 100%),
linear-gradient(to bottom, #210, #310 15%, #fff 50%, #310 80%);
-moz-background-clip: text !important;
-webkit-background-clip: text !important;
background-clip: text !important;
-moz-text-fill-color: transparent !important;
-webkit-text-fill-color: transparent !important;
text-fill-color: transparent !important;
filter: drop-shadow(0 0 2px #000);
-webkit-text-stroke-color: #999;
-webkit-text-stroke-width: 0.02em;
}
#sitename:hover,
#sitename:focus {
background: #951;
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, .3) 2px),
linear-gradient(to bottom, rgba(255, 255, 255, .2), rgba(0, 0, 0, .7) 100%),
linear-gradient(to bottom, #930, #d50 15%, #fff 50%, #930 80%, #000 100%, #ff0 150%);
filter: drop-shadow(0 0 2px #b00);
-webkit-text-stroke-color: #bbb;
}
}
@media screen and (max-width: 1200px) {
#help {
font-size: 90%;
}
.external::before {
margin-left: -2px;
}
#sitename::before {
height: 52px;
background-size: 52px 52px;
}
}
@media screen and (max-width: 1400px) {
#container {
padding: 2% !important;
}
#panel {
padding: 20px 35px 10px;
}
#sitename,
#sitename:hover,
#sitename:focus {
margin: -14px 0;
}
code:not(#b64) {
display: inline-block;
white-space: normal;
text-align: left;
}
br + code {
margin-top: 3px;
}
#config li br {
display: block;
}
}
</style>
</head>
<body id="help">
<div id="container">
<div id="panel">
<a href="http://127.0.0.1:7662/" title="OpenTracker homepage" alt="OpenTracker homepage"><span id="sitename">ZZZOT</span></a>
<span id="v">$VERSION</span>
<h2>Welcome to the ZzzOT I2P Plugin!</h2>
<hr class="heading">
<p class="warn" id="docroot">This help file is located at: <code>$PLUGIN/eepsite/docroot/help.html</code><br><span class="emphasis"><b>You should probably move it outside of the document root before you announce your eepsite as it may contain your username.</b></span></p>
<p>ZzzOT is a simple in-memory BitTorrent open <a href="https://en.wikipedia.org/wiki/BitTorrent_tracker" class="external" target="_blank">tracker</a>, wrapped into an I2P plugin. The software depends on several I2P libraries, in addition to <code>i2psnark.jar</code> from the I2P installation for <a href="https://en.wikipedia.org/wiki/Bencode" class="external" target="_blank">bencoding</a>. Please report bugs on <a href="http://trac.i2p2.i2p/" target="_blank">trac.i2p2.i2p</a> or add comments and feature requests on <a href="http://zzz.i2p/forums/16" target="_blank">the plugin forum</a> on zzz.i2p.</p>
<p>Source code is available (under the <a href="https://www.apache.org/licenses/LICENSE-2.0.html" class="external" target="_blank">Apache 2.0 license</a>) via the <code>i2p.plugins.zzzot</code> branch in the <a href="https://geti2p.net/en/get-involved/guides/monotone" class="external" target="_blank">I2P monotone</a> database, or on <a href="https://github.com/i2p/i2p.plugins.zzzot" class="external" target="_blank">github</a>. Note that the I2P source code must be available (and compiled) in <code>../i2p.i2p</code> in order to build ZzzOT.</p>
<h3>Configuration &amp; Customization</h3>
<hr class="heading">
<ul id="config">
<li>A new eepsite tunnel and Jetty server have been started for your open tracker, configured for 2 inbound and 2 outbound tunnels, 3 hops each. If your tracker gets over 1000 peers, you will probably want to increase the number of tunnels.</li>
<li>To change the tunnel settings, edit: <code>$PLUGIN/i2ptunnel.config</code> (the tunnel will not appear in the <a href="http://127.0.0.1:7657/i2ptunnelmgr" target="_blank">Tunnel Manager</a>), then stop and restart the plugin from the <a href="http://127.0.0.1:7657/configplugins" target="_blank">Plugin Manager</a>.</li>
<li>The Jetty webserver is running on port <code>7662</code>. If you must change it, edit: <code>jetty.xml</code>, <code>i2ptunnel.config</code>, and <code>plugins.config</code> in: <code>$PLUGIN/</code>, then stop and restart the plugin.</li>
<li>The configuration file for ZzzOT is located at: <code>$PLUGIN/zzzot.config</code>
<li>The only configuration for ZzzOT itself is the <i>interval</i>, which tells clients how long to wait between announces. The default is 1620 seconds (27 minutes).</li>
<li>The easiest way to reduce the load on your tracker is to increase the interval. To do so, edit the configuration file, then stop and restart the plugin.</li>
<li>Live stats are available on the <a href="http://127.0.0.1:7662/tracker" target="_blank">tracker page</a> (this link is also on your router console sidebar when ZzzOT is running).</li>
<li>To change the display name for the site (for the logo and page titles), add the line: <code>sitename=<i>mytracker</i></code> to the configuration file (substituting <i>mytracker</i> with your desired name) and then stop and restart the plugin.</li>
<li>To change the footer text displayed on the tracker stats page, add the line: <code>footertext=<i>alternative text</i></code> to the configuration file, and then stop and restart the plugin.</li>
<li>To hide the version footer displayed on the stats page, add the line: <code>showfooter=false</code> to the configuration file, and then stop and restart the plugin.
<li>Your eepsite document root is: <code>$PLUGIN/eepsite/docroot/</code>. You may put other files there if you wish to host additional content on your eepsite.</li>
<li>Your eepsite home page is located at: <code>$PLUGIN/eepsite/docroot/index.html</code>, you may edit it to add information about your tracker or links to additional content, or to change the displayed name and page title.</li>
<li>If you wish to change the appearance of the site, edit the css file located at: <br><code>$PLUGIN/eepsite/docroot/tracker.css</code>. There's an alternative purple-themed css file in the same directory that should be renamed to <code>tracker.css</code> (having renamed the existing file) if you wish to use that instead, or deleted if you don't intend to use it.</li>
<li>Directory listings are enabled on the server, so you don't need to create a web page to serve files; content in any folders you create in your document root will be immediately available to your visitors. The quickest method to disable listings is to place an empty <code>index.html</code> file in any folder you wish to disable.</li>
<li>Open trackers do not require torrents to be registered, and do not host torrent files. However, you can host torrent files elsewhere on the eepsite, for example at <a href="/torrents/" target="_blank">/torrents</a>.</li>
</ul>
<h3>Eepsite Key &amp; Hostname Registration</h3>
<hr class="heading">
<p class="warn">Your private key file is located at: <code>$PLUGIN/eepPriv.dat</code><br><span class="emphasis">Make sure you backup this file and move the copy outside of <code>$PLUGIN/</code> as it controls ownership of your B64/B32 address.</span></p>
<p>Your Base 32 address is: <a href="http://$B32/" target="_blank">$B32</a><br>Others may access your eepsite using this address, even if you do not publish a hostname.</p>
<p>Your Base 64 key is:<br><code id="b64" tabindex="1">$B64</code><br>Once you decide on a hostname, you may <a href="http://127.0.0.1:7657/susidns/addressbook.jsp?book=private&destination=$B64" target="_blank">add the key</a> to your local addressbook.</p>
<p>To generate a registration authentication string for your hostname (required for <a href="http://stats.i2p/i2p/addkey.html" target="_blank">hostname registration</a> at stats.i2p), run the following from the command line:<br><code>java -jar $I2P/lib/i2p.jar privatekeyfile -a <i>your-hostname</i>.i2p $PLUGIN/eepPriv.dat</code></p>
<h3>Tracker URLs</h3>
<hr class="heading">
<p>Tracker statistics:</p>
<ul class="urls">
<li><a href="http://$B32/tracker">http://$B32/tracker</a></li>
<li><a href="http://$B32/tracker/">http://$B32/tracker/</a></li>
<li><a href="http://$B32/tracker/index.html">http://$B32/tracker/index.html</a></li>
<li><a href="http://$B32/tracker/index.jsp">http://$B32/tracker/index.jsp</a></li>
</ul>
<p>Supported announce URLs:</p>
<ul class="urls">
<li><a href="http://$B32/a">http://$B32/a</a></li>
<li><a href="http://$B32/announce">http://$B32/announce</a></li>
<li><a href="http://$B32/announce.jsp">http://$B32/announce.jsp</a></li>
<li><a href="http://$B32/announce.php">http://$B32/announce.php</a></li>
<li><a href="http://$B32/tracker/a">http://$B32/tracker/a</a></li>
<li><a href="http://$B32/tracker/announce">http://$B32/tracker/announce</a></li>
<li><a href="http://$B32/tracker/announce.jsp">http://$B32/tracker/announce.jsp</a></li>
<li><a href="http://$B32/tracker/announce.php">http://$B32/tracker/announce.php</a></li>
</ul>
<p>Supported scrape URLs:</p>
<ul class="urls">
<li><a href="http://$B32/scrape">http://$B32/scrape</a></li>
<li><a href="http://$B32/scrape.jsp">http://$B32/scrape.jsp</a></li>
<li><a href="http://$B32/scrape.php">http://$B32/scrape.php</a></li>
<li><a href="http://$B32/tracker/scrape">http://$B32/tracker/scrape</a></li>
<li><a href="http://$B32/tracker/scrape.jsp">http://$B32/tracker/scrape.jsp</a></li>
<li><a href="http://$B32/tracker/scrape.php">http://$B32/tracker/scrape.php</a></li>
</ul>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,25 @@
<!DOCTYPE HTML>
<html>
<!-- edit this file if you want to change your home page -->
<head>
<meta charset="UTF-8">
<title>ZZZOT OPENTRACKER</title>
<style type="text/css">
body, html {min-height: 100%; font-size: 0;}
body {background: #111;}
body {background: repeating-linear-gradient(45deg, #444, #333 2px, #222 3px), repeating-linear-gradient(135deg, #444, #333 2px, #222 3px); background-blend-mode: multiply, normal;}
</style>
<link href="/tracker.css" rel="stylesheet" type="text/css">
</head>
<body>
<div id="container">
<div id="panel">
<a href="/tracker" title="View OpenTracker stats"><span id="sitename">zzzot</span></a>
<span id="footer" class="b32">$B32</span>
</div>
</div>
</body>
</html>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure 1.2//EN" "http://jetty.mortbay.org/configure_1_2.dtd"> <!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!-- ========================================================================= --> <!-- ========================================================================= -->
<!-- This file configures the Jetty server. --> <!-- This file configures the Jetty server. -->
@ -9,20 +9,20 @@
<!-- * host: Change 127.0.0.1 to 0.0.0.0 in the addListener section --> <!-- * host: Change 127.0.0.1 to 0.0.0.0 in the addListener section -->
<!-- to access the server directly (bypassing i2p) --> <!-- to access the server directly (bypassing i2p) -->
<!-- from other computers. --> <!-- from other computers. -->
<!-- * port: Default 7662 in the addConnector section --> <!-- * port: Default 7662 in the addConnector section -->
<!-- * docroot: Change the ResourceBase in the addContext section --> <!-- * docroot: Change the ResourceBase in the contexts/base-context.xml file -->
<!-- to serve files from a different location. --> <!-- to serve files from a different location. -->
<!-- * threads: Raise MinThreads and/or MaxThreads in the addListener section --> <!-- * threads: Raise maximumPoolSize in the ThreadPool section -->
<!-- if you have a high-traffic site and get a lot of warnings. --> <!-- if you have a high-traffic site and get a lot of warnings. -->
<!-- --> <!-- -->
<!-- I2P uses Jetty 6.1.26. If you need web server features not found --> <!-- I2P uses Jetty 9. If you need web server features not found -->
<!-- in Jetty 6, you may install and run Jetty 7 or 8 in a different JVM --> <!-- in I2P's Jetty 9, you may install and run Jetty 9 in a different JVM -->
<!-- or run any other web server such as Apache. If you do run another web --> <!-- or run any other web server such as Apache. If you do run another web -->
<!-- server instead, be sure and disable the Jetty 6 server for your --> <!-- server instead, be sure and disable the Jetty 9 server for your -->
<!-- eepsite on http://127.0.0.1:7657/configclients.jsp . --> <!-- eepsite on http://127.0.0.1:7657/configclients.jsp . -->
<!-- --> <!-- -->
<!-- Jetty now uses the I2P logging system rather than wrapper.log. --> <!-- Jetty now uses the I2P logging system rather than wrapper.log. -->
<!-- Use the log override org.mortbay.jetty.Server to adjust the log level. --> <!-- Use the log override org.eclipse.jetty.server.Server to adjust the log level. -->
<!-- --> <!-- -->
<!-- Note that the XML encoding for this file is UTF-8. --> <!-- Note that the XML encoding for this file is UTF-8. -->
<!-- --> <!-- -->
@ -37,40 +37,35 @@
<!-- =============================================================== --> <!-- =============================================================== -->
<Configure id="Server" class="org.mortbay.jetty.Server"> <Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Server Thread Pool --> <!-- Server Thread Pool -->
<!-- =========================================================== --> <!-- =========================================================== -->
<Set name="ThreadPool"> <Arg>
<!-- PICK ONE --> <!--
Requests above the maxThreads + queue_size will be rejected and logged.
<!-- If you don't have or want threadpool ref:
Requests above the max will be queued https://wiki.eclipse.org/Jetty/Howto/High_Load
http://trac.i2p2.i2p/ticket/1395
--> -->
<!-- <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<New class="org.mortbay.thread.QueuedThreadPool"> <Arg type="int">20</Arg> <!-- maxThreads, overridden below -->
<Set name="minThreads">1</Set> <Arg type="int">3</Arg> <!-- minThreads, overridden below -->
<Set name="maxThreads">16</Set> <Arg type="int">60000</Arg> <!-- maxIdleTimeMs, overridden below -->
<Set name="lowThreads">2</Set> <Arg>
<New class="java.util.concurrent.LinkedBlockingQueue">
<Arg type="int">50</Arg>
</New>
</Arg>
<Set name="minThreads">4</Set>
<Set name="maxThreads">20</Set>
<Set name="idleTimeout">60000</Set>
<Set name="daemon">true</Set>
<Set name="name">Zzzot Jetty</Set>
</New> </New>
--> </Arg>
<!-- Optional Java 5 bounded threadpool with job queue
Requests above the max will be rejected
TODO: would be nice to use the 5-arg constructor but
how do you use an Enum as the TimeUnit argument?
Alternatively, make a custom class where we can
set the thread name, set daemon, etc.
See RouterConsoleRunner.
-->
<New class="org.mortbay.thread.concurrent.ThreadPool">
<Arg type="int">0</Arg>
<Set name="corePoolSize">1</Set>
<Set name="maximumPoolSize">16</Set>
</New>
</Set>
@ -82,18 +77,34 @@
<!-- Use this connector for many frequently idle connections <!-- Use this connector for many frequently idle connections
and for threadless continuations. and for threadless continuations.
--> Not recommended on Java 5 - comment this out, and uncomment the
SocketConnector below.
Do not use for gij or JamVM - comment this out, and uncomment the
SocketConnector below.
-->
<Call name="addConnector"> <Call name="addConnector">
<Arg> <Arg>
<New class="org.mortbay.jetty.nio.SelectChannelConnector"> <New class="org.eclipse.jetty.server.ServerConnector">
<Arg><Ref id="Server" /></Arg>
<Arg type="int">1</Arg> <!-- number of acceptors -->
<Arg type="int">0</Arg> <!-- default number of selectors -->
<Arg>
<Array type="org.eclipse.jetty.server.ConnectionFactory"> <!-- varargs so we need an array -->
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg>
<New class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="sendServerVersion">false</Set>
<Set name="sendDateHeader">true</Set>
</New>
</Arg>
</New>
</Item>
</Array>
</Arg>
<Set name="host">127.0.0.1</Set> <Set name="host">127.0.0.1</Set>
<Set name="port">7662</Set> <Set name="port">7662</Set>
<Set name="maxIdleTime">60000</Set> <Set name="idleTimeout">600000</Set>
<Set name="Acceptors">1</Set>
<Set name="statsOn">false</Set>
<Set name="confidentialPort">8443</Set>
<Set name="lowResourcesConnections">5000</Set>
<Set name="lowResourcesMaxIdleTime">5000</Set>
</New> </New>
</Arg> </Arg>
</Call> </Call>
@ -103,89 +114,101 @@
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- <!--
<Set name="sessionIdManager"> <Set name="sessionIdManager">
<New class="org.mortbay.jetty.servlet.HashSessionIdManager"> <New class="org.eclipse.jetty.server.session.HashSessionIdManager">
<Set name="workerName">node1</Set> <Set name="workerName">node1</Set>
</New> </New>
</Set> </Set>
--> -->
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Set handler Collection Structure --> <!-- Set handler Collection Structure -->
<!-- --> <!-- -->
<!-- We use a RewriteHandler at the top level so that we may --> <!-- We use a RewriteHandler at the top level so that we may -->
<!-- convert any top-level URLs for the tracker down into the --> <!-- convert any top-level URLs for the tracker down into the -->
<!-- tracker servlet, without interfering with anything else --> <!-- tracker servlet, without interfering with anything else -->
<!-- that is hosted on the same eepsite. --> <!-- that is hosted on the same eepsite. -->
<!-- =========================================================== --> <!-- =========================================================== -->
<Set name="handler"> <Set name="handler">
<New id="Rewrite" class="org.mortbay.jetty.handler.rewrite.RewriteHandler"> <New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
<Set name="rewriteRequestURI">true</Set> <Set name="rewriteRequestURI">true</Set>
<Set name="rewritePathInfo">false</Set> <Set name="rewritePathInfo">false</Set>
<Set name="originalPathAttribute">requestedPath</Set> <Set name="originalPathAttribute">requestedPath</Set>
<Set name="rules"> <Set name="rules">
<Array type="org.mortbay.jetty.handler.rewrite.Rule"> <Array type="org.eclipse.jetty.rewrite.handler.Rule">
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/help</Set>
<Set name="replacement">/help.html</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/help/</Set>
<Set name="replacement">/help.html</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/a</Set> <Set name="pattern">/a</Set>
<Set name="replacement">/tracker/announce.jsp</Set> <Set name="replacement">/tracker/announce.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/announce</Set> <Set name="pattern">/announce</Set>
<Set name="replacement">/tracker/announce.jsp</Set> <Set name="replacement">/tracker/announce.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/announce.jsp</Set> <Set name="pattern">/announce.jsp</Set>
<Set name="replacement">/tracker/announce.jsp</Set> <Set name="replacement">/tracker/announce.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/announce.php</Set> <Set name="pattern">/announce.php</Set>
<Set name="replacement">/tracker/announce.jsp</Set> <Set name="replacement">/tracker/announce.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/scrape</Set> <Set name="pattern">/scrape</Set>
<Set name="replacement">/tracker/scrape.jsp</Set> <Set name="replacement">/tracker/scrape.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/scrape.jsp</Set> <Set name="pattern">/scrape.jsp</Set>
<Set name="replacement">/tracker/scrape.jsp</Set> <Set name="replacement">/tracker/scrape.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/scrape.php</Set> <Set name="pattern">/scrape.php</Set>
<Set name="replacement">/tracker/scrape.jsp</Set> <Set name="replacement">/tracker/scrape.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/Seedless</Set> <Set name="pattern">/Seedless</Set>
<Set name="replacement">/tracker/seedless.jsp</Set> <Set name="replacement">/tracker/seedless.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/Seedless/</Set> <Set name="pattern">/Seedless/</Set>
<Set name="replacement">/tracker/seedless.jsp</Set> <Set name="replacement">/tracker/seedless.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/Seedless/index.jsp</Set> <Set name="pattern">/Seedless/index.jsp</Set>
<Set name="replacement">/tracker/seedless.jsp</Set> <Set name="replacement">/tracker/seedless.jsp</Set>
</New> </New>
</Item> </Item>
<Item> <Item>
<New class="org.mortbay.jetty.handler.rewrite.RewritePatternRule"> <New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/Seedless/seedless</Set> <Set name="pattern">/Seedless/seedless</Set>
<Set name="replacement">/tracker/seedless.jsp</Set> <Set name="replacement">/tracker/seedless.jsp</Set>
</New> </New>
@ -193,17 +216,17 @@
</Array> </Array>
</Set> </Set>
<Set name="handler"> <Set name="handler">
<New id="Handlers" class="org.mortbay.jetty.handler.HandlerCollection"> <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
<Set name="handlers"> <Set name="handlers">
<Array type="org.mortbay.jetty.Handler"> <Array type="org.eclipse.jetty.server.Handler">
<Item> <Item>
<New id="Contexts" class="org.mortbay.jetty.handler.ContextHandlerCollection"/> <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
</Item> </Item>
<Item> <Item>
<New id="DefaultHandler" class="org.mortbay.jetty.handler.DefaultHandler"/> <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
</Item> </Item>
<Item> <Item>
<New id="RequestLog" class="org.mortbay.jetty.handler.RequestLogHandler"/> <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
</Item> </Item>
</Array> </Array>
</Set> </Set>
@ -211,7 +234,32 @@
</Set> </Set>
</New> </New>
</Set> </Set>
<!-- =============================================================== -->
<!-- Create the deployment manager -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- The deplyment manager handles the lifecycle of deploying web -->
<!-- applications. Apps are provided by instances of the -->
<!-- AppProvider interface. Typically these are provided by -->
<!-- one or more of: -->
<!-- jetty-webapps.xml - monitors webapps for wars and dirs -->
<!-- jetty-contexts.xml - monitors contexts for context xml -->
<!-- jetty-templates.xml - monitors contexts and templates -->
<!-- =============================================================== -->
<Call name="addBean">
<Arg>
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
<Set name="contexts">
<Ref id="Contexts" />
</Set>
<Call name="setContextAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/.*jsp-api-[^/]*\.jar$|.*/.*jsp-[^/]*\.jar$|.*/.*taglibs[^/]*\.jar$</Arg>
</Call>
</New>
</Arg>
</Call>
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Configure the context deployer --> <!-- Configure the context deployer -->
<!-- A context deployer will deploy contexts described in --> <!-- A context deployer will deploy contexts described in -->
@ -223,15 +271,16 @@
<!-- in the $JETTY_HOME/contexts directory --> <!-- in the $JETTY_HOME/contexts directory -->
<!-- --> <!-- -->
<!-- =========================================================== --> <!-- =========================================================== -->
<Call name="addLifeCycle"> <Ref id="DeploymentManager">
<Arg> <Call name="addAppProvider">
<New class="org.mortbay.jetty.deployer.ContextDeployer"> <Arg>
<Set name="contexts"><Ref id="Contexts"/></Set> <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
<Set name="configurationDir">$PLUGIN/contexts</Set> <Set name="monitoredDirName">$PLUGIN/contexts</Set>
<Set name="scanInterval">0</Set> <Set name="scanInterval">120</Set>
</New> </New>
</Arg> </Arg>
</Call> </Call>
</Ref>
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Configure the webapp deployer. --> <!-- Configure the webapp deployer. -->
@ -246,18 +295,18 @@
<!-- Normally only one type of deployer need be used. --> <!-- Normally only one type of deployer need be used. -->
<!-- --> <!-- -->
<!-- =========================================================== --> <!-- =========================================================== -->
<Call name="addLifeCycle"> <Ref id="DeploymentManager">
<Arg> <Call id="webappprovider" name="addAppProvider">
<New class="org.mortbay.jetty.deployer.WebAppDeployer"> <Arg>
<Set name="contexts"><Ref id="Contexts"/></Set> <New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
<Set name="webAppDir">$PLUGIN/eepsite/webapps</Set> <Set name="monitoredDirName">$PLUGIN/eepsite/webapps</Set>
<Set name="parentLoaderPriority">false</Set> <Set name="parentLoaderPriority">false</Set>
<Set name="extract">true</Set> <Set name="extractWars">false</Set>
<Set name="allowDuplicates">false</Set> <Set name="defaultsDescriptor">$PLUGIN/etc/webdefault.xml</Set>
<Set name="defaultsDescriptor">$PLUGIN/etc/webdefault.xml</Set> </New>
</New> </Arg>
</Arg> </Call>
</Call> </Ref>
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Configure Authentication Realms --> <!-- Configure Authentication Realms -->
@ -268,9 +317,9 @@
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- UNCOMMENT TO ACTIVATE <!-- UNCOMMENT TO ACTIVATE
<Set name="UserRealms"> <Set name="UserRealms">
<Array type="org.mortbay.jetty.security.UserRealm"> <Array type="org.eclipse.jetty.security.LoginService">
<Item> <Item>
<New class="org.mortbay.jetty.security.HashUserRealm"> <New class="org.eclipse.jetty.security.HashLoginService">
<Set name="name">Test Realm</Set> <Set name="name">Test Realm</Set>
<Set name="config">$PLUGIN/etc/realm.properties</Set> <Set name="config">$PLUGIN/etc/realm.properties</Set>
<Set name="refreshInterval">0</Set> <Set name="refreshInterval">0</Set>
@ -305,8 +354,6 @@
<!-- extra options --> <!-- extra options -->
<!-- =========================================================== --> <!-- =========================================================== -->
<Set name="stopAtShutdown">true</Set> <Set name="stopAtShutdown">true</Set>
<Set name="sendServerVersion">false</Set> <Set name="stopTimeout">1000</Set>
<Set name="sendDateHeader">true</Set>
<Set name="gracefulShutdown">1000</Set>
</Configure> </Configure>

BIN
scripts/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -2,27 +2,31 @@ tunnel.0.description=ZzzOT
tunnel.0.i2cpHost=127.0.0.1 tunnel.0.i2cpHost=127.0.0.1
tunnel.0.i2cpPort=7654 tunnel.0.i2cpPort=7654
tunnel.0.name=zzzot tunnel.0.name=zzzot
tunnel.0.option.crypto.lowTagThreshold=4
tunnel.0.option.crypto.tagsToSend=10
tunnel.0.option.i2cp.destination.sigType=7
tunnel.0.option.i2cp.enableAccessList=false tunnel.0.option.i2cp.enableAccessList=false
tunnel.0.option.i2cp.encryptLeaseSet=false tunnel.0.option.i2cp.encryptLeaseSet=false
tunnel.0.option.i2cp.leaseSetEncType=4,0
tunnel.0.option.i2cp.reduceIdleTime=1200000 tunnel.0.option.i2cp.reduceIdleTime=1200000
tunnel.0.option.i2cp.reduceOnIdle=true tunnel.0.option.i2cp.reduceOnIdle=true
tunnel.0.option.i2cp.reduceQuantity=1 tunnel.0.option.i2cp.reduceQuantity=1
tunnel.0.option.i2p.streaming.connectDelay=0 tunnel.0.option.i2p.streaming.connectDelay=0
tunnel.0.option.i2p.streaming.maxConcurrentStreams=40 tunnel.0.option.i2p.streaming.maxConcurrentStreams=80
tunnel.0.option.i2p.streaming.maxConnsPerHour=100 tunnel.0.option.i2p.streaming.maxConnsPerHour=300
tunnel.0.option.i2p.streaming.maxConnsPerMinute=10 tunnel.0.option.i2p.streaming.maxConnsPerMinute=20
tunnel.0.option.i2p.streaming.maxTotalConnsPerHour=2500 tunnel.0.option.i2p.streaming.maxTotalConnsPerHour=50000
tunnel.0.option.i2p.streaming.maxTotalConnsPerMinute=60 tunnel.0.option.i2p.streaming.maxTotalConnsPerMinute=2000
tunnel.0.option.inbound.backupQuantity=0 tunnel.0.option.inbound.backupQuantity=0
tunnel.0.option.inbound.length=3 tunnel.0.option.inbound.length=3
tunnel.0.option.inbound.lengthVariance=0 tunnel.0.option.inbound.lengthVariance=0
tunnel.0.option.inbound.nickname=ZzzOT tunnel.0.option.inbound.nickname=ZzzOT
tunnel.0.option.inbound.quantity=2 tunnel.0.option.inbound.quantity=3
tunnel.0.option.outbound.backupQuantity=0 tunnel.0.option.outbound.backupQuantity=0
tunnel.0.option.outbound.length=3 tunnel.0.option.outbound.length=3
tunnel.0.option.outbound.lengthVariance=0 tunnel.0.option.outbound.lengthVariance=0
tunnel.0.option.outbound.nickname=ZzzOT tunnel.0.option.outbound.nickname=ZzzOT
tunnel.0.option.outbound.quantity=2 tunnel.0.option.outbound.quantity=3
tunnel.0.privKeyFile=plugins/zzzot/eepPriv.dat tunnel.0.privKeyFile=plugins/zzzot/eepPriv.dat
tunnel.0.startOnLoad=true tunnel.0.startOnLoad=true
tunnel.0.targetHost=127.0.0.1 tunnel.0.targetHost=127.0.0.1

View File

@ -5,58 +5,84 @@
# usage: makeplugin.sh plugindir # usage: makeplugin.sh plugindir
# #
# zzz 2010-02 # zzz 2010-02
# zzz 2014-08 added support for su3 files
# #
PUBKEYDIR=$HOME/.i2p-plugin-keys PUBKEYDIR=$HOME/.i2p-plugin-keys
PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key
PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key
B64KEYFILE=$PUBKEYDIR/plugin-public-signing.txt B64KEYFILE=$PUBKEYDIR/plugin-public-signing.txt
export I2P=../i2p/pkg-temp 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} PLUGINDIR=${1:-plugin}
PC=plugin.config PC=plugin.config
PCT=${PC}.tmp PCT=${PC}.tmp
if [ ! -f $PRIVKEYFILE ]
then
mkdir -p $PUBKEYDIR
java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate keygen $PUBKEYFILE $PRIVKEYFILE || exit 1
java -cp $I2P/lib/i2p.jar net.i2p.data.Base64 encode $PUBKEYFILE $B64KEYFILE || exit 1
rm -rf logs/
chmod 444 $PUBKEYFILE $B64KEYFILE
chmod 400 $PRIVKEYFILE
echo "Created new keys: $PUBKEYFILE $PRIVKEYFILE"
fi
rm -f plugin.zip
if [ ! -d $PLUGINDIR ] if [ ! -d $PLUGINDIR ]
then then
echo "You must have a $PLUGINDIR directory" echo "You must have a $PLUGINDIR directory"
exit 1 exit 1
fi fi
OPWD=$PWD if [ ! -f $PLUGINDIR/$PC ]
cd $PLUGINDIR
if [ ! -f $PC ]
then then
echo "You must have a $PC file" echo "You must have a $PLUGINDIR/$PC file"
exit 1 exit 1
fi fi
grep -q '^signer=' $PC SIGNER=`grep '^signer=' $PLUGINDIR/$PC`
if [ "$?" -ne "0" ] if [ "$?" -ne "0" ]
then then
echo "You must have a signer in $PC" echo "You must have a plugin name in $PC"
echo 'For example signer=joe@mail.i2p' echo 'For example name=foo'
exit 1 exit 1
fi fi
SIGNER=`echo $SIGNER | cut -f 2 -d '='`
if [ ! -f $PRIVKEYFILE ]
then
echo "Creating new XPI2P DSA keys"
mkdir -p $PUBKEYDIR || exit 1
java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate keygen $PUBKEYFILE $PRIVKEYFILE || exit 1
java -cp $I2P/lib/i2p.jar net.i2p.data.Base64 encode $PUBKEYFILE $B64KEYFILE || exit 1
rm -rf logs/
chmod 444 $PUBKEYFILE $B64KEYFILE
chmod 400 $PRIVKEYFILE
echo "Created new XPI2P keys: $PUBKEYFILE $PRIVKEYFILE"
fi
if [ ! -f $PRIVKEYSTORE ]
then
echo "Creating new SU3 $KEYTYPE keys for $SIGNER"
java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File keygen -t $KEYTYPE $PUBKEYSTORE $PRIVKEYSTORE $SIGNER || exit 1
echo '*** Save your password in a safe place!!! ***'
rm -rf logs/
# copy to the router dir so verify will work
CDIR=$I2P/certificates/plugin
mkdir -p $CDIR || exit 1
CFILE=$CDIR/`echo $SIGNER | sed s/@/_at_/`.crt
cp $PUBKEYSTORE $CFILE
chmod 444 $PUBKEYSTORE
chmod 400 $PRIVKEYSTORE
chmod 644 $CFILE
echo "Created new SU3 keys: $PUBKEYSTORE $PRIVKEYSTORE"
echo "Copied public key to $CFILE for testing"
fi
rm -f plugin.zip
OPWD=$PWD
cd $PLUGINDIR
grep -q '^name=' $PC grep -q '^name=' $PC
if [ "$?" -ne "0" ] if [ "$?" -ne "0" ]
then then
echo "You must have a plugin name in $PC" echo "You must have a plugin name in $PC"
echo 'For example name=foo' echo 'For example name=foo'
exit 1 exit 1
fi fi
@ -64,7 +90,7 @@ grep -q '^version=' $PC
if [ "$?" -ne "0" ] if [ "$?" -ne "0" ]
then then
echo "You must have a version in $PC" echo "You must have a version in $PC"
echo 'For example version=0.1.2' echo 'For example version=0.1.2'
exit 1 exit 1
fi fi
@ -72,33 +98,41 @@ fi
grep -v '^date=' $PC > $PCT grep -v '^date=' $PC > $PCT
DATE=`date '+%s000'` DATE=`date '+%s000'`
echo "date=$DATE" >> $PCT echo "date=$DATE" >> $PCT
mv $PCT $PC mv $PCT $PC || exit 1
# add our Base64 key # add our Base64 key
grep -v '^key=' $PC > $PCT grep -v '^key=' $PC > $PCT
B64KEY=`cat $B64KEYFILE` B64KEY=`cat $B64KEYFILE`
echo "key=$B64KEY" >> $PCT || exit 1 echo "key=$B64KEY" >> $PCT || exit 1
mv $PCT $PC mv $PCT $PC || exit 1
# zip it # zip it
zip -r $OPWD/plugin.zip * -x \*.jar || exit 1 zip -r $OPWD/plugin.zip * || exit 1
# get the version and use it for the sud header # get the version and use it for the sud header
VERSION=`grep '^version=' $PC | cut -f 2 -d '='` VERSION=`grep '^version=' $PC | cut -f 2 -d '='`
# get the name and use it for the file name # get the name and use it for the file name
NAME=`grep '^name=' $PC | cut -f 2 -d '='` NAME=`grep '^name=' $PC | cut -f 2 -d '='`
XPI2P=${NAME}.xpi2p XPI2P=${NAME}.xpi2p
SU3=${NAME}.su3
cd $OPWD cd $OPWD
# sign it # sign it
echo 'Signing. ...'
java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate sign plugin.zip $XPI2P $PRIVKEYFILE $VERSION || exit 1 java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate sign plugin.zip $XPI2P $PRIVKEYFILE $VERSION || exit 1
java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File sign -c PLUGIN -t $KEYTYPE plugin.zip $SU3 $PRIVKEYSTORE $VERSION $SIGNER || exit 1
rm -f plugin.zip rm -f plugin.zip
# verify # verify
echo 'Verifying. ...' echo 'Verifying. ...'
java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate showversion $XPI2P || exit 1 java -cp $I2P/lib/i2p.jar net.i2p.crypto.TrustedUpdate showversion $XPI2P || exit 1
java -cp $I2P/lib/i2p.jar -Drouter.trustedUpdateKeys=$B64KEY net.i2p.crypto.TrustedUpdate verifysig $XPI2P || exit 1 java -cp $I2P/lib/i2p.jar -Drouter.trustedUpdateKeys=$B64KEY net.i2p.crypto.TrustedUpdate verifysig $XPI2P || exit 1
java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File showversion $SU3 || exit 1
java -cp $I2P/lib/i2p.jar net.i2p.crypto.SU3File verifysig -k $PUBKEYSTORE $SU3 || exit 1
rm -rf logs/ rm -rf logs/
echo -n 'Plugin created: ' echo 'Plugin files created: '
wc -c $XPI2P wc -c $XPI2P
wc -c $SU3
exit 0

View File

@ -1,11 +1,14 @@
name=zzzot name=zzzot
signer=zzz-plugin@mail.i2p signer=zzz-plugin@mail.i2p
consoleLinkName=ZzzOT consoleLinkName=ZzzOT
consoleLinkURL=http://127.0.0.1:7662/tracker/index.jsp consoleLinkURL=http://127.0.0.1:7662/tracker/
description=Open tracker consoleLinkTooltip=BitTorrent OpenTracker
icon-code=iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAABm1BMVEUAAAAZGRkZGRkaGhoZGRkZGRkZGRkZGRkYGBgZGRkYGBgZGRkYGBgZGRkZGRkXFxcYGBgZGRkZGRkZGRkZGRkAAAAZGRnziRYTExPzfhbzbhbzdxbzgxbzlRYLCwvzjhbzcxbMzMzzkBb~~~~~~Pl0dHRdXV3GxsYnJyfe3t7OycbBwcGcnJyHh4cjEgP39~f96dSurq5~f397e3tra2tlZWU7OzseHh7haRSGSAsOBwGpqKeWlpbgpnRUVFRNTU00NDTxmynPbRJwNgpXLwdEJQY4IAUVCwLy8vL83sK7u7uysrL7yqf6v5Tdr4H4t3H4rGz2pEvnjUnqnkQvLy~zii30gyjxdCLqdhXLexK9bhGQTQ1-QAtlLgn-9u~Pxr7Uvqr71KbYuJz6yZn5v4T3pVv3s1rkmVr1nzr1kzLyjiPzex~pbBXJYxLJXRK~XxG4aBCYRQ5iNQlMKgctGgT7-~vas4zhnGj2qlNERETlhRTefxTedRTVfxOsZhC0YBC4UhCpWw9wQgr4sH~kqF7sizVpSyfWXxOZVw56Yx8pAAAAFXRSTlMAjPQOr8vV7eVwaVtPNyUa27WCwoZvol0xAAAEx0lEQVRYw72XZ1PbQBCGXQEnVMNJAjtyjpPcbWxsMB2SQOg1tCT0EiA9kN578rOzd4eQZUuO8iXPjGfkmdv3dm-v7DqsaKht8XhrBKDG6~HXNjj-iTpnlVBClbPOrrXb18htJElmSNK5hs9tx9zp4saCXOgOq6qaH-qWkCQzEZf~rxKXqvnU0c6pCUKIohDKREKNckeqL1U0r~cyc6kzRpTJg8OTILBysji~nyEkpnKJ5npr-8suZp7sUHbvXTFybX6SvEhKVMJ12creSaeXUx1bB6utJqzsKx0pthZOc3sPnX4kpsw-uNozmr5xtZzVGRIboU54zOyb6PQ5kjkWxbdphNBd0YTFDFGpE03m88v9ZD8Ew04R8Fw0I7RLsrKZD05qH1EWqKePEUNz~GGPIY4FMkUVnCXrT-2nlMNWyhhirLM~66MIpZ-0FnGoJKjCZUP-XWCfJTx3txHnNvt3hoBxQ0rvKf2g4CreD83ggErmg4xRxPkUBE7ZZ1~QwALJgQveov0L9iNkpo3Rg855Bn82-Odmm5FZMgIKF7vaXQ0BxDIil7-rCWzo39eDRsRMDIKo1k6WHxxIkcV2TloTGG3~iDjP20s5VlJ6JtywglLHTIDzCHHo2mtfvYEyZjskWEfugg8cSCqrAc4NdEGf9tFTLvBgKwku-JhAFazAi93QOeOojLGQCQfUhUZ2~7EUnoicp6icL6IJ95VOcKGOb2I5NqklaBOZkN589ritlJmYDMvII4iSBW2f9iErxt63GlgkURZDAzigkmtXOHeQNaNXjLAYGhy1sASJjCZ7iiqwbnRhcgoup1pHiyCgiVktMMsI-N428G0C0UVogl1E5kWOdQQmV8xrIkhwsXgFqZu8DnG-oop8CBVzTLolOJI1gjxEVq5yxlFFjBdtgIRlocYBSciTVf0cVOTs7qOAjqLCTnCwLLZzepENbn79-JYP39IEwttBThrZ5IwNx2FNIM7z8gvZ5jMMX8NDIEAXcRgHGbYi0O~LFfwGFpGmcRnznTyGbMJv7Ft4mabRAxsJL7VSxpFtrsPwIww3QhM9zSjxir1AyD70sRpMwFZuYYdpYI~vY~uIwE5SpoepAQS64iLw-98E7rMsQvnXKEgFvAQubSLb9MHwI1yQhKrzKy37EhblJrJNLwzfmz6~0upYDJDIPmSTsQ-0aGIRsOKzkVYGg8H3yB7jX4KUOSzzCPjD0rm99hnZ4sbDNspaPAcO-PSnLTLYa-ckbmjP7FxEpk-bXt7k8c-~GffeCV1wC-fBAX~x8z7dX3HmJ-uGl3Eny5734gIjilXruAMlzOEoLzA0vDSVeBhZ8DRk5DtLYXNpkZWKdFsJiAZu4RQrssrKvGTCQsH4si7FB1iZZ1JoDuBhq9OvcxRPskLTtNRN4Zz5~aMzx~wXPBbFdhj3Ryt58GMHd-nFtpkP0SzOSWX3TxtnbRBPR1m5X6nhyEciaonEO17UDG5H8rzhqNjyyHIO44FhuViAbt6X8Uhn5ZYHqG8WmER4GuN-dWhEYq4Ull7txXG2izdd3nobbZ8sF8LJBGZE4JcY6CrYavsAt98lcA0kLb8ZCofDw8sSor4DLqfbVutbZd76Nvrc~6P51tt~v97-t1i3~38A57d5M52iLPcAAAAASUVORK5CYII=
description=BitTorrent OpenTracker
author=zzz author=zzz
updateURL=http://stats.i2p/i2p/plugins/zzzot-update.xpi2p updateURL=http://stats.i2p/i2p/plugins/zzzot-update.xpi2p
updateURL.su3=http://stats.i2p/i2p/plugins/zzzot-update.su3
websiteURL=http://zzz.i2p/forums/16 websiteURL=http://zzz.i2p/forums/16
license=Apache 2.0 license=Apache 2.0
min-jetty-version=6 min-jetty-version=9
max-jetty-version=6.99999 min-i2p-version=0.9.31

235
scripts/tracker-purple.css Normal file
View File

@ -0,0 +1,235 @@
/* ZZZOT OpenTracker theme (purrrrple) */
/* Author: dr|z3d 2019 */
html, body {
margin: 0;
padding: 0;
min-height: 100%;
color: #fef;
background: #111;
font-size: 14pt;
font-family: "Droid Sans", "Open Sans", "Noto Sans", Ubuntu, "Segoe UI", "Lucida Grande", Verdana, Helvetica, sans-serif;
}
@supports (background-blend-mode: overlay) {
html, body {
background: repeating-linear-gradient(45deg, #313, #000 2px, #000 3px), repeating-linear-gradient(135deg, #414, #313 2px, #212 3px);
background-blend-mode: overlay, normal;
background-size: 100% 100%, 100% 100%;
background-attachment: fixed;
}
}
#container {
padding: 2%;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: table;
text-align: center;
box-sizing: border-box;
}
#panel {
padding: 20px;
position: relative;
display: table-cell;
vertical-align: middle;
text-align: center;
border: 1px solid #535;
box-shadow: inset 0 0 0 1px #111, inset 0 0 2px 1px #444, 0 0 2px 2px #000;
background: #180618;
background: repeating-linear-gradient(to right, rgba(255, 200, 255, .05), rgba(0, 0, 0, .08) 2px), repeating-linear-gradient(to bottom, #212, #101 2px); /* purple */
background-blend-mode: overlay;
will-change: transform;
}
code {
font-family: "Droid Sans Mono", "Noto Mono", "DejaVu Sans Mono", "Lucida Console", monospace;
}
#sitename, #sitename:hover, #sitename:focus {
margin: 0 auto;
font-size: 10em;
font-weight: bold;
text-transform: uppercase;
line-height: 1;
letter-spacing: .05em;
transition: ease background .3s;
color: #731;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
#stats #sitename, #stats #sitename:hover, #stats #sitename:focus {
font-size: 8em;
}
#sitename:hover, #sitename:focus {
color: #951;
}
@supports (-webkit-text-stroke-width: 1px) {
#sitename, #sitename:hover, #sitename:focus {
background: #731;
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, .5) 2px), linear-gradient(to bottom, rgba(255, 96, 0, .2), rgba(0, 0, 0, .9) 100%), linear-gradient(to bottom, #414, #313 15%, #fff 50%, #313 80%);
-moz-background-clip: text !important;
-webkit-background-clip: text !important;
background-clip: text !important;
-moz-text-fill-color: transparent !important;
-webkit-text-fill-color: transparent !important;
text-fill-color: transparent !important;
filter: hue-rotate(0) drop-shadow(0 0 2px #181818) drop-shadow(0 3px 0.01em #000);
-webkit-text-stroke-color: #fef;
-webkit-text-stroke-width: 0.02em;
animation: ease-in-out spinwash 120s alternate infinite;
mix-blend-mode: soft-light;
}
#sitename:hover, #sitename:focus {
background: #951;
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, .3) 2px), linear-gradient(to bottom, rgba(255, 255, 255, .2), rgba(0, 0, 0, .95)), linear-gradient(to bottom, #d59, #a39 15%, #fff 50%, #839 70%, #000);
filter: drop-shadow(0 0 0.02em #f00);
-webkit-text-stroke-color: #fef;
opacity: .5;
mix-blend-mode: normal;
background-blend-mode: overlay;
animation: none;
}
}
@keyframes spinwash {
from {
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, .3) 2px), linear-gradient(45deg, rgba(64,16,64,.1), rgba(16,0,16,.2)), linear-gradient(to bottom, rgba(255, 255, 255, .2), rgba(0, 0, 0, .7) 100%), linear-gradient(to bottom, #930, #d50 15%, #fff 50%, #930 80%, #000 100%, #ff0 150%);
filter: sepia(0) hue-rotate(0) drop-shadow(0 0 2px #181818) drop-shadow(0 3px 0.01em #000);
}
to {
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, .3) 2px), linear-gradient(45deg, rgba(16,0,16,.1), rgba(64,16,64,.2)), linear-gradient(to bottom, rgba(255, 255, 255, .2), rgba(0, 0, 0, .7) 100%), linear-gradient(to bottom, #930, #d50 15%, #fff 50%, #930 80%, #000 100%, #ff0 150%);
filter: sepia(0) hue-rotate(1440deg) drop-shadow(0 0 2px #181818) drop-shadow(0 3px 0.01em #000);
}
}
a, a:visited {
color: #f60;
text-decoration: none;
outline: none;
}
a:hover, a:focus {
color: #f90;
}
hr {
margin: 20px 10px;
height: 1px;
color: transparent;
border: none;
background: #555;
background: linear-gradient(to right, rgba(0, 0, 0, 0), #535, rgba(0, 0, 0, 0));
}
::selection,
::-moz-selection {
text-shadow: none;
background: #515;
color: #fff;
}
#totals {
padding-top: 6px;
line-height: 140%;
text-shadow: 0 1px 1px #000;
}
#footer {
padding: 4px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
font-size: 11pt;
font-weight: bold;
border-top: 1px solid #535;
box-shadow: inset 0 0 0 1px #000, inset 0 0 2px 1px #333;
background: rgba(16, 0, 16, .5);
-moz-user-select: all;
-webkit-user-select: all;
user-select: all;
}
#footer.version {
font-size: 9.5pt;
}
#footer.version a {
margin: 0 1px 0 2px;
}
#footer.version::before {
margin-left: -10px;
content: "";
display: inline-block;
margin-top: 1px;
width: 20px;
height: 18px;
vertical-align: middle;
background: url(/favicon.png) left top no-repeat;
background-size: 16px 16px;
filter: hue-rotate(-110deg) saturate(.4) brightness(1.4);
}
#initializing::before {
margin-left: -20px;
content: "";
display: inline-block;
width: 28px;
height: 24px;
vertical-align: text-bottom;
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABRhJREFUeNq0Vd1vk1UYP+f9bruPrmu70W1lrLKJzE0o+BEFt4yLRZJpgl4YZ6IXhiuT/Slcqlw6QyIauNEYIhHlAgMrJDjUDQejdF3Xj7Xv24/3+xyf01LWOpArTvLr6XnOOc/X7znPiyml6HkOgf28+cXDxwLMcYgDsBljHAS8AeJjgDFAGMAByoAk4Ddw8GdAihKCmLMcz7N7s7CXu/JpOCE8xbAXMC0KaGYwLMeG9yj7ujuEPkXmAiDnDZNYasUx1jeNqVTWOG3Z6CLIzwK2AfNgaOGRniNPMtADB+ZiEXl8Yn9nTOD4AcMgnXoVydUy4R4FKXtFQT482tU98YIvcvtuuf+fhzUW6RXiuvMfn4zEv/4hnXicIhA209MlYDrz6rg/Gu33BlWViLbtPDW/lQpBoojlw2Pdw8Fu0Xd9uRh9/8TAIU0jiDjODgcuLCBvEqY0fnQyEOzrUXybmzZqFgDsMUWI53F97boU2Tat71sWQrUaRn0BJXRqJhJKpy3U3S3AGXfHAAGCQEn0xeFOPyj3bm5a9ctMsSxjkteqhdRWNV8qWyxFnL9TUgb7fIFgl7fXNCnPzqbTO5FacMxtjQBSJMsSHz4w4vekUnrdQ6Zc8WDnxp+5B4WSsQqCH+HoHRZQxrQnMrnKh71+5fgro6GYoVOptdwtC7cbcGy7JzbUIWsli9drdqOMfDxZupNNbavGZSi975nBJl9w+VdYD23l7COel/sktWi0cWOZHNPZxkHXUL9Pym2V4T+lmMNORrdT2Xx5GRTVlZt6DYJohM7z/DwsFk4eG4kn10u7yDcNuisCiSOE08t19/OixOfXN0oaHPqdUoKqmlo/LCkeNs06jrMwNx2Lq0WoXWl3dVHi/CcCWDimqVpGTW2sOVRUa1XXJduu20KeriNBFHMs3xcu/Z14RpdItEbw+cXLq/Fn9RVK6GkovwSk7EhTBpyIHIdfOjDY6WvKIgM96NK1da2Vg/hHHxz6XwPfnL/FPAoxkiVFqZMNqQKjJNDhk4dtjLtY9cGwq7arurajtUaAdIugm8vpXYoPj0fQt98tMa/PQBP8yTZNVCmVkCBJjGz2+o+HwsFxmxd4m7oFxmFZtwnotNo4qDoUGbzYpnxyfxCdO3ctAX6dgeWiY1moJTUICvdUj987HR7slZPZSg7xHPbKAtpIFyzISksEEGqFYKQL8mMFPkVAEJTlD/i8+az6FqSC9fTbjArAQSD6HdgbfW3qoLyypRead30y56aSOdOxnWJbBJrLI13cMaBDKyEqlV6fjY9m7m323l/deLuU19iLIv5gF9k3OoD7R/aQv7JmfhvuIpFHHTKP0itrulHVs5BSs63ZlVHDQCwgoZTmINMhaAMyks+4/N7IQDg+ujfcIXH17FQsUs5UnOLVjF0wHZDBPVngkJHZqKVXH5SAl2Tz5TcMQD41JKJQQEQ3Fy/c6j06OYijwyGd8cLyojKw7ui2MAQKMLwyoM0jQO9JrldTN/7IU1bGlFqo1QDr3RUsoNTZxQRsLG5dvT7lGytMipMTewzFK0MwT/7egvOKUbP1pdv56spaEu5eBu815O44gtmrFN/9aulR42eluAiyAJD6GZbl9+ThaB8fGwlQf49CPR7WGFys69u4VNxy1+7dN9eTa9Q0meJfADX2uWsO4/wnDQPC3JdxmEOsztlHm8lYGcI8COdOAI4DooBOFjAgC1gBXAVcg3znucabQE808DzHvwIMALq5vhJjsCXwAAAAAElFTkSuQmCC") center center no-repeat;
background-size: 16px 16px;
filter: hue-rotate(90deg) saturate(.6);
animation: spin linear 3s forwards infinite;
}
@keyframes spin {
from {
transform: rotate(0)
}
to {
transform: rotate(360deg)
}
}
@media screen and (max-height: 600px) {
#container {
padding: 1%;
}
}
@media screen and (max-width: 1000px) {
html, body {
font-size: 12pt;
}
#container {
padding: 1%;
}
#sitename, #sitename:hover, #sitename:focus {
font-size: 8em;
}
#stats #sitename, #stats #sitename:hover, #stas #sitename:focus {
font-size: 6em;
}
#footer.b32 {
font-size: 10pt;
}
#footer.version {
font-size: 8.5pt;
}
#initializing::before {
height: 20px;
}
}

247
scripts/tracker.css Normal file
View File

@ -0,0 +1,247 @@
/* ZZZOT OpenTracker theme */
/* Author: dr|z3d 2019 */
html, body {
margin: 0;
padding: 0;
min-height: 100%;
color: #bbb;
background: #111;
font-size: 14pt;
font-family: "Droid Sans", "Open Sans", "Noto Sans", Ubuntu, "Segoe UI", "Lucida Grande", Verdana, Helvetica, sans-serif;
}
@supports (background-blend-mode: overlay) {
html, body {
background: repeating-linear-gradient(45deg, #333, #111 2px, #111 3px),
repeating-linear-gradient(135deg, #444, #333 2px, #222 3px);
background-blend-mode: overlay, normal;
background-size: 100% 100%, 100% 100%;
background-attachment: fixed;
}
}
#container {
padding: 2%;
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
display: table;
text-align: center;
box-sizing: border-box;
}
#panel {
padding: 20px;
position: relative;
display: table-cell;
vertical-align: middle;
text-align: center;
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-blend-mode: overlay;
will-change: transform;
}
code {
font-family: "Droid Sans Mono", "Noto Mono", "DejaVu Sans Mono", "Lucida Console", monospace;
}
#sitename, #sitename:hover, #sitename:focus {
margin: 0 auto;
font-size: 10em;
font-weight: bold;
text-transform: uppercase;
line-height: 1;
letter-spacing: .05em;
transition: ease background .3s;
color: #731;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
#stats #sitename, #stats #sitename:hover, #stats #sitename:focus {
font-size: 8em;
}
#sitename:hover, #sitename:focus {
color: #951;
}
@supports (-webkit-text-stroke-width: 1px) {
#sitename, #sitename:hover, #sitename:focus {
background: #731;
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, .5) 2px),
linear-gradient(to bottom, rgba(255, 96, 0, .2), rgba(0, 0, 0, .9) 100%),
linear-gradient(to bottom, #210, #310 15%, #fff 50%, #310 80%);
-moz-background-clip: text !important;
-webkit-background-clip: text !important;
background-clip: text !important;
-moz-text-fill-color: transparent !important;
-webkit-text-fill-color: transparent !important;
text-fill-color: transparent !important;
filter: drop-shadow(0 0 2px #000);
-webkit-text-stroke-color: #999;
-webkit-text-stroke-width: 0.02em;
animation: ease-in-out spinwash 60s 15s forwards infinite;
}
#sitename:hover, #sitename:focus {
background: #951;
background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .2), rgba(0, 0, 0, .3) 2px),
linear-gradient(to bottom, rgba(255, 255, 255, .2), rgba(0, 0, 0, .7) 100%),
linear-gradient(to bottom, #930, #d50 15%, #fff 50%, #930 80%, #000 100%, #ff0 150%);
filter: drop-shadow(0 0 2px #b00);
-webkit-text-stroke-color: #bbb;
animation: none;
}
}
@keyframes spinwash {
from {
filter: hue-rotate(0) drop-shadow(0 0 2px #000);
}
to {
filter: hue-rotate(360deg) drop-shadow(0 0 2px #000);
}
}
a, a:visited {
font-weight: bold;
color: #c4ad9d;
text-decoration: none;
outline: none;
}
a:hover, a:focus {
color: #e88b44;
}
a:active {
color: #f60;
}
hr {
margin: 20px 10px;
height: 1px;
color: transparent;
border: none;
background: #555;
background: linear-gradient(to right, rgba(0,0,0,0) 35%, rgba(255,255,255,.3), rgba(0,0,0,0) 65%),
linear-gradient(to right, rgba(0, 0, 0, 0), #605555, rgba(0, 0, 0, 0));
}
::selection,
::-moz-selection {
text-shadow: none;
background: #431;
color: #fff;
}
#totals {
padding-top: 6px;
line-height: 140%;
text-shadow: 0 1px 1px #000;
}
#footer {
padding: 4px;
position: absolute;
bottom: 0;
left: 0;
right: 0;
font-size: 11pt;
font-weight: bold;
border-top: 1px solid #555;
box-shadow: inset 0 0 0 1px #000, inset 0 0 2px 1px #333;
background: rgba(0, 0, 0, .5);
-moz-user-select: all;
-webkit-user-select: all;
user-select: all;
}
#footer.version {
padding: 4px;
line-height: 19px;
font-size: 9.5pt;
-moz-user-select: none;
-webkit-user-select: none;
user-select: none;
}
#footer.version a {
margin: 0 1px 0 2px;
}
#footer.version::before {
margin-left: -10px;
content: "";
display: inline-block;
margin-top: 1px;
width: 20px;
height: 18px;
vertical-align: middle;
background: url(/favicon.png) left top no-repeat;
background-size: 16px 16px;
mix-blend-mode: luminosity;
}
#initializing::before {
margin-left: -20px;
content: "";
display: inline-block;
width: 28px;
height: 24px;
vertical-align: text-bottom;
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABRhJREFUeNq0Vd1vk1UYP+f9bruPrmu70W1lrLKJzE0o+BEFt4yLRZJpgl4YZ6IXhiuT/Slcqlw6QyIauNEYIhHlAgMrJDjUDQejdF3Xj7Xv24/3+xyf01LWOpArTvLr6XnOOc/X7znPiyml6HkOgf28+cXDxwLMcYgDsBljHAS8AeJjgDFAGMAByoAk4Ddw8GdAihKCmLMcz7N7s7CXu/JpOCE8xbAXMC0KaGYwLMeG9yj7ujuEPkXmAiDnDZNYasUx1jeNqVTWOG3Z6CLIzwK2AfNgaOGRniNPMtADB+ZiEXl8Yn9nTOD4AcMgnXoVydUy4R4FKXtFQT482tU98YIvcvtuuf+fhzUW6RXiuvMfn4zEv/4hnXicIhA209MlYDrz6rg/Gu33BlWViLbtPDW/lQpBoojlw2Pdw8Fu0Xd9uRh9/8TAIU0jiDjODgcuLCBvEqY0fnQyEOzrUXybmzZqFgDsMUWI53F97boU2Tat71sWQrUaRn0BJXRqJhJKpy3U3S3AGXfHAAGCQEn0xeFOPyj3bm5a9ctMsSxjkteqhdRWNV8qWyxFnL9TUgb7fIFgl7fXNCnPzqbTO5FacMxtjQBSJMsSHz4w4vekUnrdQ6Zc8WDnxp+5B4WSsQqCH+HoHRZQxrQnMrnKh71+5fgro6GYoVOptdwtC7cbcGy7JzbUIWsli9drdqOMfDxZupNNbavGZSi975nBJl9w+VdYD23l7COel/sktWi0cWOZHNPZxkHXUL9Pym2V4T+lmMNORrdT2Xx5GRTVlZt6DYJohM7z/DwsFk4eG4kn10u7yDcNuisCiSOE08t19/OixOfXN0oaHPqdUoKqmlo/LCkeNs06jrMwNx2Lq0WoXWl3dVHi/CcCWDimqVpGTW2sOVRUa1XXJduu20KeriNBFHMs3xcu/Z14RpdItEbw+cXLq/Fn9RVK6GkovwSk7EhTBpyIHIdfOjDY6WvKIgM96NK1da2Vg/hHHxz6XwPfnL/FPAoxkiVFqZMNqQKjJNDhk4dtjLtY9cGwq7arurajtUaAdIugm8vpXYoPj0fQt98tMa/PQBP8yTZNVCmVkCBJjGz2+o+HwsFxmxd4m7oFxmFZtwnotNo4qDoUGbzYpnxyfxCdO3ctAX6dgeWiY1moJTUICvdUj987HR7slZPZSg7xHPbKAtpIFyzISksEEGqFYKQL8mMFPkVAEJTlD/i8+az6FqSC9fTbjArAQSD6HdgbfW3qoLyypRead30y56aSOdOxnWJbBJrLI13cMaBDKyEqlV6fjY9m7m323l/deLuU19iLIv5gF9k3OoD7R/aQv7JmfhvuIpFHHTKP0itrulHVs5BSs63ZlVHDQCwgoZTmINMhaAMyks+4/N7IQDg+ujfcIXH17FQsUs5UnOLVjF0wHZDBPVngkJHZqKVXH5SAl2Tz5TcMQD41JKJQQEQ3Fy/c6j06OYijwyGd8cLyojKw7ui2MAQKMLwyoM0jQO9JrldTN/7IU1bGlFqo1QDr3RUsoNTZxQRsLG5dvT7lGytMipMTewzFK0MwT/7egvOKUbP1pdv56spaEu5eBu815O44gtmrFN/9aulR42eluAiyAJD6GZbl9+ThaB8fGwlQf49CPR7WGFys69u4VNxy1+7dN9eTa9Q0meJfADX2uWsO4/wnDQPC3JdxmEOsztlHm8lYGcI8COdOAI4DooBOFjAgC1gBXAVcg3znucabQE808DzHvwIMALq5vhJjsCXwAAAAAElFTkSuQmCC") center center no-repeat;
background-size: 16px 16px;
filter: hue-rotate(180deg) saturate(.6);
animation: spin linear 3s forwards infinite;
}
@keyframes spin {
from {
transform: rotate(0)
}
to {
transform: rotate(360deg)
}
}
@media screen and (max-height: 600px) {
#container {
padding: 1%;
}
}
@media screen and (max-width: 1000px) {
html, body {
font-size: 12pt;
}
#container {
padding: 1%;
}
#sitename, #sitename:hover, #sitename:focus {
font-size: 8em;
}
#stats #sitename, #stats #sitename:hover, #stas #sitename:focus {
font-size: 6em;
}
#footer.b32 {
font-size: 10pt;
}
#footer.version {
margin-top: 0;
line-height: 18px;
font-size: 8.5pt;
}
#initializing::before {
height: 20px;
}
}

3
scripts/zzzot.config Normal file
View File

@ -0,0 +1,3 @@
# announce interval in seconds
# minimum 900 (15 minutes), maximum 21600 (6 hours)
interval=1620

View File

@ -11,10 +11,10 @@
<pathelement location="${i2plib}/mstreaming.jar" /> <pathelement location="${i2plib}/mstreaming.jar" />
<pathelement location="${ant.home}/lib/ant.jar"/> <pathelement location="${ant.home}/lib/ant.jar"/>
<pathelement location="${jettylib}/org.mortbay.jetty.jar"/> <pathelement location="${jettylib}/org.mortbay.jetty.jar"/>
<pathelement location="${jettylib}/jasper-compiler.jar" />
<pathelement location="${jettylib}/jasper-runtime.jar" /> <pathelement location="${jettylib}/jasper-runtime.jar" />
<pathelement location="${jettylib}/javax.servlet.jar" /> <pathelement location="${jettylib}/javax.servlet.jar" />
<pathelement location="${jettylib}/jetty-util.jar" /> <pathelement location="${jettylib}/jetty-util.jar" />
<pathelement location="${jettylib}/jetty-xml.jar" />
<pathelement location="${jettylib}/commons-logging.jar" /> <pathelement location="${jettylib}/commons-logging.jar" />
<pathelement location="${jettylib}/commons-el.jar" /> <pathelement location="${jettylib}/commons-el.jar" />
</path> </path>
@ -25,16 +25,17 @@
</target> </target>
<property name="javac.compilerargs" value="" /> <property name="javac.compilerargs" value="" />
<property name="javac.version" value="1.7" />
<target name="compile"> <target name="compile">
<mkdir dir="./build" /> <mkdir dir="./build" />
<mkdir dir="./build/obj" /> <mkdir dir="./build/obj" />
<javac <javac
srcdir="./java" srcdir="./java"
debug="true" deprecation="on" source="1.5" target="1.5" debug="true" deprecation="on" source="${javac.version}" target="${javac.version}"
destdir="./build/obj" destdir="./build/obj"
includeAntRuntime="false" includeAntRuntime="false"
classpath="${i2plib}/i2p.jar:${i2plib}/i2ptunnel.jar:${i2plib}/i2psnark.jar:${i2plib}/mstreaming.jar:${i2plib}/systray.jar:${jettylib}/org.mortbay.jetty.jar:${jettylib}/jetty-util.jar" > classpath="${i2plib}/i2p.jar:${i2plib}/i2ptunnel.jar:${i2plib}/i2psnark.jar:${i2plib}/mstreaming.jar:${i2plib}/systray.jar:${jettylib}/org.mortbay.jetty.jar:${jettylib}/jetty-util.jar:${jettylib}/jetty-xml.jar" >
<compilerarg line="${javac.compilerargs}" /> <compilerarg line="${javac.compilerargs}" />
</javac> </javac>
</target> </target>
@ -66,13 +67,15 @@
<javac <javac
debug="true" debug="true"
deprecation="on" deprecation="on"
source="1.5" target="1.5" source="${javac.version}" target="${javac.version}"
destdir="build/war/WEB-INF/classes" destdir="build/war/WEB-INF/classes"
srcdir="./build/jspjava" srcdir="./build/jspjava"
includes="**/*.java" includes="**/*.java"
includeAntRuntime="false" includeAntRuntime="false"
classpathref="jspcp" classpathref="jspcp"
failonerror="true" /> failonerror="true" >
<compilerarg line="${javac.compilerargs}" />
</javac>
<copy file="jsp/WEB-INF/web.xml" tofile="build/web.xml" /> <copy file="jsp/WEB-INF/web.xml" tofile="build/web.xml" />
<loadfile property="jspc.web.fragment" srcfile="build/web-fragment.xml" /> <loadfile property="jspc.web.fragment" srcfile="build/web-fragment.xml" />
@ -83,7 +86,6 @@
</target> </target>
<target name="war" depends="precompilejsp"> <target name="war" depends="precompilejsp">
<copy file="jsp/index.html" todir="build/war" />
<war destfile="build/tracker.war.jar" webxml="build/web.xml"> <war destfile="build/tracker.war.jar" webxml="build/web.xml">
<fileset dir="build/war" /> <fileset dir="build/war" />
</war> </war>

View File

@ -16,22 +16,20 @@ package net.i2p.zzzot;
* *
*/ */
import java.io.UnsupportedEncodingException; import net.i2p.data.SimpleDataStructure;
import net.i2p.data.ByteArray;
/** /**
* A 20-byte SHA1 info hash * A 20-byte SHA1 info hash
*/ */
public class InfoHash extends ByteArray { public class InfoHash extends SimpleDataStructure {
public InfoHash(String data) throws UnsupportedEncodingException { public static final int LENGTH = 20;
this(data.getBytes("ISO-8859-1"));
}
public InfoHash(byte[] data) { public InfoHash(byte[] data) {
super(data); super(data);
if (data.length != 20) }
throw new IllegalArgumentException("Bad infohash length: " + data.length);
public int length() {
return LENGTH;
} }
} }

View File

@ -16,22 +16,20 @@ package net.i2p.zzzot;
* *
*/ */
import java.io.UnsupportedEncodingException; import net.i2p.data.SimpleDataStructure;
import net.i2p.data.ByteArray;
/** /**
* A 20-byte peer ID * A 20-byte peer ID
*/ */
public class PID extends ByteArray { public class PID extends SimpleDataStructure {
public PID(String data) throws UnsupportedEncodingException { public static final int LENGTH = 20;
this(data.getBytes("ISO-8859-1"));
}
public PID(byte[] data) { public PID(byte[] data) {
super(data); super(data);
if (data.length != 20) }
throw new IllegalArgumentException("Bad peer ID length: " + data.length);
public int length() {
return LENGTH;
} }
} }

View File

@ -18,14 +18,12 @@ package net.i2p.zzzot;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap;
import net.i2p.crypto.SHA256Generator; import net.i2p.crypto.SHA256Generator;
import net.i2p.data.Base64; import net.i2p.data.Base64;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.data.Hash; import net.i2p.data.Hash;
import net.i2p.util.SimpleScheduler;
import net.i2p.util.SimpleTimer;
/* /*
* A single peer for a single torrent. * A single peer for a single torrent.
@ -38,15 +36,9 @@ public class Peer extends HashMap<String, Object> {
private long lastSeen; private long lastSeen;
private long bytesLeft; private long bytesLeft;
private static final ConcurrentHashMap<String, String> destCache = new ConcurrentHashMap();
private static final Integer PORT = Integer.valueOf(6881); private static final Integer PORT = Integer.valueOf(6881);
private static final long CLEAN_TIME = 3*60*60*1000;
static { public Peer(byte[] id, Destination address, ConcurrentMap<String, String> destCache) {
SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME);
}
public Peer(byte[] id, Destination address) {
super(3); super(3);
if (id.length != 20) if (id.length != 20)
throw new IllegalArgumentException("Bad peer ID length: " + id.length); throw new IllegalArgumentException("Bad peer ID length: " + id.length);
@ -83,10 +75,4 @@ public class Peer extends HashMap<String, Object> {
return new String(h.getData(), "ISO-8859-1"); return new String(h.getData(), "ISO-8859-1");
} catch (UnsupportedEncodingException uee) { return null; } } catch (UnsupportedEncodingException uee) { return null; }
} }
private static class Cleaner implements SimpleTimer.TimedEvent {
public void timeReached() {
destCache.clear();
}
}
} }

View File

@ -0,0 +1,225 @@
package net.i2p.zzzot;
/*
* Modified from:
* http://www.lockergnome.com/awarberg/2007/04/22/random-iterator-in-java/
* No license, free to use
*/
import java.util.ArrayList;
import java.util.BitSet;
import java.util.List;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Random;
import net.i2p.util.RandomSource;
import net.i2p.util.SystemVersion;
/**
* Copied from net.i2p.router.util
*
* This is some Java code I wrote for a school project to save some time when iterating in
* random order over a part of list (until some condition becomes true):
*
* Here is a sample on how to use the code:
*
<pre>
for(Iterator<Object> iter = new RandomIterator<Object>(myObjList); iter.hasNext();){
Object o = iter.next();
if(someCondition(o) )
return o; // iteration stopped early
}
</pre>
*
* I wrote it to replace a Collection.shuffle call and this code gave us an overall increase in program execution speed of about 25%.
* As the javadoc description says, you are better off calling Collection.shuffle if you need to iterate over the entire list. But if you may stop early this class can save you some time, as it did in our case.
*
* Provides a random iteration over the given list.
*
* This effect can be achieved by using Collections.shuffle,
* which shuffles the entire collection in linear time.
*
* If the iteration process may end before all items
* are processed, this class may give a speed increase
* because the shuffling process is performed as items are requested
* rather than in the beginning.
*
* I2P changes:
*<pre>
* - Use BitSet instead of boolean[]
* - Use I2P RandomSource
* - Done check in next(), throw NSEE
* - Ensure lower and upper bounds are always clear
* - Replace unbounded loop in next(). It is now O(N) time, but now
* the iterator will tend to "clump" results and thus is not truly random.
* *** This class is not recommended for small Lists,
* *** or for iterating through a large portion of a List.
* *** Use Collections.shuffle() instead.
* - Add test code
*</pre>
*
* @since zzzot 0.14.0
*/
public class RandomIterator<E> implements Iterator<E> {
/**
* Mapping indicating which items were served (by index).
* if served[i] then the item with index i in the list
* has already been served.
*
* Note it is possible to save memory here by using
* BitSet rather than a boolean array, however it will
* increase the running time slightly.
*/
private final BitSet served;
/** The amount of items served so far */
private int servedCount = 0;
private final List<E> list;
private final int LIST_SIZE;
/**
* The random number generator has a great influence
* on the running time of this iterator.
*
* See, for instance,
* <a href="http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation" title="http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation" target="_blank">http://www.qbrundage.com/michaelb/pubs/e&#8230;</a>
* for some implementations, which are faster than java.util.Random.
*/
private final Random rand = RandomSource.getInstance();
/** Used to narrow the range to take random indexes from */
private int lower, upper;
private static final boolean hasAndroidBug;
static {
if (SystemVersion.isAndroid()) {
// only present on Gingerbread (API 11), but set if version check failed also
int ver = SystemVersion.getAndroidVersion();
hasAndroidBug = ver == 11 || ver == 0;
if (hasAndroidBug)
testAndroid();
} else {
hasAndroidBug = false;
}
}
public RandomIterator(List<E> list){
this.list = list;
LIST_SIZE = list.size();
served = new BitSet(LIST_SIZE);
upper = LIST_SIZE - 1;
}
public boolean hasNext() {
return servedCount < LIST_SIZE;
}
public E next() {
if (!hasNext())
throw new NoSuchElementException();
int range = upper - lower + 1;
// This has unbounded behavior, even with lower/upper
//int index;
//do {
// index = lower + rand.nextInt(range);
//} while (served.get(index));
// This tends to "clump" results, escpecially toward the end of the iteration.
// It also tends to leave the first and last few elements until the end.
int start = lower + rand.nextInt(range);
int index;
if ((start % 2) == 0) // coin flip
index = served.nextClearBit(start);
else
index = previousClearBit(start);
if (index < 0)
throw new NoSuchElementException("shouldn't happen");
servedCount++;
served.set(index);
// check if the range from which random values
// are taken can be reduced
// I2P - ensure lower and upper are always clear
if (hasNext()) {
if (index == lower)
// workaround for Android ICS bug - see below
lower = hasAndroidBug ? nextClearBit(index) : served.nextClearBit(index);
else if (index == upper)
upper = previousClearBit(index - 1);
}
return list.get(index);
}
/** just like nextClearBit() */
private int previousClearBit(int n) {
for (int i = n; i >= lower; i--) {
if (!served.get(i)) {
return i;
}
}
return -1;
}
/**
* Workaround for bug in Android (ICS only?)
* http://code.google.com/p/android/issues/detail?id=31036
* @since 0.9.2
*/
private int nextClearBit(int n) {
for (int i = n; i <= upper; i++) {
if (!served.get(i)) {
return i;
}
}
return -1;
}
/**
* @throws UnsupportedOperationException always
*/
public void remove() {
throw new UnsupportedOperationException();
}
/****
public static void main(String[] args) {
testAndroid();
test(0);
test(1);
test(2);
test(1000);
}
private static void test(int n) {
System.out.println("testing with " + n);
List<Integer> l = new ArrayList<Integer>(n);
for (int i = 0; i < n; i++) {
l.add(Integer.valueOf(i));
}
for (Iterator<Integer> iter = new RandomIterator<Integer>(l); iter.hasNext(); ) {
System.out.println(iter.next().toString());
}
}
****/
/**
* Test case from android ticket above
* @since 0.9.2
*/
private static void testAndroid() {
System.out.println("Checking for Android BitSet bug");
BitSet theBitSet = new BitSet(864);
for (int exp =0; exp < 864; exp++) {
int act = theBitSet.nextClearBit(0);
if (exp != act) {
System.err.println(String.format("Test failed for: exp=%d, act=%d", exp, act));
System.err.println("Android BitSet bug detected, workaround implemented!");
return;
}
theBitSet.set(exp);
}
System.err.println("Android BitSet bug NOT detected, no workaround needed!");
}
}

View File

@ -18,13 +18,30 @@ package net.i2p.zzzot;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import net.i2p.CoreVersion;
import net.i2p.data.DataHelper;
import net.i2p.data.SDSCache;
import net.i2p.util.VersionComparator;
/** /**
* All the torrents * All the torrents
*/ */
public class Torrents extends ConcurrentHashMap<InfoHash, Peers> { public class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
public Torrents() { private static final int CACHE_SIZE = 2048;
private final SDSCache<InfoHash> _hashCache;
private final SDSCache<PID> _pidCache;
private final Integer _interval;
/**
* @param interval in seconds
*/
public Torrents(int interval) {
super(); super();
_hashCache = new SDSCache<InfoHash>(InfoHash.class, InfoHash.LENGTH, CACHE_SIZE);
_pidCache = new SDSCache<PID>(PID.class, PID.LENGTH, CACHE_SIZE);
_interval = Integer.valueOf(interval);
} }
public int countPeers() { public int countPeers() {
@ -34,4 +51,60 @@ public class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
} }
return rv; return rv;
} }
/**
* @return in seconds
* @since 0.12.0
*/
public Integer getInterval() {
return _interval;
}
/**
* Pull from cache or return new
*
* @throws IllegalArgumentException if data is not the correct number of bytes
* @since 0.12.0
*/
public InfoHash createInfoHash(String data) throws IllegalArgumentException {
byte[] d = DataHelper.getASCII(data);
if (d.length != InfoHash.LENGTH)
throw new IllegalArgumentException("bad infohash length " + d.length);
return _hashCache.get(d);
}
/**
* Pull from cache or return new
*
* @throws IllegalArgumentException if data is not the correct number of bytes
* @since 0.12.0
*/
public PID createPID(String data) throws IllegalArgumentException {
byte[] d = DataHelper.getASCII(data);
if (d.length != PID.LENGTH)
throw new IllegalArgumentException("bad peer id length " + d.length);
return _pidCache.get(d);
}
/**
* @since 0.12.0
*/
@Override
public void clear() {
super.clear();
clearCaches();
}
/**
* @since 0.12.0
*/
private void clearCaches() {
// not available until 0.9.17
if (VersionComparator.comp(CoreVersion.VERSION, "0.9.17") >= 0) {
try {
_hashCache.clear();
_pidCache.clear();
} catch (Throwable t) {}
}
}
} }

View File

@ -17,34 +17,74 @@ package net.i2p.zzzot;
*/ */
import java.util.Iterator; import java.util.Iterator;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import net.i2p.util.SimpleScheduler; import net.i2p.I2PAppContext;
import net.i2p.util.SimpleTimer; import net.i2p.util.SimpleTimer2;
/** /**
* Instantiate this to fire it up * Instantiate this to fire it up
*/ */
class ZzzOT { class ZzzOT {
private Torrents _torrents; private final Torrents _torrents;
private static final long CLEAN_TIME = 4*60*1000; private final Cleaner _cleaner;
private static final long EXPIRE_TIME = 60*60*1000; private final ConcurrentHashMap<String, String> _destCache = new ConcurrentHashMap<String, String>();
private final long EXPIRE_TIME;
ZzzOT() { private static final String PROP_INTERVAL = "interval";
_torrents = new Torrents(); private static final long CLEAN_TIME = 4*60*1000;
SimpleScheduler.getInstance().addPeriodicEvent(new Cleaner(), CLEAN_TIME); private static final long DEST_CACHE_CLEAN_TIME = 3*60*60*1000;
private static final int DEFAULT_INTERVAL = 27*60;
private static final int MIN_INTERVAL = 15*60;
private static final int MAX_INTERVAL = 6*60*60;
ZzzOT(I2PAppContext ctx, Properties p) {
String intv = p.getProperty(PROP_INTERVAL);
int interval = DEFAULT_INTERVAL;
if (intv != null) {
try {
interval = Integer.parseInt(intv);
if (interval < MIN_INTERVAL)
interval = MIN_INTERVAL;
else if (interval > MAX_INTERVAL)
interval = MAX_INTERVAL;
} catch (NumberFormatException nfe) {}
}
_torrents = new Torrents(interval);
EXPIRE_TIME = 1000 * (interval + interval / 2);
_cleaner = new Cleaner(ctx);
} }
Torrents getTorrents() { Torrents getTorrents() {
return _torrents; return _torrents;
} }
void stop() { /** @since 0.9.14 */
_torrents.clear(); ConcurrentHashMap<String, String> getDestCache() {
// no way to stop the cleaner return _destCache;
} }
private class Cleaner implements SimpleTimer.TimedEvent { void start() {
_cleaner.forceReschedule(CLEAN_TIME);
}
void stop() {
_cleaner.cancel();
_torrents.clear();
_destCache.clear();
}
private class Cleaner extends SimpleTimer2.TimedEvent {
private final AtomicInteger _runCount = new AtomicInteger();
/** must schedule later */
public Cleaner(I2PAppContext ctx) {
super(ctx.simpleTimer2());
}
public void timeReached() { public void timeReached() {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
@ -61,6 +101,10 @@ class ZzzOT {
if (recent <= 0) if (recent <= 0)
iter.remove(); iter.remove();
} }
if (_runCount.incrementAndGet() % (DEST_CACHE_CLEAN_TIME / CLEAN_TIME) == 0) {
_destCache.clear();
}
schedule(CLEAN_TIME);
} }
} }
} }

View File

@ -21,20 +21,28 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ConcurrentMap;
import net.i2p.CoreVersion;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
import net.i2p.app.ClientApp;
import net.i2p.app.ClientAppManager;
import net.i2p.app.ClientAppState;
import static net.i2p.app.ClientAppState.*;
import net.i2p.apps.systray.UrlLauncher;
import net.i2p.data.Base32; import net.i2p.data.Base32;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.data.PrivateKeyFile; import net.i2p.data.PrivateKeyFile;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.util.FileUtil; import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread; import net.i2p.util.I2PAppThread;
import net.i2p.util.Log; import net.i2p.util.Log;
import net.i2p.i2ptunnel.TunnelController; import net.i2p.util.VersionComparator;
import net.i2p.apps.systray.UrlLauncher;
import org.mortbay.jetty.Server; import org.eclipse.jetty.server.Server;
import org.mortbay.xml.XmlConfiguration; import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
/** /**
* This handles the starting and stopping of an eepsite tunnel and jetty * This handles the starting and stopping of an eepsite tunnel and jetty
@ -47,39 +55,107 @@ import org.mortbay.xml.XmlConfiguration;
* *
* @author zzz * @author zzz
*/ */
public class ZzzOTController { public class ZzzOTController implements ClientApp {
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ZzzOTController.class); private final I2PAppContext _context;
private static Server _server; private final Log _log;
private static TunnelController _tunnel; private final String[] _args;
private static ZzzOT _zzzot; private final ClientAppManager _mgr;
private static Object _lock = new Object(); private Server _server;
private TunnelController _tunnel;
private final ZzzOT _zzzot;
/** only for main() */
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 String _footertext;
private static final String BACKUP = "jetty5.xml"; 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.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 PROP_FOOTERTEXT = "footertext";
private static final String CONFIG_FILE = "zzzot.config";
private static final String BACKUP_SUFFIX = ".jetty8";
private static final String[] xmlFiles = { private static final String[] xmlFiles = {
"jetty.xml", "contexts/base-context.xml", "contexts/cgi-context.xml", "jetty.xml", "contexts/base-context.xml", "contexts/cgi-context.xml",
"etc/realm.properties", "etc/webdefault.xml" }; "etc/realm.properties", "etc/webdefault.xml" };
public static void main(String args[]) { /**
if (args.length != 3 || (!"-d".equals(args[0]))) * @since 0.12.0
throw new IllegalArgumentException("Usage: PluginController -d $PLUGIN [start|stop]"); */
if ("start".equals(args[2])) public ZzzOTController(I2PAppContext ctx, ClientAppManager mgr, String args[]) {
start(args); _context = ctx;
else if ("stop".equals(args[2])) _log = ctx.logManager().getLog(ZzzOTController.class);
stop(); _mgr = mgr;
else _args = args;
throw new IllegalArgumentException("Usage: PluginController -d $PLUGIN [start|stop]"); File cfile = new File(_context.getAppDir(), "plugins/zzzot/" + CONFIG_FILE);
} Properties props = new Properties();
if (cfile.exists()) {
public static Torrents getTorrents() { try {
synchronized(_lock) { DataHelper.loadProps(props, cfile);
if (_zzzot == null) } catch (IOException ioe) {
_zzzot = new ZzzOT(); _log.error("Failed loading zzzot config from " + cfile, ioe);
}
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("No config file " + cfile);
} }
return _zzzot.getTorrents(); _zzzot = new ZzzOT(ctx, props);
_sitename = props.getProperty(PROP_SITENAME, DEFAULT_SITENAME);
_showfooter = props.getProperty(PROP_SHOWFOOTER, DEFAULT_SHOWFOOTER);
_footertext = props.getProperty(PROP_FOOTERTEXT, DEFAULT_FOOTERTEXT);
_state = INITIALIZED;
} }
private static void start(String args[]) { /**
File pluginDir = new File(args[1]); * No longer supported, as we now need the ClientAppManager for the webapp to find us
*/
public synchronized static void main(String args[]) {
throw new UnsupportedOperationException("Must use ClientApp interface");
}
/**
* @return null if not running
*/
public static Torrents getTorrents() {
ClientAppManager mgr = I2PAppContext.getGlobalContext().clientAppManager();
if (mgr == null)
return null;
ClientApp z = mgr.getRegisteredApp(NAME);
if (z == null)
return null;
ZzzOTController ctrlr = (ZzzOTController) z;
return ctrlr._zzzot.getTorrents();
}
/**
* @return null if not running
* @since 0.9.14
*/
public static ConcurrentMap<String, String> getDestCache() {
ClientAppManager mgr = I2PAppContext.getGlobalContext().clientAppManager();
if (mgr == null)
return null;
ClientApp z = mgr.getRegisteredApp(NAME);
if (z == null)
return null;
ZzzOTController ctrlr = (ZzzOTController) z;
return ctrlr._zzzot.getDestCache();
}
/**
* @param args ignored
*/
private void start(String args[]) {
//File pluginDir = new File(args[1]);
File pluginDir = new File(_context.getAppDir(), "plugins/zzzot");
if (!pluginDir.exists()) if (!pluginDir.exists())
throw new IllegalArgumentException("Plugin directory " + pluginDir.getAbsolutePath() + " does not exist"); throw new IllegalArgumentException("Plugin directory " + pluginDir.getAbsolutePath() + " does not exist");
@ -96,20 +172,21 @@ public class ZzzOTController {
_log.error("Unable to create " + key.getAbsolutePath() + ' ' + e); _log.error("Unable to create " + key.getAbsolutePath() + ' ' + e);
throw new IllegalArgumentException("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.logAlways(Log.INFO, "NOTICE: ZzzOT: New eepsite keys created in " + key.getAbsolutePath());
_log.error("NOTICE: ZzzOT: You should back up this file!"); _log.logAlways(Log.INFO, "NOTICE: ZzzOT: You should back up this file!");
String b32 = Base32.encode(dest.calculateHash().getData()) + ".b32.i2p"; String b32 = Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
String b64 = dest.toBase64(); String b64 = dest.toBase64();
_log.error("NOTICE: ZzzOT: Your base 32 address is " + b32); _log.logAlways(Log.INFO, "NOTICE: ZzzOT: Your base 32 address is " + b32);
_log.error("NOTICE: ZzzOT: Your base 64 address is " + b64); _log.logAlways(Log.INFO, "NOTICE: ZzzOT: Your base 64 address is " + b64);
} }
startJetty(pluginDir, dest); startJetty(pluginDir, dest);
startI2PTunnel(pluginDir, dest); startI2PTunnel(pluginDir, dest);
_zzzot.start();
// SeedlessAnnouncer.announce(_tunnel); // SeedlessAnnouncer.announce(_tunnel);
} }
private static void startI2PTunnel(File pluginDir, Destination dest) { private void startI2PTunnel(File pluginDir, Destination dest) {
File i2ptunnelConfig = new File(pluginDir, "i2ptunnel.config"); File i2ptunnelConfig = new File(pluginDir, "i2ptunnel.config");
Properties i2ptunnelProps = new Properties(); Properties i2ptunnelProps = new Properties();
try { try {
@ -118,28 +195,31 @@ public class ZzzOTController {
_log.error("Cannot open " + i2ptunnelConfig.getAbsolutePath() + ' ' + ioe); _log.error("Cannot open " + i2ptunnelConfig.getAbsolutePath() + ' ' + ioe);
throw new IllegalArgumentException("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."); TunnelController tun = new TunnelController(i2ptunnelProps, "tunnel.0.");
// start in foreground so we can get the destination
//tun.startTunnelBackground();
tun.startTunnel();
if (dest != null) { if (dest != null) {
// start in foreground so we can get the destination
tun.startTunnel();
List msgs = tun.clearMessages(); List msgs = tun.clearMessages();
for (Object s : msgs) { for (Object s : msgs) {
_log.error("NOTICE: ZzzOT Tunnel message: " + s); _log.logAlways(Log.INFO, "NOTICE: ZzzOT Tunnel message: " + s);
} }
} else {
tun.startTunnelBackground();
} }
_tunnel = tun; _tunnel = tun;
} }
private static void startJetty(File pluginDir, Destination dest) { private void startJetty(File pluginDir, Destination dest) {
if (_server != null) if (_server != null)
throw new IllegalArgumentException("Jetty already running!"); throw new IllegalArgumentException("Jetty already running!");
migrateJettyXML(pluginDir); migrateJettyXML(pluginDir);
I2PAppContext context = I2PAppContext.getGlobalContext(); File tmpdir = new File(_context.getTempDir().getAbsolutePath(), "/zzzot-work");
File tmpdir = new File(context.getTempDir().getAbsolutePath(), "/zzzot-work");
tmpdir.mkdir(); tmpdir.mkdir();
File jettyXml = new File(pluginDir, "jetty.xml"); File jettyXml = new File(pluginDir, "jetty.xml");
try { try {
Resource.setDefaultUseCaches(false);
XmlConfiguration xmlc = new XmlConfiguration(jettyXml.toURI().toURL()); XmlConfiguration xmlc = new XmlConfiguration(jettyXml.toURI().toURL());
Server serv = (Server) xmlc.configure(); Server serv = (Server) xmlc.configure();
//HttpContext[] hcs = serv.getContexts(); //HttpContext[] hcs = serv.getContexts();
@ -155,18 +235,26 @@ public class ZzzOTController {
launchHelp(pluginDir, dest); launchHelp(pluginDir, dest);
} }
private static void stop() { private void stop() {
stopI2PTunnel(); stopI2PTunnel();
stopJetty(); stopJetty();
if (_zzzot != null) _zzzot.stop();
_zzzot.stop();
} }
private static void stopI2PTunnel() { private void stopI2PTunnel() {
if (_tunnel == null) if (_tunnel == null)
return; return;
try { try {
_tunnel.stopTunnel(); // destroyTunnel() not available until 0.9.17
if (VersionComparator.comp(CoreVersion.VERSION, "0.9.17") >= 0) {
try {
_tunnel.destroyTunnel();
} catch (Throwable t) {
_tunnel.stopTunnel();
}
} else {
_tunnel.stopTunnel();
}
} catch (Throwable t) { } catch (Throwable t) {
_log.error("ZzzOT tunnel stop failed", t); _log.error("ZzzOT tunnel stop failed", t);
throw new IllegalArgumentException("Tunnel stop failed " + t); throw new IllegalArgumentException("Tunnel stop failed " + t);
@ -174,7 +262,7 @@ public class ZzzOTController {
_tunnel = null; _tunnel = null;
} }
private static void stopJetty() { private void stopJetty() {
if (_server == null) if (_server == null)
return; return;
try { try {
@ -190,35 +278,66 @@ public class ZzzOTController {
* Migate the jetty configuration files. * Migate the jetty configuration files.
* Save old jetty.xml if moving from jetty 5 to jetty 6 * Save old jetty.xml if moving from jetty 5 to jetty 6
*/ */
private static void migrateJettyXML(File pluginDir) { private void migrateJettyXML(File pluginDir) {
// contexts dir does not exist in Jetty 5 // contexts dir does not exist in Jetty 5
File file = new File(pluginDir, "contexts"); File file = new File(pluginDir, "contexts");
if (file.exists())
return;
file.mkdir(); file.mkdir();
file = new File(pluginDir, "etc"); file = new File(pluginDir, "etc");
file.mkdir(); file.mkdir();
file = new File(pluginDir, "jetty.xml"); file = new File(pluginDir, "jetty.xml");
if (file.exists()) { if (!shouldMigrate(file))
File backup = new File(pluginDir, BACKUP); return;
if (backup.exists()) {
I2PAppContext ctx = I2PAppContext.getGlobalContext();
backup = new File(pluginDir, BACKUP + ctx.random().nextInt());
}
boolean ok = FileUtil.copy(file, backup, false, true);
if (!ok) {
_log.error("WARNING: Failed to back up " + file + " to " + backup);
}
}
for (int i = 0; i < xmlFiles.length; i++) { for (int i = 0; i < xmlFiles.length; i++) {
migrateJettyFile(pluginDir, xmlFiles[i]); backupAndMigrateFile(pluginDir, xmlFiles[i]);
} }
} }
/** /**
* Migate a single jetty config file, replacing $PLUGIN as we copy it. * @return should we copy over all the files, based on the contents of this one
* @since 0.10 (Jetty 7)
*/ */
private static void migrateJettyFile(File pluginDir, String name) { private static boolean shouldMigrate(File f) {
String xml = FileUtil.readTextFile(f.getAbsolutePath(), 400, true);
if (xml == null)
return true;
return xml.contains("class=\"org.eclipse.jetty.server.nio.SelectChannelConnector\"");
}
/**
* Backup a file and migrate new XML
* @return success
* @since Jetty 7
*/
private boolean backupAndMigrateFile(File toDir, String filename) {
File to = new File(toDir, filename);
boolean rv = backupFile(to);
boolean rv2 = migrateJettyFile(toDir, filename);
return rv && rv2;
}
/**
* Backup a file
* @return success
* @since Jetty 7
*/
private static boolean backupFile(File from) {
if (!from.exists())
return true;
File to = new File(from.getAbsolutePath() + BACKUP_SUFFIX);
if (to.exists())
to = new File(to.getAbsolutePath() + "." + System.currentTimeMillis());
boolean rv = FileUtil.copy(from, to, false, true);
if (rv)
System.err.println("Backed up file " + from + " to " + to);
else
System.err.println("WARNING: Failed to back up file " + from + " to " + to);
return rv;
}
/**
* Migrate a single jetty config file, replacing $PLUGIN as we copy it.
*/
private boolean migrateJettyFile(File pluginDir, String name) {
File templateDir = new File(pluginDir, "templates"); File templateDir = new File(pluginDir, "templates");
File fileTmpl = new File(templateDir, name); File fileTmpl = new File(templateDir, name);
File outFile = new File(pluginDir, name); File outFile = new File(pluginDir, name);
@ -230,29 +349,57 @@ public class ZzzOTController {
props = props.replace("$PLUGIN", pluginDir.getAbsolutePath()); props = props.replace("$PLUGIN", pluginDir.getAbsolutePath());
os = new FileOutputStream(outFile); os = new FileOutputStream(outFile);
os.write(props.getBytes("UTF-8")); os.write(props.getBytes("UTF-8"));
return true;
} catch (IOException ioe) { } catch (IOException ioe) {
_log.error(outFile + " migrate failed", ioe); _log.error(outFile + " migrate failed", ioe);
return false;
} finally { } finally {
if (os != null) try { os.close(); } catch (IOException ioe) {} if (os != null) try { os.close(); } catch (IOException ioe) {}
} }
} }
/** put the directory, base32, and base64 info in the help.html file and launch a browser window to display it */ /** 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) { private void launchHelp(File pluginDir, Destination dest) {
File fileTmpl = new File(pluginDir, "templates/help.html"); File fileTmpl = new File(pluginDir, "templates/help.html");
File outFile = new File(pluginDir, "eepsite/docroot/help.html"); File outFile = new File(pluginDir, "eepsite/docroot/help.html");
File index_in = new File(pluginDir, "templates/index.html");
File index_out = new File(pluginDir, "eepsite/docroot/index.html");
String b32 = Base32.encode(dest.calculateHash().getData()) + ".b32.i2p"; String b32 = Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
String b64 = dest.toBase64(); String b64 = dest.toBase64();
try { try {
String html = FileUtil.readTextFile(fileTmpl.getAbsolutePath(), 100, true); // help.html
String html = FileUtil.readTextFile(fileTmpl.getAbsolutePath(), 500, true);
if (html == null) if (html == null)
throw new IOException(fileTmpl.getAbsolutePath() + " open failed"); throw new IOException(fileTmpl.getAbsolutePath() + " open failed");
html = html.replace("$PLUGIN", pluginDir.getAbsolutePath()); // replace $HOME in path
String home = System.getProperty("user.home");
String pdir = pluginDir.getAbsolutePath();
if (pdir.startsWith(home)) {
pdir = "$HOME" + pdir.substring(home.length());
// only warn about username in help if we haven't replaced it with $HOME
html = html.replace("<p class=\"warn\" id=\"docroot\">", "<p id=\"docroot\">");
html = html.replace("<br><span class=\"emphasis\"><b>You should probably move it outside of the document root " +
"before you announce your eepsite as it may contain your username.</b></span>", "");
}
html = html.replace("$PLUGIN", pdir);
html = html.replace("$B32", b32); html = html.replace("$B32", b32);
html = html.replace("$B64", b64); html = html.replace("$B64", b64);
html = html.replace("$VERSION", VERSION);
String bdir = _context.getBaseDir().getAbsolutePath();
if (bdir.startsWith(home))
bdir = "$HOME" + bdir.substring(home.length());
html = html.replace("$I2P", bdir);
FileOutputStream os = new FileOutputStream(outFile); FileOutputStream os = new FileOutputStream(outFile);
os.write(html.getBytes("UTF-8")); os.write(html.getBytes("UTF-8"));
os.close(); os.close();
// index.html
String html2 = FileUtil.readTextFile(index_in.getAbsolutePath(), 50, true);
if (html2 == null)
throw new IOException(fileTmpl.getAbsolutePath() + " open failed");
html2 = html2.replace("$B32", b32);
FileOutputStream os2 = new FileOutputStream(index_out);
os2.write(html2.getBytes("UTF-8"));
os2.close();
Thread t = new I2PAppThread(new Launcher(), "ZzzOTHelp", true); Thread t = new I2PAppThread(new Launcher(), "ZzzOTHelp", true);
t.start(); t.start();
} catch (IOException ioe) { } catch (IOException ioe) {
@ -265,4 +412,103 @@ public class ZzzOTController {
UrlLauncher.main(new String[] { "http://127.0.0.1:7662/help.html" } ); UrlLauncher.main(new String[] { "http://127.0.0.1:7662/help.html" } );
} }
} }
/////// ClientApp methods
/** @since 0.12.0 */
public synchronized void startup() {
if (_mgr != null) {
// this is really ugly, but thru 0.9.16,
// stopping a ClientApp plugin with $PLUGIN in the args fails,
// and it tries to start a second one instead.
// Find the first one and stop it.
ClientApp z = _mgr.getRegisteredApp(NAME);
if (z != null) {
if (VersionComparator.comp(CoreVersion.VERSION, "0.9.17") < 0) {
ZzzOTController ctrlr = (ZzzOTController) z;
_log.warn("Got start when another zzzot running, stopping him instead");
ctrlr.shutdown(null);
} else {
_log.error("ZzzOT already running");
}
changeState(START_FAILED);
return;
}
}
if (_state != STOPPED && _state != INITIALIZED && _state != START_FAILED) {
_log.error("Start while state = " + _state);
return;
}
changeState(STARTING);
try {
start(_args);
changeState(RUNNING);
if (_mgr != null)
_mgr.register(this);
} catch (Exception e) {
changeState(START_FAILED, "Start failed", e);
}
}
/** @since 0.12.0 */
public synchronized void shutdown(String[] args) {
if (_state == STOPPED)
return;
changeState(STOPPING);
if (_mgr != null)
_mgr.unregister(this);
stop();
changeState(STOPPED);
}
/** @since 0.12.0 */
public ClientAppState getState() {
return _state;
}
/** @since 0.12.0 */
public String getName() {
return NAME;
}
/** @since 0.12.0 */
public String getDisplayName() {
return NAME;
}
/////// end ClientApp methods
/** @since 0.17.0 */
public static String getSiteName() {
return _sitename;
}
/** @since 0.17.0 */
public static String getVersion() {
return VERSION;
}
/** @since 0.17.0 */
public static String shouldShowFooter() {
return _showfooter;
}
/** @since 0.17.0 */
public static String footerText() {
return _footertext;
}
/** @since 0.12.0 */
private synchronized void changeState(ClientAppState state) {
_state = state;
if (_mgr != null)
_mgr.notify(this, state, null, null);
}
/** @since 0.12.0 */
private synchronized void changeState(ClientAppState state, String msg, Exception e) {
_state = state;
if (_mgr != null)
_mgr.notify(this, state, msg, e);
}
} }

View File

@ -4,10 +4,24 @@
<!-- precompiled servlets --> <!-- precompiled servlets -->
<welcome-file-list> <welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file> <welcome-file>index.jsp</welcome-file>
</welcome-file-list> </welcome-file-list>
<servlet-mapping>
<servlet-name>net.i2p.zzzot.index_jsp</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.zzzot.index_jsp</servlet-name>
<url-pattern>/index</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>net.i2p.zzzot.index_jsp</servlet-name>
<url-pattern>/index.html</url-pattern>
</servlet-mapping>
<servlet-mapping> <servlet-mapping>
<servlet-name>net.i2p.zzzot.announce_jsp</servlet-name> <servlet-name>net.i2p.zzzot.announce_jsp</servlet-name>
<url-pattern>/announce.php</url-pattern> <url-pattern>/announce.php</url-pattern>

View File

@ -1,4 +1,4 @@
<%@page import="java.util.ArrayList" %><%@page import="java.util.Collections" %><%@page import="java.util.List" %><%@page import="java.util.Map" %><%@page import="java.util.HashMap" %><%@page import="net.i2p.data.Base64" %><%@page import="net.i2p.data.Destination" %><%@page import="net.i2p.zzzot.*" %><%@page import="org.klomp.snark.bencode.BEncoder" %><% <%@page import="java.io.ByteArrayInputStream,java.util.ArrayList,java.util.Collections,java.util.List,java.util.Map,java.util.HashMap,java.util.concurrent.ConcurrentMap,net.i2p.data.Base64,net.i2p.data.Destination,net.i2p.zzzot.*,org.klomp.snark.bencode.BEncoder" %><%
/* /*
* Above one-liner is so there is no whitespace -> IllegalStateException * Above one-liner is so there is no whitespace -> IllegalStateException
@ -27,12 +27,15 @@
*/ */
// would be nice to make these configurable // would be nice to make these configurable
final int MAX_RESPONSES = 25; final int MAX_RESPONSES = 25;
final int INTERVAL = 27*60;
final boolean ALLOW_IP_MISMATCH = false; final boolean ALLOW_IP_MISMATCH = false;
final boolean ALLOW_COMPACT_RESPONSE = true; final boolean ALLOW_COMPACT_RESPONSE = true;
// so the chars will turn into bytes correctly // so the chars will turn into bytes correctly
request.setCharacterEncoding("ISO-8859-1"); request.setCharacterEncoding("ISO-8859-1");
// above doesn't work for the query string
// https://wiki.eclipse.org/Jetty/Howto/International_Characters
// we could also do ((org.eclipse.jetty.server.Request) request).setQueryEncoding("ISO-8859-1")
request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "ISO-8859-1");
java.io.OutputStream cout = response.getOutputStream(); java.io.OutputStream cout = response.getOutputStream();
response.setCharacterEncoding("ISO-8859-1"); response.setCharacterEncoding("ISO-8859-1");
response.setContentType("text/plain"); response.setContentType("text/plain");
@ -61,7 +64,8 @@
if (xff != null || xfs != null) { if (xff != null || xfs != null) {
fail = true; fail = true;
msg = "Non-I2P access denied"; msg = "Non-I2P access denied";
response.setStatus(403, msg); //response.setStatus(403, msg);
response.setStatus(403);
} }
if (info_hash == null && !fail) { if (info_hash == null && !fail) {
@ -69,6 +73,11 @@
msg = "no info hash"; msg = "no info hash";
} }
if (!fail && info_hash.length() != 20) {
fail = true;
msg = "bad info hash length " + info_hash.length();
}
if (ip == null && !fail) { if (ip == null && !fail) {
fail = true; fail = true;
msg = "no ip (dest)"; msg = "no ip (dest)";
@ -79,11 +88,22 @@
msg = "no peer id"; msg = "no peer id";
} }
if (!fail && peer_id.length() != 20) {
fail = true;
msg = "bad peer id length " + peer_id.length();
}
Torrents torrents = ZzzOTController.getTorrents();
if (torrents == null && !fail) {
fail = true;
msg = "tracker is down";
}
InfoHash ih = null; InfoHash ih = null;
if (!fail) { if (!fail) {
try { try {
ih = new InfoHash(info_hash); ih = torrents.createInfoHash(info_hash);
} catch (Exception e) { } catch (IllegalArgumentException e) {
fail = true; fail = true;
msg = "bad infohash " + e; msg = "bad infohash " + e;
} }
@ -94,7 +114,10 @@
try { try {
if (ip.endsWith(".i2p")) if (ip.endsWith(".i2p"))
ip = ip.substring(0, ip.length() - 4); ip = ip.substring(0, ip.length() - 4);
d = new Destination(ip); // from b64 string byte[] b = Base64.decode(ip);
if (b == null)
throw new Exception();
d = Destination.create(new ByteArrayInputStream(b)); // cache
} catch (Exception e) { } catch (Exception e) {
fail = true; fail = true;
msg = "bad dest " + e; msg = "bad dest " + e;
@ -104,8 +127,8 @@
PID pid = null; PID pid = null;
if (!fail) { if (!fail) {
try { try {
pid = new PID(peer_id); pid = torrents.createPID(peer_id);
} catch (Exception e) { } catch (IllegalArgumentException e) {
fail = true; fail = true;
msg = "bad peer id " + e; msg = "bad peer id " + e;
} }
@ -155,15 +178,14 @@
} catch (NumberFormatException nfe) {}; } catch (NumberFormatException nfe) {};
} }
Torrents torrents = ZzzOTController.getTorrents(); Map<String, Object> m = new HashMap<String, Object>(8);
Map<String, Object> m = new HashMap();
if (fail) { if (fail) {
m.put("failure reason", msg); m.put("failure reason", msg);
} else if ("stopped".equals(event)) { } else if ("stopped".equals(event)) {
Peers peers = torrents.get(ih); Peers peers = torrents.get(ih);
if (matchIP && peers != null) if (matchIP && peers != null)
peers.remove(pid); peers.remove(pid);
m.put("interval", Integer.valueOf(INTERVAL)); m.put("interval", torrents.getInterval());
} else { } else {
Peers peers = torrents.get(ih); Peers peers = torrents.get(ih);
if (peers == null) { if (peers == null) {
@ -176,7 +198,8 @@
// fixme same peer id, different dest // fixme same peer id, different dest
Peer p = peers.get(pid); Peer p = peers.get(pid);
if (p == null) { if (p == null) {
p = new Peer(pid.getData(), d); ConcurrentMap<String, String> destCache = ZzzOTController.getDestCache();
p = new Peer(pid.getData(), d, destCache);
// don't add if spoofed // don't add if spoofed
if (matchIP) { if (matchIP) {
Peer p2 = peers.putIfAbsent(pid, p); Peer p2 = peers.putIfAbsent(pid, p);
@ -188,7 +211,7 @@
if (matchIP) if (matchIP)
p.setLeft(left); p.setLeft(left);
m.put("interval", Integer.valueOf(INTERVAL)); m.put("interval", torrents.getInterval());
int size = peers.size(); int size = peers.size();
int seeds = peers.countSeeds(); int seeds = peers.countSeeds();
m.put("complete", Integer.valueOf(seeds)); m.put("complete", Integer.valueOf(seeds));
@ -197,11 +220,20 @@
// snark < 0.7.13 always wants a list // snark < 0.7.13 always wants a list
m.put("peers", java.util.Collections.EMPTY_LIST); m.put("peers", java.util.Collections.EMPTY_LIST);
} else { } else {
List<Peer> peerlist = new ArrayList(peers.values()); List<Peer> peerlist = new ArrayList<Peer>(peers.values());
peerlist.remove(p); // them peerlist.remove(p); // them
if (want < size - 1) { if (want < size - 1) {
Collections.shuffle(peerlist); if (size > 150) {
peerlist = peerlist.subList(0, want); // 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);
peerlist = peerlist.subList(0, want);
}
} }
if (compact) { if (compact) {
// old experimental way - list of hashes // old experimental way - list of hashes

View File

@ -1,10 +0,0 @@
<html>
<head>
<meta http-equiv="refresh" content="0;url=index.jsp" />
<title>zzzot</title>
</head>
<body>
<a href="index.jsp">Enter</a>
</body>
</html>

View File

@ -1,14 +1,65 @@
<%@page import="net.i2p.zzzot.ZzzOTController" %> <%@page import="net.i2p.zzzot.ZzzOTController,net.i2p.zzzot.Torrents" %>
<%@page trimDirectiveWhitespaces="true"%>
<!DOCTYPE HTML>
<html> <html>
<head> <head>
<title>ZzzOT</title> <meta charset="UTF-8">
</head><body style="background-color: #000; color: #c30; font-size: 400%;"> <noscript><meta http-equiv="refresh" content="300;url=."></noscript>
<p> <title><%=ZzzOTController.getSiteName()%> OPENTRACKER | STATS</title>
zzzot <link href="/tracker.css" rel="stylesheet" type="text/css">
<p> <link rel="icon" type="image/png" href="/favicon.png">
<table cellspacing="8"> </head>
<tr><td>Torrents:<td align="right"><%=ZzzOTController.getTorrents().size()%> <body id="stats">
<tr><td>Peers:<td align="right"><%=ZzzOTController.getTorrents().countPeers()%> <div id="container">
</table> <div id="panel">
<a href="/" title="Return to home page" alt="Return to home page"><span id="sitename"><%=ZzzOTController.getSiteName()%></span></a><hr>
<%
Torrents torrents = ZzzOTController.getTorrents();
if (torrents != null) {
%>
<p id="totals">
<b>Torrents:</b> <%=torrents.size()%><br>
<b>Peers:</b> <%=torrents.countPeers()%><br>
</p>
<%
} else {
%>
<p id="initializing"><b><i>Initializing OpenTracker&hellip;</i></b></p>
<%
}
%>
<%
String showfooter = ZzzOTController.shouldShowFooter();
if (showfooter == "true") {
%>
<span id="footer" class="version"><%=ZzzOTController.footerText()%></span>
<%
}
%>
</div>
</div>
<script type="text/javascript">
setInterval(function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/tracker/?' + new Date().getTime(), true);
xhr.responseType = "text";
xhr.onreadystatechange = function () {
if (xhr.readyState==4 && xhr.status==200) {
document.getElementById("stats").innerHTML = xhr.responseText;
}
}
xhr.send();
<%
if (torrents != null) {
%>
}, 60000);
<%
} else {
%>
}, 15000);
<%
}
%>
</script>
</body> </body>
</html> </html>

View File

@ -1,4 +1,4 @@
<%@page import="java.util.ArrayList" %><%@page import="java.util.List" %><%@page import="java.util.Map" %><%@page import="java.util.HashMap" %><%@page import="net.i2p.zzzot.*" %><%@page import="org.klomp.snark.bencode.BEncoder" %><% <%@page import="java.util.ArrayList,java.util.List,java.util.Map,java.util.HashMap,net.i2p.zzzot.*,org.klomp.snark.bencode.BEncoder" %><%
/* /*
* Above one-liner is so there is no whitespace -> IllegalStateException * Above one-liner is so there is no whitespace -> IllegalStateException
@ -27,6 +27,10 @@
*/ */
// so the chars will turn into bytes correctly // so the chars will turn into bytes correctly
request.setCharacterEncoding("ISO-8859-1"); request.setCharacterEncoding("ISO-8859-1");
// above doesn't work for the query string
// https://wiki.eclipse.org/Jetty/Howto/International_Characters
// we could also do ((org.eclipse.jetty.server.Request) request).setQueryEncoding("ISO-8859-1")
request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "ISO-8859-1");
java.io.OutputStream cout = response.getOutputStream(); java.io.OutputStream cout = response.getOutputStream();
response.setCharacterEncoding("ISO-8859-1"); response.setCharacterEncoding("ISO-8859-1");
response.setContentType("text/plain"); response.setContentType("text/plain");
@ -41,49 +45,60 @@
if (xff != null || xfs != null) { if (xff != null || xfs != null) {
fail = true; fail = true;
msg = "Non-I2P access denied"; msg = "Non-I2P access denied";
response.setStatus(403, msg); //response.setStatus(403, msg);
response.setStatus(403);
} }
boolean all = info_hash == null; boolean all = info_hash == null;
Torrents torrents = ZzzOTController.getTorrents();
if (torrents == null && !fail) {
fail = true;
msg = "tracker is down";
}
InfoHash ih = null; InfoHash ih = null;
if ((!all) && !fail) { if ((!all) && !fail) {
try { try {
ih = new InfoHash(info_hash); ih = torrents.createInfoHash(info_hash);
} catch (Exception e) { } catch (Exception e) {
fail = true; fail = true;
msg = "bad infohash " + e; msg = "bad infohash " + e;
} }
} }
Torrents torrents = ZzzOTController.getTorrents();
// build 3-level dictionary // build 3-level dictionary
Map<String, Object> m = new HashMap(); Map<String, Object> m = new HashMap<String, Object>(4);
if (fail) { if (fail) {
m.put("failure reason", msg); m.put("failure reason", msg);
} else { } else {
List<InfoHash> ihList = new ArrayList(); List<InfoHash> ihList = new ArrayList<InfoHash>();
if (all) if (all)
ihList.addAll(torrents.keySet()); ihList.addAll(torrents.keySet());
else else
ihList.add(ih); ihList.add(ih);
Map<String, Map> files = new HashMap(); // requires I2P 0.9.30-8
Map<byte[], Map> files = new HashMap<byte[], Map>();
for (InfoHash ihash : ihList) { for (InfoHash ihash : ihList) {
Peers peers = torrents.get(ihash); Peers peers = torrents.get(ihash);
if (peers == null) if (peers == null)
continue; continue;
Map<String, Object> dict = new HashMap(); Map<String, Object> dict = new HashMap<String, Object>();
int size = peers.size(); int size = peers.size();
int seeds = peers.countSeeds(); int seeds = peers.countSeeds();
dict.put("complete", Integer.valueOf(seeds)); dict.put("complete", Integer.valueOf(seeds));
dict.put("incomplete", Integer.valueOf(size - seeds)); dict.put("incomplete", Integer.valueOf(size - seeds));
dict.put("downloaded", Integer.valueOf(0)); dict.put("downloaded", Integer.valueOf(0));
files.put(new String(ihash.getData(), "ISO-8859-1"), dict); files.put(ihash.getData(), dict);
} }
m.put("files", files); m.put("files", files);
} }
BEncoder.bencode(m, cout); try {
BEncoder.bencode(m, cout);
} catch (IllegalArgumentException iae) {
// before I2P 0.9.30-8
// just let it truncate, wasn't valid before anyway
}
/* /*
* Remove the newline on the last line or * Remove the newline on the last line or

View File

@ -1,4 +1,4 @@
<%@page import="net.i2p.crypto.SHA256Generator" %><%@page import="net.i2p.data.Base32" %><%@page import="net.i2p.data.Base64" %><%@page import="net.i2p.data.DataHelper" %><%@page import="net.i2p.zzzot.*" %><% <%@page import="net.i2p.crypto.SHA256Generator,net.i2p.data.Base32,net.i2p.data.Base64,net.i2p.data.DataHelper,net.i2p.zzzot.*" %><%
/* /*
* Copyright 2010 zzz (zzz@mail.i2p) * Copyright 2010 zzz (zzz@mail.i2p)
@ -40,7 +40,8 @@
if (xff != null || xfs != null) { if (xff != null || xfs != null) {
String msg = "Non-I2P access denied"; String msg = "Non-I2P access denied";
response.setStatus(403, msg); //response.setStatus(403, msg);
response.setStatus(403);
out.println(msg); out.println(msg);
} else if (req == null) { } else if (req == null) {
// probe // probe
@ -62,6 +63,11 @@
} else if (req.startsWith("locate dG9ycmVud")) { // locate b64(torrent) } else if (req.startsWith("locate dG9ycmVud")) { // locate b64(torrent)
// all the peers // all the peers
Torrents torrents = ZzzOTController.getTorrents(); Torrents torrents = ZzzOTController.getTorrents();
if (torrents == null) {
//response.setStatus(503, "Down");
response.setStatus(503);
return;
}
for (InfoHash ihash : torrents.keySet()) { for (InfoHash ihash : torrents.keySet()) {
Peers peers = torrents.get(ihash); Peers peers = torrents.get(ihash);
if (peers == null) if (peers == null)
@ -88,7 +94,8 @@
} }
} else { } else {
// error code // error code
response.setStatus(406, "Bad request"); //response.setStatus(406, "Bad request");
response.setStatus(406);
out.println("SC_NOT_ACCEPTABLE"); out.println("SC_NOT_ACCEPTABLE");
} }