diff --git a/.gitignore b/.gitignore index 2506fd93..1fc6cefe 100644 --- a/.gitignore +++ b/.gitignore @@ -7,8 +7,10 @@ netDb /i2pd /libi2pd.a /libi2pdclient.a +/libi2pdlang.a /libi2pd.so /libi2pdclient.so +/libi2pdlang.so *.exe diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index af47f458..00000000 --- a/.travis.yml +++ /dev/null @@ -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 diff --git a/Makefile b/Makefile index b1db36a8..abea5fd7 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/README.md b/README.md index 92a2f46f..37c4553a 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 89d85494..00000000 --- a/appveyor.yml +++ /dev/null @@ -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 diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index d1b51f23..d1502deb 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -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}") diff --git a/build/docker/README.md b/build/docker/README.md deleted file mode 100644 index 877a9ce5..00000000 --- a/build/docker/README.md +++ /dev/null @@ -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. :) - - - diff --git a/build/docker/old-ubuntu-based/Dockerfile b/build/docker/old-ubuntu-based/Dockerfile deleted file mode 100644 index 3faddf2e..00000000 --- a/build/docker/old-ubuntu-based/Dockerfile +++ /dev/null @@ -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 diff --git a/build/fig.yml b/build/fig.yml deleted file mode 100644 index 372ef350..00000000 --- a/build/fig.yml +++ /dev/null @@ -1,2 +0,0 @@ -i2pd: - build: . diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index 6470e148..71af141e 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.13 +FROM alpine:latest LABEL authors "Mikal Villa , Darknet Villain " # Expose git branch, tag and URL variables as arguments diff --git a/contrib/i18n/English.po b/contrib/i18n/English.po index 1de2ddaa..76d58390 100644 --- a/contrib/i18n/English.po +++ b/contrib/i18n/English.po @@ -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 "i2pd 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" -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 -msgid "LeaseSets" -msgstr "" - -#: daemon/HTTPServer.cpp:232 daemon/HTTPServer.cpp:652 -msgid "Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:233 daemon/HTTPServer.cpp:727 -#: daemon/HTTPServer.cpp:743 -msgid "Transit tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:234 daemon/HTTPServer.cpp:792 -msgid "Transports" -msgstr "" - -#: daemon/HTTPServer.cpp:235 -msgid "I2P tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:237 daemon/HTTPServer.cpp:854 -#: daemon/HTTPServer.cpp:864 -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 -msgid "ERROR" -msgstr "" - -#: daemon/HTTPServer.cpp:260 -msgid "OK" -msgstr "" - -#: daemon/HTTPServer.cpp:261 -msgid "Testing" -msgstr "" - -#: daemon/HTTPServer.cpp:262 -msgid "Firewalled" -msgstr "" - -#: daemon/HTTPServer.cpp:263 daemon/HTTPServer.cpp:284 -#: daemon/HTTPServer.cpp:370 -msgid "Unknown" -msgstr "" - -#: daemon/HTTPServer.cpp:264 daemon/HTTPServer.cpp:394 -#: daemon/HTTPServer.cpp:395 daemon/HTTPServer.cpp:922 -#: daemon/HTTPServer.cpp:931 -msgid "Proxy" -msgstr "" - -#: daemon/HTTPServer.cpp:265 -msgid "Mesh" -msgstr "" - -#: daemon/HTTPServer.cpp:268 -msgid "Error" -msgstr "" - -#: daemon/HTTPServer.cpp:272 -msgid "Clock skew" -msgstr "" - -#: daemon/HTTPServer.cpp:275 -msgid "Offline" -msgstr "" - -#: daemon/HTTPServer.cpp:278 -msgid "Symmetric NAT" -msgstr "" - -#: daemon/HTTPServer.cpp:290 -msgid "Uptime" -msgstr "" - -#: daemon/HTTPServer.cpp:293 -msgid "Network status" -msgstr "" - -#: daemon/HTTPServer.cpp:298 -msgid "Network status v6" -msgstr "" - -#: daemon/HTTPServer.cpp:304 daemon/HTTPServer.cpp:311 -msgid "Stopping in" -msgstr "" - -#: daemon/HTTPServer.cpp:318 -msgid "Family" -msgstr "" - -#: daemon/HTTPServer.cpp:319 -msgid "Tunnel creation success rate" -msgstr "" - -#: daemon/HTTPServer.cpp:320 -msgid "Received" -msgstr "" - -#: daemon/HTTPServer.cpp:322 daemon/HTTPServer.cpp:325 -#: daemon/HTTPServer.cpp:328 -msgid "KiB/s" -msgstr "" - -#: daemon/HTTPServer.cpp:323 -msgid "Sent" -msgstr "" - -#: daemon/HTTPServer.cpp:326 -msgid "Transit" -msgstr "" - -#: daemon/HTTPServer.cpp:329 -msgid "Data path" -msgstr "" - -#: daemon/HTTPServer.cpp:332 -msgid "Hidden content. Press on text to see." -msgstr "" - -#: daemon/HTTPServer.cpp:335 -msgid "Router Ident" -msgstr "" - -#: daemon/HTTPServer.cpp:337 -msgid "Router Family" -msgstr "" - -#: daemon/HTTPServer.cpp:338 -msgid "Router Caps" -msgstr "" - -#: daemon/HTTPServer.cpp:339 -msgid "Version" -msgstr "" - -#: daemon/HTTPServer.cpp:340 -msgid "Our external address" -msgstr "" - -#: daemon/HTTPServer.cpp:348 -msgid "supported" -msgstr "" - -#: daemon/HTTPServer.cpp:380 -msgid "Routers" -msgstr "" - -#: daemon/HTTPServer.cpp:381 -msgid "Floodfills" -msgstr "" - -#: daemon/HTTPServer.cpp:388 daemon/HTTPServer.cpp:908 -msgid "Client Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:389 -msgid "Transit Tunnels" -msgstr "" - -#: daemon/HTTPServer.cpp:393 -msgid "Services" -msgstr "" - -#: daemon/HTTPServer.cpp:407 daemon/HTTPServer.cpp:419 +#: daemon/HTTPServer.cpp:234 daemon/HTTPServer.cpp:413 +#: daemon/HTTPServer.cpp:425 msgid "Local Destinations" msgstr "" -#: daemon/HTTPServer.cpp:442 +#: 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:238 daemon/HTTPServer.cpp:659 +msgid "Tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:239 daemon/HTTPServer.cpp:395 +#: daemon/HTTPServer.cpp:753 daemon/HTTPServer.cpp:769 +msgid "Transit Tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:240 daemon/HTTPServer.cpp:818 +msgid "Transports" +msgstr "" + +#: daemon/HTTPServer.cpp:241 +msgid "I2P tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:243 daemon/HTTPServer.cpp:880 +#: daemon/HTTPServer.cpp:890 +msgid "SAM sessions" +msgstr "" + +#: 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:266 +msgid "OK" +msgstr "" + +#: daemon/HTTPServer.cpp:267 +msgid "Testing" +msgstr "" + +#: daemon/HTTPServer.cpp:268 +msgid "Firewalled" +msgstr "" + +#: daemon/HTTPServer.cpp:269 daemon/HTTPServer.cpp:290 +#: daemon/HTTPServer.cpp:376 +msgid "Unknown" +msgstr "" + +#: 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:271 +msgid "Mesh" +msgstr "" + +#: daemon/HTTPServer.cpp:274 +msgid "Error" +msgstr "" + +#: daemon/HTTPServer.cpp:278 +msgid "Clock skew" +msgstr "" + +#: daemon/HTTPServer.cpp:281 +msgid "Offline" +msgstr "" + +#: daemon/HTTPServer.cpp:284 +msgid "Symmetric NAT" +msgstr "" + +#: daemon/HTTPServer.cpp:296 +msgid "Uptime" +msgstr "" + +#: daemon/HTTPServer.cpp:299 +msgid "Network status" +msgstr "" + +#: daemon/HTTPServer.cpp:304 +msgid "Network status v6" +msgstr "" + +#: daemon/HTTPServer.cpp:310 daemon/HTTPServer.cpp:317 +msgid "Stopping in" +msgstr "" + +#: daemon/HTTPServer.cpp:324 +msgid "Family" +msgstr "" + +#: daemon/HTTPServer.cpp:325 +msgid "Tunnel creation success rate" +msgstr "" + +#: daemon/HTTPServer.cpp:326 +msgid "Received" +msgstr "" + +#. tr: Kibibit/s +#: daemon/HTTPServer.cpp:328 daemon/HTTPServer.cpp:331 +#: daemon/HTTPServer.cpp:334 +msgid "KiB/s" +msgstr "" + +#: daemon/HTTPServer.cpp:329 +msgid "Sent" +msgstr "" + +#: daemon/HTTPServer.cpp:332 +msgid "Transit" +msgstr "" + +#: daemon/HTTPServer.cpp:335 +msgid "Data path" +msgstr "" + +#: daemon/HTTPServer.cpp:338 +msgid "Hidden content. Press on text to see." +msgstr "" + +#: daemon/HTTPServer.cpp:341 +msgid "Router Ident" +msgstr "" + +#: daemon/HTTPServer.cpp:343 +msgid "Router Family" +msgstr "" + +#: daemon/HTTPServer.cpp:344 +msgid "Router Caps" +msgstr "" + +#: daemon/HTTPServer.cpp:345 +msgid "Version" +msgstr "" + +#: daemon/HTTPServer.cpp:346 +msgid "Our external address" +msgstr "" + +#: daemon/HTTPServer.cpp:354 +msgid "supported" +msgstr "" + +#: daemon/HTTPServer.cpp:386 +msgid "Routers" +msgstr "" + +#: daemon/HTTPServer.cpp:387 +msgid "Floodfills" +msgstr "" + +#: daemon/HTTPServer.cpp:394 daemon/HTTPServer.cpp:934 +msgid "Client Tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:399 +msgid "Services" +msgstr "" + +#: 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 "" "Note: 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 "" "Note: 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 "" diff --git a/contrib/i18n/regex.txt b/contrib/i18n/regex.txt index 768a4b01..e74f9d2d 100644 --- a/contrib/i18n/regex.txt +++ b/contrib/i18n/regex.txt @@ -5,3 +5,6 @@ msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"( msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n {"$1", "$2"},\n + +^#:(.*)$\n + diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf index f3cea2b5..599a2081 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -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] diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index f7f2ef2f..445c4dfd 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -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); diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 7eb296c4..fd26d8f1 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -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 << " " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << ", "; - s << " " << (int) (bytes / 1024) << " " << tr("KiB") << "\r\n"; + s << " " << (int) (bytes / 1024) << " " << tr(/* tr: Kibibit */ "KiB") << "\r\n"; } static void SetLogLevel (const std::string& level) @@ -223,18 +229,18 @@ namespace http { "
" << tr("i2pd webconsole") << "
\r\n" "
\r\n" "\r\n" "
"; @@ -319,13 +325,13 @@ namespace http { s << "" << tr("Tunnel creation success rate") << ": " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
\r\n"; s << "" << tr("Received") << ": "; ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); - s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; s << "" << tr("Sent") << ": "; ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); - s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; s << "" << tr("Transit") << ": "; ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); - s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; s << "" << tr("Data path") << ": " << i2p::fs::GetUTF8DataDir() << "
\r\n"; s << "
"; if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { @@ -476,7 +482,7 @@ namespace http { s << "
"; 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 << "
\r\n"; } @@ -534,7 +540,8 @@ namespace http { if (dest) { ShowLeaseSetDestination (s, dest, token); - // show streams + + // Print table with streams information s << "\r\n\r\n\r\n"; s << ""; s << "
" << tr("Streams") << "
StreamID"; // 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 << "" << tr("Router commands") << "
\r\n
\r\n
\r\n"; - s << " " << tr("Run peer test") << "\r\n"; - //s << " Reload config
\r\n"; + s << " " << tr("Run peer test") << "
\r\n"; + + // s << " Reload config
\r\n"; + if (i2p::context.AcceptsTunnels ()) - s << " " << tr("Decline transit tunnels") << "\r\n"; + s << " " << tr("Decline transit tunnels") << "
\r\n"; else - s << " " << tr("Accept transit tunnels") << "\r\n"; + s << " " << tr("Accept transit tunnels") << "
\r\n"; + #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) if (Daemon.gracefulShutdownInterval) - s << " " << tr("Cancel graceful shutdown") << "\r\n"; + s << " " << tr("Cancel graceful shutdown") << "
\r\n"; else - s << " " << tr("Start graceful shutdown") << "\r\n"; + s << " " << tr("Start graceful shutdown") << "
\r\n"; #elif defined(WIN32_APP) if (i2p::util::DaemonWin32::Instance().isGraceful) - s << " " << tr("Cancel graceful shutdown") << "\r\n"; + s << " " << tr("Cancel graceful shutdown") << "
\r\n"; else - s << " " << tr("Start graceful shutdown") << "\r\n"; + s << " " << tr("Start graceful shutdown") << "
\r\n"; #endif + s << " " << tr("Force shutdown") << "\r\n"; s << "
"; @@ -718,13 +729,26 @@ namespace http { s << " \r\n"; s << " \r\n"; s << "\r\n
\r\n"; + + std::string currLang = i2p::context.GetLanguage ()->GetLanguage(); // get current used language + s << "" << tr("Change language") << "
\r\n"; + s << "
\r\n"; + s << " \r\n"; + s << " \r\n"; + s << " \r\n"; + s << " \r\n"; + s << "
\r\n
\r\n"; + } void ShowTransitTunnels (std::stringstream& s) { if(i2p::tunnel::tunnels.CountTransitTunnels()) { - s << "" << tr("Transit tunnels") << ":
\r\n
\r\n"; + s << "" << tr("Transit Tunnels") << ":
\r\n
\r\n"; for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) { s << "
\r\n"; @@ -740,7 +764,7 @@ namespace http { } else { - s << "" << tr("Transit tunnels") << ": " << tr("no transit tunnels currently built") << ".
\r\n"; + s << "" << tr("Transit Tunnels") << ": " << tr("no transit tunnels currently built") << ".
\r\n"; } } @@ -1260,7 +1284,7 @@ namespace http { s << "" << tr("ERROR") << ": " << tr("StreamID can't be null") << "
\r\n
\r\n"; s << "" << tr("Return to destination page") << "
\r\n"; - s << "

" << tr("You will be redirected back in 5 seconds") << ""; + s << "

" << tr("You will be redirected in 5 seconds") << ""; 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 << "" << tr("ERROR") << ": " << tr("Transit tunnels count must not exceed 65535") << "\r\n
\r\n
\r\n"; s << "" << tr("Back to commands list") << "\r\n
\r\n"; - s << "

" << tr("You will be redirected back in 5 seconds") << ""; + s << "

" << tr("You will be redirected in 5 seconds") << ""; 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 << "" << tr("Return to destination page") << "\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; diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index 12602c99..73dde618 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -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) diff --git a/i18n/Afrikaans.cpp b/i18n/Afrikaans.cpp index d2b652b4..96ee52ee 100644 --- a/i18n/Afrikaans.cpp +++ b/i18n/Afrikaans.cpp @@ -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 GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/English.cpp b/i18n/English.cpp index 6015f8e1..2670e984 100644 --- a/i18n/English.cpp +++ b/i18n/English.cpp @@ -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 GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/I18N.h b/i18n/I18N.h index 272f65e8..03add48d 100644 --- a/i18n/I18N.h +++ b/i18n/I18N.h @@ -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) diff --git a/i18n/I18N_langs.h b/i18n/I18N_langs.h index 181a4793..949e5844 100644 --- a/i18n/I18N_langs.h +++ b/i18n/I18N_langs.h @@ -17,10 +17,17 @@ namespace i18n { public: Locale ( + const std::string& language, const std::map& strings, const std::map>& plurals, std::function 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 m_Strings; const std::map> m_Plurals; std::function m_Formula; }; + struct langData + { + std::string LocaleName; //localized name + std::function (void)> LocaleFunc; + }; + // Add localization here with language name as namespace namespace afrikaans { std::shared_ptr GetLocale (); } namespace english { std::shared_ptr GetLocale (); } @@ -62,6 +76,18 @@ namespace i18n namespace turkmen { std::shared_ptr GetLocale (); } namespace ukrainian { std::shared_ptr GetLocale (); } + /** + * That map contains international language name lower-case and name in it's language + */ + static std::map 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 diff --git a/i18n/Russian.cpp b/i18n/Russian.cpp index 7df82d54..5e7f9c6b 100644 --- a/i18n/Russian.cpp +++ b/i18n/Russian.cpp @@ -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,23 +31,152 @@ namespace russian // language static std::map strings { - // HTTP Proxy + {"Disabled", "Выключено"}, + {"Enabled", "Включено"}, + {"KiB", "КиБ"}, + {"MiB", "МиБ"}, + {"GiB", "ГиБ"}, + {"building", "строится"}, + {"failed", "неудачный"}, + {"expiring", "истекает"}, + {"established", "работает"}, + {"unknown", "неизвестно"}, + {"exploratory", "исследовательский"}, + {"i2pd webconsole", "Веб-консоль i2pd"}, + {"Main page", "Главная"}, + {"Router commands", "Команды роутера"}, + {"Local destinations", "Локальные назначения"}, + {"LeaseSets", "Лизсеты"}, + {"Tunnels", "Туннели"}, + {"Transit tunnels", "Транзитные туннели"}, + {"Transports", "Транспорты"}, + {"I2P tunnels", "I2P туннели"}, + {"SAM sessions", "SAM сессии"}, + {"ERROR", "ОШИБКА"}, + {"OK", "OK"}, + {"Testing", "Тестирование"}, + {"Firewalled", "Заблокировано извне"}, + {"Unknown", "Неизвестно"}, + {"Proxy", "Прокси"}, + {"Mesh", "MESH-сеть"}, + {"Error", "Ошибка"}, + {"Clock skew", "Не точное время"}, + {"Offline", "Оффлайн"}, + {"Symmetric NAT", "Симметричный NAT"}, + {"Uptime", "В сети"}, + {"Network status", "Сетевой статус"}, + {"Network status v6", "Сетевой статус v6"}, + {"Stopping in", "Остановка через"}, + {"Family", "Семейство"}, + {"Tunnel creation success rate", "Успешно построенных туннелей"}, + {"Received", "Получено"}, + {"KiB/s", "КиБ/с"}, + {"Sent", "Отправлено"}, + {"Transit", "Транзит"}, + {"Data path", "Путь к данным"}, + {"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."}, + {"Router Ident", "Идентификатор роутера"}, + {"Router Family", "Семейство роутера"}, + {"Router Caps", "Флаги роутера"}, + {"Version", "Версия"}, + {"Our external address", "Наш внешний адрес"}, + {"supported", "поддерживается"}, + {"Routers", "Роутеры"}, + {"Floodfills", "Флудфилы"}, + {"Client Tunnels", "Клиентские туннели"}, + {"Transit Tunnels", "Транзитные туннели"}, + {"Services", "Сервисы"}, + {"Local Destinations", "Локальные назначения"}, + {"Encrypted B33 address", "Шифрованные B33 адреса"}, + {"Address registration line", "Строка регистрации адреса"}, + {"Domain", "Домен"}, + {"Generate", "Сгенерировать"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Примечание: полученная строка может быть использована только для регистрации доменов второго уровня (example.i2p). Для регистрации поддоменов используйте i2pd-tools."}, + {"Address", "Адрес"}, + {"Type", "Тип"}, + {"EncType", "ТипШифр"}, + {"Inbound tunnels", "Входящие туннели"}, + {"ms", "мс"}, + {"Outbound tunnels", "Исходящие туннели"}, + {"Tags", "Теги"}, + {"Incoming", "Входящие"}, + {"Outgoing", "Исходящие"}, + {"Destination", "Назначение"}, + {"Amount", "Количество"}, + {"Incoming Tags", "Входящие Теги"}, + {"Tags sessions", "Сессии Тегов"}, + {"Status", "Статус"}, + {"Local Destination", "Локальное назначение"}, + {"Streams", "Стримы"}, + {"Close stream", "Закрыть стрим"}, + {"I2CP session not found", "I2CP сессия не найдена"}, + {"I2CP is not enabled", "I2CP не включен"}, + {"Invalid", "Некорректный"}, + {"Store type", "Тип хранилища"}, + {"Expires", "Истекает"}, + {"Non Expired Leases", "Не истекшие Lease-ы"}, + {"Gateway", "Шлюз"}, + {"TunnelID", "ID туннеля"}, + {"EndDate", "Заканчивается"}, + {"not floodfill", "не флудфил"}, + {"Queue size", "Размер очереди"}, + {"Run peer test", "Запустить тестирование"}, + {"Decline transit tunnels", "Отклонять транзитные туннели"}, + {"Accept transit tunnels", "Принимать транзитные туннели"}, + {"Cancel graceful shutdown", "Отменить плавную остановку"}, + {"Start graceful shutdown", "Запустить плавную остановку"}, + {"Force shutdown", "Принудительная остановка"}, + {"Note: any action done here are not persistent and not changes your config files.", "Примечание: любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."}, + {"Logging level", "Уровень логирования"}, + {"Transit tunnels limit", "Лимит транзитных туннелей"}, + {"Change", "Изменить"}, + {"Change language", "Изменение языка"}, + {"no transit tunnels currently built", "нет построенных транзитных туннелей"}, + {"SAM disabled", "SAM выключен"}, + {"no sessions currently running", "нет запущенных сессий"}, + {"SAM session not found", "SAM сессия не найдена"}, + {"SAM Session", "SAM сессия"}, + {"Server Tunnels", "Серверные туннели"}, + {"Client Forwards", "Клиентские перенаправления"}, + {"Server Forwards", "Серверные перенаправления"}, + {"Unknown page", "Неизвестная страница"}, + {"Invalid token", "Неверный токен"}, + {"SUCCESS", "УСПЕШНО"}, + {"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 секунд"}, + {"Transit tunnels count must not exceed 65535", "Число транзитных туннелей не должно превышать 65535"}, + {"Back to commands list", "Вернуться к списку команд"}, + {"Register at reg.i2p", "Зарегистрировать на reg.i2p"}, + {"Description", "Описание"}, + {"A bit information about service on domain", "Немного информации о сервисе на домене"}, + {"Submit", "Отправить"}, + {"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", "Вы можете попробовать найти адрес на джамп сервисах ниже"}, + {"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", "Адрес"}, + {"Host", "Узел"}, {"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"}, - {"already in router's addressbook", "уже в адресной книге роутера"}, {"Click", "Нажмите"}, {"here", "здесь"}, {"to proceed", "чтобы продолжить"}, - {"to update record", "чтобы обновить запись"}, {"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", "Ошибка внешнего прокси"}, @@ -63,169 +195,13 @@ namespace russian // language {"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", "неизвестно"}, - {"i2pd webconsole", "Веб-консоль i2pd"}, - // ShowPageHead - {"Main page", "Главная"}, - {"Router commands", "Команды роутера"}, - {"Local destinations", "Локальные назнач."}, - {"LeaseSets", "Лизсеты"}, - {"Tunnels", "Туннели"}, - {"Transit tunnels", "Транзит. туннели"}, - {"Transports", "Транспорты"}, - {"I2P tunnels", "I2P туннели"}, - {"SAM sessions", "SAM сессии"}, - // Network Status - {"OK", "OK"}, - {"Testing", "Тестирование"}, - {"Firewalled", "Заблокировано извне"}, - {"Unknown", "Неизвестно"}, - {"Proxy", "Прокси"}, - {"Mesh", "MESH-сеть"}, - {"Error", "Ошибка"}, - {"Clock skew", "Не точное время"}, - {"Offline", "Оффлайн"}, - {"Symmetric NAT", "Симметричный NAT"}, - // Status - {"Uptime", "В сети"}, - {"Network status", "Сетевой статус"}, - {"Network status v6", "Сетевой статус v6"}, - {"Stopping in", "Остановка через"}, - {"Family", "Семейство"}, - {"Tunnel creation success rate", "Успешно построенных туннелей"}, - {"Received", "Получено"}, - {"Sent", "Отправлено"}, - {"Transit", "Транзит"}, - {"KiB/s", "КиБ/с"}, - {"Data path", "Путь к данным"}, - {"Hidden content. Press on text to see.", "Скрытый контент. Нажмите на текст чтобы отобразить."}, - {"Router Ident", "Идентификатор роутера"}, - {"Router Family", "Семейство роутера"}, - {"Router Caps", "Флаги роутера"}, - {"Version", "Версия"}, - {"Our external address", "Наш внешний адрес"}, - {"supported", "поддерживается"}, - {"Routers", "Роутеры"}, - {"Floodfills", "Флудфилы"}, - {"LeaseSets", "Лизсеты"}, - {"Client Tunnels", "Клиентские туннели"}, - {"Transit Tunnels", "Транзитные туннели"}, - {"Services", "Сервисы"}, - // ShowLocalDestinations - {"Local Destinations", "Локальные назначения"}, - // ShowLeaseSetDestination - {"Encrypted B33 address", "Шифрованные B33 адреса"}, - {"Address registration line", "Строка регистрации адреса"}, - {"Domain", "Домен"}, - {"Generate", "Сгенерировать"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", - "Примечание: полученная строка может быть использована только для регистрации доменов второго уровня. Для регистрации поддоменов используйте i2pd-tools."}, - {"Address", "Адрес"}, - {"Type", "Тип"}, - {"EncType", "ТипШифр"}, - {"Inbound tunnels", "Входящие туннели"}, - {"Outbound tunnels", "Исходящие туннели"}, - {"ms", "мс"}, // milliseconds - {"Tags", "Теги"}, - {"Incoming", "Входящие"}, - {"Outgoing", "Исходящие"}, - {"Destination", "Назначение"}, - {"Amount", "Количество"}, - {"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", "Истекает"}, - {"Non Expired Leases", "Не истекшие Lease-ы"}, - {"Gateway", "Шлюз"}, - {"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", "Принудительная остановка"}, - {"Note: any action done here are not persistent and not changes your config files.", - "Примечание: любое действие произведенное здесь не является постоянным и не изменяет ваши конфигурационные файлы."}, - {"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", "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 - {"Register at reg.i2p", "Зарегистрировать на reg.i2p"}, - {"Description", "Описание"}, - {"A bit information about service on domain", "Немного информации о сервисе на домене"}, - {"Submit", "Отправить"}, - {"Domain can't end with .b32.i2p", "Домен не может заканчиваться на .b32.i2p"}, - {"Domain must end with .i2p", "Домен должен заканчиваться на .i2p"}, - {"Such destination is not found", "Такая точка назначения не найдена"}, + {"Host is down", "Узел недоступен"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Не удалось установить соединение к запрошенному узлу, возможно он не в сети. Попробуйте повторить запрос позже."}, {"", ""}, }; static std::map> plurals { - // ShowUptime {"days", {"день", "дня", "дней"}}, {"hours", {"час", "часа", "часов"}}, {"minutes", {"минуту", "минуты", "минут"}}, @@ -235,7 +211,7 @@ namespace russian // language std::shared_ptr GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/Turkmen.cpp b/i18n/Turkmen.cpp index 0edc9a6e..8af89f6f 100644 --- a/i18n/Turkmen.cpp +++ b/i18n/Turkmen.cpp @@ -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 GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/i18n/Ukrainian.cpp b/i18n/Ukrainian.cpp index 11878cde..8da132d7 100644 --- a/i18n/Ukrainian.cpp +++ b/i18n/Ukrainian.cpp @@ -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,23 +31,151 @@ namespace ukrainian // language static std::map strings { - // HTTP Proxy + {"Disabled", "Вимкнуто"}, + {"Enabled", "Увімкнуто"}, + {"KiB", "КіБ"}, + {"MiB", "МіБ"}, + {"GiB", "ГіБ"}, + {"building", "будується"}, + {"failed", "невдалий"}, + {"expiring", "завершується"}, + {"established", "працює"}, + {"unknown", "невідомо"}, + {"exploratory", "дослідницький"}, + {"i2pd webconsole", "Веб-консоль i2pd"}, + {"Main page", "Головна"}, + {"Router commands", "Команди маршрутизатора"}, + {"Local destinations", "Локальні призначення"}, + {"LeaseSets", "Лізсети"}, + {"Tunnels", "Тунелі"}, + {"Transit tunnels", "Транзитні тунелі"}, + {"Transports", "Транспорти"}, + {"I2P tunnels", "I2P тунелі"}, + {"SAM sessions", "SAM сесії"}, + {"ERROR", "ПОМИЛКА"}, + {"OK", "OK"}, + {"Testing", "Тестування"}, + {"Firewalled", "Заблоковано ззовні"}, + {"Unknown", "Невідомо"}, + {"Proxy", "Проксі"}, + {"Mesh", "MESH-мережа"}, + {"Error", "Помилка"}, + {"Clock skew", "Неточний час"}, + {"Offline", "Офлайн"}, + {"Symmetric NAT", "Симетричний NAT"}, + {"Uptime", "У мережі"}, + {"Network status", "Мережевий статус"}, + {"Network status v6", "Мережевий статус v6"}, + {"Stopping in", "Зупинка через"}, + {"Family", "Сімейство"}, + {"Tunnel creation success rate", "Успішно побудованих тунелів"}, + {"Received", "Отримано"}, + {"KiB/s", "КіБ/с"}, + {"Sent", "Відправлено"}, + {"Transit", "Транзит"}, + {"Data path", "Шлях до даних"}, + {"Hidden content. Press on text to see.", "Прихований вміст. Щоб відобразити, натисніть на текст."}, + {"Router Ident", "Ідентифікатор маршрутизатора"}, + {"Router Family", "Сімейство маршрутизатора"}, + {"Router Caps", "Прапорці маршрутизатора"}, + {"Version", "Версія"}, + {"Our external address", "Наша зовнішня адреса"}, + {"supported", "підтримується"}, + {"Routers", "Маршрутизатори"}, + {"Floodfills", "Флудфіли"}, + {"Client Tunnels", "Клієнтські Тунелі"}, + {"Transit Tunnels", "Транзитні Тунелі"}, + {"Services", "Сервіси"}, + {"Local Destinations", "Локальні Призначення"}, + {"Encrypted B33 address", "Шифровані B33 адреси"}, + {"Address registration line", "Рядок реєстрації адреси"}, + {"Domain", "Домен"}, + {"Generate", "Згенерувати"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "Примітка: отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня (example.i2p). Для реєстрації піддоменів використовуйте i2pd-tools."}, + {"Address", "Адреса"}, + {"Type", "Тип"}, + {"EncType", "ТипШифр"}, + {"Inbound tunnels", "Вхідні тунелі"}, + {"ms", "мс"}, + {"Outbound tunnels", "Вихідні тунелі"}, + {"Tags", "Теги"}, + {"Incoming", "Вхідні"}, + {"Outgoing", "Вихідні"}, + {"Destination", "Призначення"}, + {"Amount", "Кількість"}, + {"Incoming Tags", "Вхідні Теги"}, + {"Tags sessions", "Сесії Тегів"}, + {"Status", "Статус"}, + {"Local Destination", "Локальні Призначення"}, + {"Streams", "Потоки"}, + {"Close stream", "Закрити потік"}, + {"I2CP session not found", "I2CP сесія не знайдена"}, + {"I2CP is not enabled", "I2CP не увікнуто"}, + {"Invalid", "Некоректний"}, + {"Store type", "Тип сховища"}, + {"Expires", "Завершується"}, + {"Non Expired Leases", "Не завершені Lease-и"}, + {"Gateway", "Шлюз"}, + {"TunnelID", "ID тунеля"}, + {"EndDate", "Закінчується"}, + {"not floodfill", "не флудфіл"}, + {"Queue size", "Розмір черги"}, + {"Run peer test", "Запустити тестування"}, + {"Decline transit tunnels", "Відхиляти транзитні тунелі"}, + {"Accept transit tunnels", "Ухвалювати транзитні тунелі"}, + {"Cancel graceful shutdown", "Скасувати плавну зупинку"}, + {"Start graceful shutdown", "Запустити плавну зупинку"}, + {"Force shutdown", "Примусова зупинка"}, + {"Note: any action done here are not persistent and not changes your config files.", "Примітка: будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."}, + {"Logging level", "Рівень логування"}, + {"Transit tunnels limit", "Обмеження транзитних тунелів"}, + {"Change", "Змінити"}, + {"no transit tunnels currently built", "немає побудованих транзитних тунелів"}, + {"SAM disabled", "SAM вимкнуто"}, + {"no sessions currently running", "немає запущених сесій"}, + {"SAM session not found", "SAM сесія не знайдена"}, + {"SAM Session", "SAM сесія"}, + {"Server Tunnels", "Серверні Тунелі"}, + {"Client Forwards", "Клієнтські Переспрямування"}, + {"Server Forwards", "Серверні Переспрямування"}, + {"Unknown page", "Невідома сторінка"}, + {"Invalid token", "Невірний токен"}, + {"SUCCESS", "УСПІШНО"}, + {"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 секунд"}, + {"Transit tunnels count must not exceed 65535", "Кількість транзитних тунелів не повинна перевищувати 65535"}, + {"Back to commands list", "Повернутися до списку команд"}, + {"Register at reg.i2p", "Зареєструвати на reg.i2p"}, + {"Description", "Опис"}, + {"A bit information about service on domain", "Трохи інформації про сервіс на домені"}, + {"Submit", "Надіслати"}, + {"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", "Віддалена адреса не знайдена в адресній книзі роутера"}, + {"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", "вже в адресній книзі роутера"}, + {"added to router's addressbook from helper", "доданий в адресну книгу маршрутизатора через хелпер"}, {"Click", "Натисніть"}, {"here", "тут"}, {"to proceed", "щоб продовжити"}, - {"to update record", "щоб оновити запис"}, {"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", "Помилка зовнішнього проксі"}, @@ -64,169 +195,14 @@ namespace ukrainian // language {"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", "невідомо"}, - {"i2pd webconsole", "Веб-консоль i2pd"}, - // ShowPageHead - {"Main page", "Головна"}, - {"Router commands", "Команди роутера"}, - {"Local destinations", "Локальні призначення"}, - {"LeaseSets", "Лізсети"}, - {"Tunnels", "Тунелі"}, - {"Transit tunnels", "Транзитні тунелі"}, - {"Transports", "Транспорти"}, - {"I2P tunnels", "I2P тунелі"}, - {"SAM sessions", "SAM сесії"}, - // Network Status - {"OK", "OK"}, - {"Testing", "Тестування"}, - {"Firewalled", "Заблоковано ззовні"}, - {"Unknown", "Невідомо"}, - {"Proxy", "Проксі"}, - {"Mesh", "MESH-мережа"}, - {"Error", "Помилка"}, - {"Clock skew", "Неточний час"}, - {"Offline", "Офлайн"}, - {"Symmetric NAT", "Симетричний NAT"}, - // Status - {"Uptime", "В мережі"}, - {"Network status", "Мережевий статус"}, - {"Network status v6", "Мережевий статус v6"}, - {"Stopping in", "Зупинка через"}, - {"Family", "Сімейство"}, - {"Tunnel creation success rate", "Успішно побудованих тунелів"}, - {"Received", "Отримано"}, - {"Sent", "Відправлено"}, - {"Transit", "Транзит"}, - {"KiB/s", "КіБ/с"}, - {"Data path", "Шлях до даних"}, - {"Hidden content. Press on text to see.", "Прихований вміст. Натисніть на текст щоб відобразити."}, - {"Router Ident", "Ідентифікатор Роутера"}, - {"Router Family", "Сімейство Роутера"}, - {"Router Caps", "Прапорці Роутера"}, - {"Version", "Версія"}, - {"Our external address", "Наша зовнішня адреса"}, - {"supported", "підтримується"}, - {"Routers", "Роутери"}, - {"Floodfills", "Флудфіли"}, - {"Client Tunnels", "Клієнтські Тунелі"}, - {"Transit Tunnels", "Транзитні Тунелі"}, - {"Services", "Сервіси"}, - // ShowLocalDestinations - {"Local Destinations", "Локальні Призначення"}, - // ShowLeaseSetDestination - {"Encrypted B33 address", "Шифровані B33 адреси"}, - {"Address registration line", "Рядок реєстрації адреси"}, - {"Domain", "Домен"}, - {"Generate", "Згенерувати"}, - {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", - "Примітка: отриманий рядок може бути використаний тільки для реєстрації доменів другого рівня. Для реєстрації піддоменів використовуйте i2pd-tools."}, - {"Address", "Адреса"}, - {"Type", "Тип"}, - {"EncType", "ТипШифр"}, - {"Inbound tunnels", "Вхідні тунелі"}, - {"Outbound tunnels", "Вихідні тунелі"}, - {"ms", "мс"}, // milliseconds - {"Tags", "Теги"}, - {"Incoming", "Вхідні"}, - {"Outgoing", "Вихідні"}, - {"Destination", "Призначення"}, - {"Amount", "Кількість"}, - {"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", "Завершується"}, - {"Non Expired Leases", "Не завершені Lease-и"}, - {"Gateway", "Шлюз"}, - {"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", "Примусова зупинка"}, - {"Note: any action done here are not persistent and not changes your config files.", - "Примітка: будь-яка зроблена тут дія не є постійною та не змінює ваші конфігураційні файли."}, - {"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", "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 - {"Register at reg.i2p", "Зареєструвати на reg.i2p"}, - {"Description", "Опис"}, - {"A bit information about service on domain", "Трохи інформації про сервіс на домені"}, - {"Submit", "Надіслати"}, - {"Domain can't end with .b32.i2p", "Домен не може закінчуватися на .b32.i2p"}, - {"Domain must end with .i2p", "Домен повинен закінчуватися на .i2p"}, - {"Such destination is not found", "Така точка призначення не знайдена"}, + {"Can't create connection to requested host, it may be down. Please try again later.", "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."}, {"", ""}, }; static std::map> plurals { - // ShowUptime - {"days", {"день", "дня", "днів"}}, - {"hours", {"годину", "години", "годин"}}, + {"days", {"день", "дня", "днів"}}, + {"hours", {"годину", "години", "годин"}}, {"minutes", {"хвилину", "хвилини", "хвилин"}}, {"seconds", {"секунду", "секунди", "секунд"}}, {"", {"", "", ""}}, @@ -234,7 +210,7 @@ namespace ukrainian // language std::shared_ptr GetLocale() { - return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + return std::make_shared(language, strings, plurals, [] (int n)->int { return plural(n); }); } } // language diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index a80778a1..e5640ad0 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -37,6 +37,7 @@ namespace config { ("conf", value()->default_value(""), "Path to main i2pd config file (default: try ~/.i2pd/i2pd.conf or /var/lib/i2pd/i2pd.conf)") ("tunconf", value()->default_value(""), "Path to config with tunnels list and options (default: try ~/.i2pd/tunnels.conf or /var/lib/i2pd/tunnels.conf)") ("tunnelsdir", value()->default_value(""), "Path to extra tunnels' configs folder (default: ~/.i2pd/tunnels.d or /var/lib/i2pd/tunnels.d") + ("certsdir", value()->default_value(""), "Path to certificates used for verifying .su3, families (default: ~/.i2pd/certificates or /var/lib/i2pd/certificates") ("pidfile", value()->default_value(""), "Path to pidfile (default: ~/i2pd/i2pd.pid or /var/lib/i2pd/i2pd.pid)") ("log", value()->default_value(""), "Logs destination: stdout, file, syslog (stdout if not set)") ("logfile", value()->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()->default_value(""), "Yggdrasil address to publish") ; diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index 6ac302b0..f6653e55 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -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); diff --git a/libi2pd/FS.h b/libi2pd/FS.h index f07ee35c..d51aa955 100644 --- a/libi2pd/FS.h +++ b/libi2pd/FS.h @@ -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= + * + * 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 diff --git a/libi2pd/Family.cpp b/libi2pd/Family.cpp index fbb7b9ee..a6f0e2ee 100644 --- a/libi2pd/Family.cpp +++ b/libi2pd/Family.cpp @@ -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 files; int numCertificates = 0; diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 7c4d84c3..a040e25b 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -36,10 +36,21 @@ namespace i2p return std::make_shared >(); } - std::shared_ptr NewI2NPTunnelMessage () + std::shared_ptr NewI2NPTunnelMessage (bool endpoint) { - auto msg = new I2NPMessageBuffer(); // 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(); // reserved for alignment and NTCP 16 + 6 + 12 + msg->Align (12); + } return std::shared_ptr(msg); } @@ -691,7 +702,7 @@ namespace i2p std::shared_ptr 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 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 CreateEmptyTunnelDataMsg () + std::shared_ptr CreateEmptyTunnelDataMsg (bool endpoint) { - auto msg = NewI2NPTunnelMessage (); + auto msg = NewI2NPTunnelMessage (endpoint); msg->len += i2p::tunnel::TUNNEL_DATA_MSG_SIZE; return msg; } diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index ee143734..69d6bb04 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.h @@ -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 NewI2NPMessage (); std::shared_ptr NewI2NPShortMessage (); - std::shared_ptr NewI2NPTunnelMessage (); + std::shared_ptr NewI2NPTunnelMessage (bool endpoint); std::shared_ptr NewI2NPMessage (size_t len); std::shared_ptr CreateI2NPMessage (I2NPMessageType msgType, const uint8_t * buf, size_t len, uint32_t replyMsgID = 0); @@ -307,7 +307,7 @@ namespace tunnel std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf); std::shared_ptr CreateTunnelDataMsg (uint32_t tunnelID, const uint8_t * payload); - std::shared_ptr CreateEmptyTunnelDataMsg (); + std::shared_ptr CreateEmptyTunnelDataMsg (bool endpoint); std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, const uint8_t * buf, size_t len); std::shared_ptr CreateTunnelGatewayMsg (uint32_t tunnelID, I2NPMessageType msgType, diff --git a/libi2pd/Reseed.cpp b/libi2pd/Reseed.cpp index 41111ecc..aec683d4 100644 --- a/libi2pd/Reseed.cpp +++ b/libi2pd/Reseed.cpp @@ -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 files; int numCertificates = 0; diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index c527aede..b2a19890 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -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 (); diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 3f50a4b7..a426d9b7 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -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

(); 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)); } diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index 89654757..f27a5d68 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -179,7 +179,8 @@ namespace data std::shared_ptr 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 diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index 612b1058..3269e040 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -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 (m_Owner->GetService (), *this, remote, port); std::unique_lock 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 (m_Owner->GetService (), *this); std::unique_lock 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 l(m_StreamsMutex); m_Streams.erase (stream->GetRecvStreamID ()); m_IncomingStreams.erase (stream->GetSendStreamID ()); + if (m_LastStream == stream) m_LastStream = nullptr; } } diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index fe035136..c40c49f5 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -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 #include -#include +#include #include #include #include @@ -297,12 +297,13 @@ namespace stream uint16_t m_LocalPort; bool m_Gzip; // gzip compression of data messages std::mutex m_StreamsMutex; - std::map > m_Streams; // sendStreamID->stream - std::map > m_IncomingStreams; // receiveStreamID->stream + std::unordered_map > m_Streams; // sendStreamID->stream + std::unordered_map > m_IncomingStreams; // receiveStreamID->stream + std::shared_ptr m_LastStream; Acceptor m_Acceptor; std::list > m_PendingIncomingStreams; boost::asio::deadline_timer m_PendingIncomingTimer; - std::map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN + std::unordered_map > m_SavedPackets; // receiveStreamID->packets, arrived before SYN i2p::util::MemoryPool m_PacketsPool; i2p::util::MemoryPool > m_I2NPMsgsPool; diff --git a/libi2pd/TransitTunnel.cpp b/libi2pd/TransitTunnel.cpp index 73ca977c..dc7655d1 100644 --- a/libi2pd/TransitTunnel.cpp +++ b/libi2pd/TransitTunnel.cpp @@ -39,7 +39,7 @@ namespace tunnel void TransitTunnelParticipant::HandleTunnelDataMsg (std::shared_ptr 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 tunnelMsg) { - auto newMsg = CreateEmptyTunnelDataMsg (); + auto newMsg = CreateEmptyTunnelDataMsg (true); EncryptTunnelMsg (tunnelMsg, newMsg); LogPrint (eLogDebug, "TransitTunnel: handle msg for endpoint ", GetTunnelID ()); diff --git a/libi2pd/Transports.cpp b/libi2pd/Transports.cpp index c2bed2ab..181c3663 100644 --- a/libi2pd/Transports.cpp +++ b/libi2pd/Transports.cpp @@ -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 l(m_PeersMutex); it = m_Peers.insert (std::pair(ident, { 0, r, {}, diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index e016e9e4..ba01ae20 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -234,7 +234,7 @@ namespace tunnel void InboundTunnel::HandleTunnelDataMsg (std::shared_ptr 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); diff --git a/libi2pd/TunnelEndpoint.cpp b/libi2pd/TunnelEndpoint.cpp index eb70bdca..4885c090 100644 --- a/libi2pd/TunnelEndpoint.cpp +++ b/libi2pd/TunnelEndpoint.cpp @@ -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) + // handle fragment + if (isFollowOnFragment) { - LogPrint (eLogError, "TunnelMessage: fragment is too long ", (int)size); - return; - } - if (fragment + size < decrypted + TUNNEL_DATA_ENCRYPTED_SIZE) - { - // 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); - else - { - if (msgID) // msgID is presented, assume message is fragmented + // existing message + if (m_CurrentMsgID && m_CurrentMsgID == msgID && m_CurrentMessage.nextFragmentNum == fragmentNum) + HandleCurrenMessageFollowOnFragment (fragment, size, isLastFragment); // previous + else { - if (!isFollowOnFragment) // create new incomlete message - { - m.nextFragmentNum = 1; - m.receiveTime = i2p::util::GetMillisecondsSinceEpoch (); - auto ret = m_IncompleteMessages.insert (std::pair(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); - } + HandleFollowOnFragment (msgID, isLastFragment, fragmentNum, fragment, size); // another + m_CurrentMsgID = 0; m_CurrentMessage.data = nullptr; + } + } + else + { + // 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) + { + // 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 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 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; diff --git a/libi2pd/TunnelEndpoint.h b/libi2pd/TunnelEndpoint.h index 43b836f1..f9878165 100644 --- a/libi2pd/TunnelEndpoint.h +++ b/libi2pd/TunnelEndpoint.h @@ -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 -#include +#include +#include #include #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 data; uint64_t receiveTime; // milliseconds since epoch + std::vector 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 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 m_IncompleteMessages; - std::map, Fragment> m_OutOfSequenceFragments; // (msgID, fragment#)->fragment + std::unordered_map m_IncompleteMessages; + std::unordered_map > m_OutOfSequenceFragments; // ((msgID << 8) + fragment#)->fragment bool m_IsInbound; size_t m_NumReceivedBytes; + TunnelMessageBlockEx m_CurrentMessage; + uint32_t m_CurrentMsgID; }; } } diff --git a/libi2pd/TunnelGateway.cpp b/libi2pd/TunnelGateway.cpp index 317926ae..08df569c 100644 --- a/libi2pd/TunnelGateway.cpp +++ b/libi2pd/TunnelGateway.cpp @@ -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); diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index d8b84e82..70cf78a8 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -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