forked from I2P_Developers/i2p.i2p
Compare commits
1 Commits
i2p-2.3.0-
...
i2p-jpacka
Author | SHA1 | Date | |
---|---|---|---|
18db2e0fbc |
@ -29,4 +29,3 @@ installer/build
|
||||
router/java/build
|
||||
router/build
|
||||
|
||||
override.properties
|
34
.github/workflows/ant.yml
vendored
34
.github/workflows/ant.yml
vendored
@ -1,34 +0,0 @@
|
||||
# Mostly copied from https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-ant
|
||||
# zlatinb
|
||||
|
||||
name: Java CI
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: GetText
|
||||
run: sudo apt install gettext
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '8'
|
||||
distribution: 'temurin'
|
||||
- name : Generate override.properties
|
||||
run: |
|
||||
rm -f override.properties
|
||||
echo "build.built-by=GitHub Actions" >> override.properties
|
||||
echo "noExe=true" >> override.properties
|
||||
- name: build with Ant
|
||||
run: ant distclean pkg
|
||||
- name: Upload installer.jar
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: I2P-install.jar-${{ github.sha }}
|
||||
path: install.jar
|
||||
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -61,5 +61,3 @@ sloccount.sc
|
||||
|
||||
# TODO: why does this file appear?
|
||||
apps/routerconsole/jsp/favicon.ico
|
||||
.tx
|
||||
/*.sh
|
@ -52,7 +52,6 @@ test:ant:
|
||||
- echo junit.home=/usr/share/java >> override.properties
|
||||
- echo hamcrest.home=/usr/share/java >> override.properties
|
||||
- echo mockito.home=/usr/share/java >> override.properties
|
||||
- echo build.built-by=GitHub Actions >> override.properties
|
||||
script:
|
||||
- ant test
|
||||
only:
|
||||
@ -64,15 +63,15 @@ test:ant:
|
||||
# It's cached for later jobs
|
||||
build_docker:
|
||||
stage: build
|
||||
image: docker:24.0.6
|
||||
image: docker:19.03.12
|
||||
services:
|
||||
- docker:24.0.6-dind
|
||||
- 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/
|
||||
- echo docker save $CI_REGISTRY_IMAGE:latest > ci-exports/$CI_COMMIT_REF_SLUG.tar
|
||||
- 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
|
||||
@ -90,7 +89,7 @@ build_docker:
|
||||
# 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
|
||||
DOCKER_HOST: tcp://docker:2376
|
||||
cache:
|
||||
# The same key should be used across branches
|
||||
key: "$CI_COMMIT_REF_SLUG"
|
||||
|
89
.tx/config
89
.tx/config
@ -1,12 +1,7 @@
|
||||
;; warning - conversions for all Java bundles:
|
||||
;; id->in, he->iw, iy, yi->ji
|
||||
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = ru_RU: ru, sv_SE: sv, tr_TR: tr, uk_UA: uk, zh_CN: zh, et_EE: et, id: in, he: iw
|
||||
|
||||
[o:otf:p:I2P:r:i2ptunnel]
|
||||
file_filter = apps/i2ptunnel/locale/messages_<lang>.po
|
||||
[I2P.i2ptunnel]
|
||||
source_file = apps/i2ptunnel/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/i2ptunnel/locale/messages_ar.po
|
||||
@ -14,7 +9,6 @@ trans.cs = apps/i2ptunnel/locale/messages_cs.po
|
||||
trans.da = apps/i2ptunnel/locale/messages_da.po
|
||||
trans.de = apps/i2ptunnel/locale/messages_de.po
|
||||
trans.es = apps/i2ptunnel/locale/messages_es.po
|
||||
trans.es_AR = apps/i2ptunnel/locale/messages_es_AR.po
|
||||
trans.fa = apps/i2ptunnel/locale/messages_fa.po
|
||||
trans.fi = apps/i2ptunnel/locale/messages_fi.po
|
||||
trans.fr = apps/i2ptunnel/locale/messages_fr.po
|
||||
@ -41,8 +35,7 @@ trans.vi = apps/i2ptunnel/locale/messages_vi.po
|
||||
trans.zh_CN = apps/i2ptunnel/locale/messages_zh.po
|
||||
trans.zh_TW = apps/i2ptunnel/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:proxy]
|
||||
file_filter = apps/i2ptunnel/locale-proxy/messages_<lang>.po
|
||||
[I2P.proxy]
|
||||
source_file = apps/i2ptunnel/locale-proxy/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/i2ptunnel/locale-proxy/messages_ar.po
|
||||
@ -50,7 +43,6 @@ trans.cs = apps/i2ptunnel/locale-proxy/messages_cs.po
|
||||
trans.de = apps/i2ptunnel/locale-proxy/messages_de.po
|
||||
trans.el = apps/i2ptunnel/locale-proxy/messages_el.po
|
||||
trans.es = apps/i2ptunnel/locale-proxy/messages_es.po
|
||||
trans.es_AR = apps/i2ptunnel/locale-proxy/messages_es_AR.po
|
||||
trans.fa = apps/i2ptunnel/locale-proxy/messages_fa.po
|
||||
trans.fi = apps/i2ptunnel/locale-proxy/messages_fi.po
|
||||
trans.fr = apps/i2ptunnel/locale-proxy/messages_fr.po
|
||||
@ -74,10 +66,8 @@ trans.tr_TR = apps/i2ptunnel/locale-proxy/messages_tr.po
|
||||
trans.uk_UA = apps/i2ptunnel/locale-proxy/messages_uk.po
|
||||
trans.vi = apps/i2ptunnel/locale-proxy/messages_vi.po
|
||||
trans.zh_CN = apps/i2ptunnel/locale-proxy/messages_zh.po
|
||||
trans.zh_TW = apps/i2ptunnel/locale-proxy/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:core]
|
||||
file_filter = core/locale/messages_<lang>.po
|
||||
[I2P.core]
|
||||
type = PO
|
||||
source_file = core/locale/messages_en.po
|
||||
source_lang = en
|
||||
@ -115,8 +105,7 @@ trans.vi = core/locale/messages_vi.po
|
||||
trans.zh_CN = core/locale/messages_zh.po
|
||||
trans.zh_TW = core/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:router]
|
||||
file_filter = router/locale/messages_<lang>.po
|
||||
[I2P.router]
|
||||
type = PO
|
||||
source_file = router/locale/messages_en.po
|
||||
source_lang = en
|
||||
@ -127,7 +116,6 @@ trans.da = router/locale/messages_da.po
|
||||
trans.de = router/locale/messages_de.po
|
||||
trans.el = router/locale/messages_el.po
|
||||
trans.es = router/locale/messages_es.po
|
||||
trans.es_AR = router/locale/messages_es_AR.po
|
||||
trans.et_EE = router/locale/messages_et.po
|
||||
trans.fa = router/locale/messages_fa.po
|
||||
trans.fi = router/locale/messages_fi.po
|
||||
@ -155,8 +143,7 @@ trans.vi = router/locale/messages_vi.po
|
||||
trans.zh_CN = router/locale/messages_zh.po
|
||||
trans.zh_TW = router/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:routerconsole]
|
||||
file_filter = apps/routerconsole/locale/messages_<lang>.po
|
||||
[I2P.routerconsole]
|
||||
source_file = apps/routerconsole/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/routerconsole/locale/messages_ar.po
|
||||
@ -166,7 +153,6 @@ trans.da = apps/routerconsole/locale/messages_da.po
|
||||
trans.de = apps/routerconsole/locale/messages_de.po
|
||||
trans.el = apps/routerconsole/locale/messages_el.po
|
||||
trans.es = apps/routerconsole/locale/messages_es.po
|
||||
trans.es_AR = apps/routerconsole/locale/messages_es_AR.po
|
||||
trans.et_EE = apps/routerconsole/locale/messages_et.po
|
||||
trans.fa = apps/routerconsole/locale/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale/messages_fi.po
|
||||
@ -191,8 +177,7 @@ trans.vi = apps/routerconsole/locale/messages_vi.po
|
||||
trans.zh_CN = apps/routerconsole/locale/messages_zh.po
|
||||
trans.zh_TW = apps/routerconsole/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:welcome]
|
||||
file_filter = apps/routerconsole/locale-news/messages_<lang>.po
|
||||
[I2P.welcome]
|
||||
source_file = apps/routerconsole/locale-news/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/routerconsole/locale-news/messages_ar.po
|
||||
@ -201,7 +186,6 @@ trans.cs = apps/routerconsole/locale-news/messages_cs.po
|
||||
trans.de = apps/routerconsole/locale-news/messages_de.po
|
||||
trans.el = apps/routerconsole/locale-news/messages_el.po
|
||||
trans.es = apps/routerconsole/locale-news/messages_es.po
|
||||
trans.es_AR = apps/routerconsole/locale-news/messages_es_AR.po
|
||||
trans.fa = apps/routerconsole/locale-news/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale-news/messages_fi.po
|
||||
trans.fr = apps/routerconsole/locale-news/messages_fr.po
|
||||
@ -234,8 +218,7 @@ trans.uk_UA = apps/routerconsole/locale-news/messages_uk.po
|
||||
trans.zh_CN = apps/routerconsole/locale-news/messages_zh.po
|
||||
trans.zh_TW = apps/routerconsole/locale-news/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:countries]
|
||||
file_filter = apps/routerconsole/locale-countries/messages_<lang>.po
|
||||
[I2P.countries]
|
||||
type = PO
|
||||
source_file = apps/routerconsole/locale-countries/messages_en.po
|
||||
source_lang = en
|
||||
@ -248,7 +231,6 @@ trans.da = apps/routerconsole/locale-countries/messages_da.po
|
||||
trans.de = apps/routerconsole/locale-countries/messages_de.po
|
||||
trans.el = apps/routerconsole/locale-countries/messages_el.po
|
||||
trans.es = apps/routerconsole/locale-countries/messages_es.po
|
||||
trans.es_AR = apps/routerconsole/locale-countries/messages_es_AR.po
|
||||
trans.et_EE = apps/routerconsole/locale-countries/messages_et.po
|
||||
trans.fa = apps/routerconsole/locale-countries/messages_fa.po
|
||||
trans.fi = apps/routerconsole/locale-countries/messages_fi.po
|
||||
@ -274,7 +256,6 @@ trans.pt_BR = apps/routerconsole/locale-countries/messages_pt_BR.po
|
||||
trans.ro = apps/routerconsole/locale-countries/messages_ro.po
|
||||
trans.ru_RU = apps/routerconsole/locale-countries/messages_ru.po
|
||||
trans.sk = apps/routerconsole/locale-countries/messages_sk.po
|
||||
trans.sl = apps/routerconsole/locale-countries/messages_sl.po
|
||||
trans.sq = apps/routerconsole/locale-countries/messages_sq.po
|
||||
trans.sr = apps/routerconsole/locale-countries/messages_sr.po
|
||||
trans.sv_SE = apps/routerconsole/locale-countries/messages_sv.po
|
||||
@ -285,17 +266,14 @@ trans.vi = apps/routerconsole/locale-countries/messages_vi.po
|
||||
trans.zh_CN = apps/routerconsole/locale-countries/messages_zh.po
|
||||
trans.zh_TW = apps/routerconsole/locale-countries/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:i2psnark]
|
||||
file_filter = apps/i2psnark/locale/messages_<lang>.po
|
||||
[I2P.i2psnark]
|
||||
source_file = apps/i2psnark/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/i2psnark/locale/messages_ar.po
|
||||
trans.cs = apps/i2psnark/locale/messages_cs.po
|
||||
trans.da = apps/i2psnark/locale/messages_da.po
|
||||
trans.de = apps/i2psnark/locale/messages_de.po
|
||||
trans.el = apps/i2psnark/locale/messages_el.po
|
||||
trans.es = apps/i2psnark/locale/messages_es.po
|
||||
trans.es_AR = apps/i2psnark/locale/messages_es_AR.po
|
||||
trans.fa = apps/i2psnark/locale/messages_fa.po
|
||||
trans.fi = apps/i2psnark/locale/messages_fi.po
|
||||
trans.fr = apps/i2psnark/locale/messages_fr.po
|
||||
@ -321,8 +299,7 @@ trans.vi = apps/i2psnark/locale/messages_vi.po
|
||||
trans.zh_CN = apps/i2psnark/locale/messages_zh.po
|
||||
trans.zh_TW = apps/i2psnark/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:susidns]
|
||||
file_filter = apps/susidns/locale/messages_<lang>.po
|
||||
[I2P.susidns]
|
||||
source_file = apps/susidns/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/susidns/locale/messages_ar.po
|
||||
@ -331,7 +308,6 @@ trans.da = apps/susidns/locale/messages_da.po
|
||||
trans.de = apps/susidns/locale/messages_de.po
|
||||
trans.el = apps/susidns/locale/messages_el.po
|
||||
trans.es = apps/susidns/locale/messages_es.po
|
||||
trans.es_AR = apps/susidns/locale/messages_es_AR.po
|
||||
trans.fa = apps/susidns/locale/messages_fa.po
|
||||
trans.fi = apps/susidns/locale/messages_fi.po
|
||||
trans.fr = apps/susidns/locale/messages_fr.po
|
||||
@ -355,8 +331,7 @@ trans.vi = apps/susidns/locale/messages_vi.po
|
||||
trans.zh_CN = apps/susidns/locale/messages_zh.po
|
||||
trans.zh_TW = apps/susidns/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:desktopgui]
|
||||
file_filter = apps/desktopgui/locale/messages_<lang>.po
|
||||
[I2P.desktopgui]
|
||||
source_file = apps/desktopgui/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/desktopgui/locale/messages_ar.po
|
||||
@ -389,7 +364,6 @@ trans.pt_BR = apps/desktopgui/locale/messages_pt_BR.po
|
||||
trans.ro = apps/desktopgui/locale/messages_ro.po
|
||||
trans.ru_RU = apps/desktopgui/locale/messages_ru.po
|
||||
trans.sk = apps/desktopgui/locale/messages_sk.po
|
||||
trans.sl = apps/desktopgui/locale/messages_sl.po
|
||||
trans.sr = apps/desktopgui/locale/messages_sr.po
|
||||
trans.sv_SE = apps/desktopgui/locale/messages_sv.po
|
||||
trans.sq = apps/desktopgui/locale/messages_sq.po
|
||||
@ -400,8 +374,7 @@ trans.vi = apps/desktopgui/locale/messages_vi.po
|
||||
trans.zh_CN = apps/desktopgui/locale/messages_zh.po
|
||||
trans.zh_TW = apps/desktopgui/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:susimail]
|
||||
file_filter = apps/susimail/locale/messages_<lang>.po
|
||||
[I2P.susimail]
|
||||
source_file = apps/susimail/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/susimail/locale/messages_ar.po
|
||||
@ -413,7 +386,6 @@ trans.da = apps/susimail/locale/messages_da.po
|
||||
trans.de = apps/susimail/locale/messages_de.po
|
||||
trans.el = apps/susimail/locale/messages_el.po
|
||||
trans.es = apps/susimail/locale/messages_es.po
|
||||
trans.es_AR = apps/susimail/locale/messages_es_AR.po
|
||||
trans.fa = apps/susimail/locale/messages_fa.po
|
||||
trans.fi = apps/susimail/locale/messages_fi.po
|
||||
trans.fr = apps/susimail/locale/messages_fr.po
|
||||
@ -445,8 +417,7 @@ trans.vi = apps/susimail/locale/messages_vi.po
|
||||
trans.zh_CN = apps/susimail/locale/messages_zh.po
|
||||
trans.zh_TW = apps/susimail/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:debconf]
|
||||
file_filter = debian/po/<lang>.po
|
||||
[I2P.debconf]
|
||||
source_file = debian/po/templates.pot
|
||||
source_lang = en
|
||||
trans.ar = debian/po/ar.po
|
||||
@ -455,7 +426,6 @@ trans.cs = debian/po/cs.po
|
||||
trans.de = debian/po/de.po
|
||||
trans.el = debian/po/el.po
|
||||
trans.es = debian/po/es.po
|
||||
trans.es_AR = debian/po/es_AR.po
|
||||
trans.fi = debian/po/fi.po
|
||||
trans.fr = debian/po/fr.po
|
||||
trans.gl = debian/po/gl.po
|
||||
@ -472,7 +442,6 @@ trans.pt_BR = debian/po/pt_BR.po
|
||||
trans.ro = debian/po/ro.po
|
||||
trans.ru_RU = debian/po/ru.po
|
||||
trans.sk = debian/po/sk.po
|
||||
trans.sl = debian/po/sl.po
|
||||
trans.sq = debian/po/sq.po
|
||||
trans.sv_SE = debian/po/sv.po
|
||||
trans.tk = debian/po/tk.po
|
||||
@ -481,8 +450,7 @@ trans.uk_UA = debian/po/uk.po
|
||||
trans.zh_CN = debian/po/zh.po
|
||||
trans.zh_TW = debian/po/zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:i2prouter-script]
|
||||
file_filter = installer/resources/locale/po/messages_<lang>.po
|
||||
[I2P.i2prouter-script]
|
||||
source_file = installer/resources/locale/po/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = installer/resources/locale/po/messages_ar.po
|
||||
@ -490,7 +458,6 @@ trans.az = installer/resources/locale/po/messages_az.po
|
||||
trans.ca = installer/resources/locale/po/messages_ca.po
|
||||
trans.de = installer/resources/locale/po/messages_de.po
|
||||
trans.es = installer/resources/locale/po/messages_es.po
|
||||
trans.es_AR = installer/resources/locale/po/messages_es_AR.po
|
||||
trans.fi = installer/resources/locale/po/messages_fi.po
|
||||
trans.fr = installer/resources/locale/po/messages_fr.po
|
||||
trans.id = installer/resources/locale/po/messages_id.po
|
||||
@ -509,9 +476,8 @@ trans.sv_SE = installer/resources/locale/po/messages_sv.po
|
||||
trans.tr_TR = installer/resources/locale/po/messages_tr.po
|
||||
trans.uk_UA = installer/resources/locale/po/messages_uk.po
|
||||
trans.zh_CN = installer/resources/locale/po/messages_zh.po
|
||||
trans.zh_TW = installer/resources/locale/po/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:getopt]
|
||||
[I2P.getopt]
|
||||
;;
|
||||
;; Java properties files (when not read with our DataHelper methods) must be ISO-8859-1 encoded.
|
||||
;; See https://docs.oracle.com/javase/6/docs/api/java/util/Properties.html
|
||||
@ -528,7 +494,6 @@ trans.zh_TW = installer/resources/locale/po/messages_zh_TW.po
|
||||
;; and should be escaped using native2ascii after downloading:
|
||||
;; gl, pt, pt_BR, sq, sv, tr
|
||||
;;
|
||||
file_filter = core/java/src/gnu/getopt/MessagesBundle_<lang>.properties
|
||||
source_file = core/java/src/gnu/getopt/MessagesBundle.properties
|
||||
source_lang = en
|
||||
type = PROPERTIES
|
||||
@ -537,7 +502,6 @@ trans.az = core/java/src/gnu/getopt/MessagesBundle_az.properties
|
||||
trans.cs = core/java/src/gnu/getopt/MessagesBundle_cs.properties
|
||||
trans.de = core/java/src/gnu/getopt/MessagesBundle_de.properties
|
||||
trans.es = core/java/src/gnu/getopt/MessagesBundle_es.properties
|
||||
trans.es_AR = core/java/src/gnu/getopt/MessagesBundle_es_AR.properties
|
||||
trans.fi = core/java/src/gnu/getopt/MessagesBundle_fi.properties
|
||||
trans.fr = core/java/src/gnu/getopt/MessagesBundle_fr.properties
|
||||
trans.gl = core/java/src/gnu/getopt/MessagesBundle_gl.properties
|
||||
@ -565,8 +529,7 @@ trans.uk_UA = core/java/src/gnu/getopt/MessagesBundle_uk.properties
|
||||
trans.zh_CN = core/java/src/gnu/getopt/MessagesBundle_zh.properties
|
||||
trans.zh_TW = core/java/src/gnu/getopt/MessagesBundle_zh_TW.properties
|
||||
|
||||
[o:otf:p:I2P:r:streaming]
|
||||
file_filter = apps/ministreaming/locale/messages_<lang>.po
|
||||
[I2P.streaming]
|
||||
source_file = apps/ministreaming/locale/messages_en.po
|
||||
source_lang = en
|
||||
trans.ar = apps/ministreaming/locale/messages_ar.po
|
||||
@ -575,7 +538,6 @@ trans.ca = apps/ministreaming/locale/messages_ca.po
|
||||
trans.cs = apps/ministreaming/locale/messages_cs.po
|
||||
trans.de = apps/ministreaming/locale/messages_de.po
|
||||
trans.es = apps/ministreaming/locale/messages_es.po
|
||||
trans.es_AR = apps/ministreaming/locale/messages_es_AR.po
|
||||
trans.fa = apps/ministreaming/locale/messages_fa.po
|
||||
trans.fi = apps/ministreaming/locale/messages_fi.po
|
||||
trans.fr = apps/ministreaming/locale/messages_fr.po
|
||||
@ -598,14 +560,12 @@ trans.tk = apps/ministreaming/locale/messages_tk.po
|
||||
trans.tr_TR = apps/ministreaming/locale/messages_tr.po
|
||||
trans.uk_UA = apps/ministreaming/locale/messages_uk.po
|
||||
trans.zh_CN = apps/ministreaming/locale/messages_zh.po
|
||||
trans.zh_TW = apps/ministreaming/locale/messages_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:manpages]
|
||||
[I2P.manpages]
|
||||
;;
|
||||
;; after adding languages here, add to debian/*.manpages also
|
||||
;; You must run installer/resources/poupdate-man.sh first.
|
||||
;;
|
||||
file_filter = installer/resources/locale-man/man_<lang>.po
|
||||
type = PO
|
||||
source_file = installer/resources/locale-man/man.pot
|
||||
source_lang = en
|
||||
@ -613,13 +573,11 @@ trans.ar = installer/resources/locale-man/man_ar.po
|
||||
trans.az = installer/resources/locale-man/man_az.po
|
||||
trans.de = installer/resources/locale-man/man_de.po
|
||||
trans.es = installer/resources/locale-man/man_es.po
|
||||
trans.es_AR = installer/resources/locale-man/man_es_AR.po
|
||||
trans.fi = installer/resources/locale-man/man_fi.po
|
||||
trans.fr = installer/resources/locale-man/man_fr.po
|
||||
trans.hu = installer/resources/locale-man/man_hu.po
|
||||
trans.id = installer/resources/locale-man/man_id.po
|
||||
trans.it = installer/resources/locale-man/man_it.po
|
||||
trans.ja = installer/resources/locale-man/man_ja.po
|
||||
trans.ko = installer/resources/locale-man/man_ko.po
|
||||
trans.nl = installer/resources/locale-man/man_nl.po
|
||||
trans.pl = installer/resources/locale-man/man_pl.po
|
||||
@ -630,15 +588,13 @@ trans.ru_RU = installer/resources/locale-man/man_ru.po
|
||||
trans.sv_SE = installer/resources/locale-man/man_sv.po
|
||||
trans.tr_TR = installer/resources/locale-man/man_tr.po
|
||||
trans.zh_CN = installer/resources/locale-man/man_zh.po
|
||||
trans.zh_TW = installer/resources/locale-man/man_zh_TW.po
|
||||
|
||||
[o:otf:p:I2P:r:eepsite]
|
||||
[I2P.eepsite]
|
||||
;;
|
||||
;; For any new translations, add links in installer/resources/eepsite/docroot/help/index.html
|
||||
;; and copy new flags in build.xml copyflags-unlesspkg target,
|
||||
;; and add to debian/i2p-router.links and debian-alt/*/i2p-router.links
|
||||
;;
|
||||
file_filter = installer/resources/eepsite/docroot/help/index_<lang>.po
|
||||
type = HTML
|
||||
source_file = installer/resources/eepsite/docroot/help/index.html
|
||||
source_lang = en
|
||||
@ -667,14 +623,10 @@ trans.tr_TR = installer/resources/eepsite/docroot/help/index_tr.html
|
||||
trans.uk_UA = installer/resources/eepsite/docroot/help/index_uk.html
|
||||
trans.zh_CN = installer/resources/eepsite/docroot/help/index_zh.html
|
||||
|
||||
[o:otf:p:I2P:r:readme]
|
||||
[I2P.readme]
|
||||
;;
|
||||
;; Text on /console
|
||||
;;
|
||||
;; NOTE: No support for country suffixes right now.
|
||||
;; See ResourceHelper.java
|
||||
;;
|
||||
file_filter = apps/routerconsole/resources/docs/readme_<lang>.html
|
||||
type = HTML
|
||||
source_file = apps/routerconsole/resources/docs/readme.html
|
||||
source_lang = en
|
||||
@ -690,9 +642,10 @@ trans.pl = apps/routerconsole/resources/docs/readme_pl.html
|
||||
trans.pt = apps/routerconsole/resources/docs/readme_pt.html
|
||||
trans.ro = apps/routerconsole/resources/docs/readme_ro.html
|
||||
trans.ru_RU = apps/routerconsole/resources/docs/readme_ru.html
|
||||
trans.sl = apps/routerconsole/resources/docs/readme_sl.html
|
||||
trans.tr_TR = apps/routerconsole/resources/docs/readme_tr.html
|
||||
trans.uk_UA = apps/routerconsole/resources/docs/readme_uk.html
|
||||
trans.zh_CN = apps/routerconsole/resources/docs/readme_zh.html
|
||||
trans.zh_TW = apps/routerconsole/resources/docs/readme_zh_TW.html
|
||||
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
|
80
Docker.md
80
Docker.md
@ -1,49 +1,13 @@
|
||||
# I2P in Docker
|
||||
|
||||
### Very quick start
|
||||
If you just want to give I2P a quick try or are using it on a home network, follow these steps:
|
||||
|
||||
1. Create two directories `i2pconfig` and `i2ptorrents`
|
||||
2. Copy the following text and save it in a file `docker-compose.yml`
|
||||
```
|
||||
version: "3.5"
|
||||
services:
|
||||
i2p:
|
||||
image: geti2p/i2p
|
||||
ports:
|
||||
- 127.0.0.1:4444:4444
|
||||
- 127.0.0.1:6668:6668
|
||||
- 127.0.0.1:7657:7657
|
||||
- 54321:12345
|
||||
- 54321:12345/udp
|
||||
volumes:
|
||||
- ./i2pconfig:/i2p/.i2p
|
||||
- ./i2ptorrents:/i2psnark
|
||||
```
|
||||
3. Execute `docker-compose up`
|
||||
4. Start a browser and go to `http://127.0.0.1:7657` to complete the setup wizard.
|
||||
|
||||
Note that this quick-start approach is not recommended for production deployments on remote servers. Please read the rest of this document for more information.
|
||||
|
||||
### Building an image
|
||||
There is an i2P image available over at [DockerHub](https://hub.docker.com). If you do not want to use that one, you can build one yourself:
|
||||
```
|
||||
docker build -t geti2p/i2p .
|
||||
docker build -t i2p .
|
||||
```
|
||||
|
||||
### Running a container
|
||||
|
||||
#### Environment Variables
|
||||
|
||||
It is possible to set the IP address where the I2P router is accessible by setting
|
||||
the `IP_ADDR` environment variable in your `docker run` command or your `docker-compose`
|
||||
file. For example, if you want to make your I2P router listen on all addresses, then
|
||||
you should pass `-e IP_ADDR=0.0.0.0` to your `docker run` command.
|
||||
|
||||
It is also possible to configure the memory available to the I2P router using
|
||||
environment variables. To do this, use the: `JVM_XMX` environment variable by passing,
|
||||
for example, `-e JVM_XMX=256m`.
|
||||
|
||||
#### Volumes
|
||||
The container requires a volume for the configuration data to be mounted. Optionally, you can mount a separate volume for torrent ("i2psnark") downloads. See the example below.
|
||||
|
||||
@ -53,41 +17,33 @@ By the default the image limits the memory available to the Java heap to 512MB.
|
||||
#### Ports
|
||||
There are several ports which are exposed by the image. You can choose which ones to publish depending on your specific needs.
|
||||
|
||||
|Port|Interface|Description|TCP/UDP|
|
||||
|---|---|---|---|
|
||||
|4444|127.0.0.1|HTTP Proxy|TCP|
|
||||
|4445|127.0.0.1|HTTPS Proxy|TCP|
|
||||
|6668|127.0.0.1|IRC Proxy|TCP|
|
||||
|7654|127.0.0.1|I2CP Protocol|TCP|
|
||||
|7656|127.0.0.1|SAM Bridge TCP|TCP|
|
||||
|7657|127.0.0.1|Router console|TCP|
|
||||
|7658|127.0.0.1|I2P Site|TCP|
|
||||
|7659|127.0.0.1|SMTP Proxy|TCP|
|
||||
|7660|127.0.0.1|POP Proxy|TCP|
|
||||
|7652|LAN interface|UPnP|TCP|
|
||||
|7653|LAN interface|UPnP|UDP|
|
||||
|12345|0.0.0.0|I2NP Protocol|TCP and UDP|
|
||||
|Port|Description|TCP/UDP|
|
||||
|---|---|---|
|
||||
|4444|HTTP Proxy|TCP|
|
||||
|4445|HTTPS Proxy|TCP|
|
||||
|6668|IRC Proxy|TCP|
|
||||
|7654|I2CP Protocol|TCP|
|
||||
|7656|SAM Bridge TCP|TCP|
|
||||
|7657|Router console|TCP|
|
||||
|7658|I2P Site|TCP|
|
||||
|7659|SMTP Proxy|TCP|
|
||||
|7660|POP Proxy|TCP|
|
||||
|12345|I2NP Protocol|TCP and UDP|
|
||||
|
||||
You probably want at least the Router Console (7657) and the HTTP Proxy (4444). If you want I2P to be able to receive incoming connections from the internet, and hence not think it's firewalled, publish the I2NP Protocol port (12345) - but make sure you publish to a different random port, otherwise others may be able to guess you're running I2P in a Docker image.
|
||||
|
||||
#### Networking
|
||||
A best-practices guide for cloud deployments is beyond the scope of this document, but in general you should try to minimize the number of published ports, while exposing only the `I2NP` ports to the internet. That means that the services in the list above which are bound to `127.0.0.1` (which include the router console) will need to be accessed via other methods like ssh tunneling or be manually configured to bind to a different interface.
|
||||
|
||||
#### Example
|
||||
Here is an example container that mounts `i2phome` as home directory, `i2ptorrents` for torrents, and opens HTTP Proxy, IRC, Router Console and I2NP Protocols. It also limits the memory available to the JVM to 256MB.
|
||||
|
||||
```
|
||||
docker build -t geti2p/i2p .
|
||||
# I2NP port needs TCP and UDP. Change the 54321 to something random, greater than 1024.
|
||||
docker run \
|
||||
-e JVM_XMX=256m \
|
||||
-v i2phome:/i2p/.i2p \
|
||||
-v i2ptorrents:/i2psnark \
|
||||
-p 127.0.0.1:4444:4444 \
|
||||
-p 127.0.0.1:6668:6668 \
|
||||
-p 127.0.0.1:7657:7657 \
|
||||
-p 4444:4444 \
|
||||
-p 6668:6668 \
|
||||
-p 7657:7657 \
|
||||
-p 54321:12345 \
|
||||
-p 54321:12345/udp \
|
||||
geti2p/i2p:latest
|
||||
-p 54321:12345/udp \ # I2NP port needs TCP and UDP. Change the 54321 to something random, greater than 1024.
|
||||
i2p:latest
|
||||
```
|
||||
|
||||
|
16
Dockerfile
16
Dockerfile
@ -1,27 +1,24 @@
|
||||
FROM alpine:3.17.1 as builder
|
||||
FROM jlesage/baseimage:alpine-3.10-glibc as builder
|
||||
|
||||
ENV APP_HOME="/i2p"
|
||||
|
||||
WORKDIR /tmp/build
|
||||
COPY . .
|
||||
|
||||
RUN apk add --virtual build-base gettext tar bzip2 apache-ant openjdk17 \
|
||||
&& echo "build.built-by=Docker" >> override.properties \
|
||||
RUN add-pkg --virtual build-base gettext tar bzip2 apache-ant openjdk8 \
|
||||
&& ant preppkg-linux-only \
|
||||
&& rm -rf pkg-temp/osid pkg-temp/lib/wrapper pkg-temp/lib/wrapper.* \
|
||||
&& apk del build-base gettext tar bzip2 apache-ant openjdk17
|
||||
&& del-pkg build-base gettext tar bzip2 apache-ant openjdk8
|
||||
|
||||
FROM alpine:3.17.1
|
||||
FROM jlesage/baseimage:alpine-3.10-glibc
|
||||
ENV APP_HOME="/i2p"
|
||||
|
||||
RUN apk add openjdk17-jre ttf-dejavu
|
||||
|
||||
RUN add-pkg openjdk8-jre
|
||||
WORKDIR ${APP_HOME}
|
||||
COPY --from=builder /tmp/build/pkg-temp .
|
||||
|
||||
# "install" i2p by copying over installed files
|
||||
COPY --chown=root:root docker/rootfs/ /
|
||||
RUN chmod +x /startapp.sh
|
||||
COPY docker/rootfs/ /
|
||||
|
||||
# Mount home and snark
|
||||
VOLUME ["${APP_HOME}/.i2p"]
|
||||
@ -37,4 +34,3 @@ LABEL \
|
||||
org.label-schema.vcs-url="https://github.com/i2p/i2p.i2p" \
|
||||
org.label-schema.schema-version="1.0"
|
||||
|
||||
ENTRYPOINT ["/startapp.sh"]
|
||||
|
@ -212,6 +212,10 @@ Applications:
|
||||
Copyright (c) 2006, Matthew Estes
|
||||
See licenses/LICENSE-BlockFile.txt
|
||||
|
||||
BOB (BOB.jar):
|
||||
Copyright (C) sponge
|
||||
See licenses/COPYING-BOB.txt
|
||||
|
||||
Desktopgui (desktopgui.jar):
|
||||
Copyright (c) Mathias De Maré
|
||||
See licenses/LICENSE-DesktopGUI.txt
|
||||
@ -335,9 +339,9 @@ Applications:
|
||||
Systray (systray.jar):
|
||||
Public domain.
|
||||
|
||||
Tomcat 9.0.62 (jasper-runtime.jar):
|
||||
Tomcat 9.0.54 (jasper-runtime.jar):
|
||||
(not included in most distribution packages)
|
||||
Copyright 1999-2022 The Apache Software Foundation
|
||||
Copyright 1999-2021 The Apache Software Foundation
|
||||
See licenses/LICENSE-Apache2.0.txt
|
||||
See licenses/NOTICE-Tomcat.txt
|
||||
|
||||
|
@ -64,9 +64,6 @@ your `~/.gradle/gradle.properties`:
|
||||
systemProp.socksProxyHost=localhost
|
||||
systemProp.socksProxyPort=9150
|
||||
|
||||
### Development builds
|
||||
Automatic CI builds are available at the [continuous integration](https://github.com/i2p/i2p.i2p/actions/workflows/ant.yml) page.
|
||||
|
||||
### Docker
|
||||
For more information how to run I2P in Docker, see [Docker.md](Docker.md)
|
||||
## Contact info
|
||||
|
14
apps/BOB/bob.config
Normal file
14
apps/BOB/bob.config
Normal file
@ -0,0 +1,14 @@
|
||||
#bob.config
|
||||
#Tue Dec 30 00:00:00 UTC 2008
|
||||
# Please leave this file here for testing.
|
||||
# Thank you,
|
||||
# Sponge
|
||||
i2cp.tcp.port=7654
|
||||
BOB.host=localhost
|
||||
inbound.lengthVariance=0
|
||||
i2cp.messageReliability=BestEffort
|
||||
BOB.port=45678
|
||||
outbound.length=1
|
||||
inbound.length=1
|
||||
outbound.lengthVariance=0
|
||||
i2cp.tcp.host=localhost
|
12
apps/BOB/build.gradle
Normal file
12
apps/BOB/build.gradle
Normal file
@ -0,0 +1,12 @@
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDir 'src'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':core')
|
||||
compile project(':apps:ministreaming')
|
||||
}
|
115
apps/BOB/build.xml
Normal file
115
apps/BOB/build.xml
Normal file
@ -0,0 +1,115 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- You may freely edit this file. See commented blocks below for -->
|
||||
<!-- some examples of how to customize the build. -->
|
||||
<!-- (If you delete it and reopen the project it will be recreated.) -->
|
||||
<!-- By default, only the Clean and Build commands use this build script. -->
|
||||
<!-- Commands such as Run, Debug, and Test only use this build script if -->
|
||||
<!-- the Compile on Save feature is turned off for the project. -->
|
||||
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
|
||||
<!-- in the project's Project Properties dialog box.-->
|
||||
<project name="BOB" default="default" basedir=".">
|
||||
<description>Builds, tests, and runs the project BOB.</description>
|
||||
<import file="nbproject/build-impl.xml"/>
|
||||
<!--
|
||||
|
||||
There exist several targets which are by default empty and which can be
|
||||
used for execution of your tasks. These targets are usually executed
|
||||
before and after some main targets. They are:
|
||||
|
||||
-pre-init: called before initialization of project properties
|
||||
-post-init: called after initialization of project properties
|
||||
-pre-compile: called before javac compilation
|
||||
-post-compile: called after javac compilation
|
||||
-pre-compile-single: called before javac compilation of single file
|
||||
-post-compile-single: called after javac compilation of single file
|
||||
-pre-compile-test: called before javac compilation of JUnit tests
|
||||
-post-compile-test: called after javac compilation of JUnit tests
|
||||
-pre-compile-test-single: called before javac compilation of single JUnit test
|
||||
-post-compile-test-single: called after javac compilation of single JUunit test
|
||||
-pre-jar: called before JAR building
|
||||
-post-jar: called after JAR building
|
||||
-post-clean: called after cleaning build products
|
||||
|
||||
(Targets beginning with '-' are not intended to be called on their own.)
|
||||
|
||||
Example of inserting an obfuscator after compilation could look like this:
|
||||
|
||||
<target name="-post-compile">
|
||||
<obfuscate>
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
</obfuscate>
|
||||
</target>
|
||||
|
||||
For list of available properties check the imported
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
|
||||
Another way to customize the build is by overriding existing main targets.
|
||||
The targets of interest are:
|
||||
|
||||
-init-macrodef-javac: defines macro for javac compilation
|
||||
-init-macrodef-junit: defines macro for junit execution
|
||||
-init-macrodef-debug: defines macro for class debugging
|
||||
-init-macrodef-java: defines macro for class execution
|
||||
-do-jar-with-manifest: JAR building (if you are using a manifest)
|
||||
-do-jar-without-manifest: JAR building (if you are not using a manifest)
|
||||
run: execution of project
|
||||
-javadoc-build: Javadoc generation
|
||||
test-report: JUnit report generation
|
||||
|
||||
An example of overriding the target for project execution could look like this:
|
||||
|
||||
<target name="run" depends="BOB-impl.jar">
|
||||
<exec dir="bin" executable="launcher.exe">
|
||||
<arg file="${dist.jar}"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
Notice that the overridden target depends on the jar target and not only on
|
||||
the compile target as the regular run target does. Again, for a list of available
|
||||
properties which you can use, check the target you are overriding in the
|
||||
nbproject/build-impl.xml file.
|
||||
|
||||
-->
|
||||
|
||||
<target depends="jar" description="Build BOB into a SINGLE JAR." name="onejar">
|
||||
<!-- Make needed working dirs -->
|
||||
<mkdir dir="${dist.dir}/lib" />
|
||||
<mkdir dir="${dist.dir}/classes" />
|
||||
|
||||
<!-- Copy jars -->
|
||||
<copy todir="${dist.dir}/lib" flatten="true" >
|
||||
<path>
|
||||
<pathelement path="${javac.classpath}" />
|
||||
</path>
|
||||
</copy>
|
||||
<copy todir="${dist.dir}/lib" file="../../build/jbigi.jar" />
|
||||
|
||||
<!-- Extract the classes inside the jar files -->
|
||||
<unjar dest="${dist.dir}/classes" >
|
||||
<fileset dir="${dist.dir}/lib" >
|
||||
<include name="**/*.jar" />
|
||||
</fileset>
|
||||
</unjar>
|
||||
|
||||
<!-- Recombine the classes into a new jar file -->
|
||||
<jar jarfile="${dist.dir}/lib/all-in-one.jar" >
|
||||
<fileset dir="${dist.dir}/classes" />
|
||||
</jar>
|
||||
|
||||
<!-- Clean up work area -->
|
||||
<delete dir="${dist.dir}/classes" followsymlinks="false" includeemptydirs="true"/>
|
||||
|
||||
<!-- Make the single jar file -->
|
||||
<jar jarfile="dist/BOB-one.jar" >
|
||||
<zipfileset src="${dist.jar}" excludes="META-INF/*" />
|
||||
<zipfileset src="${dist.dir}/lib/all-in-one.jar" excludes="**/META-INF/*" />
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="net.i2p.BOB.Main" />
|
||||
</manifest>
|
||||
</jar>
|
||||
|
||||
<!-- Clean up the fake jar file -->
|
||||
<delete file="${dist.dir}/lib/all-in-one.jar" />
|
||||
</target>
|
||||
</project>
|
3
apps/BOB/manifest.mf
Normal file
3
apps/BOB/manifest.mf
Normal file
@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
X-COMMENT: Main-Class will be added automatically by build
|
||||
|
643
apps/BOB/nbproject/build-impl.xml
Normal file
643
apps/BOB/nbproject/build-impl.xml
Normal file
@ -0,0 +1,643 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
*** GENERATED FROM project.xml - DO NOT EDIT ***
|
||||
*** EDIT ../build.xml INSTEAD ***
|
||||
|
||||
For the purpose of easier reading the script
|
||||
is divided into following sections:
|
||||
|
||||
- initialization
|
||||
- compilation
|
||||
- jar
|
||||
- execution
|
||||
- debugging
|
||||
- javadoc
|
||||
- junit compilation
|
||||
- junit execution
|
||||
- junit debugging
|
||||
- applet
|
||||
- cleanup
|
||||
|
||||
-->
|
||||
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="BOB-impl">
|
||||
<target depends="test,jar,javadoc" description="Build and test whole project." name="default"/>
|
||||
<!--
|
||||
======================
|
||||
INITIALIZATION SECTION
|
||||
======================
|
||||
-->
|
||||
<target name="-pre-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="-pre-init" name="-init-private">
|
||||
<property file="nbproject/private/config.properties"/>
|
||||
<property file="nbproject/private/configs/${config}.properties"/>
|
||||
<property file="nbproject/private/private.properties"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private" name="-init-user">
|
||||
<property file="${user.properties.file}"/>
|
||||
<!-- The two properties below are usually overridden -->
|
||||
<!-- by the active platform. Just a fallback. -->
|
||||
<property name="default.javac.source" value="1.8"/>
|
||||
<property name="default.javac.target" value="1.8"/>
|
||||
<property name="javac.release" value="8"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user" name="-init-project">
|
||||
<property file="nbproject/configs/${config}.properties"/>
|
||||
<property file="nbproject/project.properties"/>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
|
||||
<available file="${manifest.file}" property="manifest.available"/>
|
||||
<condition property="manifest.available+main.class">
|
||||
<and>
|
||||
<isset property="manifest.available"/>
|
||||
<isset property="main.class"/>
|
||||
<not>
|
||||
<equals arg1="${main.class}" arg2="" trim="true"/>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="manifest.available+main.class+mkdist.available">
|
||||
<and>
|
||||
<istrue value="${manifest.available+main.class}"/>
|
||||
<isset property="libs.CopyLibs.classpath"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="have.tests">
|
||||
<or>
|
||||
<available file="${test.src.dir}"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="have.sources">
|
||||
<or>
|
||||
<available file="${src.dir}"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition property="netbeans.home+have.tests">
|
||||
<and>
|
||||
<isset property="netbeans.home"/>
|
||||
<isset property="have.tests"/>
|
||||
</and>
|
||||
</condition>
|
||||
<condition property="no.javadoc.preview">
|
||||
<and>
|
||||
<isset property="javadoc.preview"/>
|
||||
<isfalse value="${javadoc.preview}"/>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="run.jvmargs" value=""/>
|
||||
<property name="javac.compilerargs" value=""/>
|
||||
<property name="work.dir" value="${basedir}"/>
|
||||
<condition property="no.deps">
|
||||
<and>
|
||||
<istrue value="${no.dependencies}"/>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="javac.debug" value="true"/>
|
||||
<property name="javadoc.preview" value="true"/>
|
||||
<property name="application.args" value=""/>
|
||||
<property name="source.encoding" value="${file.encoding}"/>
|
||||
<condition property="javadoc.encoding.used" value="${javadoc.encoding}">
|
||||
<and>
|
||||
<isset property="javadoc.encoding"/>
|
||||
<not>
|
||||
<equals arg1="${javadoc.encoding}" arg2=""/>
|
||||
</not>
|
||||
</and>
|
||||
</condition>
|
||||
<property name="javadoc.encoding.used" value="${source.encoding}"/>
|
||||
<property name="includes" value="**"/>
|
||||
<property name="excludes" value=""/>
|
||||
<property name="do.depend" value="false"/>
|
||||
<condition property="do.depend.true">
|
||||
<istrue value="${do.depend}"/>
|
||||
</condition>
|
||||
<condition else="" property="javac.compilerargs.jaxws" value="-Djava.endorsed.dirs='${jaxws.endorsed.dir}'">
|
||||
<and>
|
||||
<isset property="jaxws.endorsed.dir"/>
|
||||
<available file="nbproject/jaxws-build.xml"/>
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
<target name="-post-init">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check">
|
||||
<fail unless="src.dir">Must set src.dir</fail>
|
||||
<fail unless="test.src.dir">Must set test.src.dir</fail>
|
||||
<fail unless="build.dir">Must set build.dir</fail>
|
||||
<fail unless="dist.dir">Must set dist.dir</fail>
|
||||
<fail unless="build.classes.dir">Must set build.classes.dir</fail>
|
||||
<fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail>
|
||||
<fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail>
|
||||
<fail unless="build.test.results.dir">Must set build.test.results.dir</fail>
|
||||
<fail unless="build.classes.excludes">Must set build.classes.excludes</fail>
|
||||
<fail unless="dist.jar">Must set dist.jar</fail>
|
||||
</target>
|
||||
<target name="-init-macrodef-property">
|
||||
<macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute name="name"/>
|
||||
<attribute name="value"/>
|
||||
<sequential>
|
||||
<property name="@{name}" value="${@{value}}"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-javac">
|
||||
<macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${src.dir}" name="srcdir"/>
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<attribute default="${javac.classpath}" name="classpath"/>
|
||||
<attribute default="${includes}" name="includes"/>
|
||||
<attribute default="${excludes}" name="excludes"/>
|
||||
<attribute default="${javac.debug}" name="debug"/>
|
||||
<attribute default="/does/not/exist" name="sourcepath"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" release="${javac.release}">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<compilerarg line="${javac.compilerargs} ${javac.compilerargs.jaxws}"/>
|
||||
<customize/>
|
||||
</javac>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${src.dir}" name="srcdir"/>
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<attribute default="${javac.classpath}" name="classpath"/>
|
||||
<sequential>
|
||||
<depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
</depend>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${build.classes.dir}" name="destdir"/>
|
||||
<sequential>
|
||||
<fail unless="javac.includes">Must set javac.includes</fail>
|
||||
<pathconvert pathsep="," property="javac.includes.binary">
|
||||
<path>
|
||||
<filelist dir="@{destdir}" files="${javac.includes}"/>
|
||||
</path>
|
||||
<globmapper from="*.java" to="*.class"/>
|
||||
</pathconvert>
|
||||
<delete>
|
||||
<files includes="${javac.includes.binary}"/>
|
||||
</delete>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-junit">
|
||||
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${includes}" name="includes"/>
|
||||
<attribute default="${excludes}" name="excludes"/>
|
||||
<attribute default="**" name="testincludes"/>
|
||||
<sequential>
|
||||
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" showoutput="true">
|
||||
<batchtest todir="${build.test.results.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
|
||||
<filename name="@{testincludes}"/>
|
||||
</fileset>
|
||||
</batchtest>
|
||||
<classpath>
|
||||
<path path="${run.test.classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<formatter type="brief" usefile="false"/>
|
||||
<formatter type="xml"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
</junit>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target depends="-init-debug-args" name="-init-macrodef-nbjpda">
|
||||
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${main.class}" name="name"/>
|
||||
<attribute default="${debug.classpath}" name="classpath"/>
|
||||
<attribute default="" name="stopclassname"/>
|
||||
<sequential>
|
||||
<nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}">
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
</nbjpdastart>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
<macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${build.classes.dir}" name="dir"/>
|
||||
<sequential>
|
||||
<nbjpdareload>
|
||||
<fileset dir="@{dir}" includes="${fix.classes}">
|
||||
<include name="${fix.includes}*.class"/>
|
||||
</fileset>
|
||||
</nbjpdareload>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-debug-args">
|
||||
<property name="version-output" value="java version "${ant.java.version}"/>
|
||||
<condition property="have-jdk-older-than-1.4">
|
||||
<or>
|
||||
<contains string="${version-output}" substring="java version "1.0"/>
|
||||
<contains string="${version-output}" substring="java version "1.1"/>
|
||||
<contains string="${version-output}" substring="java version "1.2"/>
|
||||
<contains string="${version-output}" substring="java version "1.3"/>
|
||||
</or>
|
||||
</condition>
|
||||
<condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none">
|
||||
<istrue value="${have-jdk-older-than-1.4}"/>
|
||||
</condition>
|
||||
<condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem">
|
||||
<os family="windows"/>
|
||||
</condition>
|
||||
<condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}">
|
||||
<isset property="debug.transport"/>
|
||||
</condition>
|
||||
</target>
|
||||
<target depends="-init-debug-args" name="-init-macrodef-debug">
|
||||
<macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<attribute default="${main.class}" name="classname"/>
|
||||
<attribute default="${debug.classpath}" name="classpath"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
||||
<jvmarg line="${debug-args-line}"/>
|
||||
<jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
<classpath>
|
||||
<path path="@{classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-macrodef-java">
|
||||
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<attribute default="${main.class}" name="classname"/>
|
||||
<element name="customize" optional="true"/>
|
||||
<sequential>
|
||||
<java classname="@{classname}" dir="${work.dir}" fork="true">
|
||||
<jvmarg line="${run.jvmargs}"/>
|
||||
<classpath>
|
||||
<path path="${run.classpath}"/>
|
||||
</classpath>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="run-sys-prop."/>
|
||||
<mapper from="run-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<customize/>
|
||||
</java>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
</target>
|
||||
<target name="-init-presetdef-jar">
|
||||
<presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
|
||||
<jar compress="${jar.compress}" jarfile="${dist.jar}">
|
||||
<j2seproject1:fileset dir="${build.classes.dir}"/>
|
||||
</jar>
|
||||
</presetdef>
|
||||
</target>
|
||||
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar" name="init"/>
|
||||
<!--
|
||||
===================
|
||||
COMPILATION SECTION
|
||||
===================
|
||||
-->
|
||||
<target depends="init" name="deps-jar" unless="no.deps"/>
|
||||
<target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/>
|
||||
<target depends="init" name="-check-automatic-build">
|
||||
<available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/>
|
||||
</target>
|
||||
<target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build">
|
||||
<antcall target="clean"/>
|
||||
</target>
|
||||
<target depends="init,deps-jar" name="-pre-pre-compile">
|
||||
<mkdir dir="${build.classes.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-compile">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target if="do.depend.true" name="-compile-depend">
|
||||
<j2seproject3:depend/>
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile,-pre-compile,-compile-depend" if="have.sources" name="-do-compile">
|
||||
<j2seproject3:javac/>
|
||||
<copy todir="${build.classes.dir}">
|
||||
<fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/>
|
||||
<target name="-pre-compile-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single">
|
||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
||||
<j2seproject3:force-recompile/>
|
||||
<j2seproject3:javac excludes="" includes="${javac.includes}" sourcepath="${src.dir}"/>
|
||||
</target>
|
||||
<target name="-post-compile-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/>
|
||||
<!--
|
||||
====================
|
||||
JAR BUILDING SECTION
|
||||
====================
|
||||
-->
|
||||
<target depends="init" name="-pre-pre-jar">
|
||||
<dirname file="${dist.jar}" property="dist.jar.dir"/>
|
||||
<mkdir dir="${dist.jar.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-jar">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" name="-do-jar-without-manifest" unless="manifest.available">
|
||||
<j2seproject1:jar/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class">
|
||||
<j2seproject1:jar manifest="${manifest.file}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
|
||||
<j2seproject1:jar manifest="${manifest.file}">
|
||||
<j2seproject1:manifest>
|
||||
<j2seproject1:attribute name="Main-Class" value="${main.class}"/>
|
||||
</j2seproject1:manifest>
|
||||
</j2seproject1:jar>
|
||||
<echo>To run this application from the command line without Ant, try:</echo>
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
||||
<pathconvert property="run.classpath.with.dist.jar">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
|
||||
</pathconvert>
|
||||
<echo>java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="manifest.available+main.class+mkdist.available" name="-do-jar-with-libraries">
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to=""/>
|
||||
</pathconvert>
|
||||
<pathconvert pathsep=" " property="jar.classpath">
|
||||
<path path="${run.classpath.without.build.classes.dir}"/>
|
||||
<chainedmapper>
|
||||
<flattenmapper/>
|
||||
<globmapper from="*" to="lib/*"/>
|
||||
</chainedmapper>
|
||||
</pathconvert>
|
||||
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
|
||||
<copylibs compress="${jar.compress}" jarfile="${dist.jar}" manifest="${manifest.file}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
|
||||
<fileset dir="${build.classes.dir}"/>
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="${main.class}"/>
|
||||
<attribute name="Class-Path" value="${jar.classpath}"/>
|
||||
</manifest>
|
||||
</copylibs>
|
||||
<echo>To run this application from the command line without Ant, try:</echo>
|
||||
<property location="${dist.jar}" name="dist.jar.resolved"/>
|
||||
<echo>java -jar "${dist.jar.resolved}"</echo>
|
||||
</target>
|
||||
<target name="-post-jar">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
|
||||
<!--
|
||||
=================
|
||||
EXECUTION SECTION
|
||||
=================
|
||||
-->
|
||||
<target depends="init,compile" description="Run a main class." name="run">
|
||||
<j2seproject1:java>
|
||||
<customize>
|
||||
<arg line="${application.args}"/>
|
||||
</customize>
|
||||
</j2seproject1:java>
|
||||
</target>
|
||||
<target name="-do-not-recompile">
|
||||
<property name="javac.includes.binary" value=""/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-single" name="run-single">
|
||||
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
|
||||
<j2seproject1:java classname="${run.class}"/>
|
||||
</target>
|
||||
<!--
|
||||
=================
|
||||
DEBUGGING SECTION
|
||||
=================
|
||||
-->
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger">
|
||||
<j2seproject1:nbjpdastart name="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init,compile" name="-debug-start-debuggee">
|
||||
<j2seproject3:debug>
|
||||
<customize>
|
||||
<arg line="${application.args}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/>
|
||||
<target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto">
|
||||
<j2seproject1:nbjpdastart stopclassname="${main.class}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/>
|
||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single">
|
||||
<fail unless="debug.class">Must select one file in the IDE or set debug.class</fail>
|
||||
<j2seproject3:debug classname="${debug.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/>
|
||||
<target depends="init" name="-pre-debug-fix">
|
||||
<fail unless="fix.includes">Must set fix.includes</fail>
|
||||
<property name="javac.includes" value="${fix.includes}.java"/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix">
|
||||
<j2seproject1:nbjpdareload/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/>
|
||||
<!--
|
||||
===============
|
||||
JAVADOC SECTION
|
||||
===============
|
||||
-->
|
||||
<target depends="init" name="-javadoc-build">
|
||||
<mkdir dir="${dist.javadoc.dir}"/>
|
||||
<javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
|
||||
<classpath>
|
||||
<path path="${javac.classpath}"/>
|
||||
</classpath>
|
||||
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
|
||||
<filename name="**/*.java"/>
|
||||
</fileset>
|
||||
</javadoc>
|
||||
</target>
|
||||
<target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview">
|
||||
<nbbrowse file="${dist.javadoc.dir}/index.html"/>
|
||||
</target>
|
||||
<target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
|
||||
<!--
|
||||
=========================
|
||||
JUNIT COMPILATION SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
|
||||
<mkdir dir="${build.test.classes.dir}"/>
|
||||
</target>
|
||||
<target name="-pre-compile-test">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target if="do.depend.true" name="-compile-test-depend">
|
||||
<j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test">
|
||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" srcdir="${test.src.dir}"/>
|
||||
<copy todir="${build.test.classes.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile-test">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/>
|
||||
<target name="-pre-compile-test-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single">
|
||||
<fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail>
|
||||
<j2seproject3:force-recompile destdir="${build.test.classes.dir}"/>
|
||||
<j2seproject3:javac classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" sourcepath="${test.src.dir}" srcdir="${test.src.dir}"/>
|
||||
<copy todir="${build.test.classes.dir}">
|
||||
<fileset dir="${test.src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/>
|
||||
</copy>
|
||||
</target>
|
||||
<target name="-post-compile-test-single">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
|
||||
<!--
|
||||
=======================
|
||||
JUNIT EXECUTION SECTION
|
||||
=======================
|
||||
-->
|
||||
<target depends="init" if="have.tests" name="-pre-test-run">
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
|
||||
<j2seproject3:junit testincludes="**/*Test.java"/>
|
||||
</target>
|
||||
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
|
||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
||||
</target>
|
||||
<target depends="init" if="have.tests" name="test-report"/>
|
||||
<target depends="init" if="netbeans.home+have.tests" name="-test-browse"/>
|
||||
<target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/>
|
||||
<target depends="init" if="have.tests" name="-pre-test-run-single">
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
|
||||
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
|
||||
<j2seproject3:junit excludes="" includes="${test.includes}"/>
|
||||
</target>
|
||||
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
|
||||
<fail if="tests.failed">Some tests failed; see details above.</fail>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
|
||||
<!--
|
||||
=======================
|
||||
JUNIT DEBUGGING SECTION
|
||||
=======================
|
||||
-->
|
||||
<target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
|
||||
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
|
||||
<property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
|
||||
<delete file="${test.report.file}"/>
|
||||
<mkdir dir="${build.test.results.dir}"/>
|
||||
<j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
|
||||
<customize>
|
||||
<syspropertyset>
|
||||
<propertyref prefix="test-sys-prop."/>
|
||||
<mapper from="test-sys-prop.*" to="*" type="glob"/>
|
||||
</syspropertyset>
|
||||
<arg value="${test.class}"/>
|
||||
<arg value="showoutput=true"/>
|
||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
|
||||
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
|
||||
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
|
||||
</target>
|
||||
<target depends="init,-do-not-recompile,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
|
||||
<target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
|
||||
<j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
|
||||
</target>
|
||||
<target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/>
|
||||
<!--
|
||||
=========================
|
||||
APPLET EXECUTION SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile-single" name="run-applet">
|
||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
||||
<j2seproject1:java classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</j2seproject1:java>
|
||||
</target>
|
||||
<!--
|
||||
=========================
|
||||
APPLET DEBUGGING SECTION
|
||||
=========================
|
||||
-->
|
||||
<target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet">
|
||||
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
|
||||
<j2seproject3:debug classname="sun.applet.AppletViewer">
|
||||
<customize>
|
||||
<arg value="${applet.url}"/>
|
||||
</customize>
|
||||
</j2seproject3:debug>
|
||||
</target>
|
||||
<target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/>
|
||||
<!--
|
||||
===============
|
||||
CLEANUP SECTION
|
||||
===============
|
||||
-->
|
||||
<target depends="init" name="deps-clean" unless="no.deps"/>
|
||||
<target depends="init" name="-do-clean">
|
||||
<delete dir="${build.dir}"/>
|
||||
<delete dir="${dist.dir}"/>
|
||||
</target>
|
||||
<target name="-post-clean">
|
||||
<!-- Empty placeholder for easier customization. -->
|
||||
<!-- You can override this target in the ../build.xml file. -->
|
||||
</target>
|
||||
<target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/>
|
||||
</project>
|
8
apps/BOB/nbproject/genfiles.properties
Normal file
8
apps/BOB/nbproject/genfiles.properties
Normal file
@ -0,0 +1,8 @@
|
||||
build.xml.data.CRC32=209349b6
|
||||
build.xml.script.CRC32=403e69e6
|
||||
build.xml.stylesheet.CRC32=958a1d3e
|
||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||
nbproject/build-impl.xml.data.CRC32=209349b6
|
||||
nbproject/build-impl.xml.script.CRC32=c51e188e
|
||||
nbproject/build-impl.xml.stylesheet.CRC32=65b8de21
|
84
apps/BOB/nbproject/project.properties
Normal file
84
apps/BOB/nbproject/project.properties
Normal file
@ -0,0 +1,84 @@
|
||||
application.homepage=http://bob.i2p/
|
||||
application.title=BOB
|
||||
application.vendor=Sponge
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=false
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=8
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=8
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=8
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
|
||||
auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project
|
||||
build.classes.dir=${build.dir}/classes
|
||||
build.classes.excludes=**/*.java,**/*.form
|
||||
# This directory is removed when the project is cleaned:
|
||||
build.dir=build
|
||||
build.generated.dir=${build.dir}/generated
|
||||
build.generated.sources.dir=${build.dir}/generated-sources
|
||||
# Only compile against the classpath explicitly listed here:
|
||||
build.sysclasspath=ignore
|
||||
build.test.classes.dir=${build.dir}/test/classes
|
||||
build.test.results.dir=${build.dir}/test/results
|
||||
debug.classpath=\
|
||||
${run.classpath}
|
||||
debug.test.classpath=\
|
||||
${run.test.classpath}
|
||||
# This directory is removed when the project is cleaned:
|
||||
dist.dir=dist
|
||||
dist.jar=${dist.dir}/BOB.jar
|
||||
dist.javadoc.dir=${dist.dir}/javadoc
|
||||
endorsed.classpath=
|
||||
excludes=**/*.html,**/*.txt
|
||||
file.reference.build-javadoc=../../i2p.i2p/build/javadoc
|
||||
file.reference.i2p.jar=../../core/java/build/i2p.jar
|
||||
file.reference.mstreaming.jar=../ministreaming/java/build/mstreaming.jar
|
||||
includes=**
|
||||
jar.compress=true
|
||||
javac.classpath=\
|
||||
${file.reference.mstreaming.jar}:\
|
||||
${file.reference.i2p.jar}
|
||||
# Space-separated list of extra javac options
|
||||
javac.compilerargs=
|
||||
javac.deprecation=false
|
||||
javac.version=1.8
|
||||
javac.source=${javac.version}
|
||||
javac.target=${javac.version}
|
||||
javac.release=8
|
||||
javac.test.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}:\
|
||||
${libs.junit.classpath}:\
|
||||
${libs.junit_4.classpath}
|
||||
javadoc.additionalparam=
|
||||
javadoc.author=false
|
||||
javadoc.encoding=${source.encoding}
|
||||
javadoc.noindex=false
|
||||
javadoc.nonavbar=false
|
||||
javadoc.notree=false
|
||||
javadoc.private=false
|
||||
javadoc.splitindex=true
|
||||
javadoc.use=true
|
||||
javadoc.version=false
|
||||
javadoc.windowtitle=
|
||||
jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
|
||||
jnlp.codebase.type=local
|
||||
jnlp.codebase.url=file:/usblv/NetBeansProjects/i2p.i2p/apps/BOB/dist
|
||||
jnlp.descriptor=application
|
||||
jnlp.enabled=false
|
||||
jnlp.offline-allowed=false
|
||||
jnlp.signed=false
|
||||
main.class=net.i2p.BOB.Main
|
||||
manifest.file=manifest.mf
|
||||
meta.inf.dir=${src.dir}/META-INF
|
||||
platform.active=default_platform
|
||||
run.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}
|
||||
# Space-separated list of JVM arguments used when running the project
|
||||
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
|
||||
# or test-sys-prop.name=value to set system properties for unit tests):
|
||||
run.jvmargs=
|
||||
run.test.classpath=\
|
||||
${javac.test.classpath}:\
|
||||
${build.test.classes.dir}
|
||||
source.encoding=UTF-8
|
||||
src.dir=src
|
||||
test.src.dir=test
|
16
apps/BOB/nbproject/project.xml
Normal file
16
apps/BOB/nbproject/project.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||
<type>org.netbeans.modules.java.j2seproject</type>
|
||||
<configuration>
|
||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<name>BOB</name>
|
||||
<minimum-ant-version>1.9.8</minimum-ant-version>
|
||||
<source-roots>
|
||||
<root id="src.dir"/>
|
||||
</source-roots>
|
||||
<test-roots>
|
||||
<root id="test.src.dir"/>
|
||||
</test-roots>
|
||||
</data>
|
||||
</configuration>
|
||||
</project>
|
542
apps/BOB/src/net/i2p/BOB/BOB.java
Normal file
542
apps/BOB/src/net/i2p/BOB/BOB.java
Normal file
@ -0,0 +1,542 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.net.URL;
|
||||
import java.util.Arrays;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.*;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.PortMapper;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* <span style="font-size:8px;font-family:courier;color:#EEEEEE;background-color:#000000">
|
||||
* ################################################################################<br>
|
||||
* ############################.#..........#..#..........##########################<br>
|
||||
* #######################......................................###################<br>
|
||||
* ####################...........................#.......#........################<br>
|
||||
* #################..................##...................#.........##############<br>
|
||||
* ###############................###...####.....#..###.....#.........#############<br>
|
||||
* #############...........###..#..###...#####...###.##........#.......############<br>
|
||||
* ###########................#......##...#####...##..##.......#..#........########<br>
|
||||
* ##########.........................#....##.##..#...##.....................######<br>
|
||||
* #########...................................#....#.........................#####<br>
|
||||
* ########.........................................#...............#..........####<br>
|
||||
* ########.........................................#..........#######..........###<br>
|
||||
* #######.................................................############..........##<br>
|
||||
* #######..........................................####################.........##<br>
|
||||
* #######............####################......########################.........##<br>
|
||||
* ######.............###############################################.##.........##<br>
|
||||
* ######............################################################..##........##<br>
|
||||
* ######............################################################..##........##<br>
|
||||
* ######.............##############################################..##.........##<br>
|
||||
* ######............##############################################...##..........#<br>
|
||||
* ######............#..###########################################...##..........#<br>
|
||||
* ######.............#############################################....#..........#<br>
|
||||
* #######...........###############################################..##.........##<br>
|
||||
* #######...........#####.#.#.#.########################.....#.####...##........##<br>
|
||||
* ######............#..............##################.................##.........#<br>
|
||||
* ######................####.........###############........#####......##........#<br>
|
||||
* ######..............####..#.........############.......##.#.######...##.......##<br>
|
||||
* ######.................#.####.........########...........##....###...##.......##<br>
|
||||
* #######....#....###...................#######...............#...###..##.......##<br>
|
||||
* #######.........###..###.....###.......######.##.#####.........####..##.......##<br>
|
||||
* #######.....#...##############.........############......###########.###......##<br>
|
||||
* #######....##...##########.......##...##############......#.############.....###<br>
|
||||
* ########....#..########......######...##################################....####<br>
|
||||
* ########....##.####################...##################################....####<br>
|
||||
* ########..#.##..###################..##################################..#..####<br>
|
||||
* ##########..###..#################...##################################...#.####<br>
|
||||
* #########....##...##############....########..#####.################.##..#.#####<br>
|
||||
* ############.##....##########.......#########.###.......###########..#.#########<br>
|
||||
* ###############.....#######...#.......########.....##.....######.....###########<br>
|
||||
* ###############......###....##..........##.......######....#.........#.#########<br>
|
||||
* ##############............##..................##########..............##########<br>
|
||||
* ##############..............................##########..#.............##########<br>
|
||||
* ###############.......##..................#####..............####....###########<br>
|
||||
* ###############.......#####.......#.............####.....#######.....###########<br>
|
||||
* ################...#...####......##################.....########....############<br>
|
||||
* ################...##..#####.........####.##.....#....##########....############<br>
|
||||
* ##################..##..####...........#####.#....############.....#############<br>
|
||||
* ##################......#####.................################....##############<br>
|
||||
* ###################.....####..........##########..###########....###############<br>
|
||||
* ####################..#..#..........................########.....###############<br>
|
||||
* #####################.##.......###.................########....#################<br>
|
||||
* ######################.........#.......#.##.###############....#################<br>
|
||||
* #############.#######...............#####################....###################<br>
|
||||
* ###..#.....##...####..........#.....####################....####################<br>
|
||||
* ####......##........................##################....######################<br>
|
||||
* #.##...###..............###.........###############......#######################<br>
|
||||
* #...###..##............######...........................########################<br>
|
||||
* ##.......###..........##########....#...#...........############################<br>
|
||||
* ##.........##.......############################################################<br>
|
||||
* ###........##.....##############################################################<br>
|
||||
* ####.............###############################################################<br>
|
||||
* ######.........#################################################################<br>
|
||||
* #########....###################################################################<br>
|
||||
* ################################################################################<br>
|
||||
* </span>
|
||||
* BOB, main command socket listener, launches the command parser engine.
|
||||
*
|
||||
* @author sponge
|
||||
* @deprecated Please port applications to SAMv3
|
||||
*/
|
||||
@Deprecated
|
||||
public class BOB implements Runnable, ClientApp {
|
||||
|
||||
public final static String PROP_CONFIG_LOCATION = "BOB.config";
|
||||
public final static String PROP_BOB_PORT = "BOB.port";
|
||||
public final static String PROP_BOB_HOST = "BOB.host";
|
||||
public final static String PROP_CFG_VER = "BOB.CFG.VER";
|
||||
|
||||
/** unused when started via the ClientApp interface */
|
||||
private static BOB _bob;
|
||||
|
||||
private final NamedDB database;
|
||||
private final Properties props = new Properties();
|
||||
private final AtomicBoolean spin = new AtomicBoolean(true);
|
||||
private static final String P_RUNNING = "RUNNING";
|
||||
private static final String P_STARTING = "STARTING";
|
||||
private static final String P_STOPPING = "STOPPING";
|
||||
private final AtomicBoolean lock = new AtomicBoolean(false);
|
||||
// no longer used.
|
||||
// private static int maxConnections = 0;
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Logger _log;
|
||||
private final ClientAppManager _mgr;
|
||||
private final String[] _args;
|
||||
private volatile ClientAppState _state = UNINITIALIZED;
|
||||
|
||||
private volatile ServerSocket listener;
|
||||
private volatile Thread _runner;
|
||||
private volatile boolean _warned;
|
||||
|
||||
/**
|
||||
* Stop BOB gracefully
|
||||
* @deprecated unused
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized static void stop() {
|
||||
if (_bob != null)
|
||||
_bob.shutdown(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* For ClientApp interface.
|
||||
* Does NOT open the listener socket or start threads; caller must call startup()
|
||||
*
|
||||
* @param mgr may be null
|
||||
* @param args non-null
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public BOB(I2PAppContext context, ClientAppManager mgr, String[] args) {
|
||||
_context = context;
|
||||
// If we were run from command line, log to stdout
|
||||
boolean logToStdout = false;
|
||||
URL classResource = BOB.class.getResource("BOB.class");
|
||||
if (classResource != null) {
|
||||
String classPath = classResource.toString();
|
||||
if (classPath.startsWith("jar")) {
|
||||
String manifestPath = classPath.substring(0, classPath.lastIndexOf('!') + 1) +
|
||||
"/META-INF/MANIFEST.MF";
|
||||
try {
|
||||
Manifest manifest = new Manifest(new URL(manifestPath).openStream());
|
||||
Attributes attrs = manifest.getMainAttributes();
|
||||
String mainClass = attrs.getValue("Main-Class");
|
||||
if ("net.i2p.BOB.Main".equals(mainClass))
|
||||
logToStdout = true;
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
_log = new Logger(context.logManager().getLog(BOB.class), logToStdout);
|
||||
|
||||
_mgr = mgr;
|
||||
_args = args;
|
||||
_state = INITIALIZED;
|
||||
database = new NamedDB();
|
||||
loadConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for incoming connections and handle them
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public synchronized static void main(String[] args) {
|
||||
try {
|
||||
_bob = new BOB(I2PAppContext.getGlobalContext(), null, args);
|
||||
_bob.startup();
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void loadConfig() {
|
||||
int i = 0;
|
||||
boolean save = false;
|
||||
// Set up all defaults to be passed forward to other threads.
|
||||
// Re-reading the config file in each thread is pretty damn stupid.
|
||||
String configLocation = System.getProperty(PROP_CONFIG_LOCATION, "bob.config");
|
||||
// This is here just to ensure there is no interference with our threadgroups.
|
||||
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
|
||||
i = Y2.hashCode();
|
||||
{
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(_context.getConfigDir(), configLocation);
|
||||
}
|
||||
FileInputStream fi = null;
|
||||
try {
|
||||
fi = new FileInputStream(cfg);
|
||||
props.load(fi);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
_log.warn("Unable to load up the BOB config file " + cfg.getAbsolutePath() + ", Using defaults.", fnfe);
|
||||
save = true;
|
||||
} catch (IOException ioe) {
|
||||
_log.warn("IOException on BOB config file " + cfg.getAbsolutePath() + ", using defaults.", ioe);
|
||||
} finally {
|
||||
if (fi != null) try { fi.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
// Global router and client API configurations that are missing are set to defaults here.
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_HOST)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_HOST, "localhost");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(I2PClient.PROP_TCP_PORT)) {
|
||||
props.setProperty(I2PClient.PROP_TCP_PORT, Integer.toString(I2PClient.DEFAULT_LISTEN_PORT));
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_PORT)) {
|
||||
props.setProperty(PROP_BOB_PORT, "2827"); // 0xB0B
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.length")) {
|
||||
props.setProperty("inbound.length", "3");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.length")) {
|
||||
props.setProperty("outbound.length", "3");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("inbound.lengthVariance")) {
|
||||
props.setProperty("inbound.lengthVariance", "0");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey("outbound.lengthVariance")) {
|
||||
props.setProperty("outbound.lengthVariance", "0");
|
||||
save = true;
|
||||
}
|
||||
if (!props.containsKey(PROP_BOB_HOST)) {
|
||||
props.setProperty(PROP_BOB_HOST, "localhost");
|
||||
save = true;
|
||||
}
|
||||
// PROP_RELIABILITY_NONE, PROP_RELIABILITY_BEST_EFFORT, PROP_RELIABILITY_GUARANTEED
|
||||
if (!props.containsKey(PROP_CFG_VER)) {
|
||||
props.setProperty(I2PClient.PROP_RELIABILITY, I2PClient.PROP_RELIABILITY_NONE);
|
||||
props.setProperty(PROP_CFG_VER,"1");
|
||||
save = true;
|
||||
}
|
||||
if (save) {
|
||||
File cfg = new File(configLocation);
|
||||
if (!cfg.isAbsolute()) {
|
||||
cfg = new File(_context.getConfigDir(), configLocation);
|
||||
}
|
||||
FileOutputStream fo = null;
|
||||
try {
|
||||
_log.warn("Writing new defaults file " + cfg.getAbsolutePath());
|
||||
fo = new FileOutputStream(cfg);
|
||||
props.store(fo, cfg.getAbsolutePath());
|
||||
} catch (IOException ioe) {
|
||||
_log.error("IOException on BOB config file " + cfg.getAbsolutePath(), ioe);
|
||||
} finally {
|
||||
if (fo != null) try { fo.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void startListener() throws IOException {
|
||||
listener = new ServerSocket(Integer.parseInt(props.getProperty(PROP_BOB_PORT)), 10, InetAddress.getByName(props.getProperty(PROP_BOB_HOST)));
|
||||
listener.setSoTimeout(500); // .5 sec
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void startThread() {
|
||||
I2PAppThread t = new I2PAppThread(this, "BOBListener");
|
||||
t.start();
|
||||
_runner = t;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
public void run() {
|
||||
if (listener == null) return;
|
||||
changeState(RUNNING);
|
||||
_log.info("BOB is now running.");
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
_context.portMapper().register(PortMapper.SVC_BOB, props.getProperty(PROP_BOB_HOST),
|
||||
Integer.parseInt(props.getProperty(PROP_BOB_PORT)));
|
||||
|
||||
int i = 0;
|
||||
boolean g = false;
|
||||
spin.set(true);
|
||||
try {
|
||||
Socket server = null;
|
||||
|
||||
while (spin.get()) {
|
||||
//DoCMDS connection;
|
||||
|
||||
try {
|
||||
server = listener.accept();
|
||||
server.setKeepAlive(true);
|
||||
g = true;
|
||||
} catch (ConnectException ce) {
|
||||
g = false;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
}
|
||||
|
||||
if (g) {
|
||||
if (!_warned) {
|
||||
_warned = true;
|
||||
String s = "BOB is deprecated. Please port applications to SAMv3.";
|
||||
_context.logManager().getLog(BOB.class).logAlways(Log.WARN, s);
|
||||
_log.warn(s);
|
||||
}
|
||||
DoCMDS conn_c = new DoCMDS(spin, lock, server, props, database, _log);
|
||||
Thread t = new I2PAppThread(conn_c);
|
||||
t.setName("BOB.DoCMDS " + i);
|
||||
t.start();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
changeState(STOPPING);
|
||||
} catch (Exception e) {
|
||||
if (spin.get())
|
||||
_log.error("Unexpected error while listening for connections", e);
|
||||
else
|
||||
e = null;
|
||||
changeState(STOPPING, e);
|
||||
} finally {
|
||||
_log.info("BOB is now shutting down...");
|
||||
_context.portMapper().unregister(PortMapper.SVC_BOB);
|
||||
// Clean up everything.
|
||||
try {
|
||||
listener.close();
|
||||
} catch (Exception ex) {
|
||||
// nop
|
||||
}
|
||||
// Find all our "BOB.DoCMDS" threads, wait for them to be finished.
|
||||
// We could order them to stop, but that could cause nasty issues in the locks.
|
||||
visitAllThreads();
|
||||
database.getReadLock();
|
||||
NamedDB nickinfo;
|
||||
try {
|
||||
for (Object ndb : database.values()) {
|
||||
nickinfo = (NamedDB) ndb;
|
||||
nickinfo.getReadLock();
|
||||
boolean released = false;
|
||||
try {
|
||||
if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
|
||||
nickinfo.releaseReadLock();
|
||||
released = true;
|
||||
nickinfo.getWriteLock();
|
||||
try {
|
||||
nickinfo.add(P_STOPPING, Boolean.TRUE);
|
||||
} finally {
|
||||
nickinfo.releaseWriteLock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (!released)
|
||||
nickinfo.releaseReadLock();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
changeState(STOPPED);
|
||||
_log.info("BOB is now stopped.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the root thread group,
|
||||
* then find all theads with certain names and wait for them all to be dead.
|
||||
*
|
||||
*/
|
||||
private static void visitAllThreads() {
|
||||
ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
|
||||
while (root.getParent() != null) {
|
||||
root = root.getParent();
|
||||
}
|
||||
|
||||
// Visit each thread group
|
||||
waitjoin(root, 0, root.getName());
|
||||
}
|
||||
|
||||
private static void waitjoin(ThreadGroup group, int level, String tn) {
|
||||
// Get threads in `group'
|
||||
int numThreads = group.activeCount();
|
||||
Thread[] threads = new Thread[numThreads * 2];
|
||||
numThreads = group.enumerate(threads, false);
|
||||
// Enumerate each thread in `group' and wait for it to stop if it is one of ours.
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
// Get thread
|
||||
Thread thread = threads[i];
|
||||
if (thread.getName().startsWith("BOB.DoCMDS ")) {
|
||||
try {
|
||||
if (thread.isAlive()) {
|
||||
try {
|
||||
thread.join();
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
} catch (SecurityException se) {
|
||||
//nop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get thread subgroups of `group'
|
||||
int numGroups = group.activeGroupCount();
|
||||
ThreadGroup[] groups = new ThreadGroup[numGroups * 2];
|
||||
numGroups = group.enumerate(groups, false);
|
||||
|
||||
// Recursively visit each subgroup
|
||||
for (int i = 0; i < numGroups; i++) {
|
||||
waitjoin(groups[i], level + 1, groups[i].getName());
|
||||
}
|
||||
}
|
||||
|
||||
////// begin ClientApp interface
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public void startup() throws IOException {
|
||||
if (_state != INITIALIZED)
|
||||
return;
|
||||
changeState(STARTING);
|
||||
try {
|
||||
startListener();
|
||||
} catch (IOException e) {
|
||||
_log.error("Error starting BOB on"
|
||||
+ props.getProperty(PROP_BOB_HOST)
|
||||
+ ":" + props.getProperty(PROP_BOB_PORT), e);
|
||||
changeState(START_FAILED, e);
|
||||
throw e;
|
||||
}
|
||||
startThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public void shutdown(String[] args) {
|
||||
if (_state != RUNNING)
|
||||
return;
|
||||
changeState(STOPPING);
|
||||
spin.set(false);
|
||||
if (_runner != null)
|
||||
_runner.interrupt();
|
||||
else
|
||||
changeState(STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public ClientAppState getState() {
|
||||
return _state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return "BOB";
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "BOB " + Arrays.toString(_args);
|
||||
}
|
||||
|
||||
////// end ClientApp interface
|
||||
////// begin ClientApp helpers
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private void changeState(ClientAppState state) {
|
||||
changeState(state, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.10
|
||||
*/
|
||||
private synchronized void changeState(ClientAppState state, Exception e) {
|
||||
_state = state;
|
||||
if (_mgr != null)
|
||||
_mgr.notify(this, state, null, e);
|
||||
}
|
||||
|
||||
////// end ClientApp helpers
|
||||
}
|
996
apps/BOB/src/net/i2p/BOB/DoCMDS.java
Normal file
996
apps/BOB/src/net/i2p/BOB/DoCMDS.java
Normal file
@ -0,0 +1,996 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClientFactory;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
// needed only for debugging.
|
||||
// import java.util.logging.Level;
|
||||
// import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Simplistic command parser for BOB
|
||||
*
|
||||
* @author sponge
|
||||
*
|
||||
*/
|
||||
public class DoCMDS implements Runnable {
|
||||
|
||||
// FIX ME
|
||||
// I need a better way to do versioning, but this will do for now.
|
||||
public static final String BMAJ = "00", BMIN = "00", BREV = "10", BEXT = "";
|
||||
public static final String BOBversion = BMAJ + "." + BMIN + "." + BREV + BEXT;
|
||||
private final Socket server;
|
||||
private final Properties props;
|
||||
private final NamedDB database;
|
||||
private String line;
|
||||
private Destination d;
|
||||
private ByteArrayOutputStream prikey;
|
||||
private boolean dk, ns, ip, op;
|
||||
private NamedDB nickinfo;
|
||||
private final Logger _log;
|
||||
private final AtomicBoolean LIVE;
|
||||
private final AtomicBoolean lock;
|
||||
/* database strings */
|
||||
private static final String P_DEST = "DESTINATION";
|
||||
private static final String P_INHOST = "INHOST";
|
||||
private static final String P_INPORT = "INPORT";
|
||||
private static final String P_KEYS = "KEYS";
|
||||
private static final String P_NICKNAME = "NICKNAME";
|
||||
private static final String P_OUTHOST = "OUTHOST";
|
||||
private static final String P_OUTPORT = "OUTPORT";
|
||||
private static final String P_PROPERTIES = "PROPERTIES";
|
||||
private static final String P_QUIET = "QUIET";
|
||||
private static final String P_RUNNING = "RUNNING";
|
||||
private static final String P_STARTING = "STARTING";
|
||||
private static final String P_STOPPING = "STOPPING";
|
||||
|
||||
/* command strings */
|
||||
private static final String C_help = "help";
|
||||
private static final String C_clear = "clear";
|
||||
private static final String C_getdest = "getdest";
|
||||
private static final String C_getkeys = "getkeys";
|
||||
private static final String C_getnick = "getnick";
|
||||
private static final String C_inhost = "inhost";
|
||||
private static final String C_inport = "inport";
|
||||
private static final String C_list = "list";
|
||||
private static final String C_lookup = "lookup";
|
||||
private static final String C_newkeys = "newkeys";
|
||||
private static final String C_option = "option";
|
||||
private static final String C_outhost = "outhost";
|
||||
private static final String C_outport = "outport";
|
||||
private static final String C_quiet = "quiet";
|
||||
private static final String C_quit = "quit";
|
||||
private static final String C_setkeys = "setkeys";
|
||||
private static final String C_setnick = "setnick";
|
||||
private static final String C_show = "show";
|
||||
private static final String C_show_props = "showprops";
|
||||
private static final String C_start = "start";
|
||||
private static final String C_status = "status";
|
||||
private static final String C_stop = "stop";
|
||||
private static final String C_verify = "verify";
|
||||
private static final String C_visit = "visit";
|
||||
private static final String C_zap = "zap";
|
||||
|
||||
/* all the commands available, plus description */
|
||||
private static final String C_ALL[][] = {
|
||||
{C_help, C_help + " <command> * Get help on a command."},
|
||||
{C_clear, C_clear + " * Clear the current nickname out of the list."},
|
||||
{C_getdest, C_getdest + " * Return the destination for the current nickname."},
|
||||
{C_getkeys, C_getkeys + " * Return the keypair for the current nickname."},
|
||||
{C_getnick, C_getnick + " tunnelname * Set the nickname from the database."},
|
||||
{C_inhost, C_inhost + " hostname | IP * Set the inbound hostname or IP."},
|
||||
{C_inport, C_inport + " port_number * Set the inbound port number nickname listens on."},
|
||||
{C_list, C_list + " * List all tunnels."},
|
||||
{C_lookup, C_lookup + " * Lookup an i2p address."},
|
||||
{C_newkeys, C_newkeys + " * Generate a new keypair for the current nickname."},
|
||||
{C_option, C_option + " I2CPoption=something * Set an I2CP option. NOTE: Don't use any spaces."},
|
||||
{C_outhost, C_outhost + " hostname | IP * Set the outbound hostname or IP."},
|
||||
{C_outport, C_outport + " port_number * Set the outbound port that nickname contacts."},
|
||||
{C_quiet, C_quiet + " True | False * Don't send to the application the incoming destination."},
|
||||
{C_quit, C_quit + " * Quits this session with BOB."},
|
||||
{C_setkeys, C_setkeys + " BASE64_keypair * Sets the keypair for the current nickname."},
|
||||
{C_setnick, C_setnick + " nickname * Create a new nickname."},
|
||||
{C_show, C_show + " * Display the status of the current nickname."},
|
||||
{C_show_props, C_show_props + " * Display the properties of the current nickname."},
|
||||
{C_start, C_start + " * Start the current nickname tunnel."},
|
||||
{C_status, C_status + " nickname * Display status of a nicknamed tunnel."},
|
||||
{C_stop, C_stop + " * Stops the current nicknamed tunnel."},
|
||||
{C_verify, C_verify + " BASE64_key * Verifies BASE64 destination."},
|
||||
{C_visit, C_visit + " * Thread dump to wrapper.log."},
|
||||
{C_zap, C_zap + " * Shuts down BOB."},
|
||||
{"", "COMMANDS: " + // this is ugly, but...
|
||||
C_help + " " +
|
||||
C_clear + " " +
|
||||
C_getdest + " " +
|
||||
C_getkeys + " " +
|
||||
C_getnick + " " +
|
||||
C_inhost + " " +
|
||||
C_inport + " " +
|
||||
C_list + " " +
|
||||
C_lookup + " " +
|
||||
C_newkeys + " " +
|
||||
C_option + " " +
|
||||
C_outhost + " " +
|
||||
C_outport + " " +
|
||||
C_quiet + " " +
|
||||
C_quit + " " +
|
||||
C_setkeys + " " +
|
||||
C_setnick + " " +
|
||||
C_show + " " +
|
||||
C_show_props + " " +
|
||||
C_start + " " +
|
||||
C_status + " " +
|
||||
C_stop + " " +
|
||||
C_verify + " " +
|
||||
C_visit + " " +
|
||||
C_zap
|
||||
},
|
||||
{" ", " "} // end of list
|
||||
};
|
||||
|
||||
/**
|
||||
* @param LIVE
|
||||
* @param server
|
||||
* @param props
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
DoCMDS(AtomicBoolean LIVE, AtomicBoolean lock, Socket server, Properties props, NamedDB database, Logger _log) {
|
||||
this.lock = lock;
|
||||
this.LIVE = LIVE;
|
||||
this.server = server;
|
||||
this.props = new Properties();
|
||||
this.database = database;
|
||||
this._log = _log;
|
||||
Lifted.copyProperties(props, this.props);
|
||||
}
|
||||
|
||||
private void rlock() {
|
||||
rlock(nickinfo);
|
||||
}
|
||||
|
||||
private void rlock(NamedDB Arg) {
|
||||
database.getReadLock();
|
||||
Arg.getReadLock();
|
||||
}
|
||||
|
||||
private void runlock() {
|
||||
runlock(nickinfo);
|
||||
}
|
||||
|
||||
private void runlock(NamedDB Arg) {
|
||||
Arg.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
private void wlock() {
|
||||
wlock(nickinfo);
|
||||
}
|
||||
|
||||
private void wlock(NamedDB Arg) {
|
||||
database.getWriteLock();
|
||||
Arg.getWriteLock();
|
||||
}
|
||||
|
||||
private void wunlock() {
|
||||
wunlock(nickinfo);
|
||||
}
|
||||
|
||||
private void wunlock(NamedDB Arg) {
|
||||
Arg.releaseWriteLock();
|
||||
database.releaseWriteLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to print info from the database
|
||||
*
|
||||
* @param out
|
||||
* @param info
|
||||
* @param key
|
||||
*/
|
||||
private void trypnt(PrintStream out, NamedDB info, String key) {
|
||||
rlock(info);
|
||||
try {
|
||||
out.print(" " + key + ": ");
|
||||
if (info.exists(key)) {
|
||||
out.print(info.get(key));
|
||||
} else {
|
||||
out.print("not_set");
|
||||
}
|
||||
} finally {
|
||||
runlock(info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print true or false if an object exists
|
||||
*
|
||||
* @param out
|
||||
* @param info
|
||||
* @param key
|
||||
*/
|
||||
private void tfpnt(PrintStream out, NamedDB info, String key) {
|
||||
rlock(info);
|
||||
try {
|
||||
out.print(" " + key + ": ");
|
||||
out.print(info.exists(key));
|
||||
} finally {
|
||||
runlock(info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an error message
|
||||
*
|
||||
* @param out
|
||||
*/
|
||||
private static void nns(PrintStream out) {
|
||||
out.println("ERROR no nickname has been set");
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump various information from the database
|
||||
*
|
||||
* @param out
|
||||
* @param info
|
||||
*/
|
||||
private void nickprint(PrintStream out, NamedDB info) {
|
||||
trypnt(out, info, P_NICKNAME);
|
||||
trypnt(out, info, P_STARTING);
|
||||
trypnt(out, info, P_RUNNING);
|
||||
trypnt(out, info, P_STOPPING);
|
||||
tfpnt(out, info, P_KEYS);
|
||||
trypnt(out, info, P_QUIET);
|
||||
trypnt(out, info, P_INPORT);
|
||||
trypnt(out, info, P_INHOST);
|
||||
trypnt(out, info, P_OUTPORT);
|
||||
trypnt(out, info, P_OUTHOST);
|
||||
out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump properties information from the database
|
||||
*
|
||||
* @param out
|
||||
* @param info
|
||||
*/
|
||||
private void propprint(PrintStream out, NamedDB info) {
|
||||
trypnt(out, info, P_PROPERTIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print information on a specific record, indicated by NamedDB
|
||||
* @param out
|
||||
* @param Arg
|
||||
*/
|
||||
private void ttlpnt(PrintStream out, String Arg) {
|
||||
database.getReadLock();
|
||||
try {
|
||||
if (database.exists(Arg)) {
|
||||
out.print("DATA");
|
||||
nickprint(out, (NamedDB) database.get(Arg));
|
||||
}
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is this NamedDB's tunnel active?
|
||||
*
|
||||
* @param Arg
|
||||
* @return true if the tunnel is active
|
||||
*/
|
||||
private boolean tunnelactive(NamedDB Arg) {
|
||||
boolean retval;
|
||||
rlock(Arg);
|
||||
try {
|
||||
retval = (Arg.get(P_STARTING).equals(Boolean.TRUE) ||
|
||||
Arg.get(P_STOPPING).equals(Boolean.TRUE) ||
|
||||
Arg.get(P_RUNNING).equals(Boolean.TRUE));
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the base64 information look OK
|
||||
*
|
||||
* @param data
|
||||
* @return OK
|
||||
*/
|
||||
private static boolean is64ok(String data) {
|
||||
try {
|
||||
new Destination(data);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The actual parser.
|
||||
* It probabbly needs a rewrite into functions, but I kind-of like inline code.
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
dk = ns = ip = op = false;
|
||||
try {
|
||||
try {
|
||||
// Get input from the client
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(server.getInputStream()));
|
||||
PrintStream out = new PrintStream(server.getOutputStream());
|
||||
quit:
|
||||
{
|
||||
die:
|
||||
{
|
||||
prikey = new ByteArrayOutputStream();
|
||||
out.println("BOB " + BOBversion);
|
||||
out.println("OK");
|
||||
while ((line = in.readLine()) != null) {
|
||||
StringTokenizer token = new StringTokenizer(line, " "); // use a space as a delimiter
|
||||
String Command = "";
|
||||
String Arg = "";
|
||||
NamedDB info;
|
||||
|
||||
if (token.countTokens() != 0) {
|
||||
Command = token.nextToken();
|
||||
Command =
|
||||
Command.toLowerCase(Locale.US);
|
||||
if (token.countTokens() != 0) {
|
||||
Arg = token.nextToken();
|
||||
} else {
|
||||
Arg = "";
|
||||
}
|
||||
// The rest of the tokens are considered junk,
|
||||
// and discarded without any warnings.
|
||||
if (Command.equals(C_help)) {
|
||||
for (int i = 0; !C_ALL[i][0].equals(" "); i++) {
|
||||
if (C_ALL[i][0].equalsIgnoreCase(Arg)) {
|
||||
out.println("OK " + C_ALL[i][1]);
|
||||
}
|
||||
}
|
||||
} else if (Command.equals(C_visit)) {
|
||||
visitAllThreads();
|
||||
out.println("OK ");
|
||||
} else if (Command.equals(C_lookup)) {
|
||||
Destination dest = null;
|
||||
String reply = null;
|
||||
if (Arg.endsWith(".i2p")) {
|
||||
try {
|
||||
//try {
|
||||
//dest = I2PTunnel.destFromName(Arg);
|
||||
//} catch (DataFormatException ex) {
|
||||
//}
|
||||
dest = I2PAppContext.getGlobalContext().namingService().lookup(Arg);
|
||||
if(dest != null) {
|
||||
reply = dest.toBase64();
|
||||
}
|
||||
} catch (NullPointerException npe) {
|
||||
// Could not find the destination!?
|
||||
}
|
||||
}
|
||||
if (reply == null) {
|
||||
out.println("ERROR Address Not found.");
|
||||
} else {
|
||||
out.println("OK " + reply);
|
||||
}
|
||||
} else if (Command.equals(C_getdest)) {
|
||||
if (ns) {
|
||||
if (dk) {
|
||||
rlock();
|
||||
try {
|
||||
out.println("OK " + nickinfo.get(P_DEST));
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
} else {
|
||||
out.println("ERROR keys not set.");
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_list)) {
|
||||
// Produce a formatted list of all nicknames
|
||||
database.getReadLock();
|
||||
try {
|
||||
for (Object ndb : database.values()) {
|
||||
try {
|
||||
info = (NamedDB) ndb;
|
||||
out.print("DATA");
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
}
|
||||
nickprint(out, info);
|
||||
}
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
out.println("OK Listing done");
|
||||
} else if (Command.equals(C_quit)) {
|
||||
// End the command session
|
||||
break quit;
|
||||
} else if (Command.equals(C_zap)) {
|
||||
// Kill BOB!! (let's hope this works!)
|
||||
LIVE.set(false);
|
||||
// End the command session
|
||||
break quit;
|
||||
} else if (Command.equals(C_newkeys)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
try {
|
||||
// Make a new PublicKey and PrivateKey
|
||||
prikey = new ByteArrayOutputStream();
|
||||
d = I2PClientFactory.createClient().createDestination(prikey);
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_KEYS, prikey.toByteArray());
|
||||
nickinfo.add(P_DEST, d.toBase64());
|
||||
out.println("OK " + nickinfo.get(P_DEST));
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
dk = true;
|
||||
} catch (I2PException ipe) {
|
||||
_log.error("Error generating keys", ipe);
|
||||
out.println("ERROR generating keys");
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_getkeys)) {
|
||||
// Return public key
|
||||
if (dk) {
|
||||
prikey = new ByteArrayOutputStream();
|
||||
rlock();
|
||||
try {
|
||||
prikey.write(((byte[]) nickinfo.get(P_KEYS)));
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
out.println("OK " + net.i2p.data.Base64.encode(prikey.toByteArray()));
|
||||
} else {
|
||||
out.println("ERROR no public key has been set");
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_quiet)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_QUIET, Boolean.valueOf(Arg));
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK Quiet set");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_verify)) {
|
||||
if (is64ok(Arg)) {
|
||||
out.println("OK");
|
||||
} else {
|
||||
out.println("ERROR not in BASE64 format");
|
||||
}
|
||||
} else if (Command.equals(C_setkeys)) {
|
||||
// Set the NamedDB to a privatekey in BASE64 format
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
try {
|
||||
prikey = new ByteArrayOutputStream();
|
||||
prikey.write(net.i2p.data.Base64.decode(Arg));
|
||||
d = new Destination();
|
||||
d.fromBase64(Arg);
|
||||
} catch (Exception ex) {
|
||||
Arg = "";
|
||||
}
|
||||
|
||||
if ((Arg.length() == 884) && is64ok(Arg)) {
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_KEYS, prikey.toByteArray());
|
||||
nickinfo.add(P_DEST, d.toBase64());
|
||||
out.println("OK " + nickinfo.get(P_DEST));
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
dk = true;
|
||||
} else {
|
||||
out.println("ERROR not in BASE64 format");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_setnick)) {
|
||||
ns = dk = ip = op = false;
|
||||
database.getReadLock();
|
||||
try {
|
||||
nickinfo = (NamedDB) database.get(Arg);
|
||||
if (!tunnelactive(nickinfo)) {
|
||||
nickinfo = null;
|
||||
ns = true;
|
||||
}
|
||||
|
||||
} catch (Exception b) {
|
||||
nickinfo = null;
|
||||
ns = true;
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
// Clears and Sets the initial NamedDB structure to work with
|
||||
if (ns) {
|
||||
nickinfo = new NamedDB();
|
||||
wlock();
|
||||
try {
|
||||
database.add(Arg, nickinfo);
|
||||
nickinfo.add(P_NICKNAME, Arg);
|
||||
nickinfo.add(P_STARTING, Boolean.FALSE);
|
||||
nickinfo.add(P_RUNNING, Boolean.FALSE);
|
||||
nickinfo.add(P_STOPPING, Boolean.FALSE);
|
||||
nickinfo.add(P_QUIET, Boolean.FALSE);
|
||||
nickinfo.add(P_INHOST, "localhost");
|
||||
nickinfo.add(P_OUTHOST, "localhost");
|
||||
Properties Q = new Properties();
|
||||
Lifted.copyProperties(this.props, Q);
|
||||
Q.setProperty("inbound.nickname", Arg);
|
||||
Q.setProperty("outbound.nickname", Arg);
|
||||
nickinfo.add(P_PROPERTIES, Q);
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK Nickname set to " + Arg);
|
||||
} else {
|
||||
out.println("ERROR tunnel is active");
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_option)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
StringTokenizer otoken = new StringTokenizer(Arg, "="); // use an equal sign as a delimiter
|
||||
if (otoken.countTokens() != 2) {
|
||||
out.println("ERROR too many or no options.");
|
||||
} else {
|
||||
String pname = otoken.nextToken();
|
||||
String pval = otoken.nextToken();
|
||||
wlock();
|
||||
try {
|
||||
Properties Q = (Properties) nickinfo.get(P_PROPERTIES);
|
||||
Q.setProperty(pname, pval);
|
||||
nickinfo.add(P_PROPERTIES, Q);
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK " + pname + " set to " + pval);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_getnick)) {
|
||||
// Get the NamedDB to work with...
|
||||
boolean nsfail = false;
|
||||
database.getReadLock();
|
||||
try {
|
||||
nickinfo = (NamedDB) database.get(Arg);
|
||||
ns = true;
|
||||
} catch (RuntimeException b) {
|
||||
nsfail = true;
|
||||
nns(out);
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
if (ns && !nsfail) {
|
||||
rlock();
|
||||
try {
|
||||
dk = nickinfo.exists(P_KEYS);
|
||||
ip = nickinfo.exists(P_INPORT);
|
||||
op = nickinfo.exists(P_OUTPORT);
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
// Finally say OK.
|
||||
out.println("OK Nickname set to " + Arg);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_inport)) {
|
||||
// Set the NamedDB inbound TO the router port
|
||||
// app --> BOB
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
int prt;
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.kill(P_INPORT);
|
||||
prt = Integer.parseInt(Arg);
|
||||
if (prt > 1 && prt < 65536) {
|
||||
try {
|
||||
nickinfo.add(P_INPORT, Integer.valueOf(prt));
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
}
|
||||
ip = nickinfo.exists(P_INPORT);
|
||||
} catch (NumberFormatException nfe) {
|
||||
out.println("ERROR not a number");
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
if (ip) {
|
||||
out.println("OK inbound port set");
|
||||
} else {
|
||||
out.println("ERROR port out of range");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_outport)) {
|
||||
// Set the NamedDB outbound FROM the router port
|
||||
// BOB --> app
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
int prt;
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.kill(P_OUTPORT);
|
||||
prt = Integer.parseInt(Arg);
|
||||
if (prt > 1 && prt < 65536) {
|
||||
nickinfo.add(P_OUTPORT, Integer.valueOf(prt));
|
||||
}
|
||||
ip = nickinfo.exists(P_OUTPORT);
|
||||
} catch (NumberFormatException nfe) {
|
||||
out.println("ERROR not a number");
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
if (ip) {
|
||||
out.println("OK outbound port set");
|
||||
} else {
|
||||
out.println("ERROR port out of range");
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_inhost)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_INHOST, Arg);
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK inhost set");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_outhost)) {
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_OUTHOST, Arg);
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK outhost set");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_show)) {
|
||||
// Get the current NamedDB properties
|
||||
if (ns) {
|
||||
out.print("OK");
|
||||
nickprint(out, nickinfo);
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_show_props)) {
|
||||
// Get the current options properties
|
||||
if (ns) {
|
||||
out.print("OK");
|
||||
propprint(out, nickinfo);
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_start)) {
|
||||
// Start the tunnel, if we have all the information
|
||||
if (ns && dk && (ip || op)) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
MUXlisten tunnel;
|
||||
try {
|
||||
while (!lock.compareAndSet(false, true)) {
|
||||
// wait
|
||||
}
|
||||
tunnel = new MUXlisten(lock, database, nickinfo, _log);
|
||||
Thread t = new I2PAppThread(tunnel);
|
||||
t.start();
|
||||
// try {
|
||||
// Thread.sleep(1000 * 10); // Slow down the startup.
|
||||
// } catch(InterruptedException ie) {
|
||||
// // ignore it
|
||||
// }
|
||||
out.println("OK tunnel starting");
|
||||
} catch (I2PException e) {
|
||||
lock.set(false);
|
||||
out.println("ERROR starting tunnel: " + e);
|
||||
} catch (IOException e) {
|
||||
lock.set(false);
|
||||
out.println("ERROR starting tunnel: " + e);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
|
||||
} else {
|
||||
out.println("ERROR tunnel settings incomplete");
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_stop)) {
|
||||
// Stop the tunnel, if it is running
|
||||
if (ns) {
|
||||
rlock();
|
||||
boolean released = false;
|
||||
try {
|
||||
if (nickinfo.get(P_RUNNING).equals(Boolean.TRUE) && nickinfo.get(P_STOPPING).equals(Boolean.FALSE) && nickinfo.get(P_STARTING).equals(Boolean.FALSE)) {
|
||||
runlock();
|
||||
released = true;
|
||||
wlock();
|
||||
try {
|
||||
nickinfo.add(P_STOPPING, Boolean.TRUE);
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
out.println("OK tunnel stopping");
|
||||
} else {
|
||||
out.println("ERROR tunnel is inactive");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
if (!released)
|
||||
runlock();
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_clear)) {
|
||||
// Clear use of the NamedDB if stopped
|
||||
if (ns) {
|
||||
try {
|
||||
if (tunnelactive(nickinfo)) {
|
||||
out.println("ERROR tunnel is active");
|
||||
} else {
|
||||
database.getWriteLock();
|
||||
try {
|
||||
database.kill((String) nickinfo.get(P_NICKNAME));
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
database.releaseWriteLock();
|
||||
}
|
||||
dk = ns = ip = op = false;
|
||||
out.println("OK cleared");
|
||||
}
|
||||
|
||||
} catch (Exception ex) {
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
|
||||
} else if (Command.equals(C_status)) {
|
||||
database.getReadLock();
|
||||
try {
|
||||
if (database.exists(Arg)) {
|
||||
// Show status of a NamedDB
|
||||
out.print("OK ");
|
||||
try {
|
||||
ttlpnt(out, Arg);
|
||||
} catch (Exception e) {
|
||||
out.println(); // this will cause an IOE if IOE
|
||||
break die;
|
||||
}
|
||||
} else {
|
||||
nns(out);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
database.releaseReadLock();
|
||||
}
|
||||
} else {
|
||||
out.println("ERROR UNKNOWN COMMAND! Try help");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} // die
|
||||
out.print("ERROR A really bad error just happened, ");
|
||||
} // quit
|
||||
// Say goodbye.
|
||||
|
||||
out.println("OK Bye!");
|
||||
|
||||
} catch (IOException ioe) {
|
||||
// not really needed, except to debug.
|
||||
// BOB.warn("IOException on socket listen: " + ioe);
|
||||
// ioe.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
server.close();
|
||||
} catch (IOException ex) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
// Debugging... None of this is normally used.
|
||||
|
||||
/**
|
||||
* Find the root thread group and print them all.
|
||||
*
|
||||
*/
|
||||
private void visitAllThreads() {
|
||||
ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
|
||||
while (root.getParent() != null) {
|
||||
root = root.getParent();
|
||||
}
|
||||
|
||||
// Visit each thread group
|
||||
visit(root, 0, root.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively visits all thread groups under `group' and dumps them.
|
||||
* @param group ThreadGroup to visit
|
||||
* @param level Current level
|
||||
*/
|
||||
private static void visit(ThreadGroup group, int level, String tn) {
|
||||
// Get threads in `group'
|
||||
int numThreads = group.activeCount();
|
||||
Thread[] threads = new Thread[numThreads * 2];
|
||||
numThreads = group.enumerate(threads, false);
|
||||
String indent = "------------------------------------".substring(0, level) + "-> ";
|
||||
// Enumerate each thread in `group' and print it.
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
// Get thread
|
||||
Thread thread = threads[i];
|
||||
System.out.println("BOB: " + indent + tn + ": " + thread.toString());
|
||||
}
|
||||
|
||||
// Get thread subgroups of `group'
|
||||
int numGroups = group.activeGroupCount();
|
||||
ThreadGroup[] groups = new ThreadGroup[numGroups * 2];
|
||||
numGroups = group.enumerate(groups, false);
|
||||
|
||||
// Recursively visit each subgroup
|
||||
for (int i = 0; i < numGroups; i++) {
|
||||
visit(groups[i], level + 1, groups[i].getName());
|
||||
}
|
||||
}
|
||||
}
|
99
apps/BOB/src/net/i2p/BOB/I2Plistener.java
Normal file
99
apps/BOB/src/net/i2p/BOB/I2Plistener.java
Normal file
@ -0,0 +1,99 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Listen on I2P and connect to TCP
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class I2Plistener implements Runnable {
|
||||
|
||||
private final NamedDB info, database;
|
||||
private final Logger _log;
|
||||
private final I2PServerSocket serverSocket;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param SS
|
||||
* @param S unused
|
||||
* @param info
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
I2Plistener(I2PServerSocket SS, I2PSocketManager S, NamedDB info, NamedDB database, Logger _log, AtomicBoolean lives) {
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
this.serverSocket = SS;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply listen on I2P port, and thread connections
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
boolean g = false;
|
||||
I2PSocket sessSocket = null;
|
||||
int conn = 0;
|
||||
try {
|
||||
try {
|
||||
serverSocket.setSoTimeout(50);
|
||||
|
||||
while (lives.get()) {
|
||||
try {
|
||||
sessSocket = serverSocket.accept();
|
||||
g = true;
|
||||
} catch (ConnectException ce) {
|
||||
g = false;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
}
|
||||
if (g) {
|
||||
g = false;
|
||||
conn++;
|
||||
// toss the connection to a new thread.
|
||||
I2PtoTCP conn_c = new I2PtoTCP(sessSocket, info, database, lives);
|
||||
Thread t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " I2PtoTCP " + conn);
|
||||
t.start();
|
||||
}
|
||||
|
||||
}
|
||||
} catch (I2PException e) {
|
||||
// bad stuff
|
||||
System.out.println("Exception " + e);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
serverSocket.close();
|
||||
} catch (I2PException ex) {
|
||||
}
|
||||
// System.out.println("I2Plistener: Close");
|
||||
}
|
||||
}
|
||||
}
|
169
apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
Normal file
169
apps/BOB/src/net/i2p/BOB/I2PtoTCP.java
Normal file
@ -0,0 +1,169 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Process I2P->TCP
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class I2PtoTCP implements Runnable {
|
||||
|
||||
private I2PSocket I2P;
|
||||
private final NamedDB info, database;
|
||||
private Socket sock;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param I2Psock
|
||||
* @param info
|
||||
* @param database
|
||||
*/
|
||||
I2PtoTCP(I2PSocket I2Psock, NamedDB info, NamedDB database, AtomicBoolean lives) {
|
||||
this.I2P = I2Psock;
|
||||
this.info = info;
|
||||
this.database = database;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
private void rlock() {
|
||||
database.getReadLock();
|
||||
info.getReadLock();
|
||||
}
|
||||
|
||||
private void runlock() {
|
||||
info.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* I2P stream to TCP stream thread starter
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
String host;
|
||||
int port;
|
||||
boolean tell;
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
InputStream Iin = null;
|
||||
OutputStream Iout = null;
|
||||
Thread t = null;
|
||||
Thread q = null;
|
||||
try {
|
||||
die:
|
||||
{
|
||||
try {
|
||||
rlock();
|
||||
try {
|
||||
host = info.get("OUTHOST").toString();
|
||||
port = Integer.parseInt(info.get("OUTPORT").toString());
|
||||
tell = info.get("QUIET").equals(Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
break die;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
sock = new Socket(host, port);
|
||||
sock.setKeepAlive(true);
|
||||
// make readers/writers
|
||||
in = sock.getInputStream();
|
||||
out = sock.getOutputStream();
|
||||
Iin = I2P.getInputStream();
|
||||
Iout = I2P.getOutputStream();
|
||||
I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
|
||||
|
||||
if (tell) {
|
||||
// tell who is connecting
|
||||
out.write(DataHelper.getASCII(I2P.getPeerDestination().toBase64()));
|
||||
out.write(10); // nl
|
||||
out.flush(); // not really needed, but...
|
||||
}
|
||||
// setup to cross the streams
|
||||
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
|
||||
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
|
||||
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
|
||||
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
|
||||
// Fire!
|
||||
t.start();
|
||||
q.start();
|
||||
while (t.isAlive() && q.isAlive() && lives.get()) { // AND is used here to kill off the other thread
|
||||
try {
|
||||
Thread.sleep(10); //sleep for 10 ms
|
||||
} catch (InterruptedException e) {
|
||||
break die;
|
||||
}
|
||||
}
|
||||
// System.out.println("I2PtoTCP: Going away...");
|
||||
} catch (Exception e) {
|
||||
// System.out.println("I2PtoTCP: Owch! damn!");
|
||||
break die;
|
||||
}
|
||||
} // die
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
try {
|
||||
Iin.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
try {
|
||||
Iout.close();
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
try {
|
||||
t.interrupt();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
q.interrupt();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
// System.out.println("I2PtoTCP: Close I2P");
|
||||
I2P.close();
|
||||
} catch (Exception e) {
|
||||
tell = false;
|
||||
}
|
||||
//System.out.println("I2PtoTCP: Closed I2P");
|
||||
try {
|
||||
// System.out.println("I2PtoTCP: Close sock");
|
||||
sock.close();
|
||||
} catch (Exception e) {
|
||||
tell = false;
|
||||
}
|
||||
// System.out.println("I2PtoTCP: Done");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
46
apps/BOB/src/net/i2p/BOB/Lifted.java
Normal file
46
apps/BOB/src/net/i2p/BOB/Lifted.java
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Sets of "friendly" utilities to make life easier.
|
||||
* Any "Lifted" code will apear here, and credits given.
|
||||
* It's better to "Lift" a small chunk of "free" code than add in piles of
|
||||
* code we don't need, and don't want.
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class Lifted {
|
||||
|
||||
/**
|
||||
* Copy a set of properties from one Property to another.
|
||||
* Lifted from Apache Derby code svn repository.
|
||||
* Liscenced as follows:
|
||||
* http://svn.apache.org/repos/asf/db/derby/code/trunk/LICENSE
|
||||
*
|
||||
* @param src_prop Source set of properties to copy from.
|
||||
* @param dest_prop Dest Properties to copy into.
|
||||
*
|
||||
**/
|
||||
public static void copyProperties(Properties src_prop, Properties dest_prop) {
|
||||
for (Map.Entry<Object, Object> e : src_prop.entrySet()) {
|
||||
dest_prop.put((String)e.getKey(), (String)e.getValue());
|
||||
}
|
||||
}
|
||||
}
|
44
apps/BOB/src/net/i2p/BOB/Logger.java
Normal file
44
apps/BOB/src/net/i2p/BOB/Logger.java
Normal file
@ -0,0 +1,44 @@
|
||||
package net.i2p.BOB;
|
||||
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class Logger {
|
||||
public Log log;
|
||||
private boolean logToStdout;
|
||||
|
||||
public Logger(Log log, boolean logToStdout) {
|
||||
this.log = log;
|
||||
this.logToStdout = logToStdout;
|
||||
}
|
||||
|
||||
public void info(String msg) {
|
||||
if (logToStdout)
|
||||
System.out.println("INFO: " + msg);
|
||||
if (log.shouldLog(Log.INFO))
|
||||
log.info(msg);
|
||||
}
|
||||
|
||||
public void warn(String msg) {
|
||||
warn(msg, null);
|
||||
}
|
||||
|
||||
public void warn(String msg, Throwable e) {
|
||||
if (logToStdout) {
|
||||
System.out.println("WARNING: " + msg);
|
||||
if (e != null)
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (log.shouldLog(Log.WARN))
|
||||
log.warn(msg, e);
|
||||
}
|
||||
|
||||
public void error(String msg, Throwable e) {
|
||||
if (logToStdout) {
|
||||
System.out.println("ERROR: " + msg);
|
||||
if (e != null)
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (log.shouldLog(Log.ERROR))
|
||||
log.error(msg, e);
|
||||
}
|
||||
}
|
416
apps/BOB/src/net/i2p/BOB/MUXlisten.java
Normal file
416
apps/BOB/src/net/i2p/BOB/MUXlisten.java
Normal file
@ -0,0 +1,416 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
*
|
||||
* Multiplex listeners for TCP and I2P
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class MUXlisten implements Runnable {
|
||||
|
||||
private final NamedDB database, info;
|
||||
private final Logger _log;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final ByteArrayInputStream prikey;
|
||||
private ThreadGroup tg;
|
||||
private final String N;
|
||||
private ServerSocket listener;
|
||||
private static final int backlog = 50; // should this be more? less?
|
||||
private final boolean go_out;
|
||||
private final boolean come_in;
|
||||
private final AtomicBoolean lock;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor Will fail if INPORT is occupied.
|
||||
*
|
||||
* @param info DB entry for this tunnel
|
||||
* @param database master database of tunnels
|
||||
* @param _log
|
||||
* @throws net.i2p.I2PException
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
MUXlisten(AtomicBoolean lock, NamedDB database, NamedDB info, Logger _log) throws I2PException, IOException, RuntimeException {
|
||||
int port = 0;
|
||||
InetAddress host = null;
|
||||
this.lock = lock;
|
||||
this.tg = null;
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
lives = new AtomicBoolean(false);
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.TRUE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
Properties Q = new Properties();
|
||||
rlock();
|
||||
try {
|
||||
N = this.info.get("NICKNAME").toString();
|
||||
prikey = new ByteArrayInputStream((byte[]) info.get("KEYS"));
|
||||
// Make a new copy so that anything else won't muck with our database.
|
||||
Properties R = (Properties) info.get("PROPERTIES");
|
||||
Lifted.copyProperties(R, Q);
|
||||
|
||||
this.go_out = info.exists("OUTPORT");
|
||||
this.come_in = info.exists("INPORT");
|
||||
if (this.come_in) {
|
||||
port = Integer.parseInt(info.get("INPORT").toString());
|
||||
host = InetAddress.getByName(info.get("INHOST").toString());
|
||||
}
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
|
||||
String i2cpHost = Q.getProperty(I2PClient.PROP_TCP_HOST, "127.0.0.1");
|
||||
int i2cpPort = I2PClient.DEFAULT_LISTEN_PORT;
|
||||
String i2cpPortStr = Q.getProperty(I2PClient.PROP_TCP_PORT);
|
||||
if (i2cpPortStr != null) {
|
||||
try {
|
||||
i2cpPort = Integer.parseInt(i2cpPortStr);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new IllegalArgumentException("Invalid I2CP port specified [" + i2cpPortStr + "]");
|
||||
}
|
||||
}
|
||||
|
||||
if (this.come_in) {
|
||||
this.listener = new ServerSocket(port, backlog, host);
|
||||
}
|
||||
socketManager = I2PSocketManagerFactory.createManager(
|
||||
prikey, i2cpHost, i2cpPort, Q);
|
||||
} catch (IOException e) {
|
||||
// Something went bad.
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
throw e;
|
||||
} catch (RuntimeException e) {
|
||||
// Something went bad.
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// Something else went bad.
|
||||
wlock();
|
||||
try {
|
||||
this.info.add("STARTING", Boolean.FALSE);
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void rlock() {
|
||||
database.getReadLock();
|
||||
info.getReadLock();
|
||||
}
|
||||
|
||||
private void runlock() {
|
||||
info.releaseReadLock();
|
||||
database.releaseReadLock();
|
||||
}
|
||||
|
||||
private void wlock() {
|
||||
database.getWriteLock();
|
||||
info.getWriteLock();
|
||||
}
|
||||
|
||||
private void wunlock() {
|
||||
info.releaseWriteLock();
|
||||
database.releaseWriteLock();
|
||||
}
|
||||
|
||||
/**
|
||||
* MUX sockets, fire off a thread to connect, get destination info, and do I/O
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
I2PServerSocket SS = null;
|
||||
Thread t = null;
|
||||
Thread q = null;
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
try {
|
||||
info.add("RUNNING", Boolean.TRUE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
lives.set(true);
|
||||
lock.set(false);
|
||||
quit:
|
||||
{
|
||||
try {
|
||||
tg = new ThreadGroup(N);
|
||||
{
|
||||
// toss the connections to a new threads.
|
||||
// will wrap with TCP and UDP when UDP works
|
||||
|
||||
if (go_out) {
|
||||
// I2P -> TCP
|
||||
SS = socketManager.getServerSocket();
|
||||
I2Plistener conn = new I2Plistener(SS, socketManager, info, database, _log, lives);
|
||||
t = new I2PAppThread(tg, conn, "BOBI2Plistener " + N);
|
||||
t.start();
|
||||
}
|
||||
|
||||
if (come_in) {
|
||||
// TCP -> I2P
|
||||
TCPlistener conn = new TCPlistener(listener, socketManager, info, database, _log, lives);
|
||||
q = new I2PAppThread(tg, conn, "BOBTCPlistener " + N);
|
||||
q.start();
|
||||
}
|
||||
|
||||
wlock();
|
||||
try {
|
||||
try {
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
boolean spin = true;
|
||||
while (spin && lives.get()) {
|
||||
try {
|
||||
Thread.sleep(1000); //sleep for 1 second
|
||||
} catch (InterruptedException e) {
|
||||
break quit;
|
||||
}
|
||||
rlock();
|
||||
try {
|
||||
try {
|
||||
spin = info.get("STOPPING").equals(Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
break quit;
|
||||
} finally {
|
||||
runlock();
|
||||
}
|
||||
}
|
||||
} // die
|
||||
|
||||
} catch (Exception e) {
|
||||
// System.out.println("MUXlisten: Caught an exception" + e);
|
||||
break quit;
|
||||
}
|
||||
} // quit
|
||||
} finally {
|
||||
lives.set(false);
|
||||
// Some grace time.
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
info.add("STOPPING", Boolean.TRUE);
|
||||
info.add("RUNNING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
// Start cleanup.
|
||||
while (!lock.compareAndSet(false, true)) {
|
||||
// wait
|
||||
}
|
||||
if (SS != null) {
|
||||
try {
|
||||
SS.close();
|
||||
} catch (I2PException ex) {
|
||||
}
|
||||
}
|
||||
if (listener != null) {
|
||||
try {
|
||||
listener.close();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
// Some grace time.
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
}
|
||||
|
||||
// Hopefully nuke stuff here...
|
||||
{
|
||||
String groupName = tg.getName();
|
||||
try {
|
||||
_log.warn("destroySocketManager " + groupName);
|
||||
socketManager.destroySocketManager();
|
||||
_log.warn("destroySocketManager Successful" + groupName);
|
||||
} catch (Exception e) {
|
||||
// nop
|
||||
_log.warn("destroySocketManager Failed" + groupName);
|
||||
_log.warn(e.toString());
|
||||
}
|
||||
}
|
||||
// zero out everything.
|
||||
try {
|
||||
wlock();
|
||||
try {
|
||||
info.add("STARTING", Boolean.FALSE);
|
||||
info.add("STOPPING", Boolean.FALSE);
|
||||
info.add("RUNNING", Boolean.FALSE);
|
||||
} catch (Exception e) {
|
||||
lock.set(false);
|
||||
return;
|
||||
} finally {
|
||||
wunlock();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
lock.set(false); // Should we force waiting for all threads??
|
||||
|
||||
// Wait around till all threads are collected.
|
||||
if (tg != null) {
|
||||
String groupName = tg.getName();
|
||||
// System.out.println("BOB: MUXlisten: Starting thread collection for: " + groupName);
|
||||
_log.warn("BOB: MUXlisten: Starting thread collection for: " + groupName);
|
||||
if (tg.activeCount() + tg.activeGroupCount() != 0) {
|
||||
// visit(tg, 0, groupName);
|
||||
int foo = tg.activeCount() + tg.activeGroupCount();
|
||||
// hopefully no longer needed!
|
||||
// int bar = lives;
|
||||
// System.out.println("BOB: MUXlisten: Waiting on threads for " + groupName);
|
||||
// System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + groupName);
|
||||
// visit(tg, 0, groupName);
|
||||
// System.out.println("BOB: MUXlisten: ThreadGroup dump END " + groupName + "\n");
|
||||
// Happily spin forever :-(
|
||||
while (foo != 0) {
|
||||
foo = tg.activeCount() + tg.activeGroupCount();
|
||||
// if (lives != bar && lives != 0) {
|
||||
// System.out.println("\nBOB: MUXlisten: ThreadGroup dump BEGIN " + groupName);
|
||||
// visit(tg, 0, groupName);
|
||||
// System.out.println("BOB: MUXlisten: ThreadGroup dump END " + groupName + "\n");
|
||||
// }
|
||||
// bar = lives;
|
||||
try {
|
||||
Thread.sleep(100); //sleep for 100 ms (One tenth second)
|
||||
} catch (InterruptedException ex) {
|
||||
// nop
|
||||
}
|
||||
}
|
||||
}
|
||||
// System.out.println("BOB: MUXlisten: Threads went away. Success: " + groupName);
|
||||
_log.warn("BOB: MUXlisten: Threads went away. Success: " + groupName);
|
||||
tg.destroy();
|
||||
// Zap reference to the ThreadGroup so the JVM can GC it.
|
||||
tg = null;
|
||||
}
|
||||
try {
|
||||
socketManager.destroySocketManager();
|
||||
} catch (Exception e) {
|
||||
// nop
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Debugging... None of this is normally used.
|
||||
/**
|
||||
* Find the root thread group and print them all.
|
||||
*
|
||||
*/
|
||||
private void visitAllThreads() {
|
||||
ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
|
||||
while (root.getParent() != null) {
|
||||
root = root.getParent();
|
||||
}
|
||||
|
||||
// Visit each thread group
|
||||
visit(root, 0, root.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively visits all thread groups under `group' and dumps them.
|
||||
* @param group ThreadGroup to visit
|
||||
* @param level Current level
|
||||
*/
|
||||
private static void visit(ThreadGroup group, int level, String tn) {
|
||||
// Get threads in `group'
|
||||
int numThreads = group.activeCount();
|
||||
Thread[] threads = new Thread[numThreads * 2];
|
||||
numThreads = group.enumerate(threads, false);
|
||||
String indent = "------------------------------------".substring(0, level) + "-> ";
|
||||
// Enumerate each thread in `group' and print it.
|
||||
for (int i = 0; i < numThreads; i++) {
|
||||
// Get thread
|
||||
Thread thread = threads[i];
|
||||
System.out.println("BOB: MUXlisten: " + tn + ": " + indent + thread.toString());
|
||||
}
|
||||
|
||||
// Get thread subgroups of `group'
|
||||
int numGroups = group.activeGroupCount();
|
||||
ThreadGroup[] groups = new ThreadGroup[numGroups * 2];
|
||||
numGroups = group.enumerate(groups, false);
|
||||
|
||||
// Recursively visit each subgroup
|
||||
for (int i = 0; i < numGroups; i++) {
|
||||
visit(groups[i], level + 1, groups[i].getName());
|
||||
}
|
||||
}
|
||||
}
|
39
apps/BOB/src/net/i2p/BOB/Main.java
Normal file
39
apps/BOB/src/net/i2p/BOB/Main.java
Normal file
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* Start from command line
|
||||
*
|
||||
* @author sponge
|
||||
*
|
||||
*/
|
||||
public class Main {
|
||||
|
||||
/**
|
||||
* @param args the command line arguments, these are not used yet
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
// THINK THINK THINK THINK THINK THINK
|
||||
SimpleTimer2 Y2 = SimpleTimer2.getInstance();
|
||||
|
||||
BOB.main(args);
|
||||
|
||||
Y2.stop();
|
||||
}
|
||||
}
|
106
apps/BOB/src/net/i2p/BOB/NamedDB.java
Normal file
106
apps/BOB/src/net/i2p/BOB/NamedDB.java
Normal file
@ -0,0 +1,106 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
|
||||
/**
|
||||
* Internal database to relate nicknames to options to values
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class NamedDB {
|
||||
|
||||
private final Map<String, Object> data;
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public NamedDB() {
|
||||
this.data = new HashMap<String, Object>();
|
||||
}
|
||||
|
||||
public void getReadLock() {
|
||||
lock.readLock().lock();
|
||||
}
|
||||
|
||||
public void releaseReadLock() {
|
||||
lock.readLock().unlock();
|
||||
}
|
||||
|
||||
public void getWriteLock() {
|
||||
lock.writeLock().lock();
|
||||
}
|
||||
|
||||
public void releaseWriteLock() {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete an object if it exists
|
||||
*
|
||||
* @param key
|
||||
*/
|
||||
public void kill(String key) {
|
||||
data.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add object, deletes the old one if it exists
|
||||
*
|
||||
* @param key
|
||||
* @param val
|
||||
*/
|
||||
public void add(String key, Object val) {
|
||||
data.put(key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object, and return it, throws RuntimeException if not found
|
||||
*
|
||||
* @param key non-null
|
||||
* @return Object non-null
|
||||
* @throws java.lang.RuntimeException if not found
|
||||
*/
|
||||
public Object get(String key) throws RuntimeException {
|
||||
Object rv = data.get(key);
|
||||
if (rv != null)
|
||||
return rv;
|
||||
throw new RuntimeException("Key not found");
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if an object exists, else returns false
|
||||
*
|
||||
* @param key
|
||||
* @return true if an object exists, else returns false
|
||||
*/
|
||||
public boolean exists(String key) {
|
||||
return data.containsKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.29 replaces getcount() and getnext(int)
|
||||
*/
|
||||
public Collection<Object> values() {
|
||||
return data.values();
|
||||
}
|
||||
}
|
121
apps/BOB/src/net/i2p/BOB/TCPio.java
Normal file
121
apps/BOB/src/net/i2p/BOB/TCPio.java
Normal file
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Shove data from one stream to the other.
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class TCPio implements Runnable {
|
||||
|
||||
private final InputStream Ain;
|
||||
private final OutputStream Aout;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Ain InputStream
|
||||
* @param Aout OutputStream
|
||||
*
|
||||
* param database
|
||||
*/
|
||||
TCPio(InputStream Ain, OutputStream Aout, AtomicBoolean lives) {
|
||||
this.Ain = Ain;
|
||||
this.Aout = Aout;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy from source to destination...
|
||||
* and yes, we are totally OK to block here on writes,
|
||||
* The OS has buffers, and I intend to use them.
|
||||
* We send an interrupt signal to the threadgroup to
|
||||
* unwedge any pending writes.
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
/*
|
||||
* NOTE:
|
||||
* The write method of OutputStream calls the write method of
|
||||
* one argument on each of the bytes to be written out.
|
||||
* Subclasses are encouraged to override this method and provide
|
||||
* a more efficient implementation.
|
||||
*
|
||||
* So, is this really a performance problem?
|
||||
* Should we expand to several bytes?
|
||||
* I don't believe there would be any gain, since read method
|
||||
* has the same reccomendations. If anyone has a better way to
|
||||
* do this, I'm interested in performance improvements.
|
||||
*
|
||||
* --Sponge
|
||||
*
|
||||
* Tested with 128 bytes, and there was no performance gain.
|
||||
* 8192 bytes did lower load average across many connections.
|
||||
* Should I raise it higer? The correct thing to do would be to
|
||||
* override... perhaps use NTCP, but I2P's streaming lib lacks
|
||||
* anything NTCP compatable.
|
||||
*
|
||||
* --Sponge
|
||||
*/
|
||||
|
||||
int b;
|
||||
byte a[] = new byte[8192];
|
||||
try {
|
||||
try {
|
||||
while (lives.get()) {
|
||||
b = Ain.read(a, 0, 8192);
|
||||
if (b > 0) {
|
||||
Aout.write(a, 0, b);
|
||||
} else if (b == 0) {
|
||||
while(Ain.available() == 0) {
|
||||
Thread.sleep(20);
|
||||
}
|
||||
} else {
|
||||
/* according to the specs:
|
||||
*
|
||||
* The total number of bytes read into the buffer,
|
||||
* or -1 if there is no more data because the end of
|
||||
* the stream has been reached.
|
||||
*
|
||||
*/
|
||||
// System.out.println("TCPio: End Of Stream");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
// System.out.println("TCPio: Leaving.");
|
||||
} finally {
|
||||
// Eject!!! Eject!!!
|
||||
//System.out.println("TCPio: Caught an exception " + e);
|
||||
try {
|
||||
Ain.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
try {
|
||||
Aout.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
95
apps/BOB/src/net/i2p/BOB/TCPlistener.java
Normal file
95
apps/BOB/src/net/i2p/BOB/TCPlistener.java
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
* Listen on TCP port and connect to I2P
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class TCPlistener implements Runnable {
|
||||
|
||||
private final NamedDB info, database;
|
||||
private final Logger _log;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final ServerSocket listener;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param S
|
||||
* @param info
|
||||
* @param database
|
||||
* @param _log
|
||||
*/
|
||||
TCPlistener(ServerSocket listener, I2PSocketManager S, NamedDB info, NamedDB database, Logger _log, AtomicBoolean lives) {
|
||||
this.database = database;
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
this.socketManager = S;
|
||||
this.listener = listener;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply listen on TCP port, and thread connections
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
boolean g = false;
|
||||
int conn = 0;
|
||||
Socket server = null;
|
||||
try {
|
||||
try {
|
||||
listener.setSoTimeout(50); // We don't block, we cycle and check.
|
||||
while (lives.get()) {
|
||||
try {
|
||||
server = listener.accept();
|
||||
server.setKeepAlive(true);
|
||||
g = true;
|
||||
} catch (SocketTimeoutException ste) {
|
||||
g = false;
|
||||
}
|
||||
if (g) {
|
||||
conn++;
|
||||
// toss the connection to a new thread.
|
||||
TCPtoI2P conn_c = new TCPtoI2P(socketManager, server, info, database, lives);
|
||||
Thread t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPtoI2P " + conn);
|
||||
t.start();
|
||||
g = false;
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
listener.close();
|
||||
} catch (IOException ex) {
|
||||
}
|
||||
//System.out.println("TCPlistener: " + Thread.currentThread().getName() + "Done.");
|
||||
}
|
||||
}
|
||||
}
|
222
apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
Normal file
222
apps/BOB/src/net/i2p/BOB/TCPtoI2P.java
Normal file
@ -0,0 +1,222 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.ConnectException;
|
||||
import java.net.NoRouteToHostException;
|
||||
import java.net.Socket;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
|
||||
/**
|
||||
*
|
||||
* Process TCP->I2P
|
||||
*
|
||||
* @author sponge
|
||||
*/
|
||||
public class TCPtoI2P implements Runnable {
|
||||
|
||||
private I2PSocket I2P;
|
||||
private final Socket sock;
|
||||
private final I2PSocketManager socketManager;
|
||||
private final AtomicBoolean lives;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param i2p
|
||||
* @param socket
|
||||
* @param info unused
|
||||
* @param database unused
|
||||
*/
|
||||
TCPtoI2P(I2PSocketManager i2p, Socket socket, NamedDB info, NamedDB database, AtomicBoolean lives) {
|
||||
this.sock = socket;
|
||||
this.socketManager = i2p;
|
||||
this.lives = lives;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a more forgiving readline,
|
||||
* it works on unbuffered streams
|
||||
*
|
||||
* @param in
|
||||
* @return line of text as a String
|
||||
* @throws IOException
|
||||
*/
|
||||
private static String lnRead(InputStream in) throws IOException {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int b;
|
||||
char c;
|
||||
|
||||
while (true) {
|
||||
b = in.read();
|
||||
if (b == 13) {
|
||||
//skip CR
|
||||
continue;
|
||||
}
|
||||
if (b < 20 || b > 126) {
|
||||
// exit on anything not legal
|
||||
break;
|
||||
}
|
||||
c = (char) (b & 0x7f); // We only care about ASCII
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Print an error message to out
|
||||
*
|
||||
* @param e
|
||||
* @param out
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private void Emsg(String e, OutputStream out) throws IOException {
|
||||
// Debugging System.out.println("ERROR TCPtoI2P: " + e);
|
||||
out.write("ERROR ".concat(e).getBytes("UTF-8"));
|
||||
out.write(13);
|
||||
out.write(10);
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* TCP stream to I2P stream thread starter
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
String line, input;
|
||||
InputStream Iin = null;
|
||||
OutputStream Iout = null;
|
||||
InputStream in = null;
|
||||
OutputStream out = null;
|
||||
Thread t = null;
|
||||
Thread q = null;
|
||||
try {
|
||||
try {
|
||||
|
||||
in = sock.getInputStream();
|
||||
out = sock.getOutputStream();
|
||||
line = lnRead(in);
|
||||
input = line.toLowerCase(Locale.US);
|
||||
Destination dest = null;
|
||||
if (input.endsWith(".i2p")) {
|
||||
//dest = I2PTunnel.destFromName(input);
|
||||
dest = I2PAppContext.getGlobalContext().namingService().lookup(input);
|
||||
if (dest != null) {
|
||||
line = dest.toBase64();
|
||||
} else {
|
||||
Emsg("Can't find destination: " + input, out);
|
||||
return;
|
||||
}
|
||||
}
|
||||
dest = new Destination();
|
||||
dest.fromBase64(line);
|
||||
|
||||
try {
|
||||
// get a client socket
|
||||
I2P = socketManager.connect(dest);
|
||||
I2P.setReadTimeout(0); // temp bugfix, this *SHOULD* be the default
|
||||
// make readers/writers
|
||||
Iin = I2P.getInputStream();
|
||||
Iout = I2P.getOutputStream();
|
||||
// setup to cross the streams
|
||||
TCPio conn_c = new TCPio(in, Iout, lives); // app -> I2P
|
||||
TCPio conn_a = new TCPio(Iin, out, lives); // I2P -> app
|
||||
t = new I2PAppThread(conn_c, Thread.currentThread().getName() + " TCPioA");
|
||||
q = new I2PAppThread(conn_a, Thread.currentThread().getName() + " TCPioB");
|
||||
// Fire!
|
||||
t.start();
|
||||
q.start();
|
||||
while (t.isAlive() && q.isAlive() && lives.get()) { // AND is used here to kill off the other thread
|
||||
Thread.sleep(10); //sleep for 10 ms
|
||||
}
|
||||
} catch (I2PException e) {
|
||||
Emsg(e.toString(), out);
|
||||
} catch (ConnectException e) {
|
||||
Emsg(e.toString(), out);
|
||||
} catch (NoRouteToHostException e) {
|
||||
Emsg(e.toString(), out);
|
||||
}
|
||||
|
||||
} catch (InterruptedIOException e) {
|
||||
// We're breaking away.
|
||||
} catch (InterruptedException e) {
|
||||
// ditto
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
Emsg(e.toString(), out);
|
||||
} catch (IOException ex) {
|
||||
// ditto
|
||||
}
|
||||
} catch (DataFormatException e) {
|
||||
try {
|
||||
Emsg(e.toString(), out);
|
||||
} catch (IOException ex) {
|
||||
// ditto
|
||||
}
|
||||
}
|
||||
|
||||
} finally {
|
||||
try {
|
||||
t.interrupt();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
q.interrupt();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
out.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
Iin.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
Iout.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
try {
|
||||
// System.out.println("TCPtoI2P: Close I2P");
|
||||
I2P.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
// System.out.println("TCPtoI2P: Close sock");
|
||||
sock.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
// System.out.println("TCPtoI2P: Done.");
|
||||
}
|
||||
}
|
140
apps/BOB/src/net/i2p/BOB/UDPIOthread.java
Normal file
140
apps/BOB/src/net/i2p/BOB/UDPIOthread.java
Normal file
@ -0,0 +1,140 @@
|
||||
/**
|
||||
* WTFPL
|
||||
* Version 2, December 2004
|
||||
*
|
||||
* Copyright (C) sponge
|
||||
* Planet Earth
|
||||
*
|
||||
* See...
|
||||
*
|
||||
* http://sam.zoy.org/wtfpl/
|
||||
* and
|
||||
* http://en.wikipedia.org/wiki/WTFPL
|
||||
*
|
||||
* ...for any additional details and license questions.
|
||||
*/
|
||||
package net.i2p.BOB;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.I2PSessionListener;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
/**
|
||||
* UDP IO on I2P
|
||||
*
|
||||
* FIX ME: Untested, and incomplete!
|
||||
* I have no personal need to UDP yet,
|
||||
* however alot of p2p apps pretty much demand it.
|
||||
* The skeletal frame is here, just needs to be finished.
|
||||
*
|
||||
* @author sponge
|
||||
* @deprecated incomplete, unused
|
||||
*/
|
||||
@Deprecated
|
||||
public class UDPIOthread implements I2PSessionListener, Runnable {
|
||||
|
||||
private final NamedDB info;
|
||||
private final Log _log;
|
||||
private final Socket socket;
|
||||
private DataInputStream in;
|
||||
private DataOutputStream out;
|
||||
private final I2PSession _session;
|
||||
// FIXME never set
|
||||
private Destination _peerDestination;
|
||||
private boolean up;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param info
|
||||
* @param _log
|
||||
* @param socket
|
||||
* @param _session
|
||||
*/
|
||||
UDPIOthread(NamedDB info, Log _log, Socket socket, I2PSession _session) {
|
||||
this.info = info;
|
||||
this._log = _log;
|
||||
this.socket = socket;
|
||||
this._session = _session;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public void run() {
|
||||
byte data[] = new byte[1024];
|
||||
up = true;
|
||||
try {
|
||||
in = new DataInputStream(socket.getInputStream());
|
||||
out = new DataOutputStream(socket.getOutputStream());
|
||||
while (up) {
|
||||
int c = in.read(data);
|
||||
// Note: could do a loopback test here with a wrapper.
|
||||
boolean ok = _session.sendMessage(_peerDestination, data, 0, c);
|
||||
|
||||
if (!ok) {
|
||||
up = false; // Is this the right thing to do??
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
_log.error("Error running", ioe);
|
||||
} catch (I2PSessionException ise) {
|
||||
_log.error("Error communicating", ise);
|
||||
// } catch(DataFormatException dfe) {
|
||||
// _log.error("Peer destination file is not valid", dfe);
|
||||
} finally {
|
||||
if (_session != null) {
|
||||
try {
|
||||
_session.destroySession();
|
||||
} catch (I2PSessionException ise) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param session
|
||||
* @param msgId
|
||||
* @param size
|
||||
*/
|
||||
public void messageAvailable(I2PSession session, int msgId, long size) {
|
||||
// _log.debug("Message available: id = " + msgId + " size = " + size);
|
||||
try {
|
||||
byte msg[] = session.receiveMessage(msgId);
|
||||
if (msg != null) {
|
||||
out.write(msg);
|
||||
out.flush();
|
||||
}
|
||||
} catch (I2PSessionException ise) {
|
||||
up = false;
|
||||
} catch (IOException ioe) {
|
||||
up = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Great, can these be used to kill ourselves.
|
||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of disconnect */
|
||||
public void disconnected(I2PSession session) {
|
||||
_log.debug("Disconnected");
|
||||
// up = false;
|
||||
}
|
||||
|
||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of error */
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
_log.debug("Error occurred: " + message, error);
|
||||
// up = false;
|
||||
}
|
||||
|
||||
/** required by {@link I2PSessionListener I2PSessionListener} to notify of abuse */
|
||||
public void reportAbuse(I2PSession session, int severity) {
|
||||
_log.debug("Abuse reported of severity " + severity);
|
||||
// up = false;
|
||||
}
|
||||
}
|
21
apps/BOB/src/net/i2p/BOB/license-WTFPL.txt
Normal file
21
apps/BOB/src/net/i2p/BOB/license-WTFPL.txt
Normal file
@ -0,0 +1,21 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
Version 2, December 2004
|
||||
|
||||
Copyright (C) sponge
|
||||
Planet Earth
|
||||
Everyone is permitted to copy and distribute verbatim or modified
|
||||
copies of this license document, and changing it is allowed as long
|
||||
as the name is changed.
|
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO.
|
||||
|
||||
See...
|
||||
|
||||
http://sam.zoy.org/wtfpl/
|
||||
and
|
||||
http://en.wikipedia.org/wiki/WTFPL
|
||||
|
||||
...for any additional details and license questions.
|
5
apps/BOB/src/net/i2p/BOB/package.html
Normal file
5
apps/BOB/src/net/i2p/BOB/package.html
Normal file
@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
<p>BOB, the Basic Open Bridge, allows TCP applications to talk over I2P - DEPRECATED - Please port applications to SAMv3.</p>
|
||||
</body>
|
||||
</html>
|
@ -9,7 +9,6 @@
|
||||
<property name="javac.compilerargs" value="" />
|
||||
<property name="javac.version" value="1.8" />
|
||||
<property name="javac.release" value="8" />
|
||||
<property name="manifest.classpath.name" value="Class-Path" />
|
||||
|
||||
<target name="all" depends="jar, emptyWar"/>
|
||||
|
||||
@ -58,14 +57,8 @@
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<jar basedir="${build}" destfile="${dist}/${jar}">
|
||||
<manifest>
|
||||
<attribute name="${manifest.classpath.name}" value="i2p.jar" />
|
||||
<attribute name="Main-Class" value="net.i2p.addressbook.CommandLine"/>
|
||||
<attribute name="Specification-Title" value="I2P Address Book" />
|
||||
<attribute name="Specification-Version" value="${api.version}" />
|
||||
<attribute name="Specification-Vendor" value="The I2P Project https://geti2p.net/" />
|
||||
<attribute name="Implementation-Title" value="I2P Java Address Book" />
|
||||
<attribute name="Main-Class" value="net.i2p.addressbook.Daemon"/>
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Implementation-Vendor" value="The I2P Project https://geti2p.net/" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
<attribute name="Base-Revision" value="${workspace.version}" />
|
||||
@ -84,36 +77,28 @@
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<!-- actually the jar -->
|
||||
<target name="warUpToDate">
|
||||
<uptodate property="war.uptodate" targetfile="${dist}/${jar}">
|
||||
<srcfiles dir= "." includes="${build}/**/*.class"/>
|
||||
<uptodate property="war.uptodate" targetfile="${dist}/${war}">
|
||||
<srcfiles dir= "." includes="${build}/**/*.class, web.xml"/>
|
||||
</uptodate>
|
||||
<condition property="shouldListChanges" >
|
||||
<and>
|
||||
<not>
|
||||
<isset property="war.uptodate" />
|
||||
</not>
|
||||
<isset property="git.available" />
|
||||
<isset property="mtn.available" />
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
|
||||
<target name="changes" depends="warUpToDate" if="shouldListChanges" >
|
||||
<exec executable="git" outputproperty="workspace.changes" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="status" />
|
||||
<arg value="-s" />
|
||||
<arg value="--porcelain" />
|
||||
<arg value="-uno" />
|
||||
<arg value="." />
|
||||
</exec>
|
||||
<!-- trim flags -->
|
||||
<exec executable="sed" inputstring="${workspace.changes}" outputproperty="workspace.changes.sed" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-e" />
|
||||
<arg value="s/^[MTADRCU ]*//" />
|
||||
</exec>
|
||||
<exec executable="mtn" outputproperty="workspace.changes" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="list" />
|
||||
<arg value="changed" />
|
||||
<arg value="." />
|
||||
</exec>
|
||||
<!-- \n in an attribute value generates an invalid manifest -->
|
||||
<exec executable="tr" inputstring="${workspace.changes.sed}" outputproperty="workspace.changes.tr" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<exec executable="tr" inputstring="${workspace.changes}" outputproperty="workspace.changes.tr" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-s" />
|
||||
<arg value="[:space:]" />
|
||||
<arg value="," />
|
||||
|
@ -1,42 +0,0 @@
|
||||
package net.i2p.addressbook;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.CoreVersion;
|
||||
|
||||
/**
|
||||
* Simple command line access to various utilities.
|
||||
* Not a public API. Subject to change.
|
||||
* Apps and plugins should use specific classes.
|
||||
*
|
||||
* @since 0.9.55
|
||||
*/
|
||||
public class CommandLine extends net.i2p.util.CommandLine {
|
||||
|
||||
protected static final List<String> ACLASSES = Arrays.asList(new String[] {
|
||||
"net.i2p.addressbook.HostTxtParser",
|
||||
"net.i2p.router.naming.BlockfileNamingService",
|
||||
"net.metanotion.io.block.BlockFile",
|
||||
});
|
||||
|
||||
protected CommandLine() {}
|
||||
|
||||
public static void main(String args[]) {
|
||||
List<String> classes = new ArrayList<String>(ACLASSES.size() + CLASSES.size());
|
||||
classes.addAll(ACLASSES);
|
||||
classes.addAll(CLASSES);
|
||||
if (args.length > 0) {
|
||||
exec(args, classes);
|
||||
}
|
||||
usage(classes);
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private static void usage(List<String> classes) {
|
||||
System.err.println("I2P Address book version " + CoreVersion.VERSION + '\n' +
|
||||
"USAGE: java -jar /path/to/addressbook.jar command [args]");
|
||||
printCommands(classes);
|
||||
}
|
||||
}
|
@ -697,7 +697,7 @@ class Daemon {
|
||||
StringBuilder buf = new StringBuilder(16);
|
||||
final int sz = dests.size();
|
||||
for (int i = 0; i < sz; i++) {
|
||||
buf.append(dests.get(i).toBase64(), 0, 6);
|
||||
buf.append(dests.get(i).toBase64().substring(0, 6));
|
||||
if (i != sz - 1)
|
||||
buf.append(", ");
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ import java.util.Properties;
|
||||
|
||||
import net.i2p.client.naming.HostTxtEntry;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SystemVersion;
|
||||
@ -26,9 +25,9 @@ import net.i2p.util.SystemVersion;
|
||||
* Utility class providing methods to parse and write files in a hosts.txt file
|
||||
* format, and subscription file format.
|
||||
*
|
||||
* @since 0.9.26 modified from ConfigParser, public since 0.9.55 for CLI
|
||||
* @since 0.9.26 modified from ConfigParser
|
||||
*/
|
||||
public class HostTxtParser {
|
||||
class HostTxtParser {
|
||||
|
||||
private static final boolean isWindows = SystemVersion.isWindows();
|
||||
|
||||
@ -256,23 +255,8 @@ public class HostTxtParser {
|
||||
System.exit(2);
|
||||
}
|
||||
if (!e.hasValidSig()) {
|
||||
if (!quiet) {
|
||||
System.err.println("Bad signature for " + e.getName());
|
||||
String dest = e.getDest();
|
||||
try {
|
||||
Destination d = new Destination(dest);
|
||||
System.err.println(dest);
|
||||
System.err.println(d.toString());
|
||||
} catch (Exception ex) {
|
||||
System.err.println("Invalid destination: " + dest);
|
||||
}
|
||||
Properties p = e.getProps();
|
||||
if (p != null) {
|
||||
for (Map.Entry<?,?> m : p.entrySet()) {
|
||||
System.err.println(m.getKey() + "=" + m.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!quiet)
|
||||
System.err.println("Bad signature");
|
||||
System.exit(3);
|
||||
}
|
||||
Properties p = e.getProps();
|
||||
@ -282,30 +266,14 @@ public class HostTxtParser {
|
||||
p.containsKey(HostTxtEntry.PROP_OLDNAME) ||
|
||||
p.containsKey(HostTxtEntry.PROP_OLDSIG)) {
|
||||
if (!e.hasValidSig()) {
|
||||
if (!quiet) {
|
||||
System.err.println("Bad inner signature for " + e.getName());
|
||||
for (Map.Entry<?,?> m : p.entrySet()) {
|
||||
System.err.println(m.getKey() + "=" + m.getValue());
|
||||
}
|
||||
}
|
||||
if (!quiet)
|
||||
System.err.println("Bad inner signature");
|
||||
System.exit(4);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!quiet) {
|
||||
if (!quiet)
|
||||
System.err.println("Good signature for " + e.getName());
|
||||
try {
|
||||
String dest = e.getDest();
|
||||
Destination d = new Destination(dest);
|
||||
System.err.println(dest);
|
||||
System.err.println(d.toString());
|
||||
} catch (Exception ex) {}
|
||||
if (p != null) {
|
||||
for (Map.Entry<?,?> m : p.entrySet()) {
|
||||
System.err.println(m.getKey() + "=" + m.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,6 @@
|
||||
<property name="javac.version" value="1.8" />
|
||||
<property name="javac.release" value="8" />
|
||||
<property name="require.gettext" value="true" />
|
||||
<property name="manifest.classpath.name" value="Class-Path" />
|
||||
|
||||
<condition property="no.bundle">
|
||||
<isfalse value="${require.gettext}" />
|
||||
@ -64,24 +63,17 @@
|
||||
</target>
|
||||
|
||||
<target name="listChangedFiles" depends="jarUpToDate" if="shouldListChanges" >
|
||||
<exec executable="git" outputproperty="workspace.changes" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="status" />
|
||||
<arg value="-s" />
|
||||
<arg value="--porcelain" />
|
||||
<arg value="-uno" />
|
||||
<arg value="." />
|
||||
</exec>
|
||||
<!-- trim flags -->
|
||||
<exec executable="sed" inputstring="${workspace.changes}" outputproperty="workspace.changes.sed" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-e" />
|
||||
<arg value="s/^[MTADRCU ]*//" />
|
||||
</exec>
|
||||
<!-- \n in an attribute value generates an invalid manifest -->
|
||||
<exec executable="tr" inputstring="${workspace.changes.sed}" outputproperty="workspace.changes.tr" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-s" />
|
||||
<arg value="[:space:]" />
|
||||
<arg value="," />
|
||||
</exec>
|
||||
<exec executable="mtn" outputproperty="workspace.changes" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="list" />
|
||||
<arg value="changed" />
|
||||
<arg value="." />
|
||||
</exec>
|
||||
<!-- \n in an attribute value generates an invalid manifest -->
|
||||
<exec executable="tr" inputstring="${workspace.changes}" outputproperty="workspace.changes.tr" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-s" />
|
||||
<arg value="[:space:]" />
|
||||
<arg value="," />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile, bundle, listChangedFiles" unless="jar.uptodate" >
|
||||
@ -94,7 +86,6 @@
|
||||
<jar basedir="${build}" excludes="messages-src/**" destfile="${dist}/${jar}">
|
||||
<manifest>
|
||||
<attribute name="Main-Class" value="net.i2p.desktopgui.Main"/>
|
||||
<attribute name="${manifest.classpath.name}" value="i2p.jar router.jar" />
|
||||
<attribute name="Implementation-Version" value="${full.version}" />
|
||||
<attribute name="Built-By" value="${build.built-by}" />
|
||||
<attribute name="Build-Date" value="${build.timestamp}" />
|
||||
@ -115,7 +106,7 @@
|
||||
<not>
|
||||
<isset property="jar.uptodate" />
|
||||
</not>
|
||||
<isset property="git.available" />
|
||||
<isset property="mtn.available" />
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
|
@ -10,9 +10,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-09 19:23+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-09-20 20:31+0000\n"
|
||||
"Last-Translator: Vitaly Zdorovenko <stenliterziev@gmail.com>\n"
|
||||
"Language-Team: Bulgarian (http://www.transifex.com/otf/I2P/language/bg/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -27,81 +27,69 @@ msgstr "Стартиране на I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P е стартиран!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Стартиране"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Стартиране на I2P Браузер"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr ""
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Деактивиране"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Рестартиране на I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Спиране на I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Рестартирайте Незабавно"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Изключване в {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Мрежа"
|
||||
msgstr ""
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
|
@ -4,15 +4,14 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# slrslr, 2022
|
||||
# slrslr, 2021
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: slrslr, 2022\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2021-07-08 09:41+0000\n"
|
||||
"Last-Translator: slrslr\n"
|
||||
"Language-Team: Czech (http://www.transifex.com/otf/I2P/language/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -27,81 +26,69 @@ msgstr "Spustit I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P startuje!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Startuji"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Spouštím I2P Browser"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Nastavit I2P Systémovou Lištu"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Zapnout upozornění"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Zakázat"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Vypnout upozornění"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Vypnout ikonu systémové lišty"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Restart I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Zastavit I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Restartovat I2P Hned"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Zastavit I2P Hned"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Zrušit I2P Zastavení"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Zastavení za {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Vypínání"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Síť"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Pravé-kliknutí pro menu"
|
||||
|
@ -7,15 +7,14 @@
|
||||
# blabla <blabla@trash-mail.com>, 2011
|
||||
# Ettore Atalan <atalanttore@googlemail.com>, 2016
|
||||
# foo <foo@bar>, 2009
|
||||
# Georg Stadler, 2022
|
||||
# Lars Schimmer <echelon@i2pmail.org>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: Georg Stadler, 2022\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Lars Schimmer <echelon@i2pmail.org>\n"
|
||||
"Language-Team: German (http://www.transifex.com/otf/I2P/language/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -30,81 +29,69 @@ msgstr "I2P starten"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P startet gerade!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Startend"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P-Browser öffnen"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "I2P System Tray konfigurieren"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Benachrichtigungen aktivieren"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Deaktivieren"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Benachrichtigungen deaktivieren"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P neustarten"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P beenden"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "I2P sofort neustarten"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "I2P sofort beenden"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Herunterfahren von I2P abbrechen"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Herunterfahren in {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Herunterfahren bevorstehend"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Netzwerk"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Rechtsklick für Menü"
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P desktopgui\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-03-06 14:52+0000\n"
|
||||
"POT-Creation-Date: 2021-08-11 15:33+0000\n"
|
||||
"PO-Revision-Date: 2010-06-15 14:09+0100\n"
|
||||
"Last-Translator: duck <duck@mail.i2p>\n"
|
||||
"Language-Team: duck <duck@mail.i2p>\n"
|
||||
@ -19,84 +19,75 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "Starting"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:221
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:54
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:206
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:242
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:75
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:227
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:88
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:244
|
||||
msgid "Disable system tray"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Disable"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:104
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:260
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:92
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:244
|
||||
msgid "Restart I2P"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:121
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:277
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:109
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:261
|
||||
msgid "Stop I2P"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:137
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:293
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:125
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:277
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:154
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:310
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:142
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:294
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:168
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:324
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:156
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:308
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:381
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:362
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:383
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:364
|
||||
msgid "Shutdown imminent"
|
||||
msgstr ""
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:388
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:369
|
||||
msgid "Network"
|
||||
msgstr ""
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:75
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:310
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:347
|
||||
msgid "Enable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:325
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:362
|
||||
msgid "Disable notifications"
|
||||
msgstr ""
|
||||
|
@ -6,22 +6,21 @@
|
||||
# Translators:
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# foo <foo@bar>, 2009
|
||||
# Juan Jaramillo <juanda097@protonmail.ch>, 2022
|
||||
# punkibastardo <transifex.symons@slmail.me>, 2011
|
||||
# punkibastardo <punkibastardo@gmail.com>, 2011
|
||||
# strel, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: Juan Jaramillo <juanda097@protonmail.ch>, 2022\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: strel\n"
|
||||
"Language-Team: Spanish (http://www.transifex.com/otf/I2P/language/es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
@ -30,81 +29,69 @@ msgstr "Iniciar I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P está iniciando!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Iniciando"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Lanzar navegador I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Configurar la bandeja de sistema de I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Activar las notificaciones"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Deshabilitar"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Desactivar las notificaciones"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Desactivar la bandeja del sistema"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Reiniciar I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Detener I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Reiniciar I2P inmediatamente"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Detener I2P inmediatamente"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Cancelar el cierre de I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Cierre en {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Cierre inminente"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Red"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Clic secundario para menú"
|
||||
|
@ -9,15 +9,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: kaze kaze <kaze@rlab.be>, 2017\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-11-16 15:29+0000\n"
|
||||
"Last-Translator: kaze kaze <kaze@rlab.be>\n"
|
||||
"Language-Team: Spanish (Argentina) (http://www.transifex.com/otf/I2P/language/es_AR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es_AR\n"
|
||||
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
@ -26,81 +26,69 @@ msgstr "Iniciando I2P..."
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P esta iniciando!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Iniciando"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Lanzar el Navegador I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Configurar la Bandeja de Sistema de I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Activar las notificaciones"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Deshabilitar"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Desactivar las notificaciones"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Desactivar la bandeja del sistema"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Riniciar I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Detener I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr " Reiniciar I2P inmediatamente "
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr " Detener I2P inmediatamente "
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Cancelar el Apagado de I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Apagar en {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Apagado inminente"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Red"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P Clic derecho para el menú"
|
||||
|
@ -13,20 +13,19 @@
|
||||
# AO <ao@localizationlab.org>, 2017
|
||||
# Boxoa590, 2013
|
||||
# Towinet, 2016
|
||||
# vex vex, 2022
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: vex vex, 2022\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2019-12-03 16:10+0000\n"
|
||||
"Last-Translator: AO <ao@localizationlab.org>\n"
|
||||
"Language-Team: French (http://www.transifex.com/otf/I2P/language/fr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
@ -35,81 +34,69 @@ msgstr "Démarrer I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P démarre !"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Démarrage"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Lancer le navigateur d’I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Configurer la zone de notification d’I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Activer les notifications"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Désactiver"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Désactiver les notifications"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Désactiver la zone de notification"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Redémarrer I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Arrêter I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Redémarrer I2P immédiatement"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Arrêter I2P immédiatement"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Annuler la fermeture d’I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Fermeture dans {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "La fermeture est imminente"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Réseau"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P : clic droit pour obtenir le menu"
|
||||
|
@ -5,14 +5,13 @@
|
||||
#
|
||||
# Translators:
|
||||
# Hunor Paksy <heds@cock.li>, 2018
|
||||
# vargaviktor <viktor.varga@gmail.com>, 2022
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: vargaviktor <viktor.varga@gmail.com>, 2022\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2021-06-20 09:44+0000\n"
|
||||
"Last-Translator: AdminLMH <lehetmashogy@i2pmail.org>\n"
|
||||
"Language-Team: Hungarian (http://www.transifex.com/otf/I2P/language/hu/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -27,81 +26,69 @@ msgstr "I2P indítása"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P indul!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "indítás"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P Böngésző Indítása"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "I2P rendszertálca beállítások"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Értesítések engedélyezése"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Kikapcsol"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Értesítések tiltása"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Tálcaikon letiltása"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P Újraindítása"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P Leállítása"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "I2P Azonnali újraindítása"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "I2P Azonnali megállítása"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "I2P leállításának visszavonása"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Kikapcsolás: {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Kikapcsolás hamarosan"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Hálózat"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Jobb-klikk a menüért"
|
||||
|
@ -4,16 +4,15 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# daingewuvzeevisiddfddd, 2022
|
||||
# タカハシ, 2013
|
||||
# タカハシ <indexial@outlook.jp>, 2013
|
||||
# Masayuki Hatta <mhatta@mhatta.org>, 2018
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-13 03:11+0000\n"
|
||||
"Last-Translator: daingewuvzeevisiddfddd\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2018-08-17 22:08+0000\n"
|
||||
"Last-Translator: Masayuki Hatta <mhatta@mhatta.org>\n"
|
||||
"Language-Team: Japanese (http://www.transifex.com/otf/I2P/language/ja/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -28,81 +27,69 @@ msgstr "I2P を開始"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P を起動中!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "起動中"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P ブラウザを起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "I2P システムトレイを設定"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "通知を有効化"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "無効"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "通知を無効化"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "システムトレイを無効化"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P を再起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P を停止"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "すぐに I2P を再起動"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "すぐに I2P を停止"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "I2P のシャットダウンを中止"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "{0} でシャットダウン"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "即時シャットダウン"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "ネットワーク"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: 右クリックでメニュー"
|
||||
|
@ -6,15 +6,14 @@
|
||||
# Translators:
|
||||
# PolishAnon <b790979@klzlk.com>, 2011
|
||||
# polacco <polacco@i2pmail.org>, 2015
|
||||
# ☆Verdulo, 2016
|
||||
# Waldemar Napora, 2022
|
||||
# Verdulo :-), 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: Waldemar Napora, 2022\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Verdulo :-)\n"
|
||||
"Language-Team: Polish (http://www.transifex.com/otf/I2P/language/pl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -29,81 +28,69 @@ msgstr "Uruchom I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "Uruchamianie I2P!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Uruchamianie"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Uruchom przeglądarkę I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Konfiguruj I2P w zasobniku systemowym"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Włącz powiadomenia"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Wyłącz"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Wyłącz powiadomienia"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Wyłącz zasobnik systemowy"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Zrestartuj I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Zatrzymaj I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Zrestartuj I2P natychmiast"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Wyłącz I2P natychmiast"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Anuluj zamykanie I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Wyłączenie za {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Zaraz zamknę"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Sieć"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: kliknij prawym aby otworzyć menu"
|
||||
|
@ -4,22 +4,22 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Manuela Silva <mmsrs@sky.com>, 2016
|
||||
# 1c13465e24d91aca4d3ddaa1bc3e7027_ae6ba28, 2013
|
||||
# 1c13465e24d91aca4d3ddaa1bc3e7027_ae6ba28, 2012
|
||||
# Manuela Silva <manuela.silva@sky.com>, 2016
|
||||
# wicked, 2013
|
||||
# wicked, 2012
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: Manuela Silva <mmsrs@sky.com>, 2016\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Manuela Silva <manuela.silva@sky.com>\n"
|
||||
"Language-Team: Portuguese (http://www.transifex.com/otf/I2P/language/pt/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
@ -28,81 +28,69 @@ msgstr "Iniciar I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P está a iniciar!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "A Iniciar"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Iniciar o browser I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Configurar Bandeja do Sistema do I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Ativar notificações"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Desativar"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Desativar notificações"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Reiniciar o I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Parar o I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Reiniciar o I2P imediatamente"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Parar o I2P de imediatamente"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Cancelar Encerramento do I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Encerramento em {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Encerramento eminente"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Rede"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Clique direito para menu"
|
||||
|
@ -5,106 +5,93 @@
|
||||
#
|
||||
# Translators:
|
||||
# testsubject67 <deborinha97@hotmail.com>, 2014
|
||||
# Kyrie Eleis, 2022
|
||||
# L., 2013
|
||||
# L., 2015
|
||||
# blueboy, 2013
|
||||
# blueboy, 2015
|
||||
# Rafael Ferrari, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: Kyrie Eleis, 2022\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Rafael Ferrari\n"
|
||||
"Language-Team: Portuguese (Brazil) (http://www.transifex.com/otf/I2P/language/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt_BR\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr "Iniciar I2P"
|
||||
msgstr "Conectar-se à I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "Conectando-se a I2P!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Conectando"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Lançar o navegador I2P "
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Configurar o ícone de sistema I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Ativar notificações"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Desabilitar"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Desativar notificações"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Reinicializar o roteador I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Interromper o roteador I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Reinicializar o I2P Imediatamente"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Parar o I2P Imediatamente"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Cancelar o desligamento do I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Desligando em {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Desligando agora"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Rede"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Clique com o botão direito para o menu"
|
||||
|
@ -9,15 +9,14 @@
|
||||
# foo <foo@bar>, 2009
|
||||
# Foster Snowhill, 2013
|
||||
# brianhopes <voganc-12@live.ru>, 2015
|
||||
# Артём Котлубай <artemkotlubai@yandex.ru>, 2023
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-03-06 14:52+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: Артём Котлубай <artemkotlubai@yandex.ru>, 2023\n"
|
||||
"Language-Team: Russian (Russia) (http://app.transifex.com/otf/I2P/language/ru_RU/)\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: c4sp3r\n"
|
||||
"Language-Team: Russian (Russia) (http://www.transifex.com/otf/I2P/language/ru_RU/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@ -25,84 +24,75 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr "Запустить I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P запускается!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
msgid "Starting"
|
||||
msgstr "Запускается"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:221
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Запустить браузер I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:242
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Настроить область уведомлений"
|
||||
msgstr "Настроить системный трей I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:88
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:244
|
||||
msgid "Disable system tray"
|
||||
msgstr "Убрать значок из области уведомлений"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Отключить"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:104
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:260
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Перезапустить I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:121
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:277
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Остановить I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:137
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:293
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Перезапустить I2P немедленно"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:154
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:310
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Остановить I2P немедленно"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:168
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:324
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Отменить выключение I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:381
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Выключение через {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:383
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Неотменяемое выключение"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:388
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Сеть"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:75
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: ПКМ для вызова меню"
|
||||
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:310
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:347
|
||||
msgid "Enable notifications"
|
||||
msgstr "Включить уведомления"
|
||||
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:325
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:362
|
||||
msgid "Disable notifications"
|
||||
msgstr "Отключить уведомления"
|
||||
msgstr "I2P: Правый щелчок для вызова меню"
|
||||
|
@ -1,106 +0,0 @@
|
||||
# I2P
|
||||
# Copyright (C) 2009 The I2P Project
|
||||
# This file is distributed under the same license as the desktopgui package.
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Žan Šadl-Ferš, 2021
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-09 19:23+0000\n"
|
||||
"Last-Translator: zzzi2p\n"
|
||||
"Language-Team: Slovenian (http://www.transifex.com/otf/I2P/language/sl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: sl\n"
|
||||
"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
msgid "Start I2P"
|
||||
msgstr "Zaženi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P se zaganja!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Zaganja"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Zaženi I2P brskalnik"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Konfiguriraj I2P opravilno vrstico"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr ""
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
msgid "Restart I2P"
|
||||
msgstr "Ponovno zaženi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
msgid "Stop I2P"
|
||||
msgstr "Ustavi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Nemudoma ponovno zaženi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Nemudoma ustavi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Prekliči zaustavitev od I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Zaustavi v {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Zaustavitev je neizbežna"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
msgid "Network"
|
||||
msgstr "Omrežje"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Pritisnite na desno tipko miške za meni"
|
@ -4,16 +4,15 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Besnik Bleta <besnik@programeshqip.org>, 2022
|
||||
# Besnik Bleta <besnik@programeshqip.org>, 2016,2019
|
||||
# Besnik <besnik@programeshqip.org>, 2016,2019
|
||||
# Shpetim <shpetim@privacysolutions.no>, 2014
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-10 10:34+0000\n"
|
||||
"Last-Translator: Besnik Bleta <besnik@programeshqip.org>\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2019-01-10 14:28+0000\n"
|
||||
"Last-Translator: Besnik <besnik@programeshqip.org>\n"
|
||||
"Language-Team: Albanian (http://www.transifex.com/otf/I2P/language/sq/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -28,81 +27,69 @@ msgstr "Nise I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P po niset!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Po niset"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "Nis Shfletuesin I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "Formësoni Panel Sistemi I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Aktivizoni njoftimet"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Çaktivizoje"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Çaktivizoni njoftimet"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Çaktivizo panel sistemi"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "Rinise I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "Ndale I2P-në"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "Rinise I2P-në Menjëherë"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "Ndale I2P-në Menjëherë"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "Anuloje Mbylljen e I2P-së"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "Mbylle për {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Mbyllje shumë shpejt"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Rrjet"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Djathtasklikoni për menu"
|
||||
|
@ -4,20 +4,20 @@
|
||||
# To contribute translations, see http://www.i2p2.de/newdevelopers
|
||||
#
|
||||
# Translators:
|
||||
# Kaya Zeren <kayazeren@gmail.com>, 2013,2016,2022
|
||||
# Kaya Zeren <kayazeren@gmail.com>, 2013,2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-10 04:40+0000\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2017-06-30 21:32+0000\n"
|
||||
"Last-Translator: Kaya Zeren <kayazeren@gmail.com>\n"
|
||||
"Language-Team: Turkish (Turkey) (http://www.transifex.com/otf/I2P/language/tr_TR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: tr_TR\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:31
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:59
|
||||
@ -26,81 +26,69 @@ msgstr "I2P başlasın"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P başlatılıyor!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "Başlatılıyor"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "I2P tarayıcısını aç"
|
||||
msgstr "I2P Tarayıcısını Açın"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "I2P sistem tepsisi ayarları"
|
||||
msgstr "I2P Sistem Tepsisi Ayarları"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "Bildirimleri etkinleştir"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "Devre Dışı"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "Bildirimleri devre dışı bırak"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "Sistem tepsisini devre dışı bırak"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "I2P yeniden başlasın"
|
||||
msgstr "I2P Yeniden Başlasın"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "I2P durdurulsun"
|
||||
msgstr "I2P Durdurulsun"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "I2P hemen yeniden başlatılsın"
|
||||
msgstr "I2P Hemen Yeniden Başlatılsın"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "I2P hemen durdurulsun"
|
||||
msgstr "I2P Hemen Durdurulsun"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "I2P kapatmayı iptal et"
|
||||
msgstr "I2P Kapatmayı İptal Et"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "{0} içinde kapat"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "Kapatılmak üzere"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "Ağ"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P: Menüde sağ tık"
|
||||
|
@ -5,15 +5,15 @@
|
||||
#
|
||||
# Translators:
|
||||
# ducki2p <ducki2p@gmail.com>, 2011
|
||||
# Scott Rhodes <starring169@gmail.com>, 2021-2022
|
||||
# Scott Rhodes <starring169@gmail.com>, 2021
|
||||
# walking <walking@i2pmail.org>, 2011
|
||||
# YFdyh000 <yfdyh000@gmail.com>, 2016
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2022-02-16 16:05+0000\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2021-03-07 07:58+0000\n"
|
||||
"Last-Translator: Scott Rhodes <starring169@gmail.com>\n"
|
||||
"Language-Team: Chinese (China) (http://www.transifex.com/otf/I2P/language/zh_CN/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@ -29,81 +29,69 @@ msgstr "启动 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr " I2P 正在启动!"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "正在启动"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "启动 I2P 浏览器"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "配置 I2P 系统托盘"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "启用通知"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "禁用"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "禁用通知"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "禁用系统托盘"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "重启 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "停止 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "立即重启 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "立即停止 I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "取消 I2P 关闭"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "{0} 后关闭"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "立即关闭"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "网络"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P:右击获得菜单"
|
||||
|
@ -9,9 +9,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: I2P\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-09 19:13+0000\n"
|
||||
"PO-Revision-Date: 2011-02-13 12:05+0000\n"
|
||||
"Last-Translator: 黃彥儒 <r1235613@gmail.com>, 2017\n"
|
||||
"POT-Creation-Date: 2016-05-25 12:29+0000\n"
|
||||
"PO-Revision-Date: 2018-10-04 00:43+0000\n"
|
||||
"Last-Translator: erinm\n"
|
||||
"Language-Team: Chinese (Taiwan) (http://www.transifex.com/otf/I2P/language/zh_TW/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@ -26,81 +26,69 @@ msgstr "啟動I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "I2P is starting!"
|
||||
msgstr "I2P已啟動"
|
||||
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:44
|
||||
#: src/net/i2p/desktopgui/ExternalTrayManager.java:72
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
msgid "Starting"
|
||||
msgstr "啟動中"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:65
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:249
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:55
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:207
|
||||
msgid "Launch I2P Browser"
|
||||
msgstr "開啟I2P瀏覽器"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:86
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:270
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:76
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:228
|
||||
msgid "Configure I2P System Tray"
|
||||
msgstr "設定I2P系統文件夾"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:87
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:271
|
||||
msgid "Enable notifications"
|
||||
msgstr "启用通知"
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:77
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:229
|
||||
msgid "Disable"
|
||||
msgstr "停用"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:101
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:285
|
||||
msgid "Disable notifications"
|
||||
msgstr "禁用通知"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:115
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:299
|
||||
msgid "Disable system tray"
|
||||
msgstr "禁用系统托盘"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:131
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:315
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:93
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:245
|
||||
msgid "Restart I2P"
|
||||
msgstr "重啟I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:148
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:332
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:110
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:262
|
||||
msgid "Stop I2P"
|
||||
msgstr "停止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:164
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:348
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:126
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:278
|
||||
msgid "Restart I2P Immediately"
|
||||
msgstr "強制重啟I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:181
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:143
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:295
|
||||
msgid "Stop I2P Immediately"
|
||||
msgstr "強制終止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:195
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:379
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:157
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:309
|
||||
msgid "Cancel I2P Shutdown"
|
||||
msgstr "取消停止I2P"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:437
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:363
|
||||
#, java-format
|
||||
msgid "Shutdown in {0}"
|
||||
msgstr "關閉於 {0}"
|
||||
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:439
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:365
|
||||
msgid "Shutdown imminent"
|
||||
msgstr "強制關閉"
|
||||
|
||||
#. status translations are in the console bundle
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:444
|
||||
#: src/net/i2p/desktopgui/InternalTrayManager.java:370
|
||||
msgid "Network"
|
||||
msgstr "網路"
|
||||
|
||||
#. Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:73
|
||||
#: src/net/i2p/desktopgui/TrayManager.java:63
|
||||
msgid "I2P: Right-click for menu"
|
||||
msgstr "I2P:右鍵開啟選單"
|
||||
|
@ -1,312 +0,0 @@
|
||||
package net.i2p.desktopgui;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.Toolkit;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientApp;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import net.i2p.app.MenuCallback;
|
||||
import net.i2p.app.MenuHandle;
|
||||
import net.i2p.app.MenuService;
|
||||
import net.i2p.app.NotificationService;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
* A simplified Main that does not require router.jar, for App Context only.
|
||||
* Invokes ExternalTrayManager only.
|
||||
* No state tracking, ClientAppManager doesn't care.
|
||||
*
|
||||
* @since 0.9.54
|
||||
*/
|
||||
public class ExternalMain implements ClientApp, NotificationService, MenuService {
|
||||
|
||||
private final I2PAppContext _appContext;
|
||||
private final ClientAppManager _mgr;
|
||||
private final Log log;
|
||||
private TrayManager _trayManager;
|
||||
|
||||
private static final String PROP_SWING = "desktopgui.swing";
|
||||
|
||||
public ExternalMain(I2PAppContext ctx, ClientAppManager mgr, String args[]) {
|
||||
_appContext = ctx;
|
||||
_mgr = mgr;
|
||||
log = _appContext.logManager().getLog(ExternalMain.class);
|
||||
}
|
||||
|
||||
public ExternalMain() {
|
||||
_appContext = I2PAppContext.getGlobalContext();
|
||||
_mgr = _appContext.clientAppManager();
|
||||
log = _appContext.logManager().getLog(ExternalMain.class);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// early check so we can bail out when started via CLI
|
||||
if (!SystemTray.isSupported()) {
|
||||
System.err.println("SystemTray not supported");
|
||||
return;
|
||||
}
|
||||
ExternalMain main = new ExternalMain();
|
||||
main.beginStartup(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the tray icon code (loads tray icon in the tray area).
|
||||
* @throws AWTException on startup error, including systray not supported
|
||||
*/
|
||||
private synchronized void startUp() throws Exception {
|
||||
boolean useSwingDefault = !(SystemVersion.isWindows() || SystemVersion.isMac());
|
||||
boolean useSwing = _appContext.getProperty(PROP_SWING, useSwingDefault);
|
||||
_trayManager = new ExternalTrayManager(_appContext, useSwing);
|
||||
_trayManager.startManager();
|
||||
if (_mgr != null)
|
||||
_mgr.register(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method launching the application.
|
||||
*
|
||||
* @param args unused
|
||||
*/
|
||||
private void beginStartup(String[] args) {
|
||||
String headless = System.getProperty("java.awt.headless");
|
||||
boolean isHeadless = Boolean.parseBoolean(headless);
|
||||
if (isHeadless) {
|
||||
log.warn("Headless environment: not starting desktopgui!");
|
||||
return;
|
||||
}
|
||||
if (SystemVersion.isMac())
|
||||
setMacTrayIcon();
|
||||
launchForeverLoop();
|
||||
|
||||
// We'll be doing GUI work, so let's stay in the event dispatcher thread.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
startUp();
|
||||
} catch(Exception e) {
|
||||
log.error("Failed while running desktopgui!", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unless we do this, when we start DesktopGUI we get a Java coffee cup
|
||||
* in the tray.
|
||||
*
|
||||
* Based on code from https://gist.github.com/bchapuis/1562406 , no apparent license.
|
||||
* See also https://stackoverflow.com/questions/6006173/how-do-you-change-the-dock-icon-of-a-java-program
|
||||
*
|
||||
* TODO, if we wanted to add our own menu, see
|
||||
* https://stackoverflow.com/questions/1319805/java-os-x-dock-menu
|
||||
*
|
||||
* TODO, if we want to make it bounce, see
|
||||
* https://stackoverflow.com/questions/15079783/how-to-make-my-app-icon-bounce-in-the-mac-dock
|
||||
*
|
||||
* TODO, if we want to handle Quit, see
|
||||
* https://nakkaya.com/2009/04/19/java-osx-integration/
|
||||
*
|
||||
* @since 0.9.33
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void setMacTrayIcon() {
|
||||
File f = new File(_appContext.getBaseDir(), "docs/themes/console/images/itoopie_sm.png");
|
||||
if (!f.exists())
|
||||
return;
|
||||
try {
|
||||
Class util = Class.forName("com.apple.eawt.Application");
|
||||
Method getApplication = util.getMethod("getApplication", new Class[0]);
|
||||
Object application = getApplication.invoke(util);
|
||||
Class params[] = new Class[1];
|
||||
params[0] = Image.class;
|
||||
Method setDockIconImage = util.getMethod("setDockIconImage", params);
|
||||
URL url = f.toURI().toURL();
|
||||
Image image = Toolkit.getDefaultToolkit().getImage(url);
|
||||
setDockIconImage.invoke(application, image);
|
||||
} catch (Exception e) {
|
||||
if (log.shouldWarn())
|
||||
log.warn("Can't set OSX Dock icon", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoids the app terminating because no Window is opened anymore.
|
||||
* More info: http://java.sun.com/javase/6/docs/api/java/awt/doc-files/AWTThreadIssues.html#Autoshutdown
|
||||
*/
|
||||
private static void launchForeverLoop() {
|
||||
Runnable r = new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Object o = new Object();
|
||||
synchronized (o) {
|
||||
o.wait();
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
}
|
||||
};
|
||||
Thread t = new Thread(r, "DesktopGUI spinner");
|
||||
t.setDaemon(false);
|
||||
t.start();
|
||||
}
|
||||
|
||||
/////// NotificationService methods
|
||||
|
||||
/**
|
||||
* Send a notification to the user.
|
||||
*
|
||||
* @param source unsupported
|
||||
* @param category unsupported
|
||||
* @param priority unsupported
|
||||
* @param title for the popup, translated
|
||||
* @param message translated
|
||||
* @param path unsupported
|
||||
* @return 0, or -1 on failure
|
||||
*/
|
||||
public int notify(String source, String category, int priority, String title, String message, String path) {
|
||||
TrayManager tm = _trayManager;
|
||||
if (tm == null)
|
||||
return -1;
|
||||
return tm.displayMessage(priority, title, message, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a notification if possible.
|
||||
* Unsupported.
|
||||
*
|
||||
* @return false always
|
||||
*/
|
||||
public boolean cancel(int id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the text of a notification if possible.
|
||||
* Unsupported.
|
||||
*
|
||||
* @return false always
|
||||
*/
|
||||
public boolean update(int id, String title, String message, String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/////// MenuService methods
|
||||
|
||||
/**
|
||||
* Menu will start out shown and enabled, in the root menu
|
||||
*
|
||||
* @param message for the menu, translated
|
||||
* @param callback fired on click
|
||||
* @return null on error
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public MenuHandle addMenu(String message, MenuCallback callback) {
|
||||
return addMenu(message, callback, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu will start out enabled, as a submenu
|
||||
*
|
||||
* @param message for the menu, translated
|
||||
* @param callback fired on click
|
||||
* @param parent the parent menu this will be a submenu of, or null for top level
|
||||
* @return null on error
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public MenuHandle addMenu(String message, MenuCallback callback, MenuHandle parent) {
|
||||
if (_trayManager == null)
|
||||
return null;
|
||||
return _trayManager.addMenu(message, callback, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void removeMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.removeMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void showMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.showMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void hideMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.hideMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void enableMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.enableMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void disableMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.disableMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void updateMenu(String message, MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.updateMenu(message, item);
|
||||
}
|
||||
|
||||
/////// ClientApp methods
|
||||
|
||||
public synchronized void startup() {
|
||||
beginStartup(null);
|
||||
}
|
||||
|
||||
public synchronized void shutdown(String[] args) {
|
||||
if (_trayManager != null)
|
||||
_trayManager.stopManager();
|
||||
}
|
||||
|
||||
public ClientAppState getState() {
|
||||
return ClientAppState.INITIALIZED;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "desktopgui";
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return "Desktop GUI";
|
||||
}
|
||||
|
||||
/////// end ClientApp methods
|
||||
}
|
@ -22,8 +22,8 @@ import net.i2p.desktopgui.router.RouterManager;
|
||||
*/
|
||||
class ExternalTrayManager extends TrayManager {
|
||||
|
||||
public ExternalTrayManager(I2PAppContext ctx, boolean useSwing) {
|
||||
super(ctx, useSwing);
|
||||
public ExternalTrayManager(I2PAppContext ctx, Main main, boolean useSwing) {
|
||||
super(ctx, main, useSwing);
|
||||
}
|
||||
|
||||
public PopupMenu getMainMenu() {
|
||||
@ -51,15 +51,11 @@ class ExternalTrayManager extends TrayManager {
|
||||
}
|
||||
});
|
||||
popup.add(startItem);
|
||||
initializeNotificationItems();
|
||||
popup.add(_notificationItem2);
|
||||
popup.add(_notificationItem1);
|
||||
return popup;
|
||||
}
|
||||
|
||||
public JPopupMenu getSwingMainMenu() {
|
||||
JPopupMenu popup = new JPopupMenu();
|
||||
/*
|
||||
JMenuItem startItem = new JMenuItem(_t("Start I2P"));
|
||||
startItem.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
@ -83,10 +79,6 @@ class ExternalTrayManager extends TrayManager {
|
||||
}
|
||||
});
|
||||
popup.add(startItem);
|
||||
*/
|
||||
initializeJNotificationItems();
|
||||
popup.add(_jnotificationItem2);
|
||||
popup.add(_jnotificationItem1);
|
||||
return popup;
|
||||
}
|
||||
|
||||
@ -94,14 +86,5 @@ class ExternalTrayManager extends TrayManager {
|
||||
* Update the menu
|
||||
* @since 0.9.26
|
||||
*/
|
||||
protected void updateMenu() {
|
||||
if (_notificationItem1 != null)
|
||||
_notificationItem1.setEnabled(_showNotifications);
|
||||
if (_notificationItem2 != null)
|
||||
_notificationItem2.setEnabled(!_showNotifications);
|
||||
if (_jnotificationItem1 != null)
|
||||
_jnotificationItem1.setVisible(_showNotifications);
|
||||
if (_jnotificationItem2 != null)
|
||||
_jnotificationItem2.setVisible(!_showNotifications);
|
||||
}
|
||||
protected void updateMenu() {}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
package net.i2p.desktopgui;
|
||||
|
||||
import java.awt.AWTException;
|
||||
import java.awt.Desktop;
|
||||
import java.awt.Desktop.Action;
|
||||
import java.awt.MenuItem;
|
||||
@ -30,7 +29,6 @@ class InternalTrayManager extends TrayManager {
|
||||
|
||||
private final RouterContext _context;
|
||||
private final Log log;
|
||||
private final Main _main;
|
||||
private MenuItem _statusItem, _browserItem, _configItem, _restartItem, _stopItem,
|
||||
_restartHardItem, _stopHardItem, _cancelItem;
|
||||
private JMenuItem _jstatusItem, _jbrowserItem, _jconfigItem, _jrestartItem, _jstopItem,
|
||||
@ -41,20 +39,11 @@ class InternalTrayManager extends TrayManager {
|
||||
private static final String CONSOLE_BUNDLE_NAME = "net.i2p.router.web.messages";
|
||||
|
||||
public InternalTrayManager(RouterContext ctx, Main main, boolean useSwing) {
|
||||
super(ctx, useSwing);
|
||||
super(ctx, main, useSwing);
|
||||
_context = ctx;
|
||||
_main = main;
|
||||
log = ctx.logManager().getLog(InternalTrayManager.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void startManager() throws AWTException {
|
||||
super.startManager();
|
||||
displayMessage(Log.INFO, _t("Starting"), _t("I2P is starting!"), null);
|
||||
}
|
||||
|
||||
public synchronized PopupMenu getMainMenu() {
|
||||
PopupMenu popup = new PopupMenu();
|
||||
|
||||
@ -84,8 +73,7 @@ class InternalTrayManager extends TrayManager {
|
||||
}
|
||||
|
||||
PopupMenu desktopguiConfigurationLauncher = new PopupMenu(_t("Configure I2P System Tray"));
|
||||
|
||||
MenuItem configSubmenu = new MenuItem(_t("Disable system tray"));
|
||||
MenuItem configSubmenu = new MenuItem(_t("Disable"));
|
||||
configSubmenu.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
@ -185,9 +173,6 @@ class InternalTrayManager extends TrayManager {
|
||||
popup.add(browserLauncher);
|
||||
popup.addSeparator();
|
||||
}
|
||||
initializeNotificationItems();
|
||||
desktopguiConfigurationLauncher.add(_notificationItem2);
|
||||
desktopguiConfigurationLauncher.add(_notificationItem1);
|
||||
desktopguiConfigurationLauncher.add(configSubmenu);
|
||||
popup.add(desktopguiConfigurationLauncher);
|
||||
popup.addSeparator();
|
||||
@ -240,8 +225,7 @@ class InternalTrayManager extends TrayManager {
|
||||
}
|
||||
|
||||
JMenu desktopguiConfigurationLauncher = new JMenu(_t("Configure I2P System Tray"));
|
||||
|
||||
JMenuItem configSubmenu = new JMenuItem(_t("Disable system tray"));
|
||||
JMenuItem configSubmenu = new JMenuItem(_t("Disable"));
|
||||
configSubmenu.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
@ -341,9 +325,6 @@ class InternalTrayManager extends TrayManager {
|
||||
popup.add(browserLauncher);
|
||||
popup.addSeparator();
|
||||
}
|
||||
initializeJNotificationItems();
|
||||
desktopguiConfigurationLauncher.add(_jnotificationItem2);
|
||||
desktopguiConfigurationLauncher.add(_jnotificationItem1);
|
||||
desktopguiConfigurationLauncher.add(configSubmenu);
|
||||
popup.add(desktopguiConfigurationLauncher);
|
||||
popup.addSeparator();
|
||||
@ -409,10 +390,6 @@ class InternalTrayManager extends TrayManager {
|
||||
_stopHardItem.setEnabled(!imminent);
|
||||
if (_cancelItem != null)
|
||||
_cancelItem.setEnabled(x && !imminent);
|
||||
if (_notificationItem1 != null)
|
||||
_notificationItem1.setEnabled(_showNotifications);
|
||||
if (_notificationItem2 != null)
|
||||
_notificationItem2.setEnabled(!_showNotifications);
|
||||
|
||||
if (_jstatusItem != null)
|
||||
_jstatusItem.setText(status);
|
||||
@ -430,10 +407,6 @@ class InternalTrayManager extends TrayManager {
|
||||
_jstopHardItem.setVisible(!imminent);
|
||||
if (_jcancelItem != null)
|
||||
_jcancelItem.setVisible(x && !imminent);
|
||||
if (_jnotificationItem1 != null)
|
||||
_jnotificationItem1.setVisible(_showNotifications);
|
||||
if (_jnotificationItem2 != null)
|
||||
_jnotificationItem2.setVisible(!_showNotifications);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -442,23 +415,16 @@ class InternalTrayManager extends TrayManager {
|
||||
private void configureDesktopgui(boolean enable) {
|
||||
String property = Main.PROP_ENABLE;
|
||||
String value = Boolean.toString(enable);
|
||||
if (!_context.router().saveConfig(property, value))
|
||||
log.error("Error saving config");
|
||||
if (!enable) {
|
||||
// TODO popup that explains how to re-enable in console
|
||||
_main.shutdown(null);
|
||||
}
|
||||
}
|
||||
try {
|
||||
|
||||
/**
|
||||
* @since 0.9.53
|
||||
*/
|
||||
@Override
|
||||
protected void configureNotifications(boolean enable) {
|
||||
_showNotifications = enable;
|
||||
String value = Boolean.toString(enable);
|
||||
if (!_context.router().saveConfig(PROP_NOTIFICATIONS, value))
|
||||
log.error("Error saving config");
|
||||
_context.router().saveConfig(property, value);
|
||||
if (!enable) {
|
||||
// TODO popup that explains how to re-enable in console
|
||||
_main.shutdown(null);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
log.error("Error saving config", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -5,7 +5,6 @@ package net.i2p.desktopgui;
|
||||
*/
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.Toolkit;
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
@ -17,10 +16,6 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import static net.i2p.app.ClientAppState.*;
|
||||
import net.i2p.app.MenuCallback;
|
||||
import net.i2p.app.MenuHandle;
|
||||
import net.i2p.app.MenuService;
|
||||
import net.i2p.app.NotificationService;
|
||||
import net.i2p.desktopgui.router.RouterManager;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.router.app.RouterApp;
|
||||
@ -32,7 +27,7 @@ import net.i2p.util.I2PProperties.I2PPropertyCallback;
|
||||
/**
|
||||
* The main class of the application.
|
||||
*/
|
||||
public class Main implements RouterApp, NotificationService, MenuService {
|
||||
public class Main implements RouterApp {
|
||||
|
||||
// non-null
|
||||
private final I2PAppContext _appContext;
|
||||
@ -60,7 +55,7 @@ public class Main implements RouterApp, NotificationService, MenuService {
|
||||
*/
|
||||
public Main() {
|
||||
_appContext = I2PAppContext.getGlobalContext();
|
||||
if (_appContext.isRouterContext())
|
||||
if (_appContext instanceof RouterContext)
|
||||
_context = (RouterContext) _appContext;
|
||||
else
|
||||
_context = null;
|
||||
@ -80,7 +75,7 @@ public class Main implements RouterApp, NotificationService, MenuService {
|
||||
if (_context != null)
|
||||
trayManager = new InternalTrayManager(_context, this, useSwing);
|
||||
else
|
||||
trayManager = new ExternalTrayManager(_appContext, useSwing);
|
||||
trayManager = new ExternalTrayManager(_appContext, this, useSwing);
|
||||
trayManager.startManager();
|
||||
_trayManager = trayManager;
|
||||
changeState(RUNNING);
|
||||
@ -100,11 +95,6 @@ public class Main implements RouterApp, NotificationService, MenuService {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// early check so we can bail out when started via CLI
|
||||
if (!SystemTray.isSupported()) {
|
||||
System.err.println("SystemTray not supported");
|
||||
return;
|
||||
}
|
||||
Main main = new Main();
|
||||
main.beginStartup(args);
|
||||
}
|
||||
@ -208,129 +198,6 @@ public class Main implements RouterApp, NotificationService, MenuService {
|
||||
t.start();
|
||||
}
|
||||
|
||||
/////// NotificationService methods
|
||||
|
||||
/**
|
||||
* Send a notification to the user.
|
||||
*
|
||||
* @param source unsupported
|
||||
* @param category unsupported
|
||||
* @param priority unsupported
|
||||
* @param title for the popup, translated
|
||||
* @param message translated
|
||||
* @param path unsupported
|
||||
* @return 0, or -1 on failure
|
||||
*/
|
||||
public int notify(String source, String category, int priority, String title, String message, String path) {
|
||||
TrayManager tm = _trayManager;
|
||||
if (tm == null)
|
||||
return -1;
|
||||
return tm.displayMessage(priority, title, message, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel a notification if possible.
|
||||
* Unsupported.
|
||||
*
|
||||
* @return false always
|
||||
*/
|
||||
public boolean cancel(int id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the text of a notification if possible.
|
||||
* Unsupported.
|
||||
*
|
||||
* @return false always
|
||||
*/
|
||||
public boolean update(int id, String title, String message, String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/////// MenuService methods
|
||||
|
||||
/**
|
||||
* Menu will start out shown and enabled, in the root menu
|
||||
*
|
||||
* @param message for the menu, translated
|
||||
* @param callback fired on click
|
||||
* @return null on error
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public MenuHandle addMenu(String message, MenuCallback callback) {
|
||||
return addMenu(message, callback, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Menu will start out enabled, as a submenu
|
||||
*
|
||||
* @param message for the menu, translated
|
||||
* @param callback fired on click
|
||||
* @param parent the parent menu this will be a submenu of, or null for top level
|
||||
* @return null on error
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public MenuHandle addMenu(String message, MenuCallback callback, MenuHandle parent) {
|
||||
if (_trayManager == null)
|
||||
return null;
|
||||
return _trayManager.addMenu(message, callback, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void removeMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.removeMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void showMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.showMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void hideMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.hideMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void enableMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.enableMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void disableMenu(MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.disableMenu(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void updateMenu(String message, MenuHandle item) {
|
||||
if (_trayManager == null)
|
||||
return;
|
||||
_trayManager.updateMenu(message, item);
|
||||
}
|
||||
|
||||
/////// ClientApp methods
|
||||
|
||||
/** @since 0.9.26 */
|
||||
|
@ -4,38 +4,25 @@ import java.awt.AWTException;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Image;
|
||||
import java.awt.MenuItem;
|
||||
import java.awt.PopupMenu;
|
||||
import java.awt.SystemTray;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.TrayIcon;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.MenuKeyEvent;
|
||||
import javax.swing.event.MenuKeyListener;
|
||||
import javax.swing.event.PopupMenuEvent;
|
||||
import javax.swing.event.PopupMenuListener;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.MenuCallback;
|
||||
import net.i2p.app.MenuHandle;
|
||||
import net.i2p.apps.systray.UrlLauncher;
|
||||
import net.i2p.desktopgui.i18n.DesktopguiTranslator;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
/**
|
||||
@ -44,32 +31,25 @@ import net.i2p.util.SystemVersion;
|
||||
abstract class TrayManager {
|
||||
|
||||
protected final I2PAppContext _appContext;
|
||||
protected final Main _main;
|
||||
protected final boolean _useSwing;
|
||||
///The tray area, or null if unsupported
|
||||
protected SystemTray tray;
|
||||
///Our tray icon, or null if unsupported
|
||||
protected TrayIcon trayIcon;
|
||||
protected volatile boolean _showNotifications;
|
||||
protected MenuItem _notificationItem1, _notificationItem2;
|
||||
protected JMenuItem _jnotificationItem1, _jnotificationItem2;
|
||||
private final AtomicInteger _id = new AtomicInteger();
|
||||
private final List<MenuInternal> _menus;
|
||||
private JPopupMenu _jPopupMenu;
|
||||
|
||||
private static final String PNG_DIR = "/desktopgui/resources/images/";
|
||||
private static final String MAC_ICON = "itoopie_black_24.png";
|
||||
private static final String WIN_ICON_LIGHT = "itoopie_white_24.png";
|
||||
private static final String WIN_ICON_DARK = "itoopie_black_24.png";
|
||||
private static final String WIN_ICON = "itoopie_white_24.png";
|
||||
private static final String LIN_ICON = "logo.png";
|
||||
protected static final String PROP_NOTIFICATIONS = "desktopgui.showNotifications";
|
||||
|
||||
/**
|
||||
* Instantiate tray manager.
|
||||
*/
|
||||
protected TrayManager(I2PAppContext ctx, boolean useSwing) {
|
||||
protected TrayManager(I2PAppContext ctx, Main main, boolean useSwing) {
|
||||
_appContext = ctx;
|
||||
_main = main;
|
||||
_useSwing = useSwing;
|
||||
_menus = new ArrayList<MenuInternal>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,7 +58,6 @@ abstract class TrayManager {
|
||||
public synchronized void startManager() throws AWTException {
|
||||
if (!SystemTray.isSupported())
|
||||
throw new AWTException("SystemTray not supported");
|
||||
_showNotifications = _appContext.getBooleanPropertyDefaultTrue(PROP_NOTIFICATIONS);
|
||||
tray = SystemTray.getSystemTray();
|
||||
// Windows typically has tooltips; Linux (at least Ubuntu) doesn't
|
||||
String tooltip = SystemVersion.isWindows() ? _t("I2P: Right-click for menu") : null;
|
||||
@ -118,7 +97,6 @@ abstract class TrayManager {
|
||||
frame.setMinimumSize(new Dimension(0, 0));
|
||||
frame.setSize(0, 0);
|
||||
final JPopupMenu menu = getSwingMainMenu();
|
||||
_jPopupMenu = menu;
|
||||
menu.setFocusable(true);
|
||||
frame.add(menu);
|
||||
TrayIcon ti = new TrayIcon(getTrayImage(), tooltip, null);
|
||||
@ -213,20 +191,12 @@ abstract class TrayManager {
|
||||
*/
|
||||
private Image getTrayImage() throws AWTException {
|
||||
String img;
|
||||
if (SystemVersion.isWindows()) {
|
||||
// too hard to get the theme out of the registry,
|
||||
// use our console theme as a best guess
|
||||
// so we have a contrasting icon
|
||||
String theme = _appContext.getProperty("routerconsole.theme", "light");
|
||||
if (theme.equals("dark"))
|
||||
img = WIN_ICON_LIGHT;
|
||||
else
|
||||
img = WIN_ICON_DARK;
|
||||
} else if (SystemVersion.isMac()) {
|
||||
if (SystemVersion.isWindows())
|
||||
img = WIN_ICON;
|
||||
else if (SystemVersion.isMac())
|
||||
img = MAC_ICON;
|
||||
} else {
|
||||
else
|
||||
img = LIN_ICON;
|
||||
}
|
||||
URL url = getClass().getResource(PNG_DIR + img);
|
||||
if (url == null)
|
||||
throw new AWTException("cannot load tray image " + img);
|
||||
@ -234,316 +204,6 @@ abstract class TrayManager {
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the user.
|
||||
*
|
||||
* @param title for the popup, translated
|
||||
* @param message translated
|
||||
* @param path unsupported
|
||||
* @return 0, or -1 on failure
|
||||
*/
|
||||
public int displayMessage(int priority, String title, String message, String path) {
|
||||
if (!_showNotifications)
|
||||
return -1;
|
||||
final TrayIcon ti = trayIcon;
|
||||
if (ti == null)
|
||||
return -1;
|
||||
TrayIcon.MessageType type;
|
||||
if (priority <= Log.DEBUG)
|
||||
type = TrayIcon.MessageType.NONE;
|
||||
else if (priority <= Log.INFO)
|
||||
type = TrayIcon.MessageType.INFO;
|
||||
else if (priority <= Log.WARN)
|
||||
type = TrayIcon.MessageType.WARNING;
|
||||
else
|
||||
type = TrayIcon.MessageType.ERROR;
|
||||
ti.displayMessage(title, message, type);
|
||||
/*
|
||||
* There's apparently no way to bind a particular message to an action
|
||||
that comes back. We can't keep a queue because we don't get
|
||||
an action back when the message is removed via timeout or user x-out.
|
||||
On OSX, new messages dismiss previous ones.
|
||||
On LXDE (and Gnome?), new messages go under previous ones. Timeout is only 10 seconds.
|
||||
Message timeout is platform-dependent.
|
||||
So the order of events is unknowable.
|
||||
This only works if there is only one message ever.
|
||||
|
||||
if (path != null && path.length() > 0) {
|
||||
if (path.charAt(0) == '/');
|
||||
path = path.substring(1);
|
||||
final String url = _appContext.portMapper().getConsoleURL() + path;
|
||||
ti.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
ti.removeActionListener(this);
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
System.out.println("DIB " + arg0);
|
||||
UrlLauncher launcher = new UrlLauncher(_appContext, null, null);
|
||||
try {
|
||||
launcher.openUrl(url);
|
||||
System.out.println("DIB success " + url);
|
||||
} catch (IOException e1) {
|
||||
System.out.println("DIB fail " + url);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
System.out.println("done " + arg0);
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
}
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not save. See InternalTrayManager.
|
||||
*
|
||||
* @since 0.9.58 moved up from InternalTrayManager
|
||||
*/
|
||||
protected void configureNotifications(boolean enable) {
|
||||
_showNotifications = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes _notificationItem 1 and 2
|
||||
*
|
||||
* @since 0.9.58 pulled out of InternalTrayManager
|
||||
*/
|
||||
protected void initializeNotificationItems() {
|
||||
final MenuItem notificationItem2 = new MenuItem(_t("Enable notifications"));
|
||||
notificationItem2.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
configureNotifications(true);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
_notificationItem2 = notificationItem2;
|
||||
|
||||
final MenuItem notificationItem1 = new MenuItem(_t("Disable notifications"));
|
||||
notificationItem1.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
configureNotifications(false);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
_notificationItem1 = notificationItem1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes _jnotificationItem 1 and 2
|
||||
*
|
||||
* @since 0.9.58 pulled out of InternalTrayManager
|
||||
*/
|
||||
protected void initializeJNotificationItems() {
|
||||
final JMenuItem notificationItem2 = new JMenuItem(_t("Enable notifications"));
|
||||
notificationItem2.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
configureNotifications(true);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
_jnotificationItem2 = notificationItem2;
|
||||
|
||||
final JMenuItem notificationItem1 = new JMenuItem(_t("Disable notifications"));
|
||||
notificationItem1.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
configureNotifications(false);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
_jnotificationItem1 = notificationItem1;
|
||||
}
|
||||
|
||||
/////// MenuService delegation methods
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public MenuHandle addMenu(String message, final MenuCallback callback, MenuHandle p) {
|
||||
MenuInternal parent = p != null ? (MenuInternal) p : null;
|
||||
final int id = _id.incrementAndGet();
|
||||
final MenuInternal rv;
|
||||
if (_useSwing) {
|
||||
final JMenuItem m = new JMenuItem(message);
|
||||
rv = new MenuInternal(null, m, callback, id);
|
||||
m.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
rv.cb.clicked(rv);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
_jPopupMenu.add(m);
|
||||
} else {
|
||||
final MenuItem m = new MenuItem(message);
|
||||
rv = new MenuInternal(m, null, callback, id);
|
||||
m.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent arg0) {
|
||||
new SwingWorker<Object, Object>() {
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
rv.cb.clicked(rv);
|
||||
return null;
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
});
|
||||
trayIcon.getPopupMenu().add(m);
|
||||
}
|
||||
synchronized(_menus) {
|
||||
_menus.add(rv);
|
||||
}
|
||||
updateMenu();
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void removeMenu(MenuHandle item) {
|
||||
MenuInternal mi = (MenuInternal) item;
|
||||
if (_useSwing) {
|
||||
_jPopupMenu.remove(mi.jm);
|
||||
} else {
|
||||
trayIcon.getPopupMenu().remove(mi.m);
|
||||
}
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void showMenu(MenuHandle item) {
|
||||
MenuInternal mi = (MenuInternal) item;
|
||||
mi.setVisible(true);
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void hideMenu(MenuHandle item) {
|
||||
MenuInternal mi = (MenuInternal) item;
|
||||
mi.setVisible(false);
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void enableMenu(MenuHandle item) {
|
||||
MenuInternal mi = (MenuInternal) item;
|
||||
mi.setEnabled(true);
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void disableMenu(MenuHandle item) {
|
||||
MenuInternal mi = (MenuInternal) item;
|
||||
mi.setEnabled(false);
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
public void updateMenu(String message, MenuHandle item) {
|
||||
MenuInternal mi = (MenuInternal) item;
|
||||
mi.setText(message);
|
||||
updateMenu();
|
||||
}
|
||||
|
||||
/////// MenuService internals
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
private MenuInternal getMenu(int id) {
|
||||
synchronized(_menus) {
|
||||
for (MenuInternal mi : _menus) {
|
||||
if (mi.getID() == id)
|
||||
return mi;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.59
|
||||
*/
|
||||
private static class MenuInternal implements MenuHandle {
|
||||
private final MenuItem m;
|
||||
private final JMenuItem jm;
|
||||
private final MenuCallback cb;
|
||||
private final int id;
|
||||
|
||||
public MenuInternal(MenuItem mm, JMenuItem jmm, MenuCallback cbb, int idd) {
|
||||
m = mm; jm = jmm; cb = cbb; id = idd;
|
||||
}
|
||||
|
||||
public int getID() { return id; }
|
||||
|
||||
private void setEnabled(boolean yes) {
|
||||
if (m != null)
|
||||
m.setEnabled(yes);
|
||||
else
|
||||
jm.setEnabled(yes);
|
||||
}
|
||||
|
||||
private void setVisible(boolean yes) {
|
||||
if (m != null)
|
||||
m.setEnabled(yes);
|
||||
else
|
||||
jm.setVisible(yes);
|
||||
}
|
||||
|
||||
private void setText(String text) {
|
||||
if (m != null)
|
||||
m.setLabel(text);
|
||||
else
|
||||
jm.setText(text);
|
||||
}
|
||||
}
|
||||
|
||||
protected String _t(String s) {
|
||||
return DesktopguiTranslator._t(_appContext, s);
|
||||
}
|
||||
|
@ -92,29 +92,7 @@
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="listChangedFiles" if="git.available" >
|
||||
<exec executable="git" outputproperty="workspace.changes" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="status" />
|
||||
<arg value="-s" />
|
||||
<arg value="--porcelain" />
|
||||
<arg value="-uno" />
|
||||
<arg value="." />
|
||||
<arg value="../resources" />
|
||||
</exec>
|
||||
<!-- trim flags -->
|
||||
<exec executable="sed" inputstring="${workspace.changes}" outputproperty="workspace.changes.sed" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-e" />
|
||||
<arg value="s/^[MTADRCU ]*//" />
|
||||
</exec>
|
||||
<!-- \n in an attribute value generates an invalid manifest -->
|
||||
<exec executable="tr" inputstring="${workspace.changes.sed}" outputproperty="workspace.changes.tr" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-s" />
|
||||
<arg value="[:space:]" />
|
||||
<arg value="," />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="compile, listChangedFiles">
|
||||
<target name="jar" depends="compile">
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<jar destfile="build/i2pcontrol.jar" basedir="./build/obj" includes="**/*.class" >
|
||||
@ -130,7 +108,7 @@
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="socketJar" depends="compileSocketJar, listChangedFiles">
|
||||
<target name="socketJar" depends="compileSocketJar">
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<jar destfile="build/i2pcontrol.jar" basedir="./build/obj" includes="**/*.class" >
|
||||
@ -146,7 +124,7 @@
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
<target name="war" depends="compile, listChangedFiles" >
|
||||
<target name="war" depends="compile" >
|
||||
<!-- set if unset -->
|
||||
<property name="workspace.changes.tr" value="" />
|
||||
<war destfile="build/jsonrpc.war" webxml="web.xml" >
|
||||
|
@ -12,7 +12,7 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
import net.i2p.apache.http.conn.util.InetAddressUtils;
|
||||
import org.apache.http.conn.util.InetAddressUtils;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
|
@ -156,11 +156,11 @@ public class JSONRPC2Servlet extends HttpServlet {
|
||||
} else {
|
||||
out.println("<p>Current API password:<input name=\"password\" type=\"password\">");
|
||||
}
|
||||
out.println("<p>New API password (twice): <input name=\"password2\" type=\"password\"> " +
|
||||
"<input name=\"password3\" type=\"password\"> " +
|
||||
out.println("<p>New API password (twice):<input name=\"password2\" type=\"password\">" +
|
||||
"<input name=\"password3\" type=\"password\">" +
|
||||
"<input name=\"save\" type=\"submit\" value=\"Change API Password\">" +
|
||||
"<p>If you forget the API password, <a href=\"/configwebapps\">stop jsonrpc</a>, delete the file <tt>" + _conf.getConfFile() +
|
||||
"</tt>, and <a href=\"/configwebapps\">restart jsonrpc</a>.");
|
||||
"<p>If you forget the API password, stop i2pcontrol, delete the file <tt>" + _conf.getConfFile() +
|
||||
"</tt>, and restart i2pcontrol.");
|
||||
out.println("</form>");
|
||||
} else {
|
||||
out.println("<p><a href=\"password\">Change API Password</a>");
|
||||
|
@ -96,7 +96,7 @@ public class RouterInfoHandler implements RequestHandler {
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.status")) {
|
||||
outParams.put("i2p.router.status", _context.throttle().getLocalizedTunnelStatus());
|
||||
outParams.put("i2p.router.status", _context.throttle().getTunnelStatus());
|
||||
}
|
||||
|
||||
if (inParams.containsKey("i2p.router.net.status")) {
|
||||
@ -170,7 +170,7 @@ public class RouterInfoHandler implements RequestHandler {
|
||||
&& (!_context.router().gracefulShutdownInProgress())
|
||||
&& !_context.clientManager().isAlive())
|
||||
return (NETWORK_STATUS.ERROR_I2CP);
|
||||
long skew = _context.commSystem().getFramedAveragePeerClockSkew(10);
|
||||
long skew = _context.commSystem().getFramedAveragePeerClockSkew(33);
|
||||
// Display the actual skew, not the offset
|
||||
if (Math.abs(skew) > 60 * 1000)
|
||||
return NETWORK_STATUS.ERROR_CLOCK_SKEW;
|
||||
@ -179,44 +179,25 @@ public class RouterInfoHandler implements RequestHandler {
|
||||
|
||||
int status = _context.commSystem().getStatus().getCode();
|
||||
switch (status) {
|
||||
|
||||
case CommSystemFacade.STATUS_OK:
|
||||
case CommSystemFacade.STATUS_IPV4_OK_IPV6_UNKNOWN:
|
||||
case CommSystemFacade.STATUS_IPV4_OK_IPV6_FIREWALLED:
|
||||
case CommSystemFacade.STATUS_IPV4_FIREWALLED_IPV6_OK:
|
||||
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_OK:
|
||||
RouterAddress ra = _context.router().getRouterInfo().getTargetAddress("NTCP2");
|
||||
case CommSystemFacade.STATUS_OK:
|
||||
RouterAddress ra = _context.router().getRouterInfo().getTargetAddress("NTCP");
|
||||
if (ra == null || TransportUtil.isPubliclyRoutable(ra.getIP(), true))
|
||||
return NETWORK_STATUS.OK;
|
||||
return NETWORK_STATUS.ERROR_PRIVATE_TCP_ADDRESS;
|
||||
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
case CommSystemFacade.STATUS_IPV4_SNAT_IPV6_OK:
|
||||
case CommSystemFacade.STATUS_IPV4_SNAT_IPV6_UNKNOWN:
|
||||
case CommSystemFacade.STATUS_DIFFERENT:
|
||||
return NETWORK_STATUS.ERROR_SYMMETRIC_NAT;
|
||||
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
case CommSystemFacade.STATUS_IPV4_FIREWALLED_IPV6_UNKNOWN:
|
||||
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_FIREWALLED:
|
||||
if (_context.router().getRouterInfo().getTargetAddress("NTCP2") != null)
|
||||
case CommSystemFacade.STATUS_REJECT_UNSOLICITED:
|
||||
if (_context.router().getRouterInfo().getTargetAddress("NTCP") != null)
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_WITH_INBOUND_TCP;
|
||||
if (_context.netDb().floodfillEnabled())
|
||||
if (((FloodfillNetworkDatabaseFacade) _context.netDb()).floodfillEnabled())
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_AND_FLOODFILL;
|
||||
if (_context.router().getRouterInfo().getCapabilities().indexOf('O') >= 0)
|
||||
return NETWORK_STATUS.WARN_FIREWALLED_AND_FAST;
|
||||
return NETWORK_STATUS.FIREWALLED;
|
||||
|
||||
case CommSystemFacade.STATUS_HOSED:
|
||||
case CommSystemFacade.STATUS_HOSED:
|
||||
return NETWORK_STATUS.ERROR_UDP_PORT_IN_USE;
|
||||
|
||||
case CommSystemFacade.STATUS_DISCONNECTED:
|
||||
return NETWORK_STATUS.ERROR_NO_ACTIVE_PEERS_CHECK_CONNECTION_AND_FIREWALL;
|
||||
|
||||
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
|
||||
case CommSystemFacade.STATUS_IPV4_UNKNOWN_IPV6_OK:
|
||||
case CommSystemFacade.STATUS_IPV4_UNKNOWN_IPV6_FIREWALLED:
|
||||
case CommSystemFacade.STATUS_IPV4_DISABLED_IPV6_UNKNOWN:
|
||||
default:
|
||||
case CommSystemFacade.STATUS_UNKNOWN: // fallthrough
|
||||
default:
|
||||
ra = _context.router().getRouterInfo().getTargetAddress("SSU");
|
||||
if (ra == null && _context.router().getUptime() > 5 * 60 * 1000) {
|
||||
if (_context.commSystem().countActivePeers() <= 0)
|
||||
|
@ -17,9 +17,6 @@ dependencies {
|
||||
compile 'gnu.getopt:java-getopt:1.0.13'
|
||||
providedCompile project(':apps:ministreaming')
|
||||
providedCompile project(':apps:jetty')
|
||||
// this is not needed except for standalone,
|
||||
// but we build the standalone classes even for non-standalone
|
||||
providedCompile project(':apps:desktopgui')
|
||||
}
|
||||
|
||||
task i2psnarkJar(type: Jar) {
|
||||
|
@ -2,13 +2,3 @@
|
||||
# This is for app context configuration of standalone i2psnark.
|
||||
# Almost all configuration settings are in i2psnark.config.d/i2psnark.config
|
||||
#
|
||||
# disable browser launch on startup
|
||||
#routerconsole.browser=/bin/false
|
||||
# disable browser launch on startup (Windows)
|
||||
#routerconsole.browser=NUL
|
||||
# change browser
|
||||
#routerconsole.browser=firefox
|
||||
# disable system tray
|
||||
#desktopgui.enabled=false
|
||||
# disable system tray notification popups
|
||||
#desktopgui.showNotifications=false
|
||||
|
@ -21,8 +21,6 @@
|
||||
<pathelement location="../../jetty/jettylib/javax.servlet.jar" />
|
||||
<!-- jsp-api.jar only present for debian builds -->
|
||||
<pathelement location="../../jetty/jettylib/jsp-api.jar" />
|
||||
<!-- following jars only for standalone builds -->
|
||||
<pathelement location="../../desktopgui/dist/desktopgui.jar" />
|
||||
</classpath>
|
||||
</depend>
|
||||
</target>
|
||||
@ -62,26 +60,18 @@
|
||||
<pathelement location="../../systray/java/build/systray.jar" />
|
||||
<pathelement location="../../jetty/jettylib/org.mortbay.jetty.jar" />
|
||||
<pathelement location="../../jetty/jettylib/jetty-util.jar" />
|
||||
<pathelement location="../../desktopgui/dist/desktopgui.jar" />
|
||||
</classpath>
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<target name="listChangedFiles" depends="jarUpToDate" if="shouldListChanges" >
|
||||
<exec executable="git" outputproperty="workspace.changes" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="status" />
|
||||
<arg value="-s" />
|
||||
<arg value="--porcelain" />
|
||||
<arg value="-uno" />
|
||||
<exec executable="mtn" outputproperty="workspace.changes" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="list" />
|
||||
<arg value="changed" />
|
||||
<arg value=".." />
|
||||
</exec>
|
||||
<!-- trim flags -->
|
||||
<exec executable="sed" inputstring="${workspace.changes}" outputproperty="workspace.changes.sed" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-e" />
|
||||
<arg value="s/^[MTADRCU ]*//" />
|
||||
</exec>
|
||||
<!-- \n in an attribute value generates an invalid manifest -->
|
||||
<exec executable="tr" inputstring="${workspace.changes.sed}" outputproperty="workspace.changes.tr" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<exec executable="tr" inputstring="${workspace.changes}" outputproperty="workspace.changes.tr" errorproperty="mtn.error2" failifexecutionfails="false" >
|
||||
<arg value="-s" />
|
||||
<arg value="[:space:]" />
|
||||
<arg value="," />
|
||||
@ -118,7 +108,7 @@
|
||||
<not>
|
||||
<isset property="war.uptodate" />
|
||||
</not>
|
||||
<isset property="git.available" />
|
||||
<isset property="mtn.available" />
|
||||
</and>
|
||||
</condition>
|
||||
</target>
|
||||
@ -207,16 +197,9 @@
|
||||
</target>
|
||||
|
||||
<target name="standalone" depends="standalone_prep">
|
||||
<!-- doesn't support file permissions
|
||||
<zip destfile="i2psnark-standalone.zip">
|
||||
<zipfileset dir="./i2psnark/" />
|
||||
<zipfileset dir="./dist/" prefix="i2psnark/" />
|
||||
</zip>
|
||||
-->
|
||||
<exec executable="zip" failifexecutionfails="true" failonerror="true" >
|
||||
<arg value="-r" />
|
||||
<arg value="i2psnark-standalone.zip" />
|
||||
<arg value="i2psnark" />
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- make a fat jar for standalone -->
|
||||
@ -245,8 +228,6 @@
|
||||
<zipfileset src="../../ministreaming/java/build/mstreaming.jar" />
|
||||
<zipfileset src="../../streaming/java/build/streaming.jar" />
|
||||
<zipfileset src="../../systray/java/build/systray.jar" />
|
||||
<zipfileset src="../../../build/jbigi.jar" />
|
||||
<zipfileset src="../../desktopgui/dist/desktopgui.jar" />
|
||||
<!-- Countries translations. The i2psnark translations are in the war but it's easier to put these here -->
|
||||
<!-- 300KB just to translate "Brazil", but why not... -->
|
||||
<!--
|
||||
@ -305,12 +286,6 @@
|
||||
value="url(/i2psnark/.resources/themes/ubergine/images/" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
<replace dir="build/standalone-resources/.resources/themes"
|
||||
summary="true"
|
||||
token="url(/themes/console/images/buttons/"
|
||||
value="url(/i2psnark/.resources/icons/" >
|
||||
<include name="**/*.css" />
|
||||
</replace>
|
||||
|
||||
<!-- Rather than pulling in all the console theme images, let's just specify the ones we need -->
|
||||
<copy file="../../routerconsole/jsp/themes/console/images/transparent.gif"
|
||||
@ -321,8 +296,6 @@
|
||||
todir="build/standalone-resources/.resources/themes/light/images" />
|
||||
<copy file="../../routerconsole/jsp/themes/console/images/info/errortriangle.png"
|
||||
todir="build/standalone-resources/.resources/themes/ubergine/images" />
|
||||
<copy file="../../routerconsole/jsp/themes/console/images/buttons/search.png"
|
||||
todir="build/standalone-resources/.resources/icons" />
|
||||
|
||||
<mkdir dir="build/standalone-resources/.resources/js" />
|
||||
<copy file="../../routerconsole/jsp/js/ajax.js" todir="build/standalone-resources/.resources/js" />
|
||||
@ -333,34 +306,33 @@
|
||||
</target>
|
||||
|
||||
<target name="standalone_prep" depends="standalone_jar, standalone_war">
|
||||
<delete dir="./i2psnark" />
|
||||
<mkdir dir="./i2psnark" />
|
||||
<copy file="../launch-i2psnark" todir="./i2psnark/" />
|
||||
<chmod type="file" file="./i2psnark/launch-i2psnark" perm="+x" />
|
||||
<copy file="../launch-i2psnark.bat" todir="./i2psnark/" />
|
||||
<mkdir dir="./i2psnark/contexts" />
|
||||
<copy file="../standalone-context.xml" tofile="./i2psnark/contexts/context.xml" />
|
||||
<mkdir dir="./i2psnark/docroot" />
|
||||
<copy file="../standalone-index.html" tofile="./i2psnark/docroot/index.html" />
|
||||
<mkdir dir="./i2psnark/webapps" />
|
||||
<copy file="../i2psnark.war" tofile="./i2psnark/webapps/i2psnark.war" />
|
||||
<copy file="../jetty-i2psnark.xml" tofile="./i2psnark/jetty-i2psnark.xml" />
|
||||
<copy file="../i2psnark-appctx.config" tofile="./i2psnark/i2psnark-appctx.config" />
|
||||
<copy file="./build/i2psnark-standalone.jar" tofile="./i2psnark/i2psnark.jar" />
|
||||
<copy file="../readme-standalone.txt" tofile="./i2psnark/readme.txt" />
|
||||
<delete dir="./dist" />
|
||||
<mkdir dir="./dist" />
|
||||
<copy file="../launch-i2psnark" todir="./dist/" />
|
||||
<copy file="../launch-i2psnark.bat" todir="./dist/" />
|
||||
<mkdir dir="./dist/contexts" />
|
||||
<copy file="../standalone-context.xml" tofile="./dist/contexts/context.xml" />
|
||||
<mkdir dir="./dist/docroot" />
|
||||
<copy file="../standalone-index.html" tofile="./dist/docroot/index.html" />
|
||||
<mkdir dir="./dist/webapps" />
|
||||
<copy file="../i2psnark.war" tofile="./dist/webapps/i2psnark.war" />
|
||||
<copy file="../jetty-i2psnark.xml" tofile="./dist/jetty-i2psnark.xml" />
|
||||
<copy file="../i2psnark-appctx.config" tofile="./dist/i2psnark-appctx.config" />
|
||||
<copy file="./build/i2psnark-standalone.jar" tofile="./dist/i2psnark.jar" />
|
||||
<copy file="../readme-standalone.txt" tofile="./dist/readme.txt" />
|
||||
<!-- temp so announces work -->
|
||||
<copy file="../../../installer/resources/hosts.txt" tofile="./i2psnark/hosts.txt" />
|
||||
<copy todir="./i2psnark/licenses" >
|
||||
<copy file="../../../installer/resources/hosts.txt" tofile="./dist/hosts.txt" />
|
||||
<copy todir="./dist/licenses" >
|
||||
<fileset dir="../../../licenses" includes="LICENSE-GPLv2.txt, ABOUT-Jetty.html" />
|
||||
</copy>
|
||||
<mkdir dir="./i2psnark/logs" />
|
||||
<mkdir dir="./dist/logs" />
|
||||
</target>
|
||||
|
||||
<target name="clean">
|
||||
<delete dir="./build" />
|
||||
<delete file="../i2psnark.war" />
|
||||
<delete file="./i2psnark-standalone.zip" />
|
||||
<delete dir="./i2psnark" />
|
||||
<delete dir="./dist" />
|
||||
</target>
|
||||
<target name="cleandep" depends="clean">
|
||||
</target>
|
||||
|
@ -4,6 +4,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.i2p.CoreVersion;
|
||||
|
||||
/**
|
||||
* Simple command line access to various utilities.
|
||||
* Not a public API. Subject to change.
|
||||
@ -36,7 +38,7 @@ public class CommandLine extends net.i2p.util.CommandLine {
|
||||
}
|
||||
|
||||
private static void usage(List<String> classes) {
|
||||
System.err.println("I2PSnark version " + SnarkManager.FULL_VERSION + '\n' +
|
||||
System.err.println("I2PSnark version " + CoreVersion.VERSION + '\n' +
|
||||
"USAGE: java -jar /path/to/i2psnark.jar command [args]");
|
||||
printCommands(classes);
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.RouterRestartException;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.Log;
|
||||
@ -132,13 +131,12 @@ class ConnectionAcceptor implements Runnable
|
||||
/**
|
||||
* Effectively unused, would only be called if we changed
|
||||
* I2CP host/port, which is hidden in the gui if in router context
|
||||
* FIXME this only works if already running
|
||||
*/
|
||||
public synchronized void restart() {
|
||||
Thread t = thread;
|
||||
if (t != null)
|
||||
t.interrupt();
|
||||
else
|
||||
startAccepting();
|
||||
}
|
||||
|
||||
public int getPort()
|
||||
@ -203,24 +201,6 @@ class ConnectionAcceptor implements Runnable
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
catch (RouterRestartException rre) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Waiting for router restart", rre);
|
||||
try {
|
||||
Thread.sleep(2*60*1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
while (true) {
|
||||
if (_util.connected())
|
||||
break;
|
||||
if (_util.connect())
|
||||
break;
|
||||
try {
|
||||
Thread.sleep(60*1000);
|
||||
} catch (InterruptedException ie) { break; }
|
||||
}
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Router restarted");
|
||||
}
|
||||
catch (I2PException ioe)
|
||||
{
|
||||
int level = stop ? Log.WARN : Log.ERROR;
|
||||
|
@ -1,15 +1,8 @@
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Arrays;
|
||||
@ -20,7 +13,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.I2PException;
|
||||
@ -31,12 +23,10 @@ import net.i2p.client.streaming.I2PServerSocket;
|
||||
import net.i2p.client.streaming.I2PSocket;
|
||||
import net.i2p.client.streaming.I2PSocketEepGet;
|
||||
import net.i2p.client.streaming.I2PSocketManager;
|
||||
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener;
|
||||
import net.i2p.client.streaming.I2PSocketManagerFactory;
|
||||
import net.i2p.client.streaming.I2PSocketOptions;
|
||||
import net.i2p.data.Base32;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.ConcurrentHashSet;
|
||||
@ -45,9 +35,7 @@ import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureDirectory;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SimpleTimer;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.Translate;
|
||||
|
||||
import org.klomp.snark.dht.DHT;
|
||||
@ -59,7 +47,7 @@ import org.klomp.snark.dht.KRPC;
|
||||
* so we can run multiple instances of single Snarks
|
||||
* (but not multiple SnarkManagers, it is still static)
|
||||
*/
|
||||
public class I2PSnarkUtil implements DisconnectListener {
|
||||
public class I2PSnarkUtil {
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
private final String _baseName;
|
||||
@ -87,8 +75,6 @@ public class I2PSnarkUtil implements DisconnectListener {
|
||||
private List<String> _openTrackers;
|
||||
private DHT _dht;
|
||||
private long _startedTime;
|
||||
private final DisconnectListener _discon;
|
||||
private int _maxFilesPerTorrent = SnarkManager.DEFAULT_MAX_FILES_PER_TORRENT;
|
||||
|
||||
private static final int EEPGET_CONNECT_TIMEOUT = 45*1000;
|
||||
private static final int EEPGET_CONNECT_TIMEOUT_SHORT = 5*1000;
|
||||
@ -105,18 +91,17 @@ public class I2PSnarkUtil implements DisconnectListener {
|
||||
|
||||
|
||||
public I2PSnarkUtil(I2PAppContext ctx) {
|
||||
this(ctx, "i2psnark", null);
|
||||
this(ctx, "i2psnark");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param baseName generally "i2psnark"
|
||||
* @since Jetty 7
|
||||
*/
|
||||
public I2PSnarkUtil(I2PAppContext ctx, String baseName, DisconnectListener discon) {
|
||||
public I2PSnarkUtil(I2PAppContext ctx, String baseName) {
|
||||
_context = ctx;
|
||||
_log = _context.logManager().getLog(I2PSnarkUtil.class);
|
||||
_log = _context.logManager().getLog(Snark.class);
|
||||
_baseName = baseName;
|
||||
_discon = discon;
|
||||
_opts = new HashMap<String, String>();
|
||||
//setProxy("127.0.0.1", 4444);
|
||||
setI2CPConfig("127.0.0.1", I2PClient.DEFAULT_LISTEN_PORT, null);
|
||||
@ -254,11 +239,6 @@ public class I2PSnarkUtil implements DisconnectListener {
|
||||
/** @since 0.9.1 */
|
||||
public File getTempDir() { return _tmpDir; }
|
||||
|
||||
/** @since 0.9.58 */
|
||||
public int getMaxFilesPerTorrent() { return _maxFilesPerTorrent; }
|
||||
/** @since 0.9.58 */
|
||||
public void setMaxFilesPerTorrent(int max) { _maxFilesPerTorrent = Math.max(max, 1); }
|
||||
|
||||
/**
|
||||
* Connect to the router, if we aren't already
|
||||
*/
|
||||
@ -344,11 +324,8 @@ public class I2PSnarkUtil implements DisconnectListener {
|
||||
if (opts.getProperty(I2PClient.PROP_GZIP) == null)
|
||||
opts.setProperty(I2PClient.PROP_GZIP, "false");
|
||||
_manager = I2PSocketManagerFactory.createManager(_i2cpHost, _i2cpPort, opts);
|
||||
if (_manager != null) {
|
||||
if (_manager != null)
|
||||
_startedTime = _context.clock().now();
|
||||
if (_discon != null)
|
||||
_manager.addDisconnectListener(this);
|
||||
}
|
||||
_connecting = false;
|
||||
}
|
||||
if (_shouldUseDHT && _manager != null && _dht == null)
|
||||
@ -356,23 +333,6 @@ public class I2PSnarkUtil implements DisconnectListener {
|
||||
return (_manager != null);
|
||||
}
|
||||
|
||||
/**
|
||||
* DisconnectListener interface
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void sessionDisconnected() {
|
||||
synchronized(this) {
|
||||
_manager = null;
|
||||
_connecting = false;
|
||||
if (_dht != null) {
|
||||
_dht.stop();
|
||||
_dht = null;
|
||||
}
|
||||
}
|
||||
if (_discon != null)
|
||||
_discon.sessionDisconnected();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if disabled or not started
|
||||
* @since 0.8.4
|
||||
@ -577,9 +537,6 @@ public class I2PSnarkUtil implements DisconnectListener {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Full Base64 of Destination
|
||||
*/
|
||||
public String getOurIPString() {
|
||||
Destination dest = getMyDestination();
|
||||
if (dest != null)
|
||||
@ -846,90 +803,4 @@ public class I2PSnarkUtil implements DisconnectListener {
|
||||
public String getString(int n, String s, String p) {
|
||||
return Translate.getString(n, s, p, _context, BUNDLE_NAME);
|
||||
}
|
||||
|
||||
private static final boolean SHOULD_SYNC = !(SystemVersion.isAndroid() || SystemVersion.isARM());
|
||||
private static final Pattern ILLEGAL_KEY = Pattern.compile("[#=\\r\\n;]");
|
||||
private static final Pattern ILLEGAL_VALUE = Pattern.compile("[\\r\\n]");
|
||||
|
||||
/**
|
||||
* Same as DataHelper.loadProps() but allows '#' in values,
|
||||
* so we can have filenames with '#' in them in torrent config files.
|
||||
* '#' must be in column 1 for a comment.
|
||||
*
|
||||
* @since 0.9.58
|
||||
*/
|
||||
static void loadProps(Properties props, File f) throws IOException {
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream(f), "UTF-8"), 1024);
|
||||
String line = null;
|
||||
while ( (line = in.readLine()) != null) {
|
||||
if (line.trim().length() <= 0) continue;
|
||||
if (line.charAt(0) == '#') continue;
|
||||
if (line.charAt(0) == ';') continue;
|
||||
int split = line.indexOf('=');
|
||||
if (split <= 0) continue;
|
||||
String key = line.substring(0, split);
|
||||
String val = line.substring(split+1).trim();
|
||||
props.setProperty(key, val);
|
||||
}
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as DataHelper.loadProps() but allows '#' in values,
|
||||
* so we can have filenames with '#' in them in torrent config files.
|
||||
* '#' must be in column 1 for a comment.
|
||||
*
|
||||
* @since 0.9.58
|
||||
*/
|
||||
static void storeProps(Properties props, File file) throws IOException {
|
||||
FileOutputStream fos = null;
|
||||
PrintWriter out = null;
|
||||
IOException ioe = null;
|
||||
File tmpFile = new File(file.getPath() + ".tmp");
|
||||
try {
|
||||
fos = new SecureFileOutputStream(tmpFile);
|
||||
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(fos, "UTF-8")));
|
||||
out.println("# NOTE: This I2P config file must use UTF-8 encoding");
|
||||
out.println("# Last saved: " + DataHelper.formatTime(System.currentTimeMillis()));
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
String name = (String) entry.getKey();
|
||||
String val = (String) entry.getValue();
|
||||
if (ILLEGAL_KEY.matcher(name).find()) {
|
||||
if (ioe == null)
|
||||
ioe = new IOException("Invalid character (one of \"#;=\\r\\n\") in key: \"" +
|
||||
name + "\" = \"" + val + '\"');
|
||||
continue;
|
||||
}
|
||||
if (ILLEGAL_VALUE.matcher(val).find()) {
|
||||
if (ioe == null)
|
||||
ioe = new IOException("Invalid character (one of \"\\r\\n\") in value: \"" +
|
||||
name + "\" = \"" + val + '\"');
|
||||
continue;
|
||||
}
|
||||
out.println(name + "=" + val);
|
||||
}
|
||||
if (SHOULD_SYNC) {
|
||||
out.flush();
|
||||
fos.getFD().sync();
|
||||
}
|
||||
out.close();
|
||||
if (out.checkError()) {
|
||||
out = null;
|
||||
tmpFile.delete();
|
||||
throw new IOException("Failed to write properties to " + tmpFile);
|
||||
}
|
||||
out = null;
|
||||
if (!FileUtil.rename(tmpFile, file))
|
||||
throw new IOException("Failed rename from " + tmpFile + " to " + file);
|
||||
} finally {
|
||||
if (out != null) out.close();
|
||||
if (fos != null) try { fos.close(); } catch (IOException e) {}
|
||||
}
|
||||
if (ioe != null)
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
|
@ -82,9 +82,8 @@ public class MetaInfo
|
||||
* @param created_by may be null
|
||||
* @param url_list may be null
|
||||
* @param comment may be null
|
||||
* @since public since 0.9.53, was package private
|
||||
*/
|
||||
public MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
|
||||
MetaInfo(String announce, String name, String name_utf8, List<List<String>> files, List<Long> lengths,
|
||||
int piece_length, byte[] piece_hashes, long length, boolean privateTorrent,
|
||||
List<List<String>> announce_list, String created_by, List<String> url_list, String comment)
|
||||
{
|
||||
@ -186,20 +185,11 @@ public class MetaInfo
|
||||
if (val == null) {
|
||||
this.url_list = null;
|
||||
} else {
|
||||
List<String> urllist;
|
||||
try {
|
||||
List<BEValue> bl1 = val.getList();
|
||||
urllist = new ArrayList<String>(bl1.size());
|
||||
for (BEValue bev : bl1) {
|
||||
urllist.add(bev.getString());
|
||||
}
|
||||
} catch (InvalidBEncodingException ibee) {
|
||||
// BEP 19 says it's a list but the example there
|
||||
// is for a single byte string, and we've seen this
|
||||
// in the wild.
|
||||
urllist = Collections.singletonList(val.getString());
|
||||
List<BEValue> bl1 = val.getList();
|
||||
this.url_list = new ArrayList<String>(bl1.size());
|
||||
for (BEValue bev : bl1) {
|
||||
this.url_list.add(bev.getString());
|
||||
}
|
||||
this.url_list = urllist;
|
||||
}
|
||||
|
||||
// misc. optional top-level stuff
|
||||
@ -452,12 +442,9 @@ public class MetaInfo
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the piece hashes.
|
||||
*
|
||||
* @return not a copy, do not modify
|
||||
* @since public since 0.9.53, was package private
|
||||
* Returns the piece hashes. Only used by storage so package local.
|
||||
*/
|
||||
public byte[] getPieceHashes()
|
||||
byte[] getPieceHashes()
|
||||
{
|
||||
return piece_hashes;
|
||||
}
|
||||
@ -729,9 +716,8 @@ public class MetaInfo
|
||||
if (infoMap != null)
|
||||
return Collections.unmodifiableMap(infoMap);
|
||||
// we should only get here if serving a magnet on a torrent we created
|
||||
// or on edit torrent save
|
||||
if (_log.shouldDebug())
|
||||
_log.debug("Creating new infomap", new Exception());
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Creating new infomap", new Exception());
|
||||
// otherwise we must create it
|
||||
Map<String, BEValue> info = new HashMap<String, BEValue>();
|
||||
info.put("name", new BEValue(DataHelper.getUTF8(name)));
|
||||
|
@ -279,6 +279,7 @@ class PeerCoordinator implements PeerListener
|
||||
|
||||
/**
|
||||
* Bytes not yet in storage. Does NOT account for skipped files.
|
||||
* Not exact (does not adjust for last piece size).
|
||||
* Returns how many bytes are still needed to get the complete torrent.
|
||||
* @return -1 if in magnet mode
|
||||
*/
|
||||
@ -286,13 +287,8 @@ class PeerCoordinator implements PeerListener
|
||||
{
|
||||
if (metainfo == null | storage == null)
|
||||
return -1;
|
||||
int psz = metainfo.getPieceLength(0);
|
||||
long rv = ((long) storage.needed()) * psz;
|
||||
int last = metainfo.getPieces() - 1;
|
||||
BitField bf = storage.getBitField();
|
||||
if (bf != null && !bf.get(last))
|
||||
rv -= psz - metainfo.getPieceLength(last);
|
||||
return rv;
|
||||
// XXX - Only an approximation.
|
||||
return ((long) storage.needed()) * metainfo.getPieceLength(0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -590,12 +590,8 @@ public class Snark
|
||||
|
||||
private void x_startTorrent() {
|
||||
boolean ok = _util.connect();
|
||||
if (!ok) {
|
||||
if (_util.getContext().isRouterContext())
|
||||
fatalRouter(_util.getString("Unable to connect to I2P"), null);
|
||||
else
|
||||
fatalRouter(_util.getString("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort(), null);
|
||||
}
|
||||
if (!ok)
|
||||
fatalRouter("Unable to connect to I2P", null);
|
||||
if (coordinator == null) {
|
||||
I2PServerSocket serversocket = _util.getServerSocket();
|
||||
if (serversocket == null)
|
||||
@ -1259,9 +1255,7 @@ public class Snark
|
||||
*/
|
||||
private void fatalRouter(String s, Throwable t) throws RouterException {
|
||||
_log.error(s, t);
|
||||
if (!_util.getContext().isRouterContext())
|
||||
System.out.println(s);
|
||||
stopTorrent(true);
|
||||
stopTorrent();
|
||||
if (completeListener != null)
|
||||
completeListener.fatal(this, s);
|
||||
throw new RouterException(s, t);
|
||||
@ -1327,18 +1321,6 @@ public class Snark
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call after editing torrent.
|
||||
* Caller must ensure infohash, files, etc. did not change.
|
||||
*
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void replaceMetaInfo(MetaInfo metainfo) {
|
||||
meta = metainfo;
|
||||
TrackerClient tc = trackerclient;
|
||||
if (tc != null)
|
||||
tc.reinitialize();
|
||||
}
|
||||
|
||||
///////////// Begin StorageListener methods
|
||||
|
||||
|
@ -25,15 +25,11 @@ import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.CoreVersion;
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.ClientApp;
|
||||
import net.i2p.app.ClientAppManager;
|
||||
import net.i2p.app.ClientAppState;
|
||||
import net.i2p.app.NavService;
|
||||
import net.i2p.app.NotificationService;
|
||||
import net.i2p.client.I2PClient;
|
||||
import net.i2p.client.streaming.I2PSocketManager.DisconnectListener;
|
||||
import net.i2p.crypto.SHA1Hash;
|
||||
import net.i2p.crypto.SigType;
|
||||
import net.i2p.data.Base64;
|
||||
@ -61,7 +57,7 @@ import org.klomp.snark.dht.KRPC;
|
||||
/**
|
||||
* Manage multiple snarks
|
||||
*/
|
||||
public class SnarkManager implements CompleteListener, ClientApp, DisconnectListener {
|
||||
public class SnarkManager implements CompleteListener, ClientApp {
|
||||
|
||||
/**
|
||||
* Map of (canonical) filename of the .torrent file to Snark instance.
|
||||
@ -87,8 +83,8 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
private final Log _log;
|
||||
private final UIMessages _messages;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final PeerCoordinatorSet _peerCoordinatorSet;
|
||||
private final ConnectionAcceptor _connectionAcceptor;
|
||||
private PeerCoordinatorSet _peerCoordinatorSet;
|
||||
private ConnectionAcceptor _connectionAcceptor;
|
||||
private Thread _monitor;
|
||||
private volatile boolean _running;
|
||||
private volatile boolean _stopping;
|
||||
@ -129,12 +125,12 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
private static final String PROP_META_ACTIVITY = "activity";
|
||||
|
||||
private static final String CONFIG_FILE_SUFFIX = ".config";
|
||||
public static final String CONFIG_FILE = "i2psnark" + CONFIG_FILE_SUFFIX;
|
||||
private static final String CONFIG_FILE = "i2psnark" + CONFIG_FILE_SUFFIX;
|
||||
private static final String COMMENT_FILE_SUFFIX = ".comments.txt.gz";
|
||||
public static final String PROP_FILES_PUBLIC = "i2psnark.filesPublic";
|
||||
public static final String PROP_OLD_AUTO_START = "i2snark.autoStart"; // oops
|
||||
public static final String PROP_AUTO_START = "i2psnark.autoStart"; // convert in migration to new config file
|
||||
private final boolean DEFAULT_AUTO_START;
|
||||
public static final String DEFAULT_AUTO_START = "false";
|
||||
//public static final String PROP_LINK_PREFIX = "i2psnark.linkPrefix";
|
||||
//public static final String DEFAULT_LINK_PREFIX = "file:///";
|
||||
public static final String PROP_STARTUP_DELAY = "i2psnark.startupDelay";
|
||||
@ -144,8 +140,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
public static final String RC_PROP_UNIVERSAL_THEMING = "routerconsole.universal.theme";
|
||||
public static final String PROP_THEME = "i2psnark.theme";
|
||||
public static final String DEFAULT_THEME = "ubergine";
|
||||
// Translators: Translate "ubergine" as "aubergine" or "eggplant" or "purple"
|
||||
private static final String[] THEMES = new String[] { _x("ubergine"), _x("dark"), _x("light"), _x("vanilla") };
|
||||
private static final String[] THEMES = new String[] { "dark", "light", "ubergine", "vanilla" };
|
||||
/** From CSSHelper */
|
||||
private static final String PROP_DISABLE_OLD = "routerconsole.disableOldThemes";
|
||||
private static final boolean DEFAULT_DISABLE_OLD = true;
|
||||
@ -164,8 +159,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
private static final String PROP_COMMENTS = "i2psnark.comments";
|
||||
/** @since 0.9.31 */
|
||||
private static final String PROP_COMMENTS_NAME = "i2psnark.commentsName";
|
||||
/** @since 0.9.58 */
|
||||
public static final String PROP_MAX_FILES_PER_TORRENT = "i2psnark.maxFilesPerTorrent";
|
||||
|
||||
public static final int MIN_UP_BW = 10;
|
||||
public static final int DEFAULT_MAX_UP_BW = 25;
|
||||
@ -173,14 +166,10 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
public static final int DEFAULT_REFRESH_DELAY_SECS = 15;
|
||||
private static final int DEFAULT_PAGE_SIZE = 50;
|
||||
public static final int DEFAULT_TUNNEL_QUANTITY = 3;
|
||||
public static final int DEFAULT_MAX_FILES_PER_TORRENT = 2000;
|
||||
public static final String CONFIG_DIR_SUFFIX = ".d";
|
||||
private static final String SUBDIR_PREFIX = "s";
|
||||
private static final String B64 = Base64.ALPHABET_I2P;
|
||||
private static final int MAX_MESSAGES = 100;
|
||||
private static final String EXTRA = "";
|
||||
/** @since 0.9.58 */
|
||||
public static final String FULL_VERSION = CoreVersion.VERSION + EXTRA;
|
||||
|
||||
/**
|
||||
* "name", "announceURL=websiteURL" pairs
|
||||
@ -278,10 +267,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
_contextName = ctxName;
|
||||
_log = _context.logManager().getLog(SnarkManager.class);
|
||||
_messages = new UIMessages(MAX_MESSAGES);
|
||||
_util = new I2PSnarkUtil(_context, ctxName, this);
|
||||
_peerCoordinatorSet = new PeerCoordinatorSet();
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util, _peerCoordinatorSet);
|
||||
DEFAULT_AUTO_START = !ctx.isRouterContext();
|
||||
_util = new I2PSnarkUtil(_context, ctxName);
|
||||
String cfile = ctxName + CONFIG_FILE_SUFFIX;
|
||||
File configFile = new File(cfile);
|
||||
if (!configFile.isAbsolute())
|
||||
@ -299,22 +285,15 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
*/
|
||||
public void start() {
|
||||
_running = true;
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if ("i2psnark".equals(_contextName)) {
|
||||
// Register with the ClientAppManager so the rpc plugin can find us
|
||||
// only if default instance
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if (cmgr != null)
|
||||
cmgr.register(this);
|
||||
} else {
|
||||
// Register link with NavHelper
|
||||
if (cmgr != null) {
|
||||
NavService nav = (NavService) cmgr.getRegisteredApp("NavHelper");
|
||||
if (nav != null) {
|
||||
String name = DataHelper.stripHTML(_contextPath.substring(1));
|
||||
nav.registerApp(name, name, _contextPath, null, "/themes/console/images/i2psnark.png");
|
||||
}
|
||||
}
|
||||
}
|
||||
_peerCoordinatorSet = new PeerCoordinatorSet();
|
||||
_connectionAcceptor = new ConnectionAcceptor(_util, _peerCoordinatorSet);
|
||||
_monitor = new I2PAppThread(new DirMonitor(), "Snark DirMonitor", true);
|
||||
_monitor.start();
|
||||
// only if default instance
|
||||
@ -363,18 +342,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DisconnectListener interface
|
||||
* @since 0.9.53
|
||||
*/
|
||||
public void sessionDisconnected() {
|
||||
if (!_context.isRouterContext()) {
|
||||
addMessage(_t("Unable to connect to I2P"));
|
||||
stopAllTorrents(true);
|
||||
_stopping = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the webapp at Jetty shutdown.
|
||||
* Stops all torrents. Does not close the tunnel, so the announces have a chance.
|
||||
@ -394,20 +361,11 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
_connectionAcceptor.halt();
|
||||
_idleChecker.cancel();
|
||||
stopAllTorrents(true);
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if ("i2psnark".equals(_contextName)) {
|
||||
// only if default instance
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if (cmgr != null)
|
||||
cmgr.unregister(this);
|
||||
} else {
|
||||
// Unregister link with NavHelper
|
||||
if (cmgr != null) {
|
||||
NavService nav = (NavService) cmgr.getRegisteredApp("NavHelper");
|
||||
if (nav != null) {
|
||||
String name = DataHelper.stripHTML(_contextPath.substring(1));
|
||||
nav.unregisterApp(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Snark stop() end");
|
||||
@ -505,7 +463,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
}
|
||||
|
||||
public boolean shouldAutoStart() {
|
||||
return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, Boolean.toString(DEFAULT_AUTO_START)));
|
||||
return Boolean.parseBoolean(_config.getProperty(PROP_AUTO_START, DEFAULT_AUTO_START));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -727,7 +685,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
File conf = configFile(_configDir, ih);
|
||||
synchronized(_configLock) { // one lock for all
|
||||
try {
|
||||
I2PSnarkUtil.loadProps(rv, conf);
|
||||
DataHelper.loadProps(rv, conf);
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
return rv;
|
||||
@ -856,7 +814,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
if (!_config.containsKey(PROP_DIR))
|
||||
_config.setProperty(PROP_DIR, _contextName);
|
||||
if (!_config.containsKey(PROP_AUTO_START))
|
||||
_config.setProperty(PROP_AUTO_START, Boolean.toString(DEFAULT_AUTO_START));
|
||||
_config.setProperty(PROP_AUTO_START, DEFAULT_AUTO_START);
|
||||
if (!_config.containsKey(PROP_REFRESH_DELAY))
|
||||
_config.setProperty(PROP_REFRESH_DELAY, Integer.toString(DEFAULT_REFRESH_DELAY_SECS));
|
||||
if (!_config.containsKey(PROP_STARTUP_DELAY))
|
||||
@ -889,7 +847,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
|
||||
/**
|
||||
* Get current theme.
|
||||
* @return String -- the current theme, untranslated
|
||||
* @return String -- the current theme
|
||||
*/
|
||||
public String getTheme() {
|
||||
String theme;
|
||||
@ -954,29 +912,20 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
|
||||
/**
|
||||
* Get all themes
|
||||
* @return Array of all the themes found, non-null, unsorted, untranslated. Not a copy, do not modify.
|
||||
* @return String[] -- Array of all the themes found, non-null, unsorted
|
||||
*/
|
||||
public static String[] getThemes() {
|
||||
return THEMES;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Call from DirMonitor since loadConfig() is called before router I2CP is up.
|
||||
* We also use this as a test that the router is there for standalone.
|
||||
*
|
||||
* @return true if we got a response from the router
|
||||
*/
|
||||
private boolean getBWLimit() {
|
||||
boolean shouldSet = !_config.containsKey(PROP_UPBW_MAX);
|
||||
if (shouldSet || !_context.isRouterContext()) {
|
||||
/** call from DirMonitor since loadConfig() is called before router I2CP is up */
|
||||
private void getBWLimit() {
|
||||
if (!_config.containsKey(PROP_UPBW_MAX)) {
|
||||
int[] limits = BWLimits.getBWLimits(_util.getI2CPHost(), _util.getI2CPPort());
|
||||
if (limits == null)
|
||||
return false;
|
||||
if (shouldSet && limits[1] > 0)
|
||||
if (limits != null && limits[1] > 0)
|
||||
_util.setMaxUpBW(limits[1]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateConfig() {
|
||||
@ -1003,7 +952,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
// _util.setProxy(eepHost, eepPort);
|
||||
_util.setMaxUploaders(getInt(PROP_UPLOADERS_TOTAL, Snark.MAX_TOTAL_UPLOADERS));
|
||||
_util.setMaxUpBW(getInt(PROP_UPBW_MAX, DEFAULT_MAX_UP_BW));
|
||||
_util.setMaxFilesPerTorrent(getInt(PROP_MAX_FILES_PER_TORRENT, DEFAULT_MAX_FILES_PER_TORRENT));
|
||||
_util.setStartupDelay(getInt(PROP_STARTUP_DELAY, DEFAULT_STARTUP_DELAY));
|
||||
_util.setFilesPublic(areFilesPublic());
|
||||
_util.setOpenTrackers(getListConfig(PROP_OPENTRACKERS, DEFAULT_OPENTRACKERS));
|
||||
@ -1483,6 +1431,9 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
}
|
||||
}
|
||||
|
||||
/** hardcoded for sanity. perhaps this should be customizable, for people who increase their ulimit, etc. */
|
||||
public static final int MAX_FILES_PER_TORRENT = 2000;
|
||||
|
||||
/**
|
||||
* Set of canonical .torrent filenames that we are dealing with.
|
||||
* An unsynchronized copy.
|
||||
@ -1625,7 +1576,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
}
|
||||
if (dataDir == null)
|
||||
dataDir = getDataDir();
|
||||
Snark torrent;
|
||||
Snark torrent = null;
|
||||
synchronized (_snarks) {
|
||||
torrent = _snarks.get(filename);
|
||||
}
|
||||
@ -1742,31 +1693,18 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
String link = linkify(torrent);
|
||||
if (!dontAutoStart && shouldAutoStart() && running) {
|
||||
if (!_util.connected()) {
|
||||
String msg = _t("Connecting to I2P");
|
||||
addMessage(msg);
|
||||
if (!_context.isRouterContext())
|
||||
System.out.println(msg);
|
||||
addMessage(_t("Connecting to I2P"));
|
||||
boolean ok = _util.connect();
|
||||
if (!ok) {
|
||||
if (_context.isRouterContext()) {
|
||||
addMessage(_t("Unable to connect to I2P"));
|
||||
} else {
|
||||
msg = _t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort();
|
||||
addMessage(msg);
|
||||
System.out.println(msg);
|
||||
}
|
||||
addMessage(_t("Error connecting to I2P - check your I2CP settings!"));
|
||||
// this would rename the torrent to .BAD
|
||||
//return false;
|
||||
}
|
||||
}
|
||||
torrent.startTorrent();
|
||||
addMessageNoEscape(_t("Torrent added and started: {0}", link));
|
||||
if (!_context.isRouterContext())
|
||||
System.out.println(_t("Torrent added and started: {0}", torrent.getBaseName()));
|
||||
} else {
|
||||
addMessageNoEscape(_t("Torrent added: {0}", link));
|
||||
if (!_context.isRouterContext())
|
||||
System.out.println(_t("Torrent added: {0}", torrent.getBaseName()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -2309,7 +2247,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
if (!subdir.exists())
|
||||
subdir.mkdirs();
|
||||
try {
|
||||
I2PSnarkUtil.storeProps(config, conf);
|
||||
DataHelper.storeProps(config, conf);
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Saved config to " + conf /* , new Exception() */ );
|
||||
} catch (IOException ioe) {
|
||||
@ -2451,11 +2389,8 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
*/
|
||||
private String validateTorrent(MetaInfo info) {
|
||||
List<List<String>> files = info.getFiles();
|
||||
if (files != null && files.size() > _util.getMaxFilesPerTorrent()) {
|
||||
return _t("Too many files in \"{0}\" ({1})!", info.getName(), files.size()) +
|
||||
" - limit is " + _util.getMaxFilesPerTorrent() + ", zip them or set " +
|
||||
PROP_MAX_FILES_PER_TORRENT + '=' + files.size() + " in " +
|
||||
_configFile.getAbsolutePath() + " and restart";
|
||||
if ( (files != null) && (files.size() > MAX_FILES_PER_TORRENT) ) {
|
||||
return _t("Too many files in \"{0}\" ({1})!", info.getName(), files.size());
|
||||
} else if ( (files == null) && (info.getName().endsWith(".torrent")) ) {
|
||||
return _t("Torrent file \"{0}\" cannot end in \".torrent\"!", info.getName());
|
||||
} else if (info.getPieces() <= 0) {
|
||||
@ -2557,13 +2492,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
addMessage(_t("Torrent removed: \"{0}\"", torrent.getBaseName()));
|
||||
}
|
||||
|
||||
/**
|
||||
* This calls monitorTorrents() once a minute.
|
||||
* It also gets the bandwidth limits and loads magnets on first run.
|
||||
* For standalone, it also handles checking that the external router is there,
|
||||
* and restarting torrents once the router appears.
|
||||
*
|
||||
*/
|
||||
private class DirMonitor implements Runnable {
|
||||
public void run() {
|
||||
// don't bother delaying if auto start is false
|
||||
@ -2578,80 +2506,17 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
|
||||
// here because we need to delay until I2CP is up
|
||||
// although the user will see the default until then
|
||||
boolean routerOK = false;
|
||||
getBWLimit();
|
||||
boolean doMagnets = true;
|
||||
while (_running) {
|
||||
File dir = getDataDir();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("Directory Monitor loop over " + dir.getAbsolutePath());
|
||||
if (routerOK &&
|
||||
(_context.isRouterContext() || _util.connected() || _util.isConnecting())) {
|
||||
autostart = shouldAutoStart();
|
||||
} else {
|
||||
// Test if the router is there
|
||||
// For standalone, this will probe the router every 60 seconds if not connected
|
||||
boolean oldOK = routerOK;
|
||||
// standalone, first time only
|
||||
if (doMagnets && !_context.isRouterContext())
|
||||
dtgNotify(Log.INFO, _t("Connecting to I2P") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort());
|
||||
routerOK = getBWLimit();
|
||||
if (routerOK) {
|
||||
autostart = shouldAutoStart();
|
||||
if (autostart && !oldOK && !doMagnets && !_snarks.isEmpty()) {
|
||||
// Start previously added torrents
|
||||
for (Snark snark : _snarks.values()) {
|
||||
Properties config = getConfig(snark);
|
||||
String prop = config.getProperty(PROP_META_RUNNING);
|
||||
if (prop == null || Boolean.parseBoolean(prop)) {
|
||||
if (!_util.connected()) {
|
||||
String msg = _t("Connecting to I2P");
|
||||
addMessage(msg);
|
||||
if (!_context.isRouterContext())
|
||||
dtgNotify(Log.INFO, msg + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort());
|
||||
// getBWLimit() was successful so this should work
|
||||
boolean ok = _util.connect();
|
||||
if (!ok) {
|
||||
if (_context.isRouterContext()) {
|
||||
addMessage(_t("Unable to connect to I2P"));
|
||||
} else {
|
||||
msg = _t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort();
|
||||
addMessage(msg);
|
||||
dtgNotify(Log.ERROR, msg);
|
||||
}
|
||||
routerOK = false;
|
||||
autostart = false;
|
||||
break;
|
||||
} else {
|
||||
if (!_context.isRouterContext()) {
|
||||
msg = "Connected to I2P at " + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort();
|
||||
addMessage(msg);
|
||||
dtgNotify(Log.INFO, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
addMessageNoEscape(_t("Starting up torrent {0}", linkify(snark)));
|
||||
try {
|
||||
snark.startTorrent();
|
||||
} catch (Snark.RouterException re) {
|
||||
// Snark.fatal() will log and call fatal() here for user message before throwing
|
||||
break;
|
||||
} catch (RuntimeException re) {
|
||||
// Snark.fatal() will log and call fatal() here for user message before throwing
|
||||
}
|
||||
}
|
||||
}
|
||||
if (routerOK)
|
||||
addMessage(_t("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
|
||||
}
|
||||
} else {
|
||||
autostart = false;
|
||||
}
|
||||
}
|
||||
boolean ok;
|
||||
try {
|
||||
// Don't let this interfere with .torrent files being added or deleted
|
||||
synchronized (_snarks) {
|
||||
ok = monitorTorrents(dir, autostart);
|
||||
ok = monitorTorrents(dir);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
_log.error("Error in the DirectoryMonitor", e);
|
||||
@ -2665,7 +2530,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
} catch (RuntimeException e) {
|
||||
_log.error("Error in the DirectoryMonitor", e);
|
||||
}
|
||||
if (routerOK && !_snarks.isEmpty())
|
||||
if (!_snarks.isEmpty())
|
||||
addMessage(_t("Up bandwidth limit is {0} KBps", _util.getMaxUpBW()));
|
||||
// To fix bug where files were left behind,
|
||||
// but also good for when user removes snarks when i2p is not running
|
||||
@ -2674,15 +2539,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
// time i2psnark starts. See ticket #1658.
|
||||
if (ok)
|
||||
cleanupTorrentStatus();
|
||||
if (!routerOK) {
|
||||
if (_context.isRouterContext()) {
|
||||
addMessage(_t("Unable to connect to I2P"));
|
||||
} else {
|
||||
String msg = _t("Error connecting to I2P - check your I2CP settings!") + ' ' + _util.getI2CPHost() + ':' + _util.getI2CPPort();
|
||||
addMessage(msg);
|
||||
dtgNotify(Log.ERROR, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
try { Thread.sleep(60*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
@ -2699,12 +2555,8 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
Storage storage = snark.getStorage();
|
||||
if (meta == null || storage == null)
|
||||
return;
|
||||
if (snark.getDownloaded() > 0) {
|
||||
if (snark.getDownloaded() > 0)
|
||||
addMessageNoEscape(_t("Download finished: {0}", linkify(snark)));
|
||||
dtgNotify(Log.INFO,
|
||||
_t("Download finished: {0}", snark.getName()),
|
||||
"/i2psnark/" + linkify(snark));
|
||||
}
|
||||
updateStatus(snark);
|
||||
}
|
||||
|
||||
@ -2794,38 +2646,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
|
||||
// End Snark.CompleteListeners
|
||||
|
||||
/**
|
||||
* Send a notification to the user via desktopgui and,
|
||||
* if standalone, on the console.
|
||||
*
|
||||
* @param priority log level
|
||||
* @param message translated
|
||||
* @since 0.9.54
|
||||
*/
|
||||
private void dtgNotify(int priority, String message) {
|
||||
dtgNotify(priority, message, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a notification to the user via desktopgui and,
|
||||
* if standalone, on the console.
|
||||
*
|
||||
* @param priority log level
|
||||
* @param message translated
|
||||
* @param path in console for more information, starting with /, must be URL-escaped, or null
|
||||
* @since 0.9.54
|
||||
*/
|
||||
private void dtgNotify(int priority, String message, String path) {
|
||||
ClientAppManager cmgr = _context.clientAppManager();
|
||||
if (cmgr != null) {
|
||||
NotificationService ns = (NotificationService) cmgr.getRegisteredApp("desktopgui");
|
||||
if (ns != null)
|
||||
ns.notify("I2PSnark", null, priority, _t("I2PSnark"), message, path);
|
||||
}
|
||||
if (!_context.isRouterContext())
|
||||
System.out.println(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* An HTML link to the file if complete and a single file,
|
||||
* to the directory if not complete or not a single file,
|
||||
@ -2884,10 +2704,9 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
/**
|
||||
* caller must synchronize on _snarks
|
||||
*
|
||||
* @param shouldStart should we autostart the torrents
|
||||
* @return success, false if an error adding any torrent.
|
||||
*/
|
||||
private boolean monitorTorrents(File dir, boolean shouldStart) {
|
||||
private boolean monitorTorrents(File dir) {
|
||||
boolean rv = true;
|
||||
File files[] = dir.listFiles(new FileSuffixFilter(".torrent"));
|
||||
List<String> foundNames = new ArrayList<String>(0);
|
||||
@ -2907,7 +2726,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
//if (_log.shouldLog(Log.DEBUG))
|
||||
// _log.debug("DirMon found: " + DataHelper.toString(foundNames) + " existing: " + DataHelper.toString(existingNames));
|
||||
// lets find new ones first...
|
||||
int count = 0;
|
||||
boolean shouldStart = shouldAutoStart();
|
||||
for (String name : foundNames) {
|
||||
if (existingNames.contains(name)) {
|
||||
// already known. noop
|
||||
@ -2935,10 +2754,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
disableTorrentFile(name);
|
||||
rv = false;
|
||||
}
|
||||
if (shouldStart && (count++ & 0x0f) == 15) {
|
||||
// try to prevent OOMs at startup
|
||||
try { Thread.sleep(250); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Don't remove magnet torrents that don't have a torrent file yet
|
||||
@ -2976,14 +2791,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
return _util.getString(s, o, o2);
|
||||
}
|
||||
|
||||
/**
|
||||
* mark for translation, does not translate
|
||||
* @since 0.9.53
|
||||
*/
|
||||
private static String _x(String s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsorted map of name to Tracker object
|
||||
* Modifiable, not a copy
|
||||
@ -3118,19 +2925,21 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
}
|
||||
|
||||
/**
|
||||
* Always thread it
|
||||
* If not connected, thread it, otherwise inline
|
||||
* @since 0.9.1
|
||||
*/
|
||||
public void startAllTorrents() {
|
||||
if (!_util.connected()) {
|
||||
if (_util.connected()) {
|
||||
startAll();
|
||||
} else {
|
||||
addMessage(_t("Opening the I2P tunnel and starting all torrents."));
|
||||
for (Snark snark : _snarks.values()) {
|
||||
// mark it for the UI
|
||||
snark.setStarting();
|
||||
}
|
||||
(new I2PAppThread(new ThreadedStarter(null), "TorrentStarterAll", true)).start();
|
||||
try { Thread.sleep(200); } catch (InterruptedException ie) {}
|
||||
}
|
||||
(new I2PAppThread(new ThreadedStarter(null), "TorrentStarterAll", true)).start();
|
||||
try { Thread.sleep(200); } catch (InterruptedException ie) {}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -3160,7 +2969,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private void startAll() {
|
||||
int count = 0;
|
||||
for (Snark snark : _snarks.values()) {
|
||||
if (snark.isStopped()) {
|
||||
try {
|
||||
@ -3168,10 +2976,6 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
} catch (RuntimeException re) {
|
||||
// Snark.fatal() will log and call fatal() here for user message before throwing
|
||||
}
|
||||
if ((count++ & 0x0f) == 15) {
|
||||
// try to prevent OOMs
|
||||
try { Thread.sleep(250); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3190,29 +2994,7 @@ public class SnarkManager implements CompleteListener, ClientApp, DisconnectList
|
||||
if (finalShutdown && _log.shouldLog(Log.WARN))
|
||||
_log.warn("SnarkManager final shutdown");
|
||||
int count = 0;
|
||||
Collection<Snark> snarks = _snarks.values();
|
||||
// We do two passes so we shutdown the high-priority snarks first.
|
||||
// Pass 1: All running, incomplete torrents,
|
||||
// to make sure the status gets saved so there will be no recheck on restart.
|
||||
for (Snark snark : snarks) {
|
||||
if (!snark.isStopped()) {
|
||||
Storage storage = snark.getStorage();
|
||||
if (storage != null && !storage.complete()) {
|
||||
if (count == 0)
|
||||
addMessage(_t("Stopping all torrents and closing the I2P tunnel."));
|
||||
count++;
|
||||
if (finalShutdown)
|
||||
snark.stopTorrent(true);
|
||||
else
|
||||
stopTorrent(snark, false);
|
||||
if (count % 8 == 0) {
|
||||
try { Thread.sleep(20); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pass 2: All the rest of the torrents
|
||||
for (Snark snark : snarks) {
|
||||
for (Snark snark : _snarks.values()) {
|
||||
if (!snark.isStopped()) {
|
||||
if (count == 0)
|
||||
addMessage(_t("Stopping all torrents and closing the I2P tunnel."));
|
||||
|
@ -87,7 +87,7 @@ public class Storage implements Closeable
|
||||
/** bigger than this will be rejected */
|
||||
public static final int MAX_PIECE_SIZE = 32*1024*1024;
|
||||
/** The maximum number of pieces in a torrent. */
|
||||
public static final int MAX_PIECES = 64*1024;
|
||||
public static final int MAX_PIECES = 32*1024;
|
||||
public static final long MAX_TOTAL_SIZE = MAX_PIECE_SIZE * (long) MAX_PIECES;
|
||||
public static final int PRIORITY_SKIP = -9;
|
||||
public static final int PRIORITY_NORMAL = 0;
|
||||
@ -288,13 +288,9 @@ public class Storage implements Closeable
|
||||
* @throws IOException if too many total files
|
||||
*/
|
||||
private void addFiles(List<File> l, File f) throws IOException {
|
||||
int max = _util.getMaxFilesPerTorrent();
|
||||
if (!f.isDirectory()) {
|
||||
if (l.size() >= max)
|
||||
throw new IOException(_util.getString("Too many files in \"{0}\" ({1})!", metainfo.getName(), l.size()) +
|
||||
" - limit is " + max + ", zip them or set " +
|
||||
SnarkManager.PROP_MAX_FILES_PER_TORRENT + '=' + l.size() + " in " +
|
||||
SnarkManager.CONFIG_FILE + " and restart");
|
||||
if (l.size() >= SnarkManager.MAX_FILES_PER_TORRENT)
|
||||
throw new IOException("Too many files, limit is " + SnarkManager.MAX_FILES_PER_TORRENT + ", zip them?");
|
||||
l.add(f);
|
||||
} else {
|
||||
File[] files = f.listFiles();
|
||||
|
@ -94,8 +94,7 @@ public class TrackerClient implements Runnable {
|
||||
private static final Hash DSA_ONLY_TRACKER = ConvertToHash.getHash("cfmqlafjfmgkzbt4r3jsfyhgsr5abgxryl6fnz3d3y5a365di5aa.b32.i2p");
|
||||
|
||||
private final I2PSnarkUtil _util;
|
||||
// non-final for reinitialize()
|
||||
private MetaInfo meta;
|
||||
private final MetaInfo meta;
|
||||
private final String infoHash;
|
||||
private final String peerID;
|
||||
private final String additionalTrackerURL;
|
||||
@ -267,22 +266,8 @@ public class TrackerClient implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call after editing torrent
|
||||
* @since 0.9.57
|
||||
*/
|
||||
public synchronized void reinitialize() {
|
||||
if (!_initialized || !stop)
|
||||
return;
|
||||
trackers.clear();
|
||||
backupTrackers.clear();
|
||||
meta = snark.getMetaInfo();
|
||||
setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Do this one time only (not every time it is started).
|
||||
* Unless torrent was edited.
|
||||
* @since 0.9.1
|
||||
*/
|
||||
private void setup() {
|
||||
|
@ -1,753 +0,0 @@
|
||||
package org.klomp.snark;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.client.I2PSession;
|
||||
import net.i2p.client.I2PSessionException;
|
||||
import net.i2p.client.I2PSessionMuxedListener;
|
||||
import net.i2p.client.SendMessageOptions;
|
||||
import net.i2p.client.datagram.I2PDatagramDissector;
|
||||
import net.i2p.client.datagram.I2PDatagramMaker;
|
||||
import net.i2p.client.datagram.I2PInvalidDatagramException;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SimpleTimer2;
|
||||
|
||||
/**
|
||||
* One of these for all trackers and info hashes.
|
||||
* Ref: BEP 15, proposal 160
|
||||
*
|
||||
* The main difference from BEP 15 is that the announce response
|
||||
* contains a 32-byte hash instead of a 4-byte IP and a 2-byte port.
|
||||
*
|
||||
* This implements only "fast mode".
|
||||
* We send only repliable datagrams, and
|
||||
* receive only raw datagrams, as follows:
|
||||
*
|
||||
*<pre>
|
||||
* client tracker type
|
||||
* ------ ------- ----
|
||||
* announce --> repliable
|
||||
* <-- ann resp raw
|
||||
*</pre>
|
||||
*
|
||||
* @since 0.9.53, enabled in 0.9.54
|
||||
*/
|
||||
class UDPTrackerClient implements I2PSessionMuxedListener {
|
||||
|
||||
private final I2PAppContext _context;
|
||||
private final Log _log;
|
||||
/** hook to inject and receive datagrams */
|
||||
private final I2PSession _session;
|
||||
private final I2PSnarkUtil _util;
|
||||
private final Hash _myHash;
|
||||
/** unsigned dgrams */
|
||||
private final int _rPort;
|
||||
/** dest and port to tracker data */
|
||||
private final ConcurrentHashMap<HostPort, Tracker> _trackers;
|
||||
/** our TID to tracker */
|
||||
private final Map<Integer, ReplyWaiter> _sentQueries;
|
||||
private boolean _isRunning;
|
||||
|
||||
public static final int EVENT_NONE = 0;
|
||||
public static final int EVENT_COMPLETED = 1;
|
||||
public static final int EVENT_STARTED = 2;
|
||||
public static final int EVENT_STOPPED = 3;
|
||||
|
||||
private static final int ACTION_CONNECT = 0;
|
||||
private static final int ACTION_ANNOUNCE = 1;
|
||||
private static final int ACTION_SCRAPE = 2;
|
||||
private static final int ACTION_ERROR = 3;
|
||||
|
||||
private static final int SEND_CRYPTO_TAGS = 8;
|
||||
private static final int LOW_CRYPTO_TAGS = 4;
|
||||
|
||||
private static final long DEFAULT_TIMEOUT = 15*1000;
|
||||
private static final long DEFAULT_QUERY_TIMEOUT = 60*1000;
|
||||
private static final long CLEAN_TIME = 163*1000;
|
||||
|
||||
/** in seconds */
|
||||
private static final int DEFAULT_INTERVAL = 60*60;
|
||||
private static final int MIN_INTERVAL = 15*60;
|
||||
private static final int MAX_INTERVAL = 8*60*60;
|
||||
|
||||
private enum WaitState { INIT, SUCCESS, TIMEOUT, FAIL }
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public UDPTrackerClient(I2PAppContext ctx, I2PSession session, I2PSnarkUtil util) {
|
||||
_context = ctx;
|
||||
_session = session;
|
||||
_util = util;
|
||||
_log = ctx.logManager().getLog(UDPTrackerClient.class);
|
||||
_rPort = TrackerClient.PORT - 1;
|
||||
_myHash = session.getMyDestination().calculateHash();
|
||||
_trackers = new ConcurrentHashMap<HostPort, Tracker>(8);
|
||||
_sentQueries = new ConcurrentHashMap<Integer, ReplyWaiter>(32);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Can't be restarted after stopping?
|
||||
*/
|
||||
public synchronized void start() {
|
||||
if (_isRunning)
|
||||
return;
|
||||
_session.addMuxedSessionListener(this, I2PSession.PROTO_DATAGRAM_RAW, _rPort);
|
||||
_isRunning = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop everything.
|
||||
*/
|
||||
public synchronized void stop() {
|
||||
if (!_isRunning)
|
||||
return;
|
||||
_isRunning = false;
|
||||
_session.removeListener(I2PSession.PROTO_DATAGRAM_RAW, _rPort);
|
||||
_trackers.clear();
|
||||
for (ReplyWaiter w : _sentQueries.values()) {
|
||||
w.cancel();
|
||||
}
|
||||
_sentQueries.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Announce and get peers for a torrent.
|
||||
* Blocking!
|
||||
* Caller should run in a thread.
|
||||
*
|
||||
* @param ih the Info Hash (torrent)
|
||||
* @param max maximum number of peers to return
|
||||
* @param maxWait the maximum time to wait (ms) must be greater than 0
|
||||
* @param fast if true, don't wait for dest, no retx, ...
|
||||
* @return null on fail or if fast is true
|
||||
*/
|
||||
public TrackerResponse announce(byte[] ih, byte[] peerID, int max, long maxWait,
|
||||
String toHost, int toPort,
|
||||
long downloaded, long left, long uploaded,
|
||||
int event, boolean fast) {
|
||||
long now = _context.clock().now();
|
||||
long end = now + maxWait;
|
||||
if (toPort < 0)
|
||||
throw new IllegalArgumentException();
|
||||
Tracker tr = getTracker(toHost, toPort);
|
||||
if (tr.getDest(fast) == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("cannot resolve " + tr);
|
||||
return null;
|
||||
}
|
||||
long toWait = end - now;
|
||||
if (!fast)
|
||||
toWait = toWait * 3 / 4;
|
||||
if (toWait < 1000) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("out of time after resolving: " + tr);
|
||||
return null;
|
||||
}
|
||||
if (fast) {
|
||||
toWait = 0;
|
||||
} else {
|
||||
toWait = end - now;
|
||||
if (toWait < 1000) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("out of time after getting conn: " + tr);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
ReplyWaiter w = sendAnnounce(tr, 0, ih, peerID,
|
||||
downloaded, left, uploaded, event, max, toWait);
|
||||
if (fast)
|
||||
return null;
|
||||
if (w == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("initial announce failed: " + tr);
|
||||
return null;
|
||||
}
|
||||
boolean success = waitAndRetransmit(w, end);
|
||||
_sentQueries.remove(w.getID());
|
||||
if (success)
|
||||
return w.getReplyObject();
|
||||
if (_log.shouldInfo())
|
||||
_log.info("announce failed after retx: " + tr);
|
||||
return null;
|
||||
}
|
||||
|
||||
//////// private below here
|
||||
|
||||
/**
|
||||
* @return non-null
|
||||
*/
|
||||
private Tracker getTracker(String host, int port) {
|
||||
Tracker ndp = new Tracker(host, port);
|
||||
Tracker odp = _trackers.putIfAbsent(ndp, ndp);
|
||||
if (odp != null)
|
||||
ndp = odp;
|
||||
return ndp;
|
||||
}
|
||||
|
||||
///// Sending.....
|
||||
|
||||
/**
|
||||
* Send one time with a new tid
|
||||
* @param toWait if <= 0 does not register
|
||||
* @return null on failure or if toWait <= 0
|
||||
*/
|
||||
private ReplyWaiter sendAnnounce(Tracker tr, long connID,
|
||||
byte[] ih, byte[] id,
|
||||
long downloaded, long left, long uploaded,
|
||||
int event, int numWant, long toWait) {
|
||||
int tid = _context.random().nextInt();
|
||||
byte[] payload = sendAnnounce(tr, tid, connID, ih, id, downloaded, left, uploaded, event, numWant);
|
||||
if (payload != null) {
|
||||
if (toWait > 0) {
|
||||
ReplyWaiter rv = new ReplyWaiter(tid, tr, payload, toWait);
|
||||
_sentQueries.put(Integer.valueOf(tid), rv);
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Sent: " + rv + " timeout: " + toWait);
|
||||
return rv;
|
||||
}
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Sent annc " + event + " to " + tr + " no wait");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send one time with given tid
|
||||
* @return the payload or null on failure
|
||||
*/
|
||||
private byte[] sendAnnounce(Tracker tr, int tid, long connID,
|
||||
byte[] ih, byte[] id,
|
||||
long downloaded, long left, long uploaded,
|
||||
int event, int numWant) {
|
||||
byte[] payload = new byte[98];
|
||||
DataHelper.toLong8(payload, 0, connID);
|
||||
DataHelper.toLong(payload, 8, 4, ACTION_ANNOUNCE);
|
||||
DataHelper.toLong(payload, 12, 4, tid);
|
||||
System.arraycopy(ih, 0, payload, 16, 20);
|
||||
System.arraycopy(id, 0, payload, 36, 20);
|
||||
DataHelper.toLong(payload, 56, 8, downloaded);
|
||||
DataHelper.toLong(payload, 64, 8, left);
|
||||
DataHelper.toLong(payload, 72, 8, uploaded);
|
||||
DataHelper.toLong(payload, 80, 4, event);
|
||||
DataHelper.toLong(payload, 92, 4, numWant);
|
||||
DataHelper.toLong(payload, 96, 2, TrackerClient.PORT);
|
||||
boolean rv = sendMessage(tr.getDest(true), tr.getPort(), payload, true);
|
||||
return rv ? payload : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait after initial send, resend if necessary
|
||||
*/
|
||||
private boolean waitAndRetransmit(ReplyWaiter w, long untilTime) {
|
||||
synchronized(w) {
|
||||
while(true) {
|
||||
try {
|
||||
long toWait = untilTime - _context.clock().now();
|
||||
if (toWait <= 0)
|
||||
return false;
|
||||
w.wait(toWait);
|
||||
} catch (InterruptedException ie) {
|
||||
return false;
|
||||
}
|
||||
switch (w.getState()) {
|
||||
case INIT:
|
||||
continue;
|
||||
|
||||
case SUCCESS:
|
||||
return true;
|
||||
|
||||
case FAIL:
|
||||
return false;
|
||||
|
||||
case TIMEOUT:
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Timeout: " + w);
|
||||
long toWait = untilTime - _context.clock().now();
|
||||
if (toWait <= 1000)
|
||||
return false;
|
||||
boolean ok = resend(w, Math.min(toWait, w.getSentTo().getTimeout()));
|
||||
if (!ok)
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend the stored payload
|
||||
* @return success
|
||||
*/
|
||||
private boolean resend(ReplyWaiter w, long toWait) {
|
||||
Tracker tr = w.getSentTo();
|
||||
int port = tr.getPort();
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Resending: " + w + " timeout: " + toWait);
|
||||
boolean rv = sendMessage(tr.getDest(true), port, w.getPayload(), true);
|
||||
if (rv) {
|
||||
_sentQueries.put(Integer.valueOf(w.getID()), w);
|
||||
w.schedule(toWait);
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lowest-level send message call.
|
||||
* @param dest may be null, returns false
|
||||
* @param repliable true for conn request, false for announce
|
||||
* @return success
|
||||
*/
|
||||
private boolean sendMessage(Destination dest, int toPort, byte[] payload, boolean repliable) {
|
||||
if (!_isRunning) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("send failed, not running");
|
||||
return false;
|
||||
}
|
||||
if (dest == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("send failed, no dest");
|
||||
return false;
|
||||
}
|
||||
if (dest.calculateHash().equals(_myHash))
|
||||
throw new IllegalArgumentException("don't send to ourselves");
|
||||
|
||||
if (repliable) {
|
||||
I2PDatagramMaker dgMaker = new I2PDatagramMaker(_session);
|
||||
payload = dgMaker.makeI2PDatagram(payload);
|
||||
if (payload == null) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("DGM fail");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SendMessageOptions opts = new SendMessageOptions();
|
||||
opts.setDate(_context.clock().now() + 60*1000);
|
||||
opts.setTagsToSend(SEND_CRYPTO_TAGS);
|
||||
opts.setTagThreshold(LOW_CRYPTO_TAGS);
|
||||
if (!repliable)
|
||||
opts.setSendLeaseSet(false);
|
||||
try {
|
||||
boolean success = _session.sendMessage(dest, payload, 0, payload.length,
|
||||
repliable ? I2PSession.PROTO_DATAGRAM : I2PSession.PROTO_DATAGRAM_RAW,
|
||||
_rPort, toPort, opts);
|
||||
if (success) {
|
||||
// ...
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("sendMessage fail");
|
||||
}
|
||||
return success;
|
||||
} catch (I2PSessionException ise) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("sendMessage fail", ise);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
///// Reception.....
|
||||
|
||||
/**
|
||||
* @param from dest or null if it didn't come in on signed port
|
||||
*/
|
||||
private void receiveMessage(Destination from, int fromPort, byte[] payload) {
|
||||
if (payload.length < 8) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Got short message: " + payload.length + " bytes");
|
||||
return;
|
||||
}
|
||||
|
||||
int action = (int) DataHelper.fromLong(payload, 0, 4);
|
||||
int tid = (int) DataHelper.fromLong(payload, 4, 4);
|
||||
ReplyWaiter waiter = _sentQueries.remove(Integer.valueOf(tid));
|
||||
if (waiter == null) {
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Rcvd msg with no one waiting: " + tid);
|
||||
return;
|
||||
}
|
||||
|
||||
if (action == ACTION_ANNOUNCE) {
|
||||
receiveAnnounce(waiter, payload);
|
||||
} else if (action == ACTION_ERROR) {
|
||||
receiveError(waiter, payload);
|
||||
} else {
|
||||
// includes ACTION_CONNECT
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Rcvd msg with unknown action: " + action + " for: " + waiter);
|
||||
waiter.gotReply(false);
|
||||
Tracker tr = waiter.getSentTo();
|
||||
tr.gotError();
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveAnnounce(ReplyWaiter waiter, byte[] payload) {
|
||||
Tracker tr = waiter.getSentTo();
|
||||
if (payload.length >= 22) {
|
||||
int interval = Math.min(MAX_INTERVAL, Math.max(MIN_INTERVAL,
|
||||
(int) DataHelper.fromLong(payload, 8, 4)));
|
||||
int leeches = (int) DataHelper.fromLong(payload, 12, 4);
|
||||
int seeds = (int) DataHelper.fromLong(payload, 16, 4);
|
||||
int peers = (int) DataHelper.fromLong(payload, 20, 2);
|
||||
if (22 + (peers * Hash.HASH_LENGTH) > payload.length) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("Short reply");
|
||||
waiter.gotReply(false);
|
||||
tr.gotError();
|
||||
return;
|
||||
}
|
||||
if (_log.shouldInfo())
|
||||
_log.info("Rcvd " + peers + " peers from " + tr);
|
||||
Set<Hash> hashes;
|
||||
if (peers > 0) {
|
||||
hashes = new HashSet<Hash>(peers);
|
||||
for (int off = 20; off < payload.length; off += Hash.HASH_LENGTH) {
|
||||
hashes.add(Hash.create(payload, off));
|
||||
}
|
||||
} else {
|
||||
hashes = Collections.emptySet();
|
||||
}
|
||||
TrackerResponse resp = new TrackerResponse(interval, seeds, leeches, hashes);
|
||||
waiter.gotResponse(resp);
|
||||
tr.setInterval(interval);
|
||||
} else {
|
||||
waiter.gotReply(false);
|
||||
tr.gotError();
|
||||
}
|
||||
}
|
||||
|
||||
private void receiveError(ReplyWaiter waiter, byte[] payload) {
|
||||
String msg;
|
||||
if (payload.length > 8) {
|
||||
msg = DataHelper.getUTF8(payload, 8, payload.length - 8);
|
||||
} else {
|
||||
msg = "";
|
||||
}
|
||||
TrackerResponse resp = new TrackerResponse(msg);
|
||||
waiter.gotResponse(resp);
|
||||
Tracker tr = waiter.getSentTo();
|
||||
tr.gotError();
|
||||
}
|
||||
|
||||
// I2PSessionMuxedListener interface ----------------
|
||||
|
||||
/**
|
||||
* Instruct the client that the given session has received a message
|
||||
*
|
||||
* Will be called only if you register via addMuxedSessionListener().
|
||||
* Will be called only for the proto(s) and toPort(s) you register for.
|
||||
*
|
||||
* @param session session to notify
|
||||
* @param msgId message number available
|
||||
* @param size size of the message - why it's a long and not an int is a mystery
|
||||
* @param proto 1-254 or 0 for unspecified
|
||||
* @param fromPort 1-65535 or 0 for unspecified
|
||||
* @param toPort 1-65535 or 0 for unspecified
|
||||
*/
|
||||
public void messageAvailable(I2PSession session, int msgId, long size, int proto, int fromPort, int toPort) {
|
||||
// TODO throttle
|
||||
try {
|
||||
byte[] payload = session.receiveMessage(msgId);
|
||||
if (payload == null)
|
||||
return;
|
||||
if (toPort == _rPort) {
|
||||
// raw
|
||||
receiveMessage(null, fromPort, payload);
|
||||
} else {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("msg on bad port");
|
||||
}
|
||||
} catch (I2PSessionException e) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("bad msg");
|
||||
}
|
||||
}
|
||||
|
||||
/** for non-muxed */
|
||||
public void messageAvailable(I2PSession session, int msgId, long size) {}
|
||||
|
||||
public void reportAbuse(I2PSession session, int severity) {}
|
||||
|
||||
public void disconnected(I2PSession session) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("UDPTC disconnected");
|
||||
}
|
||||
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("UDPTC got error msg: ", error);
|
||||
}
|
||||
|
||||
public static class TrackerResponse {
|
||||
|
||||
private final int interval, complete, incomplete;
|
||||
private final String error;
|
||||
private final Set<Hash> peers;
|
||||
|
||||
/** success */
|
||||
public TrackerResponse(int interval, int seeds, int leeches, Set<Hash> peers) {
|
||||
this.interval = interval;
|
||||
complete = seeds;
|
||||
incomplete = leeches;
|
||||
this.peers = peers;
|
||||
error = null;
|
||||
}
|
||||
|
||||
/** failure */
|
||||
public TrackerResponse(String errorMsg) {
|
||||
interval = DEFAULT_INTERVAL;
|
||||
complete = 0;
|
||||
incomplete = 0;
|
||||
peers = null;
|
||||
error = errorMsg;
|
||||
}
|
||||
|
||||
public Set<Hash> getPeers() {
|
||||
return peers;
|
||||
}
|
||||
|
||||
public int getPeerCount() {
|
||||
int pc = peers == null ? 0 : peers.size();
|
||||
return Math.max(pc, complete + incomplete - 1);
|
||||
}
|
||||
|
||||
public int getSeedCount() {
|
||||
return complete;
|
||||
}
|
||||
|
||||
public int getLeechCount() {
|
||||
return incomplete;
|
||||
}
|
||||
|
||||
public String getFailureReason() {
|
||||
return error;
|
||||
}
|
||||
|
||||
/** in seconds */
|
||||
public int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
}
|
||||
|
||||
private static class HostPort {
|
||||
|
||||
protected final String host;
|
||||
protected final int port;
|
||||
|
||||
/**
|
||||
* @param port the announce port
|
||||
*/
|
||||
public HostPort(String host, int port) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the announce port
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return host.hashCode() ^ port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !(o instanceof HostPort))
|
||||
return false;
|
||||
HostPort dp = (HostPort) o;
|
||||
return port == dp.port && host.equals(dp.host);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UDP Tracker " + host + ':' + port;
|
||||
}
|
||||
}
|
||||
|
||||
private class Tracker extends HostPort {
|
||||
|
||||
private final Object destLock = new Object();
|
||||
private Destination dest;
|
||||
private long expires;
|
||||
private long lastHeardFrom;
|
||||
private long lastFailed;
|
||||
private int consecFails;
|
||||
private int interval = DEFAULT_INTERVAL;
|
||||
|
||||
private static final long DELAY = 15*1000;
|
||||
|
||||
public Tracker(String host, int port) {
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fast if true, do not lookup
|
||||
* @return dest or null
|
||||
*/
|
||||
public Destination getDest(boolean fast) {
|
||||
synchronized(destLock) {
|
||||
if (dest == null && !fast)
|
||||
dest = _util.getDestination(host);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
/** does not change state */
|
||||
public synchronized void replyTimeout() {
|
||||
consecFails++;
|
||||
lastFailed = _context.clock().now();
|
||||
}
|
||||
|
||||
public synchronized int getInterval() {
|
||||
return interval;
|
||||
}
|
||||
|
||||
/** sets heardFrom; calls notify */
|
||||
public synchronized void setInterval(int interval) {
|
||||
long now = _context.clock().now();
|
||||
lastHeardFrom = now;
|
||||
consecFails = 0;
|
||||
this.interval = interval;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
/** sets heardFrom; calls notify */
|
||||
public synchronized void gotError() {
|
||||
long now = _context.clock().now();
|
||||
lastHeardFrom = now;
|
||||
consecFails = 0;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
/** doubled for each consecutive failure */
|
||||
public synchronized long getTimeout() {
|
||||
return DEFAULT_TIMEOUT << Math.min(consecFails, 3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UDP Tracker " + host + ':' + port + " hasDest? " + (dest != null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for replies
|
||||
*/
|
||||
private class ReplyWaiter extends SimpleTimer2.TimedEvent {
|
||||
private final int tid;
|
||||
private final Tracker sentTo;
|
||||
private final byte[] data;
|
||||
private TrackerResponse replyObject;
|
||||
private WaitState state = WaitState.INIT;
|
||||
|
||||
/**
|
||||
* Either wait on this object with a timeout, or use non-null Runnables.
|
||||
* Any sent data to be remembered may be stored by setSentObject().
|
||||
* Reply object may be in getReplyObject().
|
||||
*/
|
||||
public ReplyWaiter(int tid, Tracker tracker, byte[] payload, long toWait) {
|
||||
super(SimpleTimer2.getInstance(), toWait);
|
||||
this.tid = tid;
|
||||
sentTo = tracker;
|
||||
data = payload;
|
||||
}
|
||||
|
||||
public int getID() {
|
||||
return tid;
|
||||
}
|
||||
|
||||
public Tracker getSentTo() {
|
||||
return sentTo;
|
||||
}
|
||||
|
||||
public byte[] getPayload() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return may be null depending on what happened. Cast to expected type.
|
||||
*/
|
||||
public synchronized TrackerResponse getReplyObject() {
|
||||
return replyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true, we got a reply, and getReplyObject() may contain something.
|
||||
*/
|
||||
public synchronized WaitState getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will notify this.
|
||||
* Also removes from _sentQueries and calls heardFrom().
|
||||
* Sets state to SUCCESS or FAIL.
|
||||
*/
|
||||
public synchronized void gotReply(boolean success) {
|
||||
cancel();
|
||||
_sentQueries.remove(Integer.valueOf(tid));
|
||||
setState(success ? WaitState.SUCCESS : WaitState.FAIL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will notify this and run onReply.
|
||||
* Also removes from _sentQueries and calls heardFrom().
|
||||
*/
|
||||
private synchronized void setState(WaitState state) {
|
||||
this.state = state;
|
||||
this.notifyAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Will notify this.
|
||||
* Also removes from _sentQueries and calls heardFrom().
|
||||
* Sets state to SUCCESS.
|
||||
*/
|
||||
public synchronized void gotResponse(TrackerResponse resp) {
|
||||
replyObject = resp;
|
||||
gotReply(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets state to INIT.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void schedule(long toWait) {
|
||||
state = WaitState.INIT;
|
||||
super.schedule(toWait);
|
||||
}
|
||||
|
||||
/** timer callback on timeout */
|
||||
public synchronized void timeReached() {
|
||||
// don't trump success or failure
|
||||
if (state != WaitState.INIT)
|
||||
return;
|
||||
//if (action == ACTION_CONNECT)
|
||||
// sentTo.connFailed();
|
||||
//else
|
||||
sentTo.replyTimeout();
|
||||
setState(WaitState.TIMEOUT);
|
||||
if (_log.shouldWarn())
|
||||
_log.warn("timeout waiting for reply from " + sentTo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Waiting for ID: " + tid + " to: " + sentTo + " state: " + state;
|
||||
}
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ public class Comment implements Comparable<Comment> {
|
||||
public static final int MAX_NAME_LEN = 32;
|
||||
// same as IRC, more or less
|
||||
private static final int MAX_TEXT_LEN = 512;
|
||||
private static final int BUCKET_SIZE = 4*60*60*1000;
|
||||
private static final int BUCKET_SIZE = 10*60*1000;
|
||||
private static final long TIME_SHRINK = 1000L;
|
||||
private static final int MAX_SKEW = (int) (BUCKET_SIZE / TIME_SHRINK);
|
||||
// 1/1/2005
|
||||
|
@ -100,7 +100,6 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
private final ConcurrentHashMap<NID, Token> _incomingTokens;
|
||||
/** recently unreachable, with lastSeen() as the added-to-blacklist time */
|
||||
private final Set<NID> _blacklist;
|
||||
private SimpleTimer2.TimedEvent _cleaner, _explorer;
|
||||
|
||||
/** hook to inject and receive datagrams */
|
||||
private final I2PSession _session;
|
||||
@ -624,8 +623,6 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
public synchronized void start() {
|
||||
if (_isRunning)
|
||||
return;
|
||||
if (_log.shouldInfo())
|
||||
_log.info("KRPC start", new Exception());
|
||||
_session.addMuxedSessionListener(this, I2PSession.PROTO_DATAGRAM_RAW, _rPort);
|
||||
_session.addMuxedSessionListener(this, I2PSession.PROTO_DATAGRAM, _qPort);
|
||||
_knownNodes.start();
|
||||
@ -633,8 +630,9 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
PersistDHT.loadDHT(this, _dhtFile, _backupDhtFile);
|
||||
// start the explore thread
|
||||
_isRunning = true;
|
||||
_cleaner = new Cleaner();
|
||||
_explorer = new Explorer(5*1000);
|
||||
// no need to keep ref, it will eventually stop
|
||||
new Cleaner();
|
||||
new Explorer(5*1000);
|
||||
_txPkts.set(0);
|
||||
_rxPkts.set(0);
|
||||
_txBytes.set(0);
|
||||
@ -650,10 +648,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
if (!_isRunning)
|
||||
return;
|
||||
_isRunning = false;
|
||||
if (_log.shouldInfo())
|
||||
_log.info("KRPC stop", new Exception());
|
||||
_cleaner.cancel();
|
||||
_explorer.cancel();
|
||||
// FIXME stop the explore thread
|
||||
// unregister port listeners
|
||||
_session.removeListener(I2PSession.PROTO_DATAGRAM, _qPort);
|
||||
_session.removeListener(I2PSession.PROTO_DATAGRAM_RAW, _rPort);
|
||||
@ -1645,7 +1640,6 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
public void disconnected(I2PSession session) {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("KRPC disconnected");
|
||||
stop();
|
||||
}
|
||||
|
||||
public void errorOccurred(I2PSession session, String message, Throwable error) {
|
||||
@ -1766,7 +1760,7 @@ public class KRPC implements I2PSessionMuxedListener, DHT {
|
||||
}
|
||||
if (_log.shouldLog(Log.INFO))
|
||||
_log.info("Explore of " + keys.size() + " buckets done, new size: " + _knownNodes.size());
|
||||
_explorer = new Explorer(EXPLORE_TIME);
|
||||
new Explorer(EXPLORE_TIME);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,17 +31,15 @@ public class ConfigUIHelper {
|
||||
{ "az", "az", "Azerbaijani", null },
|
||||
{ "cs", "cz", "Čeština", null },
|
||||
{ "zh", "cn", "Chinese 中文", null },
|
||||
{ "zh_TW", "tw", "Chinese 中文", "Taiwan" },
|
||||
{ "da", "dk", "Dansk", null },
|
||||
//{ "zh_TW", "tw", "Chinese 中文", "Taiwan" },
|
||||
//{ "da", "dk", "Dansk", null },
|
||||
{ "de", "de", "Deutsch", null },
|
||||
//{ "et", "ee", "Eesti", null },
|
||||
{ "en", "us", "English", null },
|
||||
{ "es", "es", "Español", null },
|
||||
{ "es_AR", "ar", "Español" ,"Argentina" },
|
||||
{ "fa", "ir", "Persian فارسی", null },
|
||||
{ "fr", "fr", "Français", null },
|
||||
//{ "gl", "lang_gl", "Galego", null },
|
||||
{ "el", "gr", "Greek Ελληνικά", null },
|
||||
//{ "el", "gr", "Greek Ελληνικά", null },
|
||||
{ "in", "id", "bahasa Indonesia", null },
|
||||
{ "it", "it", "Italiano", null },
|
||||
{ "ja", "jp", "Japanese 日本語", null },
|
||||
|
@ -1,25 +1,13 @@
|
||||
package org.klomp.snark.standalone;
|
||||
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
|
||||
import net.i2p.I2PAppContext;
|
||||
import net.i2p.app.MenuCallback;
|
||||
import net.i2p.app.MenuHandle;
|
||||
import net.i2p.app.MenuService;
|
||||
import net.i2p.apps.systray.UrlLauncher;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.desktopgui.ExternalMain;
|
||||
import net.i2p.jetty.I2PLogger;
|
||||
import net.i2p.jetty.JettyStart;
|
||||
import net.i2p.util.I2PAppThread;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
import org.klomp.snark.SnarkManager;
|
||||
|
||||
/**
|
||||
* @since moved from ../web and fixed in 0.9.27
|
||||
@ -32,7 +20,6 @@ public class RunStandalone {
|
||||
private String _host = "127.0.0.1";
|
||||
private static RunStandalone _instance;
|
||||
static final File APP_CONFIG_FILE = new File("i2psnark-appctx.config");
|
||||
private static final String PROP_DTG_ENABLED = "desktopgui.enabled";
|
||||
|
||||
private RunStandalone(String args[]) throws Exception {
|
||||
Properties p = new Properties();
|
||||
@ -42,13 +29,6 @@ public class RunStandalone {
|
||||
} catch (IOException ioe) {}
|
||||
}
|
||||
_context = new I2PAppContext(p);
|
||||
// Do this after we have a context
|
||||
// To take effect, must be set before any Jetty classes are loaded
|
||||
try {
|
||||
Log.setLog(new I2PLogger(_context));
|
||||
} catch (Throwable t) {
|
||||
System.err.println("INFO: I2P Jetty logging class not found, logging to stdout");
|
||||
}
|
||||
File base = _context.getBaseDir();
|
||||
File xml = new File(base, "jetty-i2psnark.xml");
|
||||
_jettyStart = new JettyStart(_context, null, new String[] { xml.getAbsolutePath() } );
|
||||
@ -76,20 +56,13 @@ public class RunStandalone {
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
String url = "http://" + _host + ':' + _port + "/i2psnark/";
|
||||
System.out.println("Starting i2psnark " + SnarkManager.FULL_VERSION + " at " + url);
|
||||
MenuService dtg = startTrayApp();
|
||||
_jettyStart.startup();
|
||||
String url = "http://" + _host + ':' + _port + "/i2psnark/";
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ie) {}
|
||||
String p = _context.getProperty("routerconsole.browser");
|
||||
if (!("/bin/false".equals(p) || "NUL".equals(p))) {
|
||||
UrlLauncher launch = new UrlLauncher(_context, null, new String[] { url } );
|
||||
launch.startup();
|
||||
}
|
||||
if (dtg != null)
|
||||
dtg.addMenu("Shutdown I2PSnark", new StandaloneStopper(dtg));
|
||||
UrlLauncher launch = new UrlLauncher(_context, null, new String[] { url } );
|
||||
launch.startup();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -109,70 +82,4 @@ public class RunStandalone {
|
||||
} catch (InterruptedException ie) {}
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.54 adapted from RouterConsoleRunner
|
||||
*/
|
||||
private static boolean isSystrayEnabled(I2PAppContext context) {
|
||||
if (GraphicsEnvironment.isHeadless())
|
||||
return false;
|
||||
// default false except on OSX and Windows,
|
||||
// and on Linux KDE and LXDE.
|
||||
// Xubuntu XFCE works but doesn't look very good
|
||||
// Ubuntu Unity was far too buggy to enable
|
||||
// Ubuntu GNOME does not work, SystemTray.isSupported() returns false
|
||||
String xdg = System.getenv("XDG_CURRENT_DESKTOP");
|
||||
boolean dflt = SystemVersion.isWindows() ||
|
||||
SystemVersion.isMac() ||
|
||||
//"XFCE".equals(xdg) ||
|
||||
"KDE".equals(xdg) ||
|
||||
"LXDE".equals(xdg);
|
||||
return context.getProperty(PROP_DTG_ENABLED, dflt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.54 adapted from RouterConsoleRunner
|
||||
* @return null on failure
|
||||
*/
|
||||
private MenuService startTrayApp() {
|
||||
try {
|
||||
if (isSystrayEnabled(_context)) {
|
||||
System.setProperty("java.awt.headless", "false");
|
||||
ExternalMain dtg = new ExternalMain(_context, _context.clientAppManager(), null);
|
||||
dtg.startup();
|
||||
return dtg;
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback when shutdown is clicked in systray
|
||||
* @since 0.9.60
|
||||
*/
|
||||
public static class StandaloneStopper implements MenuCallback {
|
||||
private final MenuService _ms;
|
||||
|
||||
public StandaloneStopper(MenuService ms) { _ms = ms; }
|
||||
|
||||
public void clicked(MenuHandle menu) {
|
||||
_ms.disableMenu(menu);
|
||||
_ms.updateMenu("I2PSnark shutting down", menu);
|
||||
Thread t = new I2PAppThread(new StopperThread(), "Snark Stopper", true);
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Threaded shutdown
|
||||
* @since 0.9.60
|
||||
*/
|
||||
public static class StopperThread implements Runnable {
|
||||
public void run() {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,14 +3,12 @@ package org.klomp.snark.web;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.Collator;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@ -24,7 +22,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
@ -38,10 +35,8 @@ import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Hash;
|
||||
import net.i2p.servlet.util.ServletUtil;
|
||||
import net.i2p.util.FileUtil;
|
||||
import net.i2p.util.Log;
|
||||
import net.i2p.util.SecureFile;
|
||||
import net.i2p.util.SecureFileOutputStream;
|
||||
import net.i2p.util.SystemVersion;
|
||||
import net.i2p.util.Translate;
|
||||
import net.i2p.util.UIMessages;
|
||||
@ -57,8 +52,6 @@ import org.klomp.snark.Storage;
|
||||
import org.klomp.snark.Tracker;
|
||||
import org.klomp.snark.TrackerClient;
|
||||
import org.klomp.snark.URIUtil;
|
||||
import org.klomp.snark.bencode.BEValue;
|
||||
import org.klomp.snark.bencode.InvalidBEncodingException;
|
||||
import org.klomp.snark.dht.DHT;
|
||||
import org.klomp.snark.comments.Comment;
|
||||
import org.klomp.snark.comments.CommentSet;
|
||||
@ -214,7 +207,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
req.setCharacterEncoding("UTF-8");
|
||||
|
||||
String pOverride = _manager.util().connected() ? null : "";
|
||||
String peerString = getQueryString(req, pOverride, null, null, "");
|
||||
String peerString = getQueryString(req, pOverride, null, null);
|
||||
|
||||
String cspNonce = Integer.toHexString(_context.random().nextInt());
|
||||
// AJAX for mainsection
|
||||
@ -258,12 +251,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
} else {
|
||||
String base = addPaths(req.getRequestURI(), "/");
|
||||
boolean showEdit = req.getParameter("showEdit") != null;
|
||||
String listing = getListHTML(resource, base, true, method.equals("POST") ? req.getParameterMap() : null,
|
||||
req.getParameter("sort"), showEdit);
|
||||
req.getParameter("sort"));
|
||||
if (method.equals("POST")) {
|
||||
// P-R-G
|
||||
sendRedirect(req, resp, showEdit ? "?showEdit" : "");
|
||||
sendRedirect(req, resp, "");
|
||||
} else if (listing != null) {
|
||||
setHTMLHeaders(resp, cspNonce, true);
|
||||
resp.getWriter().write(listing);
|
||||
@ -322,12 +314,9 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
|
||||
// we want it to go to the base URI so we don't refresh with some funky action= value
|
||||
int delay = 0;
|
||||
if (isConfigure) {
|
||||
out.write("<script src=\".resources/js/configui.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>\n");
|
||||
} else {
|
||||
if (!isConfigure) {
|
||||
delay = _manager.getRefreshDelaySeconds();
|
||||
// init for search even if refresh disabled
|
||||
//if (delay > 0) {
|
||||
if (delay > 0) {
|
||||
String jsPfx = _context.isRouterContext() ? "" : ".resources";
|
||||
String downMsg = _context.isRouterContext() ? _t("Router is down") : _t("I2PSnark has stopped");
|
||||
// fallback to metarefresh when javascript is disabled
|
||||
@ -338,13 +327,12 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
"var ajaxDelay = " + (delay * 1000) + ";\n" +
|
||||
"</script>\n" +
|
||||
"<script src=\".resources/js/initajax.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>\n");
|
||||
//}
|
||||
}
|
||||
out.write("<script nonce=\"" + cspNonce + "\" type=\"text/javascript\">\n" +
|
||||
"var deleteMessage1 = \"" + _t("Are you sure you want to delete the file \\''{0}\\'' (downloaded data will not be deleted) ?") + "\";\n" +
|
||||
"var deleteMessage2 = \"" + _t("Are you sure you want to delete the torrent \\''{0}\\'' and all downloaded data?") + "\";\n" +
|
||||
"</script>\n" +
|
||||
"<script src=\".resources/js/delete.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>\n" +
|
||||
"<script src=\".resources/js/search.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>\n");
|
||||
"<script src=\".resources/js/delete.js?" + CoreVersion.VERSION + "\" type=\"text/javascript\"></script>\n");
|
||||
}
|
||||
out.write(HEADER_A + _themePath + HEADER_B);
|
||||
|
||||
@ -355,24 +343,25 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write("</head>\n" +
|
||||
"<body>" +
|
||||
"<center>");
|
||||
List<Tracker> sortedTrackers = null;
|
||||
if (isConfigure) {
|
||||
out.write("<div class=\"snarknavbar\"><a href=\"" + _contextPath + "/\" title=\"");
|
||||
out.write(_t("Torrents"));
|
||||
out.write("\" class=\"snarkNav nav_main\">");
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
out.write(_t("I2PSnark"));
|
||||
else
|
||||
out.write(_contextName);
|
||||
out.write("</a>");
|
||||
} else {
|
||||
out.write("<div class=\"snarknavbar\"><a href=\"" + _contextPath + '/' + peerString + "\" title=\"");
|
||||
out.write(_t("Refresh page"));
|
||||
}
|
||||
out.write("\" class=\"snarkNav nav_main\">");
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
out.write(_t("I2PSnark"));
|
||||
else
|
||||
out.write(_contextName);
|
||||
if (!_context.isRouterContext()) {
|
||||
out.write(' ' + SnarkManager.FULL_VERSION);
|
||||
}
|
||||
out.write("</a>");
|
||||
List<Tracker> sortedTrackers = null;
|
||||
if (!isConfigure) {
|
||||
out.write("\" class=\"snarkNav nav_main\">");
|
||||
if (_contextName.equals(DEFAULT_NAME))
|
||||
out.write(_t("I2PSnark"));
|
||||
else
|
||||
out.write(_contextName);
|
||||
out.write("</a>\n");
|
||||
sortedTrackers = _manager.getSortedTrackers();
|
||||
if (_context.isRouterContext() && _manager.hasModifiedTrackers()) {
|
||||
for (Tracker t : sortedTrackers) {
|
||||
@ -380,30 +369,15 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
continue;
|
||||
if (_manager.util().isKnownOpenTracker(t.announceURL))
|
||||
continue;
|
||||
out.write(" <a href=\"" + t.baseURL + "\" class=\"snarkNav nav_tracker\" target=\"_blank\">" + t.name + "</a>\n");
|
||||
out.write(" <a href=\"" + t.baseURL + "\" class=\"snarkNav nav_tracker\" target=\"_blank\">" + t.name + "</a>");
|
||||
}
|
||||
}
|
||||
}
|
||||
// end snarkNavBar
|
||||
out.write("</div>\n");
|
||||
|
||||
if (!isConfigure) {
|
||||
String search = req.getParameter("nf_s");
|
||||
if (_manager.getTorrents().size() > 1 || (search != null && search.length() > 0)) {
|
||||
out.write("<form class=\"search\" id = \"search\" action=\"" + _contextPath + "\" method=\"GET\">" +
|
||||
"<input type=\"text\" name=\"nf_s\" size=\"20\" class=\"search\" id=\"searchbox\"");
|
||||
if (search != null)
|
||||
out.write(" value=\"" + DataHelper.escapeHTML(search) + '"');
|
||||
out.write(">" +
|
||||
"<a class=\"cancel\" id=\"searchcancel\" href=\"" + _contextPath + "/\"></a>" +
|
||||
"</form>\n");
|
||||
}
|
||||
}
|
||||
|
||||
String newURL = req.getParameter("newURL");
|
||||
if (newURL != null && newURL.trim().length() > 0 && req.getMethod().equals("GET"))
|
||||
_manager.addMessage(_t("Click \"Add torrent\" button to fetch torrent"));
|
||||
out.write("<div id=\"page\" class=\"page\"><div id=\"mainsection\" class=\"mainsection\">");
|
||||
out.write("<div class=\"page\"><div id=\"mainsection\" class=\"mainsection\">");
|
||||
|
||||
writeMessages(out, isConfigure, peerString);
|
||||
|
||||
@ -505,17 +479,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
boolean isDegraded = ua != null && ServletUtil.isTextBrowser(ua);
|
||||
boolean noThinsp = isDegraded || (ua != null && ua.startsWith("Opera"));
|
||||
|
||||
// search
|
||||
boolean isSearch = false;
|
||||
String search = req.getParameter("nf_s");
|
||||
if (search != null && search.length() > 0) {
|
||||
List<Snark> matches = search(search, snarks);
|
||||
if (matches != null) {
|
||||
snarks = matches;
|
||||
isSearch = true;
|
||||
}
|
||||
}
|
||||
|
||||
// pages
|
||||
int start = 0;
|
||||
int total = snarks.size();
|
||||
@ -771,8 +734,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write(_t("Unreadable") + ": " + DataHelper.escapeHTML(dd.toString()));
|
||||
} else if (!canWrite) {
|
||||
out.write(_t("No write permissions for data directory") + ": " + DataHelper.escapeHTML(dd.toString()));
|
||||
} else if (isSearch) {
|
||||
out.write(_t("No torrents found."));
|
||||
} else {
|
||||
out.write(_t("No torrents loaded."));
|
||||
}
|
||||
@ -847,48 +808,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
return start == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* search torrents for matching terms
|
||||
*
|
||||
* @param search non-null and %-encoded, will be decoded here
|
||||
* @param snarks unmodified, order will be honored
|
||||
* @return null if not a valid search, or matching torrents in same order, possibly empty
|
||||
* @since 0.9.58
|
||||
*/
|
||||
private static List<Snark> search(String search, Collection<Snark> snarks) {
|
||||
try {
|
||||
search = decodePath(search);
|
||||
} catch (IOException ioe) {
|
||||
return null;
|
||||
}
|
||||
List<String> searchList = null;
|
||||
String[] terms = DataHelper.split(search, " ");
|
||||
for (int i = 0; i < terms.length; i++) {
|
||||
String term = terms[i];
|
||||
if (term.length() > 0) {
|
||||
if (searchList == null)
|
||||
searchList = new ArrayList<String>(4);
|
||||
searchList.add(term.toLowerCase(Locale.US));
|
||||
}
|
||||
}
|
||||
if (searchList == null)
|
||||
return null;
|
||||
List<Snark> matches = new ArrayList<Snark>(32);
|
||||
for (Snark snark : snarks) {
|
||||
String lcname = snark.getBaseName().toLowerCase(Locale.US);
|
||||
// search for any term (OR)
|
||||
for (int j = 0; j < searchList.size(); j++) {
|
||||
String term = searchList.get(j);
|
||||
if (lcname.contains(term)) {
|
||||
matches.add(snark);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* hidden inputs for nonce and paramters p, st, and sort
|
||||
*
|
||||
@ -930,19 +849,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (action != null) {
|
||||
buf.append("<input type=\"hidden\" name=\"action\" value=\"")
|
||||
.append(action).append("\" >\n");
|
||||
} else {
|
||||
// for buttons, keep the search term
|
||||
String sParam = req.getParameter("nf_s");
|
||||
if (sParam != null) {
|
||||
buf.append("<input type=\"hidden\" name=\"nf_s\" value=\"")
|
||||
.append(DataHelper.escapeHTML(sParam)).append("\" >\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build HTML-escaped and stripped query string.
|
||||
* Keeps any existing search param.
|
||||
* Build HTML-escaped and stripped query string
|
||||
*
|
||||
* @param p override or "" for default or null to keep the same as in req
|
||||
* @param st override or "" for default or null to keep the same as in req
|
||||
@ -951,14 +862,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
* @since 0.9.16
|
||||
*/
|
||||
private static String getQueryString(HttpServletRequest req, String p, String st, String so) {
|
||||
return getQueryString(req, p, st, so, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param s search param override or "" for default or null to keep the same as in req
|
||||
* @since 0.9.58
|
||||
*/
|
||||
private static String getQueryString(HttpServletRequest req, String p, String st, String so, String s) {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
if (p == null) {
|
||||
p = req.getParameter("p");
|
||||
@ -991,18 +894,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("&st=");
|
||||
buf.append(st);
|
||||
}
|
||||
if (s == null) {
|
||||
s = req.getParameter("nf_s");
|
||||
if (s != null)
|
||||
s = DataHelper.escapeHTML(s);
|
||||
}
|
||||
if (s != null && !s.equals("")) {
|
||||
if (buf.length() <= 0)
|
||||
buf.append("?nf_s=");
|
||||
else
|
||||
buf.append("&nf_s=");
|
||||
buf.append(s);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@ -1439,9 +1330,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
} else if ("Create".equals(action)) {
|
||||
String baseData = req.getParameter("nofilter_baseFile");
|
||||
if (baseData != null && baseData.trim().length() > 0) {
|
||||
// drag and drop, no js
|
||||
if (baseData.startsWith("file://"))
|
||||
baseData = baseData.substring(7);
|
||||
File baseFile = new File(baseData.trim());
|
||||
if (!baseFile.isAbsolute())
|
||||
baseFile = new File(_manager.getDataDir(), baseData);
|
||||
@ -1560,36 +1448,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
_manager.addMessage(_t("Error creating torrent - you must enter a file or directory"));
|
||||
}
|
||||
} else if ("StopAll".equals(action)) {
|
||||
String search = req.getParameter("nf_s");
|
||||
if (search != null && search.length() > 0) {
|
||||
List<Snark> matches = search(search, _manager.getTorrents());
|
||||
if (matches != null) {
|
||||
for (Snark snark : matches) {
|
||||
_manager.stopTorrent(snark, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
_manager.stopAllTorrents(false);
|
||||
} else if ("StartAll".equals(action)) {
|
||||
String search = req.getParameter("nf_s");
|
||||
if (search != null && search.length() > 0) {
|
||||
List<Snark> matches = search(search, _manager.getTorrents());
|
||||
if (matches != null) {
|
||||
// TODO thread it
|
||||
int count = 0;
|
||||
for (Snark snark : matches) {
|
||||
if (!snark.isStopped())
|
||||
continue;
|
||||
_manager.startTorrent(snark);
|
||||
if ((count++ & 0x0f) == 15) {
|
||||
// try to prevent OOMs
|
||||
try { Thread.sleep(250); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
_manager.startAllTorrents();
|
||||
} else if ("Clear".equals(action)) {
|
||||
String sid = req.getParameter("id");
|
||||
@ -1895,7 +1755,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
":</b> " + curPeers + thinsp(noThinsp) +
|
||||
ngettext("1 peer", "{0} peers", knownPeers);
|
||||
} else if (isRunning && curPeers > 0 && !showPeers) {
|
||||
statusString = toThemeImg("stalled", "", _t("Stalled") + " (" + ngettext("Connected to {0} peer", "Connected to {0} peers", curPeers) + ")") + "</td>" +
|
||||
statusString = toThemeImg("stalled", "", _t("Stalled") + " (" + ngettext("Connected to {0} peer", "Connected to {0} peers", curPeers)) + "</td>" +
|
||||
"<td class=\"snarkTorrentStatus\"><b>" + _t("Stalled") +
|
||||
":</b> <a href=\"" + uri + getQueryString(req, b64, null, null) + '#' + b64Short + "\">" +
|
||||
curPeers + thinsp(noThinsp) +
|
||||
@ -2137,73 +1997,30 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
if (ch.startsWith("WebSeed@")) {
|
||||
out.write(ch);
|
||||
} else {
|
||||
// most clients start -xx, see
|
||||
// BT spec or libtorrent identify_client.cpp
|
||||
// Base64 encode -xx
|
||||
// Anything starting with L is -xx and has an Az version
|
||||
// snark is 9 nulls followed by 3 3 3 (binary), see Snark
|
||||
// PeerID.toString() skips nulls
|
||||
// Base64 encode '\3\3\3' = AwMD
|
||||
boolean addVersion = true;
|
||||
ch = ch.substring(0, 4);
|
||||
String client;
|
||||
if ("AwMD".equals(ch))
|
||||
client = _t("I2PSnark");
|
||||
else if ("LUJJ".equals(ch))
|
||||
client = "BiglyBT";
|
||||
client = "BiglyBT" + getAzVersion(pid.getID());
|
||||
else if ("LUFa".equals(ch))
|
||||
client = "Vuze";
|
||||
client = "Vuze" + getAzVersion(pid.getID());
|
||||
else if ("LVhE".equals(ch))
|
||||
client = "XD";
|
||||
else if (ch.startsWith("LV")) // LVCS 1.0.2?; LVRS 1.0.4
|
||||
client = "Transmission";
|
||||
else if ("LUtU".equals(ch))
|
||||
client = "KTorrent";
|
||||
// libtorrent and downstreams
|
||||
// https://www.libtorrent.org/projects.html
|
||||
else if ("LURF".equals(ch)) // DL
|
||||
client = "Deluge";
|
||||
else if ("LXFC".equals(ch)) // qB
|
||||
client = "qBitorrent";
|
||||
else if ("LUxU".equals(ch)) // LT
|
||||
client = "libtorrent";
|
||||
// ancient below here
|
||||
client = "XD" + getAzVersion(pid.getID());
|
||||
else if ("ZV".equals(ch.substring(2,4)) || "VUZP".equals(ch))
|
||||
client = "Robert" + getRobtVersion(pid.getID());
|
||||
else if (ch.startsWith("LV")) // LVCS 1.0.2?; LVRS 1.0.4
|
||||
client = "Transmission" + getAzVersion(pid.getID());
|
||||
else if ("LUtU".equals(ch))
|
||||
client = "KTorrent" + getAzVersion(pid.getID());
|
||||
else if ("CwsL".equals(ch))
|
||||
client = "I2PSnarkXL";
|
||||
else if ("BFJT".equals(ch))
|
||||
client = "I2PRufus";
|
||||
else if ("TTMt".equals(ch))
|
||||
client = "I2P-BT";
|
||||
else {
|
||||
// get client + version from handshake
|
||||
client = null;
|
||||
Map<String, BEValue> handshake = peer.getHandshakeMap();
|
||||
if (handshake != null) {
|
||||
BEValue bev = handshake.get("v");
|
||||
if (bev != null) {
|
||||
try {
|
||||
String s = bev.getString();
|
||||
if (s.length() > 0) {
|
||||
if (s.length() > 64)
|
||||
s = s.substring(0, 64);
|
||||
client = DataHelper.escapeHTML(s);
|
||||
addVersion = false;
|
||||
}
|
||||
} catch (InvalidBEncodingException ibee) {}
|
||||
}
|
||||
}
|
||||
if (client == null)
|
||||
client = _t("Unknown") + " (" + ch + ')';
|
||||
}
|
||||
|
||||
if (addVersion) {
|
||||
byte[] id = pid.getID();
|
||||
if (id != null && id[0] == '-')
|
||||
client += getAzVersion(id);
|
||||
}
|
||||
|
||||
else
|
||||
client = _t("Unknown") + " (" + ch + ')';
|
||||
out.write(client + " <tt title=\"");
|
||||
out.write(_t("Destination (identity) of peer"));
|
||||
out.write("\">" + peer.toString().substring(5, 9)+ "</tt>");
|
||||
@ -2488,19 +2305,19 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
|
||||
out.write("<hr>\n<table border=\"0\"><tr><td>");
|
||||
out.write(_t("From URL"));
|
||||
out.write(":<td><input type=\"text\" id=\"nofilter_newURL\" name=\"nofilter_newURL\" size=\"85\" value=\"" + newURL + "\" spellcheck=\"false\"" +
|
||||
out.write(":<td><input type=\"text\" name=\"nofilter_newURL\" size=\"85\" value=\"" + newURL + "\" spellcheck=\"false\"" +
|
||||
" title=\"");
|
||||
out.write(_t("Enter the torrent file download URL (I2P only), magnet link, or info hash"));
|
||||
out.write("\">\n");
|
||||
// not supporting from file at the moment, since the file name passed isn't always absolute (so it may not resolve)
|
||||
//out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>");
|
||||
out.write("<input type=\"submit\" id=\"addButton\" class=\"add\" value=\"");
|
||||
out.write("<input type=\"submit\" class=\"add\" value=\"");
|
||||
out.write(_t("Add torrent"));
|
||||
out.write("\" name=\"foo\" ><br>\n" +
|
||||
"<tr><td>");
|
||||
|
||||
out.write(_t("Data dir"));
|
||||
out.write(":<td><input type=\"text\" id=\"nofilter_newDir\" name=\"nofilter_newDir\" size=\"85\" value=\"\" spellcheck=\"false\"" +
|
||||
out.write(":<td><input type=\"text\" name=\"nofilter_newDir\" size=\"85\" value=\"\" spellcheck=\"false\"" +
|
||||
" title=\"");
|
||||
out.write(_t("Enter the directory to save the data in (default {0})", _manager.getDataDir().getAbsolutePath()));
|
||||
out.write("\"></td></tr>\n");
|
||||
@ -2526,11 +2343,11 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
//out.write("From file: <input type=\"file\" name=\"newFile\" size=\"50\" value=\"" + newFile + "\" /><br>\n");
|
||||
out.write(_t("Data to seed"));
|
||||
out.write(":<td>"
|
||||
+ "<input type=\"text\" id=\"nofilter_baseFile\" name=\"nofilter_baseFile\" size=\"85\" value=\""
|
||||
+ "<input type=\"text\" name=\"nofilter_baseFile\" size=\"85\" value=\""
|
||||
+ "\" spellcheck=\"false\" title=\"");
|
||||
out.write(_t("File or directory to seed (full path or within the directory {0} )",
|
||||
_manager.getDataDir().getAbsolutePath() + File.separatorChar));
|
||||
out.write("\" > <input type=\"submit\" id=\"createButton\" class=\"create\" value=\"");
|
||||
out.write("\" > <input type=\"submit\" class=\"create\" value=\"");
|
||||
out.write(_t("Create torrent"));
|
||||
out.write("\" name=\"foo\" >" +
|
||||
"<tr><td>\n");
|
||||
@ -2681,7 +2498,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write(_t("Theme"));
|
||||
out.write(":<td colspan=\"2\">");
|
||||
if (_manager.getUniversalTheming()) {
|
||||
out.write("<select id=\"theme\" name=\"theme\" disabled=\"disabled\" title=\"");
|
||||
out.write("<select name='theme' disabled=\"disabled\" title=\"");
|
||||
out.write(_t("To change themes manually, disable universal theming"));
|
||||
out.write("\"><option>");
|
||||
out.write(_manager.getTheme());
|
||||
@ -2691,21 +2508,15 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
out.write(_t("Configure"));
|
||||
out.write("]</a>");
|
||||
} else {
|
||||
out.write("<select id=\"theme\" name=\"theme\">");
|
||||
out.write("<select name='theme'>");
|
||||
String theme = _manager.getTheme();
|
||||
String[] themes = _manager.getThemes();
|
||||
// translated sort
|
||||
Map<String, String> tmap = new TreeMap<String, String>(Collator.getInstance());
|
||||
Arrays.sort(themes);
|
||||
for (int i = 0; i < themes.length; i++) {
|
||||
tmap.put(_t(themes[i]), themes[i]);
|
||||
}
|
||||
for (Map.Entry<String, String> e : tmap.entrySet()) {
|
||||
String tr = e.getKey();
|
||||
String opt = e.getValue();
|
||||
if(opt.equals(theme))
|
||||
out.write("\n<option value=\"" + opt + "\" SELECTED>" + tr + "</option>");
|
||||
if(themes[i].equals(theme))
|
||||
out.write("\n<OPTION value=\"" + themes[i] + "\" SELECTED>" + themes[i]);
|
||||
else
|
||||
out.write("\n<option value=\"" + opt + "\">" + tr + "</option>");
|
||||
out.write("\n<OPTION value=\"" + themes[i] + "\">" + themes[i]);
|
||||
}
|
||||
out.write("</select>\n");
|
||||
}
|
||||
@ -3131,8 +2942,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
return escaped;
|
||||
}
|
||||
|
||||
private static final String DOCTYPE = "<!DOCTYPE html>\n";
|
||||
private static final String HEADER_A = "<link id=\"pagestyle\" href=\"";
|
||||
private static final String DOCTYPE = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n";
|
||||
private static final String HEADER_A = "<link href=\"";
|
||||
private static final String HEADER_B = "snark.css?" + CoreVersion.VERSION + "\" rel=\"stylesheet\" type=\"text/css\" >";
|
||||
private static final String HEADER_C = "nocollapse.css?" + CoreVersion.VERSION + "\" rel=\"stylesheet\" type=\"text/css\" >";
|
||||
|
||||
@ -3173,8 +2984,8 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
* @return String of HTML or null if postParams != null
|
||||
* @since 0.7.14
|
||||
*/
|
||||
private String getListHTML(File xxxr, String base, boolean parent, Map<String, String[]> postParams,
|
||||
String sortParam, boolean showEdit) throws IOException
|
||||
private String getListHTML(File xxxr, String base, boolean parent, Map<String, String[]> postParams, String sortParam)
|
||||
throws IOException
|
||||
{
|
||||
String decodedBase = decodePath(base);
|
||||
String title = decodedBase;
|
||||
@ -3216,10 +3027,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
_manager.startTorrent(snark);
|
||||
} else if (postParams.get("recheck") != null) {
|
||||
_manager.recheckTorrent(snark);
|
||||
} else if (postParams.get("editTorrent") != null) {
|
||||
saveTorrentEdit(snark, postParams);
|
||||
} else if (postParams.get("showEdit") != null) {
|
||||
// P-R-G only
|
||||
} else {
|
||||
_manager.addMessage("Unknown command");
|
||||
}
|
||||
@ -3248,7 +3055,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
r = new File("");
|
||||
}
|
||||
|
||||
boolean showStopStart = snark != null && !showEdit;
|
||||
boolean showStopStart = snark != null;
|
||||
Storage storage = snark != null ? snark.getStorage() : null;
|
||||
boolean showPriority = storage != null && !storage.complete() &&
|
||||
r.isDirectory();
|
||||
@ -3286,7 +3093,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
final boolean includeForm = showStopStart || showPriority || er || ec;
|
||||
if (includeForm) {
|
||||
buf.append("<form action=\"").append(base).append("\" method=\"POST\">\n" +
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\">\n");
|
||||
"<input type=\"hidden\" name=\"nonce\" value=\"").append(_nonce).append("\" >\n");
|
||||
if (sortParam != null) {
|
||||
buf.append("<input type=\"hidden\" name=\"sort\" value=\"")
|
||||
.append(DataHelper.stripHTML(sortParam)).append("\" >\n");
|
||||
@ -3296,7 +3103,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// first table - torrent info
|
||||
buf.append("<table class=\"snarkTorrentInfo\">\n" +
|
||||
"<tr><th></th><th><b>")
|
||||
.append(showEdit ? _t("Edit Torrent") : _t("Torrent"))
|
||||
.append(_t("Torrent"))
|
||||
.append("</b></th><th>")
|
||||
.append(DataHelper.escapeHTML(snark.getBaseName()))
|
||||
.append("</th></tr>\n");
|
||||
@ -3310,7 +3117,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append("</b></td><td><a href=\"").append(_contextPath).append('/').append(baseName).append("\">")
|
||||
.append(DataHelper.escapeHTML(fullPath))
|
||||
.append("</a></td></tr>\n");
|
||||
if (storage != null && !showEdit) {
|
||||
if (storage != null) {
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "file");
|
||||
buf.append("</td><td><b>")
|
||||
@ -3320,19 +3127,17 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
String hex = I2PSnarkUtil.toHex(snark.getInfoHash());
|
||||
if (!showEdit) {
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Info hash"))
|
||||
.append("</b></td><td><span id=\"infohash\">")
|
||||
.append(hex.toUpperCase(Locale.US))
|
||||
.append("</span></td></tr>\n");
|
||||
}
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Info hash"))
|
||||
.append("</b></td><td><span id=\"infohash\">")
|
||||
.append(hex.toUpperCase(Locale.US))
|
||||
.append("</span></td></tr>\n");
|
||||
|
||||
String announce = null;
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta != null && !showEdit) {
|
||||
if (meta != null) {
|
||||
announce = meta.getAnnounce();
|
||||
if (announce == null)
|
||||
announce = snark.getTrackerURL();
|
||||
@ -3350,7 +3155,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("</span></td></tr>");
|
||||
}
|
||||
List<List<String>> alist = meta.getAnnounceList();
|
||||
Set<String> annlist = new TreeSet<String>();
|
||||
List<String> annlist = new ArrayList<String>();
|
||||
if (alist != null && !alist.isEmpty()) {
|
||||
// strip non-i2p trackers
|
||||
for (List<String> alist2 : alist) {
|
||||
@ -3413,7 +3218,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Comment")).append("</b></td><td>")
|
||||
.append(DataHelper.escapeHTML(com).replace("\r\n", "<br>").replace("\n", "<br>"))
|
||||
.append(DataHelper.stripHTML(com))
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
long dat = meta.getCreationDate();
|
||||
@ -3434,7 +3239,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Created By")).append("</b></td><td>")
|
||||
.append(DataHelper.escapeHTML(cby))
|
||||
.append(DataHelper.stripHTML(cby))
|
||||
.append("</td></tr>\n");
|
||||
}
|
||||
long[] dates = _manager.getSavedAddedAndCompleted(snark);
|
||||
@ -3470,7 +3275,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
}
|
||||
|
||||
if (!showEdit) { // don't bother to reindent
|
||||
if (meta == null || !meta.isPrivate()) {
|
||||
buf.append("<tr><td><a href=\"")
|
||||
.append(MagnetURI.MAGNET_FULL).append(hex);
|
||||
@ -3573,7 +3377,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
.append(":</b> ")
|
||||
.append(formatSize(snark.getPieceLength(0)))
|
||||
.append("</span></td></tr>\n");
|
||||
} // !showEdit
|
||||
|
||||
// buttons
|
||||
if (showStopStart) {
|
||||
@ -3587,7 +3390,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("<b>").append(_t("Starting")).append("…</b>");
|
||||
} else if (snark.isAllocating()) {
|
||||
buf.append("<b>").append(_t("Allocating")).append("…</b>");
|
||||
} else if (isTopLevel && !showEdit) {
|
||||
} else {
|
||||
boolean isRunning = !snark.isStopped();
|
||||
buf.append("<input type=\"submit\" value=\"");
|
||||
if (isRunning)
|
||||
@ -3595,23 +3398,14 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
else
|
||||
buf.append(_t("Start")).append("\" name=\"start\" class=\"starttorrent\">\n");
|
||||
buf.append("<input type=\"submit\" name=\"recheck\" value=\"").append(_t("Force Recheck"));
|
||||
if (isRunning) {
|
||||
if (isRunning)
|
||||
buf.append("\" class=\"disabled\" disabled=\"disabled\" title=\"")
|
||||
.append(_t("Torrent must be stopped"));
|
||||
} else {
|
||||
.append(_t("Stop the torrent in order to check file integrity"))
|
||||
.append("\">\n");
|
||||
else
|
||||
buf.append("\" class=\"reload\" title=\"")
|
||||
.append(_t("Check integrity of the downloaded files"));
|
||||
}
|
||||
buf.append("\">\n" +
|
||||
"<input type=\"submit\" name=\"showEdit\" value=\"").append(_t("Edit Torrent"));
|
||||
if (isRunning) {
|
||||
buf.append("\" class=\"disabled\" disabled=\"disabled\" title=\"")
|
||||
.append(_t("Torrent must be stopped"));
|
||||
} else {
|
||||
buf.append("\" class=\"reload\" title=\"")
|
||||
.append(_t("Add or remove trackers"));
|
||||
}
|
||||
buf.append("\">\n");
|
||||
.append(_t("Check integrity of the downloaded files"))
|
||||
.append("\">\n");
|
||||
}
|
||||
boolean showInOrder = storage != null && !storage.complete() &&
|
||||
meta != null;
|
||||
@ -3645,13 +3439,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
}
|
||||
buf.append("</table>\n");
|
||||
|
||||
if (snark != null && isTopLevel && showEdit) {
|
||||
// Edit torrent. Show edit section only.
|
||||
displayTorrentEdit(snark, base, buf);
|
||||
buf.append("</form></div></div></center></body></html>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
if (snark != null && !r.exists()) {
|
||||
// fixup TODO
|
||||
buf.append("<table class=\"resourceError\" id=\"DoesNotExist\"><tr><th colspan=\"2\">")
|
||||
@ -3694,7 +3481,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
displayComments(snark, er, ec, esc, buf);
|
||||
if (includeForm)
|
||||
buf.append("</form>");
|
||||
buf.append("</div></div></center></body></html>");
|
||||
buf.append("</div></div></body></html>");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@ -3991,7 +3778,7 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
// for stop/start/check
|
||||
if (includeForm)
|
||||
buf.append("</form>");
|
||||
buf.append("</div></div></center></body></html>");
|
||||
buf.append("</div></div></body></html>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
@ -4363,136 +4150,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
buf.append("</div>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param snark non-null
|
||||
* @since 0.9.53
|
||||
*/
|
||||
private void displayTorrentEdit(Snark snark, String base, StringBuilder buf) {
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta == null)
|
||||
return;
|
||||
buf.append("<div id=\"snarkCommentSection\"><table class=\"snarkTorrentInfo\">\n");
|
||||
//.append("<tr><th colspan=\"5\">")
|
||||
//.append(_t("Edit Torrent"))
|
||||
//.append("</th>")
|
||||
//.append("</tr>");
|
||||
boolean isRunning = !snark.isStopped();
|
||||
if (isRunning) {
|
||||
// shouldn't happen
|
||||
buf.append("<tr><td colspan=\"5\">")
|
||||
.append(_t("Torrent must be stopped"))
|
||||
.append("</td></tr></table></div></form>");
|
||||
return;
|
||||
}
|
||||
String announce = meta.getAnnounce();
|
||||
if (announce == null)
|
||||
announce = snark.getTrackerURL();
|
||||
if (announce != null) {
|
||||
// strip non-i2p trackers
|
||||
if (!isI2PTracker(announce))
|
||||
announce = null;
|
||||
}
|
||||
List<List<String>> alist = meta.getAnnounceList();
|
||||
Set<String> annlist = new TreeSet<String>();
|
||||
if (alist != null && !alist.isEmpty()) {
|
||||
// strip non-i2p trackers
|
||||
for (List<String> alist2 : alist) {
|
||||
for (String s : alist2) {
|
||||
if (isI2PTracker(s))
|
||||
annlist.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (announce != null)
|
||||
annlist.add(announce);
|
||||
if (!annlist.isEmpty()) {
|
||||
buf.append("<tr><td colspan=\"3\"></td><td>").append(_t("Primary")).append("</td><td>")
|
||||
.append(_t("Delete")).append("</td></tr>");
|
||||
for (String s : annlist) {
|
||||
int hc = s.hashCode();
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Tracker")).append("</b></td><td>");
|
||||
s = DataHelper.stripHTML(s);
|
||||
buf.append("<span class=\"info_tracker\">");
|
||||
buf.append(getShortTrackerLink(s, snark.getInfoHash()));
|
||||
buf.append("</span> ");
|
||||
//buf.append(s);
|
||||
buf.append("</td><td>");
|
||||
buf.append("<input type=\"radio\" class=\"optbox\" name=\"primary\" ");
|
||||
if (s.equals(announce))
|
||||
buf.append("checked=\"checked\" ");
|
||||
buf.append("value=\"").append(hc);
|
||||
buf.append("\"></td><td>");
|
||||
buf.append("<input type=\"checkbox\" class=\"optbox\" name=\"trdelete.")
|
||||
.append(hc).append("\" title=\"").append(_t("Mark for deletion")).append("\">");
|
||||
buf.append("</td></tr>\n");
|
||||
}
|
||||
}
|
||||
|
||||
List<Tracker> newTrackers = _manager.getSortedTrackers();
|
||||
for (Iterator<Tracker> iter = newTrackers.iterator(); iter.hasNext(); ) {
|
||||
Tracker t = iter.next();
|
||||
String announceURL = t.announceURL.replace("=", "=");
|
||||
if (announceURL.equals(announce) || annlist.contains(announceURL))
|
||||
iter.remove();
|
||||
}
|
||||
if (!newTrackers.isEmpty()) {
|
||||
buf.append("<tr><td colspan=\"3\"></td><td>").append(_t("Primary")).append("</td><td>")
|
||||
.append(_t("Add")).append("</td></tr>");
|
||||
for (Tracker t : newTrackers) {
|
||||
String name = t.name;
|
||||
int hc = t.announceURL.hashCode();
|
||||
String announceURL = t.announceURL.replace("=", "=");
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Add Tracker")).append("</b></td><td>");
|
||||
buf.append(name);
|
||||
buf.append("</td><td><input type=\"radio\" class=\"optbox\" name=\"primary\" value=\"");
|
||||
buf.append(hc);
|
||||
buf.append("\"></td><td>");
|
||||
buf.append("<input type=\"checkbox\" class=\"optbox\" id=\"").append(name).append("\" name=\"tradd.")
|
||||
.append(hc).append("\" title=\"").append(_t("Add tracker")).append("\"> ")
|
||||
.append("</td><td></td></tr>\n");
|
||||
}
|
||||
}
|
||||
|
||||
String com = meta.getComment();
|
||||
if (com == null) {
|
||||
com = "";
|
||||
} else if (com.length() > 0) {
|
||||
com = DataHelper.escapeHTML(com);
|
||||
}
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Comment")).append("</b></td>");
|
||||
buf.append("<td colspan=\"2\" id=\"addCommentText\"><textarea name=\"nofilter_newTorrentComment\" cols=\"88\" rows=\"4\">")
|
||||
.append(com).append("</textarea></td><td></td>");
|
||||
buf.append("</tr>\n");
|
||||
|
||||
String cb = meta.getCreatedBy();
|
||||
if (cb == null) {
|
||||
cb = "";
|
||||
} else if (cb.length() > 0) {
|
||||
cb = DataHelper.escapeHTML(cb);
|
||||
}
|
||||
buf.append("<tr><td>");
|
||||
toThemeImg(buf, "details");
|
||||
buf.append("</td><td><b>")
|
||||
.append(_t("Created By")).append("</b></td>");
|
||||
buf.append("<td id=\"editTorrentCreatedBy\"><input type=\"text\" name=\"nofilter_newTorrentCreatedBy\" cols=\"44\" rows=\"1\" value=\"")
|
||||
.append(cb).append("\"></td></tr>");
|
||||
|
||||
buf.append("<tr id=\"torrentInfoControl\"><td colspan=\"5\">");
|
||||
buf.append("<input type=\"submit\" name=\"editTorrent\" value=\"");
|
||||
buf.append(_t("Save Changes"));
|
||||
buf.append("\" class=\"accept\"></td></tr>\n");
|
||||
buf.append("</table></div>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param so null ok
|
||||
* @return query string or ""
|
||||
@ -4713,147 +4370,6 @@ public class I2PSnarkServlet extends BasicServlet {
|
||||
_manager.setSavedCommentsEnabled(snark, yes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.53
|
||||
*/
|
||||
private void saveTorrentEdit(Snark snark, Map<String, String[]> postParams) {
|
||||
if (!snark.isStopped()) {
|
||||
// shouldn't happen
|
||||
_manager.addMessage(_t("Torrent must be stopped"));
|
||||
return;
|
||||
}
|
||||
List<Integer> toAdd = new ArrayList<Integer>();
|
||||
List<Integer> toDel = new ArrayList<Integer>();
|
||||
Integer primary = null;
|
||||
String newComment = "";
|
||||
String newCreatedBy = "";
|
||||
for (Map.Entry<String, String[]> entry : postParams.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String val = entry.getValue()[0]; // jetty arrays
|
||||
if (key.startsWith("tradd.")) {
|
||||
try {
|
||||
toAdd.add(Integer.parseInt(key.substring(6)));
|
||||
} catch (NumberFormatException nfe) {}
|
||||
} else if (key.startsWith("trdelete.")) {
|
||||
try {
|
||||
toDel.add(Integer.parseInt(key.substring(9)));
|
||||
} catch (NumberFormatException nfe) {}
|
||||
} else if (key.equals("primary")) {
|
||||
try {
|
||||
primary = Integer.parseInt(val);
|
||||
} catch (NumberFormatException nfe) {}
|
||||
} else if (key.equals("nofilter_newTorrentComment")) {
|
||||
newComment = val.trim();
|
||||
} else if (key.equals("nofilter_newTorrentCreatedBy")) {
|
||||
newCreatedBy = val.trim();
|
||||
}
|
||||
}
|
||||
MetaInfo meta = snark.getMetaInfo();
|
||||
if (meta == null) {
|
||||
// shouldn't happen
|
||||
_manager.addMessage("Can't edit magnet");
|
||||
return;
|
||||
}
|
||||
String oldPrimary = meta.getAnnounce();
|
||||
String oldComment = meta.getComment();
|
||||
if (oldComment == null)
|
||||
oldComment = "";
|
||||
String oldCreatedBy = meta.getCreatedBy();
|
||||
if (oldCreatedBy == null)
|
||||
oldCreatedBy = "";
|
||||
if (toAdd.isEmpty() && toDel.isEmpty() &&
|
||||
(primary == null || primary.equals(oldPrimary)) &&
|
||||
oldComment.equals(newComment) &&
|
||||
oldCreatedBy.equals(newCreatedBy)) {
|
||||
_manager.addMessage("No changes to torrent, not saved");
|
||||
return;
|
||||
}
|
||||
List<List<String>> alist = meta.getAnnounceList();
|
||||
Set<String> annlist = new TreeSet<String>();
|
||||
if (alist != null && !alist.isEmpty()) {
|
||||
// strip non-i2p trackers
|
||||
for (List<String> alist2 : alist) {
|
||||
for (String s : alist2) {
|
||||
if (isI2PTracker(s))
|
||||
annlist.add(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (oldPrimary != null)
|
||||
annlist.add(oldPrimary);
|
||||
List<Tracker> newTrackers = _manager.getSortedTrackers();
|
||||
for (Integer i : toDel) {
|
||||
int hc = i.intValue();
|
||||
for (Iterator<String> iter = annlist.iterator(); iter.hasNext(); ) {
|
||||
String s = iter.next();
|
||||
if (s.hashCode() == hc)
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
for (Integer i : toAdd) {
|
||||
int hc = i.intValue();
|
||||
for (Tracker t : newTrackers) {
|
||||
if (t.announceURL.hashCode() == hc) {
|
||||
annlist.add(t.announceURL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
String thePrimary = oldPrimary;
|
||||
if (primary != null) {
|
||||
int hc = primary.intValue();
|
||||
for (String s : annlist) {
|
||||
if (s.hashCode() == hc) {
|
||||
thePrimary = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
List<List<String>> newAnnList;
|
||||
if (annlist.isEmpty()) {
|
||||
newAnnList = null;
|
||||
thePrimary = null;
|
||||
} else {
|
||||
List<String> aalist = new ArrayList<String>(annlist);
|
||||
newAnnList = Collections.singletonList(aalist);
|
||||
if (!aalist.contains(thePrimary))
|
||||
thePrimary = aalist.get(0);
|
||||
}
|
||||
if (newComment.equals(""))
|
||||
newComment = null;
|
||||
if (newCreatedBy.equals(""))
|
||||
newCreatedBy = null;
|
||||
MetaInfo newMeta = new MetaInfo(thePrimary, meta.getName(), null, meta.getFiles(), meta.getLengths(),
|
||||
meta.getPieceLength(0), meta.getPieceHashes(), meta.getTotalLength(), meta.isPrivate(),
|
||||
newAnnList, newCreatedBy, meta.getWebSeedURLs(), newComment);
|
||||
if (!DataHelper.eq(meta.getInfoHash(), newMeta.getInfoHash())) {
|
||||
// shouldn't happen
|
||||
_manager.addMessage("Torrent edit failed, infohash mismatch");
|
||||
return;
|
||||
}
|
||||
File f = new File(_manager.util().getTempDir(), "edit-" + _manager.util().getContext().random().nextLong() + ".torrent");
|
||||
OutputStream out = null;
|
||||
try {
|
||||
out = new SecureFileOutputStream(f);
|
||||
out.write(newMeta.getTorrentData());
|
||||
out.close();
|
||||
boolean ok = FileUtil.rename(f, new File(snark.getName()));
|
||||
if (!ok) {
|
||||
_manager.addMessage("Save edit changes failed");
|
||||
return;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
try { if (out != null) out.close(); } catch (IOException ioe2) {}
|
||||
_manager.addMessage("Save edit changes failed: " + ioe);
|
||||
return;
|
||||
} finally {
|
||||
f.delete();
|
||||
}
|
||||
snark.replaceMetaInfo(newMeta);
|
||||
_manager.addMessage("Torrent changes saved");
|
||||
}
|
||||
|
||||
|
||||
/** @since 0.9.32 */
|
||||
private static boolean noCollapsePanels(HttpServletRequest req) {
|
||||
// check for user agents that can't toggle the collapsible panels...
|
||||
|
@ -14,10 +14,6 @@
|
||||
|
||||
package org.klomp.snark.web;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@ -25,9 +21,6 @@ import java.util.MissingResourceException;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.util.SystemVersion;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
@ -54,8 +47,6 @@ class MimeTypes
|
||||
|
||||
public MimeTypes() {
|
||||
_mimeMap = new ConcurrentHashMap<String, String>();
|
||||
if (!(SystemVersion.isWindows() || SystemVersion.isMac() || SystemVersion.getMaxMemory() < 100*1024*1024L))
|
||||
loadSystemMimeTypes();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@ -95,37 +86,6 @@ class MimeTypes
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load mime types from /etc/mime.types
|
||||
* Format: mimetype suffix1 suffix2 ...
|
||||
*
|
||||
* @since 0.9.54
|
||||
*/
|
||||
private void loadSystemMimeTypes() {
|
||||
BufferedReader in = null;
|
||||
try {
|
||||
in = new BufferedReader(new InputStreamReader(new FileInputStream("/etc/mime.types"), "ISO-8859-1"));
|
||||
while (true) {
|
||||
String line = in.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
if (line.startsWith("#"))
|
||||
continue;
|
||||
String[] s = DataHelper.split(line, "[ \t]+", 16);
|
||||
if (s.length < 2)
|
||||
continue;
|
||||
for (int i = 1; i < s.length; i++) {
|
||||
_mimeMap.put(s[i].toLowerCase(Locale.US), s[0]);
|
||||
//System.out.println("Mapping: '" + s[i] + "' -> '" + s[0] + "'");
|
||||
}
|
||||
}
|
||||
//System.out.println("Loaded " + _mimeMap.size() + " mime types from /etc/mime.types");
|
||||
} catch (IOException ioe) {
|
||||
} finally {
|
||||
if (in != null) try { in.close(); } catch (IOException ioe) {}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get the MIME type by filename extension.
|
||||
*
|
||||
|
@ -4,37 +4,5 @@
|
||||
# The file jetty-i2psnark.xml must be present in the current directory.
|
||||
# i2psnark will be accessed at http://127.0.0.1:8002/
|
||||
#
|
||||
|
||||
# Raise the soft open files soft ulimit to this value, if able
|
||||
OPEN_FILES_ULIMIT=2048
|
||||
|
||||
# Increase memory to 512 MB
|
||||
JAVA_OPTS='-Xmx512m'
|
||||
|
||||
raiseopenfilesulimit() {
|
||||
OPEN_FILES_SOFT=`ulimit -S -n` 2> /dev/null || return
|
||||
if [ "$OPEN_FILES_SOFT" != "unlimited" ]
|
||||
then
|
||||
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_SOFT" ]
|
||||
then
|
||||
OPEN_FILES_HARD=`ulimit -H -n` 2> /dev/null || return
|
||||
if [ "$OPEN_FILES_HARD" != "unlimited" ]
|
||||
then
|
||||
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_HARD" ]
|
||||
then
|
||||
OPEN_FILES_ULIMIT="$OPEN_FILES_HARD"
|
||||
fi
|
||||
fi
|
||||
if [ "$OPEN_FILES_ULIMIT" -gt "$OPEN_FILES_SOFT" ]
|
||||
then
|
||||
ulimit -S -n "$OPEN_FILES_ULIMIT" > /dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
raiseopenfilesulimit
|
||||
|
||||
I2P="`dirname $0`"
|
||||
cd "$I2P"
|
||||
java $JAVA_OPTS -jar i2psnark.jar
|
||||
I2P="."
|
||||
java -jar "$I2P/i2psnark.jar"
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user