diff --git a/.github/workflows/build-freebsd.yml b/.github/workflows/build-freebsd.yml index 4e31bed7..c6e0addf 100644 --- a/.github/workflows/build-freebsd.yml +++ b/.github/workflows/build-freebsd.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v2 - name: Test in FreeBSD id: test - uses: vmactions/freebsd-vm@v0.1.2 + uses: vmactions/freebsd-vm@v0.1.4 with: usesh: true prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a776df92..22ba60bf 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,9 +3,9 @@ name: Build on Ubuntu on: [push, pull_request] jobs: - build: - name: With USE_UPNP=${{ matrix.with_upnp }} - runs-on: ubuntu-16.04 + build-make: + name: Make with USE_UPNP=${{ matrix.with_upnp }} + runs-on: ubuntu-18.04 strategy: fail-fast: true matrix: @@ -19,3 +19,70 @@ jobs: sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev - name: build application run: make USE_UPNP=${{ matrix.with_upnp }} -j3 + build-cmake: + name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }} + runs-on: ubuntu-18.04 + strategy: + fail-fast: true + matrix: + with_upnp: ['ON', 'OFF'] + steps: + - uses: actions/checkout@v2 + - name: install packages + run: | + sudo add-apt-repository ppa:mhier/libboost-latest + sudo apt-get update + sudo apt-get install build-essential cmake libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev + - name: build application + run: | + cd build + cmake -DWITH_UPNP=${{ matrix.with_upnp }} . + make -j3 + build-deb-stretch: + name: Build package for stretch + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: change debian changelog + run: | + sudo apt-get update + sudo apt-get install devscripts + debchange -v "`git describe --tags`-stretch" -M --distribution stretch "trunk build" + - uses: singingwolfboy/build-dpkg-stretch@v1 + id: build + with: + args: --unsigned-source --unsigned-changes -b + - uses: actions/upload-artifact@v1 + with: + name: ${{ steps.build.outputs.filename }} + path: ${{ steps.build.outputs.filename }} + - uses: actions/upload-artifact@v1 + with: + name: ${{ steps.build.outputs.filename-dbgsym }} + path: ${{ steps.build.outputs.filename-dbgsym }} + build-deb-buster: + name: Build package for buster + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: change debian changelog + run: | + sudo apt-get update + sudo apt-get install devscripts + debchange -v "`git describe --tags`-buster" -M --distribution buster "trunk build" + - uses: singingwolfboy/build-dpkg-buster@v1 + id: build + with: + args: --unsigned-source --unsigned-changes -b + - uses: actions/upload-artifact@v1 + with: + name: ${{ steps.build.outputs.filename }} + path: ${{ steps.build.outputs.filename }} + - uses: actions/upload-artifact@v1 + with: + name: ${{ steps.build.outputs.filename-dbgsym }} + path: ${{ steps.build.outputs.filename-dbgsym }} diff --git a/Makefile b/Makefile index 7ff74787..40e72918 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ I2PD := i2pd LIB_SRC_DIR := libi2pd LIB_CLIENT_SRC_DIR := libi2pd_client +LANG_SRC_DIR := i18n DAEMON_SRC_DIR := daemon # import source files lists @@ -49,12 +50,13 @@ ifeq ($(USE_MESHNET),yes) NEEDED_CXXFLAGS += -DMESHNET endif -NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) +NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SRC_DIR) LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) +LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC)) DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) -DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) +DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD) @@ -63,6 +65,7 @@ mk_obj_dir: @mkdir -p obj/Win32 @mkdir -p obj/$(LIB_SRC_DIR) @mkdir -p obj/$(LIB_CLIENT_SRC_DIR) + @mkdir -p obj/$(LANG_SRC_DIR) @mkdir -p obj/$(DAEMON_SRC_DIR) api: mk_obj_dir $(SHLIB) $(ARLIB) @@ -82,7 +85,7 @@ obj/%.o: %.cpp # '-' is 'ignore if missing' on first run -include $(DEPS) -$(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) +$(I2PD): $(LANG_OBJS) $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS) $(SHLIB): $(LIB_OBJS) diff --git a/README.md b/README.md index d21df6e7..92a2f46f 100644 --- a/README.md +++ b/README.md @@ -68,15 +68,15 @@ Build instructions: **Supported systems:** -* GNU/Linux - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) +* GNU/Linux - [![Build on Ubuntu](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build.yml) * CentOS / Fedora / Mageia - [![Build Status](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/status_image/last_build.png)](https://copr.fedorainfracloud.org/coprs/supervillain/i2pd/package/i2pd-git/) * Alpine, ArchLinux, openSUSE, Gentoo, Debian, Ubuntu, etc. -* Windows - [![Build status](https://ci.appveyor.com/api/projects/status/1908qe4p48ff1x23?svg=true)](https://ci.appveyor.com/project/PurpleI2P/i2pd) -* Mac OS X - [![Build Status](https://travis-ci.org/PurpleI2P/i2pd.svg?branch=openssl)](https://travis-ci.org/PurpleI2P/i2pd) +* 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/) -* Snap -* FreeBSD -* Android +* 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) * iOS Using i2pd @@ -94,6 +94,7 @@ ETH: 0x9e5bac70d20d1079ceaa111127f4fb3bccce379d DASH: Xw8YUrQpYzP9tZBmbjqxS3M97Q7v3vJKUF ZEC: t1cTckLuXsr1dwVrK4NDzfhehss4NvMadAJ GST: GbD2JSQHBHCKLa9WTHmigJRpyFgmBj4woG +XMR: 497pJc7X4xqKvcLBLpSUtRgWqMMyo24u4btCos3cak6gbMkpobgSU6492ztUcUBghyeHpYeczB55s38NpuHoH5WGNSPDRMH License ------- diff --git a/Win32/Win32App.cpp b/Win32/Win32App.cpp index db5a1f3d..7104ec7f 100644 --- a/Win32/Win32App.cpp +++ b/Win32/Win32App.cpp @@ -131,7 +131,7 @@ namespace win32 transfer >>= 10; auto mbytes = transfer & 0x03ff; transfer >>= 10; - auto gbytes = transfer & 0x03ff; + auto gbytes = transfer; if (gbytes) s << gbytes << " GB, "; diff --git a/build/CMakeLists.txt b/build/CMakeLists.txt index 6f9fbae6..d1b51f23 100644 --- a/build/CMakeLists.txt +++ b/build/CMakeLists.txt @@ -33,61 +33,15 @@ target_architecture(ARCHITECTURE) set(LIBI2PD_SRC_DIR ../libi2pd) set(LIBI2PD_CLIENT_SRC_DIR ../libi2pd_client) +set(LANG_SRC_DIR ../i18n) set(DAEMON_SRC_DIR ../daemon) include_directories(${LIBI2PD_SRC_DIR}) include_directories(${LIBI2PD_CLIENT_SRC_DIR}) +include_directories(${LANG_SRC_DIR}) include_directories(${DAEMON_SRC_DIR}) -set(LIBI2PD_SRC - "${LIBI2PD_SRC_DIR}/api.cpp" - "${LIBI2PD_SRC_DIR}/Base.cpp" - "${LIBI2PD_SRC_DIR}/Blinding.cpp" - "${LIBI2PD_SRC_DIR}/BloomFilter.cpp" - "${LIBI2PD_SRC_DIR}/ChaCha20.cpp" - "${LIBI2PD_SRC_DIR}/Config.cpp" - "${LIBI2PD_SRC_DIR}/CPU.cpp" - "${LIBI2PD_SRC_DIR}/Crypto.cpp" - "${LIBI2PD_SRC_DIR}/CryptoKey.cpp" - "${LIBI2PD_SRC_DIR}/Datagram.cpp" - "${LIBI2PD_SRC_DIR}/Destination.cpp" - "${LIBI2PD_SRC_DIR}/ECIESX25519AEADRatchetSession.cpp" - "${LIBI2PD_SRC_DIR}/Ed25519.cpp" - "${LIBI2PD_SRC_DIR}/Elligator.cpp" - "${LIBI2PD_SRC_DIR}/Family.cpp" - "${LIBI2PD_SRC_DIR}/FS.cpp" - "${LIBI2PD_SRC_DIR}/Garlic.cpp" - "${LIBI2PD_SRC_DIR}/Gost.cpp" - "${LIBI2PD_SRC_DIR}/Gzip.cpp" - "${LIBI2PD_SRC_DIR}/HTTP.cpp" - "${LIBI2PD_SRC_DIR}/I2NPProtocol.cpp" - "${LIBI2PD_SRC_DIR}/Identity.cpp" - "${LIBI2PD_SRC_DIR}/LeaseSet.cpp" - "${LIBI2PD_SRC_DIR}/Log.cpp" - "${LIBI2PD_SRC_DIR}/NetDb.cpp" - "${LIBI2PD_SRC_DIR}/NetDbRequests.cpp" - "${LIBI2PD_SRC_DIR}/NTCP2.cpp" - "${LIBI2PD_SRC_DIR}/Poly1305.cpp" - "${LIBI2PD_SRC_DIR}/Profiling.cpp" - "${LIBI2PD_SRC_DIR}/Reseed.cpp" - "${LIBI2PD_SRC_DIR}/RouterContext.cpp" - "${LIBI2PD_SRC_DIR}/RouterInfo.cpp" - "${LIBI2PD_SRC_DIR}/Signature.cpp" - "${LIBI2PD_SRC_DIR}/SSU.cpp" - "${LIBI2PD_SRC_DIR}/SSUData.cpp" - "${LIBI2PD_SRC_DIR}/SSUSession.cpp" - "${LIBI2PD_SRC_DIR}/Streaming.cpp" - "${LIBI2PD_SRC_DIR}/Timestamp.cpp" - "${LIBI2PD_SRC_DIR}/TransitTunnel.cpp" - "${LIBI2PD_SRC_DIR}/Transports.cpp" - "${LIBI2PD_SRC_DIR}/Tunnel.cpp" - "${LIBI2PD_SRC_DIR}/TunnelEndpoint.cpp" - "${LIBI2PD_SRC_DIR}/TunnelGateway.cpp" - "${LIBI2PD_SRC_DIR}/TunnelPool.cpp" - "${LIBI2PD_SRC_DIR}/TunnelConfig.cpp" - "${LIBI2PD_SRC_DIR}/util.cpp" -) - +FILE(GLOB LIBI2PD_SRC ${LIBI2PD_SRC_DIR}/*.cpp) add_library(libi2pd ${LIBI2PD_SRC}) set_target_properties(libi2pd PROPERTIES PREFIX "") @@ -102,19 +56,7 @@ if(WITH_LIBRARY) # install(EXPORT libi2pd DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() -set(CLIENT_SRC - "${LIBI2PD_CLIENT_SRC_DIR}/AddressBook.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/BOB.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/ClientContext.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/MatchedDestination.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/I2PTunnel.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/I2PService.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/SAM.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/SOCKS.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/HTTPProxy.cpp" - "${LIBI2PD_CLIENT_SRC_DIR}/I2CP.cpp" -) - +FILE(GLOB CLIENT_SRC ${LIBI2PD_CLIENT_SRC_DIR}/*.cpp) add_library(libi2pdclient ${CLIENT_SRC}) set_target_properties(libi2pdclient PROPERTIES PREFIX "") @@ -126,6 +68,8 @@ if(WITH_LIBRARY) COMPONENT Libraries) endif() +FILE(GLOB LANG_SRC ${LANG_SRC_DIR}/*.cpp) + set(DAEMON_SRC "${DAEMON_SRC_DIR}/Daemon.cpp" "${DAEMON_SRC_DIR}/HTTPServer.cpp" @@ -321,7 +265,7 @@ message(STATUS "---------------------------------------") include(GNUInstallDirs) if(WITH_BINARY) - add_executable("${PROJECT_NAME}" ${DAEMON_SRC}) + add_executable("${PROJECT_NAME}" ${LANG_SRC} ${DAEMON_SRC}) if(WITH_STATIC) set_target_properties("${PROJECT_NAME}" PROPERTIES LINK_FLAGS "-static") diff --git a/contrib/docker/Dockerfile b/contrib/docker/Dockerfile index adb7ba75..dc9f5501 100644 --- a/contrib/docker/Dockerfile +++ b/contrib/docker/Dockerfile @@ -25,24 +25,24 @@ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \ # 1. install deps, clone and build. # 2. strip binaries. # 3. Purge all dependencies and other unrelated packages, including build directory. -RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl git \ +RUN apk --no-cache --virtual build-dependendencies add make gcc g++ libtool zlib-dev boost-dev build-base openssl-dev openssl miniupnpc-dev git \ && mkdir -p /tmp/build \ && cd /tmp/build && git clone -b ${GIT_BRANCH} ${REPO_URL} \ && cd i2pd \ && if [ -n "${GIT_TAG}" ]; then git checkout tags/${GIT_TAG}; fi \ - && make \ + && make USE_UPNP=yes \ && cp -R contrib/certificates /i2pd_certificates \ && mkdir -p /usr/local/bin \ && mv i2pd /usr/local/bin \ && cd /usr/local/bin \ && strip i2pd \ && rm -fr /tmp/build && apk --no-cache --purge del build-dependendencies build-base fortify-headers boost-dev zlib-dev openssl-dev \ - boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \ + miniupnpc-dev boost-python3 python3 gdbm boost-unit_test_framework linux-headers boost-prg_exec_monitor \ boost-serialization boost-wave boost-wserialization boost-math boost-graph boost-regex git pcre2 \ libtool g++ gcc # 2. Adding required libraries to run i2pd to ensure it will run. -RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl musl-utils libstdc++ +RUN apk --no-cache add boost-filesystem boost-system boost-program_options boost-date_time boost-thread boost-iostreams openssl miniupnpc musl-utils libstdc++ COPY entrypoint.sh /entrypoint.sh RUN chmod a+x /entrypoint.sh diff --git a/contrib/i18n/English.po b/contrib/i18n/English.po new file mode 100644 index 00000000..1de2ddaa --- /dev/null +++ b/contrib/i18n/English.po @@ -0,0 +1,724 @@ +# i2pd +# Copyright (C) 2021 PurpleI2P team +# This file is distributed under the same license as the i2pd package. +# R4SAS , 2021. +# +msgid "" +msgstr "" +"Project-Id-Version: i2pd\n" +"Report-Msgid-Bugs-To: https://github.com/PurpleI2P/i2pd/issues\n" +"POT-Creation-Date: 2021-06-15 17:40\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: Poedit 3.0\n" +"X-Poedit-SourceCharset: UTF-8\n" +"X-Poedit-Basepath: .\n" +"X-Poedit-KeywordsList: ;tr\n" +"X-Poedit-SearchPath-0: daemon/HTTPServer.cpp\n" +"X-Poedit-SearchPath-1: libi2pd_client/HTTPProxy.cpp\n" + +#: daemon/HTTPServer.cpp:85 +msgid "Disabled" +msgstr "" + +#: daemon/HTTPServer.cpp:86 +msgid "Enabled" +msgstr "" + +#: daemon/HTTPServer.cpp:141 +msgid "day" +msgid_plural "days" +msgstr[0] "" +msgstr[1] "" +#: daemon/HTTPServer.cpp:145 +msgid "hour" +msgid_plural "hours" +msgstr[0] "" +msgstr[1] "" +#: daemon/HTTPServer.cpp:149 +msgid "minute" +msgid_plural "minutes" +msgstr[0] "" +msgstr[1] "" + +#: daemon/HTTPServer.cpp:152 +msgid "second" +msgid_plural "seconds" +msgstr[0] "" +msgstr[1] "" + +#: daemon/HTTPServer.cpp:160 daemon/HTTPServer.cpp:188 +msgid "KiB" +msgstr "" + +#: daemon/HTTPServer.cpp:162 +msgid "MiB" +msgstr "" + +#: daemon/HTTPServer.cpp:164 +msgid "GiB" +msgstr "" + +#: daemon/HTTPServer.cpp:181 +msgid "building" +msgstr "" + +#: daemon/HTTPServer.cpp:182 +msgid "failed" +msgstr "" + +#: daemon/HTTPServer.cpp:183 +msgid "expiring" +msgstr "" + +#: daemon/HTTPServer.cpp:184 +msgid "established" +msgstr "" + +#: daemon/HTTPServer.cpp:185 +msgid "unknown" +msgstr "" + +#: daemon/HTTPServer.cpp:187 +msgid "exploratory" +msgstr "" + +#: daemon/HTTPServer.cpp:223 +msgid "i2pd webconsole" +msgstr "" + +#: daemon/HTTPServer.cpp:226 +msgid "Main page" +msgstr "" + +#: daemon/HTTPServer.cpp:227 daemon/HTTPServer.cpp:683 +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 +msgid "Local Destinations" +msgstr "" + +#: daemon/HTTPServer.cpp:442 +msgid "Encrypted B33 address" +msgstr "" + +#: daemon/HTTPServer.cpp:451 +msgid "Address registration line" +msgstr "" + +#: daemon/HTTPServer.cpp:456 +msgid "Domain" +msgstr "" + +#: daemon/HTTPServer.cpp:457 +msgid "Generate" +msgstr "" + +#: daemon/HTTPServer.cpp:458 +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 +msgid "Address" +msgstr "" + +#: daemon/HTTPServer.cpp:464 +msgid "Type" +msgstr "" + +#: daemon/HTTPServer.cpp:464 +msgid "EncType" +msgstr "" + +#: daemon/HTTPServer.cpp:474 daemon/HTTPServer.cpp:657 +msgid "Inbound tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:479 daemon/HTTPServer.cpp:489 +#: daemon/HTTPServer.cpp:662 daemon/HTTPServer.cpp:672 +#: Means milliseconds +msgid "ms" +msgstr "" + +#: daemon/HTTPServer.cpp:484 daemon/HTTPServer.cpp:667 +msgid "Outbound tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:496 +msgid "Tags" +msgstr "" + +#: daemon/HTTPServer.cpp:496 +msgid "Incoming" +msgstr "" + +#: daemon/HTTPServer.cpp:503 daemon/HTTPServer.cpp:506 +msgid "Outgoing" +msgstr "" + +#: daemon/HTTPServer.cpp:504 daemon/HTTPServer.cpp:520 +msgid "Destination" +msgstr "" + +#: daemon/HTTPServer.cpp:504 +msgid "Amount" +msgstr "" + +#: daemon/HTTPServer.cpp:511 +msgid "Incoming Tags" +msgstr "" + +#: daemon/HTTPServer.cpp:519 daemon/HTTPServer.cpp:522 +msgid "Tags sessions" +msgstr "" + +#: daemon/HTTPServer.cpp:520 +msgid "Status" +msgstr "" + +#: daemon/HTTPServer.cpp:529 daemon/HTTPServer.cpp:584 +msgid "Local Destination" +msgstr "" + +#: daemon/HTTPServer.cpp:538 daemon/HTTPServer.cpp:887 +msgid "Streams" +msgstr "" + +#: daemon/HTTPServer.cpp:560 +msgid "Close stream" +msgstr "" + +#: daemon/HTTPServer.cpp:589 +msgid "I2CP session not found" +msgstr "" + +#: daemon/HTTPServer.cpp:592 +msgid "I2CP is not enabled" +msgstr "" + +#: daemon/HTTPServer.cpp:618 +msgid "Invalid" +msgstr "" + +#: daemon/HTTPServer.cpp:621 +msgid "Store type" +msgstr "" + +#: daemon/HTTPServer.cpp:622 +msgid "Expires" +msgstr "" + +#: daemon/HTTPServer.cpp:627 +msgid "Non Expired Leases" +msgstr "" + +#: daemon/HTTPServer.cpp:630 +msgid "Gateway" +msgstr "" + +#: daemon/HTTPServer.cpp:631 +msgid "TunnelID" +msgstr "" + +#: daemon/HTTPServer.cpp:632 +msgid "EndDate" +msgstr "" + +#: daemon/HTTPServer.cpp:642 +msgid "not floodfill" +msgstr "" + +#: daemon/HTTPServer.cpp:653 +msgid "Queue size" +msgstr "" + +#: daemon/HTTPServer.cpp:684 +msgid "Run peer test" +msgstr "" + +#: daemon/HTTPServer.cpp:687 +msgid "Decline transit tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:689 +msgid "Accept transit tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:692 daemon/HTTPServer.cpp:697 +msgid "Cancel graceful shutdown" +msgstr "" + +#: daemon/HTTPServer.cpp:694 daemon/HTTPServer.cpp:699 +msgid "Start graceful shutdown" +msgstr "" + +#: daemon/HTTPServer.cpp:701 +msgid "Force shutdown" +msgstr "" + +#: daemon/HTTPServer.cpp:704 +msgid "" +"Note: any action done here are not persistent and not changes your " +"config files." +msgstr "" + +#: daemon/HTTPServer.cpp:706 +msgid "Logging level" +msgstr "" + +#: daemon/HTTPServer.cpp:714 +msgid "Transit tunnels limit" +msgstr "" + +#: daemon/HTTPServer.cpp:719 +msgid "Change" +msgstr "" + +#: daemon/HTTPServer.cpp:743 +msgid "no transit tunnels currently built" +msgstr "" + +#: daemon/HTTPServer.cpp:848 daemon/HTTPServer.cpp:871 +msgid "SAM disabled" +msgstr "" + +#: daemon/HTTPServer.cpp:864 +msgid "no sessions currently running" +msgstr "" + +#: daemon/HTTPServer.cpp:877 +msgid "SAM session not found" +msgstr "" + +#: daemon/HTTPServer.cpp:882 +msgid "SAM Session" +msgstr "" + +#: daemon/HTTPServer.cpp:939 +msgid "Server Tunnels" +msgstr "" + +#: daemon/HTTPServer.cpp:955 +msgid "Client Forwards" +msgstr "" + +#: daemon/HTTPServer.cpp:969 +msgid "Server Forwards" +msgstr "" + +#: daemon/HTTPServer.cpp:1175 +msgid "Unknown page" +msgstr "" + +#: daemon/HTTPServer.cpp:1194 +msgid "Invalid token" +msgstr "" + +#: daemon/HTTPServer.cpp:1252 daemon/HTTPServer.cpp:1309 +#: daemon/HTTPServer.cpp:1337 +msgid "SUCCESS" +msgstr "" + +#: daemon/HTTPServer.cpp:1252 +msgid "Stream closed" +msgstr "" + +#: daemon/HTTPServer.cpp:1254 +msgid "Stream not found or already was closed" +msgstr "" + +#: daemon/HTTPServer.cpp:1257 +msgid "Destination not found" +msgstr "" + +#: daemon/HTTPServer.cpp:1260 +msgid "StreamID can't be null" +msgstr "" + +#: daemon/HTTPServer.cpp:1262 daemon/HTTPServer.cpp:1327 +msgid "Return to destination page" +msgstr "" + +#: daemon/HTTPServer.cpp:1263 daemon/HTTPServer.cpp:1276 +msgid "You will be redirected back in 5 seconds" +msgstr "" + +#: daemon/HTTPServer.cpp:1274 +msgid "Transit tunnels count must not exceed 65535" +msgstr "" + +#: daemon/HTTPServer.cpp:1275 daemon/HTTPServer.cpp:1338 +msgid "Back to commands list" +msgstr "" + +#: daemon/HTTPServer.cpp:1311 +msgid "Register at reg.i2p" +msgstr "" + +#: daemon/HTTPServer.cpp:1312 +msgid "Description" +msgstr "" + +#: daemon/HTTPServer.cpp:1312 +msgid "A bit information about service on domain" +msgstr "" + +#: daemon/HTTPServer.cpp:1313 +msgid "Submit" +msgstr "" + +#: daemon/HTTPServer.cpp:1319 +msgid "Domain can't end with .b32.i2p" +msgstr "" + +#: daemon/HTTPServer.cpp:1322 +msgid "Domain must end with .i2p" +msgstr "" + +#: daemon/HTTPServer.cpp:1325 +msgid "Such destination is not found" +msgstr "" + +#: daemon/HTTPServer.cpp:1333 +msgid "Unknown command" +msgstr "" + +#: daemon/HTTPServer.cpp:1337 +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 "" + +#: libi2pd_client/HTTPProxy.cpp:165 +msgid "Proxy info" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:173 +msgid "Proxy error: Host not found" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:174 +msgid "Remote host not found in router's addressbook" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:175 +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 +msgid "Invalid request" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:273 +msgid "Proxy unable to parse your request" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:288 +msgid "addresshelper is not supported" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:297 libi2pd_client/HTTPProxy.cpp:306 +#: libi2pd_client/HTTPProxy.cpp:385 +msgid "Host" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:297 +msgid "added to router's addressbook from helper" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:307 +msgid "Click" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:298 libi2pd_client/HTTPProxy.cpp:308 +msgid "here" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:298 +msgid "to proceed" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:299 libi2pd_client/HTTPProxy.cpp:309 +msgid "Addresshelper found" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:306 +msgid "already in router's addressbook" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:308 +msgid "to update record" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:322 +msgid "Invalid Request" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:322 +msgid "invalid request uri" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:365 +msgid "Can't detect destination host from request" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:382 libi2pd_client/HTTPProxy.cpp:386 +msgid "Outproxy failure" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:382 +msgid "bad outproxy settings" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:385 +msgid "not inside I2P network, but outproxy is not enabled" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:474 +msgid "unknown outproxy url" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:480 +msgid "cannot resolve upstream proxy" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:488 +msgid "hostname too long" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:515 +msgid "cannot connect to upstream socks proxy" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:521 +msgid "Cannot negotiate with socks proxy" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:563 +msgid "CONNECT error" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:563 +msgid "Failed to Connect" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:574 libi2pd_client/HTTPProxy.cpp:600 +msgid "socks proxy error" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:582 +msgid "failed to send request to upstream" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:603 +msgid "No Reply From socks proxy" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:610 +msgid "cannot connect" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:610 +msgid "http out proxy not implemented" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:611 +msgid "cannot connect to upstream http proxy" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:644 +msgid "Host is down" +msgstr "" + +#: libi2pd_client/HTTPProxy.cpp:644 +msgid "" +"Can't create connection to requested host, it may be down. Please try again " +"later." +msgstr "" diff --git a/contrib/i18n/regex.txt b/contrib/i18n/regex.txt new file mode 100644 index 00000000..768a4b01 --- /dev/null +++ b/contrib/i18n/regex.txt @@ -0,0 +1,7 @@ +Regex for transforming gettext translations to our format + +msgid\ \"(.*)\"\nmsgid_plural\ \"(.*)\"\nmsgstr\[0\]\ \"(.*)\"\nmsgstr\[1\]\ \"(.*)\"\n(msgstr\[2\]\ \"(.*)\"\n)?(msgstr\[3\]\ \"(.*)\"\n)?(msgstr\[4\]\ \"(.*)\"\n)?(msgstr\[5\]\ \"(.*)\"\n)? +#{"$2", {"$3", "$4", "$6", "$8", "$10"}},\n + +msgid\ \"(.*)\"\nmsgstr\ \"(.*)\"\n +{"$1", "$2"},\n diff --git a/contrib/i2pd.conf b/contrib/i2pd.conf index e8c397f5..f3cea2b5 100644 --- a/contrib/i2pd.conf +++ b/contrib/i2pd.conf @@ -103,6 +103,9 @@ port = 7070 # auth = true # user = i2pd # pass = changeme +## Select webconsole language +## Currently supported english (default), russian, turkmen and ukrainian languages +# lang = english [httpproxy] ## Uncomment and set to 'false' to disable HTTP Proxy diff --git a/debian/i2pd.openrc b/contrib/openrc/i2pd.openrc similarity index 100% rename from debian/i2pd.openrc rename to contrib/openrc/i2pd.openrc diff --git a/debian/i2pd.upstart b/contrib/upstart/i2pd.upstart similarity index 100% rename from debian/i2pd.upstart rename to contrib/upstart/i2pd.upstart diff --git a/daemon/Daemon.cpp b/daemon/Daemon.cpp index 77000652..f7f2ef2f 100644 --- a/daemon/Daemon.cpp +++ b/daemon/Daemon.cpp @@ -32,6 +32,7 @@ #include "UPnP.h" #include "Timestamp.h" #include "util.h" +#include "I18N.h" namespace i2p { @@ -343,6 +344,10 @@ namespace util LogPrint(eLogInfo, "Daemon: using hidden mode"); i2p::data::netdb.SetHidden(true); } + + std::string httpLang; i2p::config::GetOption("http.lang", httpLang); + i2p::i18n::SetLanguage(httpLang); + return true; } diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index d56c2894..7eb296c4 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -30,6 +30,7 @@ #include "Daemon.h" #include "util.h" #include "ECIESX25519AEADRatchetSession.h" +#include "I18N.h" #ifdef WIN32_APP #include "Win32App.h" @@ -40,7 +41,7 @@ namespace i2p { namespace http { - const char *itoopieFavicon = + const std::string itoopieFavicon = "data:image/png;base64," "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx" "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0" @@ -58,49 +59,51 @@ namespace http { "JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ" "RU5ErkJggg=="; - const char *cssStyles = - "\r\n"; + static void GetStyles (std::stringstream& s) + { + s << "\r\n"; + } const char HTTP_PAGE_TUNNELS[] = "tunnels"; const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels"; @@ -135,18 +138,18 @@ namespace http { int num; if ((num = seconds / 86400) > 0) { - s << num << " days, "; + s << num << " " << tr("day", "days", num) << ", "; seconds -= num * 86400; } if ((num = seconds / 3600) > 0) { - s << num << " hours, "; + s << num << " " << tr("hour", "hours", num) << ", "; seconds -= num * 3600; } if ((num = seconds / 60) > 0) { - s << num << " min, "; + s << num << " " << tr("minute", "minutes", num) << ", "; seconds -= num * 60; } - s << seconds << " seconds"; + s << seconds << " " << tr("second", "seconds", seconds); } static void ShowTraffic (std::stringstream& s, uint64_t bytes) @@ -154,28 +157,35 @@ namespace http { s << std::fixed << std::setprecision(2); auto numKBytes = (double) bytes / 1024; if (numKBytes < 1024) - s << numKBytes << " KiB"; + s << numKBytes << " " << tr("KiB"); else if (numKBytes < 1024 * 1024) - s << numKBytes / 1024 << " MiB"; + s << numKBytes / 1024 << " " << tr("MiB"); else - s << numKBytes / 1024 / 1024 << " GiB"; + s << numKBytes / 1024 / 1024 << " " << tr("GiB"); } static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) { - std::string state; + std::string state, stateText; switch (eState) { case i2p::tunnel::eTunnelStateBuildReplyReceived : - case i2p::tunnel::eTunnelStatePending : state = "building"; break; + case i2p::tunnel::eTunnelStatePending : state = "building"; break; case i2p::tunnel::eTunnelStateBuildFailed : case i2p::tunnel::eTunnelStateTestFailed : - case i2p::tunnel::eTunnelStateFailed : state = "failed"; break; - case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break; + case i2p::tunnel::eTunnelStateFailed : state = "failed"; break; + case i2p::tunnel::eTunnelStateExpiring : state = "expiring"; break; case i2p::tunnel::eTunnelStateEstablished : state = "established"; break; default: state = "unknown"; break; } - s << " " << state << ((explr) ? " (exploratory)" : "") << ", "; - s << " " << (int) (bytes / 1024) << " KiB\r\n"; + + if (state == "building") stateText = tr("building"); + else if (state == "failed") stateText = tr("failed"); + else if (state == "expiring") stateText = tr("expiring"); + else if (state == "established") stateText = tr("established"); + else stateText = tr("unknown"); + + s << " " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << ", "; + s << " " << (int) (bytes / 1024) << " " << tr("KiB") << "\r\n"; } static void SetLogLevel (const std::string& level) @@ -191,39 +201,40 @@ namespace http { static void ShowPageHead (std::stringstream& s) { - std::string webroot; - i2p::config::GetOption("http.webroot", webroot); + std::string webroot; i2p::config::GetOption("http.webroot", webroot); + + // Page language + std::string lang, langCode; i2p::config::GetOption("http.lang", lang); + if (lang == "russian") langCode = "ru"; + else langCode = "en"; + s << "\r\n" - "\r\n" /* TODO: Add support for locale */ + "\r\n" " \r\n" /* TODO: Find something to parse html/template system. This is horrible. */ -#if (!defined(WIN32)) " \r\n" -#else - " \r\n" -#endif " \r\n" " \r\n" - " Purple I2P " VERSION " Webconsole\r\n" - << cssStyles << - "\r\n"; + " Purple I2P " VERSION " Webconsole\r\n"; + GetStyles(s); s << + "\r\n" "\r\n" - "
i2pd webconsole
\r\n" + "
" << tr("i2pd webconsole") << "
\r\n" "
\r\n" "
\r\n" - " Main page
\r\n" - " Router commands\r\n" - " Local destinations\r\n"; + " " << tr("Main page") << "
\r\n" + " " << tr("Router commands") << "\r\n" + " " << tr("Local destinations") << "\r\n"; if (i2p::context.IsFloodfill ()) - s << " LeaseSets\r\n"; + s << " " << tr("LeaseSets") << "\r\n"; s << - " Tunnels\r\n" - " Transit tunnels\r\n" - " Transports\r\n" - " I2P tunnels\r\n"; + " " << tr("Tunnels") << "\r\n" + " " << tr("Transit tunnels") << "\r\n" + " " << tr ("Transports") << "\r\n" + " " << tr("I2P tunnels") << "\r\n"; if (i2p::client::context.GetSAMBridge ()) - s << " SAM sessions\r\n"; + s << " " << tr("SAM sessions") << "\r\n"; s << "
\r\n" "
"; @@ -239,94 +250,94 @@ namespace http { static void ShowError(std::stringstream& s, const std::string& string) { - s << "ERROR: " << string << "
\r\n"; + s << "" << tr("ERROR") << ": " << string << "
\r\n"; } static void ShowNetworkStatus (std::stringstream& s, RouterStatus status) { switch (status) { - case eRouterStatusOK: s << "OK"; break; - case eRouterStatusTesting: s << "Testing"; break; - case eRouterStatusFirewalled: s << "Firewalled"; break; - case eRouterStatusUnknown: s << "Unknown"; break; - case eRouterStatusProxy: s << "Proxy"; break; - case eRouterStatusMesh: s << "Mesh"; break; + case eRouterStatusOK: s << tr("OK"); break; + case eRouterStatusTesting: s << tr("Testing"); break; + case eRouterStatusFirewalled: s << tr("Firewalled"); break; + case eRouterStatusUnknown: s << tr("Unknown"); break; + case eRouterStatusProxy: s << tr("Proxy"); break; + case eRouterStatusMesh: s << tr("Mesh"); break; case eRouterStatusError: { - s << "Error"; + s << tr("Error"); switch (i2p::context.GetError ()) { case eRouterErrorClockSkew: - s << " - Clock skew"; + s << " - " << tr("Clock skew"); break; case eRouterErrorOffline: - s << " - Offline"; + s << " - " << tr("Offline"); break; case eRouterErrorSymmetricNAT: - s << " - Symmetric NAT"; + s << " - " << tr("Symmetric NAT"); break; default: ; } break; } - default: s << "Unknown"; + default: s << tr("Unknown"); } } void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat) { - s << "Uptime: "; + s << "" << tr("Uptime") << ": "; ShowUptime(s, i2p::context.GetUptime ()); s << "
\r\n"; - s << "Network status: "; + s << "" << tr("Network status") << ": "; ShowNetworkStatus (s, i2p::context.GetStatus ()); s << "
\r\n"; if (i2p::context.SupportsV6 ()) { - s << "Network status 6: "; + s << "" << tr("Network status v6") << ": "; ShowNetworkStatus (s, i2p::context.GetStatusV6 ()); s << "
\r\n"; } #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) if (auto remains = Daemon.gracefulShutdownInterval) { - s << "Stopping in: "; + s << "" << tr("Stopping in") << ": "; ShowUptime(s, remains); s << "
\r\n"; } #elif defined(WIN32_APP) if (i2p::win32::g_GracefulShutdownEndtime != 0) { uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000; - s << "Stopping in: "; + s << "" << tr("Stopping in") << ": "; ShowUptime(s, remains); s << "
\r\n"; } #endif auto family = i2p::context.GetFamily (); if (family.length () > 0) - s << "Family: " << family << "
\r\n"; - s << "Tunnel creation success rate: " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
\r\n"; - s << "Received: "; + s << ""<< tr("Family") << ": " << family << "
\r\n"; + 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 << " KiB/s)
\r\n"; - s << "Sent: "; + s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << "" << tr("Sent") << ": "; ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); - s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " KiB/s)
\r\n"; - s << "Transit: "; + s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << "" << tr("Transit") << ": "; ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); - s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " KiB/s)
\r\n"; - s << "Data path: " << i2p::fs::GetDataDir() << "
\r\n"; + s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr("KiB/s") << ")
\r\n"; + s << "" << tr("Data path") << ": " << i2p::fs::GetUTF8DataDir() << "
\r\n"; s << "
"; - if((outputFormat==OutputFormatEnum::forWebConsole)||!includeHiddenContent) { - s << "\r\n\r\n
\r\n"; + if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { + s << "\r\n\r\n
\r\n"; } if(includeHiddenContent) { - s << "Router Ident: " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; + s << "" << tr("Router Ident") << ": " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; if (!i2p::context.GetRouterInfo().GetProperty("family").empty()) - s << "Router Family: " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; - s << "Router Caps: " << i2p::context.GetRouterInfo().GetProperty("caps") << "
\r\n"; - s << "Version: " VERSION "
\r\n"; - s << "Our external address:" << "
\r\n\r\n"; + s << "" << tr("Router Family") << ": " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; + s << "" << tr("Router Caps") << ": " << i2p::context.GetRouterInfo().GetProperty("caps") << "
\r\n"; + s << "" << tr("Version") << ": " VERSION "
\r\n"; + s << ""<< tr("Our external address") << ":" << "
\r\n
\r\n"; for (const auto& address : i2p::context.GetRouterInfo().GetAddresses()) { s << "\r\n"; @@ -334,7 +345,7 @@ namespace http { { s << "\r\n\r\n"; + s << "\r\n\r\n"; continue; } switch (address->transportStyle) @@ -356,32 +367,32 @@ namespace http { break; } default: - s << "\r\n"; + s << "\r\n"; } s << "\r\n\r\n"; } s << "
NTCP2"; if (address->host.is_v6 ()) s << "v6"; - s << "supported
" << tr("supported") << "
Unknown" << tr("Unknown") << "" << address->host.to_string() << ":" << address->port << "
\r\n"; } s << "
\r\n
\r\n"; - if(outputFormat==OutputFormatEnum::forQtUi) { + if(outputFormat == OutputFormatEnum::forQtUi) { s << "
"; } - s << "Routers: " << i2p::data::netdb.GetNumRouters () << " "; - s << "Floodfills: " << i2p::data::netdb.GetNumFloodfills () << " "; - s << "LeaseSets: " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; + s << "" << tr("Routers") << ": " << i2p::data::netdb.GetNumRouters () << " "; + s << "" << tr("Floodfills") << ": " << i2p::data::netdb.GetNumFloodfills () << " "; + s << "" << tr("LeaseSets") << ": " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels(); clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels(); size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels(); - s << "Client Tunnels: " << std::to_string(clientTunnelCount) << " "; - s << "Transit Tunnels: " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; + s << "" << tr("Client Tunnels") << ": " << std::to_string(clientTunnelCount) << " "; + s << "" << tr("Transit Tunnels") << ": " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; if(outputFormat==OutputFormatEnum::forWebConsole) { bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; + s << "
Services
" << "HTTP Proxy" << "
" << "SOCKS Proxy" << "
\r\n"; + s << "\r\n"; + s << "\r\n"; s << "\r\n"; s << "\r\n"; s << "\r\n"; @@ -393,7 +404,7 @@ namespace http { void ShowLocalDestinations (std::stringstream& s) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "Local Destinations:
\r\n
\r\n"; + s << "" << tr("Local Destinations") << ":
\r\n
\r\n"; for (auto& it: i2p::client::context.GetDestinations ()) { auto ident = it.second->GetIdentHash (); @@ -405,7 +416,7 @@ namespace http { auto i2cpServer = i2p::client::context.GetI2CPServer (); if (i2cpServer && !(i2cpServer->GetSessions ().empty ())) { - s << "
I2CP Local Destinations:
\r\n
\r\n"; + s << "
I2CP "<< tr("Local Destinations") << ":
\r\n
\r\n"; for (auto& it: i2cpServer->GetSessions ()) { auto dest = it.second->GetDestination (); @@ -428,7 +439,7 @@ namespace http { if (dest->IsEncryptedLeaseSet ()) { i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ()); - s << "
\r\n\r\n
\r\n"; + s << "
\r\n\r\n
\r\n"; s << blinded.ToB33 () << ".b32.i2p
\r\n"; s << "
\r\n
\r\n"; } @@ -437,67 +448,67 @@ namespace http { { std::string webroot; i2p::config::GetOption("http.webroot", webroot); auto base32 = dest->GetIdentHash ().ToBase32 (); - s << "
\r\n\r\n
\r\n" + s << "
\r\n\r\n
\r\n" "
\r\n" " \r\n" " \r\n" " \r\n" - " Domain:\r\n\r\n" - " \r\n" - "\r\nNote: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.\r\n
\r\n
\r\n
\r\n"; + " " << tr("Domain") << ":\r\n\r\n" + " \r\n" + "\r\n" << tr("Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "\r\n
\r\n
\r\n
\r\n"; } if(dest->GetNumRemoteLeaseSets()) { - s << "
\r\n\r\n
\r\n
" << tr("Services") << "
" << "HTTP " << tr("Proxy") << "
" << "SOCKS " << tr("Proxy") << "
" << "BOB" << "
" << "SAM" << "
" << "I2CP" << "
"; + s << "
\r\n\r\n
\r\n
AddressTypeEncType
"; for(auto& it: dest->GetLeaseSets ()) s << "\r\n"; s << "
"<< tr("Address") << "" << tr("Type") << "" << tr("EncType") << "
" << it.first.ToBase32 () << "" << (int)it.second->GetStoreType () << "" << (int)it.second->GetEncryptionType () <<"
\r\n
\r\n
\r\n
\r\n"; } else - s << "LeaseSets: 0
\r\n
\r\n"; + s << "" << tr("LeaseSets") << ": 0
\r\n
\r\n"; auto pool = dest->GetTunnelPool (); if (pool) { - s << "Inbound tunnels:
\r\n
\r\n"; + s << "" << tr("Inbound tunnels") << ":
\r\n
\r\n"; for (auto & it : pool->GetInboundTunnels ()) { s << "
"; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << "ms )"; + s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); s << "
\r\n"; } s << "
\r\n"; - s << "Outbound tunnels:
\r\n
\r\n"; + s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; for (auto & it : pool->GetOutboundTunnels ()) { s << "
"; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << "ms )"; + s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); s << "
\r\n"; } } s << "
\r\n"; - s << "Tags
\r\nIncoming: " << dest->GetNumIncomingTags () << "
\r\n"; + s << "" << tr("Tags") << "
\r\n" << tr("Incoming") << ": " << dest->GetNumIncomingTags () << "
\r\n"; if (!dest->GetSessions ().empty ()) { std::stringstream tmp_s; uint32_t out_tags = 0; for (const auto& it: dest->GetSessions ()) { tmp_s << "" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "" << it.second->GetNumOutgoingTags () << "\r\n"; out_tags += it.second->GetNumOutgoingTags (); } - s << "
\r\n\r\n" - << "
\r\n\r\n\r\n\r\n" << tmp_s.str () << "
DestinationAmount
\r\n
\r\n
\r\n"; + s << "
\r\n\r\n" + << "
\r\n\r\n\r\n\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Amount") << "
\r\n
\r\n
\r\n"; } else - s << "Outgoing: 0
\r\n"; + s << tr("Outgoing") << ": 0
\r\n"; s << "
\r\n"; auto numECIESx25519Tags = dest->GetNumIncomingECIESx25519Tags (); if (numECIESx25519Tags > 0) { - s << "ECIESx25519
\r\nIncoming Tags: " << numECIESx25519Tags << "
\r\n"; + s << "ECIESx25519
\r\n" << tr("Incoming Tags") << ": " << numECIESx25519Tags << "
\r\n"; if (!dest->GetECIESx25519Sessions ().empty ()) { std::stringstream tmp_s; uint32_t ecies_sessions = 0; @@ -505,17 +516,17 @@ namespace http { tmp_s << "" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "" << it.second->GetState () << "\r\n"; ecies_sessions++; } - s << "
\r\n\r\n" - << "
\r\n\r\n\r\n\r\n" << tmp_s.str () << "
DestinationStatus
\r\n
\r\n
\r\n"; + s << "
\r\n\r\n" + << "
\r\n\r\n\r\n\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Status") << "
\r\n
\r\n
\r\n"; } else - s << "Tags sessions: 0
\r\n"; + s << tr("Tags sessions") << ": 0
\r\n"; s << "
\r\n"; } } void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token) { - s << "Local Destination:
\r\n
\r\n"; + s << "" << tr("Local Destination") << ":
\r\n
\r\n"; i2p::data::IdentHash ident; ident.FromBase32 (b32); auto dest = i2p::client::context.FindLocalDestination (ident); @@ -524,7 +535,7 @@ namespace http { { ShowLeaseSetDestination (s, dest, token); // show streams - s << "\r\n\r\n\r\n"; + s << "
Streams
\r\n\r\n\r\n"; s << ""; s << ""; @@ -546,7 +557,7 @@ namespace http { s << ""; if (it->GetRecvStreamID ()) { s << ""; + << it->GetRecvStreamID () << "&token=" << token << "\" title=\"" << tr("Close stream") << "\"> ✘ "; } else { s << "
" << tr("Streams") << "
StreamID"; // Stream closing button column s << "Destination" << it->GetRecvStreamID () << ""; } @@ -570,22 +581,22 @@ namespace http { auto i2cpServer = i2p::client::context.GetI2CPServer (); if (i2cpServer) { - s << "I2CP Local Destination:
\r\n
\r\n"; + s << "I2CP " << tr("Local Destination") << ":
\r\n
\r\n"; auto it = i2cpServer->GetSessions ().find (std::stoi (id)); if (it != i2cpServer->GetSessions ().end ()) ShowLeaseSetDestination (s, it->second->GetDestination (), 0); else - ShowError(s, "I2CP session not found"); + ShowError(s, tr("I2CP session not found")); } else - ShowError(s, "I2CP is not enabled"); + ShowError(s, tr("I2CP is not enabled")); } void ShowLeasesSets(std::stringstream& s) { if (i2p::data::netdb.GetNumLeaseSets ()) { - s << "LeaseSets:
\r\n
\r\n"; + s << "" << tr("LeaseSets") << ":
\r\n
\r\n"; int counter = 1; // for each lease set i2p::data::netdb.VisitLeaseSets( @@ -604,21 +615,21 @@ namespace http { s << " expired"; // additional css class for expired s << "\">\r\n"; if (!ls->IsValid()) - s << "
!! Invalid !!
\r\n"; + s << "
!! " << tr("Invalid") << " !!
\r\n"; s << "
\r\n"; s << "\r\n
\r\n"; - s << "Store type: " << (int)storeType << "
\r\n"; - s << "Expires: " << ConvertTime(ls->GetExpirationTime()) << "
\r\n"; + s << "" << tr("Store type") << ": " << (int)storeType << "
\r\n"; + s << "" << tr("Expires") << ": " << ConvertTime(ls->GetExpirationTime()) << "
\r\n"; if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) { // leases information is available auto leases = ls->GetNonExpiredLeases(); - s << "Non Expired Leases: " << leases.size() << "
\r\n"; + s << "" << tr("Non Expired Leases") << ": " << leases.size() << "
\r\n"; for ( auto & l : leases ) { - s << "Gateway: " << l->tunnelGateway.ToBase64() << "
\r\n"; - s << "TunnelID: " << l->tunnelID << "
\r\n"; - s << "EndDate: " << ConvertTime(l->endDate) << "
\r\n"; + s << "" << tr("Gateway") << ": " << l->tunnelGateway.ToBase64() << "
\r\n"; + s << "" << tr("TunnelID") << ": " << l->tunnelID << "
\r\n"; + s << "" << tr("EndDate") << ": " << ConvertTime(l->endDate) << "
\r\n"; } } s << "
\r\n
\r\n
\r\n"; @@ -628,37 +639,37 @@ namespace http { } else if (!i2p::context.IsFloodfill ()) { - s << "LeaseSets: not floodfill.
\r\n"; + s << "" << tr("LeaseSets") << ": " << tr("not floodfill") << ".
\r\n"; } else { - s << "LeaseSets: 0
\r\n"; + s << "" << tr("LeaseSets") << ": 0
\r\n"; } } void ShowTunnels (std::stringstream& s) { - s << "Tunnels:
\r\n"; - s << "Queue size: " << i2p::tunnel::tunnels.GetQueueSize () << "
\r\n
\r\n"; + s << "" << tr("Tunnels") << ":
\r\n"; + s << "" << tr("Queue size") << ": " << i2p::tunnel::tunnels.GetQueueSize () << "
\r\n
\r\n"; auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool (); - s << "Inbound tunnels:
\r\n
\r\n"; + s << "" << tr("Inbound tunnels") << ":
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { s << "
"; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << "ms )"; + s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); s << "
\r\n"; } s << "
\r\n
\r\n"; - s << "Outbound tunnels:
\r\n
\r\n"; + s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) { s << "
"; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << "ms )"; + s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); s << "
\r\n"; } @@ -669,30 +680,30 @@ namespace http { { std::string webroot; i2p::config::GetOption("http.webroot", webroot); /* commands */ - s << "Router Commands
\r\n
\r\n
\r\n"; - s << " Run peer test\r\n"; + s << "" << tr("Router commands") << "
\r\n
\r\n
\r\n"; + s << " " << tr("Run peer test") << "\r\n"; //s << " Reload config
\r\n"; if (i2p::context.AcceptsTunnels ()) - s << " Decline transit tunnels\r\n"; + s << " " << tr("Decline transit tunnels") << "\r\n"; else - s << " 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 << " Cancel graceful shutdown\r\n"; + s << " " << tr("Cancel graceful shutdown") << "\r\n"; else - s << " Start graceful shutdown
\r\n"; + s << " " << tr("Start graceful shutdown") << "\r\n"; #elif defined(WIN32_APP) if (i2p::util::DaemonWin32::Instance().isGraceful) - s << " Cancel graceful shutdown\r\n"; + s << " " << tr("Cancel graceful shutdown") << "\r\n"; else - s << " Graceful shutdown\r\n"; + s << " " << tr("Start graceful shutdown") << "\r\n"; #endif - s << " Force shutdown\r\n"; + s << " " << tr("Force shutdown") << "\r\n"; s << "
"; - s << "
\r\nNote: any action done here are not persistent and not changes your config files.\r\n
\r\n"; + s << "
\r\n" << tr("Note: any action done here are not persistent and not changes your config files.") << "\r\n
\r\n"; - s << "Logging level
\r\n"; + s << "" << tr("Logging level") << "
\r\n"; s << " none \r\n"; s << " error \r\n"; s << " warn \r\n"; @@ -700,12 +711,12 @@ namespace http { s << " debug
\r\n
\r\n"; uint16_t maxTunnels = GetMaxNumTransitTunnels (); - s << "Transit tunnels limit
\r\n"; + s << "" << tr("Transit tunnels limit") << "
\r\n"; s << "
\r\n"; s << " \r\n"; s << " \r\n"; s << " \r\n"; - s << " \r\n"; + s << " \r\n"; s << "
\r\n
\r\n"; } @@ -713,7 +724,7 @@ namespace http { { if(i2p::tunnel::tunnels.CountTransitTunnels()) { - s << "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"; @@ -729,7 +740,7 @@ namespace http { } else { - s << "Transit tunnels: no transit tunnels currently built.
\r\n"; + s << "" << tr("Transit tunnels") << ": " << tr("no transit tunnels currently built") << ".
\r\n"; } } @@ -778,7 +789,7 @@ namespace http { void ShowTransports (std::stringstream& s) { - s << "Transports:
\r\n"; + s << "" << tr("Transports") << ":
\r\n"; auto ntcp2Server = i2p::transport::transports.GetNTCP2Server (); if (ntcp2Server) { @@ -834,13 +845,13 @@ namespace http { auto sam = i2p::client::context.GetSAMBridge (); if (!sam) { - ShowError(s, "SAM disabled"); + ShowError(s, tr("SAM disabled")); return; } if(sam->GetSessions ().size ()) { - s << "SAM Sessions:
\r\n
\r\n"; + s << "" << tr("SAM sessions") << ":
\r\n
\r\n"; for (auto& it: sam->GetSessions ()) { auto& name = it.second->GetLocalDestination ()->GetNickname (); @@ -850,30 +861,30 @@ namespace http { s << "
\r\n"; } else - s << "SAM Sessions: no sessions currently running.
\r\n"; + s << "" << tr("SAM sessions") << ": " << tr("no sessions currently running") << ".
\r\n"; } void ShowSAMSession (std::stringstream& s, const std::string& id) { auto sam = i2p::client::context.GetSAMBridge (); if (!sam) { - ShowError(s, "SAM disabled"); + ShowError(s, tr("SAM disabled")); return; } auto session = sam->FindSession (id); if (!session) { - ShowError(s, "SAM session not found"); + ShowError(s, tr("SAM session not found")); return; } std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "SAM Session:
\r\n
\r\n"; + s << "" << tr("SAM Session") << ":
\r\n
\r\n"; auto& ident = session->GetLocalDestination ()->GetIdentHash(); s << "\r\n"; s << "
\r\n"; - s << "Streams:
\r\n
\r\n"; + s << "" << tr("Streams") << ":
\r\n
\r\n"; for (const auto& it: sam->ListSockets(id)) { s << "
"; @@ -882,7 +893,7 @@ namespace http { case i2p::client::eSAMSocketTypeSession : s << "session"; break; case i2p::client::eSAMSocketTypeStream : s << "stream"; break; case i2p::client::eSAMSocketTypeAcceptor : s << "acceptor"; break; - case i2p::client::eSAMSocketTypeForward : s << "forward"; break; + case i2p::client::eSAMSocketTypeForward : s << "forward"; break; default: s << "unknown"; break; } s << " [" << it->GetSocket ().remote_endpoint() << "]"; @@ -894,7 +905,7 @@ namespace http { void ShowI2PTunnels (std::stringstream& s) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "Client Tunnels:
\r\n
\r\n"; + s << "" << tr("Client Tunnels") << ":
\r\n
\r\n"; for (auto& it: i2p::client::context.GetClientTunnels ()) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); @@ -908,7 +919,7 @@ namespace http { { auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); s << "
"; - s << "HTTP Proxy" << " ⇐ "; + s << "HTTP " << tr("Proxy") << " ⇐ "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << "
\r\n"<< std::endl; } @@ -917,7 +928,7 @@ namespace http { { auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); s << "
"; - s << "SOCKS Proxy" << " ⇐ "; + s << "SOCKS " << tr("Proxy") << " ⇐ "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << "
\r\n"<< std::endl; } @@ -925,7 +936,7 @@ namespace http { auto& serverTunnels = i2p::client::context.GetServerTunnels (); if (!serverTunnels.empty ()) { - s << "
\r\nServer Tunnels:
\r\n
\r\n"; + s << "
\r\n" << tr("Server Tunnels") << ":
\r\n
\r\n"; for (auto& it: serverTunnels) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); @@ -941,7 +952,7 @@ namespace http { auto& clientForwards = i2p::client::context.GetClientForwards (); if (!clientForwards.empty ()) { - s << "
\r\nClient Forwards:
\r\n
\r\n"; + s << "
\r\n" << tr("Client Forwards") << ":
\r\n
\r\n"; for (auto& it: clientForwards) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); @@ -955,7 +966,7 @@ namespace http { auto& serverForwards = i2p::client::context.GetServerForwards (); if (!serverForwards.empty ()) { - s << "
\r\nServer Forwards:
\r\n
\r\n"; + s << "
\r\n" << tr("Server Forwards") << ":
\r\n
\r\n"; for (auto& it: serverForwards) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); @@ -1087,7 +1098,7 @@ namespace http { return; } } - // Html5 head start + // HTML head start ShowPageHead (s); if (req.uri.find("page=") != std::string::npos) { HandlePage (req, res, s); @@ -1161,7 +1172,7 @@ namespace http { ShowLeasesSets(s); else { res.code = 400; - ShowError(s, "Unknown page: " + page); + ShowError(s, tr("Unknown page") + ": " + page); return; } } @@ -1180,7 +1191,7 @@ namespace http { if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) { - ShowError(s, "Invalid token"); + ShowError(s, tr("Invalid token")); return; } @@ -1238,18 +1249,18 @@ namespace http { if (dest) { if(dest->DeleteStream (streamID)) - s << "SUCCESS: Stream closed
\r\n
\r\n"; + s << "" << tr("SUCCESS") << ": " << tr("Stream closed") << "
\r\n
\r\n"; else - s << "ERROR: Stream not found or already was closed
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " << tr("Stream not found or already was closed") << "
\r\n
\r\n"; } else - s << "ERROR: Destination not found
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " << tr("Destination not found") << "
\r\n
\r\n"; } else - s << "ERROR: StreamID can be null
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " << tr("StreamID can't be null") << "
\r\n
\r\n"; - s << "Return to destination page
\r\n"; - s << "

You will be redirected back in 5 seconds"; + s << "" << tr("Return to destination page") << "
\r\n"; + s << "

" << tr("You will be redirected back in 5 seconds") << ""; redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32; res.add_header("Refresh", redirect.c_str()); return; @@ -1260,9 +1271,9 @@ namespace http { if (limit > 0 && limit <= 65535) SetMaxNumTransitTunnels (limit); else { - s << "ERROR: Transit tunnels count must not exceed 65535\r\n
\r\n
\r\n"; - s << "Back to commands list\r\n
\r\n"; - s << "

You will be redirected back in 5 seconds"; + 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") << ""; res.add_header("Refresh", redirect.c_str()); return; } @@ -1295,37 +1306,37 @@ namespace http { auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2); sig[len] = 0; out << "#!sig=" << sig; - s << "SUCCESS:
\r\n

\r\n" + s << "" << tr("SUCCESS") << ":
\r\n\r\n" "\r\n
\r\n
\r\n" - "Register at reg.i2p:\r\n
\r\n" - "Description:\r\n\r\n" - "\r\n" + "" << tr("Register at reg.i2p") << ":\r\n
\r\n" + "" << tr("Description") << ":\r\n\r\n" + "\r\n" "
\r\n
\r\n"; delete[] signature; delete[] sig; } else - s << "ERROR: Domain can't end with .b32.i2p\r\n
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " << tr("Domain can't end with .b32.i2p") << "\r\n
\r\n
\r\n"; } else - s << "ERROR: Domain must end with .i2p\r\n
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " << tr("Domain must end with .i2p") << "\r\n
\r\n
\r\n"; } else - s << "ERROR: Such destination is not found\r\n
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " << tr("Such destination is not found") << "\r\n
\r\n
\r\n"; - s << "Return to destination page\r\n"; + s << "" << tr("Return to destination page") << "\r\n"; return; } else { res.code = 400; - ShowError(s, "Unknown command: " + cmd); + ShowError(s, tr("Unknown command") + ": " + cmd); return; } - s << "SUCCESS: Command accepted

\r\n"; - s << "Back to commands list
\r\n"; - s << "

You will be redirected in 5 seconds"; + s << "" << tr("SUCCESS") << ": " << tr("Command accepted") << "

\r\n"; + s << "" << tr("Back to commands list") << "
\r\n"; + s << "

" << tr("You will be redirected in 5 seconds") << ""; res.add_header("Refresh", redirect.c_str()); } diff --git a/debian/compat b/debian/compat index 9a037142..ec635144 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -10 \ No newline at end of file +9 diff --git a/debian/control b/debian/control index 843a153c..318463bc 100644 --- a/debian/control +++ b/debian/control @@ -3,30 +3,16 @@ Section: net Priority: optional Maintainer: r4sas Build-Depends: debhelper (>= 9), dpkg-dev (>= 1.17.2~), gcc (>= 4.7) | clang (>= 3.3), libboost-system-dev (>= 1.46), libboost-date-time-dev (>= 1.46), libboost-filesystem-dev (>= 1.46), libboost-program-options-dev (>= 1.46), libminiupnpc-dev, libssl-dev, zlib1g-dev -Standards-Version: 3.9.6 +Standards-Version: 3.9.8 Homepage: http://i2pd.website/ Vcs-Git: git://github.com/PurpleI2P/i2pd.git Vcs-Browser: https://github.com/PurpleI2P/i2pd Package: i2pd Architecture: any -Pre-Depends: adduser +Pre-Depends: ${misc:Pre-Depends}, adduser Depends: ${shlibs:Depends}, ${misc:Depends}, lsb-base, Description: Full-featured C++ implementation of I2P client. I2P (Invisible Internet Protocol) is a universal anonymous network layer. All communications over I2P are anonymous and end-to-end encrypted, participants don't reveal their real IP addresses. - . - This package contains the full-featured C++ implementation of I2P router. - -Package: i2pd-dbg -Architecture: any -Priority: extra -Section: debug -Depends: i2pd (= ${binary:Version}), ${misc:Depends} -Description: i2pd debugging symbols - I2P (Invisible Internet Protocol) is a universal anonymous network layer. All - communications over I2P are anonymous and end-to-end encrypted, participants - don't reveal their real IP addresses. - . - This package contains symbols required for debugging. diff --git a/debian/copyright b/debian/copyright index 9f18f53a..73df9da0 100644 --- a/debian/copyright +++ b/debian/copyright @@ -6,13 +6,6 @@ Files: * Copyright: 2013-2020 PurpleI2P License: BSD-3-clause -Files: qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistro.aidl - qt/i2pd_qt/android/src/org/kde/necessitas/ministro/IMinistroCallback.aidl - qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtActivity.java - qt/i2pd_qt/android/src/org/qtproject/qt5/android/bindings/QtApplication.java -Copyright: 2011-2013 BogDan Vatra -License: BSD-2-Clause - Files: debian/* Copyright: 2013-2015 Kill Your TV 2014-2016 hagen @@ -49,28 +42,6 @@ License: BSD-3-clause TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -License: BSD-2-Clause - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - . - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE HOLDERS OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - License: GPL-2+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/debian/docs b/debian/docs index dfa6f572..b43bf86b 100644 --- a/debian/docs +++ b/debian/docs @@ -1,5 +1 @@ README.md -contrib/i2pd.conf -contrib/subscriptions.txt -contrib/tunnels.conf -contrib/tunnels.d diff --git a/debian/i2pd.dirs b/debian/i2pd.dirs deleted file mode 100644 index 3b643352..00000000 --- a/debian/i2pd.dirs +++ /dev/null @@ -1,2 +0,0 @@ -etc/i2pd -var/lib/i2pd diff --git a/debian/i2pd.install b/debian/i2pd.install index d20b2c17..6eb6c8c2 100644 --- a/debian/i2pd.install +++ b/debian/i2pd.install @@ -1,5 +1,5 @@ i2pd usr/sbin/ -contrib/i2pd.conf etc/i2pd/ +contrib/i2pd.conf etc/i2pd/ contrib/tunnels.conf etc/i2pd/ contrib/subscriptions.txt etc/i2pd/ contrib/certificates/ usr/share/i2pd/ diff --git a/debian/postinst b/debian/postinst index 9c9e74ae..720753fd 100755 --- a/debian/postinst +++ b/debian/postinst @@ -12,7 +12,6 @@ case "$1" in # Create user and group as a system user. if getent passwd $I2PDUSER > /dev/null 2>&1; then groupadd -f $I2PDUSER || true - usermod -s "/bin/false" -e 1 $I2PDUSER > /dev/null || true else adduser --system --quiet --group --home $I2PDHOME $I2PDUSER fi @@ -23,7 +22,7 @@ case "$1" in chmod 640 $LOGFILE chown -f ${I2PDUSER}:adm $LOGFILE mkdir -p -m0750 $I2PDHOME - chown -f -R -P ${I2PDUSER}:${I2PDUSER} ${I2PDHOME} + chown -f -P ${I2PDUSER}:${I2PDUSER} ${I2PDHOME} ;; abort-upgrade|abort-remove|abort-deconfigure) echo "Aborting upgrade" diff --git a/debian/rules b/debian/rules index 77ecd7b7..24a44f55 100755 --- a/debian/rules +++ b/debian/rules @@ -1,22 +1,16 @@ #!/usr/bin/make -f -# -*- makefile -*- - -# Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 -DEB_BUILD_MAINT_OPTIONS=hardening=+bindnow -#DPKG_EXPORT_BUILDFLAGS = 1 -#include /usr/share/dpkg/buildflags.mk -#CXXFLAGS+=$(CPPFLAGS) -#PREFIX=/usr + +export DEB_BUILD_MAINT_OPTIONS = hardening=+all + + +include /usr/share/dpkg/architecture.mk + +export DEB_CXXFLAGS_MAINT_APPEND = -Wall -pedantic -O3 + +export DEB_LDFLAGS_MAINT_APPEND = + %: dh $@ --parallel -# dh_apparmor --profile-name=usr.sbin.i2pd -pi2pd - -override_dh_strip: - dh_strip --dbg-package=i2pd-dbg - -## uncomment this if you have "missing info" problem when building package -#override_dh_shlibdeps: -# dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info diff --git a/debian/watch b/debian/watch index 55cda021..64367c81 100644 --- a/debian/watch +++ b/debian/watch @@ -1,3 +1,3 @@ -version=3 -opts=filenamemangle=s/.+\/v?(\d\S*)\.tar\.gz/i2pd-$1\.tar\.gz/ \ - https://github.com/PurpleI2P/i2pd/tags .*/v?(\d\S*)\.tar\.gz +version=4 opts="filenamemangle=s%(?:.*?)?v?(\d[\d.]*)\.tar\.gz%i2pd-$1.tar.gz%" \ + https://github.com/PurpleI2P/i2pd/tags \ + (?:.*?/)?(\d[\d.]*)\.tar\.gz debian uupdate diff --git a/filelist.mk b/filelist.mk index 7d13fb2f..e2a5d40e 100644 --- a/filelist.mk +++ b/filelist.mk @@ -19,4 +19,6 @@ LIB_CLIENT_SRC = $(wildcard $(LIB_CLIENT_SRC_DIR)/*.cpp) #DAEMON_SRC = \ # HTTPServer.cpp I2PControl.cpp UPnP.cpp Daemon.cpp i2pd.cpp +LANG_SRC = $(wildcard $(LANG_SRC_DIR)/*.cpp) + DAEMON_SRC = $(wildcard $(DAEMON_SRC_DIR)/*.cpp) diff --git a/i18n/Afrikaans.cpp b/i18n/Afrikaans.cpp new file mode 100644 index 00000000..098c1105 --- /dev/null +++ b/i18n/Afrikaans.cpp @@ -0,0 +1,74 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "I18N.h" + +// Afrikaans localization file +// This is an example translation file without strings in it. + +namespace i2p +{ +namespace i18n +{ +namespace afrikaans // language +{ + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural (int n) { + return n != 1 ? 1 : 0; + } + + static std::map strings + { + {"Disabled", "Gedeaktiveer"}, + {"Enabled", "Geaktiveer"}, + {"failed", "Het misluk"}, + {"unknown", "onbekend"}, + {"Tunnels", "Tonnels"}, + {"Transit tunnels", "Deurgang tonnels"}, + {"I2P tunnels", "I2P tonnels"}, + {"SAM sessions", "SAM sessies"}, + {"OK", "LEKKER"}, + {"Testing", "Besig om te toets"}, + {"Firewalled", "Vuurmuur'd"}, + {"Unknown", "Onbekend"}, + {"Error", "Fout"}, + {"Offline", "Aflyn"}, + {"Uptime", "Optyd"}, + {"Network status", "Netwerk status"}, + {"Network status v6", "Netwerk status v6"}, + {"Family", "Familie"}, + {"Received", "Ontvang"}, + {"Sent", "Gestuur"}, + {"Hidden content. Press on text to see.", "Hidden content. Druk om te sien."}, + {"Router Ident", "Router Ident"}, + {"Router Family", "Router Familie"}, + {"", ""}, + }; + + static std::map> plurals + { + {"days", {"dag", "dae"}}, + {"hours", {"uur", "ure"}}, + {"minutes", {"minuut", "minute"}}, + {"seconds", {"seconde", "sekondes"}}, + {"", {"", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff --git a/i18n/English.cpp b/i18n/English.cpp new file mode 100644 index 00000000..6015f8e1 --- /dev/null +++ b/i18n/English.cpp @@ -0,0 +1,47 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "I18N.h" + +// English localization file +// This is an example translation file without strings in it. + +namespace i2p +{ +namespace i18n +{ +namespace english // language +{ + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural (int n) { + return n != 1 ? 1 : 0; + } + + static std::map strings + { + {"", ""}, + }; + + static std::map> plurals + { + {"", {"", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff --git a/i18n/I18N.h b/i18n/I18N.h new file mode 100644 index 00000000..272f65e8 --- /dev/null +++ b/i18n/I18N.h @@ -0,0 +1,50 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef __I18N_H__ +#define __I18N_H__ + +#include "RouterContext.h" + +namespace i2p +{ +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 + i2p::context.SetLanguage (i2p::i18n::english::GetLocale()); + } + + inline std::string translate (const std::string& arg) + { + return i2p::context.GetLanguage ()->GetString (arg); + } + + inline std::string translate (const std::string& arg, const std::string& arg2, const int& n) + { + return i2p::context.GetLanguage ()->GetPlural (arg, arg2, n); + } +} // i18n +} // i2p + +template +std::string tr (TArgs&&... args) +{ + return i2p::i18n::translate(std::forward(args)...); +} + +#endif // __I18N_H__ diff --git a/i18n/I18N_langs.h b/i18n/I18N_langs.h new file mode 100644 index 00000000..181a4793 --- /dev/null +++ b/i18n/I18N_langs.h @@ -0,0 +1,68 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#ifndef __I18N_LANGS_H__ +#define __I18N_LANGS_H__ + +namespace i2p +{ +namespace i18n +{ + class Locale + { + public: + Locale ( + const std::map& strings, + const std::map>& plurals, + std::function formula + ): m_Strings (strings), m_Plurals (plurals), m_Formula (formula) { }; + + std::string GetString (const std::string& arg) const + { + const auto it = m_Strings.find(arg); + if (it == m_Strings.end()) + { + return arg; + } + else + { + return it->second; + } + } + + std::string GetPlural (const std::string& arg, const std::string& arg2, const int& n) const + { + const auto it = m_Plurals.find(arg2); + if (it == m_Plurals.end()) // not found, fallback to english + { + return n == 1 ? arg : arg2; + } + else + { + int form = m_Formula(n); + return it->second[form]; + } + } + + private: + const std::map m_Strings; + const std::map> m_Plurals; + std::function m_Formula; + }; + + // Add localization here with language name as namespace + namespace afrikaans { std::shared_ptr GetLocale (); } + namespace english { std::shared_ptr GetLocale (); } + namespace russian { std::shared_ptr GetLocale (); } + namespace turkmen { std::shared_ptr GetLocale (); } + namespace ukrainian { std::shared_ptr GetLocale (); } + +} // i18n +} // i2p + +#endif // __I18N_LANGS_H__ diff --git a/i18n/Russian.cpp b/i18n/Russian.cpp new file mode 100644 index 00000000..7df82d54 --- /dev/null +++ b/i18n/Russian.cpp @@ -0,0 +1,243 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "I18N.h" + +// Russian localization file + +namespace i2p +{ +namespace i18n +{ +namespace russian // language +{ + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural (int n) { + return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; + } + + static std::map strings + { + // HTTP Proxy + {"Proxy error", "Ошибка прокси"}, + {"Proxy info", "Информация прокси"}, + {"Proxy error: Host not found", "Ошибка прокси: Адрес не найден"}, + {"Remote host not found in router's addressbook", "Запрошенный адрес не найден в адресной книге роутера"}, + {"You may try to find this host on jump services below", "Вы можете попробовать найти адрес на джамп сервисах ниже"}, + {"Invalid request", "Некорректный запрос"}, + {"Proxy unable to parse your request", "Прокси не может разобрать ваш запрос"}, + {"addresshelper is not supported", "addresshelper не поддерживается"}, + {"Host", "Адрес"}, + {"added to router's addressbook from helper", "добавлен в адресную книгу роутера через хелпер"}, + {"already in router's addressbook", "уже в адресной книге роутера"}, + {"Click", "Нажмите"}, + {"here", "здесь"}, + {"to proceed", "чтобы продолжить"}, + {"to update record", "чтобы обновить запись"}, + {"Addresshelper found", "Найден addresshelper"}, + {"invalid request uri", "некорректный URI запроса"}, + {"Can't detect destination host from request", "Не удалось определить адрес назначения из запроса"}, + {"Outproxy failure", "Ошибка внешнего прокси"}, + {"bad outproxy settings", "некорректные настройки внешнего прокси"}, + {"not inside I2P network, but outproxy is not enabled", "не в I2P сети, но внешний прокси не включен"}, + {"unknown outproxy url", "неизвестный URL внешнего прокси"}, + {"cannot resolve upstream proxy", "не удается определить вышестоящий прокси"}, + {"hostname too long", "имя хоста слишком длинное"}, + {"cannot connect to upstream socks proxy", "не удается подключиться к вышестоящему SOCKS прокси"}, + {"Cannot negotiate with socks proxy", "Не удается договориться с вышестоящим SOCKS прокси"}, + {"CONNECT error", "Ошибка CONNECT запроса"}, + {"Failed to Connect", "Не удалось подключиться"}, + {"socks proxy error", "ошибка SOCKS прокси"}, + {"failed to send request to upstream", "не удалось отправить запрос вышестоящему прокси"}, + {"No Reply From socks proxy", "Нет ответа от SOCKS прокси сервера"}, + {"cannot connect", "не удалось подключиться"}, + {"http out proxy not implemented", "поддержка внешнего HTTP прокси сервера не реализована"}, + {"cannot connect to upstream http proxy", "не удалось подключиться к вышестоящему HTTP прокси серверу"}, + {"Host is down", "Адрес недоступен"}, + {"Can't create connection to requested host, it may be down. Please try again later.", + "Не удалось установить соединение к запрошенному адресу, возможно он не в сети. Попробуйте повторить запрос позже."}, + + // Webconsole // + // cssStyles + {"Disabled", "Выключено"}, + {"Enabled", "Включено"}, + // ShowTraffic + {"KiB", "КиБ"}, + {"MiB", "МиБ"}, + {"GiB", "ГиБ"}, + // ShowTunnelDetails + {"building", "строится"}, + {"failed", "неудачный"}, + {"expiring", "истекает"}, + {"established", "работает"}, + {"exploratory", "исследовательский"}, + {"unknown", "неизвестно"}, + {"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", "Такая точка назначения не найдена"}, + {"", ""}, + }; + + static std::map> plurals + { + // ShowUptime + {"days", {"день", "дня", "дней"}}, + {"hours", {"час", "часа", "часов"}}, + {"minutes", {"минуту", "минуты", "минут"}}, + {"seconds", {"секунду", "секунды", "секунд"}}, + {"", {"", "", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff --git a/i18n/Turkmen.cpp b/i18n/Turkmen.cpp new file mode 100644 index 00000000..0edc9a6e --- /dev/null +++ b/i18n/Turkmen.cpp @@ -0,0 +1,242 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "I18N.h" + +// Turkmen localization file + +namespace i2p +{ +namespace i18n +{ +namespace turkmen // language +{ + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural (int n) { + return n != 1 ? 1 : 0; + } + + static std::map strings + { + // HTTP Proxy + {"Proxy error", "Proksi ýalňyşlygy"}, + {"Proxy info", "Proksi maglumat"}, + {"Proxy error: Host not found", "Proksi ýalňyşlygy: Host tapylmady"}, + {"Remote host not found in router's addressbook", "Uzakdaky öý eýesi marşruteriň salgy kitabynda tapylmady"}, + {"You may try to find this host on jump services below", "Aşakdaky böküş hyzmatlarynda bu öý eýesini tapmaga synanyşyp bilersiňiz"}, + {"Invalid request", "Nädogry haýyş"}, + {"Proxy unable to parse your request", "Proksi haýyşyňyzy derňäp bilmeýär"}, + {"addresshelper is not supported", "Salgylandyryjy goldanok"}, + {"Host", "Adres"}, + {"added to router's addressbook from helper", "marşruteriň adresini kömekçiden goşdy"}, + {"already in router's addressbook", "marşruteriň adres kitaby"}, + {"Click", "Basyň"}, + {"here", "bu ýerde"}, + {"to proceed", "dowam etmek"}, + {"to update record", "recordazgyny täzelemek üçin"}, + {"Addresshelper found", "Forgelper tapyldy"}, + {"invalid request uri", "nädogry haýyş URI"}, + {"Can't detect destination host from request", "Haýyşdan barmaly ýerini tapyp bilemok"}, + {"Outproxy failure", "Daşarky proksi ýalňyşlyk"}, + {"bad outproxy settings", "daşarky daşarky proksi sazlamalary nädogry"}, + {"not inside I2P network, but outproxy is not enabled", "I2P torunda däl, ýöne daşarky proksi goşulmaýar"}, + {"unknown outproxy url", "näbelli daşarky proksi URL"}, + {"cannot resolve upstream proxy", "has ýokary proksi kesgitläp bilmeýär"}, + {"hostname too long", "hoster eýesi ady gaty uzyn"}, + {"cannot connect to upstream socks proxy", "ýokary jorap SOCKS proksi bilen birigip bolmaýar"}, + {"Cannot negotiate with socks proxy", "Iň ýokary jorap SOCKS proksi bilen ylalaşyp bilmeýärler"}, + {"CONNECT error", "Bagyr haýyşy säwligi"}, + {"Failed to Connect", "Birikdirip bilmedi"}, + {"socks proxy error", "socks proksi ýalňyşlygy"}, + {"failed to send request to upstream", "öý eýesi proksi üçin haýyş iberip bilmedi"}, + {"No Reply From socks proxy", "Jorap proksi serwerinden hiç hili jogap ýok"}, + {"cannot connect", "birikdirip bilmedi"}, + {"http out proxy not implemented", "daşarky HTTP proksi serwerini goldamak amala aşyrylmaýar"}, + {"cannot connect to upstream http proxy", "ýokary akym HTTP proksi serwerine birigip bilmedi"}, + {"Host is down", "Salgy elýeterli däl"}, + {"Can't create connection to requested host, it may be down. Please try again later.", + "Talap edilýän salgyda birikmäni gurup bilmedim, onlaýn bolup bilmez. Soňra haýyşy soň gaýtalamaga synanyşyň."}, + + // Webconsole // + // cssStyles + {"Disabled", "Öçürildi"}, + {"Enabled", "Goşuldy"}, + // ShowTraffic + {"KiB", "KiB"}, + {"MiB", "MiB"}, + {"GiB", "GiB"}, + // ShowTunnelDetails + {"building", "bina"}, + {"failed", "şowsuz"}, + {"expiring", "möhleti gutarýar"}, + {"established", "işleýär"}, + {"exploratory", "gözleg"}, + {"unknown", "näbelli"}, + {"i2pd webconsole", "Web konsoly i2pd"}, + // ShowPageHead + {"Main page", "Esasy sahypa"}, + {"Router commands", "Marşrutizator buýruklary"}, + {"Local destinations", "Ýerli ýerler"}, + {"LeaseSets", "Lizset"}, + {"Tunnels", "Tuneller"}, + {"Transit tunnels", "Tranzit tunels"}, + {"Transports", "Daşamak"}, + {"I2P tunnels", "I2P tuneller"}, + {"SAM sessions", "SAM Sessiýasy"}, + // Network Status + {"OK", "OK"}, + {"Testing", "Synag etmek"}, + {"Firewalled", "Daşynda petiklendi"}, + {"Unknown", "Näbelli"}, + {"Proxy", "Proksi"}, + {"Mesh", "MESH-tor"}, + {"Error", "Ýalňyşlyk"}, + {"Clock skew", "Takyk wagt däl"}, + {"Offline", "Awtonom"}, + {"Symmetric NAT", "Simmetriklik NAT"}, + // Status + {"Uptime", "Onlaýn onlaýn sözlügi"}, + {"Network status", "Tor ýagdaýy"}, + {"Network status v6", "Tor ýagdaýy v6"}, + {"Stopping in", "Soň duruň"}, + {"Family", "Maşgala"}, + {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"}, + {"Received", "Alnan"}, + {"Sent", "Ýerleşdirildi"}, + {"Transit", "Tranzit"}, + {"KiB/s", "KiB/s"}, + {"Data path", "Maglumat ýoly"}, + {"Hidden content. Press on text to see.", "Gizlin mazmun. Görkezmek üçin tekste basyň."}, + {"Router Ident", "Marşrutly kesgitleýji"}, + {"Router Family", "Marşrutler maşgalasy"}, + {"Router Caps", "Baýdaklar marşruteri"}, + {"Version", "Wersiýasy"}, + {"Our external address", "Daşarky salgymyz"}, + {"supported", "goldanýar"}, + {"Routers", "Marşrutizatorlar"}, + {"Floodfills", "Fludfillar"}, + {"Client Tunnels", "Müşderi tunelleri"}, + {"Transit Tunnels", "Tranzit Tunelleri"}, + {"Services", "Hyzmatlar"}, + // ShowLocalDestinations + {"Local Destinations", "Ýerli ýerler"}, + // ShowLeaseSetDestination + {"Encrypted B33 address", "Şifrlenen B33 salgylar"}, + {"Address registration line", "Hasaba alyş salgysy"}, + {"Domain", "Domen"}, + {"Generate", "Öndürmek"}, + {"Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", + "Bellik: Alnan setir diňe ikinji derejeli domenleri bellige almak üçin ulanylyp bilner. Subýutmalary hasaba almak üçin i2pd ulanyň-tools."}, + {"Address", "Salgysy"}, + {"Type", "Görnüş"}, + {"EncType", "Şifrlemek görnüşi"}, + {"Inbound tunnels", "Gelýän tuneller"}, + {"Outbound tunnels", "Çykýan tuneller"}, + {"ms", "ms"}, // milliseconds + {"Tags", "Bellikler"}, + {"Incoming", "Gelýän"}, + {"Outgoing", "Çykýan"}, + {"Destination", "Maksat"}, + {"Amount", "Sany"}, + {"Incoming Tags", "Gelýän bellikler"}, + {"Tags sessions", "Sapaklar bellikler"}, + {"Status", "Ýagdaýy"}, + // ShowLocalDestination + {"Local Destination", "Ýerli maksat"}, + {"Streams", "Strimlary"}, + {"Close stream", "Yap strim"}, + // ShowI2CPLocalDestination + {"I2CP session not found", "I2CP Sessiýa tapylmady"}, + {"I2CP is not enabled", "I2CP goşulmaýar"}, + // ShowLeasesSets + {"Invalid", "Nädogry"}, + {"Store type", "Ammar görnüşi"}, + {"Expires", "Möhleti gutarýar"}, + {"Non Expired Leases", "Möhleti gutarmady Lizsetlary"}, + {"Gateway", "Derweze"}, + {"TunnelID", "Tuneliň ID"}, + {"EndDate", "Gutarýar"}, + {"not floodfill", "fludfil däl"}, + // ShowTunnels + {"Queue size", "Nobatyň ululygy"}, + // ShowCommands + {"Run peer test", "Synag başlaň"}, + {"Decline transit tunnels", "Tranzit tunellerini ret ediň"}, + {"Accept transit tunnels", "Tranzit tunellerini alyň"}, + {"Cancel graceful shutdown", "Tekiz durmagy ýatyryň"}, + {"Start graceful shutdown", "Tekiz durmak"}, + {"Force shutdown", "Mejbury duralga"}, + {"Note: any action done here are not persistent and not changes your config files.", + "Bellik: Bu ýerde öndürilen islendik çäre hemişelik däl we konfigurasiýa faýllaryňyzy üýtgetmeýär."}, + {"Logging level", "Giriş derejesi"}, + {"Transit tunnels limit", "Tranzit tunelleriniň çägi"}, + {"Change", "Üýtgetmek"}, + // ShowTransitTunnels + {"no transit tunnels currently built", "gurlan tranzit tunelleri ýok"}, + // ShowSAMSessions/ShowSAMSession + {"SAM disabled", "SAM öçürilen"}, + {"SAM session not found", "SAM Sessiýa tapylmady"}, + {"no sessions currently running", "başlamagyň sessiýalary ýok"}, + {"SAM Session", "SAM Sessiýa"}, + // ShowI2PTunnels + {"Server Tunnels", "Serwer tunelleri"}, + {"Client Forwards", "Müşderi gönükdirýär"}, + {"Server Forwards", "Serweriň täzeden düzlüleri"}, + // HandlePage + {"Unknown page", "Näbelli sahypa"}, + // HandleCommand, ShowError + {"Invalid token", "Nädogry token"}, + {"SUCCESS", "Üstünlikli"}, + {"ERROR", "Ýalňyşlyk"}, + {"Unknown command", "Näbelli topar"}, + {"Command accepted", "Topar kabul edilýär"}, + {"Back to commands list", "Topar sanawyna dolan"}, + {"You will be redirected in 5 seconds", "5 sekuntdan soň täzeden ugrukdyrylarsyňyz"}, + // HTTP_COMMAND_KILLSTREAM + {"Stream closed", "Strim ýapyk"}, + {"Stream not found or already was closed", "Strim tapylmady ýa-da eýýäm ýapyldy"}, + {"Destination not found", "Niýetlenen ýeri tapylmady"}, + {"StreamID can't be null", "StreamID boş bolup bilmez"}, + {"Return to destination page", "Barmaly nokadynyň nokadyna gaýdyp geliň"}, + {"You will be redirected back in 5 seconds", "5 sekuntda yzyna iberiler"}, + // HTTP_COMMAND_LIMITTRANSIT + {"Transit tunnels count must not exceed 65535", "Tranzit tagtalaryň sany 65535-den geçmeli däldir"}, + // HTTP_COMMAND_GET_REG_STRING + {"Register at reg.i2p", "Reg.i2P-de hasaba duruň"}, + {"Description", "Beýany"}, + {"A bit information about service on domain", "Domendäki hyzmat barada käbir maglumatlar"}, + {"Submit", "Iber"}, + {"Domain can't end with .b32.i2p", "Domain .b32.i2p bilen gutaryp bilmez"}, + {"Domain must end with .i2p", "Domeni .i2p bilen gutarmaly"}, + {"Such destination is not found", "Bu barmaly ýer tapylmady"}, + {"", ""}, + }; + + static std::map> plurals + { + // ShowUptime + {"days", {"gün", "gün"}}, + {"hours", {"sagat", "sagat"}}, + {"minutes", {"minut", "minut"}}, + {"seconds", {"sekunt", "sekunt"}}, + {"", {"", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff --git a/i18n/Ukrainian.cpp b/i18n/Ukrainian.cpp new file mode 100644 index 00000000..11878cde --- /dev/null +++ b/i18n/Ukrainian.cpp @@ -0,0 +1,242 @@ +/* +* Copyright (c) 2021, The PurpleI2P Project +* +* This file is part of Purple i2pd project and licensed under BSD3 +* +* See full license text in LICENSE file at top of project tree +*/ + +#include +#include +#include +#include +#include "I18N.h" + +// Ukrainian localization file + +namespace i2p +{ +namespace i18n +{ +namespace ukrainian // language +{ + // See for language plural forms here: + // https://localization-guide.readthedocs.io/en/latest/l10n/pluralforms.html + static int plural (int n) { + return n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2; + } + + static std::map strings + { + // HTTP Proxy + {"Proxy error", "Помилка проксі"}, + {"Proxy info", "Інформація проксі"}, + {"Proxy error: Host not found", "Помилка проксі: Адреса не знайдена"}, + {"Remote host not found in router's addressbook", "Віддалена адреса не знайдена в адресній книзі роутера"}, + {"You may try to find this host on jump services below", "Ви можете спробувати знайти дану адресу на джамп сервісах нижче"}, + {"Invalid request", "Некоректний запит"}, + {"Proxy unable to parse your request", "Проксі не може розібрати ваш запит"}, + {"addresshelper is not supported", "addresshelper не підтримується"}, + {"Host", "Адреса"}, + {"added to router's addressbook from helper", "доданий в адресну книгу роутера через хелпер"}, + {"already in router's addressbook", "вже в адресній книзі роутера"}, + {"Click", "Натисніть"}, + {"here", "тут"}, + {"to proceed", "щоб продовжити"}, + {"to update record", "щоб оновити запис"}, + {"Addresshelper found", "Знайдено addresshelper"}, + {"invalid request uri", "некоректний URI запиту"}, + {"Can't detect destination host from request", "Не вдалось визначити адресу призначення з запиту"}, + {"Outproxy failure", "Помилка зовнішнього проксі"}, + {"bad outproxy settings", "некоректні налаштування зовнішнього проксі"}, + {"not inside I2P network, but outproxy is not enabled", "не в I2P мережі, але зовнішній проксі не включений"}, + {"unknown outproxy url", "невідомий URL зовнішнього проксі"}, + {"cannot resolve upstream proxy", "не вдається визначити висхідний проксі"}, + {"hostname too long", "ім'я вузла надто довге"}, + {"cannot connect to upstream socks proxy", "не вдається підключитися до висхідного SOCKS проксі"}, + {"Cannot negotiate with socks proxy", "Не вдається домовитися з висхідним SOCKS проксі"}, + {"CONNECT error", "Помилка CONNECT запиту"}, + {"Failed to Connect", "Не вдалося підключитися"}, + {"socks proxy error", "помилка SOCKS проксі"}, + {"failed to send request to upstream", "не вдалося відправити запит висхідному проксі"}, + {"No Reply From socks proxy", "Немає відповіді від SOCKS проксі сервера"}, + {"cannot connect", "не вдалося підключитися"}, + {"http out proxy not implemented", "підтримка зовнішнього HTTP проксі сервера не реалізована"}, + {"cannot connect to upstream http proxy", "не вдалося підключитися до висхідного HTTP проксі сервера"}, + {"Host is down", "Вузол недоступний"}, + {"Can't create connection to requested host, it may be down. Please try again later.", + "Не вдалося встановити з'єднання до запитаного вузла, можливо він не в мережі. Спробуйте повторити запит пізніше."}, + + // Webconsole // + // cssStyles + {"Disabled", "Вимкнуто"}, + {"Enabled", "Увімкнуто"}, + // ShowTraffic + {"KiB", "КіБ"}, + {"MiB", "МіБ"}, + {"GiB", "ГіБ"}, + // ShowTunnelDetails + {"building", "будується"}, + {"failed", "невдалий"}, + {"expiring", "завершується"}, + {"established", "працює"}, + {"exploratory", "дослідницький"}, + {"unknown", "невідомо"}, + {"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", "Така точка призначення не знайдена"}, + {"", ""}, + }; + + static std::map> plurals + { + // ShowUptime + {"days", {"день", "дня", "днів"}}, + {"hours", {"годину", "години", "годин"}}, + {"minutes", {"хвилину", "хвилини", "хвилин"}}, + {"seconds", {"секунду", "секунди", "секунд"}}, + {"", {"", "", ""}}, + }; + + std::shared_ptr GetLocale() + { + return std::make_shared(strings, plurals, [] (int n)->int { return plural(n); }); + } + +} // language +} // i18n +} // i2p diff --git a/libi2pd/Config.cpp b/libi2pd/Config.cpp index b316ba83..a80778a1 100644 --- a/libi2pd/Config.cpp +++ b/libi2pd/Config.cpp @@ -62,11 +62,11 @@ namespace config { ("floodfill", bool_switch()->default_value(false), "Router will be floodfill (default: disabled)") ("bandwidth", value()->default_value(""), "Bandwidth limit: integer in KBps or letters: L (32), O (256), P (2048), X (>9000)") ("share", value()->default_value(100), "Limit of transit traffic from max bandwidth in percents. (default: 100)") - ("ntcp", bool_switch()->default_value(false), "Deprecated option. Always false") + ("ntcp", bool_switch()->default_value(false), "Ignored. Always false") ("ssu", bool_switch()->default_value(true), "Enable SSU transport (default: enabled)") - ("ntcpproxy", value()->default_value(""), "Deprecated option") + ("ntcpproxy", value()->default_value(""), "Ignored") #ifdef _WIN32 - ("svcctl", value()->default_value(""), "Deprecated option") + ("svcctl", value()->default_value(""), "Ignored") ("insomnia", bool_switch()->default_value(false), "Prevent system from sleeping (default: disabled)") ("close", value()->default_value("ask"), "Action on close: minimize, exit, ask") #endif @@ -77,9 +77,9 @@ namespace config { ("limits.coresize", value()->default_value(0), "Maximum size of corefile in Kb (0 - use system limit)") ("limits.openfiles", value()->default_value(0), "Maximum number of open files (0 - use system default)") ("limits.transittunnels", value()->default_value(2500), "Maximum active transit sessions (default:2500)") - ("limits.ntcpsoft", value()->default_value(0), "Deprecated option") - ("limits.ntcphard", value()->default_value(0), "Deprecated option") - ("limits.ntcpthreads", value()->default_value(1), "Deprecated option") + ("limits.ntcpsoft", value()->default_value(0), "Threshold to start probabilistic backoff with ntcp sessions (default: use system limit)") + ("limits.ntcphard", value()->default_value(0), "Maximum number of ntcp sessions (default: use system limit)") + ("limits.ntcpthreads", value()->default_value(1), "Maximum number of threads used by NTCP DH worker (default: 1)") ; options_description httpserver("HTTP Server options"); @@ -93,6 +93,7 @@ namespace config { ("http.strictheaders", value()->default_value(true), "Enable strict host checking on WebUI") ("http.hostname", value()->default_value("localhost"), "Expected hostname for WebUI") ("http.webroot", value()->default_value("/"), "WebUI root path (default: / )") + ("http.lang", value()->default_value("english"), "WebUI language (default: english )") ; options_description httpproxy("HTTP Proxy options"); @@ -113,6 +114,7 @@ namespace config { ("httpproxy.addresshelper", value()->default_value(true), "Enable or disable addresshelper") ("httpproxy.i2cp.leaseSetType", value()->default_value("3"), "Local destination's LeaseSet type") ("httpproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), "Local destination's LeaseSet encryption type") + ("httpproxy.i2cp.leaseSetPrivKey", value()->default_value(""), "LeaseSet private key") ; options_description socksproxy("SOCKS Proxy options"); @@ -134,6 +136,7 @@ namespace config { ("socksproxy.outproxyport", value()->default_value(9050), "Upstream outproxy port for SOCKS Proxy") ("socksproxy.i2cp.leaseSetType", value()->default_value("3"), "Local destination's LeaseSet type") ("socksproxy.i2cp.leaseSetEncType", value()->default_value("0,4"), "Local destination's LeaseSet encryption type") + ("socksproxy.i2cp.leaseSetPrivKey", value()->default_value(""), "LeaseSet private key") ; options_description sam("SAM bridge options"); @@ -218,7 +221,7 @@ namespace config { ("addressbook.defaulturl", value()->default_value( "http://shx5vqsw7usdaunyzr2qmes2fq37oumybpudrd4jjj4e4vk4uusa.b32.i2p/hosts.txt" ), "AddressBook subscription URL for initial setup") - ("addressbook.subscriptions", value()->default_value(""), "AddressBook subscriptions URLs, separated by comma") + ("addressbook.subscriptions", value()->default_value("http://reg.i2p/hosts.txt"), "AddressBook subscriptions URLs, separated by comma") ("addressbook.hostsfile", value()->default_value(""), "File to dump addresses in hosts.txt format"); options_description trust("Trust options"); @@ -281,7 +284,7 @@ namespace config { options_description meshnets("Meshnet transports options"); meshnets.add_options() - ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (default: false)") + ("meshnets.yggdrasil", bool_switch()->default_value(false), "Support transports through the Yggdrasil (deafult: false)") ("meshnets.yggaddress", value()->default_value(""), "Yggdrasil address to publish") ; diff --git a/libi2pd/Destination.cpp b/libi2pd/Destination.cpp index 9962599b..7bcfe111 100644 --- a/libi2pd/Destination.cpp +++ b/libi2pd/Destination.cpp @@ -82,6 +82,14 @@ namespace client if (it != params->end ()) m_Nickname = it->second; // otherwise we set default nickname in Start when we know local address } + it = params->find (I2CP_PARAM_DONT_PUBLISH_LEASESET); + if (it != params->end ()) + { + // oveeride isPublic + bool dontpublish = false; + i2p::config::GetOption (it->second, dontpublish); + m_IsPublic = !dontpublish; + } it = params->find (I2CP_PARAM_LEASESET_TYPE); if (it != params->end ()) m_LeaseSetType = std::stoi(it->second); @@ -509,7 +517,7 @@ namespace client // schedule verification m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, - shared_from_this (), std::placeholders::_1)); + shared_from_this (), std::placeholders::_1)); } else i2p::garlic::GarlicDestination::HandleDeliveryStatusMessage (msgID); @@ -592,7 +600,8 @@ namespace client // assume it successive and try to verify m_PublishVerificationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_VERIFICATION_TIMEOUT)); m_PublishVerificationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishVerificationTimer, - shared_from_this (), std::placeholders::_1)); + shared_from_this (), std::placeholders::_1)); + } } } diff --git a/libi2pd/Destination.h b/libi2pd/Destination.h index 6e149cf5..6695796d 100644 --- a/libi2pd/Destination.h +++ b/libi2pd/Destination.h @@ -61,14 +61,15 @@ namespace client const char I2CP_PARAM_RATCHET_OUTBOUND_TAGS[] = "crypto.ratchet.outboundTags"; // not used yet const char I2CP_PARAM_INBOUND_NICKNAME[] = "inbound.nickname"; const char I2CP_PARAM_OUTBOUND_NICKNAME[] = "outbound.nickname"; + const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet"; const char I2CP_PARAM_LEASESET_TYPE[] = "i2cp.leaseSetType"; - const int DEFAULT_LEASESET_TYPE = 1; + const int DEFAULT_LEASESET_TYPE = 3; const char I2CP_PARAM_LEASESET_ENCRYPTION_TYPE[] = "i2cp.leaseSetEncType"; const char I2CP_PARAM_LEASESET_PRIV_KEY[] = "i2cp.leaseSetPrivKey"; // PSK decryption key, base64 const char I2CP_PARAM_LEASESET_AUTH_TYPE[] = "i2cp.leaseSetAuthType"; const char I2CP_PARAM_LEASESET_CLIENT_DH[] = "i2cp.leaseSetClient.dh"; // group of i2cp.leaseSetClient.dh.nnn - const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn - + const char I2CP_PARAM_LEASESET_CLIENT_PSK[] = "i2cp.leaseSetClient.psk"; // group of i2cp.leaseSetClient.psk.nnn + // latency const char I2CP_PARAM_MIN_TUNNEL_LATENCY[] = "latency.min"; const int DEFAULT_MIN_TUNNEL_LATENCY = 0; diff --git a/libi2pd/ECIESX25519AEADRatchetSession.cpp b/libi2pd/ECIESX25519AEADRatchetSession.cpp index f9d5672f..55e962d8 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.cpp +++ b/libi2pd/ECIESX25519AEADRatchetSession.cpp @@ -117,14 +117,14 @@ namespace garlic return session->HandleNextMessage (buf, len, shared_from_this (), index); } - DatabaseLookupTagSet::DatabaseLookupTagSet (GarlicDestination * destination, const uint8_t * key): + SymmetricKeyTagSet::SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key): ReceiveRatchetTagSet (nullptr), m_Destination (destination) { memcpy (m_Key, key, 32); Expire (); } - bool DatabaseLookupTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) + bool SymmetricKeyTagSet::HandleNextMessage (uint8_t * buf, size_t len, int index) { if (len < 24) return false; uint8_t nonce[12]; @@ -133,18 +133,18 @@ namespace garlic len -= 16; // poly1305 if (!i2p::crypto::AEADChaCha20Poly1305 (buf + offset, len - offset, buf, 8, m_Key, nonce, buf + offset, len - offset, false)) // decrypt { - LogPrint (eLogWarning, "Garlic: Lookup reply AEAD decryption failed"); + LogPrint (eLogWarning, "Garlic: Symmetric key tagset AEAD decryption failed"); return false; } // we assume 1 I2NP block with delivery type local if (offset + 3 > len) { - LogPrint (eLogWarning, "Garlic: Lookup reply is too short ", len); + LogPrint (eLogWarning, "Garlic: Symmetric key tagset is too short ", len); return false; } if (buf[offset] != eECIESx25519BlkGalicClove) { - LogPrint (eLogWarning, "Garlic: Lookup reply unexpected block ", (int)buf[offset]); + LogPrint (eLogWarning, "Garlic: Symmetric key tagset unexpected block ", (int)buf[offset]); return false; } offset++; @@ -152,7 +152,7 @@ namespace garlic offset += 2; if (offset + size > len) { - LogPrint (eLogWarning, "Garlic: Lookup reply block is too long ", size); + LogPrint (eLogWarning, "Garlic: Symmetric key tagset block is too long ", size); return false; } if (m_Destination) @@ -1109,21 +1109,22 @@ namespace garlic bool RouterIncomingRatchetSession::HandleNextMessage (const uint8_t * buf, size_t len) { if (!GetOwner ()) return false; - i2p::crypto::NoiseSymmetricState state (GetNoiseState ()); + m_CurrentNoiseState = GetNoiseState (); // we are Bob - state.MixHash (buf, 32); + m_CurrentNoiseState.MixHash (buf, 32); uint8_t sharedSecret[32]; if (!GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)) // x25519(bsk, aepk) { LogPrint (eLogWarning, "Garlic: Incorrect N ephemeral public key"); return false; } - state.MixKey (sharedSecret); + m_CurrentNoiseState.MixKey (sharedSecret); buf += 32; len -= 32; uint8_t nonce[12]; CreateNonce (0, nonce); std::vector payload (len - 16); - if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, state.m_H, 32, state.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt + if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_CurrentNoiseState.m_H, 32, + m_CurrentNoiseState.m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt { LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed"); return false; diff --git a/libi2pd/ECIESX25519AEADRatchetSession.h b/libi2pd/ECIESX25519AEADRatchetSession.h index bdbb7111..cd7b9b40 100644 --- a/libi2pd/ECIESX25519AEADRatchetSession.h +++ b/libi2pd/ECIESX25519AEADRatchetSession.h @@ -104,11 +104,11 @@ namespace garlic uint64_t m_ExpirationTimestamp = 0; }; - class DatabaseLookupTagSet: public ReceiveRatchetTagSet + class SymmetricKeyTagSet: public ReceiveRatchetTagSet { public: - DatabaseLookupTagSet (GarlicDestination * destination, const uint8_t * key); + SymmetricKeyTagSet (GarlicDestination * destination, const uint8_t * key); bool IsIndexExpired (int index) const { return false; }; bool HandleNextMessage (uint8_t * buf, size_t len, int index); @@ -249,6 +249,11 @@ namespace garlic RouterIncomingRatchetSession (const i2p::crypto::NoiseSymmetricState& initState); bool HandleNextMessage (const uint8_t * buf, size_t len); + i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; + + private: + + i2p::crypto::NoiseSymmetricState m_CurrentNoiseState; }; std::shared_ptr WrapECIESX25519AEADRatchetMessage (std::shared_ptr msg, const uint8_t * key, uint64_t tag); diff --git a/libi2pd/FS.cpp b/libi2pd/FS.cpp index bd1a7ad2..6ac302b0 100644 --- a/libi2pd/FS.cpp +++ b/libi2pd/FS.cpp @@ -12,6 +12,7 @@ #ifdef _WIN32 #include #include +#include #endif #include "Base.h" @@ -41,16 +42,28 @@ namespace fs { return dataDir; } + const std::string GetUTF8DataDir () { +#ifdef _WIN32 + boost::filesystem::wpath path (dataDir); + auto loc = boost::filesystem::path::imbue(std::locale( std::locale(), new std::codecvt_utf8_utf16() ) ); // convert path to UTF-8 + auto dataDirUTF8 = path.string(); + boost::filesystem::path::imbue(loc); // Return locale settings back + return dataDirUTF8; +#else + return dataDir; // linux, osx, android uses UTF-8 by default +#endif + } + void DetectDataDir(const std::string & cmdline_param, bool isService) { if (cmdline_param != "") { dataDir = cmdline_param; return; } #ifdef _WIN32 - char localAppData[MAX_PATH]; + wchar_t localAppData[MAX_PATH]; // check executable directory first - if(!GetModuleFileName(NULL, localAppData, MAX_PATH)) + if(!GetModuleFileNameW(NULL, localAppData, MAX_PATH)) { #ifdef WIN32_APP MessageBox(NULL, TEXT("Unable to get application path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); @@ -61,14 +74,15 @@ namespace fs { } else { - auto execPath = boost::filesystem::path(localAppData).parent_path(); + auto execPath = boost::filesystem::wpath(localAppData).parent_path(); // if config file exists in .exe's folder use it if(boost::filesystem::exists(execPath/"i2pd.conf")) // TODO: magic string - dataDir = execPath.string (); - else // otherwise %appdata% { - if(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK) + dataDir = execPath.string (); + } else // otherwise %appdata% + { + if(SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, localAppData) != S_OK) { #ifdef WIN32_APP MessageBox(NULL, TEXT("Unable to get AppData path!"), TEXT("I2Pd: error"), MB_ICONERROR | MB_OK); @@ -78,7 +92,9 @@ namespace fs { exit(1); } else - dataDir = std::string(localAppData) + "\\" + appName; + { + dataDir = boost::filesystem::wpath(localAppData).string() + "\\" + appName; + } } } return; diff --git a/libi2pd/FS.h b/libi2pd/FS.h index 698e9b6b..f07ee35c 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 datadir path in UTF-8 encoding */ + const std::string GetUTF8DataDir(); + /** * @brief Set datadir either from cmdline option or using autodetection * @param cmdline_param Value of cmdline parameter --datadir= diff --git a/libi2pd/Garlic.cpp b/libi2pd/Garlic.cpp index 66a3b62f..2bf89556 100644 --- a/libi2pd/Garlic.cpp +++ b/libi2pd/Garlic.cpp @@ -471,7 +471,7 @@ namespace garlic { uint64_t t; memcpy (&t, tag, 8); - auto tagset = std::make_shared(this, key); + auto tagset = std::make_shared(this, key); m_ECIESx25519Tags.emplace (t, ECIESX25519AEADRatchetIndexTagset{0, tagset}); } diff --git a/libi2pd/HTTP.cpp b/libi2pd/HTTP.cpp index da4299e9..6ad245f0 100644 --- a/libi2pd/HTTP.cpp +++ b/libi2pd/HTTP.cpp @@ -187,6 +187,8 @@ namespace http params.clear(); for (const auto& it : tokens) { + if (!it.length()) // empty + continue; std::size_t eq = it.find ('='); if (eq != std::string::npos) { auto e = std::pair(it.substr(0, eq), it.substr(eq + 1)); diff --git a/libi2pd/I2NPProtocol.cpp b/libi2pd/I2NPProtocol.cpp index 7f059d72..7c4d84c3 100644 --- a/libi2pd/I2NPProtocol.cpp +++ b/libi2pd/I2NPProtocol.cpp @@ -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 * @@ -18,6 +18,7 @@ #include "Tunnel.h" #include "Transports.h" #include "Garlic.h" +#include "ECIESX25519AEADRatchetSession.h" #include "I2NPProtocol.h" #include "version.h" @@ -387,16 +388,16 @@ namespace i2p bufbe32toh (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), clearText + ECIES_BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, clearText + ECIES_BUILD_REQUEST_RECORD_IV_KEY_OFFSET, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, - clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) : + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) : i2p::tunnel::CreateTransitTunnel ( bufbe32toh (clearText + BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, bufbe32toh (clearText + BUILD_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), clearText + BUILD_REQUEST_RECORD_LAYER_KEY_OFFSET, clearText + BUILD_REQUEST_RECORD_IV_KEY_OFFSET, - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x80, - clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40); + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); } else @@ -424,9 +425,9 @@ namespace i2p { uint8_t nonce[12]; memset (nonce, 0, 12); - auto noiseState = std::move (i2p::context.GetCurrentNoiseState ()); - if (!noiseState || !i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, - noiseState->m_H, 32, noiseState->m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + auto& noiseState = i2p::context.GetCurrentNoiseState (); + if (!i2p::crypto::AEADChaCha20Poly1305 (reply, TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, noiseState.m_CK, nonce, reply, TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt { LogPrint (eLogWarning, "I2NP: Reply AEAD encryption failed"); return false; @@ -458,7 +459,7 @@ namespace i2p LogPrint (eLogDebug, "I2NP: VariableTunnelBuild ", num, " records"); if (len < num*TUNNEL_BUILD_RECORD_SIZE + 1) { - LogPrint (eLogError, "VaribleTunnelBuild message of ", num, " records is too short ", len); + LogPrint (eLogError, "I2NP: VaribleTunnelBuild message of ", num, " records is too short ", len); return; } @@ -486,7 +487,7 @@ namespace i2p uint8_t clearText[ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; if (HandleBuildRequestRecords (num, buf + 1, clearText)) { - if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel + if (clearText[ECIES_BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel { // so we send it to reply tunnel transports.SendMessage (clearText + ECIES_BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, @@ -505,7 +506,7 @@ namespace i2p uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; if (HandleBuildRequestRecords (num, buf + 1, clearText)) { - if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outboud tunnel + if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outboud tunnel { // so we send it to reply tunnel transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, @@ -526,18 +527,18 @@ namespace i2p { if (i2p::context.IsECIES ()) { - LogPrint (eLogWarning, "TunnelBuild is too old for ECIES router"); + LogPrint (eLogWarning, "I2NP: TunnelBuild is too old for ECIES router"); return; } if (len < NUM_TUNNEL_BUILD_RECORDS*TUNNEL_BUILD_RECORD_SIZE) { - LogPrint (eLogError, "TunnelBuild message is too short ", len); + LogPrint (eLogError, "I2NP: TunnelBuild message is too short ", len); return; } uint8_t clearText[BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE]; if (HandleBuildRequestRecords (NUM_TUNNEL_BUILD_RECORDS, buf, clearText)) { - if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & 0x40) // we are endpoint of outbound tunnel + if (clearText[BUILD_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) // we are endpoint of outbound tunnel { // so we send it to reply tunnel transports.SendMessage (clearText + BUILD_REQUEST_RECORD_NEXT_IDENT_OFFSET, @@ -558,7 +559,7 @@ namespace i2p LogPrint (eLogDebug, "I2NP: VariableTunnelBuildReplyMsg of ", num, " records replyMsgID=", replyMsgID); if (len < num*BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 1) { - LogPrint (eLogError, "VaribleTunnelBuildReply message of ", num, " records is too short ", len); + LogPrint (eLogError, "I2NP: VaribleTunnelBuildReply message of ", num, " records is too short ", len); return; } @@ -582,7 +583,112 @@ namespace i2p LogPrint (eLogWarning, "I2NP: Pending tunnel for message ", replyMsgID, " not found"); } - + void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len) + { + if (!i2p::context.IsECIES ()) + { + LogPrint (eLogWarning, "I2NP: ShortTunnelBuild can be handled by ECIES router only"); + return; + } + int num = buf[0]; + LogPrint (eLogDebug, "I2NP: ShortTunnelBuild ", num, " records"); + if (len < num*SHORT_TUNNEL_BUILD_RECORD_SIZE + 1) + { + LogPrint (eLogError, "I2NP: ShortTunnelBuild message of ", num, " records is too short ", len); + return; + } + // TODO: check replyMsgID + const uint8_t * record = buf + 1; + for (int i = 0; i < num; i++) + { + if (!memcmp (record, (const uint8_t *)i2p::context.GetRouterInfo ().GetIdentHash (), 16)) + { + LogPrint (eLogDebug, "I2NP: Short request record ", i, " is ours"); + uint8_t clearText[SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE]; + if (!i2p::context.DecryptTunnelShortRequestRecord (record + SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET, clearText)) + { + LogPrint (eLogWarning, "I2NP: Can't decrypt short request record ", i); + return; + } + auto& noiseState = i2p::context.GetCurrentNoiseState (); + uint8_t layerKeys[64]; // (layer key, iv key) + i2p::crypto::HKDF (noiseState.m_CK + 32, nullptr, 0, "LayerAndIVKeys", layerKeys); // TODO: correct domain + auto transitTunnel = i2p::tunnel::CreateTransitTunnel ( + bufbe32toh (clearText + SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET), + clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + layerKeys, layerKeys + 32, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_GATEWAY_FLAG, + clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG); + i2p::tunnel::tunnels.AddTransitTunnel (transitTunnel); + if (clearText[SHORT_REQUEST_RECORD_FLAG_OFFSET] & TUNNEL_BUILD_RECORD_ENDPOINT_FLAG) + { + // we are endpoint, create OutboundTunnelBuildReply + auto otbrm = NewI2NPShortMessage (); + auto payload = otbrm->GetPayload (); + payload[0] = num; // num + payload[1] = i; // slot + payload +=2; + // reply + htobe16buf (payload, 3); payload += 2; // length, TODO + memset (payload, 0, 3); payload += 3; // ClearText: no options, and zero ret code. TODO + // ShortBuildReplyRecords. Exclude ours + uint8_t * records = buf + 1; + if (i > 0) + { + memcpy (payload, records, i*SHORT_TUNNEL_BUILD_RECORD_SIZE); + payload += i*SHORT_TUNNEL_BUILD_RECORD_SIZE; + records += i*SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + if (i < num-1) + { + memcpy (payload, records, (num-1-i)*SHORT_TUNNEL_BUILD_RECORD_SIZE); + payload += (num-1-i)*SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + otbrm->len += (payload - otbrm->GetPayload ()); + otbrm->FillI2NPMessageHeader (eI2NPOutboundTunnelBuildReply, bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET)); + uint8_t replyKeys[64]; // (reply key, tag) + i2p::crypto::HKDF (noiseState.m_CK, nullptr, 0, "ReplyKeyAndTag", replyKeys); // TODO: correct domain + uint64_t tag; + memcpy (&tag, replyKeys + 32, 8); + // send garlic to reply tunnel + transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET, + CreateTunnelGatewayMsg (bufbe32toh (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET), + i2p::garlic::WrapECIESX25519AEADRatchetMessage (otbrm, replyKeys, tag))); + } + else + { + // we are participant, encrypt reply + uint8_t nonce[12]; + memset (nonce, 0, 12); + uint8_t * reply = buf + 1; + for (int j = 0; j < num; j++) + { + nonce[4] = j; // nonce is record # + if (j == i) + { + // TODO: fill reply + if (!i2p::crypto::AEADChaCha20Poly1305 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE - 16, + noiseState.m_H, 32, noiseState.m_CK, nonce, reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, true)) // encrypt + { + LogPrint (eLogWarning, "I2NP: Short reply AEAD encryption failed"); + return; + } + } + else + i2p::crypto::ChaCha20 (reply, SHORT_TUNNEL_BUILD_RECORD_SIZE, noiseState.m_CK, nonce, reply); + reply += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + transports.SendMessage (clearText + SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET, + CreateI2NPMessage (eI2NPShortTunnelBuild, buf, len, + bufbe32toh (clearText + SHORT_REQUEST_RECORD_SEND_MSG_ID_OFFSET))); + } + return; + } + record += SHORT_TUNNEL_BUILD_RECORD_SIZE; + } + } + std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf) { auto msg = NewI2NPTunnelMessage (); @@ -700,6 +806,9 @@ namespace i2p case eI2NPVariableTunnelBuildReply: HandleVariableTunnelBuildReplyMsg (msgID, buf, size); break; + case eI2NPShortTunnelBuild: + HandleShortTunnelBuildMsg (msgID, buf, size); + break; case eI2NPTunnelBuild: HandleTunnelBuildMsg (buf, size); break; @@ -756,6 +865,7 @@ namespace i2p case eI2NPVariableTunnelBuildReply: case eI2NPTunnelBuild: case eI2NPTunnelBuildReply: + case eI2NPShortTunnelBuild: // forward to tunnel thread i2p::tunnel::tunnels.PostTunnelData (msg); break; diff --git a/libi2pd/I2NPProtocol.h b/libi2pd/I2NPProtocol.h index c9b0fc04..ee143734 100644 --- a/libi2pd/I2NPProtocol.h +++ b/libi2pd/I2NPProtocol.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 * @@ -55,7 +55,8 @@ namespace i2p // TunnelBuild const size_t TUNNEL_BUILD_RECORD_SIZE = 528; - + const size_t SHORT_TUNNEL_BUILD_RECORD_SIZE = 236; + //BuildRequestRecordClearText const size_t BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; const size_t BUILD_REQUEST_RECORD_OUR_IDENT_OFFSET = BUILD_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; @@ -100,6 +101,19 @@ namespace i2p // ECIES BuildResponseRecord const size_t ECIES_BUILD_RESPONSE_RECORD_OPTIONS_OFFSET = 0; const size_t ECIES_BUILD_RESPONSE_RECORD_RET_OFFSET = 511; + + // ShortRequestRecordClearText + const size_t SHORT_REQUEST_RECORD_ENCRYPTED_OFFSET = 16; + const size_t SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET = 0; + const size_t SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET = SHORT_REQUEST_RECORD_RECEIVE_TUNNEL_OFFSET + 4; + const size_t SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET = SHORT_REQUEST_RECORD_NEXT_TUNNEL_OFFSET + 4; + const size_t SHORT_REQUEST_RECORD_FLAG_OFFSET = SHORT_REQUEST_RECORD_NEXT_IDENT_OFFSET + 32; + const size_t SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET = SHORT_REQUEST_RECORD_FLAG_OFFSET + 1; + const size_t SHORT_REQUEST_RECORD_LAYER_ENCRYPTION_TYPE = SHORT_REQUEST_RECORD_MORE_FLAGS_OFFSET + 2; + 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; enum I2NPMessageType { @@ -115,9 +129,13 @@ namespace i2p eI2NPTunnelBuild = 21, eI2NPTunnelBuildReply = 22, eI2NPVariableTunnelBuild = 23, - eI2NPVariableTunnelBuildReply = 24 + eI2NPVariableTunnelBuildReply = 24, + eI2NPShortTunnelBuild = 25, + eI2NPOutboundTunnelBuildReply = 26 }; + const uint8_t TUNNEL_BUILD_RECORD_GATEWAY_FLAG = 0x80; + const uint8_t TUNNEL_BUILD_RECORD_ENDPOINT_FLAG = 0x40; const int NUM_TUNNEL_BUILD_RECORDS = 8; // DatabaseLookup flags @@ -284,6 +302,7 @@ namespace tunnel bool HandleBuildRequestRecords (int num, uint8_t * records, uint8_t * clearText); void HandleVariableTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); void HandleVariableTunnelBuildReplyMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); + void HandleShortTunnelBuildMsg (uint32_t replyMsgID, uint8_t * buf, size_t len); void HandleTunnelBuildMsg (uint8_t * buf, size_t len); std::shared_ptr CreateTunnelDataMsg (const uint8_t * buf); diff --git a/libi2pd/NTCP2.cpp b/libi2pd/NTCP2.cpp index d5a03d1c..cb200b42 100644 --- a/libi2pd/NTCP2.cpp +++ b/libi2pd/NTCP2.cpp @@ -23,6 +23,10 @@ #include "HTTP.h" #include "util.h" +#ifdef __linux__ + #include +#endif + namespace i2p { namespace transport @@ -1166,7 +1170,7 @@ namespace transport if (!address) continue; if (address->IsPublishedNTCP2 () && address->port) { - if (address->host.is_v4()) + if (address->IsV4()) { try { @@ -1185,7 +1189,7 @@ namespace transport auto conn = std::make_shared(*this); m_NTCP2Acceptor->async_accept(conn->GetSocket (), std::bind (&NTCP2Server::HandleAccept, this, conn, std::placeholders::_1)); } - else if (address->host.is_v6() && (context.SupportsV6 () || context.SupportsMesh ())) + else if (address->IsV6() && (context.SupportsV6 () || context.SupportsMesh ())) { m_NTCP2V6Acceptor.reset (new boost::asio::ip::tcp::acceptor (GetService ())); try @@ -1193,6 +1197,18 @@ namespace transport m_NTCP2V6Acceptor->open (boost::asio::ip::tcp::v6()); m_NTCP2V6Acceptor->set_option (boost::asio::ip::v6_only (true)); m_NTCP2V6Acceptor->set_option (boost::asio::socket_base::reuse_address (true)); +#ifdef __linux__ + if (!m_Address6 && !m_YggdrasilAddress) // only if not binded to address + { + // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others +#if (BOOST_VERSION >= 105500) + typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; +#else + typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; +#endif + m_NTCP2V6Acceptor->set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA)); + } +#endif auto ep = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v6(), address->port); if (m_Address6 && !context.SupportsMesh ()) ep = boost::asio::ip::tcp::endpoint (m_Address6->address(), address->port); diff --git a/libi2pd/RouterContext.cpp b/libi2pd/RouterContext.cpp index ad6576ed..50dd6c59 100644 --- a/libi2pd/RouterContext.cpp +++ b/libi2pd/RouterContext.cpp @@ -45,10 +45,8 @@ namespace i2p UpdateRouterInfo (); if (IsECIES ()) { - auto initState = new i2p::crypto::NoiseSymmetricState (); - i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ()); - m_InitialNoiseState.reset (initState); - m_ECIESSession = std::make_shared(*initState); + i2p::crypto::InitNoiseNState (m_InitialNoiseState, GetIdentity ()->GetEncryptionPublicKey ()); + m_ECIESSession = std::make_shared(m_InitialNoiseState); } } @@ -486,7 +484,7 @@ namespace i2p addr->ssu->introducers.clear (); port = addr->port; } - // unpiblish NTCP2 addreeses + // unpublish NTCP2 addreeses bool ntcp2; i2p::config::GetOption("ntcp2.enabled", ntcp2); if (ntcp2) PublishNTCP2Address (port, false, v4, v6, false); @@ -679,7 +677,7 @@ namespace i2p if (addr->IsPublishedNTCP2 ()) { bool isYgg1 = i2p::util::net::IsYggdrasilAddress (addr->host); - if (addr->host.is_v6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1))) + if (addr->IsV6 () && ((isYgg && isYgg1) || (!isYgg && !isYgg1))) { if (addr->host != host) { @@ -736,14 +734,8 @@ namespace i2p } } std::shared_ptr oldIdentity; - bool rekey = m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1; - if (!rekey && m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) - { - // rekey routers with bandwidth = L (or default) this time - bool isFloodfill; i2p::config::GetOption("floodfill", isFloodfill); - if (!isFloodfill) rekey = true; - } - if (rekey) + if (m_Keys.GetPublic ()->GetSigningKeyType () == i2p::data::SIGNING_KEY_TYPE_DSA_SHA1 || + m_Keys.GetPublic ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ELGAMAL) { // update keys LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new"); @@ -881,34 +873,11 @@ namespace i2p bool RouterContext::DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data) { - if (!m_TunnelDecryptor) return false; if (IsECIES ()) - { - if (!m_InitialNoiseState) return false; - // m_InitialNoiseState is h = SHA256(h || hepk) - m_CurrentNoiseState.reset (new i2p::crypto::NoiseSymmetricState (*m_InitialNoiseState)); - m_CurrentNoiseState->MixHash (encrypted, 32); // h = SHA256(h || sepk) - uint8_t sharedSecret[32]; - if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false)) - { - LogPrint (eLogWarning, "Router: Incorrect ephemeral public key"); - return false; - } - m_CurrentNoiseState->MixKey (sharedSecret); - encrypted += 32; - uint8_t nonce[12]; - memset (nonce, 0, 12); - if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, - m_CurrentNoiseState->m_H, 32, m_CurrentNoiseState->m_CK + 32, nonce, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE, false)) // decrypt - { - LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed"); - return false; - } - m_CurrentNoiseState->MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext) - return true; - } + return DecryptECIESTunnelBuildRecord (encrypted, data, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE); else { + if (!m_TunnelDecryptor) return false; BN_CTX * ctx = BN_CTX_new (); bool success = m_TunnelDecryptor->Decrypt (encrypted, data, ctx, false); BN_CTX_free (ctx); @@ -916,6 +885,42 @@ namespace i2p } } + bool RouterContext::DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize) + { + // m_InitialNoiseState is h = SHA256(h || hepk) + m_CurrentNoiseState = m_InitialNoiseState; + m_CurrentNoiseState.MixHash (encrypted, 32); // h = SHA256(h || sepk) + uint8_t sharedSecret[32]; + if (!m_TunnelDecryptor->Decrypt (encrypted, sharedSecret, nullptr, false)) + { + LogPrint (eLogWarning, "Router: Incorrect ephemeral public key"); + return false; + } + m_CurrentNoiseState.MixKey (sharedSecret); + encrypted += 32; + uint8_t nonce[12]; + memset (nonce, 0, 12); + if (!i2p::crypto::AEADChaCha20Poly1305 (encrypted, clearTextSize, m_CurrentNoiseState.m_H, 32, + m_CurrentNoiseState.m_CK + 32, nonce, data, clearTextSize, false)) // decrypt + { + LogPrint (eLogWarning, "Router: Tunnel record AEAD decryption failed"); + return false; + } + m_CurrentNoiseState.MixHash (encrypted, clearTextSize + 16); // h = SHA256(h || ciphertext) + return true; + } + + bool RouterContext::DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data) + { + if (IsECIES ()) + return DecryptECIESTunnelBuildRecord (encrypted, data, SHORT_REQUEST_RECORD_CLEAR_TEXT_SIZE); + else + { + LogPrint (eLogWarning, "Router: Can't decrypt short request record on non-ECIES router"); + return false; + } + } + i2p::crypto::X25519Keys& RouterContext::GetStaticKeys () { if (!m_StaticKeys) diff --git a/libi2pd/RouterContext.h b/libi2pd/RouterContext.h index a4d18f82..acb33795 100644 --- a/libi2pd/RouterContext.h +++ b/libi2pd/RouterContext.h @@ -18,13 +18,14 @@ #include "Identity.h" #include "RouterInfo.h" #include "Garlic.h" +#include "I18N_langs.h" namespace i2p { namespace garlic { class RouterIncomingRatchetSession; -} +} const char ROUTER_INFO[] = "router.info"; const char ROUTER_KEYS[] = "router.keys"; @@ -39,7 +40,7 @@ namespace garlic eRouterStatusError = 3, eRouterStatusUnknown = 4, eRouterStatusProxy = 5, - eRouterStatusMesh = 6 + eRouterStatusMesh = 6 }; enum RouterError @@ -49,7 +50,7 @@ namespace garlic eRouterErrorOffline = 2, eRouterErrorSymmetricNAT = 3 }; - + class RouterContext: public i2p::garlic::GarlicDestination { private: @@ -96,9 +97,10 @@ namespace garlic int GetNetID () const { return m_NetID; }; void SetNetID (int netID) { m_NetID = netID; }; bool DecryptTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data); - + bool DecryptTunnelShortRequestRecord (const uint8_t * encrypted, uint8_t * data); + void UpdatePort (int port); // called from Daemon - void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon + void UpdateAddress (const boost::asio::ip::address& host); // called from SSU or Daemon void PublishNTCP2Address (int port, bool publish, bool v4, bool v6, bool ygg); void UpdateNTCP2Address (bool enable); void RemoveNTCPAddress (bool v4only = true); // delete NTCP address for older routers. TODO: remove later @@ -123,12 +125,12 @@ namespace garlic void SetSupportsV4 (bool supportsV4); void SetSupportsMesh (bool supportsmesh, const boost::asio::ip::address_v6& host); bool IsECIES () const { return GetIdentity ()->GetCryptoKeyType () == i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD; }; - std::unique_ptr& GetCurrentNoiseState () { return m_CurrentNoiseState; }; - + i2p::crypto::NoiseSymmetricState& GetCurrentNoiseState () { return m_CurrentNoiseState; }; + void UpdateNTCP2V6Address (const boost::asio::ip::address& host); // called from Daemon. TODO: remove void UpdateStats (); void UpdateTimestamp (uint64_t ts); // in seconds, called from NetDb before publishing - void CleanupDestination (); // garlic destination + void CleanupDestination (); // garlic destination // implements LocalDestination std::shared_ptr GetIdentity () const { return m_Keys.GetPublic (); }; @@ -144,6 +146,10 @@ namespace garlic void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatusMessage (std::shared_ptr msg); + // i18n + std::shared_ptr GetLanguage () { return m_Language; }; + void SetLanguage (const std::shared_ptr language) { m_Language = language; }; + protected: // implements GarlicDestination @@ -159,6 +165,8 @@ namespace garlic bool Load (); void SaveKeys (); + bool DecryptECIESTunnelBuildRecord (const uint8_t * encrypted, uint8_t * data, size_t clearTextSize); + private: i2p::data::RouterInfo m_RouterInfo; @@ -177,7 +185,10 @@ namespace garlic std::unique_ptr m_NTCP2Keys; std::unique_ptr m_StaticKeys; // for ECIESx25519 - std::unique_ptr m_InitialNoiseState, m_CurrentNoiseState; + i2p::crypto::NoiseSymmetricState m_InitialNoiseState, m_CurrentNoiseState; + + // i18n + std::shared_ptr m_Language; }; extern RouterContext context; diff --git a/libi2pd/RouterInfo.cpp b/libi2pd/RouterInfo.cpp index 87a987fe..dcef9463 100644 --- a/libi2pd/RouterInfo.cpp +++ b/libi2pd/RouterInfo.cpp @@ -36,7 +36,7 @@ namespace data RouterInfo::RouterInfo (const std::string& fullPath): m_FullPath (fullPath), m_IsUpdated (false), m_IsUnreachable (false), - m_SupportedTransports (0), m_Caps (0), m_Version (0) + m_SupportedTransports (0), m_ReachableTransports (0), m_Caps (0), m_Version (0) { m_Addresses = boost::make_shared(); // create empty list m_Buffer = new uint8_t[MAX_RI_BUFFER_SIZE]; @@ -45,7 +45,7 @@ namespace data RouterInfo::RouterInfo (const uint8_t * buf, int len): m_IsUpdated (true), m_IsUnreachable (false), m_SupportedTransports (0), - m_Caps (0), m_Version (0) + m_ReachableTransports (0), m_Caps (0), m_Version (0) { m_Addresses = boost::make_shared(); // create empty list if (len <= MAX_RI_BUFFER_SIZE) @@ -84,6 +84,7 @@ namespace data m_IsUpdated = true; m_IsUnreachable = false; m_SupportedTransports = 0; + m_ReachableTransports = 0; m_Caps = 0; // don't clean up m_Addresses, it will be replaced in ReadFromStream m_Properties.clear (); @@ -305,9 +306,10 @@ namespace data if (isHost) { if (address->host.is_v6 ()) - supportedTransports |= i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6; + supportedTransports |= (i2p::util::net::IsYggdrasilAddress (address->host) ? eNTCP2V6Mesh : eNTCP2V6); else supportedTransports |= eNTCP2V4; + m_ReachableTransports |= supportedTransports; } else if (!address->published) { @@ -347,10 +349,16 @@ namespace data else it.iPort = 0; } - if (!numValid) address->ssu->introducers.resize (0); + if (numValid) + m_ReachableTransports |= supportedTransports; + else + address->ssu->introducers.resize (0); } else if (isHost && address->port) + { address->published = true; + m_ReachableTransports |= supportedTransports; + } } } if (supportedTransports) @@ -546,7 +554,7 @@ namespace data if (address.IsNTCP2 ()) { WriteString ("NTCP2", s); - if (address.IsPublishedNTCP2 () && !address.host.is_unspecified ()) + if (address.IsPublishedNTCP2 () && !address.host.is_unspecified () && address.port) isPublished = true; else { @@ -860,6 +868,7 @@ namespace data for (auto& intro: addr->ssu->introducers) if (intro.iTag == introducer.iTag) return false; // already presented addr->ssu->introducers.push_back (introducer); + m_ReachableTransports |= (addr->IsV4 () ? eSSUV4 : eSSUV6); return true; } } @@ -877,6 +886,8 @@ namespace data if (boost::asio::ip::udp::endpoint (it->iHost, it->iPort) == e) { addr->ssu->introducers.erase (it); + if (addr->ssu->introducers.empty ()) + m_ReachableTransports &= ~(addr->IsV4 () ? eSSUV4 : eSSUV6); return true; } } @@ -987,9 +998,16 @@ namespace data for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; - addr->caps &= ~AddressCaps::eV6; - if (addr->host.is_v6 ()) - it = m_Addresses->erase (it); + if (addr->IsV6 ()) + { + if (addr->IsV4 ()) + { + addr->caps &= ~AddressCaps::eV6; + ++it; + } + else + it = m_Addresses->erase (it); + } else ++it; } @@ -1004,9 +1022,16 @@ namespace data for (auto it = m_Addresses->begin (); it != m_Addresses->end ();) { auto addr = *it; - addr->caps &= ~AddressCaps::eV4; - if (addr->host.is_v4 ()) - it = m_Addresses->erase (it); + if (addr->IsV4 ()) + { + if (addr->IsV6 ()) + { + addr->caps &= ~AddressCaps::eV4; + ++it; + } + else + it = m_Addresses->erase (it); + } else ++it; } @@ -1177,12 +1202,38 @@ namespace data for (auto& addr: *m_Addresses) { // TODO: implement SSU - if (addr->transportStyle == eTransportNTCP && (!addr->IsPublishedNTCP2 () || addr->port)) + if (addr->transportStyle == eTransportNTCP && !addr->IsPublishedNTCP2 ()) { addr->caps &= ~(eV4 | eV6); addr->caps |= transports; } } } + + void RouterInfo::UpdateSupportedTransports () + { + m_SupportedTransports = 0; + m_ReachableTransports = 0; + for (const auto& addr: *m_Addresses) + { + uint8_t transports = 0; + if (addr->transportStyle == eTransportNTCP) + { + if (addr->IsV4 ()) transports |= eNTCP2V4; + if (addr->IsV6 ()) + transports |= (i2p::util::net::IsYggdrasilAddress (addr->host) ? eNTCP2V6Mesh : eNTCP2V6); + if (addr->IsPublishedNTCP2 ()) + m_ReachableTransports |= transports; + } + else if (addr->transportStyle == eTransportSSU) + { + if (addr->IsV4 ()) transports |= eSSUV4; + if (addr->IsV6 ()) transports |= eSSUV6; + if (addr->IsReachableSSU ()) + m_ReachableTransports |= transports; + } + m_SupportedTransports |= transports; + } + } } } diff --git a/libi2pd/RouterInfo.h b/libi2pd/RouterInfo.h index dd3e7e13..bea83fda 100644 --- a/libi2pd/RouterInfo.h +++ b/libi2pd/RouterInfo.h @@ -59,7 +59,7 @@ namespace data { public: - enum SupportedTranports + enum SupportedTransports { eNTCP2V4 = 0x01, eNTCP2V6 = 0x02, @@ -147,7 +147,7 @@ namespace data bool IsNTCP2 () const { return (bool)ntcp2; }; bool IsPublishedNTCP2 () const { return IsNTCP2 () && published; }; - bool IsReachableSSU () const { return (bool)ssu && (!host.is_unspecified () || !ssu->introducers.empty ()); }; + bool IsReachableSSU () const { return (bool)ssu && (published || !ssu->introducers.empty ()); }; bool UsesIntroducer () const { return (bool)ssu && !ssu->introducers.empty (); }; bool IsIntroducer () const { return caps & eSSUIntroducer; }; @@ -187,6 +187,7 @@ namespace data std::string GetProperty (const std::string& key) const; // called from RouterContext only void ClearProperties () { m_Properties.clear (); }; void SetUnreachableAddressesTransportCaps (uint8_t transports); // bitmask of AddressCaps + void UpdateSupportedTransports (); bool IsFloodfill () const { return m_Caps & Caps::eFloodfill; }; bool IsReachable () const { return m_Caps & Caps::eReachable; }; bool IsSSU (bool v4only = true) const; @@ -269,7 +270,7 @@ namespace data boost::shared_ptr m_Addresses; // TODO: use std::shared_ptr and std::atomic_store for gcc >= 4.9 std::map m_Properties; bool m_IsUpdated, m_IsUnreachable; - uint8_t m_SupportedTransports, m_Caps; + uint8_t m_SupportedTransports, m_ReachableTransports, m_Caps; int m_Version; mutable std::shared_ptr m_Profile; }; diff --git a/libi2pd/SSU.cpp b/libi2pd/SSU.cpp index f7801bb0..f3da611c 100644 --- a/libi2pd/SSU.cpp +++ b/libi2pd/SSU.cpp @@ -14,6 +14,10 @@ #include "SSU.h" #include "util.h" +#ifdef __linux__ + #include +#endif + #ifdef _WIN32 #include #endif @@ -62,6 +66,18 @@ namespace transport m_SocketV6.set_option (boost::asio::ip::v6_only (true)); m_SocketV6.set_option (boost::asio::socket_base::receive_buffer_size (SSU_SOCKET_RECEIVE_BUFFER_SIZE)); m_SocketV6.set_option (boost::asio::socket_base::send_buffer_size (SSU_SOCKET_SEND_BUFFER_SIZE)); +#ifdef __linux__ + if (m_EndpointV6.address() == boost::asio::ip::address().from_string("::")) // only if not binded to address + { + // Set preference to use public IPv6 address -- tested on linux, not works on windows, and not tested on others +#if (BOOST_VERSION >= 105500) + typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; +#else + typedef boost::asio::detail::socket_option::integer ipv6PreferAddr; +#endif + m_SocketV6.set_option (ipv6PreferAddr(IPV6_PREFER_SRC_PUBLIC | IPV6_PREFER_SRC_HOME | IPV6_PREFER_SRC_NONCGA)); + } +#endif m_SocketV6.bind (m_EndpointV6); LogPrint (eLogInfo, "SSU: Start listening v6 port ", m_EndpointV6.port()); } diff --git a/libi2pd/SSUSession.cpp b/libi2pd/SSUSession.cpp index 339ac8df..59ee578b 100644 --- a/libi2pd/SSUSession.cpp +++ b/libi2pd/SSUSession.cpp @@ -383,7 +383,7 @@ namespace transport { // tell out peer to now assign relay tag flag = SSU_HEADER_EXTENDED_OPTIONS_INCLUDED; - *payload = 2; payload++; // 1 byte length + *payload = 2; payload++; // 1 byte length uint16_t flags = 0; // clear EXTENDED_OPTIONS_FLAG_REQUEST_RELAY_TAG htobe16buf (payload, flags); payload += 2; @@ -1073,7 +1073,10 @@ namespace transport LogPrint (eLogDebug, "SSU: peer test from Charlie. We are Bob"); auto session = m_Server.GetPeerTestSession (nonce); // session with Alice from PeerTest if (session && session->m_State == eSessionStateEstablished) - session->Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Alice + { + const auto& ep = session->GetRemoteEndpoint (); // Alice's endpoint as known to Bob + session->SendPeerTest (nonce, ep.address (), ep.port (), introKey, false, true); // send back to Alice + } m_Server.RemovePeerTest (nonce); // nonce has been used break; } @@ -1093,9 +1096,12 @@ namespace transport if (port) { LogPrint (eLogDebug, "SSU: peer test from Bob. We are Charlie"); - m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie); Send (PAYLOAD_TYPE_PEER_TEST, buf, len); // back to Bob - SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob + if (!addr.is_unspecified () && !i2p::util::net::IsInReservedRange(addr)) + { + m_Server.NewPeerTest (nonce, ePeerTestParticipantCharlie); + SendPeerTest (nonce, addr, port, introKey); // to Alice with her address received from Bob + } } else { diff --git a/libi2pd/Tunnel.h b/libi2pd/Tunnel.h index 7e8edca7..f30eab14 100644 --- a/libi2pd/Tunnel.h +++ b/libi2pd/Tunnel.h @@ -38,6 +38,7 @@ namespace tunnel const int TUNNEL_CREATION_TIMEOUT = 30; // 30 seconds const int STANDARD_NUM_RECORDS = 4; // in VariableTunnelBuild message const int MAX_NUM_RECORDS = 8; + const int HIGH_LATENCY_PER_HOP = 250; // in milliseconds enum TunnelState { @@ -98,6 +99,8 @@ namespace tunnel bool LatencyFitsRange(uint64_t lowerbound, uint64_t upperbound) const; bool LatencyIsKnown() const { return m_Latency > 0; } + bool IsSlow () const { return LatencyIsKnown() && (int)m_Latency > HIGH_LATENCY_PER_HOP*GetNumHops (); } + protected: void PrintHops (std::stringstream& s) const; diff --git a/libi2pd/TunnelConfig.cpp b/libi2pd/TunnelConfig.cpp index d43483c0..8f515c5d 100644 --- a/libi2pd/TunnelConfig.cpp +++ b/libi2pd/TunnelConfig.cpp @@ -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 * @@ -81,8 +81,8 @@ namespace tunnel void TunnelHopConfig::CreateBuildRequestRecord (uint8_t * record, uint32_t replyMsgID, BN_CTX * ctx) { uint8_t flag = 0; - if (isGateway) flag |= 0x80; - if (isEndpoint) flag |= 0x40; + if (isGateway) flag |= TUNNEL_BUILD_RECORD_GATEWAY_FLAG; + if (isEndpoint) flag |= TUNNEL_BUILD_RECORD_ENDPOINT_FLAG; auto encryptor = ident->CreateEncryptor (nullptr); if (IsECIES ()) { diff --git a/libi2pd/TunnelPool.cpp b/libi2pd/TunnelPool.cpp index ed9a1a82..e45c6090 100644 --- a/libi2pd/TunnelPool.cpp +++ b/libi2pd/TunnelPool.cpp @@ -142,16 +142,24 @@ namespace tunnel { std::vector > v; int i = 0; + std::shared_ptr slowTunnel; std::unique_lock l(m_InboundTunnelsMutex); for (const auto& it : m_InboundTunnels) { if (i >= num) break; if (it->IsEstablished ()) { - v.push_back (it); - i++; + if (it->IsSlow () && !slowTunnel) + slowTunnel = it; + else + { + v.push_back (it); + i++; + } } } + if (slowTunnel && (int)v.size () < (num/2+1)) + v.push_back (slowTunnel); return v; } @@ -172,13 +180,16 @@ namespace tunnel { if (tunnels.empty ()) return nullptr; uint32_t ind = rand () % (tunnels.size ()/2 + 1), i = 0; + bool skipped = false; typename TTunnels::value_type tunnel = nullptr; for (const auto& it: tunnels) { if (it->IsEstablished () && it != excluded) { - if(HasLatencyRequirement() && it->LatencyIsKnown() && !it->LatencyFitsRange(m_MinLatency, m_MaxLatency)) { - i ++; + if (it->IsSlow () || (HasLatencyRequirement() && it->LatencyIsKnown() && + !it->LatencyFitsRange(m_MinLatency, m_MaxLatency))) + { + i++; skipped = true; continue; } tunnel = it; @@ -186,7 +197,8 @@ namespace tunnel } if (i > ind && tunnel) break; } - if(HasLatencyRequirement() && !tunnel) { + if (!tunnel && skipped) + { ind = rand () % (tunnels.size ()/2 + 1), i = 0; for (const auto& it: tunnels) { @@ -393,10 +405,14 @@ namespace tunnel } } + bool TunnelPool::IsExploratory () const + { + return i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this (); + } + std::shared_ptr TunnelPool::SelectNextHop (std::shared_ptr prevHop, bool reverse) const { - bool isExploratory = (i2p::tunnel::tunnels.GetExploratoryPool () == shared_from_this ()); - auto hop = isExploratory ? i2p::data::netdb.GetRandomRouter (prevHop, reverse): + auto hop = IsExploratory () ? i2p::data::netdb.GetRandomRouter (prevHop, reverse): i2p::data::netdb.GetHighBandwidthRandomRouter (prevHop, reverse); if (!hop || hop->GetProfile ()->IsBad ()) @@ -521,6 +537,11 @@ namespace tunnel void TunnelPool::RecreateInboundTunnel (std::shared_ptr tunnel) { + if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow + { + CreateInboundTunnel (); + return; + } auto outboundTunnel = GetNextOutboundTunnel (); if (!outboundTunnel) outboundTunnel = tunnels.GetNextOutboundTunnel (); @@ -567,6 +588,11 @@ namespace tunnel void TunnelPool::RecreateOutboundTunnel (std::shared_ptr tunnel) { + if (IsExploratory () || tunnel->IsSlow ()) // always create new exploratory tunnel or if slow + { + CreateOutboundTunnel (); + return; + } auto inboundTunnel = GetNextInboundTunnel (); if (!inboundTunnel) inboundTunnel = tunnels.GetNextInboundTunnel (); diff --git a/libi2pd/TunnelPool.h b/libi2pd/TunnelPool.h index 5fd1d83c..164ca7a1 100644 --- a/libi2pd/TunnelPool.h +++ b/libi2pd/TunnelPool.h @@ -77,6 +77,7 @@ namespace tunnel void ProcessGarlicMessage (std::shared_ptr msg); void ProcessDeliveryStatus (std::shared_ptr msg); + bool IsExploratory () const; bool IsActive () const { return m_IsActive; }; void SetActive (bool isActive) { m_IsActive = isActive; }; void DetachTunnels (); diff --git a/libi2pd/util.h b/libi2pd/util.h index 0cab4121..000cb74e 100644 --- a/libi2pd/util.h +++ b/libi2pd/util.h @@ -177,13 +177,13 @@ namespace util SaveStateHelper (T& orig): m_Original (orig), m_Copy (orig) {}; ~SaveStateHelper () { m_Original = m_Copy; }; - + private: T& m_Original; T m_Copy; - }; - + }; + namespace net { int GetMTU (const boost::asio::ip::address& localAddress); diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index 2f3d4d48..94311297 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -495,6 +495,8 @@ namespace client options[I2CP_PARAM_LEASESET_TYPE] = value; if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_ENCRYPTION_TYPE, value)) options[I2CP_PARAM_LEASESET_ENCRYPTION_TYPE] = value; + if (i2p::config::GetOption(prefix + I2CP_PARAM_LEASESET_PRIV_KEY, value) && !value.empty ()) + options[I2CP_PARAM_LEASESET_PRIV_KEY] = value; } void ClientContext::ReadTunnels () diff --git a/libi2pd_client/HTTPProxy.cpp b/libi2pd_client/HTTPProxy.cpp index 6b2b8df7..d8b84e82 100644 --- a/libi2pd_client/HTTPProxy.cpp +++ b/libi2pd_client/HTTPProxy.cpp @@ -28,6 +28,7 @@ #include "I2PTunnel.h" #include "Config.h" #include "HTTP.h" +#include "I18N.h" namespace i2p { namespace proxy { @@ -71,8 +72,8 @@ namespace proxy { void SentHTTPFailed(const boost::system::error_code & ecode); void HandleStreamRequestComplete (std::shared_ptr stream); /* error helpers */ - void GenericProxyError(const char *title, const char *description); - void GenericProxyInfo(const char *title, const char *description); + void GenericProxyError(const std::string& title, const std::string& description); + void GenericProxyInfo(const std::string& title, const std::string& description); void HostNotFound(std::string & host); void SendProxyError(std::string & content); @@ -151,17 +152,17 @@ namespace proxy { Done(shared_from_this()); } - void HTTPReqHandler::GenericProxyError(const char *title, const char *description) { + void HTTPReqHandler::GenericProxyError(const std::string& title, const std::string& description) { std::stringstream ss; - ss << "

Proxy error: " << title << "

\r\n"; + ss << "

" << tr("Proxy error") << ": " << title << "

\r\n"; ss << "

" << description << "

\r\n"; std::string content = ss.str(); SendProxyError(content); } - void HTTPReqHandler::GenericProxyInfo(const char *title, const char *description) { + void HTTPReqHandler::GenericProxyInfo(const std::string& title, const std::string& description) { std::stringstream ss; - ss << "

Proxy info: " << title << "

\r\n"; + ss << "

" << tr("Proxy info") << ": " << title << "

\r\n"; ss << "

" << description << "

\r\n"; std::string content = ss.str(); SendProxyError(content); @@ -169,9 +170,9 @@ namespace proxy { void HTTPReqHandler::HostNotFound(std::string & host) { std::stringstream ss; - ss << "

Proxy error: Host not found

\r\n" - << "

Remote host not found in router's addressbook

\r\n" - << "

You may try to find this host on jump services below:

\r\n" + ss << "

" << tr("Proxy error: Host not found") << "

\r\n" + << "

" << tr("Remote host not found in router's addressbook") << "

\r\n" + << "

" << tr("You may try to find this host on jump services below") << ":

\r\n" << "
    \r\n"; for (const auto& js : jumpservices) { ss << "
  • " << js.first << "
  • \r\n"; @@ -216,6 +217,7 @@ namespace proxy { b64 = i2p::http::UrlDecode(value); // if we need update exists, request formed with update param if (params["update"] == "true") { len += std::strlen("&update=true"); confirm = true; } + if (pos != 0 && url.query[pos-1] == '&') { pos--; len++; } // if helper is not only one query option url.query.replace(pos, len, ""); return true; } @@ -268,7 +270,7 @@ namespace proxy { if (m_req_len < 0) { LogPrint(eLogError, "HTTPProxy: unable to parse request"); - GenericProxyError("Invalid request", "Proxy unable to parse your request"); + GenericProxyError(tr("Invalid request"), tr("Proxy unable to parse your request")); return true; /* parse error */ } @@ -283,7 +285,7 @@ namespace proxy { if (!m_Addresshelper) { LogPrint(eLogWarning, "HTTPProxy: addresshelper request rejected"); - GenericProxyError("Invalid request", "addresshelper is not supported"); + GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported")); return true; } if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) @@ -292,17 +294,19 @@ namespace proxy { LogPrint (eLogInfo, "HTTPProxy: added address from addresshelper for ", m_RequestURL.host); std::string full_url = m_RequestURL.to_string(); std::stringstream ss; - ss << "Host " << m_RequestURL.host << " added to router's addressbook from helper. " - << "Click here to proceed."; - GenericProxyInfo("Addresshelper found", ss.str().c_str()); + ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". "; + ss << tr("Click") << " " << tr("here") << " " << tr("to proceed") << "."; + GenericProxyInfo(tr("Addresshelper found"), ss.str()); return true; /* request processed */ } else { + std::string full_url = m_RequestURL.to_string(); std::stringstream ss; - ss << "Host " << m_RequestURL.host << " already in router's addressbook. " - << "Click here to update record."; - GenericProxyInfo("Addresshelper found", ss.str().c_str()); + ss << tr("Host") << " " << m_RequestURL.host << " " << tr("already in router's addressbook") << ". "; + ss << tr("Click") << " " << tr("here") << " " << tr("to update record") << "."; + GenericProxyInfo(tr("Addresshelper found"), ss.str()); return true; /* request processed */ } } @@ -315,7 +319,7 @@ namespace proxy { auto pos = uri.find(":"); if(pos == std::string::npos || pos == uri.size() - 1) { - GenericProxyError("Invalid Request", "invalid request uri"); + GenericProxyError(tr("Invalid Request"), tr("invalid request uri")); return true; } else @@ -358,7 +362,7 @@ namespace proxy { else { /* relative url and missing 'Host' header */ - GenericProxyError("Invalid request", "Can't detect destination host from request"); + GenericProxyError(tr("Invalid request"), tr("Can't detect destination host from request")); return true; } } @@ -375,11 +379,11 @@ namespace proxy { if(m_ProxyURL.parse(m_OutproxyUrl)) ForwardToUpstreamProxy(); else - GenericProxyError("Outproxy failure", "bad outproxy settings"); + GenericProxyError(tr("Outproxy failure"), tr("bad outproxy settings")); } else { LogPrint (eLogWarning, "HTTPProxy: outproxy failure for ", dest_host, ": no outproxy enabled"); - std::string message = "Host " + dest_host + " not inside I2P network, but outproxy is not enabled"; - GenericProxyError("Outproxy failure", message.c_str()); + std::stringstream ss; ss << tr("Host") << " " << dest_host << " " << tr("not inside I2P network, but outproxy is not enabled"); + GenericProxyError(tr("Outproxy failure"), ss.str()); } return true; } @@ -467,13 +471,13 @@ namespace proxy { else { // unknown type, complain - GenericProxyError("unknown outproxy url", m_ProxyURL.to_string().c_str()); + GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string()); } } void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler) { - if(ec) GenericProxyError("cannot resolve upstream proxy", ec.message().c_str()); + if(ec) GenericProxyError(tr("cannot resolve upstream proxy"), ec.message()); else handler(*it); } @@ -481,7 +485,7 @@ namespace proxy { { if(!ec) { if(m_RequestURL.host.size() > 255) { - GenericProxyError("hostname too long", m_RequestURL.host.c_str()); + GenericProxyError(tr("hostname too long"), m_RequestURL.host); return; } uint16_t port = m_RequestURL.port; @@ -508,13 +512,13 @@ namespace proxy { reqsize += host.size(); m_socks_buf[++reqsize] = 0; boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_socks_buf, reqsize), boost::asio::transfer_all(), std::bind(&HTTPReqHandler::HandleSocksProxySendHandshake, this, std::placeholders::_1, std::placeholders::_2)); - } else GenericProxyError("cannot connect to upstream socks proxy", ec.message().c_str()); + } else GenericProxyError(tr("cannot connect to upstream socks proxy"), ec.message()); } void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred) { LogPrint(eLogDebug, "HTTPProxy: upstream socks handshake sent"); - if(ec) GenericProxyError("Cannot negotiate with socks proxy", ec.message().c_str()); + if(ec) GenericProxyError(tr("Cannot negotiate with socks proxy"), ec.message()); else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2)); } @@ -556,7 +560,7 @@ namespace proxy { } else { - GenericProxyError("CONNECT error", "Failed to Connect"); + GenericProxyError(tr("CONNECT error"), tr("Failed to Connect")); } } @@ -567,7 +571,7 @@ namespace proxy { m_send_buf = m_ClientResponse.to_string(); boost::asio::async_write(*m_sock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&] (const boost::system::error_code & ec, std::size_t transferred) { - if(ec) GenericProxyError("socks proxy error", ec.message().c_str()); + if(ec) GenericProxyError(tr("socks proxy error"), ec.message()); else HandoverToUpstreamProxy(); }); } else { @@ -575,7 +579,7 @@ namespace proxy { LogPrint(eLogDebug, "HTTPProxy: send ", m_send_buf.size(), " bytes"); boost::asio::async_write(*m_proxysock, boost::asio::buffer(m_send_buf), boost::asio::transfer_all(), [&](const boost::system::error_code & ec, std::size_t transferred) { - if(ec) GenericProxyError("failed to send request to upstream", ec.message().c_str()); + if(ec) GenericProxyError(tr("failed to send request to upstream"), ec.message()); else HandoverToUpstreamProxy(); }); } @@ -593,18 +597,18 @@ namespace proxy { ss << "error code: "; ss << (int) m_socks_buf[1]; std::string msg = ss.str(); - GenericProxyError("Socks Proxy error", msg.c_str()); + GenericProxyError(tr("socks proxy error"), msg); } } - else GenericProxyError("No Reply From socks proxy", ec.message().c_str()); + else GenericProxyError(tr("No Reply From socks proxy"), ec.message()); } void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) { if(!ec) { LogPrint(eLogDebug, "HTTPProxy: connected to http upstream"); - GenericProxyError("cannot connect", "http out proxy not implemented"); - } else GenericProxyError("cannot connect to upstream http proxy", ec.message().c_str()); + GenericProxyError(tr("cannot connect"), tr("http out proxy not implemented")); + } else GenericProxyError(tr("cannot connect to upstream http proxy"), ec.message()); } /* will be called after some data received from client */ @@ -637,7 +641,7 @@ namespace proxy { { if (!stream) { LogPrint (eLogError, "HTTPProxy: error when creating the stream, check the previous warnings for more info"); - GenericProxyError("Host is down", "Can't create connection to requested host, it may be down. Please try again later."); + GenericProxyError(tr("Host is down"), tr("Can't create connection to requested host, it may be down. Please try again later.")); return; } if (Kill()) diff --git a/libi2pd_client/I2CP.cpp b/libi2pd_client/I2CP.cpp index d07c6248..25a5504a 100644 --- a/libi2pd_client/I2CP.cpp +++ b/libi2pd_client/I2CP.cpp @@ -544,13 +544,11 @@ namespace client offset += 8; // date if (identity->Verify (buf, offset, buf + offset)) // signature { - bool isPublic = true; - if (params[I2CP_PARAM_DONT_PUBLISH_LEASESET] == "true") isPublic = false; if (!m_Destination) { m_Destination = m_Owner.IsSingleThread () ? - std::make_shared(m_Owner.GetService (), shared_from_this (), identity, isPublic, params): - std::make_shared(shared_from_this (), identity, isPublic, params); + std::make_shared(m_Owner.GetService (), shared_from_this (), identity, true, params): + std::make_shared(shared_from_this (), identity, true, params); SendSessionStatusMessage (1); // created LogPrint (eLogDebug, "I2CP: session ", m_SessionID, " created"); m_Destination->Start (); diff --git a/libi2pd_client/I2CP.h b/libi2pd_client/I2CP.h index 085bf30a..9a8bda4e 100644 --- a/libi2pd_client/I2CP.h +++ b/libi2pd_client/I2CP.h @@ -62,7 +62,6 @@ namespace client }; // params - const char I2CP_PARAM_DONT_PUBLISH_LEASESET[] = "i2cp.dontPublishLeaseSet"; const char I2CP_PARAM_MESSAGE_RELIABILITY[] = "i2cp.messageReliability"; class I2CPSession;