Compare commits
78 Commits
zzzot-0.14
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
f88cdd730b | ||
![]() |
39a7f6d7df | ||
![]() |
808aeec562 | ||
![]() |
9a8ac980d3 | ||
![]() |
728c646dac | ||
![]() |
5c08658360 | ||
![]() |
02328bd5d4 | ||
![]() |
6bc7821f1b | ||
![]() |
cf48fcd901 | ||
![]() |
8ff48800d6 | ||
![]() |
1a90be6701 | ||
![]() |
a4729aba16 | ||
![]() |
cd2441ca4a | ||
![]() |
d9d3283b3e | ||
![]() |
1acaef9514 | ||
![]() |
0b6d22da3b | ||
![]() |
38c47f9993 | ||
![]() |
6e7406567b | ||
![]() |
b970d198ca | ||
![]() |
241b95bc65 | ||
![]() |
ee39fa92ae | ||
![]() |
26ad8bfd08 | ||
![]() |
8d82d13d1c | ||
![]() |
d9b4a5e1c4 | ||
![]() |
886a05730c | ||
![]() |
65dd1161ed | ||
![]() |
0edade465c | ||
![]() |
1e94a95a7a | ||
![]() |
b7009e3a20 | ||
![]() |
9eece7ab8b | ||
![]() |
f5ed7ec874 | ||
![]() |
071a36c348 | ||
![]() |
d07780615c | ||
![]() |
e157028db4 | ||
![]() |
8ff2c93b0a | ||
![]() |
20245a3e8e | ||
![]() |
edbb803095 | ||
![]() |
b2432833e3 | ||
![]() |
6073e61ba4 | ||
![]() |
e1e29a953b | ||
![]() |
059f1cf6e7 | ||
![]() |
32062dff20 | ||
![]() |
7a6c0bcccd | ||
![]() |
1853bbfd0e | ||
![]() |
5828c358f6 | ||
![]() |
017d8a61c3 | ||
![]() |
cc640088c4 | ||
![]() |
2016546fc1 | ||
![]() |
9ed1a1ce0c | ||
![]() |
8fd17dd938 | ||
![]() |
51e4184ea8 | ||
![]() |
df683fcf5c | ||
![]() |
ca66c075bf | ||
![]() |
f35827abcb | ||
![]() |
290b7e9ba8 | ||
![]() |
9a988e5c6d | ||
5d2343fe71 | |||
94ccfa25e6 | |||
36cee7777a | |||
2db3525fbb | |||
ea3fa57b04 | |||
66ba3f3e76 | |||
66900c2fbc | |||
96fa6adfad | |||
1227c727d0 | |||
d29211301e | |||
1b1c5a1c6b | |||
d72c0c6d61 | |||
ae250314b0 | |||
175515f629 | |||
c2482d68c8 | |||
a39186cfff | |||
22cf9e06ff | |||
758c0865dc | |||
ab14737fd1 | |||
dd0d7ba6c9 | |||
d3ccdb5970 | |||
404ec7d720 |
66
.github/workflows/sync.yaml
vendored
Normal file
66
.github/workflows/sync.yaml
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
# GitHub Actions workflow file to sync an external repository to this GitHub mirror.
|
||||
# This file was automatically generated by go-github-sync.
|
||||
#
|
||||
# The workflow does the following:
|
||||
# - Runs on a scheduled basis (and can also be triggered manually)
|
||||
# - Clones the GitHub mirror repository
|
||||
# - Fetches changes from the primary external repository
|
||||
# - Applies those changes to the mirror repository
|
||||
# - Pushes the updated content back to the GitHub mirror
|
||||
#
|
||||
# Authentication is handled by the GITHUB_TOKEN secret provided by GitHub Actions.
|
||||
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Validate Github Actions Environment
|
||||
run: if [ "$GITHUB_ACTIONS" != "true" ]; then echo 'This script must be run in a GitHub Actions environment.'; exit 1; fi
|
||||
- name: Checkout GitHub Mirror
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Configure Git
|
||||
run: |-
|
||||
git config user.name 'GitHub Actions'
|
||||
git config user.email 'actions@github.com'
|
||||
- env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: Sync Primary Repository
|
||||
run: |-
|
||||
# Add the primary repository as a remote
|
||||
git remote add primary https://i2pgit.org/I2P_Developers/i2p.plugins.zzzot.git
|
||||
|
||||
# Fetch the latest changes from the primary repository
|
||||
git fetch primary
|
||||
|
||||
# Check if the primary branch exists in the primary repository
|
||||
if git ls-remote --heads primary master | grep -q master; then
|
||||
echo "Primary branch master found in primary repository"
|
||||
else
|
||||
echo "Error: Primary branch master not found in primary repository"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if we're already on the mirror branch
|
||||
if git rev-parse --verify --quiet master; then
|
||||
git checkout master
|
||||
else
|
||||
# Create the mirror branch if it doesn't exist
|
||||
git checkout -b master
|
||||
fi
|
||||
|
||||
|
||||
# Force-apply all changes from primary, overriding any conflicts
|
||||
echo "Performing force sync from primary/master to master"
|
||||
git reset --hard primary/master
|
||||
|
||||
|
||||
# Push changes back to the mirror repository
|
||||
git push origin master
|
||||
name: Sync Primary Repository to GitHub Mirror
|
||||
"on":
|
||||
push: {}
|
||||
schedule:
|
||||
- cron: 0 * * * *
|
||||
workflow_dispatch: {}
|
166
CHANGES.txt
166
CHANGES.txt
@@ -1,77 +1,113 @@
|
||||
0.14.0
|
||||
2015-05-28
|
||||
Remove use of deprecated SimpleScheduler
|
||||
Remove static structures
|
||||
Use RandomIterator for efficiency
|
||||
2025-08-10 [0.20.0] (Requires I2P 2.9.0 or higher)
|
||||
- Support UDP announces
|
||||
- Fix dup ids in jetty.xml, existing installs must fix manually,
|
||||
s/<Ref id=/<Ref refid=/g
|
||||
- Add interval to stats page
|
||||
- Add stats to I2P stats subsystem
|
||||
- Show announce URLs on stats page
|
||||
- Remove ElGamal support
|
||||
- Remove support for non-compact announce replies
|
||||
- Reduce memory usage
|
||||
- Remove seedless support
|
||||
|
||||
0.13.0
|
||||
2014-11-29
|
||||
Fix NPE on missing announce parameters
|
||||
Increase startup delay
|
||||
2024-04-07 [0.19.0]
|
||||
- Disable full scrape by default
|
||||
- Handle BiglyBT scrape URLs
|
||||
|
||||
0.12.0
|
||||
2014-11-13
|
||||
Fix parameter decoding for scrape also
|
||||
Add caching for info hashes and peer ids
|
||||
Stop cleaner when plugin stops
|
||||
Move to the ClientApp interface, remove all static refs
|
||||
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)
|
||||
2020-08-30 [0.18.0]
|
||||
- Enable both encryption types
|
||||
- Disable pack200
|
||||
|
||||
0.11.0
|
||||
2014-11-11
|
||||
Critical fix for announce parameter decoding, triggered by recent Jetty versions
|
||||
Change request queueing in jetty.xml (new installs only)
|
||||
SU3 plugin file format
|
||||
2019-11-19 [0.17.0]
|
||||
- Add more configuration, customization, and registration info to help page
|
||||
- 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.10.0
|
||||
2013-04-14
|
||||
Updates and migration for Jetty 7 (I2P 0.9.6)
|
||||
2017-05-21 [0.16.0]
|
||||
- Fix scrape response (requires I2P 0.9.30-8) (ticket #1994)
|
||||
|
||||
0.9.0
|
||||
2013-01-25
|
||||
Add cache-control directives
|
||||
Set max Jetty to 6.99999
|
||||
2017-03-11 [0.15.0]
|
||||
- Increase default limits, set I2CP tag options
|
||||
- Update eepsite configuration for Jetty 9 (I2P 0.9.30)
|
||||
|
||||
0.8
|
||||
2012-03-10
|
||||
fix comment in jetty.xml
|
||||
2015-05-28 [0.14.0]
|
||||
- Remove use of deprecated SimpleScheduler (ticket #1523)
|
||||
- Remove static structures
|
||||
- Use RandomIterator for efficiency
|
||||
|
||||
0.7
|
||||
2012-03-10
|
||||
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
|
||||
2014-11-29 [0.13.0]
|
||||
- Fix NPE on missing announce parameters
|
||||
- Increase startup delay
|
||||
|
||||
0.5
|
||||
2010-07-11
|
||||
Final compact response format
|
||||
2014-11-13 [0.12.0]
|
||||
- Fix parameter decoding for scrape also
|
||||
- Add caching for info hashes and peer ids
|
||||
- Stop cleaner when plugin stops
|
||||
- Move to the ClientApp interface, remove all static refs
|
||||
- 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.4
|
||||
2010-07-09
|
||||
Compact request/response support - may not be final format
|
||||
Fix NPE if no ip parameter
|
||||
2014-11-11 [0.11.0]
|
||||
- Critical fix for announce parameter decoding, triggered by recent Jetty versions
|
||||
- Change request queueing in jetty.xml (new installs only)
|
||||
- SU3 plugin file format
|
||||
|
||||
0.3
|
||||
2010-04-13
|
||||
Verify dest
|
||||
Add xfs check
|
||||
2013-04-14 [0.10.0]
|
||||
- Updates and migration for Jetty 7 (I2P 0.9.6)
|
||||
|
||||
0.2
|
||||
2010-03-23
|
||||
Cache b64 dest strings
|
||||
Help typo fix (thx duck)
|
||||
Lots of seedless fixes
|
||||
Build cleanups
|
||||
2013-01-25 [0.9.0]
|
||||
- Add cache-control directives
|
||||
- Set max Jetty to 6.99999
|
||||
|
||||
0.1
|
||||
2010-03-23
|
||||
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
|
||||
|
71
README.txt
71
README.txt
@@ -1,42 +1,51 @@
|
||||
ZzzOT I2P Open Tracker Plugin
|
||||
-----------------------------
|
||||
|
||||
This is a very simple in-memory open tracker, wrapped into an I2P plugin.
|
||||
Plugin su3 binaries are available at http://stats.i2p/i2p/plugins/
|
||||
|
||||
The plugin starts a new http serer tunnel, eepsite, and Jetty server running at port 7662.
|
||||
The tracker status is available at http://127.0.0.1:7662/tracker/ .
|
||||
If other files are desired on the eepsite, they can be added at eepsite/docroot .
|
||||
The plugin starts a new http server tunnel, eepsite, and Jetty server running at
|
||||
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
|
||||
|
||||
The open tracker code and jsps were written from scratch, but depend on some code
|
||||
in i2psnark.jar from the I2P installation for bencoding, and of course
|
||||
on other i2p libraries.
|
||||
See the license files in I2P for i2p and i2psnark licenses.
|
||||
There is also some code modified from Jetty 5.1.15.
|
||||
See LICENSES.txt for the zzzot and Jetty licenses.
|
||||
The open tracker code and jsps were written from scratch, but depend on some
|
||||
code in i2psnark.jar from the I2P installation for bencoding, and of course on
|
||||
other i2p libraries. See the license files in I2P for i2p and i2psnark licenses.
|
||||
There is also some code modified from Jetty. See LICENSES.txt for the
|
||||
zzzot and Jetty licenses.
|
||||
|
||||
I2P source must be installed and built in ../i2p.i2p to compile this package.
|
||||
|
||||
Sure, as a standalone program in its own JVM with Jetty, this would be a pig -
|
||||
you should use the C opentracker instead. But since you're already running
|
||||
the JVM and Jetty, running this in the same JVM probably doesn't hog to much more memory.
|
||||
As of release 0.19.0:
|
||||
- Full scrape is disabled by default
|
||||
|
||||
As of release 0.20.0:
|
||||
- I2P 2.9.0 or higher required to build and run
|
||||
- UDP announces are supported, see http://i2p-projekt.i2p/spec/proposals/160
|
||||
- Non-compact responses are no longer supported
|
||||
- Seedless support is removed
|
||||
- Memory usage greatly reduced
|
||||
|
||||
Valid announce URLs:
|
||||
/a
|
||||
/announce
|
||||
/announce.jsp
|
||||
/announce.php
|
||||
/tracker/a
|
||||
/tracker/announce
|
||||
/tracker/announce.jsp
|
||||
/tracker/announce.php
|
||||
/a
|
||||
/announce
|
||||
/announce.jsp
|
||||
/announce.php
|
||||
/tracker/a
|
||||
/tracker/announce
|
||||
/tracker/announce.jsp
|
||||
/tracker/announce.php
|
||||
|
||||
UDP announce URLs (default port is 6969):
|
||||
udp://yourb32string.b32.i2p:6969/
|
||||
|
||||
Valid scrape URLs:
|
||||
/scrape
|
||||
/scrape.jsp
|
||||
/scrape.php
|
||||
/tracker/scrape
|
||||
/tracker/scrape.jsp
|
||||
/tracker/scrape.php
|
||||
/scrape
|
||||
/scrape.jsp
|
||||
/scrape.php
|
||||
/tracker/scrape
|
||||
/tracker/scrape.jsp
|
||||
/tracker/scrape.php
|
||||
|
||||
The tracker also responds to seedless queries at
|
||||
/Seedless/index.jsp
|
||||
|
||||
You may use the rest of the eepsite for other purposes, for example you
|
||||
may place torrent files in eepsite/docroot/torrents.
|
||||
You may use the rest of the eepsite for other purposes; for example you
|
||||
may place torrent files in: eepsite/docroot/torrents/
|
||||
|
13
TODO.txt
13
TODO.txt
@@ -1,12 +1,9 @@
|
||||
Configuration file:
|
||||
- interval
|
||||
- clean time
|
||||
- max peers in response
|
||||
- disable full scrapes
|
||||
- disable all scrapes
|
||||
- disable seedless
|
||||
|
||||
Stop the cleaner
|
||||
Remove seedless
|
||||
|
||||
Throttles:
|
||||
- full scrapes
|
||||
@@ -17,3 +14,11 @@ Bans:
|
||||
|
||||
Verifier:
|
||||
- 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
|
||||
|
36
build.xml
36
build.xml
@@ -1,13 +1,14 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<project basedir="." default="all" name="zzzot">
|
||||
|
||||
<property file="override.properties"/>
|
||||
|
||||
<target name="all" depends="clean,plugin" />
|
||||
|
||||
<target name="war" >
|
||||
<ant dir="src" target="build" />
|
||||
</target>
|
||||
|
||||
<target name="plugin" depends="war">
|
||||
<target name="plugin" depends="war">
|
||||
<delete>
|
||||
<!-- in installer but not update -->
|
||||
<fileset dir="plugin/" includes="i2ptunnel.config zzzot.config eepsite/docroot/index.html eepsite/docroot/robots.txt" />
|
||||
@@ -15,12 +16,17 @@
|
||||
<delete dir="plugin/eepsite/docroot/torrents/" />
|
||||
<!-- get version number -->
|
||||
<buildnumber file="scripts/build.number" />
|
||||
<property name="release.number" value="0.14.0" />
|
||||
<!-- NOTE: Change VERSION in ZzzOTController when you change this -->
|
||||
<property name="release.number" value="0.20.0" />
|
||||
|
||||
<!-- make the update xpi2p -->
|
||||
<!-- this contains everything except i2ptunnel.config -->
|
||||
<copy file="LICENSE.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" />
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
||||
<arg value="update-only=true" />
|
||||
@@ -28,14 +34,17 @@
|
||||
<exec executable="echo" osfamily="unix" failonerror="true" output="plugin/plugin.config" append="true">
|
||||
<arg value="version=${release.number}-b${build.number}" />
|
||||
</exec>
|
||||
<mkdir dir="plugin/lib/" />
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="-g" />
|
||||
<arg value="plugin/lib/zzzot.jar.pack" />
|
||||
<arg value="-r" />
|
||||
<arg value="plugin/lib/zzzot.jar" />
|
||||
<arg value="src/build/zzzot.jar" />
|
||||
</exec>
|
||||
<mkdir dir="plugin/eepsite/webapps/" />
|
||||
<mkdir dir="plugin/eepsite/logs/" />
|
||||
<exec executable="pack200" failonerror="true">
|
||||
<arg value="-g" />
|
||||
<arg value="plugin/eepsite/webapps/tracker.war.pack" />
|
||||
<arg value="-r" />
|
||||
<arg value="plugin/eepsite/webapps/tracker.war" />
|
||||
<arg value="src/build/tracker.war.jar" />
|
||||
</exec>
|
||||
<input message="Enter su3 signing key password:" addproperty="release.password.su3" />
|
||||
@@ -56,7 +65,6 @@
|
||||
<!-- 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/index.html" todir="plugin/eepsite/docroot/" 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">
|
||||
@@ -71,13 +79,21 @@
|
||||
|
||||
<target name="clean" >
|
||||
<ant dir="src" target="clean" />
|
||||
<defaultexcludes remove="**/*~"/>
|
||||
<delete>
|
||||
<fileset dir="." includes="*/*.~ **/*.*~ */**/*.*~ *.*~" />
|
||||
</delete>
|
||||
<delete file="plugin/i2ptunnel.config" />
|
||||
<delete file="plugin/plugin.config" />
|
||||
<delete file="plugin/zzzot.config" />
|
||||
<delete file="plugin/eepsite/docroot/index.html" />
|
||||
<delete file="plugin/eepsite/docroot/robots.txt" />
|
||||
<delete file="plugin/lib/zzzot.jar.pack" />
|
||||
<delete file="plugin/eepsite/webapps/tracker.war.pack" />
|
||||
<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/README.txt" />
|
||||
<delete file="zzzot.xpi2p" />
|
||||
|
@@ -2,7 +2,7 @@ clientApp.0.main=net.i2p.zzzot.ZzzOTController
|
||||
clientApp.0.name=ZzzOT
|
||||
clientApp.0.args=-d $PLUGIN start
|
||||
clientApp.0.stopargs=-d $PLUGIN stop
|
||||
clientApp.0.delay=40
|
||||
clientApp.0.delay=0
|
||||
clientApp.0.startOnLoad=true
|
||||
# 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
|
||||
|
@@ -16,7 +16,7 @@ to serve static html files and images.
|
||||
<Arg>max-age=3600,public</Arg>
|
||||
</Call>
|
||||
<Call name="addServlet">
|
||||
<Arg>org.eclipse.jetty.servlet.DefaultServlet</Arg>
|
||||
<Arg>net.i2p.servlet.I2PDefaultServlet</Arg>
|
||||
<Arg>/</Arg>
|
||||
</Call>
|
||||
</Configure>
|
||||
|
@@ -1,57 +1,426 @@
|
||||
<html><head><title>ZzzOT Plugin Help</title></head>
|
||||
<body style="background-color: #ddd; color: #a30;">
|
||||
<h2>Welcome to the ZzzOT I2P Plugin!</h2>
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
|
||||
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,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">
|
||||
<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>.
|
||||
This link is also at the top of your router console when ZzzOT is running.
|
||||
@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) #111;
|
||||
background-blend-mode: overlay, normal;
|
||||
background-size: 100% 100%, 100% 100%;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
}
|
||||
|
||||
<p>Report bugs or add comments on
|
||||
<a href="http://zzz.i2p//forums/16">the plugin forum on zzz.i2p</a>.
|
||||
::selection,
|
||||
::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #431;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
<p><b>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.</b>
|
||||
code::-moz-selection,
|
||||
#b64::-moz-selection,
|
||||
code::selection,
|
||||
#b64::selection {
|
||||
background: #150;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
<h3>Eepsite Key and Helpful Hints for I2P</h3>
|
||||
p,
|
||||
li,
|
||||
code {
|
||||
text-shadow: 0 1px 1px #000;
|
||||
}
|
||||
|
||||
<p>Your Base 32 address is <a href="http://$B32/">$B32</a>.
|
||||
Others may access your eepsite using this address, even if you do not publish a hostname.
|
||||
<p>Once you decide on a host name, you may
|
||||
<a href="http://127.0.0.1:7657/susidns/addressbook.jsp?book=private&destination=$B64">add the key to your local addressbook here</a>.
|
||||
<p>Your Base 64 key is: <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>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>.
|
||||
#container {
|
||||
padding: 5% 15%;
|
||||
}
|
||||
|
||||
</body></html>
|
||||
#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,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) 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://i2pforum.i2p/" target="_blank">i2pforum.i2p</a>. New releases will be announced 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>) at <a href="http://git.idk.i2p/I2P_Developers/i2p.plugins.zzzot" target="_blank">our Gitea site</a> and 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 & 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 most important configuration for ZzzOT is the <code>interval</code>, 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 <code>interval</code>. To do so, edit the configuration file, then stop and restart the plugin.</li>
|
||||
<li>UDP may also be enabled or disabled, and the connection lifetime changed, in the configuration file.</li>
|
||||
<li>All conguration changes require the plugin to be stoped and restarted.</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 & 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>UDP announce URL, if enabled (enabled by default):</p>
|
||||
<ul class="urls">
|
||||
<li><a href="udp://$B32:6969/">udp://$B32:6969/</a></li>
|
||||
</ul>
|
||||
<p>Supported scrape URLs (note that full scrapes are disabled by default):</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>
|
||||
|
25
plugin/templates/index.html
Normal file
25
plugin/templates/index.html
Normal 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>
|
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
|
||||
|
||||
<!-- ========================================================================= -->
|
||||
<!-- This file configures the Jetty server. -->
|
||||
@@ -15,10 +15,10 @@
|
||||
<!-- * threads: Raise maximumPoolSize in the ThreadPool section -->
|
||||
<!-- if you have a high-traffic site and get a lot of warnings. -->
|
||||
<!-- -->
|
||||
<!-- I2P uses Jetty 7. If you need web server features not found -->
|
||||
<!-- in Jetty 7, you may install and run Jetty 7 or 8 in a different JVM -->
|
||||
<!-- I2P uses Jetty 9. If you need web server features not found -->
|
||||
<!-- 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 -->
|
||||
<!-- 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 . -->
|
||||
<!-- -->
|
||||
<!-- Jetty now uses the I2P logging system rather than wrapper.log. -->
|
||||
@@ -42,18 +42,18 @@
|
||||
<!-- =========================================================== -->
|
||||
<!-- Server Thread Pool -->
|
||||
<!-- =========================================================== -->
|
||||
<Set name="ThreadPool">
|
||||
|
||||
<!-- PICK ONE -->
|
||||
<Arg>
|
||||
|
||||
<!--
|
||||
Recommended.
|
||||
Requests above the maxThreads + queue_size will be rejected and logged.
|
||||
ref:
|
||||
https://wiki.eclipse.org/Jetty/Howto/High_Load
|
||||
http://trac.i2p2.i2p/ticket/1395
|
||||
-->
|
||||
<New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
|
||||
<Arg type="int">20</Arg> <!-- maxThreads, overridden below -->
|
||||
<Arg type="int">3</Arg> <!-- minThreads, overridden below -->
|
||||
<Arg type="int">60000</Arg> <!-- maxIdleTimeMs, overridden below -->
|
||||
<Arg>
|
||||
<New class="java.util.concurrent.LinkedBlockingQueue">
|
||||
<Arg type="int">50</Arg>
|
||||
@@ -61,43 +61,11 @@
|
||||
</Arg>
|
||||
<Set name="minThreads">4</Set>
|
||||
<Set name="maxThreads">20</Set>
|
||||
<Set name="maxIdleTimeMs">60000</Set>
|
||||
<Set name="idleTimeout">60000</Set>
|
||||
<Set name="daemon">true</Set>
|
||||
<Set name="name">Zzzot Jetty</Set>
|
||||
</New>
|
||||
|
||||
<!-- Optional Java 5 bounded threadpool with job queue
|
||||
Requests above the max will be rejected and logged.
|
||||
High-traffic sites should increase maximumPoolSize.
|
||||
|
||||
Args are:
|
||||
corePoolSize (should be at least 3)
|
||||
maximumPoolSize
|
||||
keepAliveTime (milliseconds)
|
||||
timeout (TimeUnit)
|
||||
queue (BlockingQueue)
|
||||
|
||||
Not recommended.
|
||||
ref:
|
||||
http://trac.i2p2.i2p/ticket/1395
|
||||
http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html
|
||||
-->
|
||||
<!--
|
||||
<New class="org.eclipse.jetty.util.thread.ExecutorThreadPool">
|
||||
<Arg type="int">3</Arg>
|
||||
<Arg type="int">20</Arg>
|
||||
<Arg type="long">60000</Arg>
|
||||
<Arg>
|
||||
<Call class="java.util.concurrent.TimeUnit" name="valueOf" >
|
||||
<Arg>MILLISECONDS</Arg>
|
||||
</Call>
|
||||
</Arg>
|
||||
<Arg>
|
||||
<New class="java.util.concurrent.SynchronousQueue" />
|
||||
</Arg>
|
||||
</New>
|
||||
-->
|
||||
</Set>
|
||||
</Arg>
|
||||
|
||||
|
||||
|
||||
@@ -113,41 +81,34 @@
|
||||
SocketConnector below.
|
||||
Do not use for gij or JamVM - comment this out, and uncomment the
|
||||
SocketConnector below.
|
||||
-->
|
||||
-->
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.nio.SelectChannelConnector">
|
||||
<New class="org.eclipse.jetty.server.ServerConnector">
|
||||
<Arg><Ref refid="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="port">7662</Set>
|
||||
<Set name="maxIdleTime">60000</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>
|
||||
<Set name="useDirectBuffers">false</Set>
|
||||
<Set name="idleTimeout">600000</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
|
||||
<!-- Recommended to use this connector on Java 5, as
|
||||
Jetty 6 and Java 5 NIO don't play well together.
|
||||
-->
|
||||
<!--
|
||||
<Call name="addConnector">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.server.bio.SocketConnector">
|
||||
<Set name="host">127.0.0.1</Set>
|
||||
<Set name="port">7662</Set>
|
||||
<Set name="maxIdleTime">60000</Set>
|
||||
<Set name="Acceptors">1</Set>
|
||||
<Set name="statsOn">false</Set>
|
||||
<Set name="confidentialPort">8443</Set>
|
||||
</New>
|
||||
</Arg>
|
||||
</Call>
|
||||
-->
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Set up global session ID manager -->
|
||||
<!-- =========================================================== -->
|
||||
@@ -160,12 +121,12 @@
|
||||
-->
|
||||
|
||||
<!-- =========================================================== -->
|
||||
<!-- Set handler Collection Structure -->
|
||||
<!-- -->
|
||||
<!-- We use a RewriteHandler at the top level so that we may -->
|
||||
<!-- convert any top-level URLs for the tracker down into the -->
|
||||
<!-- tracker servlet, without interfering with anything else -->
|
||||
<!-- that is hosted on the same eepsite. -->
|
||||
<!-- Set handler Collection Structure -->
|
||||
<!-- -->
|
||||
<!-- We use a RewriteHandler at the top level so that we may -->
|
||||
<!-- convert any top-level URLs for the tracker down into the -->
|
||||
<!-- tracker servlet, without interfering with anything else -->
|
||||
<!-- that is hosted on the same eepsite. -->
|
||||
<!-- =========================================================== -->
|
||||
<Set name="handler">
|
||||
<New id="Rewrite" class="org.eclipse.jetty.rewrite.handler.RewriteHandler">
|
||||
@@ -174,6 +135,18 @@
|
||||
<Set name="originalPathAttribute">requestedPath</Set>
|
||||
<Set name="rules">
|
||||
<Array type="org.eclipse.jetty.rewrite.handler.Rule">
|
||||
<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">/help/</Set>
|
||||
<Set name="replacement">/help.html</Set>
|
||||
</New>
|
||||
</Item>
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
||||
<Set name="pattern">/a</Set>
|
||||
@@ -216,6 +189,13 @@
|
||||
<Set name="replacement">/tracker/scrape.jsp</Set>
|
||||
</New>
|
||||
</Item>
|
||||
<!-- BiglyBT -->
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
||||
<Set name="pattern">/a/scrape</Set>
|
||||
<Set name="replacement">/tracker/scrape.jsp</Set>
|
||||
</New>
|
||||
</Item>
|
||||
<Item>
|
||||
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
|
||||
<Set name="pattern">/Seedless</Set>
|
||||
@@ -277,7 +257,7 @@
|
||||
<Arg>
|
||||
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
|
||||
<Set name="contexts">
|
||||
<Ref id="Contexts" />
|
||||
<Ref refid="Contexts" />
|
||||
</Set>
|
||||
<Call name="setContextAttribute">
|
||||
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
|
||||
@@ -298,10 +278,10 @@
|
||||
<!-- in the $JETTY_HOME/contexts directory -->
|
||||
<!-- -->
|
||||
<!-- =========================================================== -->
|
||||
<Ref id="DeploymentManager">
|
||||
<Ref refid="DeploymentManager">
|
||||
<Call name="addAppProvider">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.deploy.providers.ContextProvider">
|
||||
<New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
|
||||
<Set name="monitoredDirName">$PLUGIN/contexts</Set>
|
||||
<Set name="scanInterval">120</Set>
|
||||
</New>
|
||||
@@ -322,7 +302,7 @@
|
||||
<!-- Normally only one type of deployer need be used. -->
|
||||
<!-- -->
|
||||
<!-- =========================================================== -->
|
||||
<Ref id="DeploymentManager">
|
||||
<Ref refid="DeploymentManager">
|
||||
<Call id="webappprovider" name="addAppProvider">
|
||||
<Arg>
|
||||
<New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
|
||||
@@ -363,7 +343,7 @@
|
||||
<!-- contexts configuration (see $(jetty.home)/contexts/test.xml -->
|
||||
<!-- for an example). -->
|
||||
<!-- =========================================================== -->
|
||||
<Ref id="RequestLog">
|
||||
<Ref refid="RequestLog">
|
||||
<Set name="requestLog">
|
||||
<New id="RequestLogImpl" class="net.i2p.jetty.I2PRequestLog">
|
||||
<Set name="filename">$PLUGIN/eepsite/logs/yyyy_mm_dd.request.log</Set>
|
||||
@@ -381,8 +361,6 @@
|
||||
<!-- extra options -->
|
||||
<!-- =========================================================== -->
|
||||
<Set name="stopAtShutdown">true</Set>
|
||||
<Set name="sendServerVersion">false</Set>
|
||||
<Set name="sendDateHeader">true</Set>
|
||||
<Set name="gracefulShutdown">1000</Set>
|
||||
<Set name="stopTimeout">1000</Set>
|
||||
|
||||
</Configure>
|
||||
|
BIN
scripts/favicon.png
Normal file
BIN
scripts/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
@@ -2,27 +2,31 @@ tunnel.0.description=ZzzOT
|
||||
tunnel.0.i2cpHost=127.0.0.1
|
||||
tunnel.0.i2cpPort=7654
|
||||
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.encryptLeaseSet=false
|
||||
tunnel.0.option.i2cp.leaseSetEncType=4
|
||||
tunnel.0.option.i2cp.reduceIdleTime=1200000
|
||||
tunnel.0.option.i2cp.reduceOnIdle=true
|
||||
tunnel.0.option.i2cp.reduceQuantity=1
|
||||
tunnel.0.option.i2p.streaming.connectDelay=0
|
||||
tunnel.0.option.i2p.streaming.maxConcurrentStreams=40
|
||||
tunnel.0.option.i2p.streaming.maxConnsPerHour=100
|
||||
tunnel.0.option.i2p.streaming.maxConnsPerMinute=10
|
||||
tunnel.0.option.i2p.streaming.maxTotalConnsPerHour=2500
|
||||
tunnel.0.option.i2p.streaming.maxTotalConnsPerMinute=60
|
||||
tunnel.0.option.i2p.streaming.maxConcurrentStreams=80
|
||||
tunnel.0.option.i2p.streaming.maxConnsPerHour=300
|
||||
tunnel.0.option.i2p.streaming.maxConnsPerMinute=20
|
||||
tunnel.0.option.i2p.streaming.maxTotalConnsPerHour=50000
|
||||
tunnel.0.option.i2p.streaming.maxTotalConnsPerMinute=2000
|
||||
tunnel.0.option.inbound.backupQuantity=0
|
||||
tunnel.0.option.inbound.length=3
|
||||
tunnel.0.option.inbound.lengthVariance=0
|
||||
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.length=3
|
||||
tunnel.0.option.outbound.lengthVariance=0
|
||||
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.startOnLoad=true
|
||||
tunnel.0.targetHost=127.0.0.1
|
||||
|
@@ -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>
|
@@ -7,6 +7,17 @@
|
||||
# zzz 2010-02
|
||||
# zzz 2014-08 added support for su3 files
|
||||
#
|
||||
|
||||
if [ -z "$I2P" -a -d "$PWD/../i2p.i2p/pkg-temp" ]; then
|
||||
export I2P=../i2p.i2p/pkg-temp
|
||||
fi
|
||||
|
||||
if [ ! -d "$I2P" ]; then
|
||||
echo "Can't locate your I2P installation. Please add a environment variable named I2P with the path to the folder as value"
|
||||
echo "On OSX this solved with running: export I2P=/Applications/i2p if default install directory is used."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PUBKEYDIR=$HOME/.i2p-plugin-keys
|
||||
PUBKEYFILE=$PUBKEYDIR/plugin-public-signing.key
|
||||
PRIVKEYFILE=$PUBKEYDIR/plugin-private-signing.key
|
||||
@@ -15,8 +26,6 @@ PUBKEYSTORE=$PUBKEYDIR/plugin-su3-public-signing.crt
|
||||
PRIVKEYSTORE=$PUBKEYDIR/plugin-su3-keystore.ks
|
||||
KEYTYPE=RSA_SHA512_4096
|
||||
|
||||
export I2P=../i2p/pkg-temp
|
||||
|
||||
PLUGINDIR=${1:-plugin}
|
||||
|
||||
PC=plugin.config
|
||||
|
@@ -1,13 +1,14 @@
|
||||
name=zzzot
|
||||
signer=zzz-plugin@mail.i2p
|
||||
consoleLinkName=ZzzOT
|
||||
consoleLinkURL=http://127.0.0.1:7662/tracker/index.jsp
|
||||
description=Open tracker
|
||||
consoleLinkURL=http://127.0.0.1:7662/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
|
||||
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
|
||||
license=Apache 2.0
|
||||
min-jetty-version=7
|
||||
max-jetty-version=8.9999
|
||||
min-i2p-version=0.9.9
|
||||
min-jetty-version=9
|
||||
min-i2p-version=2.9.0
|
||||
|
235
scripts/tracker-purple.css
Normal file
235
scripts/tracker-purple.css
Normal 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
247
scripts/tracker.css
Normal 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) center center / 2px 100%,
|
||||
repeating-linear-gradient(to bottom, #222, #111 2px) center center / 100% 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;
|
||||
}
|
||||
}
|
@@ -1,3 +1,25 @@
|
||||
#
|
||||
# All changes require plugin restart
|
||||
#
|
||||
# announce interval in seconds
|
||||
# minimum 900 (15 minutes), maximum 21600 (6 hours)
|
||||
interval=1620
|
||||
#
|
||||
# Enable UDP announces
|
||||
# default false
|
||||
udp=false
|
||||
#
|
||||
# UDP connection lifetime in seconds
|
||||
# minimum 60 (1 minute), maximum 21600 (6 hours)
|
||||
lifetime=1200
|
||||
#
|
||||
#
|
||||
# UDP announce port
|
||||
# default 6969
|
||||
port=6969
|
||||
#
|
||||
showfoooter=true
|
||||
#footerText=your html text here
|
||||
#
|
||||
# default false as of 0.19.0
|
||||
#allowFullScrape=false
|
||||
|
@@ -25,13 +25,14 @@
|
||||
</target>
|
||||
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.8" />
|
||||
|
||||
<target name="compile">
|
||||
<mkdir dir="./build" />
|
||||
<mkdir dir="./build/obj" />
|
||||
<javac
|
||||
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"
|
||||
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:${jettylib}/jetty-xml.jar" >
|
||||
@@ -61,18 +62,21 @@
|
||||
<arg value="build/web-fragment.xml" />
|
||||
<arg value="-webapp" />
|
||||
<arg value="jsp/" />
|
||||
<arg value="-die" />
|
||||
</java>
|
||||
|
||||
<javac
|
||||
debug="true"
|
||||
deprecation="on"
|
||||
source="1.5" target="1.5"
|
||||
source="${javac.version}" target="${javac.version}"
|
||||
destdir="build/war/WEB-INF/classes"
|
||||
srcdir="./build/jspjava"
|
||||
includes="**/*.java"
|
||||
includeAntRuntime="false"
|
||||
classpathref="jspcp"
|
||||
failonerror="true" />
|
||||
failonerror="true" >
|
||||
<compilerarg line="${javac.compilerargs}" />
|
||||
</javac>
|
||||
|
||||
<copy file="jsp/WEB-INF/web.xml" tofile="build/web.xml" />
|
||||
<loadfile property="jspc.web.fragment" srcfile="build/web-fragment.xml" />
|
||||
@@ -83,7 +87,6 @@
|
||||
</target>
|
||||
|
||||
<target name="war" depends="precompilejsp">
|
||||
<copy file="jsp/index.html" todir="build/war" />
|
||||
<war destfile="build/tracker.war.jar" webxml="build/web.xml">
|
||||
<fileset dir="build/war" />
|
||||
</war>
|
||||
|
@@ -16,40 +16,31 @@ package net.i2p.zzzot;
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.i2p.crypto.SHA256Generator;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
|
||||
/*
|
||||
* A single peer for a single torrent.
|
||||
* Save a couple stats, and implements
|
||||
* a Map so we can BEncode it
|
||||
* So it's like PeerID but in reverse - we make a Map from the
|
||||
* data. PeerID makes the data from a Map.
|
||||
* Save a couple stats. We no longer support non-compact
|
||||
* announces, so this is no longer a Map that can be BEncoded.
|
||||
* See announce.jsp.
|
||||
*/
|
||||
public class Peer extends HashMap<String, Object> {
|
||||
public class Peer {
|
||||
|
||||
private final Hash hash;
|
||||
private long lastSeen;
|
||||
private long bytesLeft;
|
||||
private static final Integer PORT = Integer.valueOf(6881);
|
||||
|
||||
public Peer(byte[] id, Destination address, ConcurrentMap<String, String> destCache) {
|
||||
super(3);
|
||||
if (id.length != 20)
|
||||
throw new IllegalArgumentException("Bad peer ID length: " + id.length);
|
||||
put("peer id", id);
|
||||
put("port", PORT);
|
||||
// cache the 520-byte address strings
|
||||
String dest = address.toBase64() + ".i2p";
|
||||
String oldDest = destCache.putIfAbsent(dest, dest);
|
||||
if (oldDest != null)
|
||||
dest = oldDest;
|
||||
put("ip", dest);
|
||||
public Peer(byte[] id, Destination address) {
|
||||
hash = address.calculateHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public Peer(byte[] id, Hash h) {
|
||||
hash = h;
|
||||
}
|
||||
|
||||
public void setLeft(long l) {
|
||||
@@ -65,14 +56,10 @@ public class Peer extends HashMap<String, Object> {
|
||||
return lastSeen;
|
||||
}
|
||||
|
||||
/** convert b64.i2p to a Hash, then to a binary string */
|
||||
/* or should we just store it in the constructor? cache it? */
|
||||
public String getHash() {
|
||||
String ip = (String) get("ip");
|
||||
byte[] b = Base64.decode(ip.substring(0, ip.length() - 4));
|
||||
Hash h = SHA256Generator.getInstance().calculateHash(b);
|
||||
try {
|
||||
return new String(h.getData(), "ISO-8859-1");
|
||||
} catch (UnsupportedEncodingException uee) { return null; }
|
||||
/**
|
||||
* @since 0.20
|
||||
*/
|
||||
public byte[] getHashBytes() {
|
||||
return hash.getData();
|
||||
}
|
||||
}
|
||||
|
@@ -1,60 +0,0 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2010 zzz (zzz@mail.i2p)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.streaming.I2PSocketEepGet;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.i2ptunnel.TunnelController;
|
||||
import net.i2p.util.EepGet;
|
||||
|
||||
/**
|
||||
* Announce to seedless
|
||||
* @since 0.6
|
||||
*/
|
||||
public class SeedlessAnnouncer {
|
||||
|
||||
private static final String SPONGE =
|
||||
"VG4Bd~q1RA3BdoF3z5fSR7p0xe1CTVgDMWVGyFchA9Wm2iXUkIR35G45XE31Uc9~IOt-ktNLL2~TYQZ13Vl8udosngDn8RJG1NtVASH4khsbgkkoFLWd6UuvuOjQKBFKjaEPJgxOzh0kxolRPPNHhFuuAGzNLKvz~LI2MTf0P6nwmRg1lBoRIUpSVocEHY4X306nT2VtY07FixbJcPCU~EeRin24yNoiZop-C3Wi1SGwJJK-NS7mnkNzd8ngDJXDJtR-wLP1vNyyBY6NySgqPiIhENHoVeXd5krlR42HORCxEDb4jhoqlbyJq-PrhTJ5HdH4-~gEq09B~~NIHzy7X02XgmBXhTYRtl6HbLMXs6SI5fq9OFgVp5YZWYUklJjMDI7jOrGrEZGSHhnJK9kT6D3CqVIM0cYEhe4ttmTegbZvC~J6DrRTIAX422qRQJBPsTUnv4iFyuJE-8SodP6ikTjRH21Qx73SxqOvmrOiu7Bsp0lvVDa84aoaYLdiGv87AAAA";
|
||||
|
||||
private static final String ANNOUNCE = "announce " + Base64.encode("seedless,eepsite,torrent");
|
||||
|
||||
public void announce(TunnelController controller) {
|
||||
// get the I2PTunnel from the controller (no method now)
|
||||
|
||||
// get the I2PTunnelTask from I2PTunnel
|
||||
|
||||
// cast to an I2PTunnelServer
|
||||
|
||||
// get the SocketManager from the server (no method now)
|
||||
I2PSocketManager mgr = null;
|
||||
|
||||
I2PAppContext ctx = I2PAppContext.getGlobalContext();
|
||||
String url = "http://" + SPONGE + "/Seedless/seedless";
|
||||
EepGet get = new I2PSocketEepGet(ctx, mgr, 1, -1, 1024, null, new DummyOutputStream(), url);
|
||||
get.addHeader("X-Seedless", ANNOUNCE);
|
||||
get.fetch();
|
||||
}
|
||||
|
||||
private static class DummyOutputStream extends OutputStream {
|
||||
public void write(int b) {}
|
||||
}
|
||||
}
|
@@ -17,6 +17,7 @@ package net.i2p.zzzot;
|
||||
*/
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.CoreVersion;
|
||||
import net.i2p.data.DataHelper;
|
||||
@@ -33,15 +34,19 @@ public class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
|
||||
private final SDSCache<InfoHash> _hashCache;
|
||||
private final SDSCache<PID> _pidCache;
|
||||
private final Integer _interval;
|
||||
private final int _udpLifetime;
|
||||
private final AtomicInteger _announces = new AtomicInteger();
|
||||
|
||||
/**
|
||||
* @param interval in seconds
|
||||
* @param udpInterval in seconds
|
||||
*/
|
||||
public Torrents(int interval) {
|
||||
public Torrents(int interval, int udpLifetime) {
|
||||
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);
|
||||
_udpLifetime = udpLifetime;
|
||||
}
|
||||
|
||||
public int countPeers() {
|
||||
@@ -60,6 +65,14 @@ public class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
|
||||
return _interval;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return in seconds
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public int getUDPLifetime() {
|
||||
return _udpLifetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull from cache or return new
|
||||
*
|
||||
@@ -86,6 +99,28 @@ public class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
|
||||
return _pidCache.get(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called for every announce except for event = STOPPED.
|
||||
* Hook it here to keep an announce counter.
|
||||
*
|
||||
* @since 0.20.0
|
||||
*/
|
||||
@Override
|
||||
public Peers putIfAbsent(InfoHash ih, Peers p) {
|
||||
_announces.incrementAndGet();
|
||||
return super.putIfAbsent(ih, p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of announces since the last call.
|
||||
* Resets the counter to zero.
|
||||
*
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public int getAnnounces() {
|
||||
return _announces.getAndSet(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.12.0
|
||||
*/
|
||||
@@ -93,6 +128,7 @@ public class Torrents extends ConcurrentHashMap<InfoHash, Peers> {
|
||||
public void clear() {
|
||||
super.clear();
|
||||
clearCaches();
|
||||
_announces.set(0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
571
src/java/net/i2p/zzzot/UDPHandler.java
Normal file
571
src/java/net/i2p/zzzot/UDPHandler.java
Normal file
@@ -0,0 +1,571 @@
|
||||
package net.i2p.zzzot;
|
||||
/*
|
||||
* Copyright 2022 zzz (zzz@mail.i2p)
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.SynchronousQueue;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.I2PSessionMuxedListener;
|
||||
import net.i2p.client.datagram.Datagram2;
|
||||
import net.i2p.client.datagram.Datagram3;
|
||||
import net.i2p.crypto.SipHashInline;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.i2ptunnel.I2PTunnel;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.LHMCache;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* Hook into the session and handle UDP announces
|
||||
* Ref: Proposal 160, BEP 15
|
||||
*
|
||||
* @since 0.19.0
|
||||
*/
|
||||
public class UDPHandler implements I2PSessionMuxedListener {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final I2PTunnel _tunnel;
|
||||
private final ZzzOT _zzzot;
|
||||
private final Cleaner _cleaner;
|
||||
private final long sipk0, sipk1;
|
||||
private final Map<Hash, Destination> _destCache;
|
||||
private final AtomicInteger _announces = new AtomicInteger();
|
||||
private volatile boolean _running;
|
||||
private ThreadPoolExecutor _executor;
|
||||
/** how long to wait before dropping an idle thread */
|
||||
private static final long HANDLER_KEEPALIVE_MS = 2*60*1000;
|
||||
|
||||
// The listen port.
|
||||
public final int PORT;
|
||||
private static final long MAGIC = 0x41727101980L;
|
||||
private static final int ACTION_CONNECT = 0;
|
||||
private static final int ACTION_ANNOUNCE = 1;
|
||||
private static final int ACTION_SCRAPE = 2;
|
||||
private static final int ACTION_ERROR = 3;
|
||||
private static final int MAX_RESPONSES = 25;
|
||||
private static final int EVENT_NONE = 0;
|
||||
private static final int EVENT_COMPLETED = 1;
|
||||
private static final int EVENT_STARTED = 2;
|
||||
private static final int EVENT_STOPPED = 3;
|
||||
// keep it short, we should have the leaseset,
|
||||
// if a new ratchet session was created
|
||||
private final long LOOKUP_TIMEOUT = 2000;
|
||||
private final long CLEAN_TIME;
|
||||
private final long STAT_TIME = 2*60*1000;
|
||||
private static final byte[] INVALID = DataHelper.getUTF8("Invalid connection ID");
|
||||
private static final byte[] PROTOCOL = DataHelper.getUTF8("Bad protocol");
|
||||
private static final byte[] SCRAPE = DataHelper.getUTF8("Scrape unsupported");
|
||||
|
||||
public UDPHandler(I2PAppContext ctx, I2PTunnel tunnel, ZzzOT zzzot, int port) {
|
||||
_context = ctx;
|
||||
_log = ctx.logManager().getLog(UDPHandler.class);
|
||||
_tunnel = tunnel;
|
||||
_zzzot = zzzot;
|
||||
CLEAN_TIME = (zzzot.getTorrents().getUDPLifetime() + 60) * 1000;
|
||||
PORT = port;
|
||||
_cleaner = new Cleaner();
|
||||
sipk0 = ctx.random().nextLong();
|
||||
sipk1 = ctx.random().nextLong();
|
||||
// the highest-traffic zzzot is running about 3000 announces/minute,
|
||||
// give us enough to respond to the first announce after the connection
|
||||
_destCache = new LHMCache<Hash, Destination>(1024);
|
||||
}
|
||||
|
||||
public synchronized void start() {
|
||||
_running = true;
|
||||
_executor = new CustomThreadPoolExecutor();
|
||||
_executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
|
||||
(new I2PAppThread(new Waiter(), "ZzzOT UDP startup", true)).start();
|
||||
long[] r = new long[] { 5*60*1000 };
|
||||
_context.statManager().createRequiredRateStat("plugin.zzzot.announces.udp", "UDP announces per minute", "Plugins", r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
_running = false;
|
||||
_executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
|
||||
_executor.shutdownNow();
|
||||
_executor = null;
|
||||
_cleaner.cancel();
|
||||
_context.statManager().removeRateStat("plugin.zzzot.announces.udp");
|
||||
_announces.set(0);
|
||||
}
|
||||
|
||||
private class Waiter implements Runnable {
|
||||
public void run() {
|
||||
while (_running) {
|
||||
// requires I2P 0.9.53 (1.7.0)
|
||||
List<I2PSession> sessions = _tunnel.getSessions();
|
||||
if (sessions.isEmpty()) {
|
||||
try { Thread.sleep(1000); } catch (InterruptedException ie) { break; }
|
||||
continue;
|
||||
}
|
||||
I2PSession session = sessions.get(0);
|
||||
session.addMuxedSessionListener(UDPHandler.this, I2PSession.PROTO_DATAGRAM2, PORT);
|
||||
session.addMuxedSessionListener(UDPHandler.this, I2PSession.PROTO_DATAGRAM3, PORT);
|
||||
_cleaner.schedule(STAT_TIME);
|
||||
if (_log.shouldInfo())
|
||||
_log.info("got session");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// begin listener methods ///
|
||||
|
||||
public void messageAvailable(I2PSession sess, int id, long size) {
|
||||
throw new IllegalStateException("muxed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void messageAvailable(I2PSession session, int id, long size, int proto, int fromPort, int toPort) {
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Got " + size + " bytes, proto: " + proto + " from port: " + fromPort + " to port: " + toPort);
|
||||
try {
|
||||
// receive message
|
||||
byte[] msg = session.receiveMessage(id);
|
||||
if (proto == I2PSession.PROTO_DATAGRAM2) {
|
||||
// load datagram into it
|
||||
Datagram2 dg = Datagram2.load(_context, session, msg);
|
||||
handle(session, dg.getSender(), null, fromPort, dg.getPayload());
|
||||
} else if (proto == I2PSession.PROTO_DATAGRAM3) {
|
||||
Datagram3 dg = Datagram3.load(_context, session, msg);
|
||||
handle(session, null, dg.getSender(), fromPort, dg.getPayload());
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping message with unknown protocol " + proto);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error receiving datagram", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void reportAbuse(I2PSession arg0, int arg1) {}
|
||||
|
||||
public void disconnected(I2PSession arg0) {
|
||||
_cleaner.cancel();
|
||||
}
|
||||
|
||||
public void errorOccurred(I2PSession arg0, String arg1, Throwable arg2) {
|
||||
_log.error(arg1, arg2);
|
||||
}
|
||||
|
||||
/// end listener methods ///
|
||||
|
||||
/**
|
||||
* One of from or fromHash non-null
|
||||
* @param from non-null for connect request
|
||||
* @param fromHash non-null for announce request
|
||||
*/
|
||||
private void handle(I2PSession session, Destination from, Hash fromHash, int fromPort, byte[] data) {
|
||||
int sz = data.length;
|
||||
if (sz < 16) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping short msg length " + sz);
|
||||
return;
|
||||
}
|
||||
long connID = DataHelper.fromLong8(data, 0);
|
||||
int action = (int) DataHelper.fromLong(data, 8, 4);
|
||||
if (action == ACTION_CONNECT) {
|
||||
if (connID != MAGIC) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping bad connect magic " + connID);
|
||||
return;
|
||||
}
|
||||
if (from == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping dg3 connect");
|
||||
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||
sendError(session, fromHash, fromPort, transID, PROTOCOL);
|
||||
return;
|
||||
}
|
||||
handleConnect(session, from, fromPort, data);
|
||||
} else if (action == ACTION_ANNOUNCE) {
|
||||
if (fromHash == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping dg2 announce");
|
||||
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||
sendError(session, from, fromPort, transID, PROTOCOL);
|
||||
return;
|
||||
}
|
||||
handleAnnounce(session, connID, fromHash, fromPort, data);
|
||||
} else if (action == ACTION_SCRAPE) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("got unsupported scrape");
|
||||
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||
if (from != null)
|
||||
sendError(session, from, fromPort, transID, SCRAPE);
|
||||
else
|
||||
sendError(session, fromHash, fromPort, transID, SCRAPE);
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping bad action " + action);
|
||||
// TODO send error?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from non-null
|
||||
*/
|
||||
private void handleConnect(I2PSession session, Destination from, int fromPort, byte[] data) {
|
||||
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||
long connID = generateCID(from.calculateHash());
|
||||
byte[] resp = new byte[18];
|
||||
DataHelper.toLong(resp, 4, 4, transID);
|
||||
DataHelper.toLong8(resp, 8, connID);
|
||||
// Addition to BEP 15
|
||||
DataHelper.toLong(resp, 16, 2, _zzzot.getTorrents().getUDPLifetime());
|
||||
try {
|
||||
session.sendMessage(from, resp, I2PSession.PROTO_DATAGRAM_RAW, PORT, fromPort);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("sent connect reply with conn ID " + connID + " to " + from.toBase32());
|
||||
synchronized(_destCache) {
|
||||
_destCache.put(from.calculateHash(), from);
|
||||
}
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error sending connect reply", ise);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from may be null
|
||||
*/
|
||||
private void handleAnnounce(I2PSession session, long connID, Hash fromHash, int fromPort, byte[] data) {
|
||||
int sz = data.length;
|
||||
if (sz < 96) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("dropping short announce length " + sz);
|
||||
return;
|
||||
}
|
||||
int transID = (int) DataHelper.fromLong(data, 12, 4);
|
||||
boolean ok = validateCID(fromHash, connID);
|
||||
if (!ok) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("conn ID invalid: " + connID);
|
||||
sendError(session, fromHash, fromPort, transID, INVALID);
|
||||
return;
|
||||
}
|
||||
|
||||
// parse packet
|
||||
byte[] bih = new byte[InfoHash.LENGTH];
|
||||
System.arraycopy(data, 16, bih, 0, InfoHash.LENGTH);
|
||||
InfoHash ih = new InfoHash(bih);
|
||||
byte[] bpid = new byte[PID.LENGTH];
|
||||
System.arraycopy(data, 36, bpid, 0, PID.LENGTH);
|
||||
PID pid = new PID(bpid);
|
||||
// ignored
|
||||
//long dl = DataHelper.fromLong8(data, 56);
|
||||
//long ul = DataHelper.fromLong8(data, 72);
|
||||
int event = (int) DataHelper.fromLong(data, 80, 4);
|
||||
long left = event == EVENT_COMPLETED ? 0 : DataHelper.fromLong8(data, 64);
|
||||
// ignored
|
||||
//long ip = DataHelper.fromLong(data, 84, 4);
|
||||
//long key = DataHelper.fromLong(data, 88, 4);
|
||||
// Note: BEP 15 spec default is -1 but we read as a positive long
|
||||
long want = DataHelper.fromLong(data, 92, 4);
|
||||
if (want > MAX_RESPONSES)
|
||||
want = MAX_RESPONSES;
|
||||
// ignored
|
||||
//int port = (int) DataHelper.fromLong(data, 96, 2);
|
||||
|
||||
Torrents torrents = _zzzot.getTorrents();
|
||||
Peers peers = torrents.get(ih);
|
||||
if (peers == null && event != EVENT_STOPPED) {
|
||||
_announces.incrementAndGet();
|
||||
peers = new Peers();
|
||||
Peers p2 = torrents.putIfAbsent(ih, peers);
|
||||
if (p2 != null)
|
||||
peers = p2;
|
||||
}
|
||||
int size;
|
||||
int seeds;
|
||||
List<Peer> peerlist;
|
||||
if (event == EVENT_STOPPED) {
|
||||
if (peers != null)
|
||||
peers.remove(pid);
|
||||
peerlist = null;
|
||||
size = 0;
|
||||
seeds = 0;
|
||||
} else {
|
||||
Peer p = peers.get(pid);
|
||||
if (p == null) {
|
||||
p = new Peer(pid.getData(), fromHash);
|
||||
Peer p2 = peers.putIfAbsent(pid, p);
|
||||
if (p2 != null)
|
||||
p = p2;
|
||||
}
|
||||
p.setLeft(left);
|
||||
|
||||
size = peers.size();
|
||||
seeds = peers.countSeeds();
|
||||
if (want <= 0 || event == EVENT_STOPPED) {
|
||||
peerlist = null;
|
||||
} else {
|
||||
peerlist = new ArrayList<Peer>(peers.values());
|
||||
peerlist.remove(p); // them
|
||||
if (want < size - 1) {
|
||||
if (size > 150) {
|
||||
// If size is huge, use random iterator for efficiency
|
||||
List<Peer> rv = new ArrayList<Peer>(size);
|
||||
for (RandomIterator<Peer> iter = new RandomIterator<Peer>(peerlist); iter.hasNext(); ) {
|
||||
rv.add(iter.next());
|
||||
}
|
||||
peerlist = rv;
|
||||
} else {
|
||||
Collections.shuffle(peerlist, _context.random());
|
||||
peerlist = peerlist.subList(0, (int) want);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int count = peerlist != null ? peerlist.size() : 0;
|
||||
byte[] resp = new byte[20 + (32 * count)];
|
||||
resp[3] = (byte) ACTION_ANNOUNCE;
|
||||
DataHelper.toLong(resp, 4, 4, transID);
|
||||
DataHelper.toLong(resp, 8, 4, torrents.getInterval());
|
||||
DataHelper.toLong(resp, 12, 4, size - seeds);
|
||||
DataHelper.toLong(resp, 16, 4, seeds);
|
||||
if (peerlist != null) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
System.arraycopy(peerlist.get(i).getHashBytes(), 0, resp, 20 + (i * 32), 32);
|
||||
}
|
||||
}
|
||||
|
||||
Destination from = lookupCache(fromHash);
|
||||
if (from == null) {
|
||||
try {
|
||||
_executor.execute(new Lookup(session, fromHash, fromPort, resp));
|
||||
} catch (RejectedExecutionException ree) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error sending announce reply - thread pool full");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
session.sendMessage(from, resp, I2PSession.PROTO_DATAGRAM_RAW, PORT, fromPort);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("sent announce reply to " + from);
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error sending announce reply", ise);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from non-null
|
||||
* @param msg non-null
|
||||
*/
|
||||
private void sendError(I2PSession session, Hash toHash, int toPort, long transID, byte[] msg) {
|
||||
Destination to = lookupCache(toHash);
|
||||
if (to == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("don't have cached dest to send error to " + toHash.toBase32());
|
||||
return;
|
||||
}
|
||||
// don't bother looking up via I2CP
|
||||
sendError(session, to, toPort, transID, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from non-null
|
||||
* @param msg non-null
|
||||
*/
|
||||
private void sendError(I2PSession session, Destination to, int toPort, long transID, byte[] msg) {
|
||||
byte[] resp = new byte[8 + msg.length];
|
||||
DataHelper.toLong(resp, 0, 4, ACTION_ERROR);
|
||||
DataHelper.toLong(resp, 4, 4, transID);
|
||||
System.arraycopy(msg, 0, resp, 8, msg.length);
|
||||
try {
|
||||
session.sendMessage(to, resp, I2PSession.PROTO_DATAGRAM_RAW, PORT, toPort);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("sent error to " + to.toBase32());
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error sending connect reply", ise);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking.
|
||||
* @return null on failure
|
||||
*/
|
||||
private Destination lookup(I2PSession session, Hash hash) {
|
||||
Destination rv = lookupCache(hash);
|
||||
if (rv != null)
|
||||
return rv;
|
||||
return lookupI2CP(session, hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nonblocking.
|
||||
* @return null on failure
|
||||
*/
|
||||
private Destination lookupCache(Hash hash) {
|
||||
// Test deferred
|
||||
//if (true) return null;
|
||||
synchronized(_destCache) {
|
||||
return _destCache.get(hash);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocking.
|
||||
* @return null on failure
|
||||
*/
|
||||
private Destination lookupI2CP(I2PSession session, Hash hash) {
|
||||
Destination rv;
|
||||
try {
|
||||
rv = session.lookupDest(hash, LOOKUP_TIMEOUT);
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("lookup error", ise);
|
||||
return null;
|
||||
}
|
||||
if (rv == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("lookup failed for response to " + hash.toBase32());
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
private long generateCID(Hash hash) {
|
||||
byte[] buf = new byte[40];
|
||||
System.arraycopy(hash.getData(), 0, buf, 0, 32);
|
||||
long time = _context.clock().now() / CLEAN_TIME;
|
||||
DataHelper.toLong8(buf, 32, time);
|
||||
return SipHashInline.hash24(sipk0, sipk1, buf);
|
||||
}
|
||||
|
||||
private boolean validateCID(Hash hash, long cid) {
|
||||
byte[] buf = new byte[40];
|
||||
System.arraycopy(hash.getData(), 0, buf, 0, 32);
|
||||
// current epoch
|
||||
long time = _context.clock().now() / CLEAN_TIME;
|
||||
DataHelper.toLong8(buf, 32, time);
|
||||
long c = SipHashInline.hash24(sipk0, sipk1, buf);
|
||||
if (cid == c)
|
||||
return true;
|
||||
// previous epoch
|
||||
time--;
|
||||
DataHelper.toLong8(buf, 32, time);
|
||||
c = SipHashInline.hash24(sipk0, sipk1, buf);
|
||||
return cid == c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the announce stat and set the announce count to 0
|
||||
*/
|
||||
private class Cleaner extends SimpleTimer2.TimedEvent {
|
||||
public Cleaner() { super(_context.simpleTimer2()); }
|
||||
public void timeReached() {
|
||||
long count = _announces.getAndSet(0);
|
||||
_context.statManager().addRateData("plugin.zzzot.announces.udp", count / (STAT_TIME / (60*1000L)));
|
||||
schedule(STAT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Until we have a nonblocking lookup API in I2CP
|
||||
*
|
||||
* @since 0.20.0
|
||||
*/
|
||||
private class Lookup implements Runnable {
|
||||
private final I2PSession _session;
|
||||
private final Hash _hash;
|
||||
private final int _port;
|
||||
private final byte[] _msg;
|
||||
|
||||
public Lookup(I2PSession sess, Hash h, int port, byte[] msg) {
|
||||
_session = sess;
|
||||
_hash = h;
|
||||
_port = port;
|
||||
_msg = msg;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// blocking
|
||||
Destination d = lookupI2CP(_session, _hash);
|
||||
if (d == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("deferred lookup failed for " + _hash.toBase32());
|
||||
return;
|
||||
}
|
||||
try {
|
||||
_session.sendMessage(d, _msg, I2PSession.PROTO_DATAGRAM_RAW, PORT, _port);
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("sent deferred reply to " + _hash.toBase32());
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("error sending deferred reply", ise);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Until we have a nonblocking lookup API in I2CP
|
||||
*
|
||||
* @since 0.20.0
|
||||
*/
|
||||
private static class CustomThreadPoolExecutor extends ThreadPoolExecutor {
|
||||
public CustomThreadPoolExecutor() {
|
||||
super(0, 25, HANDLER_KEEPALIVE_MS, TimeUnit.MILLISECONDS,
|
||||
new SynchronousQueue<Runnable>(), new CustomThreadFactory());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Just to set the name and set Daemon
|
||||
*
|
||||
* @since 0.20.0
|
||||
*/
|
||||
private static class CustomThreadFactory implements ThreadFactory {
|
||||
private final AtomicInteger _executorThreadCount = new AtomicInteger();
|
||||
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread rv = Executors.defaultThreadFactory().newThread(r);
|
||||
rv.setName("ZzzOT lookup " + _executorThreadCount.incrementAndGet());
|
||||
rv.setDaemon(true);
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
@@ -29,17 +29,20 @@ import net.i2p.util.SimpleTimer2;
|
||||
*/
|
||||
class ZzzOT {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Torrents _torrents;
|
||||
private final Cleaner _cleaner;
|
||||
private final ConcurrentHashMap<String, String> _destCache = new ConcurrentHashMap<String, String>();
|
||||
private final long EXPIRE_TIME;
|
||||
|
||||
private static final String PROP_INTERVAL = "interval";
|
||||
private static final long CLEAN_TIME = 4*60*1000;
|
||||
private static final long DEST_CACHE_CLEAN_TIME = 3*60*60*1000;
|
||||
private static final String PROP_UDP_LIFETIME = "lifetime";
|
||||
private static final long CLEAN_TIME = 2*60*1000;
|
||||
private static final int DEFAULT_INTERVAL = 27*60;
|
||||
private static final int DEFAULT_UDP_LIFETIME = 20*60;
|
||||
private static final int MIN_INTERVAL = 15*60;
|
||||
private static final int MAX_INTERVAL = 6*60*60;
|
||||
private static final int MIN_UDP_LIFETIME = 60;
|
||||
private static final int MAX_UDP_LIFETIME = 6*60*60;
|
||||
|
||||
ZzzOT(I2PAppContext ctx, Properties p) {
|
||||
String intv = p.getProperty(PROP_INTERVAL);
|
||||
@@ -53,28 +56,41 @@ class ZzzOT {
|
||||
interval = MAX_INTERVAL;
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
_torrents = new Torrents(interval);
|
||||
intv = p.getProperty(PROP_UDP_LIFETIME);
|
||||
int lifetime = DEFAULT_UDP_LIFETIME;
|
||||
if (intv != null) {
|
||||
try {
|
||||
lifetime = Integer.parseInt(intv);
|
||||
if (lifetime < MIN_UDP_LIFETIME)
|
||||
interval = MIN_UDP_LIFETIME;
|
||||
else if (interval > MAX_UDP_LIFETIME)
|
||||
interval = MAX_UDP_LIFETIME;
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
_torrents = new Torrents(interval, lifetime);
|
||||
EXPIRE_TIME = 1000 * (interval + interval / 2);
|
||||
_cleaner = new Cleaner(ctx);
|
||||
_context = ctx;
|
||||
}
|
||||
|
||||
Torrents getTorrents() {
|
||||
return _torrents;
|
||||
}
|
||||
|
||||
/** @since 0.9.14 */
|
||||
ConcurrentHashMap<String, String> getDestCache() {
|
||||
return _destCache;
|
||||
}
|
||||
|
||||
void start() {
|
||||
_cleaner.forceReschedule(CLEAN_TIME);
|
||||
long[] r = new long[] { 5*60*1000 };
|
||||
_context.statManager().createRequiredRateStat("plugin.zzzot.announces", "Total announces per minute", "Plugins", r);
|
||||
_context.statManager().createRequiredRateStat("plugin.zzzot.peers", "Number of peers", "Plugins", r);
|
||||
_context.statManager().createRequiredRateStat("plugin.zzzot.torrents", "Number of torrents", "Plugins", r);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_cleaner.cancel();
|
||||
_torrents.clear();
|
||||
_destCache.clear();
|
||||
_context.statManager().removeRateStat("plugin.zzzot.announces");
|
||||
_context.statManager().removeRateStat("plugin.zzzot.peers");
|
||||
_context.statManager().removeRateStat("plugin.zzzot.torrents");
|
||||
}
|
||||
|
||||
private class Cleaner extends SimpleTimer2.TimedEvent {
|
||||
@@ -88,6 +104,7 @@ class ZzzOT {
|
||||
|
||||
public void timeReached() {
|
||||
long now = System.currentTimeMillis();
|
||||
int peers = 0;
|
||||
for (Iterator<Peers> iter = _torrents.values().iterator(); iter.hasNext(); ) {
|
||||
Peers p = iter.next();
|
||||
int recent = 0;
|
||||
@@ -100,10 +117,12 @@ class ZzzOT {
|
||||
}
|
||||
if (recent <= 0)
|
||||
iter.remove();
|
||||
else
|
||||
peers += recent;
|
||||
}
|
||||
if (_runCount.incrementAndGet() % (DEST_CACHE_CLEAN_TIME / CLEAN_TIME) == 0) {
|
||||
_destCache.clear();
|
||||
}
|
||||
_context.statManager().addRateData("plugin.zzzot.announces", _torrents.getAnnounces() / (CLEAN_TIME / (60*1000L)));
|
||||
_context.statManager().addRateData("plugin.zzzot.peers", peers);
|
||||
_context.statManager().addRateData("plugin.zzzot.torrents", _torrents.size());
|
||||
schedule(CLEAN_TIME);
|
||||
}
|
||||
}
|
||||
|
@@ -35,6 +35,8 @@ import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.PrivateKeyFile;
|
||||
import net.i2p.i2ptunnel.TunnelController;
|
||||
import net.i2p.stat.Rate;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
@@ -65,12 +67,34 @@ public class ZzzOTController implements ClientApp {
|
||||
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 boolean _showfooter;
|
||||
private static String _footertext;
|
||||
private static boolean _fullScrape;
|
||||
private final boolean _enableUDP;
|
||||
private final int _udpPort;
|
||||
private UDPHandler _udp;
|
||||
private String _b32;
|
||||
|
||||
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.20.0";
|
||||
private static final String DEFAULT_SHOWFOOTER = "true";
|
||||
private static final String PROP_SHOWFOOTER = "showfooter";
|
||||
private static final String DEFAULT_FOOTERTEXT = "Running <a href=\"http://git.idk.i2p/i2p-hackers/i2p.plugins.zzzot\" target=\"_blank\">ZZZOT</a> " + VERSION;
|
||||
private static final String PROP_FOOTERTEXT = "footertext";
|
||||
private static final String PROP_FULLSCRAPE = "allowFullScrape";
|
||||
private static final String DEFAULT_FULLSCRAPE = "false";
|
||||
private static final String PROP_UDP = "udp";
|
||||
private static final String DEFAULT_UDP = "false";
|
||||
private static final String PROP_UDP_PORT = "udp";
|
||||
private static final int DEFAULT_UDP_PORT = 6969;
|
||||
private static final String CONFIG_FILE = "zzzot.config";
|
||||
private static final String BACKUP_SUFFIX = ".jetty6";
|
||||
private static final String BACKUP_SUFFIX = ".jetty8";
|
||||
private static final String[] xmlFiles = {
|
||||
"jetty.xml", "contexts/base-context.xml", "contexts/cgi-context.xml",
|
||||
"etc/realm.properties", "etc/webdefault.xml" };
|
||||
@@ -96,6 +120,19 @@ public class ZzzOTController implements ClientApp {
|
||||
_log.warn("No config file " + cfile);
|
||||
}
|
||||
_zzzot = new ZzzOT(ctx, props);
|
||||
_sitename = props.getProperty(PROP_SITENAME, DEFAULT_SITENAME);
|
||||
_showfooter = Boolean.parseBoolean(props.getProperty(PROP_SHOWFOOTER, DEFAULT_SHOWFOOTER));
|
||||
_footertext = props.getProperty(PROP_FOOTERTEXT, DEFAULT_FOOTERTEXT);
|
||||
_fullScrape = Boolean.parseBoolean(props.getProperty(PROP_FULLSCRAPE, DEFAULT_FULLSCRAPE));
|
||||
_enableUDP = Boolean.parseBoolean(props.getProperty(PROP_UDP, DEFAULT_UDP));
|
||||
int p = DEFAULT_UDP_PORT;
|
||||
String port = props.getProperty(PROP_UDP_PORT);
|
||||
if (port != null) {
|
||||
try {
|
||||
p = Integer.parseInt(port);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
}
|
||||
_udpPort = p;
|
||||
_state = INITIALIZED;
|
||||
}
|
||||
|
||||
@@ -109,31 +146,106 @@ public class ZzzOTController implements ClientApp {
|
||||
/**
|
||||
* @return null if not running
|
||||
*/
|
||||
public static Torrents getTorrents() {
|
||||
private static ZzzOTController getThis() {
|
||||
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 (ZzzOTController) z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return null if not running
|
||||
* @since 0.9.14
|
||||
*/
|
||||
public static ConcurrentMap<String, String> getDestCache() {
|
||||
ClientAppManager mgr = I2PAppContext.getGlobalContext().clientAppManager();
|
||||
if (mgr == null)
|
||||
public static Torrents getTorrents() {
|
||||
ZzzOTController ctrlr = getThis();
|
||||
if (ctrlr == null)
|
||||
return null;
|
||||
ClientApp z = mgr.getRegisteredApp(NAME);
|
||||
if (z == null)
|
||||
return ctrlr._zzzot.getTorrents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return announces per minute, 0 if not running
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public static double getAnnounceRate() {
|
||||
RateStat rs = I2PAppContext.getGlobalContext().statManager().getRate("plugin.zzzot.announces");
|
||||
if (rs == null)
|
||||
return 0;
|
||||
Rate r = rs.getRate(5*60*1000);
|
||||
if (r == null)
|
||||
return 0;
|
||||
return r.getAvgOrLifetimeAvg();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return announces per minute, 0 if not running or UDP not enabled
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public static double getUDPAnnounceRate() {
|
||||
RateStat rs = I2PAppContext.getGlobalContext().statManager().getRate("plugin.zzzot.announces.udp");
|
||||
if (rs == null)
|
||||
return 0;
|
||||
Rate r = rs.getRate(5*60*1000);
|
||||
if (r == null)
|
||||
return 0;
|
||||
return r.getAvgOrLifetimeAvg();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false if not running
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public static boolean isUDPEnabled() {
|
||||
ZzzOTController ctrlr = getThis();
|
||||
if (ctrlr == null)
|
||||
return false;
|
||||
return ctrlr.getUDPEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return false if not running
|
||||
* @since 0.20.0
|
||||
*/
|
||||
private boolean getUDPEnabled() {
|
||||
return _enableUDP;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 0 if not running
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public static int udpPort() {
|
||||
ZzzOTController ctrlr = getThis();
|
||||
if (ctrlr == null)
|
||||
return 0;
|
||||
return ctrlr.getUDPPort();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public int getUDPPort() {
|
||||
return _udpPort;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if not running
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public static String b32() {
|
||||
ZzzOTController ctrlr = getThis();
|
||||
if (ctrlr == null)
|
||||
return null;
|
||||
ZzzOTController ctrlr = (ZzzOTController) z;
|
||||
return ctrlr._zzzot.getDestCache();
|
||||
return ctrlr.getB32();
|
||||
}
|
||||
/**
|
||||
* @since 0.20.0
|
||||
*/
|
||||
public String getB32() {
|
||||
return _b32;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -168,7 +280,11 @@ public class ZzzOTController implements ClientApp {
|
||||
startJetty(pluginDir, dest);
|
||||
startI2PTunnel(pluginDir, dest);
|
||||
_zzzot.start();
|
||||
// SeedlessAnnouncer.announce(_tunnel);
|
||||
// requires I2P 0.9.66 (2.9.0)
|
||||
if (_enableUDP) {
|
||||
_udp = new UDPHandler(_context, _tunnel.getTunnel(), _zzzot, _udpPort);
|
||||
_udp.start();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -181,15 +297,21 @@ public class ZzzOTController implements ClientApp {
|
||||
_log.error("Cannot open " + i2ptunnelConfig.getAbsolutePath() + ' ' + ioe);
|
||||
throw new IllegalArgumentException("Cannot open " + i2ptunnelConfig.getAbsolutePath() + ' ' + ioe);
|
||||
}
|
||||
String p = i2ptunnelProps.getProperty("tunnel.0.option.i2cp.leaseSetEncType");
|
||||
if (p == null || p.equals("4,0"))
|
||||
i2ptunnelProps.setProperty("tunnel.0.option.i2cp.leaseSetEncType", "4");
|
||||
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();
|
||||
_b32 = tun.getMyDestHashBase32();
|
||||
List msgs = tun.clearMessages();
|
||||
for (Object s : msgs) {
|
||||
_log.error("NOTICE: ZzzOT Tunnel message: " + s);
|
||||
_log.logAlways(Log.INFO, "NOTICE: ZzzOT Tunnel message: " + s);
|
||||
}
|
||||
} else {
|
||||
_b32 = dest.calculateHash().toBase32();
|
||||
tun.startTunnelBackground();
|
||||
}
|
||||
_tunnel = tun;
|
||||
}
|
||||
@@ -221,6 +343,8 @@ public class ZzzOTController implements ClientApp {
|
||||
private void stop() {
|
||||
stopI2PTunnel();
|
||||
stopJetty();
|
||||
if (_udp != null)
|
||||
_udp.stop();
|
||||
_zzzot.stop();
|
||||
}
|
||||
|
||||
@@ -228,11 +352,8 @@ public class ZzzOTController implements ClientApp {
|
||||
if (_tunnel == null)
|
||||
return;
|
||||
try {
|
||||
// destroyTunnel() not available until 0.9.17, but we put 0.9.16 here for now
|
||||
// so we get testing in 0.9.16-6 or later dev builds.
|
||||
// No access to RouterVersion here.
|
||||
// TODO change to 0.9.17
|
||||
if (VersionComparator.comp(CoreVersion.VERSION, "0.9.16") >= 0) {
|
||||
// destroyTunnel() not available until 0.9.17
|
||||
if (VersionComparator.comp(CoreVersion.VERSION, "0.9.17") >= 0) {
|
||||
try {
|
||||
_tunnel.destroyTunnel();
|
||||
} catch (Throwable t) {
|
||||
@@ -283,10 +404,10 @@ public class ZzzOTController implements ClientApp {
|
||||
* @since 0.10 (Jetty 7)
|
||||
*/
|
||||
private static boolean shouldMigrate(File f) {
|
||||
String xml = FileUtil.readTextFile(f.getAbsolutePath(), 100, true);
|
||||
String xml = FileUtil.readTextFile(f.getAbsolutePath(), 400, true);
|
||||
if (xml == null)
|
||||
return true;
|
||||
return xml.contains("class=\"org.mortbay.jetty.Server\"");
|
||||
return xml.contains("class=\"org.eclipse.jetty.server.nio.SelectChannelConnector\"");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,7 +442,7 @@ public class ZzzOTController implements ClientApp {
|
||||
}
|
||||
|
||||
/**
|
||||
* Migate a single jetty config file, replacing $PLUGIN as we copy it.
|
||||
* 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");
|
||||
@@ -348,18 +469,44 @@ public class ZzzOTController implements ClientApp {
|
||||
private void launchHelp(File pluginDir, Destination dest) {
|
||||
File fileTmpl = new File(pluginDir, "templates/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 b64 = dest.toBase64();
|
||||
try {
|
||||
String html = FileUtil.readTextFile(fileTmpl.getAbsolutePath(), 100, true);
|
||||
// help.html
|
||||
String html = FileUtil.readTextFile(fileTmpl.getAbsolutePath(), 500, true);
|
||||
if (html == null)
|
||||
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("$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);
|
||||
os.write(html.getBytes("UTF-8"));
|
||||
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);
|
||||
t.start();
|
||||
} catch (IOException ioe) {
|
||||
@@ -438,6 +585,31 @@ public class ZzzOTController implements ClientApp {
|
||||
|
||||
/////// 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 boolean shouldShowFooter() {
|
||||
return _showfooter;
|
||||
}
|
||||
|
||||
/** @since 0.17.0 */
|
||||
public static String footerText() {
|
||||
return _footertext;
|
||||
}
|
||||
|
||||
/** @since 0.19.0 */
|
||||
public static boolean allowFullScrape() {
|
||||
return _fullScrape;
|
||||
}
|
||||
|
||||
/** @since 0.12.0 */
|
||||
private synchronized void changeState(ClientAppState state) {
|
||||
_state = state;
|
||||
|
@@ -4,10 +4,24 @@
|
||||
<!-- precompiled servlets -->
|
||||
|
||||
<welcome-file-list>
|
||||
<welcome-file>index.html</welcome-file>
|
||||
<welcome-file>index.jsp</welcome-file>
|
||||
</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-name>net.i2p.zzzot.announce_jsp</servlet-name>
|
||||
<url-pattern>/announce.php</url-pattern>
|
||||
@@ -33,4 +47,10 @@
|
||||
<url-pattern>/scrape.php</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- BiglyBT -->
|
||||
<servlet-mapping>
|
||||
<servlet-name>net.i2p.zzzot.scrape_jsp</servlet-name>
|
||||
<url-pattern>/a/scrape</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
</web-app>
|
||||
|
@@ -29,6 +29,7 @@
|
||||
final int MAX_RESPONSES = 25;
|
||||
final boolean ALLOW_IP_MISMATCH = false;
|
||||
final boolean ALLOW_COMPACT_RESPONSE = true;
|
||||
final boolean ALLOW_NONCOMPACT_RESPONSE = false;
|
||||
|
||||
// so the chars will turn into bytes correctly
|
||||
request.setCharacterEncoding("ISO-8859-1");
|
||||
@@ -64,9 +65,15 @@
|
||||
if (xff != null || xfs != null) {
|
||||
fail = true;
|
||||
msg = "Non-I2P access denied";
|
||||
response.setStatus(403, msg);
|
||||
//response.setStatus(403, msg);
|
||||
response.setStatus(403);
|
||||
}
|
||||
|
||||
if (!compact && !ALLOW_NONCOMPACT_RESPONSE && !fail) {
|
||||
fail = true;
|
||||
msg = "non-compact responses unsupported";
|
||||
}
|
||||
|
||||
if (info_hash == null && !fail) {
|
||||
fail = true;
|
||||
msg = "no info hash";
|
||||
@@ -177,7 +184,7 @@
|
||||
} catch (NumberFormatException nfe) {};
|
||||
}
|
||||
|
||||
Map<String, Object> m = new HashMap(8);
|
||||
Map<String, Object> m = new HashMap<String, Object>(8);
|
||||
if (fail) {
|
||||
m.put("failure reason", msg);
|
||||
} else if ("stopped".equals(event)) {
|
||||
@@ -197,8 +204,7 @@
|
||||
// fixme same peer id, different dest
|
||||
Peer p = peers.get(pid);
|
||||
if (p == null) {
|
||||
ConcurrentMap<String, String> destCache = ZzzOTController.getDestCache();
|
||||
p = new Peer(pid.getData(), d, destCache);
|
||||
p = new Peer(pid.getData(), d);
|
||||
// don't add if spoofed
|
||||
if (matchIP) {
|
||||
Peer p2 = peers.putIfAbsent(pid, p);
|
||||
@@ -219,13 +225,13 @@
|
||||
// snark < 0.7.13 always wants a list
|
||||
m.put("peers", java.util.Collections.EMPTY_LIST);
|
||||
} else {
|
||||
List<Peer> peerlist = new ArrayList(peers.values());
|
||||
List<Peer> peerlist = new ArrayList<Peer>(peers.values());
|
||||
peerlist.remove(p); // them
|
||||
if (want < size - 1) {
|
||||
if (size > 150) {
|
||||
// If size is huge, use random iterator for efficiency
|
||||
List<Peer> rv = new ArrayList<Peer>(size);
|
||||
for (RandomIterator<Peer> iter = new RandomIterator(peerlist); iter.hasNext(); ) {
|
||||
for (RandomIterator<Peer> iter = new RandomIterator<Peer>(peerlist); iter.hasNext(); ) {
|
||||
rv.add(iter.next());
|
||||
}
|
||||
peerlist = rv;
|
||||
@@ -235,18 +241,23 @@
|
||||
}
|
||||
}
|
||||
if (compact) {
|
||||
// old experimental way - list of hashes
|
||||
//List<String> peerhashes = new ArrayList(peerlist.size());
|
||||
//for (Peer pe : peerlist) {
|
||||
// peerhashes.add(pe.getHash());
|
||||
//}
|
||||
// new way - one big string
|
||||
// one big string
|
||||
byte[] peerhashes = new byte[32 * peerlist.size()];
|
||||
for (int i = 0; i < peerlist.size(); i++)
|
||||
System.arraycopy(peerlist.get(i).getHash().getBytes("ISO-8859-1"), 0, peerhashes, i * 32, 32);
|
||||
System.arraycopy(peerlist.get(i).getHashBytes(), 0, peerhashes, i * 32, 32);
|
||||
m.put("peers", peerhashes);
|
||||
} else if (ALLOW_NONCOMPACT_RESPONSE) {
|
||||
// This requires the Peer entries to be Maps
|
||||
// so they can be bencoded, but we don't save
|
||||
// the full Destination any more, and Peer does not.
|
||||
// extend HashMap, to greatly reduce memory usage.
|
||||
// We could create a Map here with the b32 as the IP,
|
||||
// but that's nonstandard. So if non-compact is enabled,
|
||||
// don't return any peers.
|
||||
//m.put("peers", peerlist);
|
||||
m.put("peers", java.util.Collections.EMPTY_LIST);
|
||||
} else {
|
||||
m.put("peers", peerlist);
|
||||
// won't get here
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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>
|
||||
|
@@ -1,25 +1,100 @@
|
||||
<%@page import="net.i2p.zzzot.ZzzOTController,net.i2p.zzzot.Torrents" %>
|
||||
<%@page trimDirectiveWhitespaces="true"%>
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>ZzzOT</title>
|
||||
</head><body style="background-color: #000; color: #c30; font-size: 400%;">
|
||||
<p>
|
||||
zzzot
|
||||
<p>
|
||||
<meta charset="UTF-8">
|
||||
<noscript><meta http-equiv="refresh" content="300;url=."></noscript>
|
||||
<title><%=ZzzOTController.getSiteName()%> OPENTRACKER | STATS</title>
|
||||
<link href="/tracker.css" rel="stylesheet" type="text/css">
|
||||
<link rel="icon" type="image/png" href="/favicon.png">
|
||||
</head>
|
||||
<body id="stats">
|
||||
<div id="container">
|
||||
<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) {
|
||||
%>
|
||||
<table cellspacing="8">
|
||||
<tr><td>Torrents:<td align="right"><%=torrents.size()%>
|
||||
<tr><td>Peers:<td align="right"><%=torrents.countPeers()%>
|
||||
</table>
|
||||
<p id="totals">
|
||||
<b>Torrents:</b> <%=torrents.size()%><br>
|
||||
<b>Peers:</b> <%=torrents.countPeers()%><br>
|
||||
<%
|
||||
boolean udp = ZzzOTController.isUDPEnabled();
|
||||
if (udp) {
|
||||
%>
|
||||
<b>Total Announce Rate:</b>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
ZzzOT is not running
|
||||
<b>Announce Rate:</b>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
<%=String.format(java.util.Locale.US, "%.1f", ZzzOTController.getAnnounceRate())%> / minute<br>
|
||||
<b>Announce Interval:</b> <%=torrents.getInterval() / 60%> minutes<br>
|
||||
<%
|
||||
String host = ZzzOTController.b32();
|
||||
if (host != null) {
|
||||
%><b>Announce URL:</b> <a href="http://<%=host%>/a">http://<%=host%>/a</a><br><%
|
||||
}
|
||||
%>
|
||||
<b>UDP Announce Support:</b> <%=udp ? "yes" : "no"%><br>
|
||||
<%
|
||||
if (udp) {
|
||||
%>
|
||||
<b>UDP Announce Rate:</b> <%=String.format(java.util.Locale.US, "%.1f", ZzzOTController.getUDPAnnounceRate())%> / minute<br>
|
||||
<b>UDP Connection Lifetime:</b> <%=torrents.getUDPLifetime() / 60%> minutes<br>
|
||||
<%
|
||||
if (host != null) {
|
||||
int port = ZzzOTController.udpPort();
|
||||
%>
|
||||
<b>UDP Announce URL:</b> <a href="udp://<%=host%>:<%=port%>/"</a>udp://<%=host%>:<%=port%>/</a><br>
|
||||
<%
|
||||
}
|
||||
}
|
||||
%>
|
||||
</p>
|
||||
<%
|
||||
} else {
|
||||
%>
|
||||
<p id="initializing"><b><i>Initializing OpenTracker…</i></b></p>
|
||||
<%
|
||||
}
|
||||
%>
|
||||
<%
|
||||
boolean showfooter = ZzzOTController.shouldShowFooter();
|
||||
if (showfooter) {
|
||||
%>
|
||||
<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>
|
||||
</html>
|
||||
|
@@ -45,12 +45,17 @@
|
||||
if (xff != null || xfs != null) {
|
||||
fail = true;
|
||||
msg = "Non-I2P access denied";
|
||||
response.setStatus(403, msg);
|
||||
//response.setStatus(403, msg);
|
||||
response.setStatus(403);
|
||||
}
|
||||
|
||||
boolean all = info_hash == null;
|
||||
if (all && !ZzzOTController.allowFullScrape()) {
|
||||
fail = true;
|
||||
msg = "unsupported";
|
||||
}
|
||||
|
||||
Torrents torrents = ZzzOTController.getTorrents();
|
||||
Torrents torrents = fail ? null : ZzzOTController.getTorrents();
|
||||
if (torrents == null && !fail) {
|
||||
fail = true;
|
||||
msg = "tracker is down";
|
||||
@@ -67,31 +72,37 @@
|
||||
}
|
||||
|
||||
// build 3-level dictionary
|
||||
Map<String, Object> m = new HashMap(4);
|
||||
Map<String, Object> m = new HashMap<String, Object>(4);
|
||||
if (fail) {
|
||||
m.put("failure reason", msg);
|
||||
} else {
|
||||
List<InfoHash> ihList = new ArrayList();
|
||||
List<InfoHash> ihList = new ArrayList<InfoHash>();
|
||||
if (all)
|
||||
ihList.addAll(torrents.keySet());
|
||||
else
|
||||
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) {
|
||||
Peers peers = torrents.get(ihash);
|
||||
if (peers == null)
|
||||
continue;
|
||||
Map<String, Object> dict = new HashMap();
|
||||
Map<String, Object> dict = new HashMap<String, Object>();
|
||||
int size = peers.size();
|
||||
int seeds = peers.countSeeds();
|
||||
dict.put("complete", Integer.valueOf(seeds));
|
||||
dict.put("incomplete", Integer.valueOf(size - seeds));
|
||||
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);
|
||||
}
|
||||
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
|
||||
|
@@ -17,83 +17,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
String req = request.getHeader("X-Seedless");
|
||||
// extension for ease of eepget and browser
|
||||
if (req == null)
|
||||
req = request.getParameter("X-Seedless");
|
||||
// we should really put in our own b32
|
||||
String me = request.getHeader("Host");
|
||||
if (me == null)
|
||||
me = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.b32.i2p";
|
||||
// unused, we don't accept announces
|
||||
String him = request.getHeader("X-I2P-DestB32");
|
||||
if (him == null)
|
||||
him = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.b32.i2p";
|
||||
String xff = request.getHeader("X-Forwarded-For");
|
||||
String xfs = request.getHeader("X-Forwarded-Server");
|
||||
|
||||
response.setContentType("text/plain");
|
||||
response.setHeader("X-Seedless", him);
|
||||
|
||||
final int US_MINUTES = 360;
|
||||
final int PEER_MINUTES = 60;
|
||||
|
||||
if (xff != null || xfs != null) {
|
||||
String msg = "Non-I2P access denied";
|
||||
response.setStatus(403, msg);
|
||||
out.println(msg);
|
||||
} else if (req == null) {
|
||||
// probe
|
||||
out.println("tracker " + US_MINUTES);
|
||||
out.println("eepsite " + US_MINUTES);
|
||||
out.println("seedless " + US_MINUTES);
|
||||
} else if (req.startsWith("announce")) {
|
||||
out.println("thanks");
|
||||
} else if (req.startsWith("locate c2VlZGxlc")) { // locate b64(seedless)
|
||||
// ignore the search string, if any, in the request
|
||||
// us
|
||||
out.println(Base64.encode(me + ' ' + US_MINUTES + " tracker"));
|
||||
out.println(Base64.encode(me + ' ' + US_MINUTES + " seedless"));
|
||||
out.println(Base64.encode(me + ' ' + US_MINUTES + " eepsite"));
|
||||
} else if (req.startsWith("locate ZWVwc2l0Z")) { // locate b64(eepsite)
|
||||
// ignore the search string, if any, in the request
|
||||
// us
|
||||
out.println(Base64.encode(me + ' ' + US_MINUTES + " zzzot"));
|
||||
} else if (req.startsWith("locate dG9ycmVud")) { // locate b64(torrent)
|
||||
// all the peers
|
||||
Torrents torrents = ZzzOTController.getTorrents();
|
||||
if (torrents == null) {
|
||||
response.setStatus(503, "Down");
|
||||
return;
|
||||
}
|
||||
for (InfoHash ihash : torrents.keySet()) {
|
||||
Peers peers = torrents.get(ihash);
|
||||
if (peers == null)
|
||||
continue;
|
||||
for (Peer p : peers.values()) {
|
||||
// dest to b32
|
||||
String ip = (String) p.get("ip");
|
||||
if (ip.endsWith(".i2p"))
|
||||
ip = ip.substring(0, ip.length() - 4);
|
||||
String b32 = Base32.encode(SHA256Generator.getInstance().calculateHash(Base64.decode(ip)).getData()) + ".b32.i2p ";
|
||||
// service type
|
||||
String role;
|
||||
if (p.isSeed())
|
||||
role = "seed";
|
||||
else
|
||||
role = "leech";
|
||||
// spg wants UTF-8 but all we have is binary data, so hex it
|
||||
String ihs = DataHelper.toHexString(ihash.getData());
|
||||
String ids = DataHelper.toHexString((byte[])p.get("peer id"));
|
||||
out.println(Base64.encode(b32 + PEER_MINUTES + ihs + '\n' +
|
||||
ids + '\n' +
|
||||
role));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// error code
|
||||
response.setStatus(406, "Bad request");
|
||||
response.setStatus(406);
|
||||
out.println("SC_NOT_ACCEPTABLE");
|
||||
}
|
||||
|
||||
%>
|
Reference in New Issue
Block a user