36 Commits

Author SHA1 Message Date
zzz
22cf9e06ff 0.15.0 2017-03-11 18:23:14 +00:00
zzz
758c0865dc clean up warnings 2017-03-07 15:33:33 +00:00
zzz
ab14737fd1 For webapp also:
Pull in override.properties for bootclasspath
Bump to java 1.6
2016-08-14 15:38:58 +00:00
zzz
dd0d7ba6c9 Pull in override.properties for bootclasspath
Bump to java 1.6
2016-08-14 15:33:23 +00:00
zzz
d3ccdb5970 increase limits, set tag options 2016-06-13 16:34:35 +00:00
zzz
404ec7d720 update for 0.9.17 2016-06-13 16:34:18 +00:00
zzz
fa37064834 0.14.0 2015-05-28 13:00:07 +00:00
zzz
7cc2d97825 Remove use of deprecated SimpleScheduler
Remove static structures
Use RandomIterator for efficiency
untested
2015-04-16 22:33:50 +00:00
zzz
9ffef703b5 Increase startup delay, so it's after the eepsite Jetty,
so it doesn't create the static RolloverFileOutputStream Timer thread,
so PluginStarter doesn't think it's running after it stops.
2014-11-29 14:05:07 +00:00
zzz
5239dfb768 fix NPE on missing announce parameters 2014-11-26 20:52:58 +00:00
zzz
953533c6e3 0.12.0 2014-11-13 20:15:02 +00:00
zzz
2e0f7ae4bf Release all resources when shut down (requires 0.9.16-6 or higher)
log tweaks
2014-11-13 16:01:15 +00:00
zzz
17e5595dd0 Don't overwrite index.html and robots.txt in the update
More build and clean fixes
2014-11-12 23:10:19 +00:00
zzz
8350d009d4 Add zzzot.config file to set interval 2014-11-12 15:19:13 +00:00
zzz
a7225a2379 Fix parameter decoding for scrape also
Add caching for info hashes and peer ids; move from ByteArray to SDS
Stop cleaner when plugin stops
Move to the ClientApp interface, remove all static refs
Attempt to fix crash after update
2014-11-12 14:48:51 +00:00
zzz
8a9094cd8e Fix critical bug preventing announces from working
0.11.0
2014-11-11 14:08:43 +00:00
zzz
7f4394aa0f add max-jetty-version 2014-10-24 14:43:51 +00:00
zzz
38f38398ef switch Jetty to QTP 2014-10-24 13:01:28 +00:00
zzz
8317e3ce9a Change new install logging from ERROR to INFO
Move up warning about removing help file
2014-08-30 14:03:56 +00:00
zzz
c1f6a14367 add su3 url 2014-08-09 18:47:16 +00:00
zzz
7e36455b05 add support for building su3 plugins 2014-08-09 18:00:17 +00:00
zzz
a85aa6af31 more import cleanup 2014-08-09 16:25:44 +00:00
zzz
f33303f394 Use cached Destinations
Specify min I2P version of 0.9.9 for Destination.create()
2014-08-09 16:21:02 +00:00
zzz
ab882f8582 0.10.0:
Updates and migration for Jetty 7 (I2P 0.9.6)
2013-04-15 14:09:10 +00:00
zzz
ce7562c547 0.9.0:
Add cache-control directives
  Set max Jetty to 6.99999
2013-01-25 20:56:04 +00:00
zzz
52d8e26ffc add changelog 2012-03-11 14:40:20 +00:00
zzz
284d7a58d6 0.8 fix comment in jetty.xml 2012-03-10 23:55:21 +00:00
zzz
af259e7038 0.7:
- Port to Jetty 6
  - Replace QForwardHandler with RewriteHandler
2012-03-10 20:10:02 +00:00
zzz
0b177f317a Use ${ant.home}/lib/ant.jar instead of pulling ant.jar from Jetty 2012-01-23 16:51:52 +00:00
zzz
0c43324657 merge of '8a5859e79c4736b333c07be2c08b196a9ea8f712'
and 'a286e484b92462d3099a12a33550ab2365d61bb2'
2012-01-23 16:51:23 +00:00
zzz
eeeedee076 version number bump, missed in last checkin 2012-01-23 16:49:47 +00:00
d1165b9f9a typo fixes 2012-01-16 23:41:04 +00:00
zzz
a6157ee8f5 set max jetty version 2011-12-31 15:13:56 +00:00
zzz
2a795f3d8a add throttle options 2011-09-12 20:36:04 +00:00
zzz
7a13998082 stub out announce-to-seedless 2011-09-12 19:57:43 +00:00
zzz
93a8e5bfd5 seedless fixes, untested 2011-09-12 15:46:19 +00:00
30 changed files with 1823 additions and 502 deletions

82
CHANGES.txt Normal file
View File

@ -0,0 +1,82 @@
0.15.0
2017-03-11
Increase default limits, set I2CP tag options
Update eepsite configuration for Jetty 9 (I2P 0.9.30)
0.14.0
2015-05-28
Remove use of deprecated SimpleScheduler (ticket #1523)
Remove static structures
Use RandomIterator for efficiency
0.13.0
2014-11-29
Fix NPE on missing announce parameters
Increase startup delay
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)
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
0.10.0
2013-04-14
Updates and migration for Jetty 7 (I2P 0.9.6)
0.9.0
2013-01-25
Add cache-control directives
Set max Jetty to 6.99999
0.8
2012-03-10
fix comment in jetty.xml
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
0.5
2010-07-11
Final compact response format
0.4
2010-07-09
Compact request/response support - may not be final format
Fix NPE if no ip parameter
0.3
2010-04-13
Verify dest
Add xfs check
0.2
2010-03-23
Cache b64 dest strings
Help typo fix (thx duck)
Lots of seedless fixes
Build cleanups
0.1
2010-03-23

View File

@ -1,7 +1,7 @@
This is a very simple in-memory open tracker, wrapped into an I2P plugin.
The plugin starts a new http serer tunnel, eepsite, and Jetty server running at port 7662.
The tracker status is available at http://127.0.0.1:7661/tracker/ .
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

View File

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<project basedir="." default="all" name="zzzot">
<property file="override.properties"/>
<target name="all" depends="clean,plugin" />
@ -8,10 +9,14 @@
</target>
<target name="plugin" depends="war">
<delete file="plugin/i2ptunnel.config" />
<delete>
<!-- in installer but not update -->
<fileset dir="plugin/" includes="i2ptunnel.config zzzot.config eepsite/docroot/index.html eepsite/docroot/robots.txt" />
</delete>
<delete dir="plugin/eepsite/docroot/torrents/" />
<!-- get version number -->
<buildnumber file="scripts/build.number" />
<property name="release.number" value="0.5" />
<property name="release.number" value="0.15.0" />
<!-- make the update xpi2p -->
<!-- this contains everything except i2ptunnel.config -->
@ -34,18 +39,31 @@
<arg value="plugin/eepsite/webapps/tracker.war.pack" />
<arg value="src/build/tracker.war.jar" />
</exec>
<exec executable="scripts/makeplugin.sh" failonerror="true" >
<input message="Enter su3 signing key password:" addproperty="release.password.su3" />
<fail message="You must enter a password." >
<condition>
<equals arg1="${release.password.su3}" arg2=""/>
</condition>
</fail>
<!-- this will fail if no su3 keys exist, as it needs the password twice -->
<exec executable="scripts/makeplugin.sh" inputstring="${release.password.su3}" failonerror="true" >
<arg value="plugin" />
</exec>
<move file="zzzot.xpi2p" tofile="zzzot-update.xpi2p" overwrite="true" />
<move file="zzzot.su3" tofile="zzzot-update.su3" overwrite="true" />
<!-- make the install xpi2p -->
<copy file="scripts/i2ptunnel.config" todir="plugin/" overwrite="true" />
<copy file="scripts/plugin.config" todir="plugin/" overwrite="true" />
<!-- Files in installer but not update. Be sure to Add to delete fileset above and clean target below -->
<copy file="scripts/i2ptunnel.config" todir="plugin/" overwrite="true" />
<copy file="scripts/zzzot.config" todir="plugin/" overwrite="true" />
<copy file="scripts/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">
<arg value="version=${release.number}-b${build.number}" />
</exec>
<exec executable="scripts/makeplugin.sh" failonerror="true" >
<exec executable="scripts/makeplugin.sh" inputstring="${release.password.su3}" failonerror="true" >
<arg value="plugin" />
</exec>
</target>
@ -56,12 +74,19 @@
<ant dir="src" target="clean" />
<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/LICENSE.txt" />
<delete file="plugin/README.txt" />
<delete file="zzzot.xpi2p" />
<delete file="zzzot-update.xpi2p" />
<delete file="zzzot.su3" />
<delete file="zzzot-update.su3" />
<delete file="plugin.zip" />
<delete dir="plugin/eepsite/docroot/torrents/" />
</target>
</project>

View File

@ -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=15
clientApp.0.delay=40
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

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!--
Configure a custom context for the eepsite.
This context contains only a ServletContextHandler with a default servlet
to serve static html files and images.
-->
<Configure class="org.eclipse.jetty.servlet.ServletContextHandler">
<Set name="contextPath">/</Set>
<Set name="resourceBase">$PLUGIN/eepsite/docroot/</Set>
<Call name="setInitParameter">
<Arg>cacheControl</Arg>
<Arg>max-age=3600,public</Arg>
</Call>
<Call name="addServlet">
<Arg>org.eclipse.jetty.servlet.DefaultServlet</Arg>
<Arg>/</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!--
Configure a custom context for the eepsite.
* CGI Servlet.
*
* The cgi bin directory can be set with the "cgibinResourceBase" init parameter
* or it will default to the resource base of the context.
*
* The "commandPrefix" init parameter may be used to set a prefix to all
* commands passed to exec. This can be used on systems that need assistance to
* execute a particular file type. For example on windows this can be set to
* "perl" so that perl scripts are executed.
*
* The "Path" init param is passed to the exec environment as PATH. Note: Must
* be run unpacked somewhere in the filesystem.
*
* Any initParameter that starts with ENV_ is used to set an environment
* variable with the name stripped of the leading ENV_ and using the init
* parameter value.
-->
<Configure class="org.eclipse.jetty.servlet.ServletContextHandler">
<Set name="contextPath">/cgi-bin</Set>
<Set name="resourceBase">$PLUGIN/eepsite/cgi-bin/</Set>
<Call name="setInitParameter">
<Arg>Path</Arg>
<Arg>/usr/local/bin:/bin:/usr/bin</Arg>
</Call>
<Call name="addServlet">
<Arg>org.eclipse.jetty.servlets.CGI</Arg>
<Arg>/</Arg>
</Call>
</Configure>

View File

@ -0,0 +1,21 @@
#
# This file defines users passwords and roles for a HashUserRealm
#
# The format is
# <username>: <password>[,<rolename> ...]
#
# Passwords may be clear text, obfuscated or checksummed. The class
# org.mortbay.util.Password should be used to generate obfuscated
# passwords or password checksums
#
# If DIGEST Authentication is used, the password must be in a recoverable
# format, either plain text or OBF:.
#
jetty: MD5:164c88b302622e17050af52c89945d44,user
admin: CRYPT:ad1ks..kc.1Ug,server-administrator,content-administrator,admin
other: OBF:1xmk1w261u9r1w1c1xmq
plain: plain
user: password
# This entry is for digest auth. The credential is a MD5 hash of username:realmname:password
digest: MD5:6e120743ad67abfbc385bc2bb754e297

View File

@ -0,0 +1,408 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- ===================================================================== -->
<!-- This file contains the default descriptor for web applications. -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- The intent of this descriptor is to include jetty specific or common -->
<!-- configuration for all webapps. If a context has a webdefault.xml -->
<!-- descriptor, it is applied before the contexts own web.xml file -->
<!-- -->
<!-- A context may be assigned a default descriptor by: -->
<!-- + Calling WebApplicationContext.setDefaultsDescriptor -->
<!-- + Passed an arg to addWebApplications -->
<!-- -->
<!-- This file is used both as the resource within the jetty.jar (which is -->
<!-- used as the default if no explicit defaults descriptor is set) and it -->
<!-- is copied to the etc directory of the Jetty distro and explicitly -->
<!-- by the jetty.xml file. -->
<!-- -->
<!-- ===================================================================== -->
<web-app
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
metadata-complete="true"
version="2.5">
<description>
Default web.xml file.
This file is applied to a Web application before it's own WEB_INF/web.xml file
</description>
<!-- ==================================================================== -->
<!-- Context params to control Session Cookies -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- UNCOMMENT TO ACTIVATE
<context-param>
<param-name>org.eclipse.jetty.servlet.SessionDomain</param-name>
<param-value>127.0.0.1</param-value>
</context-param>
<context-param>
<param-name>org.eclipse.jetty.servlet.SessionPath</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>org.eclipse.jetty.servlet.MaxAge</param-name>
<param-value>-1</param-value>
</context-param>
-->
<context-param>
<param-name>org.eclipse.jetty.webapp.NoTLDJarPattern</param-name>
<param-value>start.jar|ant-.*\.jar|dojo-.*\.jar|jetty-.*\.jar|jsp-api-.*\.jar|junit-.*\.jar|servlet-api-.*\.jar|dnsns\.jar|rt\.jar|jsse\.jar|tools\.jar|sunpkcs11\.jar|sunjce_provider\.jar|xerces.*\.jar</param-value>
</context-param>
<!-- ==================================================================== -->
<!-- The default servlet. -->
<!-- This servlet, normally mapped to /, provides the handling for static -->
<!-- content, OPTIONS and TRACE methods for the context. -->
<!-- The following initParameters are supported: -->
<!-- -->
<!-- acceptRanges If true, range requests and responses are -->
<!-- supported -->
<!-- -->
<!-- dirAllowed If true, directory listings are returned if no -->
<!-- welcome file is found. Else 403 Forbidden. -->
<!-- -->
<!-- welcomeServlets If true, attempt to dispatch to welcome files -->
<!-- that are servlets, if no matching static -->
<!-- resources can be found. -->
<!-- -->
<!-- redirectWelcome If true, redirect welcome file requests -->
<!-- else use request dispatcher forwards -->
<!-- -->
<!-- gzip If set to true, then static content will be served-->
<!-- as gzip content encoded if a matching resource is -->
<!-- found ending with ".gz" -->
<!-- -->
<!-- resoureBase Can be set to replace the context resource base -->
<!-- -->
<!-- relativeResourceBase -->
<!-- Set with a pathname relative to the base of the -->
<!-- servlet context root. Useful for only serving -->
<!-- static content from only specific subdirectories. -->
<!-- -->
<!-- useFileMappedBuffer -->
<!-- If set to true (the default), a memory mapped -->
<!-- file buffer will be used to serve static content -->
<!-- when using an NIO connector. Setting this value -->
<!-- to false means that a direct buffer will be used -->
<!-- instead. If you are having trouble with Windows -->
<!-- file locking, set this to false. -->
<!-- -->
<!-- cacheControl If set, all static content will have this value -->
<!-- set as the cache-control header. -->
<!-- -->
<!-- maxCacheSize Maximum size of the static resource cache -->
<!-- -->
<!-- maxCachedFileSize Maximum size of any single file in the cache -->
<!-- -->
<!-- maxCachedFiles Maximum number of files in the cache -->
<!-- -->
<!-- cacheType "nio", "bio" or "both" to determine the type(s) -->
<!-- of resource cache. A bio cached buffer may be used-->
<!-- by nio but is not as efficient as a nio buffer. -->
<!-- An nio cached buffer may not be used by bio. -->
<!-- -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
<init-param>
<param-name>acceptRanges</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>dirAllowed</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>welcomeServlets</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>redirectWelcome</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>maxCacheSize</param-name>
<param-value>256000000</param-value>
</init-param>
<init-param>
<param-name>maxCachedFileSize</param-name>
<param-value>10000000</param-value>
</init-param>
<init-param>
<param-name>maxCachedFiles</param-name>
<param-value>1000</param-value>
</init-param>
<init-param>
<param-name>cacheType</param-name>
<param-value>both</param-value>
</init-param>
<init-param>
<param-name>gzip</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>useFileMappedBuffer</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>cacheControl</param-name>
<param-value>max-age=3600,public</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
<!-- ==================================================================== -->
<!-- JSP Servlet -->
<!-- This is the jasper JSP servlet from the jakarta project -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- The JSP page compiler and execution servlet, which is the mechanism -->
<!-- used by Glassfish to support JSP pages. Traditionally, this servlet -->
<!-- is mapped to URL patterh "*.jsp". This servlet supports the -->
<!-- following initialization parameters (default values are in square -->
<!-- brackets): -->
<!-- -->
<!-- checkInterval If development is false and reloading is true, -->
<!-- background compiles are enabled. checkInterval -->
<!-- is the time in seconds between checks to see -->
<!-- if a JSP page needs to be recompiled. [300] -->
<!-- -->
<!-- compiler Which compiler Ant should use to compile JSP -->
<!-- pages. See the Ant documenation for more -->
<!-- information. [javac] -->
<!-- -->
<!-- classdebuginfo Should the class file be compiled with -->
<!-- debugging information? [true] -->
<!-- -->
<!-- classpath What class path should I use while compiling -->
<!-- generated servlets? [Created dynamically -->
<!-- based on the current web application] -->
<!-- Set to ? to make the container explicitly set -->
<!-- this parameter. -->
<!-- -->
<!-- development Is Jasper used in development mode (will check -->
<!-- for JSP modification on every access)? [true] -->
<!-- -->
<!-- enablePooling Determines whether tag handler pooling is -->
<!-- enabled [true] -->
<!-- -->
<!-- fork Tell Ant to fork compiles of JSP pages so that -->
<!-- a separate JVM is used for JSP page compiles -->
<!-- from the one Tomcat is running in. [true] -->
<!-- -->
<!-- ieClassId The class-id value to be sent to Internet -->
<!-- Explorer when using <jsp:plugin> tags. -->
<!-- [clsid:8AD9C840-044E-11D1-B3E9-00805F499D93] -->
<!-- -->
<!-- javaEncoding Java file encoding to use for generating java -->
<!-- source files. [UTF-8] -->
<!-- -->
<!-- keepgenerated Should we keep the generated Java source code -->
<!-- for each page instead of deleting it? [true] -->
<!-- -->
<!-- logVerbosityLevel The level of detailed messages to be produced -->
<!-- by this servlet. Increasing levels cause the -->
<!-- generation of more messages. Valid values are -->
<!-- FATAL, ERROR, WARNING, INFORMATION, and DEBUG. -->
<!-- [WARNING] -->
<!-- -->
<!-- mappedfile Should we generate static content with one -->
<!-- print statement per input line, to ease -->
<!-- debugging? [false] -->
<!-- -->
<!-- -->
<!-- reloading Should Jasper check for modified JSPs? [true] -->
<!-- -->
<!-- suppressSmap Should the generation of SMAP info for JSR45 -->
<!-- debugging be suppressed? [false] -->
<!-- -->
<!-- dumpSmap Should the SMAP info for JSR45 debugging be -->
<!-- dumped to a file? [false] -->
<!-- False if suppressSmap is true -->
<!-- -->
<!-- scratchdir What scratch directory should we use when -->
<!-- compiling JSP pages? [default work directory -->
<!-- for the current web application] -->
<!-- -->
<!-- tagpoolMaxSize The maximum tag handler pool size [5] -->
<!-- -->
<!-- xpoweredBy Determines whether X-Powered-By response -->
<!-- header is added by generated servlet [false] -->
<!-- -->
<!-- If you wish to use Jikes to compile JSP pages: -->
<!-- Set the init parameter "compiler" to "jikes". Define -->
<!-- the property "-Dbuild.compiler.emacs=true" when starting Jetty -->
<!-- to cause Jikes to emit error messages in a format compatible with -->
<!-- Jasper. -->
<!-- If you get an error reporting that jikes can't use UTF-8 encoding, -->
<!-- try setting the init parameter "javaEncoding" to "ISO-8859-1". -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<servlet id="jsp">
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
<init-param>
<param-name>logVerbosityLevel</param-name>
<param-value>DEBUG</param-value>
</init-param>
<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<!--
<init-param>
<param-name>classpath</param-name>
<param-value>?</param-value>
</init-param>
-->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspf</url-pattern>
<url-pattern>*.jspx</url-pattern>
<url-pattern>*.xsp</url-pattern>
<url-pattern>*.JSP</url-pattern>
<url-pattern>*.JSPF</url-pattern>
<url-pattern>*.JSPX</url-pattern>
<url-pattern>*.XSP</url-pattern>
</servlet-mapping>
<!-- ==================================================================== -->
<!-- Dynamic Servlet Invoker. -->
<!-- This servlet invokes anonymous servlets that have not been defined -->
<!-- in the web.xml or by other means. The first element of the pathInfo -->
<!-- of a request passed to the envoker is treated as a servlet name for -->
<!-- an existing servlet, or as a class name of a new servlet. -->
<!-- This servlet is normally mapped to /servlet/* -->
<!-- This servlet support the following initParams: -->
<!-- -->
<!-- nonContextServlets If false, the invoker can only load -->
<!-- servlets from the contexts classloader. -->
<!-- This is false by default and setting this -->
<!-- to true may have security implications. -->
<!-- -->
<!-- verbose If true, log dynamic loads -->
<!-- -->
<!-- * All other parameters are copied to the -->
<!-- each dynamic servlet as init parameters -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Uncomment for dynamic invocation
<servlet>
<servlet-name>invoker</servlet-name>
<servlet-class>org.eclipse.jetty.servlet.Invoker</servlet-class>
<init-param>
<param-name>verbose</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>nonContextServlets</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>dynamicParam</param-name>
<param-value>anyValue</param-value>
</init-param>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping> <servlet-name>invoker</servlet-name> <url-pattern>/servlet/*</url-pattern> </servlet-mapping>
-->
<!-- ==================================================================== -->
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- ==================================================================== -->
<!-- Default MIME mappings -->
<!-- The default MIME mappings are provided by the mime.properties -->
<!-- resource in the org.mortbay.jetty.jar file. Additional or modified -->
<!-- mappings may be specified here -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- UNCOMMENT TO ACTIVATE
<mime-mapping>
<extension>mysuffix</extension>
<mime-type>mymime/type</mime-type>
</mime-mapping>
-->
<!-- ==================================================================== -->
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- ==================================================================== -->
<locale-encoding-mapping-list>
<locale-encoding-mapping><locale>ar</locale><encoding>ISO-8859-6</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>be</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>bg</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ca</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>cs</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>da</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>de</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>el</locale><encoding>ISO-8859-7</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>en</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>es</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>et</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>fi</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>fr</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>hr</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>hu</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>is</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>it</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>iw</locale><encoding>ISO-8859-8</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ja</locale><encoding>Shift_JIS</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ko</locale><encoding>EUC-KR</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>lt</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>lv</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>mk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>nl</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>no</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>pl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>pt</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ro</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>ru</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sh</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sk</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sl</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sq</locale><encoding>ISO-8859-2</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sr</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>sv</locale><encoding>ISO-8859-1</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>tr</locale><encoding>ISO-8859-9</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>uk</locale><encoding>ISO-8859-5</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>zh</locale><encoding>GB2312</encoding></locale-encoding-mapping>
<locale-encoding-mapping><locale>zh_TW</locale><encoding>Big5</encoding></locale-encoding-mapping>
</locale-encoding-mapping-list>
<security-constraint>
<web-resource-collection>
<web-resource-name>Disable TRACE</web-resource-name>
<url-pattern>/</url-pattern>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
</web-app>

View File

@ -10,6 +10,9 @@ This link is also at the top of your router console when ZzzOT is running.
<p>Report bugs or add comments on
<a href="http://zzz.i2p//forums/16">the plugin forum on zzz.i2p</a>.
<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>
<h3>Eepsite Key and Helpful Hints for I2P</h3>
<p>Your Base 32 address is <a href="http://$B32/">$B32</a>.
@ -20,7 +23,7 @@ This link is also at the top of your router console when ZzzOT is running.
<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 is you wish to have additional content on your eepsite.
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>
@ -47,8 +50,6 @@ This link is also at the top of your router console when ZzzOT is running.
If your tracker gets over 1000 peers, you will probably want to increase the number of tunnels.
<p>The Jetty webserver port is 7662. If you must change it, edit jetty.xml, i2ptunnel.config, and plugins.config
in the directory $PLUGIN. Then stop and restart the plugin.
<p>This help file is $PLUGIN/eepsite/docroot/help.html, you should probably move it
outside of the document root before you announce your eepsite as it may contain your user name.
<p>As you probably know, an open tracker does not require torrents to be registered,
and it does not host torrent files. You can, however, host torrent files elsewhere on
the eepsite, for example at <a href="http://$B32/torrents/">/torrents</a>.

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure 1.2//EN" "http://jetty.mortbay.org/configure_1_2.dtd">
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure.dtd">
<!-- ========================================================================= -->
<!-- This file configures the Jetty server. -->
@ -8,183 +8,340 @@
<!-- Commonly changed settings: -->
<!-- * host: Change 127.0.0.1 to 0.0.0.0 in the addListener section -->
<!-- to access the server directly (bypassing i2p) -->
<!-- from other computers. The included version of Jetty has -->
<!-- been patched to allow IPv6 addresses as well, -->
<!-- enclosed in brackets e.g. [::1] -->
<!-- * port: Default 7662 in the addListener section -->
<!-- * threads: Raise MaxThreads in the addListener section -->
<!-- from other computers. -->
<!-- * port: Default 7662 in the addConnector section -->
<!-- * docroot: Change the ResourceBase in the contexts/base-context.xml file -->
<!-- to serve files from a different location. -->
<!-- * threads: Raise maximumPoolSize in the ThreadPool section -->
<!-- if you have a high-traffic site and get a lot of warnings. -->
<!-- -->
<!-- I2P uses Jetty 5.1.15. We have no plans to upgrade to Jetty 6, due to -->
<!-- the significant changes in the API. If you need web server features not -->
<!-- found in Jetty 5, you may install and run Jetty 6 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 5 server for your -->
<!-- eepsite on http://127.0.0.1/configclients.jsp . -->
<!-- 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 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. -->
<!-- Use the log override org.eclipse.jetty.server.Server to adjust the log level. -->
<!-- -->
<!-- Note that the XML encoding for this file is UTF-8. -->
<!-- -->
<!-- Jetty errors and warnings will appear in wrapper.log, check there -->
<!-- to diagnose problems. -->
<!-- ========================================================================= -->
<!-- =============================================================== -->
<!-- Configure the Jetty Server -->
<!-- -->
<!-- Documentation of this file format can be found at: -->
<!-- http://docs.codehaus.org/display/JETTY/jetty.xml -->
<!-- -->
<!-- =============================================================== -->
<Configure class="org.mortbay.jetty.Server">
<!-- =============================================================== -->
<!-- Configure the Request Listeners -->
<!-- =============================================================== -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add and configure a HTTP listener to port 8080 -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<Call name="addListener">
<Configure id="Server" class="org.eclipse.jetty.server.Server">
<!-- =========================================================== -->
<!-- Server Thread Pool -->
<!-- =========================================================== -->
<Arg>
<New class="org.mortbay.http.SocketListener">
<!--
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="org.mortbay.util.InetAddrPort">
<New class="java.util.concurrent.LinkedBlockingQueue">
<Arg type="int">50</Arg>
</New>
</Arg>
<Set name="minThreads">4</Set>
<Set name="maxThreads">20</Set>
<Set name="idleTimeout">60000</Set>
<Set name="daemon">true</Set>
<Set name="name">Zzzot Jetty</Set>
</New>
</Arg>
<!-- =========================================================== -->
<!-- Set connectors -->
<!-- =========================================================== -->
<!-- One of each type! -->
<!-- =========================================================== -->
<!-- Use this connector for many frequently idle connections
and for threadless continuations.
Not recommended on Java 5 - comment this out, and uncomment the
SocketConnector below.
Do not use for gij or JamVM - comment this out, and uncomment the
SocketConnector below.
-->
<Call name="addConnector">
<Arg>
<New class="org.eclipse.jetty.server.ServerConnector">
<Arg><Ref id="Server" /></Arg>
<Arg type="int">1</Arg> <!-- number of acceptors -->
<Arg type="int">0</Arg> <!-- default number of selectors -->
<Arg>
<Array type="org.eclipse.jetty.server.ConnectionFactory"> <!-- varargs so we need an array -->
<Item>
<New class="org.eclipse.jetty.server.HttpConnectionFactory">
<Arg>
<New class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="sendServerVersion">false</Set>
<Set name="sendDateHeader">true</Set>
</New>
</Arg>
</New>
</Item>
</Array>
</Arg>
<Set name="host">127.0.0.1</Set>
<Set name="port">7662</Set>
<Set name="idleTimeout">600000</Set>
</New>
</Arg>
</Call>
<!-- =========================================================== -->
<!-- Set up global session ID manager -->
<!-- =========================================================== -->
<!--
<Set name="sessionIdManager">
<New class="org.eclipse.jetty.server.session.HashSessionIdManager">
<Set name="workerName">node1</Set>
</New>
</Set>
-->
<!-- =========================================================== -->
<!-- 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">
<Set name="rewriteRequestURI">true</Set>
<Set name="rewritePathInfo">false</Set>
<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">/a</Set>
<Set name="replacement">/tracker/announce.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/announce</Set>
<Set name="replacement">/tracker/announce.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/announce.jsp</Set>
<Set name="replacement">/tracker/announce.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/announce.php</Set>
<Set name="replacement">/tracker/announce.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/scrape</Set>
<Set name="replacement">/tracker/scrape.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/scrape.jsp</Set>
<Set name="replacement">/tracker/scrape.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/scrape.php</Set>
<Set name="replacement">/tracker/scrape.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/Seedless</Set>
<Set name="replacement">/tracker/seedless.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/Seedless/</Set>
<Set name="replacement">/tracker/seedless.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/Seedless/index.jsp</Set>
<Set name="replacement">/tracker/seedless.jsp</Set>
</New>
</Item>
<Item>
<New class="org.eclipse.jetty.rewrite.handler.RewritePatternRule">
<Set name="pattern">/Seedless/seedless</Set>
<Set name="replacement">/tracker/seedless.jsp</Set>
</New>
</Item>
</Array>
</Set>
<Set name="handler">
<New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
<Set name="handlers">
<Array type="org.eclipse.jetty.server.Handler">
<Item>
<New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
</Item>
<Item>
<New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
</Item>
<Item>
<New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
</Item>
</Array>
</Set>
</New>
</Set>
</New>
</Set>
<!-- =============================================================== -->
<!-- Create the deployment manager -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- The deplyment manager handles the lifecycle of deploying web -->
<!-- applications. Apps are provided by instances of the -->
<!-- AppProvider interface. Typically these are provided by -->
<!-- one or more of: -->
<!-- jetty-webapps.xml - monitors webapps for wars and dirs -->
<!-- jetty-contexts.xml - monitors contexts for context xml -->
<!-- jetty-templates.xml - monitors contexts and templates -->
<!-- =============================================================== -->
<Call name="addBean">
<Arg>
<New id="DeploymentManager" class="org.eclipse.jetty.deploy.DeploymentManager">
<Set name="contexts">
<Ref id="Contexts" />
</Set>
<Call name="setContextAttribute">
<Arg>org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern</Arg>
<Arg>.*/.*jsp-api-[^/]*\.jar$|.*/.*jsp-[^/]*\.jar$|.*/.*taglibs[^/]*\.jar$</Arg>
</Call>
</New>
</Arg>
</Call>
<!-- =========================================================== -->
<!-- Configure the context deployer -->
<!-- A context deployer will deploy contexts described in -->
<!-- configuration files discovered in a directory. -->
<!-- The configuration directory can be scanned for hot -->
<!-- deployments at the configured scanInterval. -->
<!-- -->
<!-- This deployer is configured to deploy contexts configured -->
<!-- in the $JETTY_HOME/contexts directory -->
<!-- -->
<!-- =========================================================== -->
<Ref id="DeploymentManager">
<Call name="addAppProvider">
<Arg>
<New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
<Set name="monitoredDirName">$PLUGIN/contexts</Set>
<Set name="scanInterval">120</Set>
</New>
</Arg>
<Set name="MinThreads">3</Set>
<Set name="MaxThreads">10</Set>
<Set name="MaxIdleTimeMs">60000</Set>
<Set name="LowResourcePersistTimeMs">1000</Set>
<Set name="ConfidentialPort">8443</Set>
<Set name="IntegralPort">8443</Set>
<Set name="PoolName">main</Set>
</New>
</Arg>
</Call>
</Call>
</Ref>
<!-- =========================================================== -->
<!-- Configure the webapp deployer. -->
<!-- A webapp deployer will deploy standard webapps discovered -->
<!-- in a directory at startup, without the need for additional -->
<!-- configuration files. It does not support hot deploy or -->
<!-- non standard contexts (see ContextDeployer above). -->
<!-- -->
<!-- This deployer is configured to deploy webapps from the -->
<!-- $JETTY_HOME/webapps directory -->
<!-- -->
<!-- Normally only one type of deployer need be used. -->
<!-- -->
<!-- =========================================================== -->
<Ref id="DeploymentManager">
<Call id="webappprovider" name="addAppProvider">
<Arg>
<New class="org.eclipse.jetty.deploy.providers.WebAppProvider">
<Set name="monitoredDirName">$PLUGIN/eepsite/webapps</Set>
<Set name="parentLoaderPriority">false</Set>
<Set name="extractWars">false</Set>
<Set name="defaultsDescriptor">$PLUGIN/etc/webdefault.xml</Set>
</New>
</Arg>
</Call>
</Ref>
<!-- =============================================================== -->
<!-- Configure the Contexts -->
<!-- =============================================================== -->
<!-- =========================================================== -->
<!-- Configure Authentication Realms -->
<!-- Realms may be configured for the entire server here, or -->
<!-- they can be configured for a specific web app in a context -->
<!-- configuration (see $(jetty.home)/contexts/test.xml for an -->
<!-- example). -->
<!-- =========================================================== -->
<!-- UNCOMMENT TO ACTIVATE
<Set name="UserRealms">
<Array type="org.eclipse.jetty.security.LoginService">
<Item>
<New class="org.eclipse.jetty.security.HashLoginService">
<Set name="name">Test Realm</Set>
<Set name="config">$PLUGIN/etc/realm.properties</Set>
<Set name="refreshInterval">0</Set>
</New>
</Item>
</Array>
</Set>
-->
<!-- =========================================================== -->
<!-- Configure Request Log -->
<!-- Request logs may be configured for the entire server here, -->
<!-- or they can be configured for a specific web app in a -->
<!-- contexts configuration (see $(jetty.home)/contexts/test.xml -->
<!-- for an example). -->
<!-- =========================================================== -->
<Ref id="RequestLog">
<Set name="requestLog">
<New id="RequestLogImpl" class="net.i2p.jetty.I2PRequestLog">
<Set name="filename">$PLUGIN/eepsite/logs/yyyy_mm_dd.request.log</Set>
<Set name="filenameDateFormat">yyyy_MM_dd</Set>
<Set name="retainDays">90</Set>
<Set name="append">true</Set>
<Set name="extended">false</Set>
<Set name="logCookies">false</Set>
<Set name="LogTimeZone">GMT</Set>
</New>
</Set>
</Ref>
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<!-- Add a all web application within the webapps directory. -->
<!-- + No virtual host specified -->
<!-- + Look in the webapps directory relative to jetty.home or . -->
<!-- + Use the default webdefault.xml in jetty's install -->
<!-- + Upack the war file -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<Set name="rootWebApp">root</Set>
<Call name="addWebApplications">
<Arg></Arg>
<Arg>$PLUGIN/eepsite/webapps/</Arg>
<Arg></Arg>
<Arg type="boolean">true</Arg>
</Call>
<Call name="addContext">
<Arg>
<New class="org.mortbay.http.HttpContext">
<Set name="contextPath">/</Set>
<Set name="resourceBase">$PLUGIN/eepsite/docroot</Set>
<Call name="addHandler">
<Arg>
<New class="org.mortbay.http.handler.ResourceHandler">
<Set name="redirectWelcome">FALSE</Set>
</New>
</Arg>
</Call>
<!-- This custom handler is like Jetty's ForwardHandler,
- but it passes CGI query parameters through.
- Note that it is somewhat misnamed - it does NOT
- return a 301/302, it handles the request directly.
-->
<Call name="addHandler">
<Arg>
<New class="net.i2p.zzzot.QForwardHandler">
<Call name="setHandleQueries">
<Arg type="boolean">true</Arg>
</Call>
<!-- Forward announce requests to /tracker/announce.jsp -->
<Call name="addForward">
<Arg>/a</Arg>
<Arg>/tracker/announce.jsp</Arg>
</Call>
<Call name="addForward">
<Arg>/announce</Arg>
<Arg>/tracker/announce.jsp</Arg>
</Call>
<Call name="addForward">
<Arg>/announce.jsp</Arg>
<Arg>/tracker/announce.jsp</Arg>
</Call>
<Call name="addForward">
<Arg>/announce.php</Arg>
<Arg>/tracker/announce.jsp</Arg>
</Call>
<!-- Forward scrape requests to /tracker/scrape.jsp -->
<Call name="addForward">
<Arg>/scrape</Arg>
<Arg>/tracker/scrape.jsp</Arg>
</Call>
<Call name="addForward">
<Arg>/scrape.jsp</Arg>
<Arg>/tracker/scrape.jsp</Arg>
</Call>
<Call name="addForward">
<Arg>/scrape.php</Arg>
<Arg>/tracker/scrape.jsp</Arg>
</Call>
<!-- Forward Seedless requests to /tracker/seedless.jsp -->
<Call name="addForward">
<Arg>/Seedless</Arg>
<Arg>/tracker/seedless.jsp</Arg>
</Call>
<Call name="addForward">
<Arg>/Seedless/</Arg>
<Arg>/tracker/seedless.jsp</Arg>
</Call>
<Call name="addForward">
<Arg>/Seedless/index.jsp</Arg>
<Arg>/tracker/seedless.jsp</Arg>
</Call>
<Call name="addForward">
<Arg>/Seedless/seedless</Arg>
<Arg>/tracker/seedless.jsp</Arg>
</Call>
</New>
</Arg>
</Call>
</New>
</Arg>
</Call>
<Call name="addContext">
<Arg>/cgi-bin/*</Arg>
<Set name="ResourceBase">$PLUGIN/eepsite/cgi-bin</Set>
<Call name="addServlet">
<Arg>Common Gateway Interface</Arg>
<Arg>/</Arg>
<Arg>org.mortbay.servlet.CGI</Arg>
<Put name="Path">/usr/local/bin:/usr/ucb:/bin:/usr/bin</Put>
</Call>
</Call>
<!-- =============================================================== -->
<!-- Configure the Request Log -->
<!-- =============================================================== -->
<Set name="RequestLog">
<New class="org.mortbay.http.I2PRequestLog">
<Arg>$PLUGIN/eepsite/logs/yyyy_mm_dd.request.log</Arg>
<Set name="retainDays">30</Set>
<Set name="append">true</Set>
<Set name="extended">false</Set>
<Set name="buffered">false</Set>
<Set name="LogTimeZone">GMT</Set>
</New>
</Set>
<!-- =============================================================== -->
<!-- Configure the Other Server Options -->
<!-- =============================================================== -->
<Set name="requestsPerGC">2000</Set>
<Set name="statsOn">false</Set>
<!-- =========================================================== -->
<!-- extra options -->
<!-- =========================================================== -->
<Set name="stopAtShutdown">true</Set>
<Set name="stopTimeout">1000</Set>
</Configure>

View File

@ -2,22 +2,29 @@ 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.enableAccessList=false
tunnel.0.option.i2cp.encryptLeaseSet=false
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=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

View File

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

View File

@ -5,4 +5,8 @@ consoleLinkURL=http://127.0.0.1:7662/tracker/index.jsp
description=Open tracker
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=9
min-i2p-version=0.9.29

3
scripts/zzzot.config Normal file
View File

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

View File

@ -3,18 +3,18 @@
<property name="i2pbase" value="../../i2p.i2p"/>
<property name="i2plib" value="${i2pbase}/build"/>
<property name="jettylib" value="${i2pbase}/apps/jetty/jettylib"/>
<property name="war" value="../plugin/eepsite/webapps/blog"/>
<property name="lib" value="${war}/WEB-INF/lib"/>
<path id="cp">
<pathelement path="${java.class.path}" />
<pathelement location="${i2plib}/i2p.jar" />
<pathelement location="${i2plib}/i2ptunnel.jar" />
<pathelement location="${i2plib}/i2psnark.jar" />
<pathelement location="${jettylib}/ant.jar"/>
<pathelement location="${i2plib}/mstreaming.jar" />
<pathelement location="${ant.home}/lib/ant.jar"/>
<pathelement location="${jettylib}/org.mortbay.jetty.jar"/>
<pathelement location="${jettylib}/jasper-compiler.jar" />
<pathelement location="${jettylib}/jasper-runtime.jar" />
<pathelement location="${jettylib}/javax.servlet.jar" />
<pathelement location="${jettylib}/jetty-util.jar" />
<pathelement location="${jettylib}/jetty-xml.jar" />
<pathelement location="${jettylib}/commons-logging.jar" />
<pathelement location="${jettylib}/commons-el.jar" />
</path>
@ -25,15 +25,17 @@
</target>
<property name="javac.compilerargs" value="" />
<property name="javac.version" value="1.7" />
<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"
classpath="${i2plib}/i2p.jar:${i2plib}/i2ptunnel.jar:${i2plib}/i2psnark.jar:${i2plib}/systray.jar:${i2plib}/org.mortbay.jetty.jar" >
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" >
<compilerarg line="${javac.compilerargs}" />
</javac>
</target>
@ -65,12 +67,15 @@
<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" />

View File

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

View File

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

View File

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

View File

@ -1,158 +0,0 @@
// ========================================================================
// $Id: ForwardHandler.java,v 1.16 2005/08/13 00:01:26 gregwilkins Exp $
// Copyright 199-2004 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ========================================================================
package net.i2p.zzzot;
import java.io.IOException;
import java.util.Map;
import org.mortbay.http.HttpException;
import org.mortbay.http.HttpMessage;
import org.mortbay.http.HttpRequest;
import org.mortbay.http.HttpResponse;
import org.mortbay.http.PathMap;
import org.mortbay.http.handler.AbstractHttpHandler;
import org.mortbay.util.URI;
import org.mortbay.util.UrlEncoded;
/* ------------------------------------------------------------ */
/** Forward Request Handler.
* Forwards a request to a new URI. Experimental - use with caution.
* @version $Revision: 1.16 $
* @author Greg Wilkins (gregw)
*
* Just like ForwardHandler but forwards query parameters too
* And took out the dependency on apache logging
* @author zzz
*/
public class QForwardHandler extends AbstractHttpHandler
{
PathMap _forward = new PathMap();
String _root;
boolean _handleQueries = false;
/* ------------------------------------------------------------ */
/** Constructor.
*/
public QForwardHandler()
{}
/* ------------------------------------------------------------ */
/** Constructor.
* @param rootForward
*/
public QForwardHandler(String rootForward)
{
_root=rootForward;
}
/* ------------------------------------------------------------ */
/** Add a forward mapping.
* @param pathSpecInContext The path to forward from
* @param newPath The path to forward to.
*/
public void addForward(String pathSpecInContext,
String newPath)
{
_forward.put(pathSpecInContext,newPath);
}
/* ------------------------------------------------------------ */
/** Add a forward mapping for root path.
* This allows a forward for exactly / which is the default
* path in a pathSpec.
* @param newPath The path to forward to.
*/
public void setRootForward(String newPath)
{
_root=newPath;
}
/* ------------------------------------------------------------ */
/** Set the Handler up to cope with forwards to paths that contain query
* elements (e.g. "/blah"->"/foo?a=b").
* AND (I2P) pass params through (e.g. "/blah?c=d? -> "/foo?c=d? ).
* @param b
*/
public void setHandleQueries(boolean b)
{
_handleQueries = b;
}
/* ------------------------------------------------------------ */
public void handle(String pathInContext,
String pathParams,
HttpRequest request,
HttpResponse response)
throws HttpException, IOException
{
String newPath=null;
String query=null;
if (_root!=null && ("/".equals(pathInContext) || pathInContext.startsWith("/;")))
newPath=_root;
else
{
Map.Entry entry = _forward.getMatch(pathInContext);
if (entry!=null)
{
String match = (String)entry.getValue();
if (_handleQueries)
{
int hook = match.indexOf('?');
if (hook != -1){
query = match.substring(hook+1);
match = match.substring(0, hook);
}
}
String info=PathMap.pathInfo((String)entry.getKey(),pathInContext);
newPath=info==null?match:(URI.addPaths(match,info));
}
}
if (newPath!=null)
{
// this is the new part for i2p
// setPath() changes the request URI and loses the parameters
// so save them and add them back
Map saved = null;
if (_handleQueries){
request.setCharacterEncoding("ISO-8859-1", false);
saved = request.getParameters();
}
int last=request.setState(HttpMessage.__MSG_EDITABLE);
String context=getHttpContext().getContextPath();
if (context.length()==1)
request.setPath(newPath);
else
request.setPath(URI.addPaths(context,newPath));
if (_handleQueries && query != null){
// add forwarded to query string to parameters
UrlEncoded.decodeTo(query, request.getParameters());
}
// this is the new part for i2p
if (_handleQueries){
// add them back
request.getParameters().putAll(saved);
}
request.setState(last);
getHttpContext().getHttpServer().service(request,response);
return;
}
}
}

View File

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

View File

@ -0,0 +1,60 @@
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) {}
}
}

View File

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

View File

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

View File

@ -21,20 +21,28 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentMap;
import net.i2p.CoreVersion;
import net.i2p.I2PAppContext;
import net.i2p.app.ClientApp;
import net.i2p.app.ClientAppManager;
import net.i2p.app.ClientAppState;
import static net.i2p.app.ClientAppState.*;
import net.i2p.apps.systray.UrlLauncher;
import net.i2p.data.Base32;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
import net.i2p.data.PrivateKeyFile;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.util.FileUtil;
import net.i2p.util.I2PAppThread;
import net.i2p.util.Log;
import net.i2p.i2ptunnel.TunnelController;
import net.i2p.apps.systray.UrlLauncher;
import net.i2p.util.VersionComparator;
import org.mortbay.http.HttpContext;
import org.mortbay.jetty.Server;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.xml.XmlConfiguration;
/**
* This handles the starting and stopping of an eepsite tunnel and jetty
@ -47,34 +55,93 @@ import org.mortbay.jetty.Server;
*
* @author zzz
*/
public class ZzzOTController {
private static final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(ZzzOTController.class);
private static Server _server;
private static TunnelController _tunnel;
private static ZzzOT _zzzot;
private static Object _lock = new Object();
public class ZzzOTController implements ClientApp {
private final I2PAppContext _context;
private final Log _log;
private final String[] _args;
private final ClientAppManager _mgr;
private Server _server;
private TunnelController _tunnel;
private final ZzzOT _zzzot;
/** only for main() */
private static volatile ZzzOTController _controller;
public static void main(String args[]) {
if (args.length != 3 || (!"-d".equals(args[0])))
throw new IllegalArgumentException("Usage: PluginController -d $PLUGIN [start|stop]");
if ("start".equals(args[2]))
start(args);
else if ("stop".equals(args[2]))
stop();
else
throw new IllegalArgumentException("Usage: PluginController -d $PLUGIN [start|stop]");
}
private ClientAppState _state = UNINITIALIZED;
public static Torrents getTorrents() {
synchronized(_lock) {
if (_zzzot == null)
_zzzot = new ZzzOT();
private static final String NAME = "ZzzOT";
private static final String CONFIG_FILE = "zzzot.config";
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" };
/**
* @since 0.12.0
*/
public ZzzOTController(I2PAppContext ctx, ClientAppManager mgr, String args[]) {
_context = ctx;
_log = ctx.logManager().getLog(ZzzOTController.class);
_mgr = mgr;
_args = args;
File cfile = new File(_context.getAppDir(), "plugins/zzzot/" + CONFIG_FILE);
Properties props = new Properties();
if (cfile.exists()) {
try {
DataHelper.loadProps(props, cfile);
} catch (IOException ioe) {
_log.error("Failed loading zzzot config from " + cfile, ioe);
}
} else {
if (_log.shouldLog(Log.WARN))
_log.warn("No config file " + cfile);
}
return _zzzot.getTorrents();
_zzzot = new ZzzOT(ctx, props);
_state = INITIALIZED;
}
private static void start(String args[]) {
File pluginDir = new File(args[1]);
/**
* No longer supported, as we now need the ClientAppManager for the webapp to find us
*/
public synchronized static void main(String args[]) {
throw new UnsupportedOperationException("Must use ClientApp interface");
}
/**
* @return null if not running
*/
public static Torrents getTorrents() {
ClientAppManager mgr = I2PAppContext.getGlobalContext().clientAppManager();
if (mgr == null)
return null;
ClientApp z = mgr.getRegisteredApp(NAME);
if (z == null)
return null;
ZzzOTController ctrlr = (ZzzOTController) z;
return ctrlr._zzzot.getTorrents();
}
/**
* @return null if not running
* @since 0.9.14
*/
public static ConcurrentMap<String, String> getDestCache() {
ClientAppManager mgr = I2PAppContext.getGlobalContext().clientAppManager();
if (mgr == null)
return null;
ClientApp z = mgr.getRegisteredApp(NAME);
if (z == null)
return null;
ZzzOTController ctrlr = (ZzzOTController) z;
return ctrlr._zzzot.getDestCache();
}
/**
* @param args ignored
*/
private void start(String args[]) {
//File pluginDir = new File(args[1]);
File pluginDir = new File(_context.getAppDir(), "plugins/zzzot");
if (!pluginDir.exists())
throw new IllegalArgumentException("Plugin directory " + pluginDir.getAbsolutePath() + " does not exist");
@ -91,19 +158,21 @@ public class ZzzOTController {
_log.error("Unable to create " + key.getAbsolutePath() + ' ' + e);
throw new IllegalArgumentException("Unable to create " + key.getAbsolutePath() + ' ' + e);
}
_log.error("NOTICE: ZzzOT: New eepsite keys created in " + key.getAbsolutePath());
_log.error("NOTICE: ZzzOT: You should back up this file!");
_log.logAlways(Log.INFO, "NOTICE: ZzzOT: New eepsite keys created in " + key.getAbsolutePath());
_log.logAlways(Log.INFO, "NOTICE: ZzzOT: You should back up this file!");
String b32 = Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
String b64 = dest.toBase64();
_log.error("NOTICE: ZzzOT: Your base 32 address is " + b32);
_log.error("NOTICE: ZzzOT: Your base 64 address is " + b64);
_log.logAlways(Log.INFO, "NOTICE: ZzzOT: Your base 32 address is " + b32);
_log.logAlways(Log.INFO, "NOTICE: ZzzOT: Your base 64 address is " + b64);
}
startJetty(pluginDir, dest);
startI2PTunnel(pluginDir, dest);
_zzzot.start();
// SeedlessAnnouncer.announce(_tunnel);
}
private static void startI2PTunnel(File pluginDir, Destination dest) {
private void startI2PTunnel(File pluginDir, Destination dest) {
File i2ptunnelConfig = new File(pluginDir, "i2ptunnel.config");
Properties i2ptunnelProps = new Properties();
try {
@ -125,19 +194,20 @@ public class ZzzOTController {
_tunnel = tun;
}
private static void startJetty(File pluginDir, Destination dest) {
private void startJetty(File pluginDir, Destination dest) {
if (_server != null)
throw new IllegalArgumentException("Jetty already running!");
migrateJettyXML(pluginDir);
I2PAppContext context = I2PAppContext.getGlobalContext();
File tmpdir = new File(context.getTempDir().getAbsolutePath(), "/zzzot-work");
File tmpdir = new File(_context.getTempDir().getAbsolutePath(), "/zzzot-work");
tmpdir.mkdir();
File jettyXml = new File(pluginDir, "jetty.xml");
try {
Server serv = new Server(jettyXml.getAbsolutePath());
HttpContext[] hcs = serv.getContexts();
for (int i = 0; i < hcs.length; i++)
hcs[i].setTempDirectory(tmpdir);
Resource.setDefaultUseCaches(false);
XmlConfiguration xmlc = new XmlConfiguration(jettyXml.toURI().toURL());
Server serv = (Server) xmlc.configure();
//HttpContext[] hcs = serv.getContexts();
//for (int i = 0; i < hcs.length; i++)
// hcs[i].setTempDirectory(tmpdir);
serv.start();
_server = serv;
} catch (Throwable t) {
@ -148,18 +218,26 @@ public class ZzzOTController {
launchHelp(pluginDir, dest);
}
private static void stop() {
private void stop() {
stopI2PTunnel();
stopJetty();
if (_zzzot != null)
_zzzot.stop();
_zzzot.stop();
}
private static void stopI2PTunnel() {
private void stopI2PTunnel() {
if (_tunnel == null)
return;
try {
_tunnel.stopTunnel();
// destroyTunnel() not available until 0.9.17
if (VersionComparator.comp(CoreVersion.VERSION, "0.9.17") >= 0) {
try {
_tunnel.destroyTunnel();
} catch (Throwable t) {
_tunnel.stopTunnel();
}
} else {
_tunnel.stopTunnel();
}
} catch (Throwable t) {
_log.error("ZzzOT tunnel stop failed", t);
throw new IllegalArgumentException("Tunnel stop failed " + t);
@ -167,7 +245,7 @@ public class ZzzOTController {
_tunnel = null;
}
private static void stopJetty() {
private void stopJetty() {
if (_server == null)
return;
try {
@ -179,27 +257,92 @@ public class ZzzOTController {
_server = null;
}
/** put the directory in the jetty.xml file */
private static void migrateJettyXML(File pluginDir) {
File outFile = new File(pluginDir, "jetty.xml");
if (outFile.exists())
/**
* Migate the jetty configuration files.
* Save old jetty.xml if moving from jetty 5 to jetty 6
*/
private void migrateJettyXML(File pluginDir) {
// contexts dir does not exist in Jetty 5
File file = new File(pluginDir, "contexts");
file.mkdir();
file = new File(pluginDir, "etc");
file.mkdir();
file = new File(pluginDir, "jetty.xml");
if (!shouldMigrate(file))
return;
File fileTmpl = new File(pluginDir, "templates/jetty.xml");
for (int i = 0; i < xmlFiles.length; i++) {
backupAndMigrateFile(pluginDir, xmlFiles[i]);
}
}
/**
* @return should we copy over all the files, based on the contents of this one
* @since 0.10 (Jetty 7)
*/
private static boolean shouldMigrate(File f) {
String xml = FileUtil.readTextFile(f.getAbsolutePath(), 400, true);
if (xml == null)
return true;
return xml.contains("class=\"org.eclipse.jetty.server.nio.SelectChannelConnector\"");
}
/**
* Backup a file and migrate new XML
* @return success
* @since Jetty 7
*/
private boolean backupAndMigrateFile(File toDir, String filename) {
File to = new File(toDir, filename);
boolean rv = backupFile(to);
boolean rv2 = migrateJettyFile(toDir, filename);
return rv && rv2;
}
/**
* Backup a file
* @return success
* @since Jetty 7
*/
private static boolean backupFile(File from) {
if (!from.exists())
return true;
File to = new File(from.getAbsolutePath() + BACKUP_SUFFIX);
if (to.exists())
to = new File(to.getAbsolutePath() + "." + System.currentTimeMillis());
boolean rv = FileUtil.copy(from, to, false, true);
if (rv)
System.err.println("Backed up file " + from + " to " + to);
else
System.err.println("WARNING: Failed to back up file " + from + " to " + to);
return rv;
}
/**
* Migate a single jetty config file, replacing $PLUGIN as we copy it.
*/
private boolean migrateJettyFile(File pluginDir, String name) {
File templateDir = new File(pluginDir, "templates");
File fileTmpl = new File(templateDir, name);
File outFile = new File(pluginDir, name);
FileOutputStream os = null;
try {
String props = FileUtil.readTextFile(fileTmpl.getAbsolutePath(), 250, true);
String props = FileUtil.readTextFile(fileTmpl.getAbsolutePath(), 600, true);
if (props == null)
throw new IOException(fileTmpl.getAbsolutePath() + " open failed");
props = props.replace("$PLUGIN", pluginDir.getAbsolutePath());
FileOutputStream os = new FileOutputStream(outFile);
os = new FileOutputStream(outFile);
os.write(props.getBytes("UTF-8"));
os.close();
return true;
} catch (IOException ioe) {
_log.error("jetty.xml migrate failed", ioe);
_log.error(outFile + " migrate failed", ioe);
return false;
} finally {
if (os != null) try { os.close(); } catch (IOException ioe) {}
}
}
/** put the directory, base32, and base64 info in the help.html file and launch a browser window to display it */
private static void launchHelp(File pluginDir, Destination dest) {
private void launchHelp(File pluginDir, Destination dest) {
File fileTmpl = new File(pluginDir, "templates/help.html");
File outFile = new File(pluginDir, "eepsite/docroot/help.html");
String b32 = Base32.encode(dest.calculateHash().getData()) + ".b32.i2p";
@ -226,4 +369,83 @@ public class ZzzOTController {
UrlLauncher.main(new String[] { "http://127.0.0.1:7662/help.html" } );
}
}
/////// ClientApp methods
/** @since 0.12.0 */
public synchronized void startup() {
if (_mgr != null) {
// this is really ugly, but thru 0.9.16,
// stopping a ClientApp plugin with $PLUGIN in the args fails,
// and it tries to start a second one instead.
// Find the first one and stop it.
ClientApp z = _mgr.getRegisteredApp(NAME);
if (z != null) {
if (VersionComparator.comp(CoreVersion.VERSION, "0.9.17") < 0) {
ZzzOTController ctrlr = (ZzzOTController) z;
_log.warn("Got start when another zzzot running, stopping him instead");
ctrlr.shutdown(null);
} else {
_log.error("ZzzOT already running");
}
changeState(START_FAILED);
return;
}
}
if (_state != STOPPED && _state != INITIALIZED && _state != START_FAILED) {
_log.error("Start while state = " + _state);
return;
}
changeState(STARTING);
try {
start(_args);
changeState(RUNNING);
if (_mgr != null)
_mgr.register(this);
} catch (Exception e) {
changeState(START_FAILED, "Start failed", e);
}
}
/** @since 0.12.0 */
public synchronized void shutdown(String[] args) {
if (_state == STOPPED)
return;
changeState(STOPPING);
if (_mgr != null)
_mgr.unregister(this);
stop();
changeState(STOPPED);
}
/** @since 0.12.0 */
public ClientAppState getState() {
return _state;
}
/** @since 0.12.0 */
public String getName() {
return NAME;
}
/** @since 0.12.0 */
public String getDisplayName() {
return NAME;
}
/////// end ClientApp methods
/** @since 0.12.0 */
private synchronized void changeState(ClientAppState state) {
_state = state;
if (_mgr != null)
_mgr.notify(this, state, null, null);
}
/** @since 0.12.0 */
private synchronized void changeState(ClientAppState state, String msg, Exception e) {
_state = state;
if (_mgr != null)
_mgr.notify(this, state, msg, e);
}
}

View File

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

View File

@ -1,4 +1,4 @@
<%@page import="net.i2p.zzzot.ZzzOTController" %>
<%@page import="net.i2p.zzzot.ZzzOTController,net.i2p.zzzot.Torrents" %>
<html>
<head>
<title>ZzzOT</title>
@ -6,9 +6,20 @@
<p>
zzzot
<p>
<%
Torrents torrents = ZzzOTController.getTorrents();
if (torrents != null) {
%>
<table cellspacing="8">
<tr><td>Torrents:<td align="right"><%=ZzzOTController.getTorrents().size()%>
<tr><td>Peers:<td align="right"><%=ZzzOTController.getTorrents().countPeers()%>
<tr><td>Torrents:<td align="right"><%=torrents.size()%>
<tr><td>Peers:<td align="right"><%=torrents.countPeers()%>
</table>
<%
} else {
%>
ZzzOT is not running
<%
}
%>
</body>
</html>

View File

@ -1,4 +1,4 @@
<%@page import="java.util.ArrayList" %><%@page import="java.util.List" %><%@page import="java.util.Map" %><%@page import="java.util.HashMap" %><%@page import="net.i2p.zzzot.*" %><%@page import="org.klomp.snark.bencode.BEncoder" %><%
<%@page import="java.util.ArrayList,java.util.List,java.util.Map,java.util.HashMap,net.i2p.zzzot.*,org.klomp.snark.bencode.BEncoder" %><%
/*
* Above one-liner is so there is no whitespace -> IllegalStateException
@ -27,6 +27,10 @@
*/
// so the chars will turn into bytes correctly
request.setCharacterEncoding("ISO-8859-1");
// above doesn't work for the query string
// https://wiki.eclipse.org/Jetty/Howto/International_Characters
// we could also do ((org.eclipse.jetty.server.Request) request).setQueryEncoding("ISO-8859-1")
request.setAttribute("org.eclipse.jetty.server.Request.queryEncoding", "ISO-8859-1");
java.io.OutputStream cout = response.getOutputStream();
response.setCharacterEncoding("ISO-8859-1");
response.setContentType("text/plain");
@ -41,39 +45,44 @@
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;
Torrents torrents = ZzzOTController.getTorrents();
if (torrents == null && !fail) {
fail = true;
msg = "tracker is down";
}
InfoHash ih = null;
if ((!all) && !fail) {
try {
ih = new InfoHash(info_hash);
ih = torrents.createInfoHash(info_hash);
} catch (Exception e) {
fail = true;
msg = "bad infohash " + e;
}
}
Torrents torrents = ZzzOTController.getTorrents();
// build 3-level dictionary
Map<String, Object> m = new HashMap();
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();
Map<String, Map> files = new HashMap<String, 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));

View File

@ -1,4 +1,4 @@
<%@page import="net.i2p.crypto.SHA256Generator" %><%@page import="net.i2p.data.Base32" %><%@page import="net.i2p.data.Base64" %><%@page import="net.i2p.data.DataHelper" %><%@page import="net.i2p.zzzot.*" %><%
<%@page import="net.i2p.crypto.SHA256Generator,net.i2p.data.Base32,net.i2p.data.Base64,net.i2p.data.DataHelper,net.i2p.zzzot.*" %><%
/*
* Copyright 2010 zzz (zzz@mail.i2p)
@ -27,18 +27,21 @@
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", me);
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);
//response.setStatus(403, msg);
response.setStatus(403);
out.println(msg);
} else if (req == null) {
// probe
@ -47,14 +50,24 @@
out.println("seedless " + US_MINUTES);
} else if (req.startsWith("announce")) {
out.println("thanks");
} else if (req.startsWith("locate")) {
} 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");
response.setStatus(503);
return;
}
for (InfoHash ihash : torrents.keySet()) {
Peers peers = torrents.get(ihash);
if (peers == null)
@ -68,19 +81,21 @@
// service type
String role;
if (p.isSeed())
role = " bt-seed";
role = "seed";
else
role = " bt-leech";
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 + role +
" info_hash=" + ihs +
";peer_id=" + ids));
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");
}