resolve merge conflicts

pull/1669/head
idk 3 years ago
commit d62d2ed269
No known key found for this signature in database
GPG Key ID: D75C03B39B5E14E1

2
.gitignore vendored

@ -7,8 +7,10 @@ netDb
/i2pd
/libi2pd.a
/libi2pdclient.a
/libi2pdlang.a
/libi2pd.so
/libi2pdclient.so
/libi2pdlang.so
*.exe

@ -1,54 +0,0 @@
language: cpp
cache:
apt: true
os:
- linux
#- osx
dist: xenial
sudo: required
compiler:
- g++
- clang++
env:
global:
- MAKEFLAGS="-j 2"
matrix:
- BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes
- BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no
- BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes
- BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no
matrix:
exclude:
- os: osx
env: BUILD_TYPE=cmake UPNP=ON MAKE_UPNP=yes
- os: osx
env: BUILD_TYPE=cmake UPNP=OFF MAKE_UPNP=no
- os: linux
compiler: clang++
env: BUILD_TYPE=make UPNP=ON MAKE_UPNP=yes
- os: linux
compiler: clang++
env: BUILD_TYPE=make UPNP=OFF MAKE_UPNP=no
addons:
apt:
packages:
- build-essential
- cmake
- g++
- clang
- libboost-chrono-dev
- libboost-date-time-dev
- libboost-filesystem-dev
- libboost-program-options-dev
- libboost-system-dev
- libboost-thread-dev
- libminiupnpc-dev
- libssl-dev
before_install:
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew install libressl miniupnpc ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew outdated boost || brew upgrade boost ; fi
script:
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "cmake" ]]; then cd build && cmake -DCMAKE_BUILD_TYPE=Release -DWITH_UPNP=${UPNP} && make ; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" && "$BUILD_TYPE" == "make" ]]; then make USE_UPNP=${MAKE_UPNP} ; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make HOMEBREW=1 USE_UPNP=${MAKE_UPNP} ; fi

@ -79,7 +79,8 @@ api: mk_obj_dir $(SHLIB) $(ARLIB)
client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT)
api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP)
langs: mk_obj_dir $(LANG_OBJS) $(SHLIB_LANG) $(ARLIB_LANG)
lang: mk_obj_dir $(SHLIB_LANG) $(ARLIB_LANG)
## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time
## **without** overwriting the CXXFLAGS which we need in order to build.
@ -94,7 +95,7 @@ obj/%.o: %.cpp
# '-' is 'ignore if missing' on first run
-include $(DEPS)
$(I2PD): $(LANG_OBJS) $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT)
$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG)
$(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS)
$(SHLIB): $(LIB_OBJS)
@ -132,9 +133,9 @@ $(ARLIB_LANG): $(LANG_OBJS)
clean:
$(RM) -r obj
$(RM) -r docs/generated
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT)
$(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG)
strip: $(I2PD) $(SHLIB_CLIENT) $(SHLIB)
strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG)
strip $^
LATEST_TAG=$(shell git describe --tags --abbrev=0 openssl)
@ -158,6 +159,7 @@ doxygen:
.PHONY: api
.PHONY: api_client
.PHONY: client
.PHONY: lang
.PHONY: mk_obj_dir
.PHONY: install
.PHONY: strip

@ -73,7 +73,7 @@ Build instructions:
* Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc.
* Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml)
* Mac OS X - [![Build on OSX](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-osx.yml)
* Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/)
* Docker image - [![Build Status](https://img.shields.io/docker/cloud/build/purplei2p/i2pd)](https://hub.docker.com/r/purplei2p/i2pd/builds/) [![Build containers](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
* Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd)
* FreeBSD - [![Build on FreeBSD](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-freebsd.yml)
* Android - [![Android CI](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml/badge.svg)](https://github.com/PurpleI2P/i2pd-android/actions/workflows/android.yml)

@ -1,57 +0,0 @@
version: 2.38.0.{build}
pull_requests:
do_not_increment_build_number: true
branches:
only:
- openssl
skip_tags: true
os: Visual Studio 2015
shallow_clone: true
clone_depth: 1
# avoid building 32-bit if 64-bit failed already
matrix:
fast_finish: true
environment:
APPVEYOR_SAVE_CACHE_ON_ERROR: true
MSYS2_PATH_TYPE: inherit
CHERE_INVOKING: enabled_from_arguments
matrix:
- MSYSTEM: MINGW64
- MSYSTEM: MINGW32
cache:
- c:\msys64\var\cache\pacman\pkg\
install:
# install new signing keyring
- c:\msys64\usr\bin\bash -lc "curl -O https://mirror.selfnet.de/msys2/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
- c:\msys64\usr\bin\bash -lc "curl -O https://mirror.selfnet.de/msys2/msys/x86_64/msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- c:\msys64\usr\bin\bash -lc "pacman-key --verify msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz.sig"
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -U msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz"
# remove packages which can break build
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Rns gcc-fortran gcc mingw-w64-{i686,x86_64}-gcc-ada mingw-w64-{i686,x86_64}-gcc-objc"
# update runtime
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu"
# Kill bash before next try
- taskkill /T /F /IM bash.exe /IM gpg.exe /IM gpg-agent.exe | exit /B 0
# update packages and install required
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Syuu $MINGW_PACKAGE_PREFIX-boost $MINGW_PACKAGE_PREFIX-miniupnpc"
build_script:
- c:\msys64\usr\bin\bash -lc "make USE_UPNP=yes DEBUG=no -j3"
# prepare archive for uploading
- set "FILELIST=i2pd.exe README.txt contrib/i2pd.conf contrib/tunnels.conf contrib/certificates contrib/tunnels.d"
- echo This is development build, use it carefully! For running in portable mode, move all files from contrib directory here. > README.txt
- 7z a -tzip -mx9 -mmt i2pd-%APPVEYOR_BUILD_VERSION%-%APPVEYOR_REPO_COMMIT:~0,7%-mingw-win%MSYSTEM:~-2%.zip %FILELIST%
after_build:
- c:\msys64\usr\bin\bash -lc "pacman --noconfirm -Sc"
test: off
deploy: off
artifacts:
- path: i2pd-*.zip

@ -69,6 +69,16 @@ if(WITH_LIBRARY)
endif()
FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp)
add_library(libi2pdlang ${LANG_SRC})
set_target_properties(libi2pdlang PROPERTIES PREFIX "")
if(WITH_LIBRARY)
install(TARGETS libi2pdlang
EXPORT libi2pdlang
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
COMPONENT Libraries)
endif()
set(DAEMON_SRC
"${DAEMON_SRC_DIR}/Daemon.cpp"
@ -198,10 +208,11 @@ if(WITH_PCH)
)
target_compile_options(libi2pd PRIVATE -include libi2pd/stdafx.h)
target_compile_options(libi2pdclient PRIVATE -include libi2pd/stdafx.h)
target_compile_options(libi2pdlang PRIVATE -include libi2pd/stdafx.h)
target_link_libraries(libi2pd stdafx)
endif()
target_link_libraries(libi2pdclient libi2pd)
target_link_libraries(libi2pdclient libi2pd libi2pdlang)
find_package(Boost COMPONENTS system filesystem program_options date_time REQUIRED)
if(NOT DEFINED Boost_INCLUDE_DIRS)
@ -265,7 +276,7 @@ message(STATUS "---------------------------------------")
include(GNUInstallDirs)
if(WITH_BINARY)
add_executable("${PROJECT_NAME}" ${LANG_SRC} ${DAEMON_SRC})
add_executable("${PROJECT_NAME}" ${DAEMON_SRC})
if(WITH_STATIC)
set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static")
@ -295,7 +306,7 @@ if(WITH_BINARY)
endif()
target_link_libraries(libi2pd ${Boost_LIBRARIES} ${ZLIB_LIBRARY})
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
target_link_libraries("${PROJECT_NAME}" libi2pd libi2pdclient libi2pdlang ${DL_LIB} ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES} ${UPNP_LIB} ${ZLIB_LIBRARY} ${CMAKE_THREAD_LIBS_INIT} ${MINGW_EXTRA} ${DL_LIB} ${CMAKE_REQUIRED_LIBRARIES})
install(TARGETS "${PROJECT_NAME}" RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT Runtime)
set(APPS "\${CMAKE_INSTALL_PREFIX}/bin/${PROJECT_NAME}${CMAKE_EXECUTABLE_SUFFIX}")

@ -1,34 +0,0 @@
Howto build & run
==================
**Build**
Assuming you're in the root directory of the anoncoin source code.
$ `cd build/docker`
$ `docker -t meeh/i2pd:latest .`
**Run**
To run either the local build, or if not found - fetched prebuild from hub.docker.io, run the following command.
$ `docker run --name anonnode -v /path/to/i2pd/datadir/on/host:/var/lib/i2pd -p 7070:7070 -p 4444:4444 -p 4447:4447 -p 7656:7656 -p 2827:2827 -p 7654:7654 -p 7650:7650 -d meeh/i2pd`
All the ports ( -p HOSTPORT:DOCKERPORT ) is optional. However the command above enable all features (Webconsole, HTTP Proxy, BOB, SAM, i2cp, etc)
The volume ( -v HOSTDIR:DOCKERDIR ) is also optional, but if you don't use it, your config, routerid and private keys will die along with the container.
**Options**
Options are set via docker environment variables. This can be set at run with -e parameters.
* **ENABLE_IPV6** - Enable IPv6 support. Any value can be used - it triggers as long as it's not empty.
* **LOGLEVEL** - Set the loglevel.
* **ENABLE_AUTH** - Enable auth for the webconsole. Username and password needs to be set manually in i2pd.conf cause security reasons.
**Logging**
Logging happens to STDOUT as the best practise with docker containers, since infrastructure systems like kubernetes with ELK integration can automatically forward the log to say, kibana or greylog without manual setup. :)

@ -1,11 +0,0 @@
FROM ubuntu
RUN apt-get update && apt-get install -y libboost-dev libboost-filesystem-dev \
libboost-program-options-dev libboost-date-time-dev \
libssl-dev git build-essential
RUN git clone https://github.com/PurpleI2P/i2pd.git
WORKDIR /i2pd
RUN make
CMD ./i2pd

@ -1,2 +0,0 @@
i2pd:
build: .

@ -1,4 +1,4 @@
FROM alpine:3.13
FROM alpine:latest
LABEL authors "Mikal Villa <mikal@sigterm.no>, Darknet Villain <supervillain@riseup.net>"
# Expose git branch, tag and URL variables as arguments

@ -26,551 +26,551 @@ msgstr ""
msgid "Enabled"
msgstr ""
#: daemon/HTTPServer.cpp:141
#: daemon/HTTPServer.cpp:147
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:145
#: daemon/HTTPServer.cpp:151
msgid "hour"
msgid_plural "hours"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:149
#: daemon/HTTPServer.cpp:155
msgid "minute"
msgid_plural "minutes"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:152
#: daemon/HTTPServer.cpp:158
msgid "second"
msgid_plural "seconds"
msgstr[0] ""
msgstr[1] ""
#: daemon/HTTPServer.cpp:160 daemon/HTTPServer.cpp:188
#. tr: Kibibit
#: daemon/HTTPServer.cpp:166 daemon/HTTPServer.cpp:194
msgid "KiB"
msgstr ""
#: daemon/HTTPServer.cpp:162
#. tr: Mebibit
#: daemon/HTTPServer.cpp:168
msgid "MiB"
msgstr ""
#: daemon/HTTPServer.cpp:164
#. tr: Gibibit
#: daemon/HTTPServer.cpp:170
msgid "GiB"
msgstr ""
#: daemon/HTTPServer.cpp:181
#: daemon/HTTPServer.cpp:187
msgid "building"
msgstr ""
#: daemon/HTTPServer.cpp:182
#: daemon/HTTPServer.cpp:188
msgid "failed"
msgstr ""
#: daemon/HTTPServer.cpp:183
#: daemon/HTTPServer.cpp:189
msgid "expiring"
msgstr ""
#: daemon/HTTPServer.cpp:184
#: daemon/HTTPServer.cpp:190
msgid "established"
msgstr ""
#: daemon/HTTPServer.cpp:185
#: daemon/HTTPServer.cpp:191
msgid "unknown"
msgstr ""
#: daemon/HTTPServer.cpp:187
#: daemon/HTTPServer.cpp:193
msgid "exploratory"
msgstr ""
#: daemon/HTTPServer.cpp:223
#: daemon/HTTPServer.cpp:229
msgid "<b>i2pd</b> webconsole"
msgstr ""
#: daemon/HTTPServer.cpp:226
#: daemon/HTTPServer.cpp:232
msgid "Main page"
msgstr ""
#: daemon/HTTPServer.cpp:227 daemon/HTTPServer.cpp:683
#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:690
msgid "Router commands"
msgstr ""
#: daemon/HTTPServer.cpp:228
msgid "Local destinations"
#: daemon/HTTPServer.cpp:234 daemon/HTTPServer.cpp:413
#: daemon/HTTPServer.cpp:425
msgid "Local Destinations"
msgstr ""
#: daemon/HTTPServer.cpp:230 daemon/HTTPServer.cpp:382
#: daemon/HTTPServer.cpp:463 daemon/HTTPServer.cpp:469
#: daemon/HTTPServer.cpp:599 daemon/HTTPServer.cpp:642
#: daemon/HTTPServer.cpp:646
#: daemon/HTTPServer.cpp:236 daemon/HTTPServer.cpp:388
#: daemon/HTTPServer.cpp:469 daemon/HTTPServer.cpp:475
#: daemon/HTTPServer.cpp:606 daemon/HTTPServer.cpp:649
#: daemon/HTTPServer.cpp:653
msgid "LeaseSets"
msgstr ""
#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:652
#: daemon/HTTPServer.cpp:238 daemon/HTTPServer.cpp:659
msgid "Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:727
#: daemon/HTTPServer.cpp:743
msgid "Transit tunnels"
#: daemon/HTTPServer.cpp:239 daemon/HTTPServer.cpp:395
#: daemon/HTTPServer.cpp:753 daemon/HTTPServer.cpp:769
msgid "Transit Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:234 daemon/HTTPServer.cpp:792
#: daemon/HTTPServer.cpp:240 daemon/HTTPServer.cpp:818
msgid "Transports"
msgstr ""
#: daemon/HTTPServer.cpp:235
#: daemon/HTTPServer.cpp:241
msgid "I2P tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:237 daemon/HTTPServer.cpp:854
#: daemon/HTTPServer.cpp:864
#: daemon/HTTPServer.cpp:243 daemon/HTTPServer.cpp:880
#: daemon/HTTPServer.cpp:890
msgid "SAM sessions"
msgstr ""
#: daemon/HTTPServer.cpp:253 daemon/HTTPServer.cpp:1254
#: daemon/HTTPServer.cpp:1257 daemon/HTTPServer.cpp:1260
#: daemon/HTTPServer.cpp:1274 daemon/HTTPServer.cpp:1319
#: daemon/HTTPServer.cpp:1322 daemon/HTTPServer.cpp:1325
#: daemon/HTTPServer.cpp:259 daemon/HTTPServer.cpp:1280
#: daemon/HTTPServer.cpp:1283 daemon/HTTPServer.cpp:1286
#: daemon/HTTPServer.cpp:1300 daemon/HTTPServer.cpp:1345
#: daemon/HTTPServer.cpp:1348 daemon/HTTPServer.cpp:1351
msgid "ERROR"
msgstr ""
#: daemon/HTTPServer.cpp:260
#: daemon/HTTPServer.cpp:266
msgid "OK"
msgstr ""
#: daemon/HTTPServer.cpp:261
#: daemon/HTTPServer.cpp:267
msgid "Testing"
msgstr ""
#: daemon/HTTPServer.cpp:262
#: daemon/HTTPServer.cpp:268
msgid "Firewalled"
msgstr ""
#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:284
#: daemon/HTTPServer.cpp:370
#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:290
#: daemon/HTTPServer.cpp:376
msgid "Unknown"
msgstr ""
#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:394
#: daemon/HTTPServer.cpp:395 daemon/HTTPServer.cpp:922
#: daemon/HTTPServer.cpp:931
#: daemon/HTTPServer.cpp:270 daemon/HTTPServer.cpp:400
#: daemon/HTTPServer.cpp:401 daemon/HTTPServer.cpp:948
#: daemon/HTTPServer.cpp:957
msgid "Proxy"
msgstr ""
#: daemon/HTTPServer.cpp:265
#: daemon/HTTPServer.cpp:271
msgid "Mesh"
msgstr ""
#: daemon/HTTPServer.cpp:268
#: daemon/HTTPServer.cpp:274
msgid "Error"
msgstr ""
#: daemon/HTTPServer.cpp:272
#: daemon/HTTPServer.cpp:278
msgid "Clock skew"
msgstr ""
#: daemon/HTTPServer.cpp:275
#: daemon/HTTPServer.cpp:281
msgid "Offline"
msgstr ""
#: daemon/HTTPServer.cpp:278
#: daemon/HTTPServer.cpp:284
msgid "Symmetric NAT"
msgstr ""
#: daemon/HTTPServer.cpp:290
#: daemon/HTTPServer.cpp:296
msgid "Uptime"
msgstr ""
#: daemon/HTTPServer.cpp:293
#: daemon/HTTPServer.cpp:299
msgid "Network status"
msgstr ""
#: daemon/HTTPServer.cpp:298
#: daemon/HTTPServer.cpp:304
msgid "Network status v6"
msgstr ""
#: daemon/HTTPServer.cpp:304 daemon/HTTPServer.cpp:311
#: daemon/HTTPServer.cpp:310 daemon/HTTPServer.cpp:317
msgid "Stopping in"
msgstr ""
#: daemon/HTTPServer.cpp:318
#: daemon/HTTPServer.cpp:324
msgid "Family"
msgstr ""
#: daemon/HTTPServer.cpp:319
#: daemon/HTTPServer.cpp:325
msgid "Tunnel creation success rate"
msgstr ""
#: daemon/HTTPServer.cpp:320
#: daemon/HTTPServer.cpp:326
msgid "Received"
msgstr ""
#: daemon/HTTPServer.cpp:322 daemon/HTTPServer.cpp:325
#: daemon/HTTPServer.cpp:328
#. tr: Kibibit/s
#: daemon/HTTPServer.cpp:328 daemon/HTTPServer.cpp:331
#: daemon/HTTPServer.cpp:334
msgid "KiB/s"
msgstr ""
#: daemon/HTTPServer.cpp:323
#: daemon/HTTPServer.cpp:329
msgid "Sent"
msgstr ""
#: daemon/HTTPServer.cpp:326
#: daemon/HTTPServer.cpp:332
msgid "Transit"
msgstr ""
#: daemon/HTTPServer.cpp:329
#: daemon/HTTPServer.cpp:335
msgid "Data path"
msgstr ""
#: daemon/HTTPServer.cpp:332
#: daemon/HTTPServer.cpp:338
msgid "Hidden content. Press on text to see."
msgstr ""
#: daemon/HTTPServer.cpp:335
#: daemon/HTTPServer.cpp:341
msgid "Router Ident"
msgstr ""
#: daemon/HTTPServer.cpp:337
#: daemon/HTTPServer.cpp:343
msgid "Router Family"
msgstr ""
#: daemon/HTTPServer.cpp:338
#: daemon/HTTPServer.cpp:344
msgid "Router Caps"
msgstr ""
#: daemon/HTTPServer.cpp:339
#: daemon/HTTPServer.cpp:345
msgid "Version"
msgstr ""
#: daemon/HTTPServer.cpp:340
#: daemon/HTTPServer.cpp:346
msgid "Our external address"
msgstr ""
#: daemon/HTTPServer.cpp:348
#: daemon/HTTPServer.cpp:354
msgid "supported"
msgstr ""
#: daemon/HTTPServer.cpp:380
#: daemon/HTTPServer.cpp:386
msgid "Routers"
msgstr ""
#: daemon/HTTPServer.cpp:381
#: daemon/HTTPServer.cpp:387
msgid "Floodfills"
msgstr ""
#: daemon/HTTPServer.cpp:388 daemon/HTTPServer.cpp:908
#: daemon/HTTPServer.cpp:394 daemon/HTTPServer.cpp:934
msgid "Client Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:389
msgid "Transit Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:393
#: daemon/HTTPServer.cpp:399
msgid "Services"
msgstr ""
#: daemon/HTTPServer.cpp:407 daemon/HTTPServer.cpp:419
msgid "Local Destinations"
msgstr ""
#: daemon/HTTPServer.cpp:442
#: daemon/HTTPServer.cpp:448
msgid "Encrypted B33 address"
msgstr ""
#: daemon/HTTPServer.cpp:451
#: daemon/HTTPServer.cpp:457
msgid "Address registration line"
msgstr ""
#: daemon/HTTPServer.cpp:456
#: daemon/HTTPServer.cpp:462
msgid "Domain"
msgstr ""
#: daemon/HTTPServer.cpp:457
#: daemon/HTTPServer.cpp:463
msgid "Generate"
msgstr ""
#: daemon/HTTPServer.cpp:458
#: daemon/HTTPServer.cpp:464
msgid ""
"<b>Note:</b> result string can be used only for registering 2LD domains "
"(example.i2p). For registering subdomains please use i2pd-tools."
msgstr ""
#: daemon/HTTPServer.cpp:464
#: daemon/HTTPServer.cpp:470
msgid "Address"
msgstr ""
#: daemon/HTTPServer.cpp:464
#: daemon/HTTPServer.cpp:470
msgid "Type"
msgstr ""
#: daemon/HTTPServer.cpp:464
#: daemon/HTTPServer.cpp:470
msgid "EncType"
msgstr ""
#: daemon/HTTPServer.cpp:474 daemon/HTTPServer.cpp:657
#: daemon/HTTPServer.cpp:480 daemon/HTTPServer.cpp:664
msgid "Inbound tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:479 daemon/HTTPServer.cpp:489
#: daemon/HTTPServer.cpp:662 daemon/HTTPServer.cpp:672
#: Means milliseconds
#. tr: Milliseconds
#: daemon/HTTPServer.cpp:485 daemon/HTTPServer.cpp:495
#: daemon/HTTPServer.cpp:669 daemon/HTTPServer.cpp:679
msgid "ms"
msgstr ""
#: daemon/HTTPServer.cpp:484 daemon/HTTPServer.cpp:667
#: daemon/HTTPServer.cpp:490 daemon/HTTPServer.cpp:674
msgid "Outbound tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:496
#: daemon/HTTPServer.cpp:502
msgid "Tags"
msgstr ""
#: daemon/HTTPServer.cpp:496
#: daemon/HTTPServer.cpp:502
msgid "Incoming"
msgstr ""
#: daemon/HTTPServer.cpp:503 daemon/HTTPServer.cpp:506
#: daemon/HTTPServer.cpp:509 daemon/HTTPServer.cpp:512
msgid "Outgoing"
msgstr ""
#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:520
#: daemon/HTTPServer.cpp:510 daemon/HTTPServer.cpp:526
msgid "Destination"
msgstr ""
#: daemon/HTTPServer.cpp:504
#: daemon/HTTPServer.cpp:510
msgid "Amount"
msgstr ""
#: daemon/HTTPServer.cpp:511
#: daemon/HTTPServer.cpp:517
msgid "Incoming Tags"
msgstr ""
#: daemon/HTTPServer.cpp:519 daemon/HTTPServer.cpp:522
#: daemon/HTTPServer.cpp:525 daemon/HTTPServer.cpp:528
msgid "Tags sessions"
msgstr ""
#: daemon/HTTPServer.cpp:520
#: daemon/HTTPServer.cpp:526
msgid "Status"
msgstr ""
#: daemon/HTTPServer.cpp:529 daemon/HTTPServer.cpp:584
#: daemon/HTTPServer.cpp:535 daemon/HTTPServer.cpp:591
msgid "Local Destination"
msgstr ""
#: daemon/HTTPServer.cpp:538 daemon/HTTPServer.cpp:887
#: daemon/HTTPServer.cpp:545 daemon/HTTPServer.cpp:913
msgid "Streams"
msgstr ""
#: daemon/HTTPServer.cpp:560
#: daemon/HTTPServer.cpp:567
msgid "Close stream"
msgstr ""
#: daemon/HTTPServer.cpp:589
#: daemon/HTTPServer.cpp:596
msgid "I2CP session not found"
msgstr ""
#: daemon/HTTPServer.cpp:592
#: daemon/HTTPServer.cpp:599
msgid "I2CP is not enabled"
msgstr ""
#: daemon/HTTPServer.cpp:618
#: daemon/HTTPServer.cpp:625
msgid "Invalid"
msgstr ""
#: daemon/HTTPServer.cpp:621
#: daemon/HTTPServer.cpp:628
msgid "Store type"
msgstr ""
#: daemon/HTTPServer.cpp:622
#: daemon/HTTPServer.cpp:629
msgid "Expires"
msgstr ""
#: daemon/HTTPServer.cpp:627
#: daemon/HTTPServer.cpp:634
msgid "Non Expired Leases"
msgstr ""
#: daemon/HTTPServer.cpp:630
#: daemon/HTTPServer.cpp:637
msgid "Gateway"
msgstr ""
#: daemon/HTTPServer.cpp:631
#: daemon/HTTPServer.cpp:638
msgid "TunnelID"
msgstr ""
#: daemon/HTTPServer.cpp:632
#: daemon/HTTPServer.cpp:639
msgid "EndDate"
msgstr ""
#: daemon/HTTPServer.cpp:642
#: daemon/HTTPServer.cpp:649
msgid "not floodfill"
msgstr ""
#: daemon/HTTPServer.cpp:653
#: daemon/HTTPServer.cpp:660
msgid "Queue size"
msgstr ""
#: daemon/HTTPServer.cpp:684
#: daemon/HTTPServer.cpp:691
msgid "Run peer test"
msgstr ""
#: daemon/HTTPServer.cpp:687
#: daemon/HTTPServer.cpp:698
msgid "Decline transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:689
#: daemon/HTTPServer.cpp:700
msgid "Accept transit tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:692 daemon/HTTPServer.cpp:697
#: daemon/HTTPServer.cpp:704 daemon/HTTPServer.cpp:709
msgid "Cancel graceful shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:694 daemon/HTTPServer.cpp:699
#: daemon/HTTPServer.cpp:706 daemon/HTTPServer.cpp:711
msgid "Start graceful shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:701
#: daemon/HTTPServer.cpp:714
msgid "Force shutdown"
msgstr ""
#: daemon/HTTPServer.cpp:704
#: daemon/HTTPServer.cpp:717
msgid ""
"<b>Note:</b> any action done here are not persistent and not changes your "
"config files."
msgstr ""
#: daemon/HTTPServer.cpp:706
#: daemon/HTTPServer.cpp:719
msgid "Logging level"
msgstr ""
#: daemon/HTTPServer.cpp:714
#: daemon/HTTPServer.cpp:727
msgid "Transit tunnels limit"
msgstr ""
#: daemon/HTTPServer.cpp:719
#: daemon/HTTPServer.cpp:732 daemon/HTTPServer.cpp:744
msgid "Change"
msgstr ""
#: daemon/HTTPServer.cpp:743
#: daemon/HTTPServer.cpp:736
msgid "Change language"
msgstr ""
#: daemon/HTTPServer.cpp:769
msgid "no transit tunnels currently built"
msgstr ""
#: daemon/HTTPServer.cpp:848 daemon/HTTPServer.cpp:871
#: daemon/HTTPServer.cpp:874 daemon/HTTPServer.cpp:897
msgid "SAM disabled"
msgstr ""
#: daemon/HTTPServer.cpp:864
#: daemon/HTTPServer.cpp:890
msgid "no sessions currently running"
msgstr ""
#: daemon/HTTPServer.cpp:877
#: daemon/HTTPServer.cpp:903
msgid "SAM session not found"
msgstr ""
#: daemon/HTTPServer.cpp:882
#: daemon/HTTPServer.cpp:908
msgid "SAM Session"
msgstr ""
#: daemon/HTTPServer.cpp:939
#: daemon/HTTPServer.cpp:965
msgid "Server Tunnels"
msgstr ""
#: daemon/HTTPServer.cpp:955
#: daemon/HTTPServer.cpp:981
msgid "Client Forwards"
msgstr ""
#: daemon/HTTPServer.cpp:969
#: daemon/HTTPServer.cpp:995
msgid "Server Forwards"
msgstr ""
#: daemon/HTTPServer.cpp:1175
#: daemon/HTTPServer.cpp:1201
msgid "Unknown page"
msgstr ""
#: daemon/HTTPServer.cpp:1194
#: daemon/HTTPServer.cpp:1220
msgid "Invalid token"
msgstr ""
#: daemon/HTTPServer.cpp:1252 daemon/HTTPServer.cpp:1309
#: daemon/HTTPServer.cpp:1337
#: daemon/HTTPServer.cpp:1278 daemon/HTTPServer.cpp:1335
#: daemon/HTTPServer.cpp:1371
msgid "SUCCESS"
msgstr ""
#: daemon/HTTPServer.cpp:1252
#: daemon/HTTPServer.cpp:1278
msgid "Stream closed"
msgstr ""
#: daemon/HTTPServer.cpp:1254
#: daemon/HTTPServer.cpp:1280
msgid "Stream not found or already was closed"
msgstr ""
#: daemon/HTTPServer.cpp:1257
#: daemon/HTTPServer.cpp:1283
msgid "Destination not found"
msgstr ""
#: daemon/HTTPServer.cpp:1260
#: daemon/HTTPServer.cpp:1286
msgid "StreamID can't be null"
msgstr ""
#: daemon/HTTPServer.cpp:1262 daemon/HTTPServer.cpp:1327
#: daemon/HTTPServer.cpp:1288 daemon/HTTPServer.cpp:1353
msgid "Return to destination page"
msgstr ""
#: daemon/HTTPServer.cpp:1263 daemon/HTTPServer.cpp:1276
msgid "You will be redirected back in 5 seconds"
#: daemon/HTTPServer.cpp:1289 daemon/HTTPServer.cpp:1302
#: daemon/HTTPServer.cpp:1373
msgid "You will be redirected in 5 seconds"
msgstr ""
#: daemon/HTTPServer.cpp:1274
#: daemon/HTTPServer.cpp:1300
msgid "Transit tunnels count must not exceed 65535"
msgstr ""
#: daemon/HTTPServer.cpp:1275 daemon/HTTPServer.cpp:1338
#: daemon/HTTPServer.cpp:1301 daemon/HTTPServer.cpp:1372
msgid "Back to commands list"
msgstr ""
#: daemon/HTTPServer.cpp:1311
#: daemon/HTTPServer.cpp:1337
msgid "Register at reg.i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1312
#: daemon/HTTPServer.cpp:1338
msgid "Description"
msgstr ""
#: daemon/HTTPServer.cpp:1312
#: daemon/HTTPServer.cpp:1338
msgid "A bit information about service on domain"
msgstr ""
#: daemon/HTTPServer.cpp:1313
#: daemon/HTTPServer.cpp:1339
msgid "Submit"
msgstr ""
#: daemon/HTTPServer.cpp:1319
#: daemon/HTTPServer.cpp:1345
msgid "Domain can't end with .b32.i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1322
#: daemon/HTTPServer.cpp:1348
msgid "Domain must end with .i2p"
msgstr ""
#: daemon/HTTPServer.cpp:1325
#: daemon/HTTPServer.cpp:1351
msgid "Such destination is not found"
msgstr ""
#: daemon/HTTPServer.cpp:1333
#: daemon/HTTPServer.cpp:1367
msgid "Unknown command"
msgstr ""
#: daemon/HTTPServer.cpp:1337
#: daemon/HTTPServer.cpp:1371
msgid "Command accepted"
msgstr ""
#: daemon/HTTPServer.cpp:1339
msgid "You will be redirected in 5 seconds"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:157
msgid "Proxy error"
msgstr ""
@ -592,7 +592,7 @@ msgid "You may try to find this host on jump services below"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:273 libi2pd_client/HTTPProxy.cpp:288
#: libi2pd_client/HTTPProxy.cpp:365
#: libi2pd_client/HTTPProxy.cpp:322 libi2pd_client/HTTPProxy.cpp:365
msgid "Invalid request"
msgstr ""
@ -637,10 +637,6 @@ msgstr ""
msgid "to update record"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:322
msgid "Invalid Request"
msgstr ""
#: libi2pd_client/HTTPProxy.cpp:322
msgid "invalid request uri"
msgstr ""

@ -5,3 +5,6 @@ msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(
msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n
{"$1", "$2"},\n
^#:(.*)$\n
<to empty line>

@ -15,6 +15,10 @@
## Default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d
# tunnelsdir = /var/lib/i2pd/tunnels.d
## Path to certificates used for verifying .su3, families
## Default: ~/.i2pd/certificates or /var/lib/i2pd/certificates
# certsdir = /var/lib/i2pd/certificates
## Where to write pidfile (default: i2pd.pid, not used in Windows)
# pidfile = /run/i2pd.pid
@ -104,7 +108,8 @@ port = 7070
# user = i2pd
# pass = changeme
## Select webconsole language
## Currently supported english (default), russian, turkmen and ukrainian languages
## Currently supported english (default), afrikaans, russian, turkmen and ukrainian languages
# lang = english
[httpproxy]

@ -94,6 +94,11 @@ namespace util
i2p::config::GetOption("daemon", isDaemon);
std::string certsdir; i2p::config::GetOption("certsdir", certsdir);
i2p::fs::SetCertsDir(certsdir);
certsdir = i2p::fs::GetCertsDir();
std::string logs = ""; i2p::config::GetOption("log", logs);
std::string logfile = ""; i2p::config::GetOption("logfile", logfile);
std::string loglevel = ""; i2p::config::GetOption("loglevel", loglevel);
@ -132,6 +137,7 @@ namespace util
LogPrint(eLogNone, "i2pd v", VERSION, " starting");
LogPrint(eLogDebug, "FS: main config file: ", config);
LogPrint(eLogDebug, "FS: data directory: ", datadir);
LogPrint(eLogDebug, "FS: certificates directory: ", certsdir);
bool precomputation; i2p::config::GetOption("precomputation.elgamal", precomputation);
bool aesni; i2p::config::GetOption("cpuext.aesni", aesni);

@ -68,11 +68,11 @@ namespace http {
<< " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
<< " color: initial; padding: 0 5px; border: 1px solid #894C84; }\r\n"
<< " .header { font-size: 2.5em; text-align: center; margin: 1em 0; color: #894C84; }\r\n"
<< " .wrapper { margin: 0 auto; padding: 1em; max-width: 58em; }\r\n"
<< " .menu { float: left; } .menu a, .commands a { display: block; }\r\n"
<< " .wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n"
<< " .menu { display: block; float: left; overflow: hidden; max-width: 12em; white-space: nowrap; text-overflow: ellipsis; }\r\n"
<< " .listitem { display: block; font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
<< " .tableitem { font-family: monospace; font-size: 1.2em; white-space: nowrap; }\r\n"
<< " .content { float: left; font-size: 1em; margin-left: 4em; max-width: 45em; overflow: auto; }\r\n"
<< " .content { float: left; font-size: 1em; margin-left: 4em; max-width: 48em; overflow: auto; }\r\n"
<< " .tunnel.established { color: #56B734; } .tunnel.expiring { color: #D3AE3F; }\r\n"
<< " .tunnel.failed { color: #D33F3F; } .tunnel.building { color: #434343; }\r\n"
<< " caption { font-size: 1.5em; text-align: center; color: #894C84; }\r\n"
@ -84,19 +84,24 @@ namespace http {
<< " .slide [type=\"checkbox\"]:checked ~ div.slidecontent { display: block; margin-top: 0; padding: 0; }\r\n"
<< " .disabled:after { color: #D33F3F; content: \"" << tr("Disabled") << "\" }\r\n"
<< " .enabled:after { color: #56B734; content: \"" << tr("Enabled") << "\" }\r\n"
<< " @media screen and (max-width: 980px) {\r\n" /* adaptive style */
<< " @media screen and (max-width: 1150px) {\r\n" /* adaptive style */
<< " .wrapper { max-width: 58em; } .menu { max-width: 10em; }\r\n"
<< " .content { margin-left: 2em; max-width: 42em; }\r\n"
<< " }\r\n"
<< " @media screen and (max-width: 980px) {\r\n"
<< " body { padding: 1.5em 0 0 0; }\r\n"
<< " .menu { width: 100%; display: block; float: none; position: unset; font-size: 16px;\r\n"
<< " .menu { width: 100%; max-width: unset; display: block; float: none; position: unset; font-size: 16px;\r\n"
<< " text-align: center; }\r\n"
<< " .menu a, .commands a { padding: 2px; }\r\n"
<< " .menu a, .commands a { display: inline-block; padding: 4px; }\r\n"
<< " .content { float: none; margin-left: unset; margin-top: 16px; max-width: 100%; width: 100%;\r\n"
<< " text-align: center; }\r\n"
<< " a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n"
<< " .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n"
<< " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n"
<< " color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n"
<< " input { width: 35%; text-align: center; padding: 5px;\r\n"
<< " input, select { width: 35%; text-align: center; padding: 5px;\r\n"
<< " border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n"
<< " table.extaddr { margin: auto; text-align: unset; }\r\n"
<< " textarea { width: -webkit-fill-available; height: auto; padding:5px; border:2px solid #ccc;\r\n"
<< " -webkit-border-radius: 5px; border-radius: 5px; font-size: 12px; }\r\n"
<< " button[type=submit] { padding: 5px 15px; background: #ccc; border: 0 none; cursor: pointer;\r\n"
@ -127,6 +132,7 @@ namespace http {
const char HTTP_COMMAND_KILLSTREAM[] = "closestream";
const char HTTP_COMMAND_LIMITTRANSIT[] = "limittransit";
const char HTTP_COMMAND_GET_REG_STRING[] = "get_reg_string";
const char HTTP_COMMAND_SETLANGUAGE[] = "setlanguage";
const char HTTP_PARAM_SAM_SESSION_ID[] = "id";
const char HTTP_PARAM_ADDRESS[] = "address";
@ -157,11 +163,11 @@ namespace http {
s << std::fixed << std::setprecision(2);
auto numKBytes = (double) bytes / 1024;
if (numKBytes < 1024)
s << numKBytes << " " << tr("KiB");
s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB");
else if (numKBytes < 1024 * 1024)
s << numKBytes / 1024 << " " << tr("MiB");
s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB");
else
s << numKBytes / 1024 / 1024 << " " << tr("GiB");
s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "GiB");
}
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
@ -185,7 +191,7 @@ namespace http {
else stateText = tr("unknown");
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
s << " " << (int) (bytes / 1024) << "&nbsp;" << tr("KiB") << "\r\n";
s << " " << (int) (bytes / 1024) << "&nbsp;" << tr(/* tr: Kibibit */ "KiB") << "\r\n";
}
static void SetLogLevel (const std::string& level)
@ -223,18 +229,18 @@ namespace http {
"<div class=\"header\">" << tr("<b>i2pd</b> webconsole") << "</div>\r\n"
"<div class=\"wrapper\">\r\n"
"<div class=\"menu\">\r\n"
" <a href=\"" << webroot << "\">" << tr("Main page") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">" << tr("Router commands") << "</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">" << tr("Local destinations") << "</a>\r\n";
" <a href=\"" << webroot << "\">" << tr("Main page") << "</a><br><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_COMMANDS << "\">" << tr("Router commands") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATIONS << "\">" << tr("Local Destinations") << "</a><br>\r\n";
if (i2p::context.IsFloodfill ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">" << tr("LeaseSets") << "</a>\r\n";
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_LEASESETS << "\">" << tr("LeaseSets") << "</a><br>\r\n";
s <<
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit tunnels") << "</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a>\r\n";
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TUNNELS << "\">" << tr("Tunnels") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
if (i2p::client::context.GetSAMBridge ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a>\r\n";
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n";
s <<
"</div>\r\n"
"<div class=\"content\">";
@ -319,13 +325,13 @@ namespace http {
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
s << "<b>" << tr("Received") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n";
s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
s << "<b>" << tr("Sent") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n";
s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
s << "<b>" << tr("Transit") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr("KiB/s") << ")<br>\r\n";
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n";
s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
s << "<div class='slide'>";
if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
@ -476,7 +482,7 @@ namespace http {
s << "<div class=\"listitem\">";
it->Print(s);
if(it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr("ms") << " )";
s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
s << "</div>\r\n";
}
@ -534,7 +540,8 @@ namespace http {
if (dest)
{
ShowLeaseSetDestination (s, dest, token);
// show streams
// Print table with streams information
s << "<table>\r\n<caption>" << tr("Streams") << "</caption>\r\n<thead>\r\n<tr>";
s << "<th style=\"width:25px;\">StreamID</th>";
s << "<th style=\"width:5px;\" \\>"; // Stream closing button column
@ -679,25 +686,29 @@ namespace http {
static void ShowCommands (std::stringstream& s, uint32_t token)
{
std::string webroot; i2p::config::GetOption("http.webroot", webroot);
/* commands */
s << "<b>" << tr("Router commands") << "</b><br>\r\n<br>\r\n<div class=\"commands\">\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a>\r\n";
//s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_RUN_PEER_TEST << "&token=" << token << "\">" << tr("Run peer test") << "</a><br>\r\n";
// s << " <a href=\"/?cmd=" << HTTP_COMMAND_RELOAD_CONFIG << "\">Reload config</a><br>\r\n";
if (i2p::context.AcceptsTunnels ())
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_DISABLE_TRANSIT << "&token=" << token << "\">" << tr("Decline transit tunnels") << "</a><br>\r\n";
else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">" << tr("Accept transit tunnels") << "</a>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_ENABLE_TRANSIT << "&token=" << token << "\">" << tr("Accept transit tunnels") << "</a><br>\r\n";
#if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY))
if (Daemon.gracefulShutdownInterval)
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
#elif defined(WIN32_APP)
if (i2p::util::DaemonWin32::Instance().isGraceful)
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_CANCEL << "&token=" << token << "\">" << tr("Cancel graceful shutdown") << "</a><br>\r\n";
else
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a>\r\n";
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_START << "&token=" << token << "\">" << tr("Start graceful shutdown") << "</a><br>\r\n";
#endif
s << " <a href=\"" << webroot << "?cmd=" << HTTP_COMMAND_SHUTDOWN_NOW << "&token=" << token << "\">" << tr("Force shutdown") << "</a>\r\n";
s << "</div>";
@ -718,13 +729,26 @@ namespace http {
s << " <input type=\"number\" min=\"0\" max=\"65535\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n";
std::string currLang = i2p::context.GetLanguage ()->GetLanguage(); // get current used language
s << "<b>" << tr("Change language") << "</b><br>\r\n";
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_SETLANGUAGE << "\">\r\n";
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
s << " <select name=\"lang\" id=\"lang\">\r\n";
for (const auto& it: i2p::i18n::languages)
s << " <option value=\"" << it.first << "\"" << ((it.first.compare(currLang) == 0) ? " selected" : "") << ">" << it.second.LocaleName << "</option>\r\n";
s << " </select>\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n";
}
void ShowTransitTunnels (std::stringstream& s)
{
if(i2p::tunnel::tunnels.CountTransitTunnels())
{
s << "<b>" << tr("Transit tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{
s << "<div class=\"listitem\">\r\n";
@ -740,7 +764,7 @@ namespace http {
}
else
{
s << "<b>" << tr("Transit tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n";
s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n";
}
}
@ -1260,7 +1284,7 @@ namespace http {
s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n";
s << "<p>" << tr("You will be redirected back in 5 seconds") << "</b>";
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32;
res.add_header("Refresh", redirect.c_str());
return;
@ -1273,7 +1297,7 @@ namespace http {
else {
s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed 65535") << "\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n";
s << "<p>" << tr("You will be redirected back in 5 seconds") << "</b>";
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>";
res.add_header("Refresh", redirect.c_str());
return;
}
@ -1281,7 +1305,7 @@ namespace http {
else if (cmd == HTTP_COMMAND_GET_REG_STRING)
{
std::string b32 = params["b32"];
std::string name = params["name"];
std::string name = i2p::http::UrlDecode(params["name"]);
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
@ -1327,6 +1351,14 @@ namespace http {
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a>\r\n";
return;
}
else if (cmd == HTTP_COMMAND_SETLANGUAGE)
{
std::string lang = params["lang"];
std::string currLang = i2p::context.GetLanguage ()->GetLanguage();
if (currLang.compare(lang) != 0)
i2p::i18n::SetLanguage(lang);
}
else
{
res.code = 400;

@ -406,7 +406,7 @@ namespace client
void I2PControlService::UptimeHandler (std::ostringstream& results)
{
InsertParam (results, "i2p.router.uptime", (int)i2p::context.GetUptime ()*1000);
InsertParam (results, "i2p.router.uptime", std::to_string (i2p::context.GetUptime ()*1000LL));
}
void I2PControlService::VersionHandler (std::ostringstream& results)

@ -18,8 +18,11 @@ namespace i2p
{
namespace i18n
{
namespace afrikaans // language
namespace afrikaans // language namespace
{
// language name in lowercase
static std::string language = "afrikaans";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
@ -65,7 +68,7 @@ namespace afrikaans // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language

@ -19,8 +19,11 @@ namespace i2p
{
namespace i18n
{
namespace english // language
namespace english // language namespace
{
// language name in lowercase
static std::string language = "english";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
@ -39,7 +42,7 @@ namespace english // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language

@ -17,16 +17,11 @@ namespace i18n
{
inline void SetLanguage(const std::string &lang)
{
if (!lang.compare("afrikaans"))
i2p::context.SetLanguage (i2p::i18n::afrikaans::GetLocale());
else if (!lang.compare("russian"))
i2p::context.SetLanguage (i2p::i18n::russian::GetLocale());
else if (!lang.compare("turkmen"))
i2p::context.SetLanguage (i2p::i18n::turkmen::GetLocale());
else if (!lang.compare("ukrainian"))
i2p::context.SetLanguage (i2p::i18n::ukrainian::GetLocale());
else // fallback
const auto it = i2p::i18n::languages.find(lang);
if (it == i2p::i18n::languages.end()) // fallback
i2p::context.SetLanguage (i2p::i18n::english::GetLocale());
else
i2p::context.SetLanguage (it->second.LocaleFunc());
}
inline std::string translate (const std::string& arg)

@ -17,10 +17,17 @@ namespace i18n
{
public:
Locale (
const std::string& language,
const std::map<std::string, std::string>& strings,
const std::map<std::string, std::vector<std::string>>& plurals,
std::function<int(int)> formula
): m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
): m_Language (language), m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { };
// Get activated language name for webconsole
std::string GetLanguage() const
{
return m_Language;
}
std::string GetString (const std::string& arg) const
{
@ -50,11 +57,18 @@ namespace i18n
}
private:
const std::string m_Language;
const std::map<std::string, std::string> m_Strings;
const std::map<std::string, std::vector<std::string>> m_Plurals;
std::function<int(int)> m_Formula;
};
struct langData
{
std::string LocaleName; //localized name
std::function<std::shared_ptr<const i2p::i18n::Locale> (void)> LocaleFunc;
};
// Add localization here with language name as namespace
namespace afrikaans { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace english { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
@ -62,6 +76,18 @@ namespace i18n
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
/**
* That map contains international language name lower-case and name in it's language
*/
static std::map<std::string, langData> languages
{
{ "afrikaans", {"Afrikaans", i2p::i18n::afrikaans::GetLocale} },
{ "english", {"English", i2p::i18n::english::GetLocale} },
{ "russian", {"русский язык", i2p::i18n::russian::GetLocale} },
{ "turkmen", {"türkmen dili", i2p::i18n::turkmen::GetLocale} },
{ "ukrainian", {"украї́нська мо́ва", i2p::i18n::ukrainian::GetLocale} },
};
} // i18n
} // i2p

@ -18,8 +18,11 @@ namespace i2p
{
namespace i18n
{
namespace russian // language
namespace russian // language namespace
{
// language name in lowercase
static std::string language = "russian";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
@ -28,72 +31,28 @@ namespace russian // language
static std::map<std::string, std::string> strings
{
// HTTP Proxy
{"Proxy error", "Ошибка прокси"},
{"Proxy info", "Информация прокси"},
{"Proxy error: Host not found", "Ошибка прокси: Адрес не найден"},
{"Remote host not found in router's addressbook", "Запрошенный адрес не найден в адресной книге роутера"},
{"You may try to find this host on jump services below", "Вы можете попробовать найти адрес на джамп сервисах ниже"},
{"Invalid request", "Некорректный запрос"},
{"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"},
{"addresshelper is not supported", "addresshelper не поддерживается"},
{"Host", "Адрес"},
{"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"},
{"already in router's addressbook", "уже в адресной книге роутера"},
{"Click", "Нажмите"},
{"here", "здесь"},
{"to proceed", "чтобы продолжить"},
{"to update record", "чтобы обновить запись"},
{"Addresshelper found", "Найден addresshelper"},
{"invalid request uri", "некорректный URI запроса"},
{"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"},
{"Outproxy failure", "Ошибка внешнего прокси"},
{"bad outproxy settings", "некорректные настройки внешнего прокси"},
{"not inside I2P network, but outproxy is not enabled", "не в I2P сети, но внешний прокси не включен"},
{"unknown outproxy url", "неизвестный URL внешнего прокси"},
{"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"},
{"hostname too long", "имя хоста слишком длинное"},
{"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"},
{"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"},
{"CONNECT error", "Ошибка CONNECT запроса"},
{"Failed to Connect", "Не удалось подключиться"},
{"socks proxy error", "ошибка SOCKS прокси"},
{"failed to send request to upstream", "не удалось отправить запрос вышестоящему прокси"},
{"No Reply From socks proxy", "Нет ответа от SOCKS прокси сервера"},
{"cannot connect", "не удалось подключиться"},
{"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"},
{"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"},
{"Host is down", "Адрес недоступен"},
{"Can't create connection to requested host, it may be down. Please try again later.",
"Не удалось установить соединение к запрошенному адресу, возможно он не в сети. Попробуйте повторить запрос позже."},
// Webconsole //
// cssStyles
{"Disabled", "Выключено"},
{"Enabled", "Включено"},
// ShowTraffic
{"KiB", "КиБ"},
{"MiB", "МиБ"},
{"GiB", "ГиБ"},
// ShowTunnelDetails
{"building", "строится"},
{"failed", "неудачный"},
{"expiring", "истекает"},
{"established", "работает"},
{"exploratory", "исследовательский"},
{"unknown", "неизвестно"},
{"exploratory", "исследовательский"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
// ShowPageHead
{"Main page", "Главная"},
{"Router commands", "Команды роутера"},
{"Local destinations", "Локальные назнач."},
{"Local destinations", "Локальные назначения"},
{"LeaseSets", "Лизсеты"},
{"Tunnels", "Туннели"},
{"Transit tunnels", "Транзит. туннели"},
{"Transit tunnels", "Транзитные туннели"},
{"Transports", "Транспорты"},
{"I2P tunnels", "I2P туннели"},
{"SAM sessions", "SAM сессии"},
// Network Status
{"ERROR", "ОШИБКА"},
{"OK", "OK"},
{"Testing", "Тестирование"},
{"Firewalled", "Заблокировано извне"},
@ -104,7 +63,6 @@ namespace russian // language
{"Clock skew", "Не точное время"},
{"Offline", "Оффлайн"},
{"Symmetric NAT", "Симметричный NAT"},
// Status
{"Uptime", "В сети"},
{"Network status", "Сетевой статус"},
{"Network status v6", "Сетевой статус v6"},
@ -112,9 +70,9 @@ namespace russian // language
{"Family", "Семейство"},
{"Tunnel creation success rate", "Успешно построенных туннелей"},
{"Received", "Получено"},
{"KiB/s", "КиБ/с"},
{"Sent", "Отправлено"},
{"Transit", "Транзит"},
{"KiB/s", "КиБ/с"},
{"Data path", "Путь к данным"},
{"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."},
{"Router Ident", "Идентификатор роутера"},
@ -125,25 +83,21 @@ namespace russian // language
{"supported", "поддерживается"},
{"Routers", "Роутеры"},
{"Floodfills", "Флудфилы"},
{"LeaseSets", "Лизсеты"},
{"Client Tunnels", "Клиентские туннели"},
{"Transit Tunnels", "Транзитные туннели"},
{"Services", "Сервисы"},
// ShowLocalDestinations
{"Local Destinations", "Локальные назначения"},
// ShowLeaseSetDestination
{"Encrypted B33 address", "Шифрованные B33 адреса"},
{"Address registration line", "Строка регистрации адреса"},
{"Domain", "Домен"},
{"Generate", "Сгенерировать"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.",
"<b>Примечание:</b> полученная строка может быть использована только для регистрации доменов второго уровня. Для регистрации поддоменов используйте i2pd-tools."},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Примечание:</b> полученная строка может быть использована только для регистрации доменов второго уровня (example.i2p). Для регистрации поддоменов используйте i2pd-tools."},
{"Address", "Адрес"},
{"Type", "Тип"},
{"EncType", "ТипШифр"},
{"Inbound tunnels", "Входящие туннели"},
{"ms", "мс"},
{"Outbound tunnels", "Исходящие туннели"},
{"ms", "мс"}, // milliseconds
{"Tags", "Теги"},
{"Incoming", "Входящие"},
{"Outgoing", "Исходящие"},
@ -152,14 +106,11 @@ namespace russian // language
{"Incoming Tags", "Входящие Теги"},
{"Tags sessions", "Сессии Тегов"},
{"Status", "Статус"},
// ShowLocalDestination
{"Local Destination", "Локальное назначение"},
{"Streams", "Стримы"},
{"Close stream", "Закрыть стрим"},
// ShowI2CPLocalDestination
{"I2CP session not found", "I2CP сессия не найдена"},
{"I2CP is not enabled", "I2CP не включен"},
// ShowLeasesSets
{"Invalid", "Некорректный"},
{"Store type", "Тип хранилища"},
{"Expires", "Истекает"},
@ -168,51 +119,37 @@ namespace russian // language
{"TunnelID", "ID туннеля"},
{"EndDate", "Заканчивается"},
{"not floodfill", "не флудфил"},
// ShowTunnels
{"Queue size", "Размер очереди"},
// ShowCommands
{"Run peer test", "Запустить тестирование"},
{"Decline transit tunnels", "Отклонять транзитные туннели"},
{"Accept transit tunnels", "Принимать транзитные туннели"},
{"Cancel graceful shutdown", "Отменить плавную остановку"},
{"Start graceful shutdown", "Запустить плавную остановку"},
{"Force shutdown", "Принудительная остановка"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.",
"<b>Примечание:</b> любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примечание:</b> любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."},
{"Logging level", "Уровень логирования"},
{"Transit tunnels limit", "Лимит транзитных туннелей"},
{"Change", "Изменить"},
// ShowTransitTunnels
{"Change language", "Изменение языка"},
{"no transit tunnels currently built", "нет построенных транзитных туннелей"},
// ShowSAMSessions/ShowSAMSession
{"SAM disabled", "SAM выключен"},
{"SAM session not found", "SAM сессия не найдена"},
{"no sessions currently running", "нет запущенных сессий"},
{"SAM session not found", "SAM сессия не найдена"},
{"SAM Session", "SAM сессия"},
// ShowI2PTunnels
{"Server Tunnels", "Серверные туннели"},
{"Client Forwards", "Клиентские перенаправления"},
{"Server Forwards", "Серверные перенаправления"},
// HandlePage
{"Unknown page", "Неизвестная страница"},
// HandleCommand, ShowError
{"Invalid token", "Неверный токен"},
{"SUCCESS", "УСПЕШНО"},
{"ERROR", "ОШИБКА"},
{"Unknown command", "Неизвестная команда"},
{"Command accepted", "Команда принята"},
{"Back to commands list", "Вернуться к списку команд"},
{"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"},
// HTTP_COMMAND_KILLSTREAM
{"Stream closed", "Стрим закрыт"},
{"Stream not found or already was closed", "Стрим не найден или уже закрыт"},
{"Destination not found", "Точка назначения не найдена"},
{"StreamID can't be null", "StreamID не может быть пустым"},
{"Return to destination page", "Вернуться на страницу точки назначения"},
{"You will be redirected back in 5 seconds", "Вы будете переадресованы назад через 5 секунд"},
// HTTP_COMMAND_LIMITTRANSIT
{"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"},
// HTTP_COMMAND_GET_REG_STRING
{"Back to commands list", "Вернуться к списку команд"},
{"Register at reg.i2p", "Зарегистрировать на reg.i2p"},
{"Description", "Описание"},
{"A bit information about service on domain", "Немного информации о сервисе на домене"},
@ -220,12 +157,51 @@ namespace russian // language
{"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"},
{"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"},
{"Such destination is not found", "Такая точка назначения не найдена"},
{"Unknown command", "Неизвестная команда"},
{"Command accepted", "Команда принята"},
{"You will be redirected in 5 seconds", "Вы будете переадресованы через 5 секунд"},
{"Proxy error", "Ошибка прокси"},
{"Proxy info", "Информация прокси"},
{"Proxy error: Host not found", "Ошибка прокси: Узел не найден"},
{"Remote host not found in router's addressbook", "Запрошенный узел не найден в адресной книге роутера"},
{"You may try to find this host on jump services below", "Вы можете попробовать найти узел через джамп сервисы ниже"},
{"Invalid request", "Некорректный запрос"},
{"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"},
{"addresshelper is not supported", "addresshelper не поддерживается"},
{"Host", "Узел"},
{"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"},
{"Click", "Нажмите"},
{"here", "здесь"},
{"to proceed", "чтобы продолжить"},
{"Addresshelper found", "Найден addresshelper"},
{"already in router's addressbook", "уже в адресной книге роутера"},
{"to update record", "чтобы обновить запись"},
{"Invalid Request", "неверный запрос"},
{"invalid request uri", "некорректный URI запроса"},
{"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"},
{"Outproxy failure", "Ошибка внешнего прокси"},
{"bad outproxy settings", "некорректные настройки внешнего прокси"},
{"not inside I2P network, but outproxy is not enabled", "не в I2P сети, но внешний прокси не включен"},
{"unknown outproxy url", "неизвестный URL внешнего прокси"},
{"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"},
{"hostname too long", "имя хоста слишком длинное"},
{"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"},
{"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"},
{"CONNECT error", "Ошибка CONNECT запроса"},
{"Failed to Connect", "Не удалось подключиться"},
{"socks proxy error", "ошибка SOCKS прокси"},
{"failed to send request to upstream", "не удалось отправить запрос вышестоящему прокси"},
{"No Reply From socks proxy", "Нет ответа от SOCKS прокси сервера"},
{"cannot connect", "не удалось подключиться"},
{"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"},
{"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"},
{"Host is down", "Узел недоступен"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
// ShowUptime
{"days", {"день", "дня", "дней"}},
{"hours", {"час", "часа", "часов"}},
{"minutes", {"минуту", "минуты", "минут"}},
@ -235,7 +211,7 @@ namespace russian // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language

@ -18,8 +18,11 @@ namespace i2p
{
namespace i18n
{
namespace turkmen // language
namespace turkmen // language namespace
{
// language name in lowercase
static std::string language = "turkmen";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
@ -234,7 +237,7 @@ namespace turkmen // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language

@ -18,8 +18,11 @@ namespace i2p
{
namespace i18n
{
namespace ukrainian // language
namespace ukrainian // language namespace
{
// language name in lowercase
static std::string language = "ukrainian";
// See for language plural forms here:
// https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html
static int plural (int n) {
@ -28,64 +31,20 @@ namespace ukrainian // language
static std::map<std::string, std::string> strings
{
// HTTP Proxy
{"Proxy error", "Помилка проксі"},
{"Proxy info", "Інформація проксі"},
{"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"},
{"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі роутера"},
{"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"},
{"Invalid request", "Некоректний запит"},
{"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"},
{"addresshelper is not supported", "addresshelper не підтримується"},
{"Host", "Адреса"},
{"added to router's addressbook from helper", "доданий в адресну книгу роутера через хелпер"},
{"already in router's addressbook", "вже в адресній книзі роутера"},
{"Click", "Натисніть"},
{"here", "тут"},
{"to proceed", "щоб продовжити"},
{"to update record", "щоб оновити запис"},
{"Addresshelper found", "Знайдено addresshelper"},
{"invalid request uri", "некоректний URI запиту"},
{"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"},
{"Outproxy failure", "Помилка зовнішнього проксі"},
{"bad outproxy settings", "некоректні налаштування зовнішнього проксі"},
{"not inside I2P network, but outproxy is not enabled", "не в I2P мережі, але зовнішній проксі не включений"},
{"unknown outproxy url", "невідомий URL зовнішнього проксі"},
{"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"},
{"hostname too long", "ім'я вузла надто довге"},
{"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"},
{"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"},
{"CONNECT error", "Помилка CONNECT запиту"},
{"Failed to Connect", "Не вдалося підключитися"},
{"socks proxy error", "помилка SOCKS проксі"},
{"failed to send request to upstream", "не вдалося відправити запит висхідному проксі"},
{"No Reply From socks proxy", "Немає відповіді від SOCKS проксі сервера"},
{"cannot connect", "не вдалося підключитися"},
{"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"},
{"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"},
{"Host is down", "Вузол недоступний"},
{"Can't create connection to requested host, it may be down. Please try again later.",
"Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
// Webconsole //
// cssStyles
{"Disabled", "Вимкнуто"},
{"Enabled", "Увімкнуто"},
// ShowTraffic
{"KiB", "КіБ"},
{"MiB", "МіБ"},
{"GiB", "ГіБ"},
// ShowTunnelDetails
{"building", "будується"},
{"failed", "невдалий"},
{"expiring", "завершується"},
{"established", "працює"},
{"exploratory", "дослідницький"},
{"unknown", "невідомо"},
{"exploratory", "дослідницький"},
{"<b>i2pd</b> webconsole", "Веб-консоль <b>i2pd</b>"},
// ShowPageHead
{"Main page", "Головна"},
{"Router commands", "Команди роутера"},
{"Router commands", "Команди маршрутизатора"},
{"Local destinations", "Локальні призначення"},
{"LeaseSets", "Лізсети"},
{"Tunnels", "Тунелі"},
@ -93,7 +52,7 @@ namespace ukrainian // language
{"Transports", "Транспорти"},
{"I2P tunnels", "I2P тунелі"},
{"SAM sessions", "SAM сесії"},
// Network Status
{"ERROR", "ПОМИЛКА"},
{"OK", "OK"},
{"Testing", "Тестування"},
{"Firewalled", "Заблоковано ззовні"},
@ -104,61 +63,54 @@ namespace ukrainian // language
{"Clock skew", "Неточний час"},
{"Offline", "Офлайн"},
{"Symmetric NAT", "Симетричний NAT"},
// Status
{"Uptime", "В мережі"},
{"Uptime", "У мережі"},
{"Network status", "Мережевий статус"},
{"Network status v6", "Мережевий статус v6"},
{"Stopping in", "Зупинка через"},
{"Family", "Сімейство"},
{"Tunnel creation success rate", "Успішно побудованих тунелів"},
{"Received", "Отримано"},
{"KiB/s", "КіБ/с"},
{"Sent", "Відправлено"},
{"Transit", "Транзит"},
{"KiB/s", "КіБ/с"},
{"Data path", "Шлях до даних"},
{"Hidden content. Press on text to see.", "Прихований вміст. Натисніть на текст щоб відобразити."},
{"Router Ident", "Ідентифікатор Роутера"},
{"Router Family", "Сімейство Роутера"},
{"Router Caps", "Прапорці Роутера"},
{"Hidden content. Press on text to see.", "Прихований вміст. Щоб відобразити, натисніть на текст."},
{"Router Ident", "Ідентифікатор маршрутизатора"},
{"Router Family", "Сімейство маршрутизатора"},
{"Router Caps", "Прапорці маршрутизатора"},
{"Version", "Версія"},
{"Our external address", "Наша зовнішня адреса"},
{"supported", "підтримується"},
{"Routers", "Роутери"},
{"Routers", "Маршрутизатори"},
{"Floodfills", "Флудфіли"},
{"Client Tunnels", "Клієнтські Тунелі"},
{"Transit Tunnels", "Транзитні Тунелі"},
{"Services", "Сервіси"},
// ShowLocalDestinations
{"Local Destinations", "Локальні Призначення"},
// ShowLeaseSetDestination
{"Encrypted B33 address", "Шифровані B33 адреси"},
{"Address registration line", "Рядок реєстрації адреси"},
{"Domain", "Домен"},
{"Generate", "Згенерувати"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.",
"<b>Примітка:</b> отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня. Для реєстрації піддоменів використовуйте i2pd-tools."},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Примітка:</b> отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня (example.i2p). Для реєстрації піддоменів використовуйте i2pd-tools."},
{"Address", "Адреса"},
{"Type", "Тип"},
{"EncType", "ТипШифр"},
{"Inbound tunnels", "Вхідні тунелі"},
{"ms", "мс"},
{"Outbound tunnels", "Вихідні тунелі"},
{"ms", "мс"}, // milliseconds
{"Tags", "Теги"},
{"Incoming", "Вхідні"},
{"Outgoing", "Вихідні"},
{"Destination", "Призначення"},
{"Amount", "Кількість"},
{"Incoming Tags", "Вхідні Теги"},
{"Tags sessions", "Сесії тегів"},
{"Tags sessions", "Сесії Тегів"},
{"Status", "Статус"},
// ShowLocalDestination
{"Local Destination", "Локальні Призначення"},
{"Streams", "Потоки"},
{"Close stream", "Закрити потік"},
// ShowI2CPLocalDestination
{"I2CP session not found", "I2CP сесія не знайдена"},
{"I2CP is not enabled", "I2CP не увікнуто"},
// ShowLeasesSets
{"Invalid", "Некоректний"},
{"Store type", "Тип сховища"},
{"Expires", "Завершується"},
@ -167,51 +119,36 @@ namespace ukrainian // language
{"TunnelID", "ID тунеля"},
{"EndDate", "Закінчується"},
{"not floodfill", "не флудфіл"},
// ShowTunnels
{"Queue size", "Розмір черги"},
// ShowCommands
{"Run peer test", "Запустити тестування"},
{"Decline transit tunnels", "Відхиляти транзитні тунелі"},
{"Accept transit tunnels", "Ухвалювати транзитні тунелі"},
{"Cancel graceful shutdown", "Скасувати плавну зупинку"},
{"Start graceful shutdown", "Запустити плавну зупинку"},
{"Force shutdown", "Примусова зупинка"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.",
"<b>Примітка:</b> будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Примітка:</b> будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."},
{"Logging level", "Рівень логування"},
{"Transit tunnels limit", "Обмеження транзитних тунелів"},
{"Change", "Змінити"},
// ShowTransitTunnels
{"no transit tunnels currently built", "немає побудованих транзитних тунелів"},
// ShowSAMSessions/ShowSAMSession
{"SAM disabled", "SAM вимкнуто"},
{"SAM session not found", "SAM сесія не знайдена"},
{"no sessions currently running", "немає запущених сесій"},
{"SAM session not found", "SAM сесія не знайдена"},
{"SAM Session", "SAM сесія"},
// ShowI2PTunnels
{"Server Tunnels", "Серверні Тунелі"},
{"Client Forwards", "Клієнтські Переспрямування"},
{"Server Forwards", "Серверні Переспрямування"},
// HandlePage
{"Unknown page", "Невідома сторінка"},
// HandleCommand, ShowError
{"Invalid token", "Невірний токен"},
{"SUCCESS", "УСПІШНО"},
{"ERROR", "ПОМИЛКА"},
{"Unknown command", "Невідома команда"},
{"Command accepted", "Команда прийнята"},
{"Back to commands list", "Повернутися до списку команд"},
{"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"},
// HTTP_COMMAND_KILLSTREAM
{"Stream closed", "Потік зачинений"},
{"Stream not found or already was closed", "Потік не знайдений або вже зачинений"},
{"Destination not found", "Точка призначення не знайдена"},
{"StreamID can't be null", "Ідентифікатор потоку не може бути порожнім"},
{"Return to destination page", "Повернутися на сторінку точки призначення"},
{"You will be redirected back in 5 seconds", "Ви будете переадресовані назад через 5 секунд"},
// HTTP_COMMAND_LIMITTRANSIT
{"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"},
// HTTP_COMMAND_GET_REG_STRING
{"Back to commands list", "Повернутися до списку команд"},
{"Register at reg.i2p", "Зареєструвати на reg.i2p"},
{"Description", "Опис"},
{"A bit information about service on domain", "Трохи інформації про сервіс на домені"},
@ -219,14 +156,53 @@ namespace ukrainian // language
{"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"},
{"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"},
{"Such destination is not found", "Така точка призначення не знайдена"},
{"Unknown command", "Невідома команда"},
{"Command accepted", "Команда прийнята"},
{"You will be redirected in 5 seconds", "Ви будете переадресовані через 5 секунд"},
{"Proxy error", "Помилка проксі"},
{"Proxy info", "Інформація проксі"},
{"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"},
{"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі маршрутизатора"},
{"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"},
{"Invalid request", "Некоректний запит"},
{"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"},
{"addresshelper is not supported", "addresshelper не підтримується"},
{"Host", "Адреса"},
{"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"},
{"Click", "Натисніть"},
{"here", "тут"},
{"to proceed", "щоб продовжити"},
{"Addresshelper found", "Знайдено addresshelper"},
{"already in router's addressbook", "вже в адресній книзі маршрутизатора"},
{"to update record", "щоб оновити запис"},
{"Invalid Request", "Некоректний Запит"},
{"invalid request uri", "некоректний URI запиту"},
{"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"},
{"Outproxy failure", "Помилка зовнішнього проксі"},
{"bad outproxy settings", "некоректні налаштування зовнішнього проксі"},
{"not inside I2P network, but outproxy is not enabled", "не в I2P мережі, але зовнішній проксі не включений"},
{"unknown outproxy url", "невідомий URL зовнішнього проксі"},
{"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"},
{"hostname too long", "ім'я вузла надто довге"},
{"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"},
{"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"},
{"CONNECT error", "Помилка CONNECT запиту"},
{"Failed to Connect", "Не вдалося підключитися"},
{"socks proxy error", "помилка SOCKS проксі"},
{"failed to send request to upstream", "не вдалося відправити запит висхідному проксі"},
{"No Reply From socks proxy", "Немає відповіді від SOCKS проксі сервера"},
{"cannot connect", "не вдалося підключитися"},
{"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"},
{"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"},
{"Host is down", "Вузол недоступний"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
// ShowUptime
{"days", {"день", "дня", "днів"}},
{"hours", {"годину", "години", "годин"}},
{"days", {"день", "дня", "днів"}},
{"hours", {"годину", "години", "годин"}},
{"minutes", {"хвилину", "хвилини", "хвилин"}},
{"seconds", {"секунду", "секунди", "секунд"}},
{"", {"", "", ""}},
@ -234,7 +210,7 @@ namespace ukrainian // language
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(strings, plurals, [] (int n)->int { return plural(n); });
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language

@ -37,6 +37,7 @@ namespace config {
("conf", value<std::string>()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)")
("tunconf", value<std::string>()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)")
("tunnelsdir", value<std::string>()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d")
("certsdir", value<std::string>()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates")
("pidfile", value<std::string>()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)")
("log", value<std::string>()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)")
("logfile", value<std::string>()->default_value(""), "Path to logfile (stdout if not set, autodetect if daemon)")
@ -284,7 +285,7 @@ namespace config {
options_description meshnets("Meshnet transports options");
meshnets.add_options()
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)")
("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)")
("meshnets.yggaddress", value<std::string>()->default_value(""), "Yggdrasil address to publish")
;

@ -24,6 +24,7 @@ namespace i2p {
namespace fs {
std::string appName = "i2pd";
std::string dataDir = "";
std::string certsDir = "";
#ifdef _WIN32
std::string dirSep = "\\";
#else
@ -42,6 +43,10 @@ namespace fs {
return dataDir;
}
const std::string & GetCertsDir () {
return certsDir;
}
const std::string GetUTF8DataDir () {
#ifdef _WIN32
boost::filesystem::wpath path (dataDir);
@ -126,6 +131,21 @@ namespace fs {
#endif
}
void SetCertsDir(const std::string & cmdline_certsdir) {
if (cmdline_certsdir != "")
{
if (cmdline_certsdir[cmdline_certsdir.length()-1] == '/')
certsDir = cmdline_certsdir.substr(0, cmdline_certsdir.size()-1); // strip trailing slash
else
certsDir = cmdline_certsdir;
}
else
{
certsDir = i2p::fs::DataDirPath("certificates");
}
return;
}
bool Init() {
if (!boost::filesystem::exists(dataDir))
boost::filesystem::create_directory(dataDir);

@ -75,6 +75,9 @@ namespace fs {
/** @brief Returns datadir path */
const std::string & GetDataDir();
/** @brief Returns certsdir path */
const std::string & GetCertsDir();
/** @brief Returns datadir path in UTF-8 encoding */
const std::string GetUTF8DataDir();
@ -90,7 +93,20 @@ namespace fs {
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/
* Unix: /var/lib/i2pd/ (system=1) >> ~/.i2pd/ or /tmp/i2pd/
*/
void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
void DetectDataDir(const std::string & cmdline_datadir, bool isService = false);
/**
* @brief Set certsdir either from cmdline option or using autodetection
* @param cmdline_param Value of cmdline parameter --certsdir=<something>
*
* Examples of autodetected paths:
*
* Windows < Vista: C:\Documents and Settings\Username\Application Data\i2pd\certificates
* Windows >= Vista: C:\Users\Username\AppData\Roaming\i2pd\certificates
* Mac: /Library/Application Support/i2pd/ or ~/Library/Application Support/i2pd/certificates
* Unix: /var/lib/i2pd/certificates (system=1) >> ~/.i2pd/ or /tmp/i2pd/certificates
*/
void SetCertsDir(const std::string & cmdline_certsdir);
/**
* @brief Create subdirectories inside datadir

@ -13,6 +13,7 @@
#include "FS.h"
#include "Log.h"
#include "Family.h"
#include "Config.h"
namespace i2p
{
@ -98,7 +99,8 @@ namespace data
void Families::LoadCertificates ()
{
std::string certDir = i2p::fs::DataDirPath("certificates", "family");
std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "family";
std::vector<std::string> files;
int numCertificates = 0;

@ -36,10 +36,21 @@ namespace i2p
return std::make_shared<I2NPMessageBuffer<I2NP_MAX_SHORT_MESSAGE_SIZE> >();
}
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage ()
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint)
{
auto msg = new I2NPMessageBuffer<i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34>(); // reserved for alignment and NTCP 16 + 6 + 12
msg->Align (12);
I2NPMessage * msg = nullptr;
if (endpoint)
{
// should fit two tunnel message + tunnel gateway header, enough for one garlic encrypted streaming packet
msg = new I2NPMessageBuffer<2*i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28>(); // reserved for alignment and NTCP 16 + 6 + 6
msg->Align (6);
msg->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
}
else
{
msg = new I2NPMessageBuffer<i2p::tunnel::TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34>(); // reserved for alignment and NTCP 16 + 6 + 12
msg->Align (12);
}
return std::shared_ptr<I2NPMessage>(msg);
}
@ -691,7 +702,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf)
{
auto msg = NewI2NPTunnelMessage ();
auto msg = NewI2NPTunnelMessage (false);
msg->Concat (buf, i2p::tunnel::TUNNEL_DATA_MSG_SIZE);
msg->FillI2NPMessageHeader (eI2NPTunnelData);
return msg;
@ -699,7 +710,7 @@ namespace i2p
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload)
{
auto msg = NewI2NPTunnelMessage ();
auto msg = NewI2NPTunnelMessage (false);
htobe32buf (msg->GetPayload (), tunnelID);
msg->len += 4; // tunnelID
msg->Concat (payload, i2p::tunnel::TUNNEL_DATA_MSG_SIZE - 4);
@ -707,9 +718,9 @@ namespace i2p
return msg;
}
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ()
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint)
{
auto msg = NewI2NPTunnelMessage ();
auto msg = NewI2NPTunnelMessage (endpoint);
msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE;
return msg;
}

@ -55,7 +55,7 @@ namespace i2p
// TunnelBuild
const size_t TUNNEL_BUILD_RECORD_SIZE = 528;
const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 236;
const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 218;
//BuildRequestRecordClearText
const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0;
@ -113,7 +113,7 @@ namespace i2p
const size_t SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET = SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE + 1;
const size_t SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET = SHORT_REQUEST_RECORD_REQUEST_TIME_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET = SHORT_REQUEST_RECORD_REQUEST_EXPIRATION_OFFSET + 4;
const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 172;
const size_t SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE = 154;
enum I2NPMessageType
{
@ -278,7 +278,7 @@ namespace tunnel
std::shared_ptr<I2NPMessage> NewI2NPMessage ();
std::shared_ptr<I2NPMessage> NewI2NPShortMessage ();
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage ();
std::shared_ptr<I2NPMessage> NewI2NPTunnelMessage (bool endpoint);
std::shared_ptr<I2NPMessage> NewI2NPMessage (size_t len);
std::shared_ptr<I2NPMessage> CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0);
@ -307,7 +307,7 @@ namespace tunnel
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (const uint8_t * buf);
std::shared_ptr<I2NPMessage> CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload);
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg ();
std::shared_ptr<I2NPMessage> CreateEmptyTunnelDataMsg (bool endpoint);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType,

@ -497,7 +497,8 @@ namespace data
void Reseeder::LoadCertificates ()
{
std::string certDir = i2p::fs::DataDirPath("certificates", "reseed");
std::string certDir = i2p::fs::GetCertsDir() + i2p::fs::dirSep + "reseed";
std::vector<std::string> files;
int numCertificates = 0;

@ -470,7 +470,8 @@ namespace i2p
uint8_t caps = m_RouterInfo.GetCaps ();
caps &= ~i2p::data::RouterInfo::eReachable;
caps |= i2p::data::RouterInfo::eUnreachable;
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
if (v6 || !SupportsV6 ())
caps &= ~i2p::data::RouterInfo::eFloodfill; // can't be floodfill
m_RouterInfo.SetCaps (caps);
}
uint16_t port = 0;
@ -568,16 +569,21 @@ namespace i2p
{
bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2);
bool ntcp2Published; i2p::config::GetOption("ntcp2.published", ntcp2Published);
if (ntcp2 && ntcp2Published)
if (ntcp2)
{
std::string ntcp2Host;
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
if (ntcp2Published)
{
std::string ntcp2Host;
if (!i2p::config::IsDefault ("ntcp2.addressv6"))
i2p::config::GetOption ("ntcp2.addressv6", ntcp2Host);
else
ntcp2Host = "::1";
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (!ntcp2Port) ntcp2Port = port;
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
}
else
ntcp2Host = "::1";
uint16_t ntcp2Port; i2p::config::GetOption ("ntcp2.port", ntcp2Port);
if (!ntcp2Port) ntcp2Port = port;
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (ntcp2Host), ntcp2Port);
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV6);
}
}
m_RouterInfo.EnableV6 ();
@ -632,7 +638,7 @@ namespace i2p
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address::from_string (host), ntcp2Port);
}
else
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv);
m_RouterInfo.AddNTCP2Address (m_NTCP2Keys->staticPublicKey, m_NTCP2Keys->iv, boost::asio::ip::address(), 0, i2p::data::RouterInfo::eV4);
}
}
m_RouterInfo.EnableV4 ();

@ -840,21 +840,33 @@ namespace data
for (const auto& it: *m_Addresses) // don't insert same address twice
if (*it == *addr) return;
m_SupportedTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_ReachableTransports |= addr->host.is_v6 () ? eSSUV6 : eSSUV4;
m_Addresses->push_back(std::move(addr));
}
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host, int port)
void RouterInfo::AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
const boost::asio::ip::address& host, int port, uint8_t caps)
{
auto addr = std::make_shared<Address>();
addr->host = host;
addr->port = port;
addr->transportStyle = eTransportNTCP;
addr->caps = 0;
addr->caps = caps;
addr->date = 0;
addr->ntcp2.reset (new NTCP2Ext ());
if (port) addr->published = true;
memcpy (addr->ntcp2->staticKey, staticKey, 32);
memcpy (addr->ntcp2->iv, iv, 16);
if (addr->IsV4 ())
{
m_SupportedTransports |= eNTCP2V4;
if (addr->published) m_ReachableTransports |= eNTCP2V4;
}
if (addr->IsV6 ())
{
m_SupportedTransports |= eNTCP2V6;
if (addr->published) m_ReachableTransports |= eNTCP2V6;
}
m_Addresses->push_back(std::move(addr));
}

@ -179,7 +179,8 @@ namespace data
std::shared_ptr<const Address> GetYggdrasilAddress () const;
void AddSSUAddress (const char * host, int port, const uint8_t * key, int mtu = 0);
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv, const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0);
void AddNTCP2Address (const uint8_t * staticKey, const uint8_t * iv,
const boost::asio::ip::address& host = boost::asio::ip::address(), int port = 0, uint8_t caps = 0);
bool AddIntroducer (const Introducer& introducer);
bool RemoveIntroducer (const boost::asio::ip::udp::endpoint& e);
void SetProperty (const std::string& key, const std::string& value); // called from RouterContext only

@ -1050,6 +1050,7 @@ namespace stream
it.second->Terminate (false); // we delete here
m_Streams.clear ();
m_IncomingStreams.clear ();
m_LastStream = nullptr;
}
}
@ -1058,9 +1059,16 @@ namespace stream
uint32_t sendStreamID = packet->GetSendStreamID ();
if (sendStreamID)
{
auto it = m_Streams.find (sendStreamID);
if (it != m_Streams.end ())
it->second->HandleNextPacket (packet);
if (!m_LastStream || sendStreamID != m_LastStream->GetRecvStreamID ())
{
auto it = m_Streams.find (sendStreamID);
if (it != m_Streams.end ())
m_LastStream = it->second;
else
m_LastStream = nullptr;
}
if (m_LastStream)
m_LastStream->HandleNextPacket (packet);
else if (packet->IsEcho () && m_Owner->IsStreamingAnswerPings ())
{
// ping
@ -1166,7 +1174,7 @@ namespace stream
{
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this, remote, port);
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s;
m_Streams.emplace (s->GetRecvStreamID (), s);
return s;
}
@ -1174,8 +1182,8 @@ namespace stream
{
auto s = std::make_shared<Stream> (m_Owner->GetService (), *this);
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams[s->GetRecvStreamID ()] = s;
m_IncomingStreams[receiveStreamID] = s;
m_Streams.emplace (s->GetRecvStreamID (), s);
m_IncomingStreams.emplace (receiveStreamID, s);
return s;
}
@ -1186,6 +1194,7 @@ namespace stream
std::unique_lock<std::mutex> l(m_StreamsMutex);
m_Streams.erase (stream->GetRecvStreamID ());
m_IncomingStreams.erase (stream->GetSendStreamID ());
if (m_LastStream == stream) m_LastStream = nullptr;
}
}

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -11,7 +11,7 @@
#include <inttypes.h>
#include <string>
#include <map>
#include <unordered_map>
#include <set>
#include <queue>
#include <functional>
@ -297,12 +297,13 @@ namespace stream
uint16_t m_LocalPort;
bool m_Gzip; // gzip compression of data messages
std::mutex m_StreamsMutex;
std::map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
std::map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_Streams; // sendStreamID->stream
std::unordered_map<uint32_t, std::shared_ptr<Stream> > m_IncomingStreams; // receiveStreamID->stream
std::shared_ptr<Stream> m_LastStream;
Acceptor m_Acceptor;
std::list<std::shared_ptr<Stream> > m_PendingIncomingStreams;
boost::asio::deadline_timer m_PendingIncomingTimer;
std::map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
std::unordered_map<uint32_t, std::list<Packet *> > m_SavedPackets; // receiveStreamID->packets, arrived before SYN
i2p::util::MemoryPool<Packet> m_PacketsPool;
i2p::util::MemoryPool<I2NPMessageBuffer<I2NP_MAX_MESSAGE_SIZE> > m_I2NPMsgsPool;

@ -39,7 +39,7 @@ namespace tunnel
void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{
auto newMsg = CreateEmptyTunnelDataMsg ();
auto newMsg = CreateEmptyTunnelDataMsg (false);
EncryptTunnelMsg (tunnelMsg, newMsg);
m_NumTransmittedBytes += tunnelMsg->GetLength ();
@ -87,7 +87,7 @@ namespace tunnel
void TransitTunnelEndpoint::HandleTunnelDataMsg (std::shared_ptr<const i2p::I2NPMessage> tunnelMsg)
{
auto newMsg = CreateEmptyTunnelDataMsg ();
auto newMsg = CreateEmptyTunnelDataMsg (true);
EncryptTunnelMsg (tunnelMsg, newMsg);
LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ());

@ -401,7 +401,7 @@ namespace transport
try
{
auto r = netdb.FindRouter (ident);
if (!r || r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ())) return;
if (r && (r->IsUnreachable () || !r->IsReachableFrom (i2p::context.GetRouterInfo ()))) return; // router found but non-reachable
{
std::unique_lock<std::mutex> l(m_PeersMutex);
it = m_Peers.insert (std::pair<i2p::data::IdentHash, Peer>(ident, { 0, r, {},

@ -234,7 +234,7 @@ namespace tunnel
void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr<const I2NPMessage> msg)
{
if (IsFailed ()) SetState (eTunnelStateEstablished); // incoming messages means a tunnel is alive
auto newMsg = CreateEmptyTunnelDataMsg ();
auto newMsg = CreateEmptyTunnelDataMsg (true);
EncryptTunnelMsg (msg, newMsg);
newMsg->from = shared_from_this ();
m_Endpoint.HandleDecryptedTunnelDataMsg (newMsg);

@ -52,24 +52,25 @@ namespace tunnel
bool isFollowOnFragment = flag & 0x80, isLastFragment = true;
uint32_t msgID = 0;
int fragmentNum = 0;
TunnelMessageBlockEx m;
if (!isFollowOnFragment)
{
// first fragment
m.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
switch (m.deliveryType)
if (m_CurrentMsgID)
AddIncompleteCurrentMessage (); // we have got a new message while previous is not complete
m_CurrentMessage.deliveryType = (TunnelDeliveryType)((flag >> 5) & 0x03);
switch (m_CurrentMessage.deliveryType)
{
case eDeliveryTypeLocal: // 0
break;
case eDeliveryTypeTunnel: // 1
m.tunnelID = bufbe32toh (fragment);
m_CurrentMessage.tunnelID = bufbe32toh (fragment);
fragment += 4; // tunnelID
m.hash = i2p::data::IdentHash (fragment);
m_CurrentMessage.hash = i2p::data::IdentHash (fragment);
fragment += 32; // hash
break;
case eDeliveryTypeRouter: // 2
m.hash = i2p::data::IdentHash (fragment);
m_CurrentMessage.hash = i2p::data::IdentHash (fragment);
fragment += 32; // to hash
break;
default: ;
@ -81,6 +82,7 @@ namespace tunnel
// Message ID
msgID = bufbe32toh (fragment);
fragment += 4;
m_CurrentMsgID = msgID;
isLastFragment = false;
}
}
@ -96,50 +98,60 @@ namespace tunnel
uint16_t size = bufbe16toh (fragment);
fragment += 2;
msg->offset = fragment - msg->buf;
msg->len = msg->offset + size;
if (msg->len > msg->maxLen)
{
LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
return;
}
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
// handle fragment
if (isFollowOnFragment)
{
// this is not last message. we have to copy it
m.data = NewI2NPTunnelMessage ();
m.data->offset += TUNNEL_GATEWAY_HEADER_SIZE; // reserve room for TunnelGateway header
m.data->len += TUNNEL_GATEWAY_HEADER_SIZE;
*(m.data) = *msg;
}
else
m.data = msg;
if (!isFollowOnFragment && isLastFragment)
HandleNextMessage (m);
// existing message
if (m_CurrentMsgID && m_CurrentMsgID == msgID && m_CurrentMessage.nextFragmentNum == fragmentNum)
HandleCurrenMessageFollowOnFragment (fragment, size, isLastFragment); // previous
else
{
HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
}
else
{
if (msgID) // msgID is presented, assume message is fragmented
// new message
msg->offset = fragment - msg->buf;
msg->len = msg->offset + size;
// check message size
if (msg->len > msg->maxLen)
{
LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size);
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
return;
}
// create new or assign I2NP message
if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE)
{
if (!isFollowOnFragment) // create new incomlete message
{
m.nextFragmentNum = 1;
m.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
auto ret = m_IncompleteMessages.insert (std::pair<uint32_t, TunnelMessageBlockEx>(msgID, m));
if (ret.second)
HandleOutOfSequenceFragments (msgID, ret.first->second);
else
LogPrint (eLogError, "TunnelMessage: Incomplete message ", msgID, " already exists");
}
else
{
m.nextFragmentNum = fragmentNum;
HandleFollowOnFragment (msgID, isLastFragment, m);
}
// this is not last message. we have to copy it
m_CurrentMessage.data = NewI2NPTunnelMessage (true);
*(m_CurrentMessage.data) = *msg;
}
else
m_CurrentMessage.data = msg;
if (isLastFragment)
{
// single message
HandleNextMessage (m_CurrentMessage);
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
else if (msgID)
{
// first fragment of a new message
m_CurrentMessage.nextFragmentNum = 1;
m_CurrentMessage.receiveTime = i2p::util::GetMillisecondsSinceEpoch ();
HandleOutOfSequenceFragments (msgID, m_CurrentMessage);
}
else
{
LogPrint (eLogError, "TunnelMessage: Message is fragmented, but msgID is not presented");
}
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
}
fragment += size;
}
}
@ -147,27 +159,17 @@ namespace tunnel
LogPrint (eLogError, "TunnelMessage: zero not found");
}
void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m)
void TunnelEndpoint::HandleFollowOnFragment (uint32_t msgID, bool isLastFragment,
uint8_t fragmentNum, const uint8_t * fragment, size_t size)
{
auto fragment = m.data->GetBuffer ();
auto size = m.data->GetLength ();
auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end())
{
auto& msg = it->second;
if (m.nextFragmentNum == msg.nextFragmentNum)
if (fragmentNum == msg.nextFragmentNum)
{
if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long
if (ConcatFollowOnFragment (msg, fragment, size))
{
if (msg.data->len + size > msg.data->maxLen)
{
// LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (fragment, size) < size) // concatenate fragment
LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen);
if (isLastFragment)
{
// message complete
@ -182,26 +184,86 @@ namespace tunnel
}
else
{
LogPrint (eLogError, "TunnelMessage: Fragment ", m.nextFragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped");
LogPrint (eLogError, "TunnelMessage: Fragment ", fragmentNum, " of message ", msgID, "exceeds max I2NP message size, message dropped");
m_IncompleteMessages.erase (it);
}
}
else
{
LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)m.nextFragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
LogPrint (eLogWarning, "TunnelMessage: Unexpected fragment ", (int)fragmentNum, " instead ", (int)msg.nextFragmentNum, " of message ", msgID, ", saved");
AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size);
}
}
else
{
LogPrint (eLogWarning, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
AddOutOfSequenceFragment (msgID, m.nextFragmentNum, isLastFragment, m.data);
LogPrint (eLogDebug, "TunnelMessage: First fragment of message ", msgID, " not found, saved");
AddOutOfSequenceFragment (msgID, fragmentNum, isLastFragment, fragment, size);
}
}
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data)
bool TunnelEndpoint::ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const
{
if (!m_OutOfSequenceFragments.insert ({{msgID, fragmentNum}, {isLastFragment, data, i2p::util::GetMillisecondsSinceEpoch () }}).second)
if (msg.data->len + size < I2NP_MAX_MESSAGE_SIZE) // check if message is not too long
{
if (msg.data->len + size > msg.data->maxLen)
{
// LogPrint (eLogWarning, "TunnelMessage: I2NP message size ", msg.data->maxLen, " is not enough");
auto newMsg = NewI2NPMessage ();
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (fragment, size) < size) // concatenate fragment
{
LogPrint (eLogError, "TunnelMessage: I2NP buffer overflow ", msg.data->maxLen);
return false;
}
}
else
return false;
return true;
}
void TunnelEndpoint::HandleCurrenMessageFollowOnFragment (const uint8_t * fragment, size_t size, bool isLastFragment)
{
if (ConcatFollowOnFragment (m_CurrentMessage, fragment, size))
{
if (isLastFragment)
{
// message complete
HandleNextMessage (m_CurrentMessage);
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
else
{
m_CurrentMessage.nextFragmentNum++;
HandleOutOfSequenceFragments (m_CurrentMsgID, m_CurrentMessage);
}
}
else
{
LogPrint (eLogError, "TunnelMessage: Fragment ", m_CurrentMessage.nextFragmentNum, " of message ", m_CurrentMsgID, " exceeds max I2NP message size, message dropped");
m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr;
}
}
void TunnelEndpoint::AddIncompleteCurrentMessage ()
{
if (m_CurrentMsgID)
{
auto ret = m_IncompleteMessages.emplace (m_CurrentMsgID, m_CurrentMessage);
if (!ret.second)
LogPrint (eLogError, "TunnelMessage: Incomplete message ", m_CurrentMsgID, " already exists");
m_CurrentMessage.data = nullptr;
m_CurrentMsgID = 0;
}
}
void TunnelEndpoint::AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum,
bool isLastFragment, const uint8_t * fragment, size_t size)
{
std::unique_ptr<Fragment> f(new Fragment (isLastFragment, i2p::util::GetMillisecondsSinceEpoch (), size));
memcpy (f->data.data (), fragment, size);
if (!m_OutOfSequenceFragments.emplace ((uint64_t)msgID << 32 | fragmentNum, std::move (f)).second)
LogPrint (eLogInfo, "TunnelMessage: duplicate out-of-sequence fragment ", fragmentNum, " of message ", msgID);
}
@ -212,7 +274,14 @@ namespace tunnel
if (!msg.nextFragmentNum) // message complete
{
HandleNextMessage (msg);
m_IncompleteMessages.erase (msgID);
if (&msg == &m_CurrentMessage)
{
m_CurrentMsgID = 0;
m_CurrentMessage.data = nullptr;
}
else
m_IncompleteMessages.erase (msgID);
LogPrint (eLogDebug, "TunnelMessage: All fragments of message ", msgID, " found");
break;
}
}
@ -220,11 +289,11 @@ namespace tunnel
bool TunnelEndpoint::ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg)
{
auto it = m_OutOfSequenceFragments.find ({msgID, msg.nextFragmentNum});
auto it = m_OutOfSequenceFragments.find ((uint64_t)msgID << 32 | msg.nextFragmentNum);
if (it != m_OutOfSequenceFragments.end ())
{
LogPrint (eLogDebug, "TunnelMessage: Out-of-sequence fragment ", (int)msg.nextFragmentNum, " of message ", msgID, " found");
size_t size = it->second.data->GetLength ();
size_t size = it->second->data.size ();
if (msg.data->len + size > msg.data->maxLen)
{
LogPrint (eLogWarning, "TunnelMessage: Tunnel endpoint I2NP message size ", msg.data->maxLen, " is not enough");
@ -232,9 +301,9 @@ namespace tunnel
*newMsg = *(msg.data);
msg.data = newMsg;
}
if (msg.data->Concat (it->second.data->GetBuffer (), size) < size) // concatenate out-of-sync fragment
if (msg.data->Concat (it->second->data.data (), size) < size) // concatenate out-of-sync fragment
LogPrint (eLogError, "TunnelMessage: Tunnel endpoint I2NP buffer overflow ", msg.data->maxLen);
if (it->second.isLastFragment)
if (it->second->isLastFragment)
// message complete
msg.nextFragmentNum = 0;
else
@ -287,7 +356,7 @@ namespace tunnel
// out-of-sequence fragments
for (auto it = m_OutOfSequenceFragments.begin (); it != m_OutOfSequenceFragments.end ();)
{
if (ts > it->second.receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
if (ts > it->second->receiveTime + i2p::I2NP_MESSAGE_EXPIRATION_TIMEOUT)
it = m_OutOfSequenceFragments.erase (it);
else
++it;

@ -1,5 +1,5 @@
/*
* Copyright (c) 2013-2020, The PurpleI2P Project
* Copyright (c) 2013-2021, The PurpleI2P Project
*
* This file is part of Purple i2pd project and licensed under BSD3
*
@ -10,7 +10,8 @@
#define TUNNEL_ENDPOINT_H__
#include <inttypes.h>
#include <map>
#include <unordered_map>
#include <vector>
#include <string>
#include "I2NPProtocol.h"
#include "TunnelBase.h"
@ -29,14 +30,15 @@ namespace tunnel
struct Fragment
{
Fragment (bool last, uint64_t t, size_t size): isLastFragment (last), receiveTime (t), data (size) {};
bool isLastFragment;
std::shared_ptr<I2NPMessage> data;
uint64_t receiveTime; // milliseconds since epoch
std::vector<uint8_t> data;
};
public:
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0) {};
TunnelEndpoint (bool isInbound): m_IsInbound (isInbound), m_NumReceivedBytes (0), m_CurrentMsgID (0) {};
~TunnelEndpoint ();
size_t GetNumReceivedBytes () const { return m_NumReceivedBytes; };
void Cleanup ();
@ -45,19 +47,24 @@ namespace tunnel
private:
void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, const TunnelMessageBlockEx& m);
void HandleFollowOnFragment (uint32_t msgID, bool isLastFragment, uint8_t fragmentNum, const uint8_t * fragment, size_t size);
bool ConcatFollowOnFragment (TunnelMessageBlockEx& msg, const uint8_t * fragment, size_t size) const; // true if success
void HandleCurrenMessageFollowOnFragment (const uint8_t * frgament, size_t size, bool isLastFragment);
void HandleNextMessage (const TunnelMessageBlock& msg);
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, std::shared_ptr<I2NPMessage> data);
void AddOutOfSequenceFragment (uint32_t msgID, uint8_t fragmentNum, bool isLastFragment, const uint8_t * fragment, size_t size);
bool ConcatNextOutOfSequenceFragment (uint32_t msgID, TunnelMessageBlockEx& msg); // true if something added
void HandleOutOfSequenceFragments (uint32_t msgID, TunnelMessageBlockEx& msg);
void AddIncompleteCurrentMessage ();
private:
std::map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
std::map<std::pair<uint32_t, uint8_t>, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment
std::unordered_map<uint32_t, TunnelMessageBlockEx> m_IncompleteMessages;
std::unordered_map<uint64_t, std::unique_ptr<Fragment> > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment
bool m_IsInbound;
size_t m_NumReceivedBytes;
TunnelMessageBlockEx m_CurrentMessage;
uint32_t m_CurrentMsgID;
};
}
}

@ -215,7 +215,7 @@ namespace tunnel
const auto& tunnelDataMsgs = m_Buffer.GetTunnelDataMsgs ();
for (auto& tunnelMsg : tunnelDataMsgs)
{
auto newMsg = CreateEmptyTunnelDataMsg ();
auto newMsg = CreateEmptyTunnelDataMsg (false);
m_Tunnel->EncryptTunnelMsg (tunnelMsg, newMsg);
htobe32buf (newMsg->GetPayload (), m_Tunnel->GetNextTunnelID ());
newMsg->FillI2NPMessageHeader (eI2NPTunnelData);

@ -319,7 +319,7 @@ namespace proxy {
auto pos = uri.find(":");
if(pos == std::string::npos || pos == uri.size() - 1)
{
GenericProxyError(tr("Invalid Request"), tr("invalid request uri"));
GenericProxyError(tr("Invalid request"), tr("invalid request uri"));
return true;
}
else

Loading…
Cancel
Save