Compare commits
151 Commits
i2p-0.9.48
...
build-debi
| Author | SHA1 | Date | |
|---|---|---|---|
| c0a0aa7700 | |||
| 4a9131c39d | |||
| b47269f14e | |||
| fb317b44ba | |||
| e64e12b3fb | |||
| f71e59a049 | |||
| 169fb59d7d | |||
|
|
922178b2c7 | ||
|
|
74a9193ba5 | ||
|
|
335409f1d2 | ||
|
|
d6edb9e96c | ||
| f150855f1c | |||
|
|
655ce09796 | ||
|
|
91ebec15d5 | ||
|
|
b17d321503 | ||
|
|
a6398d88a9 | ||
|
|
59969db16c | ||
|
|
b68a5ea7fd | ||
|
|
c2234685b9 | ||
|
|
ce7daaa02a | ||
|
|
b19999f95a | ||
|
|
92ecc9f8e8 | ||
|
|
aa2ba92db8 | ||
|
|
5f3c41244b | ||
|
|
bf29132898 | ||
|
|
a424331b78 | ||
|
|
ccb0c279f7 | ||
|
|
7fe01fb9a7 | ||
|
|
66c4c10a78 | ||
|
|
163967311e | ||
|
|
75734448c5 | ||
|
|
aed1de84b8 | ||
|
|
51560a8ec8 | ||
|
|
ec89a80e80 | ||
|
|
41c7b7382a | ||
|
|
b4e1fbd857 | ||
|
|
517ff4fa24 | ||
|
|
106b1a696d | ||
|
|
6cab545c45 | ||
|
|
619923dbf8 | ||
|
|
ed0ecdf253 | ||
|
|
d42ef2223d | ||
|
|
e461004ed9 | ||
|
|
2e180d4c60 | ||
|
|
152ad1659b | ||
|
|
888311e34f | ||
|
|
2df5fb972a | ||
|
|
a481255adb | ||
|
|
139594df8f | ||
|
|
659ab97f69 | ||
|
|
963a4fe89c | ||
|
|
4c4dbae107 | ||
|
|
6978049416 | ||
|
|
46fe1ba74a | ||
|
|
13bd5e4938 | ||
|
|
bbacf85245 | ||
|
|
68f011f344 | ||
|
|
8bd2384ac8 | ||
|
|
54dda1a15f | ||
|
|
3f44a555ba | ||
|
|
86cbb2ed4e | ||
|
|
2569123055 | ||
|
|
afa4b9e66d | ||
|
|
67bd5a32fd | ||
|
|
ada3629507 | ||
|
|
dcb7314306 | ||
|
|
e3c2ad6354 | ||
|
|
178ea252d5 | ||
|
|
7e4ba4eb31 | ||
|
|
de43de17f6 | ||
|
|
2ceb9c429a | ||
|
|
0b59f53fe9 | ||
|
|
62fce859b9 | ||
|
|
9fc97764c5 | ||
|
|
2813d9412d | ||
|
|
a0bf76a4b1 | ||
|
|
d2a79e8837 | ||
|
|
738ef496d4 | ||
|
|
a2734ffa72 | ||
|
|
8606d30e9a | ||
|
|
a45084cfc3 | ||
|
|
eeaf6f3514 | ||
|
|
9e18ff1cd1 | ||
|
|
665239fd37 | ||
|
|
12f9a7187e | ||
|
|
8835351b99 | ||
|
|
a3c44912f2 | ||
|
|
db9f735376 | ||
|
|
875a7242d4 | ||
|
|
51ecdc64a4 | ||
|
|
7b785ea454 | ||
|
|
8f5fc44755 | ||
|
|
010dbfa6f2 | ||
|
|
e20a19c358 | ||
|
|
387e513949 | ||
|
|
5e005e6520 | ||
|
|
e88f40cd95 | ||
|
|
82e93a53a3 | ||
| fee5668c1c | |||
|
|
abb8cbe75d | ||
|
|
340df51429 | ||
|
|
bec8feb05a | ||
|
|
d86ccded53 | ||
|
|
db7d92a5cd | ||
|
|
30ffdd03c7 | ||
| 251d8de943 | |||
|
|
5e8de68746 | ||
|
|
8ae29c8c00 | ||
|
|
542efa0d9a | ||
|
|
23c80accfa | ||
|
|
b909430725 | ||
|
|
20b413bc67 | ||
|
|
a9b6b86183 | ||
|
|
66b724759d | ||
|
|
56059448c5 | ||
|
|
1c52eeb910 | ||
|
|
4aefe4bf7a | ||
|
|
b9444cdc51 | ||
|
|
eb72e4c9f5 | ||
|
|
aa181ee43f | ||
|
|
ab04f92072 | ||
|
|
0830329eaf | ||
|
|
2d154cc90e | ||
|
|
183280871f | ||
|
|
067ee80ba0 | ||
|
|
804e2f39f9 | ||
|
|
0ad7e52b71 | ||
|
|
e15110bbe1 | ||
|
|
2cffda6974 | ||
| 2300f6c226 | |||
| 1ed8a1b6f3 | |||
| c4ed7719e8 | |||
| a98fe45204 | |||
| 5a3e26453f | |||
| c259000cdb | |||
| d683f0d9eb | |||
| 48b8886224 | |||
| 1097220d31 | |||
| fdeae72d38 | |||
| f870bc2ccd | |||
| ec3bfa3cb7 | |||
| c3f7c5d154 | |||
| 127b93c1e2 | |||
| cd019f258f | |||
| 889b7361fe | |||
| 99f6d4aba4 | |||
| 69deddcbc7 | |||
| 58020b4b58 | |||
| df43e72a08 | |||
| 326e2c630c | |||
|
|
1154d28be7 |
31
.dockerignore
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
.idea
|
||||||
|
.git
|
||||||
|
Dockerfile
|
||||||
|
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.gradle
|
||||||
|
build
|
||||||
|
apps/BOB/build
|
||||||
|
apps/addressbook/build
|
||||||
|
apps/desktopgui/build
|
||||||
|
apps/i2pcontrol/build
|
||||||
|
apps/i2psnark/build
|
||||||
|
apps/i2ptunnel/build
|
||||||
|
apps/imagegen/build
|
||||||
|
apps/jetty/build
|
||||||
|
apps/jrobin/build
|
||||||
|
apps/ministreaming/java/build
|
||||||
|
apps/ministreaming/build
|
||||||
|
apps/routerconsole/build
|
||||||
|
apps/sam/build
|
||||||
|
apps/streaming/build
|
||||||
|
apps/susidns/build
|
||||||
|
apps/susimail/build
|
||||||
|
apps/systray/build
|
||||||
|
core/java/build
|
||||||
|
core/build
|
||||||
|
installer/build
|
||||||
|
router/java/build
|
||||||
|
router/build
|
||||||
|
|
||||||
6
.gitignore
vendored
@@ -54,4 +54,10 @@ sloccount.sc
|
|||||||
.settings/
|
.settings/
|
||||||
# IDEA
|
# IDEA
|
||||||
*.iml
|
*.iml
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.gradle
|
||||||
|
|
||||||
|
# TODO: why does this file appear?
|
||||||
|
apps/routerconsole/jsp/favicon.ico
|
||||||
|
|||||||
66
.gitlab-ci.yml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
image: openjdk:8-alpine
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- test
|
||||||
|
|
||||||
|
cache:
|
||||||
|
key: ${CI_COMMIT_REF_SLUG}
|
||||||
|
paths:
|
||||||
|
- $HOME/.gradle/caches/
|
||||||
|
- $HOME/.gradle/wrapper/
|
||||||
|
- .gradle
|
||||||
|
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
coverage: '/Total.*?([0-9]{1,3})%/'
|
||||||
|
before_script:
|
||||||
|
- apk add --no-cache grep
|
||||||
|
script:
|
||||||
|
- ./gradlew codeCoverageReport
|
||||||
|
# The actual output that will be parsed by the code coverage
|
||||||
|
- grep -oP "Total.*?%" build/reports/jacoco/html/index.html
|
||||||
|
only:
|
||||||
|
- merge_requests
|
||||||
|
- tags
|
||||||
|
|
||||||
|
# Make sure we can build a docker image
|
||||||
|
# It's cached for later jobs
|
||||||
|
build_docker:
|
||||||
|
stage: test
|
||||||
|
image: docker:19.03.12
|
||||||
|
services:
|
||||||
|
- docker:19.03.12-dind
|
||||||
|
script:
|
||||||
|
# Try to load latest branch image from local tar or from registry
|
||||||
|
- docker load -i ci-exports/$CI_COMMIT_REF_SLUG.tar || docker pull $CI_REGISTRY_IMAGE:latest || true
|
||||||
|
- docker build --cache-from $CI_REGISTRY_IMAGE:latest --tag $CI_REGISTRY_IMAGE:latest .
|
||||||
|
- mkdir -p ci-exports/
|
||||||
|
- docker save $CI_REGISTRY_IMAGE:latest > ci-exports/$CI_COMMIT_REF_SLUG.tar
|
||||||
|
variables:
|
||||||
|
# When using dind service, we need to instruct docker to talk with
|
||||||
|
# the daemon started inside of the service. The daemon is available
|
||||||
|
# with a network connection instead of the default
|
||||||
|
# /var/run/docker.sock socket. Docker 19.03 does this automatically
|
||||||
|
# by setting the DOCKER_HOST in
|
||||||
|
# https://github.com/docker-library/docker/blob/d45051476babc297257df490d22cbd806f1b11e4/19.03/docker-entrypoint.sh#L23-L29
|
||||||
|
#
|
||||||
|
# The 'docker' hostname is the alias of the service container as described at
|
||||||
|
# https://docs.gitlab.com/ee/ci/docker/using_docker_images.html#accessing-the-services.
|
||||||
|
#
|
||||||
|
# Specify to Docker where to create the certificates, Docker will
|
||||||
|
# create them automatically on boot, and will create
|
||||||
|
# `/certs/client` that will be shared between the service and job
|
||||||
|
# container, thanks to volume mount from config.toml
|
||||||
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
|
# Use TLS https://docs.gitlab.com/ee/ci/docker/using_docker_build.html#tls-enabled
|
||||||
|
DOCKER_HOST: tcp://docker:2376
|
||||||
|
cache:
|
||||||
|
# The same key should be used across branches
|
||||||
|
key: "$CI_COMMIT_REF_SLUG"
|
||||||
|
paths:
|
||||||
|
- ci-exports/*.tar
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- merge_requests
|
||||||
|
- tags
|
||||||
|
|
||||||
6
.idea/ant.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AntConfiguration">
|
|
||||||
<buildFile url="file://$PROJECT_DIR$/build.xml" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
41
.idea/compiler.xml
generated
@@ -1,41 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CompilerConfiguration">
|
|
||||||
<bytecodeTargetLevel>
|
|
||||||
<module name="addressbook_main" target="1.7" />
|
|
||||||
<module name="addressbook_test" target="1.7" />
|
|
||||||
<module name="BOB_main" target="1.7" />
|
|
||||||
<module name="BOB_test" target="1.7" />
|
|
||||||
<module name="core_main" target="1.7" />
|
|
||||||
<module name="core_test" target="1.7" />
|
|
||||||
<module name="desktopgui_main" target="1.7" />
|
|
||||||
<module name="desktopgui_test" target="1.7" />
|
|
||||||
<module name="i2psnark_main" target="1.7" />
|
|
||||||
<module name="i2psnark_test" target="1.7" />
|
|
||||||
<module name="i2ptunnel_main" target="1.7" />
|
|
||||||
<module name="i2ptunnel_test" target="1.7" />
|
|
||||||
<module name="installer_main" target="1.7" />
|
|
||||||
<module name="installer_test" target="1.7" />
|
|
||||||
<module name="jetty_main" target="1.7" />
|
|
||||||
<module name="jetty_test" target="1.7" />
|
|
||||||
<module name="jrobin_main" target="1.7" />
|
|
||||||
<module name="jrobin_test" target="1.7" />
|
|
||||||
<module name="ministreaming_main" target="1.7" />
|
|
||||||
<module name="ministreaming_test" target="1.7" />
|
|
||||||
<module name="router_main" target="1.7" />
|
|
||||||
<module name="router_test" target="1.7" />
|
|
||||||
<module name="routerconsole_main" target="1.7" />
|
|
||||||
<module name="routerconsole_test" target="1.7" />
|
|
||||||
<module name="sam_main" target="1.7" />
|
|
||||||
<module name="sam_test" target="1.7" />
|
|
||||||
<module name="streaming_main" target="1.7" />
|
|
||||||
<module name="streaming_test" target="1.7" />
|
|
||||||
<module name="susidns_main" target="1.7" />
|
|
||||||
<module name="susidns_test" target="1.7" />
|
|
||||||
<module name="susimail_main" target="1.7" />
|
|
||||||
<module name="susimail_test" target="1.7" />
|
|
||||||
<module name="systray_main" target="1.7" />
|
|
||||||
<module name="systray_test" target="1.7" />
|
|
||||||
</bytecodeTargetLevel>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
3
.idea/copyright/profiles_settings.xml
generated
@@ -1,3 +0,0 @@
|
|||||||
<component name="CopyrightManager">
|
|
||||||
<settings default="" />
|
|
||||||
</component>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="javax.servlet.jsp-2.2.0.v201112011158">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jsp/javax.servlet.jsp-2.2.0.v201112011158.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
22
.idea/libraries/jettylib.xml
generated
@@ -1,22 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="jettylib">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-security-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-servlets-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-deploy-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-util-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-servlet-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-http-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-xml-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-server-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/servlet-api-3.0.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-jmx-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-webapp-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-io-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-continuation-8.1.17.v20150415.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/lib/jetty-rewrite-8.1.17.v20150415.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
9
.idea/libraries/jrobin_1_5_9_1.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="jrobin-1.5.9.1">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jrobin/jrobin-1.5.9.1.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
10
.idea/libraries/lib.xml
generated
@@ -1,10 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="lib">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/susidns/src/lib/jstl.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/susidns/src/lib/standard.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
9
.idea/libraries/start.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="start">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/jetty-distribution-8.1.17.v20150415/start.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
9
.idea/libraries/systray4j.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="systray4j">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/systray/java/lib/systray4j.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
9
.idea/libraries/tomcat_coyote_util.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="tomcat-coyote-util">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/apache-tomcat/lib/tomcat-coyote-util.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
12
.idea/libraries/tomcat_lib.xml
generated
@@ -1,12 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="tomcat-lib">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/apache-tomcat-deployer/lib/tomcat-juli.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/apache-tomcat-deployer/lib/el-api.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/apache-tomcat-deployer/lib/jasper.jar!/" />
|
|
||||||
<root url="jar://$PROJECT_DIR$/apps/jetty/apache-tomcat-deployer/lib/jasper-el.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
9
.idea/libraries/wrapper.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="wrapper">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/installer/lib/wrapper/all/wrapper.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
9
.idea/libraries/wrapper_win.xml
generated
@@ -1,9 +0,0 @@
|
|||||||
<component name="libraryTable">
|
|
||||||
<library name="wrapper-win">
|
|
||||||
<CLASSES>
|
|
||||||
<root url="jar://$PROJECT_DIR$/installer/lib/wrapper/win-all/wrapper.jar!/" />
|
|
||||||
</CLASSES>
|
|
||||||
<JAVADOC />
|
|
||||||
<SOURCES />
|
|
||||||
</library>
|
|
||||||
</component>
|
|
||||||
57
.idea/misc.xml
generated
@@ -1,57 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ClientPropertiesManager">
|
|
||||||
<properties class="javax.swing.AbstractButton">
|
|
||||||
<property name="hideActionText" class="java.lang.Boolean" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.JComponent">
|
|
||||||
<property name="html.disable" class="java.lang.Boolean" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.JEditorPane">
|
|
||||||
<property name="JEditorPane.w3cLengthUnits" class="java.lang.Boolean" />
|
|
||||||
<property name="JEditorPane.honorDisplayProperties" class="java.lang.Boolean" />
|
|
||||||
<property name="charset" class="java.lang.String" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.JList">
|
|
||||||
<property name="List.isFileList" class="java.lang.Boolean" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.JPasswordField">
|
|
||||||
<property name="JPasswordField.cutCopyAllowed" class="java.lang.Boolean" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.JSlider">
|
|
||||||
<property name="Slider.paintThumbArrowShape" class="java.lang.Boolean" />
|
|
||||||
<property name="JSlider.isFilled" class="java.lang.Boolean" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.JTable">
|
|
||||||
<property name="Table.isFileList" class="java.lang.Boolean" />
|
|
||||||
<property name="JTable.autoStartsEdit" class="java.lang.Boolean" />
|
|
||||||
<property name="terminateEditOnFocusLost" class="java.lang.Boolean" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.JToolBar">
|
|
||||||
<property name="JToolBar.isRollover" class="java.lang.Boolean" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.JTree">
|
|
||||||
<property name="JTree.lineStyle" class="java.lang.String" />
|
|
||||||
</properties>
|
|
||||||
<properties class="javax.swing.text.JTextComponent">
|
|
||||||
<property name="caretAspectRatio" class="java.lang.Double" />
|
|
||||||
<property name="caretWidth" class="java.lang.Integer" />
|
|
||||||
</properties>
|
|
||||||
</component>
|
|
||||||
<component name="EntryPointsManager">
|
|
||||||
<entry_points version="2.0" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
|
|
||||||
<OptionsSetting value="true" id="Add" />
|
|
||||||
<OptionsSetting value="true" id="Remove" />
|
|
||||||
<OptionsSetting value="true" id="Checkout" />
|
|
||||||
<OptionsSetting value="true" id="Update" />
|
|
||||||
<OptionsSetting value="true" id="Status" />
|
|
||||||
<OptionsSetting value="true" id="Edit" />
|
|
||||||
<ConfirmationsSetting value="0" id="Add" />
|
|
||||||
<ConfirmationsSetting value="0" id="Remove" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
|
|
||||||
<output url="file://$PROJECT_DIR$/build" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
80
.idea/modules.xml
generated
@@ -1,80 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/BOB/BOB.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/BOB/BOB.iml" group="apps/BOB" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/BOB/BOB.iml" filepath="$PROJECT_DIR$/apps/BOB/BOB.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/BOB/BOB_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/BOB/BOB_main.iml" group="apps/BOB" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/BOB/BOB_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/BOB/BOB_test.iml" group="apps/BOB" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook.iml" group="apps/addressbook" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/addressbook/addressbook.iml" filepath="$PROJECT_DIR$/apps/addressbook/addressbook.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook_main.iml" group="apps/addressbook" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/addressbook/addressbook_test.iml" group="apps/addressbook" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/admin/admin.iml" filepath="$PROJECT_DIR$/apps/admin/admin.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/apps.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/apps.iml" group="apps" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core.iml" group="core" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/core/core.iml" filepath="$PROJECT_DIR$/core/core.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core_main.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core_main.iml" group="core" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/core/core_test.iml" filepath="$PROJECT_DIR$/.idea/modules/core/core_test.iml" group="core" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui.iml" group="apps/desktopgui" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/desktopgui/desktopgui.iml" filepath="$PROJECT_DIR$/apps/desktopgui/desktopgui.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui_main.iml" group="apps/desktopgui" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/desktopgui/desktopgui_test.iml" group="apps/desktopgui" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/i2p.i2p.sl.iml" filepath="$PROJECT_DIR$/.idea/modules/i2p.i2p.sl.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark.iml" group="apps/i2psnark" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/i2psnark/i2psnark.iml" filepath="$PROJECT_DIR$/apps/i2psnark/i2psnark.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark_main.iml" group="apps/i2psnark" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2psnark/i2psnark_test.iml" group="apps/i2psnark" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel.iml" group="apps/i2ptunnel" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/i2ptunnel/i2ptunnel.iml" filepath="$PROJECT_DIR$/apps/i2ptunnel/i2ptunnel.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel_main.iml" group="apps/i2ptunnel" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/i2ptunnel/i2ptunnel_test.iml" group="apps/i2ptunnel" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/imagegen/identicon/identicon.iml" filepath="$PROJECT_DIR$/apps/imagegen/identicon/identicon.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/imagegen/imagegen/imagegen.iml" filepath="$PROJECT_DIR$/apps/imagegen/imagegen/imagegen.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/installer/installer.iml" filepath="$PROJECT_DIR$/.idea/modules/installer/installer.iml" group="installer" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/installer/installer.iml" filepath="$PROJECT_DIR$/installer/installer.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/installer/installer_main.iml" filepath="$PROJECT_DIR$/.idea/modules/installer/installer_main.iml" group="installer" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/installer/installer_test.iml" filepath="$PROJECT_DIR$/.idea/modules/installer/installer_test.iml" group="installer" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jetty/jetty.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jetty/jetty.iml" group="apps/jetty" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/jetty/jetty.iml" filepath="$PROJECT_DIR$/apps/jetty/jetty.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jetty/jetty_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jetty/jetty_main.iml" group="apps/jetty" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jetty/jetty_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jetty/jetty_test.iml" group="apps/jetty" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin.iml" group="apps/jrobin" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin_main.iml" group="apps/jrobin" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/jrobin/jrobin_test.iml" group="apps/jrobin" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming.iml" group="apps/ministreaming" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/ministreaming/ministreaming.iml" filepath="$PROJECT_DIR$/apps/ministreaming/ministreaming.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming_main.iml" group="apps/ministreaming" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/ministreaming/ministreaming_test.iml" group="apps/ministreaming" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/router/router.iml" filepath="$PROJECT_DIR$/.idea/modules/router/router.iml" group="router" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/router/router.iml" filepath="$PROJECT_DIR$/router/router.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/router/router_main.iml" filepath="$PROJECT_DIR$/.idea/modules/router/router_main.iml" group="router" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/router/router_test.iml" filepath="$PROJECT_DIR$/.idea/modules/router/router_test.iml" group="router" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole.iml" group="apps/routerconsole" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/routerconsole/routerconsole.iml" filepath="$PROJECT_DIR$/apps/routerconsole/routerconsole.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole_main.iml" group="apps/routerconsole" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/routerconsole/routerconsole_test.iml" group="apps/routerconsole" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/sam/sam.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/sam/sam.iml" group="apps/sam" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/sam/sam.iml" filepath="$PROJECT_DIR$/apps/sam/sam.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/sam/sam_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/sam/sam_main.iml" group="apps/sam" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/sam/sam_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/sam/sam_test.iml" group="apps/sam" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/streaming/streaming.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/streaming/streaming.iml" group="apps/streaming" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/streaming/streaming.iml" filepath="$PROJECT_DIR$/apps/streaming/streaming.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/streaming/streaming_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/streaming/streaming_main.iml" group="apps/streaming" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/streaming/streaming_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/streaming/streaming_test.iml" group="apps/streaming" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susidns/susidns.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susidns/susidns.iml" group="apps/susidns" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/susidns/susidns.iml" filepath="$PROJECT_DIR$/apps/susidns/susidns.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susidns/susidns_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susidns/susidns_main.iml" group="apps/susidns" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susidns/susidns_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susidns/susidns_test.iml" group="apps/susidns" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susimail/susimail.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susimail/susimail.iml" group="apps/susimail" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/susimail/susimail.iml" filepath="$PROJECT_DIR$/apps/susimail/susimail.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susimail/susimail_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susimail/susimail_main.iml" group="apps/susimail" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/susimail/susimail_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/susimail/susimail_test.iml" group="apps/susimail" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/systray/systray.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/systray/systray.iml" group="apps/systray" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/systray/systray.iml" filepath="$PROJECT_DIR$/apps/systray/systray.iml" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/systray/systray_main.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/systray/systray_main.iml" group="apps/systray" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/apps/systray/systray_test.iml" filepath="$PROJECT_DIR$/.idea/modules/apps/systray/systray_test.iml" group="apps/systray" />
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/apps/imagegen/zxing/zxing.iml" filepath="$PROJECT_DIR$/apps/imagegen/zxing/zxing.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
6
.idea/runConfigurations/updater.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="updater" type="AntRunConfiguration" factoryName="Ant Target">
|
|
||||||
<antsettings antfile="file://$PROJECT_DIR$/build.xml" target="updater" />
|
|
||||||
<method />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
16
.travis.yml
@@ -1,17 +1,17 @@
|
|||||||
language: java
|
language: java
|
||||||
dist: xenial
|
dist: bionic
|
||||||
|
|
||||||
jdk:
|
jdk:
|
||||||
- oraclejdk11
|
- oraclejdk11
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- jdk: oraclejdk8
|
- jdk: oraclejdk11
|
||||||
addons:
|
addons:
|
||||||
sonarcloud:
|
sonarcloud:
|
||||||
organization: "i2p"
|
organization: "i2p"
|
||||||
before_install:
|
before_install:
|
||||||
- sed -i "1iplugins {\n id 'org.sonarqube' version '2.6.1'\n}\n" build.gradle
|
- sed -i "1iplugins {\n id 'org.sonarqube' version '3.0'\n}\n" build.gradle
|
||||||
- jdk: openjdk8
|
- jdk: openjdk8
|
||||||
|
|
||||||
before_cache:
|
before_cache:
|
||||||
@@ -24,13 +24,11 @@ cache:
|
|||||||
- $HOME/.sonar/cache/
|
- $HOME/.sonar/cache/
|
||||||
- .gradle
|
- .gradle
|
||||||
|
|
||||||
|
env:
|
||||||
|
- SONAR_SCANNER_OPTS="-Xmx2048m"
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- |
|
- travis_wait 45 ./tests/scripts/travis.sh
|
||||||
if [ "$TRAVIS_JDK_VERSION" == "oraclejdk8" ]; then
|
|
||||||
./gradlew sonarqube codeCoverageReport
|
|
||||||
else
|
|
||||||
./gradlew check codeCoverageReport
|
|
||||||
fi
|
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|||||||
26
.tx/config
@@ -619,21 +619,21 @@ trans.tr_TR = installer/resources/eepsite/docroot/help/index_tr.html
|
|||||||
;; Text on /console
|
;; Text on /console
|
||||||
;;
|
;;
|
||||||
type = HTML
|
type = HTML
|
||||||
source_file = installer/resources/readme/readme.html
|
source_file = apps/routerconsole/resources/docs/readme.html
|
||||||
source_lang = en
|
source_lang = en
|
||||||
trans.ar = installer/resources/readme/readme_ar.html
|
trans.ar = apps/routerconsole/resources/docs/readme_ar.html
|
||||||
trans.de = installer/resources/readme/readme_de.html
|
trans.de = apps/routerconsole/resources/docs/readme_de.html
|
||||||
trans.fr = installer/resources/readme/readme_fr.html
|
trans.fr = apps/routerconsole/resources/docs/readme_fr.html
|
||||||
;; Java converts id to in
|
;; Java converts id to in
|
||||||
trans.id = installer/resources/readme/readme_in.html
|
trans.id = apps/routerconsole/resources/docs/readme_in.html
|
||||||
trans.it = installer/resources/readme/readme_it.html
|
trans.it = apps/routerconsole/resources/docs/readme_it.html
|
||||||
trans.ja = installer/resources/readme/readme_ja.html
|
trans.ja = apps/routerconsole/resources/docs/readme_ja.html
|
||||||
trans.pl = installer/resources/readme/readme_pl.html
|
trans.pl = apps/routerconsole/resources/docs/readme_pl.html
|
||||||
trans.pt = installer/resources/readme/readme_pt.html
|
trans.pt = apps/routerconsole/resources/docs/readme_pt.html
|
||||||
trans.ro = installer/resources/readme/readme_ro.html
|
trans.ro = apps/routerconsole/resources/docs/readme_ro.html
|
||||||
trans.ru_RU = installer/resources/readme/readme_ru.html
|
trans.ru_RU = apps/routerconsole/resources/docs/readme_ru.html
|
||||||
trans.tr_TR = installer/resources/readme/readme_tr.html
|
trans.tr_TR = apps/routerconsole/resources/docs/readme_tr.html
|
||||||
trans.zh_CN = installer/resources/readme/readme_zh.html
|
trans.zh_CN = apps/routerconsole/resources/docs/readme_zh.html
|
||||||
|
|
||||||
[main]
|
[main]
|
||||||
host = https://www.transifex.com
|
host = https://www.transifex.com
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
export JAVA_HOME=/opt/jdk/jre
|
|
||||||
|
|
||||||
# Ensure user rights
|
|
||||||
chown -R i2p:nobody /opt/i2p
|
|
||||||
chmod -R u+rwx /opt/i2p
|
|
||||||
|
|
||||||
gosu i2p /opt/i2p/i2psvc /opt/i2p/wrapper.config wrapper.pidfile=/var/tmp/i2p.pid \
|
|
||||||
wrapper.name=i2p \
|
|
||||||
wrapper.displayname="I2P Service" \
|
|
||||||
wrapper.statusfile=/var/tmp/i2p.status \
|
|
||||||
wrapper.java.statusfile=/var/tmp/i2p.java.status \
|
|
||||||
wrapper.logfile=/var/tmp/wrapper.log
|
|
||||||
80
Dockerfile
@@ -1,62 +1,40 @@
|
|||||||
FROM meeh/java8server:latest
|
# Use a multi-stage build to reduce the size of the resulting image
|
||||||
# Docker image based on Alpine with Java.
|
# We need alpine >v3 in order to install an apache-ant > 1.9
|
||||||
|
FROM debian:buster-slim as builder
|
||||||
|
|
||||||
# We use Oracle Java to run I2P, but uses the openjdk to build it.
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
# Build installer
|
||||||
|
RUN apt-get update \
|
||||||
|
# Workaround for installing openjdk-11-jre-headless
|
||||||
|
&& mkdir -p /usr/share/man/man1 \
|
||||||
|
&& apt-get -qqqy install debhelper ant debconf gettext libgmp-dev po-debconf fakeroot \
|
||||||
|
build-essential quilt dh-apparmor dh-systemd libservice-wrapper-java libjson-simple-java \
|
||||||
|
devscripts libjetty9-java libtomcat9-java libtaglibs-standard-jstlel-java libgetopt-java \
|
||||||
|
bash-completion
|
||||||
|
|
||||||
MAINTAINER Mikal Villa <mikal@sigterm.no>
|
WORKDIR /tmp/build
|
||||||
|
COPY . ./
|
||||||
|
|
||||||
ENV GIT_BRANCH="master"
|
RUN ant debian
|
||||||
ENV I2P_PREFIX="/opt/i2p"
|
|
||||||
ENV PATH=${I2P_PREFIX}/bin:$PATH
|
|
||||||
ENV JAVA_HOME=/usr/lib/jvm/default-jvm
|
|
||||||
|
|
||||||
ENV GOSU_VERSION=1.7
|
# Second stage only using the installer from the last stage
|
||||||
ENV GOSU_SHASUM="34049cfc713e8b74b90d6de49690fa601dc040021980812b2f1f691534be8a50 /usr/local/bin/gosu"
|
# ---------------------------------------------------------
|
||||||
|
# We can't use alpine here as the java service wrapper is built with glibc
|
||||||
RUN mkdir /user && adduser -S -h /user i2p && chown -R i2p:nobody /user
|
# alpine uses musl
|
||||||
|
FROM openjdk:11.0-jre-slim
|
||||||
# Adding files first, since Docker.expt is required for installation
|
|
||||||
ADD Docker.expt /tmp/Docker.expt
|
|
||||||
ADD Docker.entrypoint.sh /entrypoint.sh
|
|
||||||
|
|
||||||
# Required for wget https
|
|
||||||
RUN apk add --no-cache openssl
|
|
||||||
# Gosu is a replacement for su/sudo in docker and not a backdoor :) See https://github.com/tianon/gosu
|
|
||||||
RUN wget -O /usr/local/bin/gosu https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-amd64 \
|
|
||||||
&& echo "${GOSU_SHASUM}" | sha256sum -c && chmod +x /usr/local/bin/gosu
|
|
||||||
|
|
||||||
#
|
|
||||||
# Each RUN is a layer, adding the dependencies and building i2pd in one layer takes around 8-900Mb, so to keep the
|
|
||||||
# image under 200mb we need to remove all the build dependencies in the same "RUN" / layer.
|
|
||||||
#
|
|
||||||
|
|
||||||
# The main layer
|
|
||||||
RUN apk --no-cache add build-base git gettext tar bzip2 apache-ant openjdk8 expect \
|
|
||||||
&& mkdir -p /usr/src/build \
|
|
||||||
&& cd /usr/src/build \
|
|
||||||
&& git clone -b ${GIT_BRANCH} https://github.com/i2p/i2p.i2p.git \
|
|
||||||
&& cd /usr/src/build/i2p.i2p \
|
|
||||||
&& echo "noExe=true" >> build.properties \
|
|
||||||
&& ant installer-linux \
|
|
||||||
&& cp i2pinstall*.jar /tmp/i2pinstall.jar \
|
|
||||||
&& mkdir -p /opt \
|
|
||||||
&& chown i2p:root /opt \
|
|
||||||
&& chmod u+rw /opt \
|
|
||||||
&& gosu i2p expect -f /tmp/Docker.expt \
|
|
||||||
&& cd ${I2P_PREFIX} \
|
|
||||||
&& rm -fr man docs *.bat *.command *.app /tmp/i2pinstall.jar /tmp/Docker.expt \
|
|
||||||
&& rm -fr /usr/src/build \
|
|
||||||
&& apk --purge del build-base apache-ant expect tcl expat git openjdk8 openjdk8-jre openjdk8-jre-base openjdk8-jre-lib bzip2 tar \
|
|
||||||
binutils-libs binutils pkgconfig libcurl libc-dev musl-dev g++ make fortify-headers pkgconf giflib libssh2 libxdmcp libxcb \
|
|
||||||
libx11 pcre alsa-lib libxi libxrender libxml2 readline bash openssl \
|
|
||||||
&& rm -fr /usr/lib/jvm/default-jre \
|
|
||||||
&& ln -sf /opt/jdk/jre /usr/lib/jvm/default-jre \
|
|
||||||
&& chmod a+x /entrypoint.sh
|
|
||||||
|
|
||||||
|
# "install" i2p by copying over installed files
|
||||||
|
COPY --from=builder /tmp/*.deb /tmp/
|
||||||
|
|
||||||
|
# Install and configure
|
||||||
|
RUN apt-get update -qqq \
|
||||||
|
&& apt-get -qqqy install geoip-database famfamfam-flag-png \
|
||||||
|
&& dpkb -i /tmp/*
|
||||||
|
|
||||||
EXPOSE 7654 7656 7657 7658 4444 6668 8998 7659 7660 4445 15000-20000
|
EXPOSE 7654 7656 7657 7658 4444 6668 8998 7659 7660 4445 15000-20000
|
||||||
|
|
||||||
ENTRYPOINT [ "/entrypoint.sh" ]
|
USER i2psvc
|
||||||
|
ENTRYPOINT [ "/usr/bin/i2prouter" ]
|
||||||
|
CMD start
|
||||||
|
|
||||||
|
|||||||
@@ -94,6 +94,12 @@ Public domain except as listed below:
|
|||||||
Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
Copyright (C) 2016 Southern Storm Software, Pty Ltd.
|
||||||
See licenses/LICENSE-Noise.txt
|
See licenses/LICENSE-Noise.txt
|
||||||
|
|
||||||
|
MiniDNS library 1.0.0
|
||||||
|
This software may be used under the terms of (at your choice)
|
||||||
|
- LGPL version 2 (or later) (see licenses/LICENSE-LGPL2.1.txt)
|
||||||
|
- Apache Software licence (see licenses/LICENSE-Apache2.0.txt)
|
||||||
|
- DWTFYWTPL
|
||||||
|
|
||||||
|
|
||||||
Router (router.jar):
|
Router (router.jar):
|
||||||
Public domain except as listed below:
|
Public domain except as listed below:
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class Daemon {
|
|||||||
* client applications.
|
* client applications.
|
||||||
* @param published
|
* @param published
|
||||||
* The published AddressBook. This address book is published on
|
* The published AddressBook. This address book is published on
|
||||||
* the user's eepsite so that others may subscribe to it.
|
* the user's I2P Site so that others may subscribe to it.
|
||||||
* May be null.
|
* May be null.
|
||||||
* If non-null, overwrite with the new addressbook.
|
* If non-null, overwrite with the new addressbook.
|
||||||
* @param subscriptions
|
* @param subscriptions
|
||||||
@@ -111,7 +111,7 @@ class Daemon {
|
|||||||
* The NamingService to update, generally the root NamingService from the context.
|
* The NamingService to update, generally the root NamingService from the context.
|
||||||
* @param published
|
* @param published
|
||||||
* The published AddressBook. This address book is published on
|
* The published AddressBook. This address book is published on
|
||||||
* the user's eepsite so that others may subscribe to it.
|
* the user's I2P Site so that others may subscribe to it.
|
||||||
* May be null.
|
* May be null.
|
||||||
* If non-null, overwrite with the new addressbook.
|
* If non-null, overwrite with the new addressbook.
|
||||||
* @param subscriptions
|
* @param subscriptions
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ sourceSets {
|
|||||||
main {
|
main {
|
||||||
java {
|
java {
|
||||||
srcDir 'src'
|
srcDir 'src'
|
||||||
|
srcDir 'build/messages-src'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,3 +13,14 @@ dependencies {
|
|||||||
compile project(':installer')
|
compile project(':installer')
|
||||||
compile project(':apps:systray')
|
compile project(':apps:systray')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create the java files from the po files. The jar task will compile them.
|
||||||
|
// This requires gettext 0.19 or higher.
|
||||||
|
// We don't support the "slow way"
|
||||||
|
task bundle {
|
||||||
|
doLast {
|
||||||
|
if (!(new File("$buildDir/classes/java/main/net/i2p/desktopgui/messages_de.class")).exists())
|
||||||
|
println "apps/desktopgui/bundle-messages.sh".execute().text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
jar.dependsOn bundle
|
||||||
|
|||||||
@@ -80,12 +80,14 @@
|
|||||||
<!-- set if unset -->
|
<!-- set if unset -->
|
||||||
<property name="workspace.changes.tr" value="" />
|
<property name="workspace.changes.tr" value="" />
|
||||||
<!-- ideal for linux: 24x24, but transparency doesn't work -->
|
<!-- ideal for linux: 24x24, but transparency doesn't work -->
|
||||||
<copy tofile="${build}/desktopgui/resources/images/logo.png" file="../../installer/resources/themes/console/images/itoopie_xsm.png" />
|
<copy tofile="${build}/desktopgui/resources/images/logo.png" file="../../apps/routerconsole/jsp/themes/console/images/itoopie_xsm.png" />
|
||||||
<copy todir="${build}/desktopgui/resources/images" file="images/itoopie_black_24.png" />
|
<copy todir="${build}/desktopgui/resources/images" file="images/itoopie_black_24.png" />
|
||||||
<copy todir="${build}/desktopgui/resources/images" file="images/itoopie_white_24.png" />
|
<copy todir="${build}/desktopgui/resources/images" file="images/itoopie_white_24.png" />
|
||||||
<jar basedir="${build}" excludes="messages-src/**" destfile="${dist}/${jar}">
|
<jar basedir="${build}" excludes="messages-src/**" destfile="${dist}/${jar}">
|
||||||
<manifest>
|
<manifest>
|
||||||
<attribute name="Main-Class" value="net.i2p.desktopgui.Main"/>
|
<attribute name="Main-Class" value="net.i2p.desktopgui.Main"/>
|
||||||
|
<attribute name="Implementation-Version" value="${full.version}" />
|
||||||
|
<attribute name="Built-By" value="${build.built-by}" />
|
||||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||||
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
|
<attribute name="Workspace-Changes" value="${workspace.changes.tr}" />
|
||||||
|
|||||||
1
apps/desktopgui/bundle-messages.sh
Normal file → Executable file
@@ -11,6 +11,7 @@
|
|||||||
# zzz - public domain
|
# zzz - public domain
|
||||||
# Mathiasdm - modifications for desktopgui
|
# Mathiasdm - modifications for desktopgui
|
||||||
#
|
#
|
||||||
|
cd `dirname $0`
|
||||||
CLASS=net.i2p.desktopgui.messages
|
CLASS=net.i2p.desktopgui.messages
|
||||||
TMPFILE=build/javafiles.txt
|
TMPFILE=build/javafiles.txt
|
||||||
export TZ=UTC
|
export TZ=UTC
|
||||||
|
|||||||
22
apps/i2pcontrol/build.gradle
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
plugins {
|
||||||
|
id 'war'
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDir 'java'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
providedCompile project(':router')
|
||||||
|
providedCompile project(':apps:jetty')
|
||||||
|
providedCompile files('../../installer/lib/wrapper/all/wrapper.jar')
|
||||||
|
}
|
||||||
|
|
||||||
|
war {
|
||||||
|
archiveName 'jsonrpc.war'
|
||||||
|
webXml = file('web.xml')
|
||||||
|
}
|
||||||
@@ -93,6 +93,8 @@
|
|||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="jar" depends="compile">
|
<target name="jar" depends="compile">
|
||||||
|
<!-- set if unset -->
|
||||||
|
<property name="workspace.changes.tr" value="" />
|
||||||
<jar destfile="build/i2pcontrol.jar" basedir="./build/obj" includes="**/*.class" >
|
<jar destfile="build/i2pcontrol.jar" basedir="./build/obj" includes="**/*.class" >
|
||||||
<manifest>
|
<manifest>
|
||||||
<attribute name="Implementation-Version" value="${full.version}" />
|
<attribute name="Implementation-Version" value="${full.version}" />
|
||||||
@@ -107,6 +109,8 @@
|
|||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="socketJar" depends="compileSocketJar">
|
<target name="socketJar" depends="compileSocketJar">
|
||||||
|
<!-- set if unset -->
|
||||||
|
<property name="workspace.changes.tr" value="" />
|
||||||
<jar destfile="build/i2pcontrol.jar" basedir="./build/obj" includes="**/*.class" >
|
<jar destfile="build/i2pcontrol.jar" basedir="./build/obj" includes="**/*.class" >
|
||||||
<manifest>
|
<manifest>
|
||||||
<attribute name="Implementation-Version" value="${full.version}" />
|
<attribute name="Implementation-Version" value="${full.version}" />
|
||||||
@@ -121,6 +125,8 @@
|
|||||||
</target>
|
</target>
|
||||||
|
|
||||||
<target name="war" depends="compile" >
|
<target name="war" depends="compile" >
|
||||||
|
<!-- set if unset -->
|
||||||
|
<property name="workspace.changes.tr" value="" />
|
||||||
<war destfile="build/jsonrpc.war" webxml="web.xml" >
|
<war destfile="build/jsonrpc.war" webxml="web.xml" >
|
||||||
<classes dir="./build/obj" excludes="net/i2p/i2pcontrol/I2PControlController.class net/i2p/i2pcontrol/HostCheckHandler.class net/i2p/i2pcontrol/SocketController*.class" />
|
<classes dir="./build/obj" excludes="net/i2p/i2pcontrol/I2PControlController.class net/i2p/i2pcontrol/HostCheckHandler.class net/i2p/i2pcontrol/SocketController*.class" />
|
||||||
<manifest>
|
<manifest>
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ import java.util.StringTokenizer;
|
|||||||
* This handles the starting and stopping of Jetty
|
* This handles the starting and stopping of Jetty
|
||||||
* from a single static class so it can be called via clients.config.
|
* from a single static class so it can be called via clients.config.
|
||||||
*
|
*
|
||||||
* This makes installation of a new eepsite a turnkey operation.
|
* This makes installation of a new I2P Site a turnkey operation.
|
||||||
*
|
*
|
||||||
* Usage: I2PControlController -d $PLUGIN [start|stop]
|
* Usage: I2PControlController -d $PLUGIN [start|stop]
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -6,13 +6,14 @@ sourceSets {
|
|||||||
main {
|
main {
|
||||||
java {
|
java {
|
||||||
srcDir 'java/src'
|
srcDir 'java/src'
|
||||||
|
srcDir 'java/build/messages-src'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':core')
|
compile project(':core')
|
||||||
compile project(':apps:systray')
|
providedCompile project(':apps:systray')
|
||||||
compile 'gnu.getopt:java-getopt:1.0.13'
|
compile 'gnu.getopt:java-getopt:1.0.13'
|
||||||
providedCompile project(':apps:ministreaming')
|
providedCompile project(':apps:ministreaming')
|
||||||
providedCompile project(':apps:jetty')
|
providedCompile project(':apps:jetty')
|
||||||
@@ -34,10 +35,26 @@ artifacts {
|
|||||||
archives i2psnarkJar
|
archives i2psnarkJar
|
||||||
}
|
}
|
||||||
|
|
||||||
war {
|
// Create the java files from the po files. The jar task will compile them.
|
||||||
into '.icons', {
|
// This requires gettext 0.19 or higher.
|
||||||
from 'icons'
|
// We don't support the "slow way"
|
||||||
|
task bundle {
|
||||||
|
doLast {
|
||||||
|
if (!(new File("$buildDir/classes/java/main/org/klomp/snark/web/messages_de.class")).exists())
|
||||||
|
println "apps/i2psnark/java/bundle-messages.sh".execute().text
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
war.dependsOn bundle
|
||||||
|
|
||||||
|
war {
|
||||||
|
rootSpec.exclude('/org/klomp/snark/*.class')
|
||||||
|
rootSpec.exclude('/org/klomp/snark/bencode/**')
|
||||||
|
rootSpec.exclude('/org/klomp/snark/comments/**')
|
||||||
|
rootSpec.exclude('/org/klomp/snark/dht/**')
|
||||||
|
rootSpec.exclude('/org/klomp/snark/standalone/**')
|
||||||
|
from ('resources', {
|
||||||
|
into ".resources"
|
||||||
|
})
|
||||||
webInf {
|
webInf {
|
||||||
into 'classes/org/klomp/snark/web'
|
into 'classes/org/klomp/snark/web'
|
||||||
from 'mime.properties'
|
from 'mime.properties'
|
||||||
|
|||||||
@@ -250,51 +250,51 @@
|
|||||||
|
|
||||||
<!-- add css, image, and js files for standalone snark to the war -->
|
<!-- add css, image, and js files for standalone snark to the war -->
|
||||||
<target name="standalone_war" depends="war">
|
<target name="standalone_war" depends="war">
|
||||||
<mkdir dir="build/standalone-resources/.resources/themes/snark" />
|
<mkdir dir="build/standalone-resources/.resources/themes/" />
|
||||||
<copy todir="build/standalone-resources/.resources/themes/snark" >
|
<copy todir="build/standalone-resources/.resources/themes/" >
|
||||||
<fileset dir="../../../installer/resources/themes/snark/" />
|
<fileset dir="../resources/themes/" />
|
||||||
</copy>
|
</copy>
|
||||||
|
|
||||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
<replace dir="build/standalone-resources/.resources/themes"
|
||||||
summary="true"
|
summary="true"
|
||||||
token="url(/themes/console/dark/images/"
|
token="url(/themes/console/dark/images/"
|
||||||
value="url(/i2psnark/.resources/themes/snark/dark/images/" >
|
value="url(/i2psnark/.resources/themes/dark/images/" >
|
||||||
<include name="**/*.css" />
|
<include name="**/*.css" />
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
<replace dir="build/standalone-resources/.resources/themes"
|
||||||
summary="true"
|
summary="true"
|
||||||
token="url(../../console/light/images/"
|
token="url(../../console/light/images/"
|
||||||
value="url(/i2psnark/.resources/themes/snark/light/images/" >
|
value="url(/i2psnark/.resources/themes/light/images/" >
|
||||||
<include name="**/*.css" />
|
<include name="**/*.css" />
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
<replace dir="build/standalone-resources/.resources/themes"
|
||||||
summary="true"
|
summary="true"
|
||||||
token="url(/themes/console/light/images/"
|
token="url(/themes/console/light/images/"
|
||||||
value="url(/i2psnark/.resources/themes/snark/light/images/" >
|
value="url(/i2psnark/.resources/themes/light/images/" >
|
||||||
<include name="**/*.css" />
|
<include name="**/*.css" />
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
<replace dir="build/standalone-resources/.resources/themes"
|
||||||
summary="true"
|
summary="true"
|
||||||
token="url(/themes/console/images/transparent.gif"
|
token="url(/themes/console/images/transparent.gif"
|
||||||
value="url(/i2psnark/.resources/themes/snark/ubergine/images/transparent.gif" >
|
value="url(/i2psnark/.resources/themes/ubergine/images/transparent.gif" >
|
||||||
<include name="**/*.css" />
|
<include name="**/*.css" />
|
||||||
</replace>
|
</replace>
|
||||||
<replace dir="build/standalone-resources/.resources/themes/snark"
|
<replace dir="build/standalone-resources/.resources/themes"
|
||||||
summary="true"
|
summary="true"
|
||||||
token="url(/themes/console/images/info/"
|
token="url(/themes/console/images/info/"
|
||||||
value="url(/i2psnark/.resources/themes/snark/ubergine/images/" >
|
value="url(/i2psnark/.resources/themes/ubergine/images/" >
|
||||||
<include name="**/*.css" />
|
<include name="**/*.css" />
|
||||||
</replace>
|
</replace>
|
||||||
|
|
||||||
<!-- Rather than pulling in all the console theme images, let's just specify the ones we need -->
|
<!-- Rather than pulling in all the console theme images, let's just specify the ones we need -->
|
||||||
<copy file="../../../installer/resources/themes/console/images/transparent.gif"
|
<copy file="../../routerconsole/jsp/themes/console/images/transparent.gif"
|
||||||
todir="build/standalone-resources/.resources/themes/snark/ubergine/images" />
|
todir="build/standalone-resources/.resources/themes/ubergine/images" />
|
||||||
<copy file="../../../installer/resources/themes/console/dark/images/header.png"
|
<copy file="../../routerconsole/jsp/themes/console/dark/images/header.png"
|
||||||
todir="build/standalone-resources/.resources/themes/snark/dark/images" />
|
todir="build/standalone-resources/.resources/themes/dark/images" />
|
||||||
<copy file="../../../installer/resources/themes/console/light/images/header.png"
|
<copy file="../../routerconsole/jsp/themes/console/light/images/header.png"
|
||||||
todir="build/standalone-resources/.resources/themes/snark/light/images" />
|
todir="build/standalone-resources/.resources/themes/light/images" />
|
||||||
<copy file="../../../installer/resources/themes/console/images/info/errortriangle.png"
|
<copy file="../../routerconsole/jsp/themes/console/images/info/errortriangle.png"
|
||||||
todir="build/standalone-resources/.resources/themes/snark/ubergine/images" />
|
todir="build/standalone-resources/.resources/themes/ubergine/images" />
|
||||||
|
|
||||||
<mkdir dir="build/standalone-resources/.resources/js" />
|
<mkdir dir="build/standalone-resources/.resources/js" />
|
||||||
<copy file="../../routerconsole/jsp/js/ajax.js" todir="build/standalone-resources/.resources/js" />
|
<copy file="../../routerconsole/jsp/js/ajax.js" todir="build/standalone-resources/.resources/js" />
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#
|
#
|
||||||
# zzz - public domain
|
# zzz - public domain
|
||||||
#
|
#
|
||||||
|
cd `dirname $0`
|
||||||
CLASS=org.klomp.snark.web.messages
|
CLASS=org.klomp.snark.web.messages
|
||||||
TMPFILE=build/javafiles.txt
|
TMPFILE=build/javafiles.txt
|
||||||
export TZ=UTC
|
export TZ=UTC
|
||||||
|
|||||||
@@ -5,8 +5,10 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
@@ -57,7 +59,6 @@ public class I2PSnarkUtil {
|
|||||||
private int _i2cpPort;
|
private int _i2cpPort;
|
||||||
private final Map<String, String> _opts;
|
private final Map<String, String> _opts;
|
||||||
private volatile I2PSocketManager _manager;
|
private volatile I2PSocketManager _manager;
|
||||||
private boolean _configured;
|
|
||||||
private volatile boolean _connecting;
|
private volatile boolean _connecting;
|
||||||
private final Set<Hash> _banlist;
|
private final Set<Hash> _banlist;
|
||||||
private int _maxUploaders;
|
private int _maxUploaders;
|
||||||
@@ -84,6 +85,10 @@ public class I2PSnarkUtil {
|
|||||||
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
|
public static final String PROP_MAX_BW = "i2cp.outboundBytesPerSecond";
|
||||||
public static final boolean DEFAULT_USE_DHT = true;
|
public static final boolean DEFAULT_USE_DHT = true;
|
||||||
public static final String EEPGET_USER_AGENT = "I2PSnark";
|
public static final String EEPGET_USER_AGENT = "I2PSnark";
|
||||||
|
private static final List<String> HIDDEN_I2CP_OPTS = Arrays.asList(new String[] {
|
||||||
|
PROP_MAX_BW, "inbound.length", "outbound.length", "inbound.quantity", "outbound.quantity"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
public I2PSnarkUtil(I2PAppContext ctx) {
|
public I2PSnarkUtil(I2PAppContext ctx) {
|
||||||
this(ctx, "i2psnark");
|
this(ctx, "i2psnark");
|
||||||
@@ -142,25 +147,34 @@ public class I2PSnarkUtil {
|
|||||||
/** @since 0.9.1 */
|
/** @since 0.9.1 */
|
||||||
public I2PAppContext getContext() { return _context; }
|
public I2PAppContext getContext() { return _context; }
|
||||||
|
|
||||||
public boolean configured() { return _configured; }
|
/**
|
||||||
|
* @param i2cpHost may be null for no change
|
||||||
|
* @param i2cpPort may be 0 for no change
|
||||||
|
* @param opts may be null for no change
|
||||||
|
*/
|
||||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) {
|
public void setI2CPConfig(String i2cpHost, int i2cpPort, Map opts) {
|
||||||
if (i2cpHost != null)
|
if (i2cpHost != null)
|
||||||
_i2cpHost = i2cpHost;
|
_i2cpHost = i2cpHost;
|
||||||
if (i2cpPort > 0)
|
if (i2cpPort > 0)
|
||||||
_i2cpPort = i2cpPort;
|
_i2cpPort = i2cpPort;
|
||||||
// can't remove any options this way...
|
if (opts != null) {
|
||||||
if (opts != null)
|
synchronized(_opts) {
|
||||||
|
// removed options...
|
||||||
|
for (Iterator<String> iter = _opts.keySet().iterator(); iter.hasNext(); ) {
|
||||||
|
String k = iter.next();
|
||||||
|
if (!HIDDEN_I2CP_OPTS.contains(k) && !opts.containsKey(k))
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
_opts.putAll(opts);
|
_opts.putAll(opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
// this updates the session options and tells the router
|
// this updates the session options and tells the router
|
||||||
setMaxUpBW(_maxUpBW);
|
setMaxUpBW(_maxUpBW);
|
||||||
_configured = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaxUploaders(int limit) {
|
public void setMaxUploaders(int limit) {
|
||||||
_maxUploaders = limit;
|
_maxUploaders = limit;
|
||||||
_configured = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,13 +183,16 @@ public class I2PSnarkUtil {
|
|||||||
*/
|
*/
|
||||||
public void setMaxUpBW(int limit) {
|
public void setMaxUpBW(int limit) {
|
||||||
_maxUpBW = limit;
|
_maxUpBW = limit;
|
||||||
|
synchronized(_opts) {
|
||||||
_opts.put(PROP_MAX_BW, Integer.toString(limit * (1024 * 6 / 5))); // add a little for overhead
|
_opts.put(PROP_MAX_BW, Integer.toString(limit * (1024 * 6 / 5))); // add a little for overhead
|
||||||
_configured = true;
|
}
|
||||||
if (_manager != null) {
|
if (_manager != null) {
|
||||||
I2PSession sess = _manager.getSession();
|
I2PSession sess = _manager.getSession();
|
||||||
if (sess != null) {
|
if (sess != null) {
|
||||||
Properties newProps = new Properties();
|
Properties newProps = new Properties();
|
||||||
|
synchronized(_opts) {
|
||||||
newProps.putAll(_opts);
|
newProps.putAll(_opts);
|
||||||
|
}
|
||||||
sess.updateOptions(newProps);
|
sess.updateOptions(newProps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,17 +200,24 @@ public class I2PSnarkUtil {
|
|||||||
|
|
||||||
public void setMaxConnections(int limit) {
|
public void setMaxConnections(int limit) {
|
||||||
_maxConnections = limit;
|
_maxConnections = limit;
|
||||||
_configured = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStartupDelay(int minutes) {
|
public void setStartupDelay(int minutes) {
|
||||||
_startupDelay = minutes;
|
_startupDelay = minutes;
|
||||||
_configured = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getI2CPHost() { return _i2cpHost; }
|
public String getI2CPHost() { return _i2cpHost; }
|
||||||
public int getI2CPPort() { return _i2cpPort; }
|
public int getI2CPPort() { return _i2cpPort; }
|
||||||
public Map<String, String> getI2CPOptions() { return _opts; }
|
|
||||||
|
/**
|
||||||
|
* @return a copy
|
||||||
|
*/
|
||||||
|
public Map<String, String> getI2CPOptions() {
|
||||||
|
synchronized(_opts) {
|
||||||
|
return new HashMap<String, String>(_opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getEepProxyHost() { return _proxyHost; }
|
public String getEepProxyHost() { return _proxyHost; }
|
||||||
public int getEepProxyPort() { return _proxyPort; }
|
public int getEepProxyPort() { return _proxyPort; }
|
||||||
public boolean getEepProxySet() { return _shouldProxy; }
|
public boolean getEepProxySet() { return _shouldProxy; }
|
||||||
@@ -225,9 +249,8 @@ public class I2PSnarkUtil {
|
|||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
_log.debug("Connecting to I2P", new Exception("I did it"));
|
_log.debug("Connecting to I2P", new Exception("I did it"));
|
||||||
Properties opts = _context.getProperties();
|
Properties opts = _context.getProperties();
|
||||||
if (_opts != null) {
|
synchronized(_opts) {
|
||||||
for (Map.Entry<String, String> entry : _opts.entrySet() )
|
opts.putAll(_opts);
|
||||||
opts.setProperty(entry.getKey(), entry.getValue());
|
|
||||||
}
|
}
|
||||||
// override preference and start with two tunnels. IdleChecker will ramp up/down as necessary
|
// override preference and start with two tunnels. IdleChecker will ramp up/down as necessary
|
||||||
String sin = opts.getProperty("inbound.quantity");
|
String sin = opts.getProperty("inbound.quantity");
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class MetaInfo
|
|||||||
private final String name_utf8;
|
private final String name_utf8;
|
||||||
private final List<List<String>> files;
|
private final List<List<String>> files;
|
||||||
private final List<List<String>> files_utf8;
|
private final List<List<String>> files_utf8;
|
||||||
private final BitField paddingFileBitfield;
|
private final List<String> attributes;
|
||||||
private final List<Long> lengths;
|
private final List<Long> lengths;
|
||||||
private final int piece_length;
|
private final int piece_length;
|
||||||
private final byte[] piece_hashes;
|
private final byte[] piece_hashes;
|
||||||
@@ -104,7 +104,7 @@ public class MetaInfo
|
|||||||
this.url_list = url_list;
|
this.url_list = url_list;
|
||||||
|
|
||||||
// TODO BEP 52 hybrid torrent with piece layers, meta version and file tree
|
// TODO BEP 52 hybrid torrent with piece layers, meta version and file tree
|
||||||
this.paddingFileBitfield = null;
|
this.attributes = null;
|
||||||
|
|
||||||
// TODO if we add a parameter for other keys
|
// TODO if we add a parameter for other keys
|
||||||
//if (other != null) {
|
//if (other != null) {
|
||||||
@@ -281,7 +281,7 @@ public class MetaInfo
|
|||||||
files = null;
|
files = null;
|
||||||
files_utf8 = null;
|
files_utf8 = null;
|
||||||
lengths = null;
|
lengths = null;
|
||||||
paddingFileBitfield = null;
|
attributes = null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -299,7 +299,7 @@ public class MetaInfo
|
|||||||
List<List<String>> m_files = new ArrayList<List<String>>(size);
|
List<List<String>> m_files = new ArrayList<List<String>>(size);
|
||||||
List<List<String>> m_files_utf8 = null;
|
List<List<String>> m_files_utf8 = null;
|
||||||
List<Long> m_lengths = new ArrayList<Long>(size);
|
List<Long> m_lengths = new ArrayList<Long>(size);
|
||||||
BitField paddingBitfield = null;
|
List<String> m_attributes = null;
|
||||||
long l = 0;
|
long l = 0;
|
||||||
for (int i = 0; i < list.size(); i++)
|
for (int i = 0; i < list.size(); i++)
|
||||||
{
|
{
|
||||||
@@ -361,18 +361,23 @@ public class MetaInfo
|
|||||||
val = desc.get("attr");
|
val = desc.get("attr");
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
String s = val.getString();
|
String s = val.getString();
|
||||||
if (s.contains("p")) {
|
if (m_attributes == null) {
|
||||||
if (paddingBitfield == null)
|
m_attributes = new ArrayList<String>(size);
|
||||||
paddingBitfield = new BitField(size);
|
for (int j = 0; j < i; j++) {
|
||||||
paddingBitfield.set(i);
|
m_attributes.add("");
|
||||||
}
|
}
|
||||||
|
m_attributes.add(s);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (m_attributes != null)
|
||||||
|
m_attributes.add("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
files = Collections.unmodifiableList(m_files);
|
files = Collections.unmodifiableList(m_files);
|
||||||
files_utf8 = m_files_utf8 != null ? Collections.unmodifiableList(m_files_utf8) : null;
|
files_utf8 = m_files_utf8 != null ? Collections.unmodifiableList(m_files_utf8) : null;
|
||||||
lengths = Collections.unmodifiableList(m_lengths);
|
lengths = Collections.unmodifiableList(m_lengths);
|
||||||
length = l;
|
length = l;
|
||||||
paddingFileBitfield = paddingBitfield;
|
attributes = m_attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
info_hash = calculateInfoHash();
|
info_hash = calculateInfoHash();
|
||||||
@@ -477,9 +482,9 @@ public class MetaInfo
|
|||||||
* @since 0.9.48
|
* @since 0.9.48
|
||||||
*/
|
*/
|
||||||
public boolean isPaddingFile(int filenum) {
|
public boolean isPaddingFile(int filenum) {
|
||||||
if (paddingFileBitfield == null)
|
if (attributes == null)
|
||||||
return false;
|
return false;
|
||||||
return paddingFileBitfield.get(filenum);
|
return attributes.get(filenum).indexOf('p') >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -749,6 +754,12 @@ public class MetaInfo
|
|||||||
file.put("path.utf-8", new BEValue(beufiles));
|
file.put("path.utf-8", new BEValue(beufiles));
|
||||||
}
|
}
|
||||||
file.put("length", new BEValue(lengths.get(i)));
|
file.put("length", new BEValue(lengths.get(i)));
|
||||||
|
String attr = null;
|
||||||
|
if (attributes != null) {
|
||||||
|
attr = attributes.get(i);
|
||||||
|
if (attr.length() > 0)
|
||||||
|
file.put("attr", new BEValue(DataHelper.getASCII(attr)));
|
||||||
|
}
|
||||||
l.add(new BEValue(file));
|
l.add(new BEValue(file));
|
||||||
}
|
}
|
||||||
info.put("files", new BEValue(l));
|
info.put("files", new BEValue(l));
|
||||||
|
|||||||
@@ -42,14 +42,14 @@ import org.klomp.snark.bencode.InvalidBEncodingException;
|
|||||||
|
|
||||||
public class Peer implements Comparable<Peer>
|
public class Peer implements Comparable<Peer>
|
||||||
{
|
{
|
||||||
private final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(Peer.class);
|
protected final Log _log = I2PAppContext.getGlobalContext().logManager().getLog(getClass());
|
||||||
// Identifying property, the peer id of the other side.
|
// Identifying property, the peer id of the other side.
|
||||||
private final PeerID peerID;
|
private final PeerID peerID;
|
||||||
|
|
||||||
private final byte[] my_id;
|
private final byte[] my_id;
|
||||||
private final byte[] infohash;
|
private final byte[] infohash;
|
||||||
/** will start out null in magnet mode */
|
/** will start out null in magnet mode */
|
||||||
private MetaInfo metainfo;
|
protected MetaInfo metainfo;
|
||||||
private Map<String, BEValue> handshakeMap;
|
private Map<String, BEValue> handshakeMap;
|
||||||
|
|
||||||
// The data in/output streams set during the handshake and used by
|
// The data in/output streams set during the handshake and used by
|
||||||
@@ -61,8 +61,11 @@ public class Peer implements Comparable<Peer>
|
|||||||
private final AtomicLong downloaded = new AtomicLong();
|
private final AtomicLong downloaded = new AtomicLong();
|
||||||
private final AtomicLong uploaded = new AtomicLong();
|
private final AtomicLong uploaded = new AtomicLong();
|
||||||
|
|
||||||
// Keeps state for in/out connections. Non-null when the handshake
|
/** `
|
||||||
// was successful, the connection setup and runs
|
* Keeps state for in/out connections. Non-null when the handshake
|
||||||
|
* was successful, the connection setup and runs.
|
||||||
|
* Do not access directly. All actions should be through Peer methods.
|
||||||
|
*/
|
||||||
volatile PeerState state;
|
volatile PeerState state;
|
||||||
|
|
||||||
/** shared across all peers on this torrent */
|
/** shared across all peers on this torrent */
|
||||||
@@ -77,8 +80,8 @@ public class Peer implements Comparable<Peer>
|
|||||||
|
|
||||||
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
|
final static long CHECK_PERIOD = PeerCoordinator.CHECK_PERIOD; // 40 seconds
|
||||||
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
|
final static int RATE_DEPTH = PeerCoordinator.RATE_DEPTH; // make following arrays RATE_DEPTH long
|
||||||
private long uploaded_old[] = {-1,-1,-1};
|
private final long uploaded_old[] = {-1,-1,-1};
|
||||||
private long downloaded_old[] = {-1,-1,-1};
|
private final long downloaded_old[] = {-1,-1,-1};
|
||||||
|
|
||||||
private static final byte[] HANDSHAKE = DataHelper.getASCII("BitTorrent protocol");
|
private static final byte[] HANDSHAKE = DataHelper.getASCII("BitTorrent protocol");
|
||||||
// See BEP 4 for definitions
|
// See BEP 4 for definitions
|
||||||
@@ -341,7 +344,6 @@ public class Peer implements Comparable<Peer>
|
|||||||
private byte[] handshake(InputStream in, OutputStream out)
|
private byte[] handshake(InputStream in, OutputStream out)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
din = new DataInputStream(in);
|
|
||||||
dout = new DataOutputStream(out);
|
dout = new DataOutputStream(out);
|
||||||
|
|
||||||
// Handshake write - header
|
// Handshake write - header
|
||||||
@@ -366,6 +368,7 @@ public class Peer implements Comparable<Peer>
|
|||||||
_log.debug("Wrote my shared hash and ID to " + toString());
|
_log.debug("Wrote my shared hash and ID to " + toString());
|
||||||
|
|
||||||
// Handshake read - header
|
// Handshake read - header
|
||||||
|
din = new DataInputStream(in);
|
||||||
byte b = din.readByte();
|
byte b = din.readByte();
|
||||||
if (b != HANDSHAKE.length)
|
if (b != HANDSHAKE.length)
|
||||||
throw new IOException("Handshake failure, expected 19, got "
|
throw new IOException("Handshake failure, expected 19, got "
|
||||||
@@ -789,4 +792,12 @@ public class Peer implements Comparable<Peer>
|
|||||||
void setTotalCommentsSent(int count) {
|
void setTotalCommentsSent(int count) {
|
||||||
_totalCommentsSent = count;
|
_totalCommentsSent = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return false
|
||||||
|
* @since 0.9.49
|
||||||
|
*/
|
||||||
|
public boolean isWebPeer() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import java.util.Arrays;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -153,6 +154,10 @@ class PeerCoordinator implements PeerListener
|
|||||||
private static final long COMMENT_REQ_DELAY = 60*60*1000L;
|
private static final long COMMENT_REQ_DELAY = 60*60*1000L;
|
||||||
private static final int MAX_COMMENT_NOT_REQ = 10;
|
private static final int MAX_COMMENT_NOT_REQ = 10;
|
||||||
|
|
||||||
|
/** hostname to expire time, sync on this */
|
||||||
|
private Map<String, Long> _webPeerBans;
|
||||||
|
private static final long WEBPEER_BAN_TIME = 30*60*1000L;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param metainfo null if in magnet mode
|
* @param metainfo null if in magnet mode
|
||||||
* @param storage null if in magnet mode
|
* @param storage null if in magnet mode
|
||||||
@@ -916,6 +921,7 @@ class PeerCoordinator implements PeerListener
|
|||||||
// As connections are already up, new Pieces will
|
// As connections are already up, new Pieces will
|
||||||
// not have their PeerID list populated, so do that.
|
// not have their PeerID list populated, so do that.
|
||||||
for (Peer p : peers) {
|
for (Peer p : peers) {
|
||||||
|
// TODO don't access state directly
|
||||||
PeerState s = p.state;
|
PeerState s = p.state;
|
||||||
if (s != null) {
|
if (s != null) {
|
||||||
BitField bf = s.bitfield;
|
BitField bf = s.bitfield;
|
||||||
@@ -1062,7 +1068,8 @@ class PeerCoordinator implements PeerListener
|
|||||||
// just in case
|
// just in case
|
||||||
removePartialPiece(piece);
|
removePartialPiece(piece);
|
||||||
// Oops. We didn't actually download this then... :(
|
// Oops. We didn't actually download this then... :(
|
||||||
downloaded.addAndGet(0 - metainfo.getPieceLength(piece));
|
// Reports of counter going negative?
|
||||||
|
//downloaded.addAndGet(0 - metainfo.getPieceLength(piece));
|
||||||
// Mark this peer as not having the piece. PeerState will update its bitfield.
|
// Mark this peer as not having the piece. PeerState will update its bitfield.
|
||||||
for (Piece pc : wantedPieces) {
|
for (Piece pc : wantedPieces) {
|
||||||
if (pc.getId() == piece) {
|
if (pc.getId() == piece) {
|
||||||
@@ -1299,6 +1306,7 @@ class PeerCoordinator implements PeerListener
|
|||||||
if (++seeds >= 4)
|
if (++seeds >= 4)
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
// TODO don't access state directly
|
||||||
PeerState state = pr.state;
|
PeerState state = pr.state;
|
||||||
if (state == null) continue;
|
if (state == null) continue;
|
||||||
BitField bf = state.bitfield;
|
BitField bf = state.bitfield;
|
||||||
@@ -1336,6 +1344,7 @@ class PeerCoordinator implements PeerListener
|
|||||||
// Temporary? So PeerState never calls wantPiece() directly for now...
|
// Temporary? So PeerState never calls wantPiece() directly for now...
|
||||||
Piece piece = wantPiece(peer, havePieces, true);
|
Piece piece = wantPiece(peer, havePieces, true);
|
||||||
if (piece != null) {
|
if (piece != null) {
|
||||||
|
// TODO padding
|
||||||
return new PartialPiece(piece, metainfo.getPieceLength(piece.getId()), _util.getTempDir());
|
return new PartialPiece(piece, metainfo.getPieceLength(piece.getId()), _util.getTempDir());
|
||||||
}
|
}
|
||||||
if (_log.shouldLog(Log.DEBUG))
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
@@ -1452,6 +1461,10 @@ class PeerCoordinator implements PeerListener
|
|||||||
if (bev.getMap().get(ExtensionHandler.TYPE_PEX) != null) {
|
if (bev.getMap().get(ExtensionHandler.TYPE_PEX) != null) {
|
||||||
List<Peer> pList = peerList();
|
List<Peer> pList = peerList();
|
||||||
pList.remove(peer);
|
pList.remove(peer);
|
||||||
|
for (Iterator<Peer> iter = pList.iterator(); iter.hasNext(); ) {
|
||||||
|
if (iter.next().isWebPeer())
|
||||||
|
iter.remove();
|
||||||
|
}
|
||||||
if (!pList.isEmpty())
|
if (!pList.isEmpty())
|
||||||
ExtensionHandler.sendPEX(peer, pList);
|
ExtensionHandler.sendPEX(peer, pList);
|
||||||
}
|
}
|
||||||
@@ -1749,5 +1762,43 @@ class PeerCoordinator implements PeerListener
|
|||||||
public I2PSnarkUtil getUtil() {
|
public I2PSnarkUtil getUtil() {
|
||||||
return _util;
|
return _util;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ban a web peer for this torrent, for while or permanently.
|
||||||
|
* @param host the host name
|
||||||
|
* @since 0.9.49
|
||||||
|
*/
|
||||||
|
public synchronized void banWebPeer(String host, boolean isPermanent) {
|
||||||
|
if (_webPeerBans == null)
|
||||||
|
_webPeerBans = new HashMap<String, Long>(4);
|
||||||
|
Long time;
|
||||||
|
if (isPermanent) {
|
||||||
|
time = Long.valueOf(Long.MAX_VALUE);
|
||||||
|
} else {
|
||||||
|
long now = _util.getContext().clock().now();
|
||||||
|
time = Long.valueOf(now + WEBPEER_BAN_TIME);
|
||||||
|
}
|
||||||
|
Long old = _webPeerBans.put(host, time);
|
||||||
|
if (old != null && old.longValue() > time)
|
||||||
|
_webPeerBans.put(host, old);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is a web peer banned?
|
||||||
|
* @param host the host name
|
||||||
|
* @since 0.9.49
|
||||||
|
*/
|
||||||
|
public synchronized boolean isWebPeerBanned(String host) {
|
||||||
|
if (_webPeerBans == null)
|
||||||
|
return false;
|
||||||
|
Long time = _webPeerBans.get(host);
|
||||||
|
if (time == null)
|
||||||
|
return false;
|
||||||
|
long now = _util.getContext().clock().now();
|
||||||
|
boolean rv = time.longValue() > now;
|
||||||
|
if (!rv)
|
||||||
|
_webPeerBans.remove(host);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -227,6 +227,10 @@ public class PeerID implements Comparable<PeerID>
|
|||||||
{
|
{
|
||||||
if (_toStringCache != null)
|
if (_toStringCache != null)
|
||||||
return _toStringCache;
|
return _toStringCache;
|
||||||
|
if (id != null && DataHelper.eq(id, 0, WebPeer.IDBytes, 0, WebPeer.IDBytes.length)) {
|
||||||
|
_toStringCache = "WebSeed@" + Base32.encode(destHash) + ".b32.i2p";
|
||||||
|
return _toStringCache;
|
||||||
|
}
|
||||||
if (id == null || address == null)
|
if (id == null || address == null)
|
||||||
return "unkn@" + Base64.encode(destHash).substring(0, 6);
|
return "unkn@" + Base64.encode(destHash).substring(0, 6);
|
||||||
int nonZero = 0;
|
int nonZero = 0;
|
||||||
|
|||||||
@@ -1388,6 +1388,7 @@ public class Storage implements Closeable
|
|||||||
//rafs[i].write(bs, off + written, len);
|
//rafs[i].write(bs, off + written, len);
|
||||||
pp.write(raf, written, len);
|
pp.write(raf, written, len);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
try { tf.closeRAF(); } catch (IOException ioe2) {}
|
||||||
// get the file name in the logs
|
// get the file name in the logs
|
||||||
IOException ioe2 = new IOException("Error writing " + tf.RAFfile.getAbsolutePath());
|
IOException ioe2 = new IOException("Error writing " + tf.RAFfile.getAbsolutePath());
|
||||||
ioe2.initCause(ioe);
|
ioe2.initCause(ioe);
|
||||||
@@ -1494,6 +1495,7 @@ public class Storage implements Closeable
|
|||||||
raf.seek(start);
|
raf.seek(start);
|
||||||
raf.readFully(bs, read, len);
|
raf.readFully(bs, read, len);
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
|
try { tf.closeRAF(); } catch (IOException ioe2) {}
|
||||||
// get the file name in the logs
|
// get the file name in the logs
|
||||||
IOException ioe2 = new IOException("Error reading " + tf.RAFfile.getAbsolutePath());
|
IOException ioe2 = new IOException("Error reading " + tf.RAFfile.getAbsolutePath());
|
||||||
ioe2.initCause(ioe);
|
ioe2.initCause(ioe);
|
||||||
|
|||||||
@@ -407,6 +407,8 @@ public class TrackerClient implements Runnable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int webPeers = getWebPeers();
|
||||||
|
|
||||||
// Local DHT tracker announce
|
// Local DHT tracker announce
|
||||||
DHT dht = _util.getDHT();
|
DHT dht = _util.getDHT();
|
||||||
if (dht != null && (meta == null || !meta.isPrivate()))
|
if (dht != null && (meta == null || !meta.isPrivate()))
|
||||||
@@ -438,7 +440,7 @@ public class TrackerClient implements Runnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// we could try and total the unique peers but that's too hard for now
|
// we could try and total the unique peers but that's too hard for now
|
||||||
snark.setTrackerSeenPeers(maxSeenPeers);
|
snark.setTrackerSeenPeers(maxSeenPeers + webPeers);
|
||||||
|
|
||||||
if (stop)
|
if (stop)
|
||||||
return;
|
return;
|
||||||
@@ -720,6 +722,62 @@ public class TrackerClient implements Runnable {
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return valid web peers from metainfo
|
||||||
|
* @since 0.9.49
|
||||||
|
*/
|
||||||
|
private int getWebPeers() {
|
||||||
|
if (meta == null)
|
||||||
|
return 0;
|
||||||
|
// prevent connecting out to a webseed for comments only
|
||||||
|
if (coordinator.getNeededLength() <= 0)
|
||||||
|
return 0;
|
||||||
|
List<String> urls = meta.getWebSeedURLs();
|
||||||
|
if (urls == null || urls.isEmpty())
|
||||||
|
return 0;
|
||||||
|
// Uncomment to skip multifile torrents
|
||||||
|
//if (meta.getLengths() != null)
|
||||||
|
// return 0;
|
||||||
|
List<Peer> peers = new ArrayList<Peer>(urls.size());
|
||||||
|
for (String url : urls) {
|
||||||
|
Hash h = getHostHash(url);
|
||||||
|
if (h == null)
|
||||||
|
continue;
|
||||||
|
try {
|
||||||
|
PeerID pID = new PeerID(h.getData(), _util);
|
||||||
|
byte[] id = new byte[20];
|
||||||
|
System.arraycopy(WebPeer.IDBytes, 0, id, 0, 12);
|
||||||
|
System.arraycopy(h.getData(), 0, id, 12, 8);
|
||||||
|
pID.setID(id);
|
||||||
|
URI uri = new URI(url);
|
||||||
|
String host = uri.getHost();
|
||||||
|
if (host == null)
|
||||||
|
continue;
|
||||||
|
if (coordinator.isWebPeerBanned(host)) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Skipping banned webseed " + url);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
peers.add(new WebPeer(coordinator, uri, pID, snark.getMetaInfo()));
|
||||||
|
} catch (InvalidBEncodingException ibe) {
|
||||||
|
} catch (URISyntaxException use) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (peers.isEmpty())
|
||||||
|
return 0;
|
||||||
|
Random r = _util.getContext().random();
|
||||||
|
if (peers.size() > 1)
|
||||||
|
Collections.shuffle(peers, r);
|
||||||
|
Iterator<Peer> it = peers.iterator();
|
||||||
|
while ((!stop) && it.hasNext() && coordinator.needOutboundPeers()) {
|
||||||
|
Peer cur = it.next();
|
||||||
|
if (coordinator.addPeer(cur) && it.hasNext()) {
|
||||||
|
int delay = r.nextInt(DELAY_RAND) + DELAY_MIN;
|
||||||
|
try { Thread.sleep(delay); } catch (InterruptedException ie) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peers.size();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a thread for each tracker in parallel if tunnel is still open
|
* Creates a thread for each tracker in parallel if tunnel is still open
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
package org.klomp.snark.web;
|
package org.klomp.snark;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
@@ -35,9 +35,9 @@ import net.i2p.data.DataHelper;
|
|||||||
* see UrlEncoded
|
* see UrlEncoded
|
||||||
*
|
*
|
||||||
* I2P modded from Jetty 8.1.15
|
* I2P modded from Jetty 8.1.15
|
||||||
* @since 0.9.15
|
* @since 0.9.15, moved from web in 0.9.49
|
||||||
*/
|
*/
|
||||||
class URIUtil
|
public class URIUtil
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Encode a URI path.
|
/** Encode a URI path.
|
||||||
667
apps/i2psnark/java/src/org/klomp/snark/WebPeer.java
Normal file
@@ -0,0 +1,667 @@
|
|||||||
|
package org.klomp.snark;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import net.i2p.I2PAppContext;
|
||||||
|
import net.i2p.client.streaming.I2PSocket;
|
||||||
|
import net.i2p.client.streaming.I2PSocketEepGet;
|
||||||
|
import net.i2p.client.streaming.I2PSocketManager;
|
||||||
|
import net.i2p.data.DataHelper;
|
||||||
|
import net.i2p.data.Destination;
|
||||||
|
import net.i2p.util.EepGet;
|
||||||
|
import net.i2p.util.Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BEP 19.
|
||||||
|
* Does not have an associated PeerState.
|
||||||
|
* All request tracking is done here.
|
||||||
|
* @since 0.9.49
|
||||||
|
*/
|
||||||
|
class WebPeer extends Peer implements EepGet.StatusListener {
|
||||||
|
|
||||||
|
private final PeerCoordinator _coordinator;
|
||||||
|
private final URI _uri;
|
||||||
|
// as received from coordinator
|
||||||
|
private final List<Request> outstandingRequests = new ArrayList<Request>();
|
||||||
|
private final boolean isMultiFile;
|
||||||
|
// needed?
|
||||||
|
private Request lastRequest;
|
||||||
|
private PeerListener listener;
|
||||||
|
private BitField bitfield;
|
||||||
|
private Thread thread;
|
||||||
|
private boolean connected;
|
||||||
|
private long lastRcvd;
|
||||||
|
private int maxRequests;
|
||||||
|
|
||||||
|
// to be recognized by the UI
|
||||||
|
public static final byte[] IDBytes = DataHelper.getASCII("WebSeedBEP19");
|
||||||
|
private static final long HEADER_TIMEOUT = 60*1000;
|
||||||
|
private static final long TOTAL_TIMEOUT = 10*60*1000;
|
||||||
|
private static final long INACTIVITY_TIMEOUT = 2*60*1000;
|
||||||
|
private static final long TARGET_FETCH_TIME = 2*60*1000;
|
||||||
|
// 128 KB
|
||||||
|
private static final int ABSOLUTE_MIN_REQUESTS = 8;
|
||||||
|
// 2 MB
|
||||||
|
private static final int ABSOLUTE_MAX_REQUESTS = 128;
|
||||||
|
private final int MIN_REQUESTS;
|
||||||
|
private final int MAX_REQUESTS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing connection.
|
||||||
|
* Creates a disconnected peer given a PeerID, your own id and the
|
||||||
|
* relevant MetaInfo.
|
||||||
|
* @param uri must be http with .i2p host
|
||||||
|
* @param metainfo non-null
|
||||||
|
*/
|
||||||
|
public WebPeer(PeerCoordinator coord, URI uri, PeerID peerID, MetaInfo metainfo) {
|
||||||
|
super(peerID, null, null, metainfo);
|
||||||
|
// no use asking for more than the number of chunks in a piece
|
||||||
|
MAX_REQUESTS = Math.max(1, Math.min(ABSOLUTE_MAX_REQUESTS, metainfo.getPieceLength(0) / PeerState.PARTSIZE));
|
||||||
|
MIN_REQUESTS = Math.min(ABSOLUTE_MIN_REQUESTS, MAX_REQUESTS);
|
||||||
|
maxRequests = MIN_REQUESTS;
|
||||||
|
isMultiFile = metainfo.getLengths() != null;
|
||||||
|
_coordinator = coord;
|
||||||
|
// We'll assume the base path is already encoded, because
|
||||||
|
// it would have failed the checks in TrackerClient.getHostHash()
|
||||||
|
_uri = uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "WebSeed " + _uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return socket debug string (for debug printing)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public synchronized String getSocket() {
|
||||||
|
return toString() + ' ' + outstandingRequests.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The hash code of a Peer is the hash code of the peerID.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return super.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Two Peers are equal when they have the same PeerID.
|
||||||
|
* All other properties are ignored.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (o instanceof WebPeer) {
|
||||||
|
WebPeer p = (WebPeer)o;
|
||||||
|
// TODO
|
||||||
|
return getPeerID().equals(p.getPeerID());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the connection to the other peer. This method does not
|
||||||
|
* return until the connection is terminated.
|
||||||
|
*
|
||||||
|
* @param ignore our bitfield, ignore
|
||||||
|
* @param uploadOnly if we are complete with skipped files, i.e. a partial seed
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void runConnection(I2PSnarkUtil util, PeerListener listener, BitField ignore,
|
||||||
|
MagnetState mState, boolean uploadOnly) {
|
||||||
|
if (uploadOnly)
|
||||||
|
return;
|
||||||
|
int fails = 0;
|
||||||
|
int successes = 0;
|
||||||
|
long dl = 0;
|
||||||
|
boolean notify = true;
|
||||||
|
ByteArrayOutputStream out = null;
|
||||||
|
// current requests per-loop
|
||||||
|
List<Request> requests = new ArrayList<Request>(8);
|
||||||
|
try {
|
||||||
|
if (!util.connected()) {
|
||||||
|
boolean ok = util.connect();
|
||||||
|
if (!ok)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This breaks out of the loop after any failure. TrackerClient will requeue eventually.
|
||||||
|
loop:
|
||||||
|
while (true) {
|
||||||
|
I2PSocketManager mgr = util.getSocketManager();
|
||||||
|
if (mgr == null)
|
||||||
|
return;
|
||||||
|
if (notify) {
|
||||||
|
synchronized(this) {
|
||||||
|
this.listener = listener;
|
||||||
|
bitfield = new BitField(metainfo.getPieces());
|
||||||
|
bitfield.setAll();
|
||||||
|
thread = Thread.currentThread();
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
listener.connected(this);
|
||||||
|
boolean want = listener.gotBitField(this, bitfield);
|
||||||
|
if (!want)
|
||||||
|
return;
|
||||||
|
listener.gotChoke(this, false);
|
||||||
|
notify = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
// clear out previous requests
|
||||||
|
if (!requests.isEmpty()) {
|
||||||
|
outstandingRequests.removeAll(requests);
|
||||||
|
requests.clear();
|
||||||
|
}
|
||||||
|
addRequest();
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Requests: " + outstandingRequests);
|
||||||
|
while (outstandingRequests.isEmpty()) {
|
||||||
|
if (_coordinator.getNeededLength() <= 0) {
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Complete: " + this);
|
||||||
|
break loop;
|
||||||
|
}
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("No requests, sleeping: " + this);
|
||||||
|
connected = false;
|
||||||
|
out = null;
|
||||||
|
try {
|
||||||
|
this.wait();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Interrupted: " + this, ie);
|
||||||
|
break loop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connected = true;
|
||||||
|
// Add current requests from outstandingRequests list and add to requests list.
|
||||||
|
// Do not remove from outstandingRequests until success.
|
||||||
|
lastRequest = outstandingRequests.get(0);
|
||||||
|
requests.add(lastRequest);
|
||||||
|
int piece = lastRequest.getPiece();
|
||||||
|
|
||||||
|
// Glue together additional requests if consecutive for a single piece.
|
||||||
|
// This will never glue together requests from different pieces,
|
||||||
|
// and the coordinator generally won't give us consecutive pieces anyway.
|
||||||
|
// Servers generally won't support multiple byte ranges anymore.
|
||||||
|
for (int i = 1; i < outstandingRequests.size(); i++) {
|
||||||
|
if (i >= maxRequests)
|
||||||
|
break;
|
||||||
|
Request r = outstandingRequests.get(i);
|
||||||
|
if (r.getPiece() == piece &&
|
||||||
|
lastRequest.off + lastRequest.len == r.off) {
|
||||||
|
requests.add(r);
|
||||||
|
lastRequest = r;
|
||||||
|
} else {
|
||||||
|
// all requests for a piece should be together, but not in practice
|
||||||
|
// as orphaned requests can get in-between
|
||||||
|
//break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// total values
|
||||||
|
Request first = requests.get(0);
|
||||||
|
Request last = requests.get(requests.size() - 1);
|
||||||
|
int piece = first.getPiece();
|
||||||
|
int off = first.off;
|
||||||
|
long toff = (((long) piece) * metainfo.getPieceLength(0)) + off;
|
||||||
|
int tlen = (last.off - first.off) + last.len;
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
///// TODO direct to file, not in-memory
|
||||||
|
if (out == null)
|
||||||
|
out = new ByteArrayOutputStream(tlen);
|
||||||
|
else
|
||||||
|
out.reset();
|
||||||
|
int filenum = -1;
|
||||||
|
|
||||||
|
// Loop for each file if multifile and crosses file boundaries.
|
||||||
|
// Once only for single file.
|
||||||
|
while (out.size() < tlen) {
|
||||||
|
|
||||||
|
// need these three things:
|
||||||
|
// url to fetch
|
||||||
|
String url;
|
||||||
|
// offset in fetched file
|
||||||
|
long foff;
|
||||||
|
// length to fetch, will be adjusted if crossing a file boundary
|
||||||
|
int flen = tlen - out.size();
|
||||||
|
|
||||||
|
if (isMultiFile) {
|
||||||
|
// multifile
|
||||||
|
List<Long> lengths = metainfo.getLengths();
|
||||||
|
long limit = 0;
|
||||||
|
if (filenum < 0) {
|
||||||
|
// find the first file number and limit
|
||||||
|
// inclusive
|
||||||
|
long fstart = 0;
|
||||||
|
// exclusive
|
||||||
|
long fend = 0;
|
||||||
|
foff = 0; // keep compiler happy, will always be re-set
|
||||||
|
for (int f = 0; f < lengths.size(); f++) {
|
||||||
|
long filelen = lengths.get(f).longValue();
|
||||||
|
fend = fstart + filelen;
|
||||||
|
if (toff < fend) {
|
||||||
|
filenum = f;
|
||||||
|
foff = toff - fstart;
|
||||||
|
limit = fend - toff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fstart += filelen;
|
||||||
|
}
|
||||||
|
if (filenum < 0)
|
||||||
|
throw new IllegalStateException(lastRequest.toString());
|
||||||
|
} else {
|
||||||
|
// next file
|
||||||
|
filenum++;
|
||||||
|
foff = 0;
|
||||||
|
limit = lengths.get(filenum).longValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (limit > 0 && flen > limit)
|
||||||
|
flen = (int) limit;
|
||||||
|
|
||||||
|
if (metainfo.isPaddingFile(filenum)) {
|
||||||
|
for (int i = 0; i < flen; i++) {
|
||||||
|
out.write((byte) 0);
|
||||||
|
}
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Skipped padding file " + filenum);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// build url
|
||||||
|
String uri = _uri.toString();
|
||||||
|
StringBuilder buf = new StringBuilder(uri.length() + 128);
|
||||||
|
buf.append(uri);
|
||||||
|
if (!uri.endsWith("/"))
|
||||||
|
buf.append('/');
|
||||||
|
// See BEP 19 rules
|
||||||
|
URIUtil.encodePath(buf, metainfo.getName());
|
||||||
|
List<String> path = metainfo.getFiles().get(filenum);
|
||||||
|
for (int i = 0; i < path.size(); i++) {
|
||||||
|
buf.append('/');
|
||||||
|
URIUtil.encodePath(buf, path.get(i));
|
||||||
|
}
|
||||||
|
url = buf.toString();
|
||||||
|
} else {
|
||||||
|
// single file
|
||||||
|
// See BEP 19 rules
|
||||||
|
String uri = _uri.toString();
|
||||||
|
if (uri.endsWith("/"))
|
||||||
|
url = uri + URIUtil.encodePath(metainfo.getName());
|
||||||
|
else
|
||||||
|
url = uri;
|
||||||
|
foff = toff;
|
||||||
|
flen = tlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
// do the fetch
|
||||||
|
EepGet get = new I2PSocketEepGet(util.getContext(), mgr, 0, flen, flen, null, out, url);
|
||||||
|
get.addHeader("User-Agent", I2PSnarkUtil.EEPGET_USER_AGENT);
|
||||||
|
get.addHeader("Range", "bytes=" + foff + '-' + (foff + flen - 1));
|
||||||
|
get.addStatusListener(this);
|
||||||
|
int osz = out.size();
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Fetching piece: " + piece + " offset: " + off + " file offset: " + foff + " len: " + flen + " from " + url);
|
||||||
|
if (get.fetch(HEADER_TIMEOUT, TOTAL_TIMEOUT, INACTIVITY_TIMEOUT)) {
|
||||||
|
int resp = get.getStatusCode();
|
||||||
|
if (resp != 200 && resp != 206) {
|
||||||
|
fail(url, resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int sz = out.size() - osz;
|
||||||
|
if (sz != flen) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Fetch of " + url + " received: " + sz + " expected: " + flen);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (out.size() > 0) {
|
||||||
|
// save any complete chunks received
|
||||||
|
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(out.toByteArray()));
|
||||||
|
for (Iterator<Request> iter = requests.iterator(); iter.hasNext(); ) {
|
||||||
|
Request req = iter.next();
|
||||||
|
if (dis.available() < req.len)
|
||||||
|
break;
|
||||||
|
req.read(dis);
|
||||||
|
iter.remove();
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Saved chunk " + req + " recvd before failure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int resp = get.getStatusCode();
|
||||||
|
fail(url, resp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
successes++;
|
||||||
|
dl += flen;
|
||||||
|
|
||||||
|
if (!isMultiFile)
|
||||||
|
break;
|
||||||
|
} // for each file
|
||||||
|
|
||||||
|
// all data received successfully, now process it
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Fetch of piece: " + piece + " chunks: " + requests.size() + " offset: " + off + " torrent offset: " + toff + " len: " + tlen + " successful");
|
||||||
|
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(out.toByteArray()));
|
||||||
|
for (Request req : requests) {
|
||||||
|
req.read(dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
PartialPiece pp = last.getPartialPiece();
|
||||||
|
synchronized(pp) {
|
||||||
|
// Last chunk needed for this piece?
|
||||||
|
if (pp.getLength() == pp.getDownloaded()) {
|
||||||
|
if (listener.gotPiece(this, pp)) {
|
||||||
|
if (_log.shouldDebug())
|
||||||
|
_log.debug("Got " + piece + ": " + this);
|
||||||
|
} else {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Got BAD " + piece + " from " + this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// piece not complete
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long time = lastRcvd - start;
|
||||||
|
if (time < TARGET_FETCH_TIME)
|
||||||
|
maxRequests = Math.min(MAX_REQUESTS, 2 * maxRequests);
|
||||||
|
else if (time > 2 * TARGET_FETCH_TIME)
|
||||||
|
maxRequests = Math.max(MIN_REQUESTS, maxRequests / 2);
|
||||||
|
} // request loop
|
||||||
|
} catch(IOException eofe) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn(toString(), eofe);
|
||||||
|
} finally {
|
||||||
|
List<Request> pcs = returnPartialPieces();
|
||||||
|
synchronized(this) {
|
||||||
|
connected = false;
|
||||||
|
outstandingRequests.clear();
|
||||||
|
}
|
||||||
|
requests.clear();
|
||||||
|
if (!pcs.isEmpty())
|
||||||
|
listener.savePartialPieces(this, pcs);
|
||||||
|
listener.disconnected(this);
|
||||||
|
disconnect();
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Completed, successful fetches: " + successes + " downloaded: " + dl + " for " + this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fail(String url, int resp) {
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Fetch of " + url + " failed, rc: " + resp);
|
||||||
|
if (resp == 301 || resp == 308 ||
|
||||||
|
resp == 401 || resp == 403 || resp == 404 || resp == 410 || resp == 414 || resp == 416 || resp == 451) {
|
||||||
|
// ban forever
|
||||||
|
_coordinator.banWebPeer(_uri.getHost(), true);
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Permanently banning the webseed " + url);
|
||||||
|
} else if (resp == 429 || resp == 503) {
|
||||||
|
// ban for a while
|
||||||
|
_coordinator.banWebPeer(_uri.getHost(), false);
|
||||||
|
if (_log.shouldWarn())
|
||||||
|
_log.warn("Temporarily banning the webseed " + url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxPipeline() {
|
||||||
|
return maxRequests;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isConnected() {
|
||||||
|
synchronized(this) {
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
synchronized void disconnect() {
|
||||||
|
if (thread != null)
|
||||||
|
thread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void have(int piece) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void cancel(int piece) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void request() {
|
||||||
|
addRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInterested() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
@Override
|
||||||
|
public void setInteresting(boolean interest) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInteresting() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChoking(boolean choke) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChoking() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChoked() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getInactiveTime() {
|
||||||
|
if (lastRcvd <= 0)
|
||||||
|
return -1;
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
return now - lastRcvd;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getMaxInactiveTime() {
|
||||||
|
return PeerCoordinator.MAX_INACTIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void keepAlive() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void retransmitRequests() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int completed() {
|
||||||
|
return metainfo.getPieces();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCompleted() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true
|
||||||
|
* @since 0.9.49
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean isWebPeer() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private methods below here implementing parts of PeerState
|
||||||
|
|
||||||
|
private synchronized void addRequest() {
|
||||||
|
boolean more_pieces = true;
|
||||||
|
while (more_pieces) {
|
||||||
|
more_pieces = outstandingRequests.size() < getMaxPipeline();
|
||||||
|
// We want something and we don't have outstanding requests?
|
||||||
|
if (more_pieces && lastRequest == null) {
|
||||||
|
// we have nothing in the queue right now
|
||||||
|
more_pieces = requestNextPiece();
|
||||||
|
} else if (more_pieces) {
|
||||||
|
// We want something
|
||||||
|
int pieceLength;
|
||||||
|
boolean isLastChunk;
|
||||||
|
pieceLength = metainfo.getPieceLength(lastRequest.getPiece());
|
||||||
|
isLastChunk = lastRequest.off + lastRequest.len == pieceLength;
|
||||||
|
|
||||||
|
// Last part of a piece?
|
||||||
|
if (isLastChunk) {
|
||||||
|
more_pieces = requestNextPiece();
|
||||||
|
} else {
|
||||||
|
PartialPiece nextPiece = lastRequest.getPartialPiece();
|
||||||
|
int nextBegin = lastRequest.off + PeerState.PARTSIZE;
|
||||||
|
int maxLength = pieceLength - nextBegin;
|
||||||
|
int nextLength = maxLength > PeerState.PARTSIZE ? PeerState.PARTSIZE
|
||||||
|
: maxLength;
|
||||||
|
Request req = new Request(nextPiece,nextBegin, nextLength);
|
||||||
|
outstandingRequests.add(req);
|
||||||
|
lastRequest = req;
|
||||||
|
this.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts requesting first chunk of next piece. Returns true if
|
||||||
|
* something has been added to the requests, false otherwise.
|
||||||
|
*/
|
||||||
|
private synchronized boolean requestNextPiece() {
|
||||||
|
// Check for adopting an orphaned partial piece
|
||||||
|
PartialPiece pp = listener.getPartialPiece(this, bitfield);
|
||||||
|
if (pp != null) {
|
||||||
|
// Double-check that r not already in outstandingRequests
|
||||||
|
if (!getRequestedPieces().contains(Integer.valueOf(pp.getPiece()))) {
|
||||||
|
Request r = pp.getRequest();
|
||||||
|
outstandingRequests.add(r);
|
||||||
|
lastRequest = r;
|
||||||
|
this.notifyAll();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
if (_log.shouldLog(Log.WARN))
|
||||||
|
_log.warn("Got dup from coord: " + pp);
|
||||||
|
pp.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// failsafe
|
||||||
|
// However this is bad as it thrashes the peer when we change our mind
|
||||||
|
// Ticket 691 cause here?
|
||||||
|
if (outstandingRequests.isEmpty())
|
||||||
|
lastRequest = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
// If we are not in the end game, we may run out of things to request
|
||||||
|
// because we are asking other peers. Set not-interesting now rather than
|
||||||
|
// wait for those other requests to be satisfied via havePiece()
|
||||||
|
if (interesting && lastRequest == null) {
|
||||||
|
interesting = false;
|
||||||
|
out.sendInterest(false);
|
||||||
|
if (_log.shouldLog(Log.DEBUG))
|
||||||
|
_log.debug(peer + " nothing more to request, now uninteresting");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return all pieces we are currently requesting, or empty Set
|
||||||
|
*/
|
||||||
|
private synchronized Set<Integer> getRequestedPieces() {
|
||||||
|
Set<Integer> rv = new HashSet<Integer>(outstandingRequests.size() + 1);
|
||||||
|
for (Request req : outstandingRequests) {
|
||||||
|
rv.add(Integer.valueOf(req.getPiece()));
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return index in outstandingRequests or -1
|
||||||
|
*/
|
||||||
|
private synchronized int getFirstOutstandingRequest(int piece) {
|
||||||
|
for (int i = 0; i < outstandingRequests.size(); i++) {
|
||||||
|
if (outstandingRequests.get(i).getPiece() == piece)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized List<Request> returnPartialPieces() {
|
||||||
|
Set<Integer> pcs = getRequestedPieces();
|
||||||
|
List<Request> rv = new ArrayList<Request>(pcs.size());
|
||||||
|
for (Integer p : pcs) {
|
||||||
|
Request req = getLowestOutstandingRequest(p.intValue());
|
||||||
|
if (req != null) {
|
||||||
|
PartialPiece pp = req.getPartialPiece();
|
||||||
|
synchronized(pp) {
|
||||||
|
int dl = pp.getDownloaded();
|
||||||
|
if (req.off != dl)
|
||||||
|
req = new Request(pp, dl);
|
||||||
|
}
|
||||||
|
rv.add(req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outstandingRequests.clear();
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized Request getLowestOutstandingRequest(int piece) {
|
||||||
|
Request rv = null;
|
||||||
|
int lowest = Integer.MAX_VALUE;
|
||||||
|
for (Request r : outstandingRequests) {
|
||||||
|
if (r.getPiece() == piece && r.off < lowest) {
|
||||||
|
lowest = r.off;
|
||||||
|
rv = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EepGet status listeners to maintain the state for the web page
|
||||||
|
|
||||||
|
public void bytesTransferred(long alreadyTransferred, int currentWrite, long bytesTransferred, long bytesRemaining, String url) {
|
||||||
|
lastRcvd = System.currentTimeMillis();
|
||||||
|
downloaded(currentWrite);
|
||||||
|
listener.downloaded(this, currentWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void attemptFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt, int numRetries, Exception cause) {}
|
||||||
|
public void transferComplete(long alreadyTransferred, long bytesTransferred, long bytesRemaining, String url, String outputFile, boolean notModified) {}
|
||||||
|
public void transferFailed(String url, long bytesTransferred, long bytesRemaining, int currentAttempt) {}
|
||||||
|
public void headerReceived(String url, int attemptNum, String key, String val) {}
|
||||||
|
public void attempting(String url) {}
|
||||||
|
|
||||||
|
// End of EepGet status listeners
|
||||||
|
}
|
||||||
@@ -42,6 +42,8 @@ import net.i2p.util.Log;
|
|||||||
import net.i2p.util.SecureFile;
|
import net.i2p.util.SecureFile;
|
||||||
import net.i2p.util.SystemVersion;
|
import net.i2p.util.SystemVersion;
|
||||||
|
|
||||||
|
import org.klomp.snark.URIUtil;
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ import org.klomp.snark.SnarkManager;
|
|||||||
import org.klomp.snark.Storage;
|
import org.klomp.snark.Storage;
|
||||||
import org.klomp.snark.Tracker;
|
import org.klomp.snark.Tracker;
|
||||||
import org.klomp.snark.TrackerClient;
|
import org.klomp.snark.TrackerClient;
|
||||||
|
import org.klomp.snark.URIUtil;
|
||||||
import org.klomp.snark.dht.DHT;
|
import org.klomp.snark.dht.DHT;
|
||||||
import org.klomp.snark.comments.Comment;
|
import org.klomp.snark.comments.Comment;
|
||||||
import org.klomp.snark.comments.CommentSet;
|
import org.klomp.snark.comments.CommentSet;
|
||||||
@@ -201,10 +202,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_context.isRouterContext())
|
_themePath = _contextPath + WARBASE + "themes/" + _manager.getTheme() + '/';
|
||||||
_themePath = "/themes/snark/" + _manager.getTheme() + '/';
|
|
||||||
else
|
|
||||||
_themePath = _contextPath + WARBASE + "themes/snark/" + _manager.getTheme() + '/';
|
|
||||||
_imgPath = _themePath + "images/";
|
_imgPath = _themePath + "images/";
|
||||||
req.setCharacterEncoding("UTF-8");
|
req.setCharacterEncoding("UTF-8");
|
||||||
|
|
||||||
@@ -463,7 +461,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
*/
|
*/
|
||||||
private boolean writeTorrents(PrintWriter out, HttpServletRequest req, boolean canWrite) throws IOException {
|
private boolean writeTorrents(PrintWriter out, HttpServletRequest req, boolean canWrite) throws IOException {
|
||||||
/** dl, ul, down rate, up rate, peers, size */
|
/** dl, ul, down rate, up rate, peers, size */
|
||||||
final long stats[] = {0,0,0,0,0,0};
|
final long stats[] = new long[6];
|
||||||
String peerParam = req.getParameter("p");
|
String peerParam = req.getParameter("p");
|
||||||
String stParam = req.getParameter("st");
|
String stParam = req.getParameter("st");
|
||||||
|
|
||||||
@@ -1995,7 +1993,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
out.write(_t("Peer attached to swarm"));
|
out.write(_t("Peer attached to swarm"));
|
||||||
out.write("\"></td><td colspan=\"5\">");
|
out.write("\"></td><td colspan=\"5\">");
|
||||||
PeerID pid = peer.getPeerID();
|
PeerID pid = peer.getPeerID();
|
||||||
String ch = pid != null ? pid.toString().substring(0, 4) : "????";
|
String ch = pid != null ? pid.toString() : "????";
|
||||||
|
if (ch.startsWith("WebSeed@")) {
|
||||||
|
out.write(ch);
|
||||||
|
} else {
|
||||||
|
ch = ch.substring(0, 4);
|
||||||
String client;
|
String client;
|
||||||
if ("AwMD".equals(ch))
|
if ("AwMD".equals(ch))
|
||||||
client = _t("I2PSnark");
|
client = _t("I2PSnark");
|
||||||
@@ -2022,6 +2024,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
out.write(client + " <tt title=\"");
|
out.write(client + " <tt title=\"");
|
||||||
out.write(_t("Destination (identity) of peer"));
|
out.write(_t("Destination (identity) of peer"));
|
||||||
out.write("\">" + peer.toString().substring(5, 9)+ "</tt>");
|
out.write("\">" + peer.toString().substring(5, 9)+ "</tt>");
|
||||||
|
}
|
||||||
if (showDebug) {
|
if (showDebug) {
|
||||||
long t = peer.getInactiveTime();
|
long t = peer.getInactiveTime();
|
||||||
if (t >= 5000)
|
if (t >= 5000)
|
||||||
@@ -2476,8 +2479,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
if (!_context.isRouterContext()) {
|
if (!_context.isRouterContext()) {
|
||||||
try {
|
try {
|
||||||
// class only in standalone builds
|
// class only in standalone builds
|
||||||
Class helper = Class.forName("org.klomp.snark.standalone.ConfigUIHelper");
|
Class<?> helper = Class.forName("org.klomp.snark.standalone.ConfigUIHelper");
|
||||||
Method getLangSettings = helper.getMethod("getLangSettings", new Class[] {I2PAppContext.class});
|
Method getLangSettings = helper.getMethod("getLangSettings", I2PAppContext.class);
|
||||||
String langSettings = (String) getLangSettings.invoke(null, _context);
|
String langSettings = (String) getLangSettings.invoke(null, _context);
|
||||||
// If we get to here, we have the language settings
|
// If we get to here, we have the language settings
|
||||||
out.write("<tr><td>");
|
out.write("<tr><td>");
|
||||||
@@ -3179,9 +3182,34 @@ public class I2PSnarkServlet extends BasicServlet {
|
|||||||
}
|
}
|
||||||
buf.append("</td></tr>\n");
|
buf.append("</td></tr>\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> weblist = meta.getWebSeedURLs();
|
||||||
|
if (weblist != null) {
|
||||||
|
List<String> wlist = new ArrayList<String>(weblist.size());
|
||||||
|
// strip non-i2p web seeds
|
||||||
|
for (String s : weblist) {
|
||||||
|
if (isI2PTracker(s))
|
||||||
|
wlist.add(s);
|
||||||
|
}
|
||||||
|
if (!wlist.isEmpty()) {
|
||||||
|
buf.append("<tr><td>");
|
||||||
|
toThemeImg(buf, "details");
|
||||||
|
buf.append("</td><td><b>")
|
||||||
|
.append(_t("Web Seeds")).append("</b></td><td>");
|
||||||
|
boolean more = false;
|
||||||
|
for (String s : wlist) {
|
||||||
|
buf.append("<span class=\"info_tracker\">");
|
||||||
|
if (more)
|
||||||
|
buf.append(' ');
|
||||||
|
else
|
||||||
|
more = true;
|
||||||
|
buf.append(getShortTrackerLink(DataHelper.stripHTML(s), snark.getInfoHash()));
|
||||||
|
buf.append("</span> ");
|
||||||
|
}
|
||||||
|
buf.append("</td></tr>\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta != null) {
|
|
||||||
String com = meta.getComment();
|
String com = meta.getComment();
|
||||||
if (com != null && com.length() > 0) {
|
if (com != null && com.length() > 0) {
|
||||||
if (com.length() > 1024)
|
if (com.length() > 1024)
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 240 B After Width: | Height: | Size: 240 B |
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 593 B |
|
Before Width: | Height: | Size: 152 B After Width: | Height: | Size: 152 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 824 B After Width: | Height: | Size: 824 B |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 780 B After Width: | Height: | Size: 780 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 496 B After Width: | Height: | Size: 496 B |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 450 B |
|
Before Width: | Height: | Size: 650 B After Width: | Height: | Size: 650 B |
|
Before Width: | Height: | Size: 773 B After Width: | Height: | Size: 773 B |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 279 B After Width: | Height: | Size: 279 B |
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 380 B |
|
Before Width: | Height: | Size: 421 B After Width: | Height: | Size: 421 B |
|
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
|
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 437 B |
|
Before Width: | Height: | Size: 409 B After Width: | Height: | Size: 409 B |
|
Before Width: | Height: | Size: 354 B After Width: | Height: | Size: 354 B |
|
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 265 B |
|
Before Width: | Height: | Size: 346 B After Width: | Height: | Size: 346 B |
|
Before Width: | Height: | Size: 977 B After Width: | Height: | Size: 977 B |
|
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 372 B |
|
Before Width: | Height: | Size: 433 B After Width: | Height: | Size: 433 B |
|
Before Width: | Height: | Size: 326 B After Width: | Height: | Size: 326 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 244 B After Width: | Height: | Size: 244 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 328 B After Width: | Height: | Size: 328 B |
|
Before Width: | Height: | Size: 372 B After Width: | Height: | Size: 372 B |
|
Before Width: | Height: | Size: 513 B After Width: | Height: | Size: 513 B |
|
Before Width: | Height: | Size: 456 B After Width: | Height: | Size: 456 B |
|
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 254 B After Width: | Height: | Size: 254 B |
|
Before Width: | Height: | Size: 599 B After Width: | Height: | Size: 599 B |
|
Before Width: | Height: | Size: 227 B After Width: | Height: | Size: 227 B |
|
Before Width: | Height: | Size: 746 B After Width: | Height: | Size: 746 B |
|
Before Width: | Height: | Size: 782 B After Width: | Height: | Size: 782 B |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 236 B After Width: | Height: | Size: 236 B |
|
Before Width: | Height: | Size: 595 B After Width: | Height: | Size: 595 B |
|
Before Width: | Height: | Size: 758 B After Width: | Height: | Size: 758 B |