Compare commits

..

37 Commits

Author SHA1 Message Date
R4SAS
a7a882582f [gha] test again with 20.04
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-22 20:42:32 +03:00
R4SAS
250696a7b5 [gha] revert to 18.04, change static build options
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-22 20:34:44 +03:00
R4SAS
af7905744e [gha] update ubuntu build base to 20.04
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-22 19:12:01 +03:00
R4SAS
db2364e9aa [gha] add ubuntu static build
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-22 19:03:49 +03:00
orignal
067fb45a25 exclude router from tunnel build for 2.5 minutes if doesn't reply too often 2023-01-21 19:40:43 -05:00
R4SAS
ac287a896c [websonsole] use a function to format the amount of tunnel traffic
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-20 23:03:00 +00:00
R4SAS
8baf62eb2c [websonsole] fix int concatenation with char strings
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-21 01:55:43 +03:00
R4SAS
e1ec79daf2 [webconsole] format transit tunnels with table
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-21 00:51:07 +03:00
orignal
d4426118c5 exclude router from tunnel build for 2.5 minutes if declined 2023-01-20 15:34:40 -05:00
orignal
64fe56aa07 Merge pull request #1854 from Vort/server_forwards_style
use correct style for Server Forwards section
2023-01-20 11:58:10 -05:00
Vort
47eb49c34e use correct style for Server Forwards section 2023-01-20 18:52:56 +02:00
orignal
cd6d86c8c3 make sure that async CreateStream complete 2023-01-19 13:40:12 -05:00
R4SAS
84d4e074ce add loglevel checker, fix fields passing to translated string formatter
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-19 08:33:03 +03:00
R4SAS
a0e71c4173 [i18n] update strings and translation file
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-19 07:46:54 +03:00
R4SAS
533c8a8a55 [i18n] set decimal point based on language
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-19 06:40:32 +03:00
R4SAS
a57ae4dc56 [i18n] add sweedish translation
Translation author: corona@mail.i2p

Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-19 05:53:29 +03:00
R4SAS
88dfe3ca4e [i18n] fix build on macos
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-18 18:56:21 +03:00
R4SAS
d68c7f8ea7 [i18n] fix build on macos
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-18 08:19:45 +03:00
R4SAS
e8ace998ba [i18n] add support of string formatting
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-18 07:21:44 +03:00
orignal
e8be39af17 memrory pool for SSU2IncompleteMessage 2023-01-17 21:32:36 -05:00
orignal
7196db09d6 Merge pull request #1852 from freeacetone/openssl
HTTPProxy message stream correcting and comments
2023-01-17 07:48:05 -05:00
acetone
b290ee1aa0 Cfg example: verbose comments for Web Console auth and addresshelper for public proxy 2023-01-17 09:00:11 +03:00
acetone
d105ab11af Joining two strings to one and correct comments 2023-01-17 08:45:18 +03:00
orignal
bc888167a7 use linked list for out of sequence fragments 2023-01-16 21:40:23 -05:00
R4SAS
6ca6591c43 [make] set PREFIX from DESTDIR if it present
Signed-off-by: R4SAS <r4sas@i2pmail.org>
2023-01-17 01:30:31 +03:00
orignal
36cb707e47 Merge pull request #1849 from freeacetone/openssl
Addresshelper updating: referer check
2023-01-16 08:33:07 -05:00
acetone
013d5ff74f Addresshelper request validation by Refer-header 2023-01-16 16:16:16 +03:00
acetone
9af5a90757 Copyright year updated 2023-01-16 13:57:15 +03:00
acetone
d8b6f5438c log typo 2023-01-16 13:56:36 +03:00
acetone
10030a4e0d Addresshelper updating: referer check 2023-01-16 13:31:13 +03:00
orignal
993dc72ce6 use separate pointer to second fragment of incomplete message 2023-01-15 22:50:54 -05:00
orignal
324ace103b memoery pool for fragments 2023-01-14 17:05:09 -05:00
orignal
d530269e4f try to insert received msgid instead lookup 2023-01-13 19:23:26 -05:00
orignal
7146a4dbae check if session socket was closed before tunnels were built 2023-01-12 15:41:57 -05:00
R4SAS
f79900653b Update README.md 2023-01-12 15:49:02 +03:00
orignal
f172f44f32 Merge pull request #1833 from TomasGlgg/feature
Использование скользящего среднего для рассчета tunnel creation success rate
2023-01-11 15:36:37 -05:00
Tomas Globis
66f82cb43f Use moving average to calculate tunnel creation success rate 2023-01-03 03:07:07 +03:00
40 changed files with 1318 additions and 855 deletions

View File

@@ -5,7 +5,7 @@ on: [push, pull_request]
jobs: jobs:
build-make: build-make:
name: Make with USE_UPNP=${{ matrix.with_upnp }} name: Make with USE_UPNP=${{ matrix.with_upnp }}
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
@@ -14,14 +14,13 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: install packages - name: install packages
run: | run: |
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get update sudo apt-get update
sudo apt-get install build-essential libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application - name: build application
run: make USE_UPNP=${{ matrix.with_upnp }} -j3 run: make USE_UPNP=${{ matrix.with_upnp }} -j3
build-cmake: build-cmake:
name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }} name: CMake with -DWITH_UPNP=${{ matrix.with_upnp }}
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: true fail-fast: true
matrix: matrix:
@@ -30,11 +29,26 @@ jobs:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
- name: install packages - name: install packages
run: | run: |
sudo add-apt-repository ppa:mhier/libboost-latest
sudo apt-get update sudo apt-get update
sudo apt-get install build-essential cmake libboost1.74-dev libminiupnpc-dev libssl-dev zlib1g-dev sudo apt-get install build-essential cmake libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application - name: build application
run: | run: |
cd build cd build
cmake -DWITH_UPNP=${{ matrix.with_upnp }} . cmake -DWITH_UPNP=${{ matrix.with_upnp }} .
make -j3 make -j3
build-static:
name: Static build with UPnP
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: install packages
run: |
sudo apt-get update
sudo apt-get install build-essential libboost-all-dev libminiupnpc-dev libssl-dev zlib1g-dev
- name: build application
run: make DEBUG=no USE_STATIC=yes USE_UPNP=yes -j`nproc`
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: i2pd-amd64-static
path: i2pd

View File

@@ -47,6 +47,10 @@ else
LD_DEBUG = -s LD_DEBUG = -s
endif endif
ifneq (, $(DESTDIR))
PREFIX = $(DESTDIR)
endif
ifneq (, $(findstring darwin, $(SYS))) ifneq (, $(findstring darwin, $(SYS)))
DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp DAEMON_SRC += $(DAEMON_SRC_DIR)/UnixDaemon.cpp
ifeq ($(HOMEBREW),1) ifeq ($(HOMEBREW),1)

View File

@@ -20,10 +20,10 @@ else ifeq ($(shell expr match ${CXXVER} "4\.[8-9]"),3) # gcc 4.8 - 4.9
else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6 else ifeq ($(shell expr match ${CXXVER} "[5-6]"),1) # gcc 5 - 6
NEEDED_CXXFLAGS += -std=c++11 NEEDED_CXXFLAGS += -std=c++11
LDLIBS = -latomic LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc 7 - 9 else ifeq ($(shell expr match ${CXXVER} "[7-9]"),1) # gcc 7 - 9
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic LDLIBS = -latomic
else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10 - 19 else ifeq ($(shell expr match ${CXXVER} "1[0-9]"),2) # gcc 10+
# NEEDED_CXXFLAGS += -std=c++20 # NEEDED_CXXFLAGS += -std=c++20
NEEDED_CXXFLAGS += -std=c++17 NEEDED_CXXFLAGS += -std=c++17
LDLIBS = -latomic LDLIBS = -latomic
@@ -33,34 +33,23 @@ endif
NEEDED_CXXFLAGS += -fPIC NEEDED_CXXFLAGS += -fPIC
ifeq ($(USE_STATIC),yes) LDLIBS += -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lssl -lcrypto -lz
# NOTE: on glibc you will get this warning:
# Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking
LIBDIR := /usr/lib/$(SYS)
LDLIBS += $(LIBDIR)/libboost_system.a
LDLIBS += $(LIBDIR)/libboost_date_time.a
LDLIBS += $(LIBDIR)/libboost_filesystem.a
LDLIBS += $(LIBDIR)/libboost_program_options.a
LDLIBS += $(LIBDIR)/libssl.a
LDLIBS += $(LIBDIR)/libcrypto.a
LDLIBS += $(LIBDIR)/libz.a
ifeq ($(USE_UPNP),yes)
LDLIBS += $(LIBDIR)/libminiupnpc.a
endif
LDLIBS += -lpthread -ldl
else
LDLIBS += -lcrypto -lssl -lz -lboost_system -lboost_date_time -lboost_filesystem -lboost_program_options -lpthread
ifeq ($(USE_UPNP),yes)
LDLIBS += -lminiupnpc
endif
endif
# UPNP Support (miniupnpc 1.5 and higher) # UPNP Support (miniupnpc 1.5 and higher)
ifeq ($(USE_UPNP),yes) ifeq ($(USE_UPNP),yes)
LDLIBS += -lminiupnpc
NEEDED_CXXFLAGS += -DUSE_UPNP NEEDED_CXXFLAGS += -DUSE_UPNP
endif endif
# NOTE: on glibc you will get this warning:
# Using 'getaddrinfo' in statically linked applications requires at runtime
# the shared libraries from the glibc version used for linking
ifeq ($(USE_STATIC),yes)
LDLIBS += -static -ldl -lpthread -static-libgcc -static-libstdc++
else
LDLIBS += -lpthread
endif
ifeq ($(USE_AESNI),yes) ifeq ($(USE_AESNI),yes)
ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
NEEDED_CXXFLAGS += -D__AES__ -maes NEEDED_CXXFLAGS += -D__AES__ -maes

View File

@@ -69,12 +69,12 @@ Build instructions:
**Supported systems:** **Supported systems:**
* 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) * GNU/Linux (Debian, Ubuntu, etc) - [![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/) * 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. * Alpine, ArchLinux, openSUSE, Gentoo, etc.
* Windows - [![Build on Windows](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/build-windows.yml) * 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) * Mac OS - [![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/) [![Build containers](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml) * Docker image - [![Build containers](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml/badge.svg)](https://github.com/PurpleI2P/i2pd/actions/workflows/docker.yml)
* Snap - [![i2pd](https://snapcraft.io/i2pd/badge.svg)](https://snapcraft.io/i2pd) [![i2pd](https://snapcraft.io/i2pd/trending.svg?name=0)](https://snapcraft.io/i2pd) * 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) * 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) * 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)

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -29,7 +29,7 @@ namespace util
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
SetConsoleCP(1251); SetConsoleCP(1251);
SetConsoleOutputCP(1251); SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian"); //setlocale(LC_ALL, "Russian");
setlocale(LC_TIME, "C"); setlocale(LC_TIME, "C");
i2p::log::SetThrowFunction ([](const std::string& s) i2p::log::SetThrowFunction ([](const std::string& s)
@@ -61,7 +61,7 @@ namespace util
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
SetConsoleCP(1251); SetConsoleCP(1251);
SetConsoleOutputCP(1251); SetConsoleOutputCP(1251);
setlocale(LC_ALL, "Russian"); //setlocale(LC_ALL, "Russian");
setlocale(LC_TIME, "C"); setlocale(LC_TIME, "C");
#ifdef WIN32_APP #ifdef WIN32_APP
if (!i2p::win32::StartWin32App ()) return false; if (!i2p::win32::StartWin32App ()) return false;

File diff suppressed because it is too large Load Diff

View File

@@ -122,6 +122,8 @@ port = 7070
## Path to web console, default "/" ## Path to web console, default "/"
# webroot = / # webroot = /
## Uncomment following lines to enable Web Console authentication ## Uncomment following lines to enable Web Console authentication
## You should not use Web Console via public networks without additional encryption.
## HTTP authentication is not encryption layer!
# auth = true # auth = true
# user = i2pd # user = i2pd
# pass = changeme # pass = changeme
@@ -139,6 +141,8 @@ port = 4444
## Optional keys file for proxy local destination ## Optional keys file for proxy local destination
# keys = http-proxy-keys.dat # keys = http-proxy-keys.dat
## Enable address helper for adding .i2p domains with "jump URLs" (default: true) ## Enable address helper for adding .i2p domains with "jump URLs" (default: true)
## You should disable this feature if your i2pd HTTP Proxy is public,
## because anyone could spoof the short domain via addresshelper and forward other users to phishing links
# addresshelper = true # addresshelper = true
## Address of a proxy server inside I2P, which is used to visit regular Internet ## Address of a proxy server inside I2P, which is used to visit regular Internet
# outproxy = http://false.i2p # outproxy = http://false.i2p

View File

@@ -1,293 +1,293 @@
/* /*
* Copyright (c) 2021-2022, The PurpleI2P Project * Copyright (c) 2021-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
* See full license text in LICENSE file at top of project tree * See full license text in LICENSE file at top of project tree
* *
****************************************************************** ******************************************************************
* *
* This is style sheet for webconsole, with @media selectors for adaptive * This is style sheet for webconsole, with @media selectors for adaptive
* view on desktop and mobile devices, respecting preferred user's color * view on desktop and mobile devices, respecting preferred user's color
* scheme used in system/browser. * scheme used in system/browser.
* *
* Minified copy of that style sheet is bundled inside i2pd sources. * Minified copy of that style sheet is bundled inside i2pd sources.
*/ */
:root { :root {
--main-bg-color: #fafafa; --main-bg-color: #fafafa;
--main-text-color: #103456; --main-text-color: #103456;
--main-link-color: #894c84; --main-link-color: #894c84;
--main-link-hover-color: #fafafa; --main-link-hover-color: #fafafa;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
:root { :root {
--main-bg-color: #242424; --main-bg-color: #242424;
--main-text-color: #17ab5c; --main-text-color: #17ab5c;
--main-link-color: #bf64b7; --main-link-color: #bf64b7;
--main-link-hover-color: #000000; --main-link-hover-color: #000000;
} }
} }
body { body {
font: 100%/1.5em sans-serif; font: 100%/1.5em sans-serif;
margin: 0; margin: 0;
padding: 1.5em; padding: 1.5em;
background: var(--main-bg-color); background: var(--main-bg-color);
color: var(--main-text-color); color: var(--main-text-color);
} }
a, .slide label { a, .slide label {
text-decoration: none; text-decoration: none;
color: var(--main-link-color); color: var(--main-link-color);
} }
a:hover, .slide label:hover, button[type=submit]:hover { a:hover, a.button.selected, .slide label:hover, button[type=submit]:hover {
color: var(--main-link-hover-color); color: var(--main-link-hover-color);
background: var(--main-link-color); background: var(--main-link-color);
} }
a.button { a.button {
appearance: button; appearance: button;
text-decoration: none; text-decoration: none;
padding: 0 5px; padding: 0 5px;
border: 1px solid var(--main-link-color); border: 1px solid var(--main-link-color);
} }
.header { .header {
font-size: 2.5em; font-size: 2.5em;
text-align: center; text-align: center;
margin: 1em 0; margin: 1em 0;
color: var(--main-link-color); color: var(--main-link-color);
} }
.wrapper { .wrapper {
margin: 0 auto; margin: 0 auto;
padding: 1em; padding: 1em;
max-width: 64em; max-width: 64em;
} }
.menu { .menu {
display: block; display: block;
float: left; float: left;
overflow: hidden; overflow: hidden;
padding: 4px; padding: 4px;
max-width: 12em; max-width: 12em;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.listitem { .listitem {
display: block; display: block;
font-family: monospace; font-family: monospace;
font-size: 1.2em; font-size: 1.2em;
white-space: nowrap; white-space: nowrap;
} }
.tableitem { .tableitem {
font-family: monospace; font-family: monospace;
font-size: 1.2em; font-size: 1.2em;
white-space: nowrap; white-space: nowrap;
} }
.content { .content {
float: left; float: left;
font-size: 1em; font-size: 1em;
margin-left: 2em; margin-left: 2em;
padding: 4px; padding: 4px;
max-width: 50em; max-width: 50em;
overflow: auto; overflow: auto;
} }
.tunnel.established { .tunnel.established {
color: #56B734; color: #56B734;
} }
.tunnel.expiring { .tunnel.expiring {
color: #D3AE3F; color: #D3AE3F;
} }
.tunnel.failed { .tunnel.failed {
color: #D33F3F; color: #D33F3F;
} }
.tunnel.building { .tunnel.building {
color: #434343; color: #434343;
} }
caption { caption {
font-size: 1.5em; font-size: 1.5em;
text-align: center; text-align: center;
color: var(--main-link-color); color: var(--main-link-color);
} }
table { table {
display: table; display: table;
border-collapse: collapse; border-collapse: collapse;
text-align: center; text-align: center;
} }
table.extaddr { table.extaddr {
text-align: left; text-align: left;
} }
table.services { table.services {
width: 100%; width: 100%;
} }
textarea { textarea {
background-color: var(--main-bg-color); background-color: var(--main-bg-color);
color: var(--main-text-color); color: var(--main-text-color);
word-break: break-all; word-break: break-all;
} }
.streamdest { .streamdest {
width: 120px; width: 120px;
max-width: 240px; max-width: 240px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.slide div.slidecontent, .slide [type="checkbox"] { .slide div.slidecontent, .slide [type="checkbox"] {
display: none; display: none;
} }
.slide [type="checkbox"]:checked ~ div.slidecontent { .slide [type="checkbox"]:checked ~ div.slidecontent {
display: block; display: block;
margin-top: 0; margin-top: 0;
padding: 0; padding: 0;
} }
.disabled { .disabled {
color: #D33F3F; color: #D33F3F;
} }
.enabled { .enabled {
color: #56B734; color: #56B734;
} }
button[type=submit] { button[type=submit] {
background-color: transparent; background-color: transparent;
color: var(--main-link-color); color: var(--main-link-color);
text-decoration: none; text-decoration: none;
padding: 5px; padding: 5px;
border: 1px solid var(--main-link-color); border: 1px solid var(--main-link-color);
font-size: 14px; font-size: 14px;
} }
input, select, select option { input, select, select option {
background-color: var(--main-bg-color); background-color: var(--main-bg-color);
color: var(--main-link-color); color: var(--main-link-color);
padding: 5px; padding: 5px;
border: 1px solid var(--main-link-color); border: 1px solid var(--main-link-color);
font-size: 14px; font-size: 14px;
} }
input:focus, select:focus, select option:focus { input:focus, select:focus, select option:focus {
outline: none; outline: none;
} }
input[type=number]::-webkit-inner-spin-button { input[type=number]::-webkit-inner-spin-button {
-webkit-appearance: none; -webkit-appearance: none;
} }
@media screen and (max-width: 1150px) { /* adaptive style */ @media screen and (max-width: 1150px) { /* adaptive style */
.wrapper { .wrapper {
max-width: 58em; max-width: 58em;
} }
.content { .content {
max-width: 40em; max-width: 40em;
} }
} }
@media screen and (max-width: 980px) { @media screen and (max-width: 980px) {
body { body {
font: 100%/1.2em sans-serif; font: 100%/1.2em sans-serif;
padding: 1.2em 0 0 0; padding: 1.2em 0 0 0;
} }
.menu { .menu {
width: 100%; width: 100%;
max-width: unset; max-width: unset;
display: block; display: block;
float: none; float: none;
position: unset; position: unset;
font-size: 16px; font-size: 16px;
text-align: center; text-align: center;
} }
.menu a, .commands a { .menu a, .commands a {
display: inline-block; display: inline-block;
padding: 4px; padding: 4px;
} }
.content { .content {
float: none; float: none;
margin-left: unset; margin-left: unset;
margin-top: 16px; margin-top: 16px;
max-width: 100%; max-width: 100%;
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
a, .slide label { a, .slide label {
display: block; display: block;
} }
.header { .header {
margin: unset; margin: unset;
font-size: 1.5em; font-size: 1.5em;
} }
small { small {
display: block display: block
} }
a.button { a.button {
appearance: button; appearance: button;
text-decoration: none; text-decoration: none;
margin-top: 10px; margin-top: 10px;
padding: 6px; padding: 6px;
border: 2px solid var(--main-link-color); border: 2px solid var(--main-link-color);
border-radius: 5px; border-radius: 5px;
width: -webkit-fill-available; width: -webkit-fill-available;
} }
input, select { input, select {
width: 35%; width: 35%;
text-align: center; text-align: center;
padding: 5px; padding: 5px;
border: 2px solid var(--main-link-color); border: 2px solid var(--main-link-color);
border-radius: 5px; border-radius: 5px;
font-size: 18px; font-size: 18px;
} }
table.extaddr { table.extaddr {
margin: auto; margin: auto;
text-align: unset; text-align: unset;
} }
textarea { textarea {
width: -webkit-fill-available; width: -webkit-fill-available;
height: auto; height: auto;
padding: 5px; padding: 5px;
border: 2px solid var(--main-link-color); border: 2px solid var(--main-link-color);
border-radius: 5px; border-radius: 5px;
font-size: 12px; font-size: 12px;
} }
button[type=submit] { button[type=submit] {
padding: 5px 15px; padding: 5px 15px;
background: transparent; background: transparent;
border: 2px solid var(--main-link-color); border: 2px solid var(--main-link-color);
cursor: pointer; cursor: pointer;
-webkit-border-radius: 5px; -webkit-border-radius: 5px;
border-radius: 5px; border-radius: 5px;
position: relative; position: relative;
height: 36px; height: 36px;
display: -webkit-inline-box; display: -webkit-inline-box;
margin-top: 10px; margin-top: 10px;
} }
} }

View File

@@ -103,18 +103,18 @@ namespace http {
int num; int num;
if ((num = seconds / 86400) > 0) { if ((num = seconds / 86400) > 0) {
s << num << " " << tr("day", "days", num) << ", "; s << ntr("%d day", "%d days", num, num) << ", ";
seconds -= num * 86400; seconds -= num * 86400;
} }
if ((num = seconds / 3600) > 0) { if ((num = seconds / 3600) > 0) {
s << num << " " << tr("hour", "hours", num) << ", "; s << ntr("%d hour", "%d hours", num, num) << ", ";
seconds -= num * 3600; seconds -= num * 3600;
} }
if ((num = seconds / 60) > 0) { if ((num = seconds / 60) > 0) {
s << num << " " << tr("minute", "minutes", num) << ", "; s << ntr("%d minute", "%d minutes", num, num) << ", ";
seconds -= num * 60; seconds -= num * 60;
} }
s << seconds << " " << tr("second", "seconds", seconds); s << ntr("%d second", "%d seconds", seconds, seconds);
} }
static void ShowTraffic (std::stringstream& s, uint64_t bytes) static void ShowTraffic (std::stringstream& s, uint64_t bytes)
@@ -122,11 +122,11 @@ namespace http {
s << std::fixed << std::setprecision(2); s << std::fixed << std::setprecision(2);
auto numKBytes = (double) bytes / 1024; auto numKBytes = (double) bytes / 1024;
if (numKBytes < 1024) if (numKBytes < 1024)
s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB"); s << tr(/* tr: Kibibyte */ "%.2f KiB", numKBytes);
else if (numKBytes < 1024 * 1024) else if (numKBytes < 1024 * 1024)
s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB"); s << tr(/* tr: Mebibyte */ "%.2f MiB", numKBytes / 1024);
else else
s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "GiB"); s << tr(/* tr: Gibibyte */ "%.2f GiB", numKBytes / 1024 / 1024);
} }
static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes) static void ShowTunnelDetails (std::stringstream& s, enum i2p::tunnel::TunnelState eState, bool explr, int bytes)
@@ -150,7 +150,8 @@ namespace http {
else stateText = tr("unknown"); else stateText = tr("unknown");
s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, "; s << "<span class=\"tunnel " << state << "\"> " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << "</span>, ";
s << " " << (int) (bytes / 1024) << "&nbsp;" << tr(/* tr: Kibibit */ "KiB") << "\r\n"; ShowTraffic(s, bytes);
s << "\r\n";
} }
static void SetLogLevel (const std::string& level) static void SetLogLevel (const std::string& level)
@@ -198,7 +199,7 @@ namespace http {
if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels()) if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSIT_TUNNELS << "\">" << tr("Transit Tunnels") << "</a><br>\r\n";
s << s <<
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr ("Transports") << "</a><br>\r\n" " <a href=\"" << webroot << "?page=" << HTTP_PAGE_TRANSPORTS << "\">" << tr("Transports") << "</a><br>\r\n"
" <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n"; " <a href=\"" << webroot << "?page=" << HTTP_PAGE_I2P_TUNNELS << "\">" << tr("I2P tunnels") << "</a><br>\r\n";
if (i2p::client::context.GetSAMBridge ()) if (i2p::client::context.GetSAMBridge ())
s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n"; s << " <a href=\"" << webroot << "?page=" << HTTP_PAGE_SAM_SESSIONS << "\">" << tr("SAM sessions") << "</a><br>\r\n";
@@ -247,7 +248,7 @@ namespace http {
break; break;
case eRouterErrorFullConeNAT: case eRouterErrorFullConeNAT:
s << " - " << tr("Full cone NAT"); s << " - " << tr("Full cone NAT");
break; break;
case eRouterErrorNoDescriptors: case eRouterErrorNoDescriptors:
s << " - " << tr("No Descriptors"); s << " - " << tr("No Descriptors");
break; break;
@@ -290,13 +291,13 @@ namespace http {
s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n"; s << "<b>" << tr("Tunnel creation success rate") << ":</b> " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%<br>\r\n";
s << "<b>" << tr("Received") << ":</b> "; s << "<b>" << tr("Received") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ());
s << " (" << (double) i2p::transport::transports.GetInBandwidth15s () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n"; s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetInBandwidth15s () / 1024) << ")<br>\r\n";
s << "<b>" << tr("Sent") << ":</b> "; s << "<b>" << tr("Sent") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ());
s << " (" << (double) i2p::transport::transports.GetOutBandwidth15s () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n"; s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetOutBandwidth15s () / 1024) << ")<br>\r\n";
s << "<b>" << tr("Transit") << ":</b> "; s << "<b>" << tr("Transit") << ":</b> ";
ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ());
s << " (" << (double) i2p::transport::transports.GetTransitBandwidth15s () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")<br>\r\n"; s << " (" << tr(/* tr: Kibibyte/s */ "%.2f KiB/s", (double) i2p::transport::transports.GetTransitBandwidth15s () / 1024) << ")<br>\r\n";
s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n"; s << "<b>" << tr("Data path") << ":</b> " << i2p::fs::GetUTF8DataDir() << "<br>\r\n";
s << "<div class='slide'>"; s << "<div class='slide'>";
if ((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { if ((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) {
@@ -338,7 +339,7 @@ namespace http {
s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n"; s << "<td>" << address->host.to_string() << ":" << address->port << "</td>\r\n";
else else
{ {
s << "<td>" << tr("supported"); s << "<td>" << tr(/* tr: Shown when router doesn't publish itself and have "Firewalled" state */ "supported");
if (address->port) if (address->port)
s << " :" << address->port; s << " :" << address->port;
s << "</td>\r\n"; s << "</td>\r\n";
@@ -466,7 +467,7 @@ namespace http {
} }
s << "&#8658; " << it->GetTunnelID () << ":me"; s << "&#8658; " << it->GetTunnelID () << ":me";
if (it->LatencyIsKnown()) if (it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )"; s << " ( " << tr(/* tr: Milliseconds */ "%dms", it->GetMeanLatency()) << " )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
@@ -486,22 +487,26 @@ namespace http {
); );
} }
if (it->LatencyIsKnown()) if (it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
} }
s << "<br>\r\n"; s << "<br>\r\n";
s << "<b>" << tr("Tags") << "</b><br>\r\n" << tr("Incoming") << ": <i>" << dest->GetNumIncomingTags () << "</i><br>\r\n"; s << "<b>" << tr("Tags") << "</b><br>\r\n"
<< tr("Incoming") << ": <i>" << dest->GetNumIncomingTags () << "</i><br>\r\n";
if (!dest->GetSessions ().empty ()) { if (!dest->GetSessions ().empty ()) {
std::stringstream tmp_s; uint32_t out_tags = 0; std::stringstream tmp_s; uint32_t out_tags = 0;
for (const auto& it: dest->GetSessions ()) { for (const auto& it: dest->GetSessions ()) {
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "</td><td>" << it.second->GetNumOutgoingTags () << "</td></tr>\r\n"; tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "</td><td>" << it.second->GetNumOutgoingTags () << "</td></tr>\r\n";
out_tags += it.second->GetNumOutgoingTags (); out_tags += it.second->GetNumOutgoingTags ();
} }
s << "<div class='slide'><label for='slide-tags'>" << tr("Outgoing") << ": <i>" << out_tags << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-tags\" />\r\n" s << "<div class='slide'><label for='slide-tags'>" << tr("Outgoing") << ": <i>" << out_tags << "</i></label>\r\n"
<< "<div class=\"slidecontent\">\r\n<table>\r\n<thead><th>" << tr("Destination") << "</th><th>" << tr("Amount") << "</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n"; << "<input type=\"checkbox\" id=\"slide-tags\" />\r\n"
<< "<div class=\"slidecontent\">\r\n"
<< "<table>\r\n<thead><th>" << tr("Destination") << "</th><th>" << tr("Amount") << "</th></thead>\r\n"
<< "<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
} else } else
s << tr("Outgoing") << ": <i>0</i><br>\r\n"; s << tr("Outgoing") << ": <i>0</i><br>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
@@ -516,8 +521,11 @@ namespace http {
tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "</td><td>" << it.second->GetState () << "</td></tr>\r\n"; tmp_s << "<tr><td>" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "</td><td>" << it.second->GetState () << "</td></tr>\r\n";
ecies_sessions++; ecies_sessions++;
} }
s << "<div class='slide'><label for='slide-ecies-sessions'>" << tr("Tags sessions") << ": <i>" << ecies_sessions << "</i></label>\r\n<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\r\n" s << "<div class='slide'><label for='slide-ecies-sessions'>" << tr("Tags sessions") << ": <i>" << ecies_sessions << "</i></label>\r\n"
<< "<div class=\"slidecontent\">\r\n<table>\r\n<thead><th>" << tr("Destination") << "</th><th>" << tr("Status") << "</th></thead>\r\n<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n"; << "<input type=\"checkbox\" id=\"slide-ecies-sessions\" />\r\n"
<< "<div class=\"slidecontent\">\r\n<table>\r\n"
<< "<thead><th>" << tr("Destination") << "</th><th>" << tr("Status") << "</th></thead>\r\n"
<< "<tbody class=\"tableitem\">\r\n" << tmp_s.str () << "</tbody></table>\r\n</div>\r\n</div>\r\n";
} else } else
s << tr("Tags sessions") << ": <i>0</i><br>\r\n"; s << tr("Tags sessions") << ": <i>0</i><br>\r\n";
s << "<br>\r\n"; s << "<br>\r\n";
@@ -642,7 +650,7 @@ namespace http {
} }
else if (!i2p::context.IsFloodfill ()) else if (!i2p::context.IsFloodfill ())
{ {
s << "<b>" << tr("LeaseSets") << ":</b> " << tr("not floodfill") << ".<br>\r\n"; s << "<b>" << tr("LeaseSets") << ":</b> " << tr(/* Message on LeaseSets page */ "floodfill mode is disabled") << ".<br>\r\n";
} }
else else
{ {
@@ -671,7 +679,7 @@ namespace http {
} }
s << "&#8658; " << it->GetTunnelID () << ":me"; s << "&#8658; " << it->GetTunnelID () << ":me";
if (it->LatencyIsKnown()) if (it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
@@ -691,7 +699,7 @@ namespace http {
); );
} }
if (it->LatencyIsKnown()) if (it->LatencyIsKnown())
s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; s << " ( " << tr("%dms", it->GetMeanLatency()) << " )";
ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ());
s << "</div>\r\n"; s << "</div>\r\n";
} }
@@ -729,19 +737,20 @@ namespace http {
s << "<br>\r\n<small>" << tr("<b>Note:</b> any action done here are not persistent and not changes your config files.") << "</small>\r\n<br>\r\n"; s << "<br>\r\n<small>" << tr("<b>Note:</b> any action done here are not persistent and not changes your config files.") << "</small>\r\n<br>\r\n";
auto loglevel = i2p::log::Logger().GetLogLevel();
s << "<b>" << tr("Logging level") << "</b><br>\r\n"; s << "<b>" << tr("Logging level") << "</b><br>\r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\"> none </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogNone ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=none&token=" << token << "\"> none </a> \r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\"> error </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogError ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=error&token=" << token << "\"> error </a> \r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\"> warn </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogWarning ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=warn&token=" << token << "\"> warn </a> \r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\"> info </a> \r\n"; s << " <a class=\"button" << (loglevel == eLogInfo ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=info&token=" << token << "\"> info </a> \r\n";
s << " <a class=\"button\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n"; s << " <a class=\"button" << (loglevel == eLogDebug ? " selected" : "") << "\" href=\"" << webroot << "?cmd=" << HTTP_COMMAND_LOGLEVEL << "&level=debug&token=" << token << "\"> debug </a><br>\r\n<br>\r\n";
uint16_t maxTunnels = GetMaxNumTransitTunnels (); uint16_t maxTunnels = GetMaxNumTransitTunnels ();
s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n"; s << "<b>" << tr("Transit tunnels limit") << "</b><br>\r\n";
s << "<form method=\"get\" action=\"" << webroot << "\">\r\n"; s << "<form method=\"get\" action=\"" << webroot << "\">\r\n";
s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n"; s << " <input type=\"hidden\" name=\"cmd\" value=\"" << HTTP_COMMAND_LIMITTRANSIT << "\">\r\n";
s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n"; s << " <input type=\"hidden\" name=\"token\" value=\"" << token << "\">\r\n";
s << " <input type=\"number\" min=\"0\" max=\"65535\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n"; s << " <input type=\"number\" min=\"0\" max=\"" << TRANSIT_TUNNELS_LIMIT <<"\" name=\"limit\" value=\"" << maxTunnels << "\">\r\n";
s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n"; s << " <button type=\"submit\">" << tr("Change") << "</button>\r\n";
s << "</form>\r\n<br>\r\n"; s << "</form>\r\n<br>\r\n";
@@ -771,23 +780,24 @@ namespace http {
{ {
if (i2p::tunnel::tunnels.CountTransitTunnels()) if (i2p::tunnel::tunnels.CountTransitTunnels())
{ {
s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n<div class=\"list\">\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b><br>\r\n";
s << "<table><thead><th>&#8658;</th><th>ID</th><th>&#8658;</th><th>" << tr("Amount") << "</th></thead><tbody class=\"tableitem\">";
for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ())
{ {
s << "<div class=\"listitem\">\r\n";
if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it)) if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelGateway>(it))
s << it->GetTunnelID () << " &#8658; "; s << "<tr><td></td><td>" << it->GetTunnelID () << "</td><td>&#8658;</td><td>";
else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it)) else if (std::dynamic_pointer_cast<i2p::tunnel::TransitTunnelEndpoint>(it))
s << " &#8658; " << it->GetTunnelID (); s << "<tr><td>&#8658;</td><td>" << it->GetTunnelID () << "</td><td></td><td>";
else else
s << " &#8658; " << it->GetTunnelID () << " &#8658; "; s << "<tr><td>&#8658;</td><td>" << it->GetTunnelID () << "</td><td>&#8658;</td><td>";
s << " " << it->GetNumTransmittedBytes () << "</div>\r\n"; ShowTraffic(s, it->GetNumTransmittedBytes ());
s << "</td></tr>\r\n";
} }
s << "</div>\r\n"; s << "</tbody></table>\r\n";
} }
else else
{ {
s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr("no transit tunnels currently built") << ".<br>\r\n"; s << "<b>" << tr("Transit Tunnels") << ":</b> " << tr(/* Message on transit tunnels page */ "no transit tunnels currently built") << ".<br>\r\n";
} }
} }
@@ -884,7 +894,7 @@ namespace http {
s << "</div>\r\n"; s << "</div>\r\n";
} }
else else
s << "<b>" << tr("SAM sessions") << ":</b> " << tr("no sessions currently running") << ".<br>\r\n"; s << "<b>" << tr("SAM sessions") << ":</b> " << tr(/* Message on SAM sessions page */ "no sessions currently running") << ".<br>\r\n";
} }
void ShowSAMSession (std::stringstream& s, const std::string& id) void ShowSAMSession (std::stringstream& s, const std::string& id)
@@ -993,7 +1003,7 @@ namespace http {
for (auto& it: serverForwards) for (auto& it: serverForwards)
{ {
auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); auto& ident = it.second->GetLocalDestination ()->GetIdentHash();
s << "<a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">"; s << "<div class=\"listitem\"><a href=\"" << webroot << "?page=" << HTTP_PAGE_LOCAL_DESTINATION << "&b32=" << ident.ToBase32 () << "\">";
s << it.second->GetName () << "</a> &#8656; "; s << it.second->GetName () << "</a> &#8656; ";
s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << i2p::client::context.GetAddressBook ().ToAddress(ident);
s << "</div>\r\n"<< std::endl; s << "</div>\r\n"<< std::endl;
@@ -1203,7 +1213,7 @@ namespace http {
url.parse_query(params); url.parse_query(params);
std::string webroot; i2p::config::GetOption("http.webroot", webroot); std::string webroot; i2p::config::GetOption("http.webroot", webroot);
std::string redirect = "5; url=" + webroot + "?page=commands"; std::string redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=commands";
std::string token = params["token"]; std::string token = params["token"];
if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ())
@@ -1277,20 +1287,20 @@ namespace http {
s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("StreamID can't be null") << "<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=local_destination&b32=" << b32 << "\">" << tr("Return to destination page") << "</a><br>\r\n";
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>"; s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
redirect = "5; url=" + webroot + "?page=local_destination&b32=" + b32; redirect = std::to_string(COMMAND_REDIRECT_TIMEOUT) + "; url=" + webroot + "?page=local_destination&b32=" + b32;
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
} }
else if (cmd == HTTP_COMMAND_LIMITTRANSIT) else if (cmd == HTTP_COMMAND_LIMITTRANSIT)
{ {
uint32_t limit = std::stoul(params["limit"], nullptr); uint32_t limit = std::stoul(params["limit"], nullptr);
if (limit > 0 && limit <= 65535) if (limit > 0 && limit <= TRANSIT_TUNNELS_LIMIT)
SetMaxNumTransitTunnels (limit); SetMaxNumTransitTunnels (limit);
else { else {
s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed 65535") << "\r\n<br>\r\n<br>\r\n"; s << "<b>" << tr("ERROR") << "</b>:&nbsp;" << tr("Transit tunnels count must not exceed %d", TRANSIT_TUNNELS_LIMIT) << "\r\n<br>\r\n<br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n"; s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a>\r\n<br>\r\n";
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>"; s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
return; return;
} }
@@ -1365,7 +1375,7 @@ namespace http {
s << "<b>" << tr("SUCCESS") << "</b>:&nbsp;" << tr("Command accepted") << "<br><br>\r\n"; s << "<b>" << tr("SUCCESS") << "</b>:&nbsp;" << tr("Command accepted") << "<br><br>\r\n";
s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a><br>\r\n"; s << "<a href=\"" << webroot << "?page=commands\">" << tr("Back to commands list") << "</a><br>\r\n";
s << "<p>" << tr("You will be redirected in 5 seconds") << "</b>"; s << "<p>" << tr("You will be redirected in %d seconds", COMMAND_REDIRECT_TIMEOUT) << "</b>";
res.add_header("Refresh", redirect.c_str()); res.add_header("Refresh", redirect.c_str());
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -24,6 +24,8 @@ namespace http
{ {
const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192; const size_t HTTP_CONNECTION_BUFFER_SIZE = 8192;
const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds const int TOKEN_EXPIRATION_TIMEOUT = 30; // in seconds
const int COMMAND_REDIRECT_TIMEOUT = 5; // in seconds
const int TRANSIT_TUNNELS_LIMIT = 65535;
class HTTPConnection: public std::enable_shared_from_this<HTTPConnection> class HTTPConnection: public std::enable_shared_from_this<HTTPConnection>
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -38,7 +38,7 @@ namespace http
"@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n" "@media (prefers-color-scheme: dark) { :root { --main-bg-color: #242424; --main-text-color: #17ab5c; --main-link-color: #bf64b7; --main-link-hover-color: #000000; } }\r\n"
"body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n" "body { font: 100%/1.5em sans-serif; margin: 0; padding: 1.5em; background: var(--main-bg-color); color: var(--main-text-color); }\r\n"
"a, .slide label { text-decoration: none; color: var(--main-link-color); }\r\n" "a, .slide label { text-decoration: none; color: var(--main-link-color); }\r\n"
"a:hover, .slide label:hover, button[type=submit]:hover { color: var(--main-link-hover-color); background: var(--main-link-color); }\r\n" "a:hover, a.button.selected, .slide label:hover, button[type=submit]:hover { color: var(--main-link-hover-color); background: var(--main-link-color); }\r\n"
"a.button { appearance: button; text-decoration: none; padding: 0 5px; border: 1px solid var(--main-link-color); }\r\n" "a.button { appearance: button; text-decoration: none; padding: 0 5px; border: 1px solid var(--main-link-color); }\r\n"
".header { font-size: 2.5em; text-align: center; margin: 1em 0; color: var(--main-link-color); }\r\n" ".header { font-size: 2.5em; text-align: center; margin: 1em 0; color: var(--main-link-color); }\r\n"
".wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n" ".wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n"

View File

@@ -64,10 +64,10 @@ namespace afrikaans // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"dag", "dae"}}, {"%d days", {"%d dag", "%d dae"}},
{"hours", {"uur", "ure"}}, {"%d hours", {"%d uur", "%d ure"}},
{"minutes", {"minuut", "minute"}}, {"%d minutes", {"%d minuut", "%d minute"}},
{"seconds", {"seconde", "sekondes"}}, {"%d seconds", {"%d seconde", "%d sekondes"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace armenian // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "ԿիԲ"}, {"%.2f KiB", "%.2f ԿիԲ"},
{"MiB", "ՄիԲ"}, {"%.2f MiB", "%.2f ՄիԲ"},
{"GiB", "ԳիԲ"}, {"%.2f GiB", "%.2f ԳիԲ"},
{"building", "կառուցվում է"}, {"building", "կառուցվում է"},
{"failed", "Անհաջող"}, {"failed", "Անհաջող"},
{"expiring", "Լրանում է"}, {"expiring", "Լրանում է"},
@@ -68,7 +68,7 @@ namespace armenian // language namespace
{"Family", "Խմբատեսակ"}, {"Family", "Խմբատեսակ"},
{"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"}, {"Tunnel creation success rate", "Հաջողությամբ կառուցված թունելներ"},
{"Received", "Ստացվել է"}, {"Received", "Ստացվել է"},
{"KiB/s", "ԿիԲ/վ"}, {"%.2f KiB/s", "%.2f ԿիԲ/վ"},
{"Sent", "Ուղարկվել է"}, {"Sent", "Ուղարկվել է"},
{"Transit", "Տարանցում"}, {"Transit", "Տարանցում"},
{"Data path", "Տվյալների ուղին"}, {"Data path", "Տվյալների ուղին"},
@@ -94,7 +94,7 @@ namespace armenian // language namespace
{"Type", "Տեսակը"}, {"Type", "Տեսակը"},
{"EncType", "Գաղտնագրի տեսակը"}, {"EncType", "Գաղտնագրի տեսակը"},
{"Inbound tunnels", "Մուտքային թունելներ"}, {"Inbound tunnels", "Մուտքային թունելներ"},
{"ms", "մլվ"}, {"%dms", "%dմլվ"},
{"Outbound tunnels", "Ելքային թունելներ"}, {"Outbound tunnels", "Ելքային թունելներ"},
{"Tags", "Թեգեր"}, {"Tags", "Թեգեր"},
{"Incoming", "Մուտքային"}, {"Incoming", "Մուտքային"},
@@ -198,10 +198,10 @@ namespace armenian // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"օր", "օր"}}, {"%d days", {"%d օր", "%d օր"}},
{"hours", {"ժամ", "ժամ"}}, {"%d hours", {"%d ժամ", "%d ժամ"}},
{"minutes", {"րոպե", "րոպե"}}, {"%d minutes", {"%d րոպե", "%d րոպե"}},
{"seconds", {"վարկյան", "վարկյան"}}, {"%d seconds", {"%d վարկյան", "%d վարկյան"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -32,9 +32,9 @@ namespace chinese // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "正在构建"}, {"building", "正在构建"},
{"failed", "连接失败"}, {"failed", "连接失败"},
{"expiring", "即将过期"}, {"expiring", "即将过期"},
@@ -70,7 +70,7 @@ namespace chinese // language namespace
{"Family", "家族"}, {"Family", "家族"},
{"Tunnel creation success rate", "隧道创建成功率"}, {"Tunnel creation success rate", "隧道创建成功率"},
{"Received", "已接收"}, {"Received", "已接收"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "已发送"}, {"Sent", "已发送"},
{"Transit", "中转"}, {"Transit", "中转"},
{"Data path", "数据文件路径"}, {"Data path", "数据文件路径"},
@@ -96,7 +96,7 @@ namespace chinese // language namespace
{"Type", "类型"}, {"Type", "类型"},
{"EncType", "加密类型"}, {"EncType", "加密类型"},
{"Inbound tunnels", "入站隧道"}, {"Inbound tunnels", "入站隧道"},
{"ms", "毫秒"}, {"%dms", "%d毫秒"},
{"Outbound tunnels", "出站隧道"}, {"Outbound tunnels", "出站隧道"},
{"Tags", "标签"}, {"Tags", "标签"},
{"Incoming", "传入"}, {"Incoming", "传入"},
@@ -200,10 +200,10 @@ namespace chinese // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {""}}, {"%d days", {"%d "}},
{"hours", {""}}, {"%d hours", {"%d "}},
{"minutes", {""}}, {"%d minutes", {"%d "}},
{"seconds", {""}}, {"%d seconds", {"%d "}},
{"", {""}}, {"", {""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace czech // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "vytváří se"}, {"building", "vytváří se"},
{"failed", "selhalo"}, {"failed", "selhalo"},
{"expiring", "končící"}, {"expiring", "končící"},
@@ -69,7 +69,7 @@ namespace czech // language namespace
{"Family", "Rodina"}, {"Family", "Rodina"},
{"Tunnel creation success rate", "Úspěšnost vytváření tunelů"}, {"Tunnel creation success rate", "Úspěšnost vytváření tunelů"},
{"Received", "Přijato"}, {"Received", "Přijato"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Odesláno"}, {"Sent", "Odesláno"},
{"Transit", "Tranzit"}, {"Transit", "Tranzit"},
{"Data path", "Cesta k data souborům"}, {"Data path", "Cesta k data souborům"},
@@ -95,7 +95,7 @@ namespace czech // language namespace
{"Type", "Typ"}, {"Type", "Typ"},
{"EncType", "EncType"}, {"EncType", "EncType"},
{"Inbound tunnels", "Příchozí tunely"}, {"Inbound tunnels", "Příchozí tunely"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Odchozí tunely"}, {"Outbound tunnels", "Odchozí tunely"},
{"Tags", "Štítky"}, {"Tags", "Štítky"},
{"Incoming", "Příchozí"}, {"Incoming", "Příchozí"},
@@ -199,10 +199,10 @@ namespace czech // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"den", "dny", "dní", "dní"}}, {"%d days", {"%d den", "%d dny", "%d dní", "%d dní"}},
{"hours", {"hodina", "hodiny", "hodin", "hodin"}}, {"%d hours", {"%d hodina", "%d hodiny", "%d hodin", "%d hodin"}},
{"minutes", {"minuta", "minuty", "minut", "minut"}}, {"%d minutes", {"%d minuta", "%d minuty", "%d minut", "%d minut"}},
{"seconds", {"vteřina", "vteřiny", "vteřin", "vteřin"}}, {"%d seconds", {"%d vteřina", "%d vteřiny", "%d vteřin", "%d vteřin"}},
{"", {"", "", "", ""}}, {"", {"", "", "", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace french // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "Kio"}, {"%.2f KiB", "%.2f Kio"},
{"MiB", "Mio"}, {"%.2f MiB", "%.2f Mio"},
{"GiB", "Gio"}, {"%.2f GiB", "%.2f Gio"},
{"building", "En construction"}, {"building", "En construction"},
{"failed", "échoué"}, {"failed", "échoué"},
{"expiring", "expiré"}, {"expiring", "expiré"},
@@ -69,7 +69,7 @@ namespace french // language namespace
{"Family", "Famille"}, {"Family", "Famille"},
{"Tunnel creation success rate", "Taux de succès de création de tunnels"}, {"Tunnel creation success rate", "Taux de succès de création de tunnels"},
{"Received", "Reçu"}, {"Received", "Reçu"},
{"KiB/s", "kio/s"}, {"%.2f KiB/s", "%.2f kio/s"},
{"Sent", "Envoyé"}, {"Sent", "Envoyé"},
{"Transit", "Transité"}, {"Transit", "Transité"},
{"Data path", "Emplacement des données"}, {"Data path", "Emplacement des données"},
@@ -93,7 +93,7 @@ namespace french // language namespace
{"Address", "Adresse"}, {"Address", "Adresse"},
{"Type", "Type"}, {"Type", "Type"},
{"Inbound tunnels", "Tunnels entrants"}, {"Inbound tunnels", "Tunnels entrants"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Tunnels sortants"}, {"Outbound tunnels", "Tunnels sortants"},
{"Tags", "Balises"}, {"Tags", "Balises"},
{"Incoming", "Entrant"}, {"Incoming", "Entrant"},
@@ -194,10 +194,10 @@ namespace french // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"jour", "jours"}}, {"%d days", {"%d jour", "%d jours"}},
{"hours", {"heure", "heures"}}, {"%d hours", {"%d heure", "%d heures"}},
{"minutes", {"minute", "minutes"}}, {"%d minutes", {"%d minute", "%d minutes"}},
{"seconds", {"seconde", "secondes"}}, {"%d seconds", {"%d seconde", "%d secondes"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace german // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "In Bau"}, {"building", "In Bau"},
{"failed", "fehlgeschlagen"}, {"failed", "fehlgeschlagen"},
{"expiring", "läuft ab"}, {"expiring", "läuft ab"},
@@ -69,7 +69,7 @@ namespace german // language namespace
{"Family", "Familie"}, {"Family", "Familie"},
{"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"}, {"Tunnel creation success rate", "Erfolgsrate der Tunnelerstellung"},
{"Received", "Eingegangen"}, {"Received", "Eingegangen"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Gesendet"}, {"Sent", "Gesendet"},
{"Transit", "Transit"}, {"Transit", "Transit"},
{"Data path", "Datenpfad"}, {"Data path", "Datenpfad"},
@@ -95,7 +95,7 @@ namespace german // language namespace
{"Type", "Typ"}, {"Type", "Typ"},
{"EncType", "Verschlüsselungstyp"}, {"EncType", "Verschlüsselungstyp"},
{"Inbound tunnels", "Eingehende Tunnel"}, {"Inbound tunnels", "Eingehende Tunnel"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Ausgehende Tunnel"}, {"Outbound tunnels", "Ausgehende Tunnel"},
{"Tags", "Tags"}, {"Tags", "Tags"},
{"Incoming", "Eingehend"}, {"Incoming", "Eingehend"},
@@ -199,10 +199,10 @@ namespace german // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"Tag", "Tage"}}, {"%d days", {"%d Tag", "%d Tage"}},
{"hours", {"Stunde", "Stunden"}}, {"%d hours", {"%d Stunde", "%d Stunden"}},
{"minutes", {"Minute", "Minuten"}}, {"%d minutes", {"%d Minute", "%d Minuten"}},
{"seconds", {"Sekunde", "Sekunden"}}, {"%d seconds", {"%d Sekunde", "%d Sekunden"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -1,11 +1,12 @@
/* /*
* Copyright (c) 2021-2022, The PurpleI2P Project * Copyright (c) 2021-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
* See full license text in LICENSE file at top of project tree * See full license text in LICENSE file at top of project tree
*/ */
#include <clocale>
#include "ClientContext.h" #include "ClientContext.h"
#include "I18N_langs.h" #include "I18N_langs.h"
#include "I18N.h" #include "I18N.h"
@@ -18,9 +19,15 @@ namespace i18n
{ {
const auto it = i2p::i18n::languages.find(lang); const auto it = i2p::i18n::languages.find(lang);
if (it == i2p::i18n::languages.end()) // fallback if (it == i2p::i18n::languages.end()) // fallback
{
i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale()); i2p::client::context.SetLanguage (i2p::i18n::english::GetLocale());
setlocale(LC_NUMERIC, "english");
}
else else
{
i2p::client::context.SetLanguage (it->second.LocaleFunc()); i2p::client::context.SetLanguage (it->second.LocaleFunc());
setlocale(LC_NUMERIC, lang.c_str()); // set decimal point based on language
}
} }
std::string translate (const std::string& arg) std::string translate (const std::string& arg)

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021-2022, The PurpleI2P Project * Copyright (c) 2021-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -67,17 +67,71 @@ namespace i18n
const std::map<std::string, std::vector<std::string>> m_Plurals; const std::map<std::string, std::vector<std::string>> m_Plurals;
std::function<int(int)> m_Formula; std::function<int(int)> m_Formula;
}; };
void SetLanguage(const std::string &lang); void SetLanguage(const std::string &lang);
std::string translate (const std::string& arg); std::string translate (const std::string& arg);
std::string translate (const std::string& arg, const std::string& arg2, const int& n); std::string translate (const std::string& arg, const std::string& arg2, const int& n);
} // i18n } // i18n
} // i2p } // i2p
template<typename... TArgs> /**
std::string tr (TArgs&&... args) * @brief Get translation of string
* @param arg String with message
*/
template<typename TValue>
std::string tr (TValue&& arg)
{ {
return i2p::i18n::translate(std::forward<TArgs>(args)...); return i2p::i18n::translate(std::forward<TValue>(arg));
}
/**
* @brief Get translation of string and format it
* @param arg String with message
* @param args Array of arguments for string formatting
*/
template<typename TValue, typename... TArgs>
std::string tr (TValue&& arg, TArgs&&... args)
{
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg));
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
size = size + 1;
std::string str(size, 0);
std::snprintf(&str.front(), size, tr_str.c_str(), std::forward<TArgs>(args)...);
return str;
}
/**
* @brief Get translation of string with plural forms
* @param arg String with message in singular form
* @param arg2 String with message in plural form
* @param n Integer, used for selection of form
*/
template<typename TValue, typename TValue2>
std::string ntr (TValue&& arg, TValue2&& arg2, int& n)
{
return i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
}
/**
* @brief Get translation of string with plural forms and format it
* @param arg String with message in singular form
* @param arg2 String with message in plural form
* @param n Integer, used for selection of form
* @param args Array of arguments for string formatting
*/
template<typename TValue, typename TValue2, typename... TArgs>
std::string ntr (TValue&& arg, TValue2&& arg2, int& n, TArgs&&... args)
{
std::string tr_str = i2p::i18n::translate(std::forward<TValue>(arg), std::forward<TValue2>(arg2), std::forward<int>(n));
size_t size = std::snprintf(NULL, 0, tr_str.c_str(), std::forward<TArgs>(args)...);
size = size + 1;
std::string str(size, 0);
std::snprintf(&str.front(), size, tr_str.c_str(), std::forward<TArgs>(args)...);
return str;
} }
#endif // __I18N_H__ #endif // __I18N_H__

View File

@@ -33,6 +33,7 @@ namespace i18n
namespace italian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace italian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace russian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace spanish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace spanish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace swedish { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace turkmen { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace ukrainian { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); } namespace uzbek { std::shared_ptr<const i2p::i18n::Locale> GetLocale (); }
@@ -52,6 +53,7 @@ namespace i18n
{ "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} }, { "italian", {"Italiano", "it", i2p::i18n::italian::GetLocale} },
{ "russian", {"Русский язык", "ru", i2p::i18n::russian::GetLocale} }, { "russian", {"Русский язык", "ru", i2p::i18n::russian::GetLocale} },
{ "spanish", {"Español", "es", i2p::i18n::spanish::GetLocale} }, { "spanish", {"Español", "es", i2p::i18n::spanish::GetLocale} },
{ "swedish", {"Svenska", "sv", i2p::i18n::swedish::GetLocale} },
{ "turkmen", {"Türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} }, { "turkmen", {"Türkmen dili", "tk", i2p::i18n::turkmen::GetLocale} },
{ "ukrainian", {"Украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} }, { "ukrainian", {"Украї́нська мо́ва", "uk", i2p::i18n::ukrainian::GetLocale} },
{ "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} }, { "uzbek", {"Oʻzbek", "uz", i2p::i18n::uzbek::GetLocale} },

View File

@@ -31,9 +31,9 @@ namespace italian // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "in costruzione"}, {"building", "in costruzione"},
{"failed", "fallito"}, {"failed", "fallito"},
{"expiring", "in scadenza"}, {"expiring", "in scadenza"},
@@ -69,7 +69,7 @@ namespace italian // language namespace
{"Family", "Famiglia"}, {"Family", "Famiglia"},
{"Tunnel creation success rate", "Percentuale di tunnel creati con successo"}, {"Tunnel creation success rate", "Percentuale di tunnel creati con successo"},
{"Received", "Ricevuti"}, {"Received", "Ricevuti"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Inviati"}, {"Sent", "Inviati"},
{"Transit", "Transitati"}, {"Transit", "Transitati"},
{"Data path", "Percorso dati"}, {"Data path", "Percorso dati"},
@@ -95,7 +95,7 @@ namespace italian // language namespace
{"Type", "Tipologia"}, {"Type", "Tipologia"},
{"EncType", "Tipo di crittografia"}, {"EncType", "Tipo di crittografia"},
{"Inbound tunnels", "Tunnel in entrata"}, {"Inbound tunnels", "Tunnel in entrata"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Tunnel in uscita"}, {"Outbound tunnels", "Tunnel in uscita"},
{"Tags", "Tag"}, {"Tags", "Tag"},
{"Incoming", "In entrata"}, {"Incoming", "In entrata"},
@@ -199,10 +199,10 @@ namespace italian // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"giorno", "giorni"}}, {"%d days", {"%d giorno", "%d giorni"}},
{"hours", {"ora", "ore"}}, {"%d hours", {"%d ora", "%d ore"}},
{"minutes", {"minuto", "minuti"}}, {"%d minutes", {"%d minuto", "%d minuti"}},
{"seconds", {"secondo", "secondi"}}, {"%d seconds", {"%d secondo", "%d secondi"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace russian // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "КиБ"}, {"%.2f KiB", "%.2f КиБ"},
{"MiB", "МиБ"}, {"%.2f MiB", "%.2f МиБ"},
{"GiB", "ГиБ"}, {"%.2f GiB", "%.2f ГиБ"},
{"building", "строится"}, {"building", "строится"},
{"failed", "неудачный"}, {"failed", "неудачный"},
{"expiring", "истекает"}, {"expiring", "истекает"},
@@ -68,7 +68,7 @@ namespace russian // language namespace
{"Family", "Семейство"}, {"Family", "Семейство"},
{"Tunnel creation success rate", "Успешно построенных туннелей"}, {"Tunnel creation success rate", "Успешно построенных туннелей"},
{"Received", "Получено"}, {"Received", "Получено"},
{"KiB/s", "КиБ/с"}, {"%.2f KiB/s", "%.2f КиБ/с"},
{"Sent", "Отправлено"}, {"Sent", "Отправлено"},
{"Transit", "Транзит"}, {"Transit", "Транзит"},
{"Data path", "Путь к данным"}, {"Data path", "Путь к данным"},
@@ -94,7 +94,7 @@ namespace russian // language namespace
{"Type", "Тип"}, {"Type", "Тип"},
{"EncType", "ТипШифр"}, {"EncType", "ТипШифр"},
{"Inbound tunnels", "Входящие туннели"}, {"Inbound tunnels", "Входящие туннели"},
{"ms", "мс"}, {"%dms", "%dмс"},
{"Outbound tunnels", "Исходящие туннели"}, {"Outbound tunnels", "Исходящие туннели"},
{"Tags", "Теги"}, {"Tags", "Теги"},
{"Incoming", "Входящие"}, {"Incoming", "Входящие"},
@@ -198,10 +198,10 @@ namespace russian // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"день", "дня", "дней"}}, {"%d days", {"%d день", "%d дня", "%d дней"}},
{"hours", {"час", "часа", "часов"}}, {"%d hours", {"%d час", "%d часа", "%d часов"}},
{"minutes", {"минуту", "минуты", "минут"}}, {"%d minutes", {"%d минуту", "%d минуты", "%d минут"}},
{"seconds", {"секунду", "секунды", "секунд"}}, {"%d seconds", {"%d секунду", "%d секунды", "%d секунд"}},
{"", {"", "", ""}}, {"", {"", "", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace spanish // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "pendiente"}, {"building", "pendiente"},
{"failed", "fallido"}, {"failed", "fallido"},
{"expiring", "expiró"}, {"expiring", "expiró"},
@@ -69,7 +69,7 @@ namespace spanish // language namespace
{"Family", "Familia"}, {"Family", "Familia"},
{"Tunnel creation success rate", "Tasa de éxito de creación de túneles"}, {"Tunnel creation success rate", "Tasa de éxito de creación de túneles"},
{"Received", "Recibido"}, {"Received", "Recibido"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Enviado"}, {"Sent", "Enviado"},
{"Transit", "Tránsito"}, {"Transit", "Tránsito"},
{"Data path", "Ruta de datos"}, {"Data path", "Ruta de datos"},
@@ -95,7 +95,7 @@ namespace spanish // language namespace
{"Type", "Tipo"}, {"Type", "Tipo"},
{"EncType", "TipoEncrip"}, {"EncType", "TipoEncrip"},
{"Inbound tunnels", "Túneles entrantes"}, {"Inbound tunnels", "Túneles entrantes"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Túneles salientes"}, {"Outbound tunnels", "Túneles salientes"},
{"Tags", "Etiquetas"}, {"Tags", "Etiquetas"},
{"Incoming", "Entrante"}, {"Incoming", "Entrante"},
@@ -199,10 +199,10 @@ namespace spanish // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"día", "días"}}, {"%d days", {"%d día", "%d días"}},
{"hours", {"hora", "horas"}}, {"%d hours", {"%d hora", "%d horas"}},
{"minutes", {"minuto", "minutos"}}, {"%d minutes", {"%d minuto", "%d minutos"}},
{"seconds", {"segundo", "segundos"}}, {"%d seconds", {"%d segundo", "%d segundos"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

217
i18n/Swedish.cpp Normal file
View File

@@ -0,0 +1,217 @@
/*
* Copyright (c) 2023, 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 <map>
#include <vector>
#include <string>
#include <memory>
#include "I18N.h"
// Swedish localization file
namespace i2p
{
namespace i18n
{
namespace swedish // language namespace
{
// language name in lowercase
static std::string language = "swedish";
// 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<std::string, std::string> strings
{
{"%.2f KiB", "%.2f KiB"},
{"%.2f MiB", "%.2f MiB"},
{"%.2f GiB", "%.2f GiB"},
{"building", "bygger"},
{"failed", "misslyckad"},
{"expiring", "utgår"},
{"established", "upprättad"},
{"unknown", "okänt"},
{"exploratory", "utforskande"},
{"Purple I2P Webconsole", "Purple I2P Webbkonsoll"},
{"<b>i2pd</b> webbkonsoll", "<b>i2pd</b>-Webbkonsoll"},
{"Main page", "Huvudsida"},
{"Router commands", "Routerkommandon"},
{"Local Destinations", "Lokala Platser"},
{"LeaseSets", "Hyresuppsättningar"},
{"Tunnels", "Tunnlar"},
{"Transit Tunnels", "Förmedlande Tunnlar"},
{"Transports", "Transporter"},
{"I2P tunnels", "I2P-tunnlar"},
{"SAM sessions", "SAM-perioder"},
{"ERROR", "FEL"},
{"OK", "OK"},
{"Testing", "Prövar"},
{"Firewalled", "Bakom Brandvägg"},
{"Unknown", "Okänt"},
{"Proxy", "Proxy"},
{"Mesh", "Mesh"},
{"Error", "Fel"},
{"Clock skew", "Tidsförskjutning"},
{"Offline", "Nedkopplad"},
{"Symmetric NAT", "Symmetrisk NAT"},
{"Uptime", "Upptid"},
{"Network status", "Nätverkstillstånd"},
{"Network status v6", "Nätverkstillstånd v6"},
{"Stopping in", "Avstängd om"},
{"Family", "Familj"},
{"Tunnel creation success rate", "Andel framgångsrika tunnlar"},
{"Received", "Mottaget"},
{"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Skickat"},
{"Transit", "Förmedlat"},
{"Data path", "Sökväg"},
{"Hidden content. Press on text to see.", "Dolt innehåll. Tryck för att visa."},
{"Router Ident", "Routeridentitet"},
{"Router Family", "Routerfamilj"},
{"Router Caps", "Routerbegränsningar"},
{"Version", "Version"},
{"Our external address", "Vår externa adress"},
{"supported", "stöds"},
{"Routers", "Routrar"},
{"Floodfills", "Översvämningsfyllare"},
{"Client Tunnels", "Klienttunnlar"},
{"Services", "Tjänster"},
{"Enabled", "Påslaget"},
{"Disabled", "Avslaget"},
{"Encrypted B33 address", "Krypterad B33-Adress"},
{"Address registration line", "Adressregistreringsrad"},
{"Domain", "Domän"},
{"Generate", "Skapa"},
{"<b>Note:</b> result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.", "<b>Uppmärksamma:</b> den resulterande strängen kan enbart användas för att registrera 2LD-domäner (exempel.i2p). För att registrera underdomäner, vänligen använd i2pd-tools."},
{"Address", "Adress"},
{"Type", "Typ"},
{"EncType", "EncTyp"},
{"Inbound tunnels", "Ingående Tunnlar"},
{"%dms", "%dms"},
{"Outbound tunnels", "Utgående Tunnlar"},
{"Tags", "Taggar"},
{"Incoming", "Ingående"},
{"Outgoing", "Utgående"},
{"Destination", "Plats"},
{"Amount", "Mängd"},
{"Incoming Tags", "Ingående Taggar"},
{"Tags sessions", "Tagg-perioder"},
{"Status", "Tillstånd"},
{"Local Destination", "Lokal Plats"},
{"Streams", "Strömmar"},
{"Close stream", "Stäng strömmen"},
{"I2CP session not found", "I2CP-period hittades inte"},
{"I2CP is not enabled", "I2CP är inte påslaget"},
{"Invalid", "Ogiltig"},
{"Store type", "Lagringstyp"},
{"Expires", "Utgångsdatum"},
{"Non Expired Leases", "Ickeutgångna Hyresuppsättningar"},
{"Gateway", "Gateway"},
{"TunnelID", "TunnelID"},
{"EndDate", "EndDate"},
{"not floodfill", "inte Översvämningsfyllare"},
{"Queue size", "Köstorlek"},
{"Run peer test", "Utför utsiktstest"},
{"Decline transit tunnels", "Avvisa förmedlande tunnlar"},
{"Accept transit tunnels", "Tillåt förmedlande tunnlar"},
{"Cancel graceful shutdown", "Avbryt välvillig avstängning"},
{"Start graceful shutdown", "Påbörja välvillig avstängning"},
{"Force shutdown", "Tvingad avstängning"},
{"Reload external CSS styles", "Ladda om externa CSS-stilar"},
{"<b>Note:</b> any action done here are not persistent and not changes your config files.", "<b>Uppmärksamma:</b> inga ändringar här är beständiga eller påverkar dina inställningsfiler."},
{"Logging level", "Protokollförningsnivå"},
{"Transit tunnels limit", "Begränsa förmedlande tunnlar"},
{"Change", "Ändra"},
{"Change language", "Ändra språk"},
{"no transit tunnels currently built", "inga förmedlande tunnlar har byggts"},
{"SAM disabled", "SAM avslaget"},
{"no sessions currently running", "inga perioder igång"},
{"SAM session not found", "SAM-perioder hittades ej"},
{"SAM Session", "SAM-period"},
{"Server Tunnels", "Värdtunnlar"},
{"Client Forwards", "Klientförpassningar"},
{"Server Forwards", "Värdförpassningar"},
{"Unknown page", "Okänd sida"},
{"Invalid token", "Ogiltig polett"},
{"SUCCESS", "FRAMGÅNG"},
{"Stream closed", "Ström stängd"},
{"Stream not found or already was closed", "Strömmen hittades inte eller var redan avslutad"},
{"Destination not found", "Plats hittades ej"},
{"StreamID can't be null", "Ström-ID kan inte vara null"},
{"Return to destination page", "Återvänd till platssidan"},
{"You will be redirected in 5 seconds", "Du omdirigeras inom fem sekunder"},
{"Transit tunnels count must not exceed 65535", "Förmedlande tunnlar får inte överstiga 65535"},
{"Back to commands list", "Tillbaka till kommandolistan"},
{"Register at reg.i2p", "Registrera vid reg.i2p"},
{"Description", "Beskrivning"},
{"A bit information about service on domain", "Ett stycke information om domänens tjänst"},
{"Submit", "Skicka"},
{"Domain can't end with .b32.i2p", "Domänen får inte sluta med .b32.i2p"},
{"Domain must end with .i2p", "Domänen måste sluta med .i2p"},
{"Such destination is not found", "En sådan plats hittas ej"},
{"Unknown command", "Okänt kommando"},
{"Command accepted", "Kommando accepterades"},
{"Proxy error", "Proxyfel"},
{"Proxy info", "Proxyinfo"},
{"Proxy error: Host not found", "Proxyfel: Värden hittades ej"},
{"Remote host not found in router's addressbook", "Främmande värd hittades inte i routerns adressbok"},
{"You may try to find this host on jump services below", "Du kan försöka att hitta värden genom hopptjänsterna nedan"},
{"Invalid request", "Ogiltig förfrågan"},
{"Proxy unable to parse your request", "Proxyt kan inte behandla din förfrågan"},
{"addresshelper is not supported", "adresshjälparen stöds ej"},
{"Host", "Värd"},
{"added to router's addressbook from helper", "tillagd i routerns adressbok från adresshjälparen"},
{"Click here to proceed:", "Tryck här för att fortsätta:"},
{"Continue", "Fortsätt"},
{"Addresshelper found", "Adresshjälpare hittad"},
{"already in router's addressbook", "finns redan i routerns adressbok"},
{"Click here to update record:", "Tryck här för att uppdatera:"},
{"invalid request uri", "ogiltig förfrågnings-URI"},
{"Can't detect destination host from request", "Kan inte upptäcka platsvärden från förfrågan"},
{"Outproxy failure", "Utproxyfel"},
{"bad outproxy settings", "ogiltig utproxyinställning"},
{"not inside I2P network, but outproxy is not enabled", "adressen är inte inom I2P-näverket, men utproxy är inte påslaget"},
{"unknown outproxy url", "okänt Utproxy-URL"},
{"cannot resolve upstream proxy", "hittar inte uppströmsproxyt"},
{"hostname too long", "värdnamnet är för långt"},
{"cannot connect to upstream socks proxy", "kan inte ansluta till uppströmsproxyt"},
{"Cannot negotiate with socks proxy", "Kan inte förhandla med socksproxyt"},
{"CONNECT error", "CONNECT-fel"},
{"Failed to Connect", "Anslutningen misslyckades"},
{"socks proxy error", "Socksproxyfel"},
{"failed to send request to upstream", "förfrågan uppströms kunde ej skickas"},
{"No Reply From socks proxy", "Fick inget svar från socksproxyt"},
{"cannot connect", "kan inte ansluta"},
{"http out proxy not implemented", "HTTP-Utproxy ej implementerat"},
{"cannot connect to upstream http proxy", "Kan inte ansluta till uppströms HTTP-proxy"},
{"Host is down", "Värden är nere"},
{"Can't create connection to requested host, it may be down. Please try again later.", "Kan inte ansluta till värden, den kan vara nere. Vänligen försök senare."},
{"", ""},
};
static std::map<std::string, std::vector<std::string>> plurals
{
{"%d days", {"%d Dag", "%d Dagar"}},
{"%d hours", {"%d Timme", "%d Timmar"}},
{"%d minutes", {"%d Minut", "%d Minuter"}},
{"%d seconds", {"%d Sekund", "%d Sekunder"}},
{"", {"", ""}},
};
std::shared_ptr<const i2p::i18n::Locale> GetLocale()
{
return std::make_shared<i2p::i18n::Locale>(language, strings, plurals, [] (int n)->int { return plural(n); });
}
} // language
} // i18n
} // i2p

View File

@@ -31,9 +31,9 @@ namespace turkmen // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "bina"}, {"building", "bina"},
{"failed", "şowsuz"}, {"failed", "şowsuz"},
{"expiring", "möhleti gutarýar"}, {"expiring", "möhleti gutarýar"},
@@ -68,7 +68,7 @@ namespace turkmen // language namespace
{"Family", "Maşgala"}, {"Family", "Maşgala"},
{"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"}, {"Tunnel creation success rate", "Gurlan teneller üstünlikli gurlan teneller"},
{"Received", "Alnan"}, {"Received", "Alnan"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Ýerleşdirildi"}, {"Sent", "Ýerleşdirildi"},
{"Transit", "Tranzit"}, {"Transit", "Tranzit"},
{"Data path", "Maglumat ýoly"}, {"Data path", "Maglumat ýoly"},
@@ -94,7 +94,7 @@ namespace turkmen // language namespace
{"Type", "Görnüş"}, {"Type", "Görnüş"},
{"EncType", "Şifrlemek görnüşi"}, {"EncType", "Şifrlemek görnüşi"},
{"Inbound tunnels", "Gelýän tuneller"}, {"Inbound tunnels", "Gelýän tuneller"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Çykýan tuneller"}, {"Outbound tunnels", "Çykýan tuneller"},
{"Tags", "Bellikler"}, {"Tags", "Bellikler"},
{"Incoming", "Gelýän"}, {"Incoming", "Gelýän"},
@@ -198,10 +198,10 @@ namespace turkmen // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"gün", "gün"}}, {"%d days", {"%d gün", "%d gün"}},
{"hours", {"sagat", "sagat"}}, {"%d hours", {"%d sagat", "%d sagat"}},
{"minutes", {"minut", "minut"}}, {"%d minutes", {"%d minut", "%d minut"}},
{"seconds", {"sekunt", "sekunt"}}, {"%d seconds", {"%d sekunt", "%d sekunt"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace ukrainian // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "КіБ"}, {"%.2f KiB", "%.2f КіБ"},
{"MiB", "МіБ"}, {"%.2f MiB", "%.2f МіБ"},
{"GiB", "ГіБ"}, {"%.2f GiB", "%.2f ГіБ"},
{"building", "будується"}, {"building", "будується"},
{"failed", "невдалий"}, {"failed", "невдалий"},
{"expiring", "завершується"}, {"expiring", "завершується"},
@@ -68,7 +68,7 @@ namespace ukrainian // language namespace
{"Family", "Сімейство"}, {"Family", "Сімейство"},
{"Tunnel creation success rate", "Успішно побудованих тунелів"}, {"Tunnel creation success rate", "Успішно побудованих тунелів"},
{"Received", "Отримано"}, {"Received", "Отримано"},
{"KiB/s", "КіБ/с"}, {"%.2f KiB/s", "%.2f КіБ/с"},
{"Sent", "Відправлено"}, {"Sent", "Відправлено"},
{"Transit", "Транзит"}, {"Transit", "Транзит"},
{"Data path", "Шлях до даних"}, {"Data path", "Шлях до даних"},
@@ -94,7 +94,7 @@ namespace ukrainian // language namespace
{"Type", "Тип"}, {"Type", "Тип"},
{"EncType", "ТипШифр"}, {"EncType", "ТипШифр"},
{"Inbound tunnels", "Вхідні тунелі"}, {"Inbound tunnels", "Вхідні тунелі"},
{"ms", "мс"}, {"%dms", "%dмс"},
{"Outbound tunnels", "Вихідні тунелі"}, {"Outbound tunnels", "Вихідні тунелі"},
{"Tags", "Теги"}, {"Tags", "Теги"},
{"Incoming", "Вхідні"}, {"Incoming", "Вхідні"},
@@ -198,10 +198,10 @@ namespace ukrainian // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"день", "дня", "днів"}}, {"%d days", {"%d день", "%d дня", "%d днів"}},
{"hours", {"годину", "години", "годин"}}, {"%d hours", {"%d годину", "%d години", "%d годин"}},
{"minutes", {"хвилину", "хвилини", "хвилин"}}, {"%d minutes", {"%d хвилину", "%d хвилини", "%d хвилин"}},
{"seconds", {"секунду", "секунди", "секунд"}}, {"%d seconds", {"%d секунду", "%d секунди", "%d секунд"}},
{"", {"", "", ""}}, {"", {"", "", ""}},
}; };

View File

@@ -31,9 +31,9 @@ namespace uzbek // language namespace
static std::map<std::string, std::string> strings static std::map<std::string, std::string> strings
{ {
{"KiB", "KiB"}, {"%.2f KiB", "%.2f KiB"},
{"MiB", "MiB"}, {"%.2f MiB", "%.2f MiB"},
{"GiB", "GiB"}, {"%.2f GiB", "%.2f GiB"},
{"building", "yaratilmoqda"}, {"building", "yaratilmoqda"},
{"failed", "muvaffaqiyatsiz"}, {"failed", "muvaffaqiyatsiz"},
{"expiring", "muddati tugaydi"}, {"expiring", "muddati tugaydi"},
@@ -68,7 +68,7 @@ namespace uzbek // language namespace
{"Family", "Oila"}, {"Family", "Oila"},
{"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"}, {"Tunnel creation success rate", "Tunnel yaratish muvaffaqiyat darajasi"},
{"Received", "Qabul qilindi"}, {"Received", "Qabul qilindi"},
{"KiB/s", "KiB/s"}, {"%.2f KiB/s", "%.2f KiB/s"},
{"Sent", "Yuborilgan"}, {"Sent", "Yuborilgan"},
{"Transit", "Tranzit"}, {"Transit", "Tranzit"},
{"Data path", "Ma'lumotlar joylanishi"}, {"Data path", "Ma'lumotlar joylanishi"},
@@ -94,7 +94,7 @@ namespace uzbek // language namespace
{"Type", "Turi"}, {"Type", "Turi"},
{"EncType", "ShifrlashTuri"}, {"EncType", "ShifrlashTuri"},
{"Inbound tunnels", "Kirish tunnellari"}, {"Inbound tunnels", "Kirish tunnellari"},
{"ms", "ms"}, {"%dms", "%dms"},
{"Outbound tunnels", "Chiquvchi tunnellar"}, {"Outbound tunnels", "Chiquvchi tunnellar"},
{"Tags", "Teglar"}, {"Tags", "Teglar"},
{"Incoming", "Kiruvchi"}, {"Incoming", "Kiruvchi"},
@@ -198,10 +198,10 @@ namespace uzbek // language namespace
static std::map<std::string, std::vector<std::string>> plurals static std::map<std::string, std::vector<std::string>> plurals
{ {
{"days", {"kun", "kun"}}, {"%d days", {"%d kun", "%d kun"}},
{"hours", {"soat", "soat"}}, {"%d hours", {"%d soat", "%d soat"}},
{"minutes", {"daqiqa", "daqiqa"}}, {"%d minutes", {"%d daqiqa", "%d daqiqa"}},
{"seconds", {"soniya", "soniya"}}, {"%d seconds", {"%d soniya", "%d soniya"}},
{"", {"", ""}}, {"", {"", ""}},
}; };

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -1138,19 +1138,25 @@ namespace client
template<typename Dest> template<typename Dest>
std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, int port) std::shared_ptr<i2p::stream::Stream> ClientDestination::CreateStreamSync (const Dest& dest, int port)
{ {
volatile bool done = false;
std::shared_ptr<i2p::stream::Stream> stream; std::shared_ptr<i2p::stream::Stream> stream;
std::condition_variable streamRequestComplete; std::condition_variable streamRequestComplete;
std::mutex streamRequestCompleteMutex; std::mutex streamRequestCompleteMutex;
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
CreateStream ( CreateStream (
[&streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s) [&done, &streamRequestComplete, &streamRequestCompleteMutex, &stream](std::shared_ptr<i2p::stream::Stream> s)
{ {
stream = s; stream = s;
std::unique_lock<std::mutex> l(streamRequestCompleteMutex); std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
streamRequestComplete.notify_all (); streamRequestComplete.notify_all ();
done = true;
}, },
dest, port); dest, port);
streamRequestComplete.wait (l); while (!done)
{
std::unique_lock<std::mutex> l(streamRequestCompleteMutex);
if (!done)
streamRequestComplete.wait (l);
}
return stream; return stream;
} }

View File

@@ -126,7 +126,7 @@ namespace log {
if (level == "none") { m_MinLevel = eLogNone; } if (level == "none") { m_MinLevel = eLogNone; }
else if (level == "error") { m_MinLevel = eLogError; } else if (level == "error") { m_MinLevel = eLogError; }
else if (level == "warn") { m_MinLevel = eLogWarning; } else if (level == "warn") { m_MinLevel = eLogWarning; }
else if (level == "info") { m_MinLevel = eLogInfo; } else if (level == "info") { m_MinLevel = eLogInfo; }
else if (level == "debug") { m_MinLevel = eLogDebug; } else if (level == "debug") { m_MinLevel = eLogDebug; }
else { else {
LogPrint(eLogError, "Log: Unknown loglevel: ", level); LogPrint(eLogError, "Log: Unknown loglevel: ", level);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2020, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -12,6 +12,7 @@
#include "Base.h" #include "Base.h"
#include "FS.h" #include "FS.h"
#include "Log.h" #include "Log.h"
#include "Timestamp.h"
#include "Profiling.h" #include "Profiling.h"
namespace i2p namespace i2p
@@ -22,6 +23,7 @@ namespace data
RouterProfile::RouterProfile (): RouterProfile::RouterProfile ():
m_LastUpdateTime (boost::posix_time::second_clock::local_time()), m_LastUpdateTime (boost::posix_time::second_clock::local_time()),
m_LastDeclineTime (0),
m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0), m_NumTunnelsAgreed (0), m_NumTunnelsDeclined (0), m_NumTunnelsNonReplied (0),
m_NumTimesTaken (0), m_NumTimesRejected (0) m_NumTimesTaken (0), m_NumTimesRejected (0)
{ {
@@ -131,15 +133,23 @@ namespace data
{ {
UpdateTime (); UpdateTime ();
if (ret > 0) if (ret > 0)
{
m_NumTunnelsDeclined++; m_NumTunnelsDeclined++;
m_LastDeclineTime = i2p::util::GetSecondsSinceEpoch ();
}
else else
{
m_NumTunnelsAgreed++; m_NumTunnelsAgreed++;
m_LastDeclineTime = 0;
}
} }
void RouterProfile::TunnelNonReplied () void RouterProfile::TunnelNonReplied ()
{ {
m_NumTunnelsNonReplied++; m_NumTunnelsNonReplied++;
UpdateTime (); UpdateTime ();
if (m_NumTunnelsNonReplied > 2*m_NumTunnelsAgreed && m_NumTunnelsNonReplied > 3)
m_LastDeclineTime = i2p::util::GetSecondsSinceEpoch ();
} }
bool RouterProfile::IsLowPartcipationRate () const bool RouterProfile::IsLowPartcipationRate () const
@@ -153,8 +163,18 @@ namespace data
return m_NumTunnelsNonReplied > 10*(total + 1); return m_NumTunnelsNonReplied > 10*(total + 1);
} }
bool RouterProfile::IsDeclinedRecently ()
{
if (!m_LastDeclineTime) return false;
auto ts = i2p::util::GetSecondsSinceEpoch ();
if (ts > m_LastDeclineTime + PEER_PROFILE_DECLINED_RECENTLY_INTERVAL)
m_LastDeclineTime = 0;
return m_LastDeclineTime;
}
bool RouterProfile::IsBad () bool RouterProfile::IsBad ()
{ {
if (IsDeclinedRecently ()) return true;
auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/; auto isBad = IsAlwaysDeclining () || IsLowPartcipationRate () /*|| IsLowReplyRate ()*/;
if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1)) if (isBad && m_NumTimesRejected > 10*(m_NumTimesTaken + 1))
{ {

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -31,6 +31,7 @@ namespace data
const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days) const int PEER_PROFILE_EXPIRATION_TIMEOUT = 72; // in hours (3 days)
const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day) const int PEER_PROFILE_AUTOCLEAN_TIMEOUT = 24 * 3600; // in seconds (1 day)
const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours) const int PEER_PROFILE_AUTOCLEAN_VARIANCE = 3 * 3600; // in seconds (3 hours)
const int PEER_PROFILE_DECLINED_RECENTLY_INTERVAL = 150; // in seconds (2.5 minutes)
class RouterProfile class RouterProfile
{ {
@@ -55,10 +56,12 @@ namespace data
bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; }; bool IsAlwaysDeclining () const { return !m_NumTunnelsAgreed && m_NumTunnelsDeclined >= 5; };
bool IsLowPartcipationRate () const; bool IsLowPartcipationRate () const;
bool IsLowReplyRate () const; bool IsLowReplyRate () const;
bool IsDeclinedRecently ();
private: private:
boost::posix_time::ptime m_LastUpdateTime; boost::posix_time::ptime m_LastUpdateTime; // TODO: use std::chrono
uint64_t m_LastDeclineTime; // in seconds
// participation // participation
uint32_t m_NumTunnelsAgreed; uint32_t m_NumTunnelsAgreed;
uint32_t m_NumTunnelsDeclined; uint32_t m_NumTunnelsDeclined;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -22,7 +22,7 @@ namespace transport
RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"), RunnableServiceWithWork ("SSU2"), m_ReceiveService ("SSU2r"),
m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()), m_SocketV4 (m_ReceiveService.GetService ()), m_SocketV6 (m_ReceiveService.GetService ()),
m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()), m_AddressV4 (boost::asio::ip::address_v4()), m_AddressV6 (boost::asio::ip::address_v6()),
m_TerminationTimer (GetService ()), m_ResendTimer (GetService ()), m_TerminationTimer (GetService ()), m_CleanupTimer (GetService ()), m_ResendTimer (GetService ()),
m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()), m_IntroducersUpdateTimer (GetService ()), m_IntroducersUpdateTimerV6 (GetService ()),
m_IsPublished (true), m_IsSyncClockFromPeers (true), m_IsThroughProxy (false) m_IsPublished (true), m_IsSyncClockFromPeers (true), m_IsThroughProxy (false)
{ {
@@ -109,6 +109,7 @@ namespace transport
m_ReceiveService.Start (); m_ReceiveService.Start ();
} }
ScheduleTermination (); ScheduleTermination ();
ScheduleCleanup ();
ScheduleResend (false); ScheduleResend (false);
} }
} }
@@ -118,6 +119,7 @@ namespace transport
if (IsRunning ()) if (IsRunning ())
{ {
m_TerminationTimer.cancel (); m_TerminationTimer.cancel ();
m_CleanupTimer.cancel ();
m_ResendTimer.cancel (); m_ResendTimer.cancel ();
m_IntroducersUpdateTimer.cancel (); m_IntroducersUpdateTimer.cancel ();
m_IntroducersUpdateTimerV6.cancel (); m_IntroducersUpdateTimerV6.cancel ();
@@ -807,6 +809,22 @@ namespace transport
it++; it++;
} }
ScheduleTermination ();
}
}
void SSU2Server::ScheduleCleanup ()
{
m_CleanupTimer.expires_from_now (boost::posix_time::seconds(SSU2_CLEANUP_INTERVAL));
m_CleanupTimer.async_wait (std::bind (&SSU2Server::HandleCleanupTimer,
this, std::placeholders::_1));
}
void SSU2Server::HandleCleanupTimer (const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
auto ts = i2p::util::GetSecondsSinceEpoch ();
for (auto it = m_Relays.begin (); it != m_Relays.begin ();) for (auto it = m_Relays.begin (); it != m_Relays.begin ();)
{ {
if (it->second && it->second->GetState () == eSSU2SessionStateTerminated) if (it->second && it->second->GetState () == eSSU2SessionStateTerminated)
@@ -830,13 +848,15 @@ namespace transport
else else
it++; it++;
} }
m_PacketsPool.CleanUpMt (); m_PacketsPool.CleanUpMt ();
m_SentPacketsPool.CleanUp (); m_SentPacketsPool.CleanUp ();
ScheduleTermination (); m_IncompleteMessagesPool.CleanUp ();
} m_FragmentsPool.CleanUp ();
} ScheduleCleanup ();
}
}
void SSU2Server::ScheduleResend (bool more) void SSU2Server::ScheduleResend (bool more)
{ {
m_ResendTimer.expires_from_now (boost::posix_time::milliseconds (more ? SSU2_RESEND_CHECK_MORE_TIMEOUT : m_ResendTimer.expires_from_now (boost::posix_time::milliseconds (more ? SSU2_RESEND_CHECK_MORE_TIMEOUT :

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2022, The PurpleI2P Project * Copyright (c) 2022-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -18,7 +18,8 @@ namespace i2p
{ {
namespace transport namespace transport
{ {
const int SSU2_TERMINATION_CHECK_TIMEOUT = 30; // in seconds const int SSU2_TERMINATION_CHECK_TIMEOUT = 25; // in seconds
const int SSU2_CLEANUP_INTERVAL = 72; // in seconds
const int SSU2_RESEND_CHECK_TIMEOUT = 400; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT = 400; // in milliseconds
const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 100; // in milliseconds const int SSU2_RESEND_CHECK_TIMEOUT_VARIANCE = 100; // in milliseconds
const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds const int SSU2_RESEND_CHECK_MORE_TIMEOUT = 10; // in milliseconds
@@ -97,7 +98,9 @@ namespace transport
void RescheduleIntroducersUpdateTimerV6 (); void RescheduleIntroducersUpdateTimerV6 ();
i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; }; i2p::util::MemoryPool<SSU2SentPacket>& GetSentPacketsPool () { return m_SentPacketsPool; };
i2p::util::MemoryPool<SSU2IncompleteMessage>& GetIncompleteMessagesPool () { return m_IncompleteMessagesPool; };
i2p::util::MemoryPool<SSU2IncompleteMessage::Fragment>& GetFragmentsPool () { return m_FragmentsPool; };
private: private:
boost::asio::ip::udp::socket& OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint); boost::asio::ip::udp::socket& OpenSocket (const boost::asio::ip::udp::endpoint& localEndpoint);
@@ -111,6 +114,9 @@ namespace transport
void ScheduleTermination (); void ScheduleTermination ();
void HandleTerminationTimer (const boost::system::error_code& ecode); void HandleTerminationTimer (const boost::system::error_code& ecode);
void ScheduleCleanup ();
void HandleCleanupTimer (const boost::system::error_code& ecode);
void ScheduleResend (bool more); void ScheduleResend (bool more);
void HandleResendTimer (const boost::system::error_code& ecode); void HandleResendTimer (const boost::system::error_code& ecode);
@@ -147,7 +153,9 @@ namespace transport
std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to std::list<i2p::data::IdentHash> m_Introducers, m_IntroducersV6; // introducers we are connected to
i2p::util::MemoryPoolMt<Packet> m_PacketsPool; i2p::util::MemoryPoolMt<Packet> m_PacketsPool;
i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool; i2p::util::MemoryPool<SSU2SentPacket> m_SentPacketsPool;
boost::asio::deadline_timer m_TerminationTimer, m_ResendTimer, i2p::util::MemoryPool<SSU2IncompleteMessage> m_IncompleteMessagesPool;
i2p::util::MemoryPool<SSU2IncompleteMessage::Fragment> m_FragmentsPool;
boost::asio::deadline_timer m_TerminationTimer, m_CleanupTimer, m_ResendTimer,
m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6; m_IntroducersUpdateTimer, m_IntroducersUpdateTimerV6;
std::shared_ptr<SSU2Session> m_LastSession; std::shared_ptr<SSU2Session> m_LastSession;
bool m_IsPublished; // if we maintain introducers bool m_IsPublished; // if we maintain introducers

View File

@@ -32,7 +32,52 @@ namespace transport
nextFragmentNum++; nextFragmentNum++;
} }
bool SSU2IncompleteMessage::ConcatOutOfSequenceFragments ()
{
bool isLast = false;
while (outOfSequenceFragments)
{
if (outOfSequenceFragments->fragmentNum == nextFragmentNum)
{
AttachNextFragment (outOfSequenceFragments->buf, outOfSequenceFragments->len);
isLast = outOfSequenceFragments->isLast;
if (isLast)
outOfSequenceFragments = nullptr;
else
outOfSequenceFragments = outOfSequenceFragments->next;
}
else
break;
}
return isLast;
}
void SSU2IncompleteMessage::AddOutOfSequenceFragment (std::shared_ptr<SSU2IncompleteMessage::Fragment> fragment)
{
if (!fragment || !fragment->fragmentNum) return; // fragment 0 not allowed
if (fragment->fragmentNum < nextFragmentNum) return; // already processed
if (!outOfSequenceFragments)
outOfSequenceFragments = fragment;
else
{
auto frag = outOfSequenceFragments;
std::shared_ptr<Fragment> prev;
do
{
if (fragment->fragmentNum < frag->fragmentNum) break; // found
if (fragment->fragmentNum == frag->fragmentNum) return; // duplicate
prev = frag; frag = frag->next;
}
while (frag);
fragment->next = frag;
if (prev)
prev->next = fragment;
else
outOfSequenceFragments = fragment;
}
lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
}
SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter, SSU2Session::SSU2Session (SSU2Server& server, std::shared_ptr<const i2p::data::RouterInfo> in_RemoteRouter,
std::shared_ptr<const i2p::data::RouterInfo::Address> addr): std::shared_ptr<const i2p::data::RouterInfo::Address> addr):
TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT), TransportSession (in_RemoteRouter, SSU2_CONNECT_TIMEOUT),
@@ -1727,13 +1772,13 @@ namespace transport
} }
else else
{ {
m = std::make_shared<SSU2IncompleteMessage>(); m = m_Server.GetIncompleteMessagesPool ().AcquireShared ();
m_IncompleteMessages.emplace (msgID, m); m_IncompleteMessages.emplace (msgID, m);
} }
m->msg = msg; m->msg = msg;
m->nextFragmentNum = 1; m->nextFragmentNum = 1;
m->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch (); m->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
if (found && ConcatOutOfSequenceFragments (m)) if (found && m->ConcatOutOfSequenceFragments ())
{ {
// we have all follow-on fragments already // we have all follow-on fragments already
m->msg->FromNTCP2 (); m->msg->FromNTCP2 ();
@@ -1746,11 +1791,17 @@ namespace transport
{ {
if (len < 5) return; if (len < 5) return;
uint8_t fragmentNum = buf[0] >> 1; uint8_t fragmentNum = buf[0] >> 1;
if (!fragmentNum || fragmentNum >= SSU2_MAX_NUM_FRAGMENTS)
{
LogPrint (eLogWarning, "SSU2: Invalid follow-on fragment num ", fragmentNum);
return;
}
bool isLast = buf[0] & 0x01; bool isLast = buf[0] & 0x01;
uint32_t msgID; memcpy (&msgID, buf + 1, 4); uint32_t msgID; memcpy (&msgID, buf + 1, 4);
auto it = m_IncompleteMessages.find (msgID); auto it = m_IncompleteMessages.find (msgID);
if (it != m_IncompleteMessages.end ()) if (it != m_IncompleteMessages.end ())
{ {
if (fragmentNum < it->second->nextFragmentNum) return; // duplicate
if (it->second->nextFragmentNum == fragmentNum && fragmentNum < SSU2_MAX_NUM_FRAGMENTS && if (it->second->nextFragmentNum == fragmentNum && fragmentNum < SSU2_MAX_NUM_FRAGMENTS &&
it->second->msg) it->second->msg)
{ {
@@ -1764,7 +1815,7 @@ namespace transport
} }
else else
{ {
if (ConcatOutOfSequenceFragments (it->second)) if (it->second->ConcatOutOfSequenceFragments ())
{ {
HandleI2NPMsg (std::move (it->second->msg)); HandleI2NPMsg (std::move (it->second->msg));
m_IncompleteMessages.erase (it); m_IncompleteMessages.erase (it);
@@ -1778,38 +1829,17 @@ namespace transport
else else
{ {
// follow-on fragment before first fragment // follow-on fragment before first fragment
auto msg = std::make_shared<SSU2IncompleteMessage> (); auto msg = m_Server.GetIncompleteMessagesPool ().AcquireShared ();
msg->nextFragmentNum = 0; msg->nextFragmentNum = 0;
it = m_IncompleteMessages.emplace (msgID, msg).first; it = m_IncompleteMessages.emplace (msgID, msg).first;
} }
// insert out of sequence fragment // insert out of sequence fragment
if (fragmentNum >= SSU2_MAX_NUM_FRAGMENTS) auto fragment = m_Server.GetFragmentsPool ().AcquireShared ();
{
LogPrint (eLogWarning, "SSU2: Fragment number ", fragmentNum, " exceeds ", SSU2_MAX_NUM_FRAGMENTS);
return;
}
auto fragment = std::make_shared<SSU2IncompleteMessage::Fragment> ();
memcpy (fragment->buf, buf + 5, len -5); memcpy (fragment->buf, buf + 5, len -5);
fragment->len = len - 5; fragment->len = len - 5;
fragment->fragmentNum = fragmentNum;
fragment->isLast = isLast; fragment->isLast = isLast;
it->second->outOfSequenceFragments.emplace (fragmentNum, fragment); it->second->AddOutOfSequenceFragment (fragment);
it->second->lastFragmentInsertTime = i2p::util::GetSecondsSinceEpoch ();
}
bool SSU2Session::ConcatOutOfSequenceFragments (std::shared_ptr<SSU2IncompleteMessage> m)
{
if (!m) return false;
bool isLast = false;
for (auto it = m->outOfSequenceFragments.begin (); it != m->outOfSequenceFragments.end ();)
if (it->first == m->nextFragmentNum)
{
m->AttachNextFragment (it->second->buf, it->second->len);
isLast = it->second->isLast;
it = m->outOfSequenceFragments.erase (it);
}
else
break;
return isLast;
} }
void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len) void SSU2Session::HandleRelayRequest (const uint8_t * buf, size_t len)
@@ -2271,23 +2301,16 @@ namespace transport
{ {
if (!msg) return; if (!msg) return;
int32_t msgID = msg->GetMsgID (); int32_t msgID = msg->GetMsgID ();
#if __cplusplus >= 202002L // C++ 20 or higher if (!msg->IsExpired ())
if (!m_ReceivedI2NPMsgIDs.contains (msgID)) {
#else // m_LastActivityTimestamp is updated in ProcessData before
if (!m_ReceivedI2NPMsgIDs.count (msgID)) if (m_ReceivedI2NPMsgIDs.emplace (msgID, (uint32_t)m_LastActivityTimestamp).second)
#endif
{
if (!msg->IsExpired ())
{
// m_LastActivityTimestamp is updated in ProcessData before
m_ReceivedI2NPMsgIDs.emplace (msgID, (uint32_t)m_LastActivityTimestamp);
m_Handler.PutNextMessage (std::move (msg)); m_Handler.PutNextMessage (std::move (msg));
}
else else
LogPrint (eLogDebug, "SSU2: Message ", msgID, " expired"); LogPrint (eLogDebug, "SSU2: Message ", msgID, " already received");
} }
else else
LogPrint (eLogDebug, "SSU2: Message ", msgID, " already received"); LogPrint (eLogDebug, "SSU2: Message ", msgID, " expired");
} }
bool SSU2Session::ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep) bool SSU2Session::ExtractEndpoint (const uint8_t * buf, size_t size, boost::asio::ip::udp::endpoint& ep)

View File

@@ -171,15 +171,19 @@ namespace transport
{ {
uint8_t buf[SSU2_MAX_PACKET_SIZE]; uint8_t buf[SSU2_MAX_PACKET_SIZE];
size_t len; size_t len;
int fragmentNum;
bool isLast; bool isLast;
std::shared_ptr<Fragment> next;
}; };
std::shared_ptr<I2NPMessage> msg; std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum; int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds uint32_t lastFragmentInsertTime; // in seconds
std::map<int, std::shared_ptr<Fragment> > outOfSequenceFragments; std::shared_ptr<Fragment> outOfSequenceFragments; // #1 and more
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
bool ConcatOutOfSequenceFragments (); // true if message complete
void AddOutOfSequenceFragment (std::shared_ptr<Fragment> fragment);
}; };
struct SSU2SentPacket struct SSU2SentPacket
@@ -306,7 +310,6 @@ namespace transport
bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate bool UpdateReceivePacketNum (uint32_t packetNum); // for Ack, returns false if duplicate
void HandleFirstFragment (const uint8_t * buf, size_t len); void HandleFirstFragment (const uint8_t * buf, size_t len);
void HandleFollowOnFragment (const uint8_t * buf, size_t len); void HandleFollowOnFragment (const uint8_t * buf, size_t len);
bool ConcatOutOfSequenceFragments (std::shared_ptr<SSU2IncompleteMessage> m); // true if message complete
void HandleRelayRequest (const uint8_t * buf, size_t len); void HandleRelayRequest (const uint8_t * buf, size_t len);
void HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0); void HandleRelayIntro (const uint8_t * buf, size_t len, int attempts = 0);
void HandleRelayResponse (const uint8_t * buf, size_t len); void HandleRelayResponse (const uint8_t * buf, size_t len);
@@ -342,7 +345,7 @@ namespace transport
uint32_t m_SendPacketNum, m_ReceivePacketNum; uint32_t m_SendPacketNum, m_ReceivePacketNum;
std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num std::set<uint32_t> m_OutOfSequencePackets; // packet nums > receive packet num
std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet std::map<uint32_t, std::shared_ptr<SSU2SentPacket> > m_SentPackets; // packetNum -> packet
std::map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // I2NP std::unordered_map<uint32_t, std::shared_ptr<SSU2IncompleteMessage> > m_IncompleteMessages; // msgID -> I2NP
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_RelaySessions; // nonce->(Alice, timestamp) for Bob or nonce->(Charlie, timestamp) for Alice
std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions std::map<uint32_t, std::pair <std::shared_ptr<SSU2Session>, uint64_t > > m_PeerTests; // same as for relay sessions
std::list<std::shared_ptr<I2NPMessage> > m_SendQueue; std::list<std::shared_ptr<I2NPMessage> > m_SendQueue;

View File

@@ -332,8 +332,7 @@ namespace tunnel
Tunnels tunnels; Tunnels tunnels;
Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr), Tunnels::Tunnels (): m_IsRunning (false), m_Thread (nullptr),
m_NumSuccesiveTunnelCreations (0), m_NumFailedTunnelCreations (0) m_TunnelCreationSuccessRate (TCSR_START_VALUE), m_TunnelCreationAttemptsNum(0) {
{
} }
Tunnels::~Tunnels () Tunnels::~Tunnels ()
@@ -634,7 +633,7 @@ namespace tunnel
} }
// delete // delete
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; FailedTunnelCreation();
} }
else else
++it; ++it;
@@ -642,7 +641,7 @@ namespace tunnel
case eTunnelStateBuildFailed: case eTunnelStateBuildFailed:
LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted"); LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted");
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumFailedTunnelCreations++; FailedTunnelCreation();
break; break;
case eTunnelStateBuildReplyReceived: case eTunnelStateBuildReplyReceived:
// intermediate state, will be either established of build failed // intermediate state, will be either established of build failed
@@ -651,7 +650,7 @@ namespace tunnel
default: default:
// success // success
it = pendingTunnels.erase (it); it = pendingTunnels.erase (it);
m_NumSuccesiveTunnelCreations++; SuccesiveTunnelCreation();
} }
} }
} }

View File

@@ -48,6 +48,9 @@ namespace tunnel
const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12 const size_t I2NP_TUNNEL_MESSAGE_SIZE = TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + 34; // reserved for alignment and NTCP 16 + 6 + 12
const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6 const size_t I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE = 2*TUNNEL_DATA_MSG_SIZE + I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE + 28; // reserved for alignment and NTCP 16 + 6 + 6
const double TCSR_SMOOTHING_CONSTANT = 0.0005; // smoothing constant in exponentially weighted moving average
const double TCSR_START_VALUE = 0.1; // start value of tunnel creation success rate
enum TunnelState enum TunnelState
{ {
eTunnelStatePending, eTunnelStatePending,
@@ -267,8 +270,19 @@ namespace tunnel
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE> > m_I2NPTunnelEndpointMessagesMemoryPool; i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_ENPOINT_MESSAGE_SIZE> > m_I2NPTunnelEndpointMessagesMemoryPool;
i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_MESSAGE_SIZE> > m_I2NPTunnelMessagesMemoryPool; i2p::util::MemoryPoolMt<I2NPMessageBuffer<I2NP_TUNNEL_MESSAGE_SIZE> > m_I2NPTunnelMessagesMemoryPool;
// some stats // Calculating of tunnel creation success rate
int m_NumSuccesiveTunnelCreations, m_NumFailedTunnelCreations; // A modified version of the EWMA algorithm, where alpha is increased at the beginning to accelerate similarity
void SuccesiveTunnelCreation() {
double alpha = TCSR_SMOOTHING_CONSTANT + (1 - TCSR_SMOOTHING_CONSTANT)/++m_TunnelCreationAttemptsNum;
m_TunnelCreationSuccessRate = alpha * 1 + (1 - alpha) * m_TunnelCreationSuccessRate;
};
void FailedTunnelCreation() {
double alpha = TCSR_SMOOTHING_CONSTANT + (1 - TCSR_SMOOTHING_CONSTANT)/++m_TunnelCreationAttemptsNum;
m_TunnelCreationSuccessRate = alpha * 0 + (1 - alpha) * m_TunnelCreationSuccessRate;
};
double m_TunnelCreationSuccessRate;
int m_TunnelCreationAttemptsNum;
public: public:
@@ -282,11 +296,7 @@ namespace tunnel
size_t CountOutboundTunnels() const; size_t CountOutboundTunnels() const;
int GetQueueSize () { return m_Queue.GetSize (); }; int GetQueueSize () { return m_Queue.GetSize (); };
int GetTunnelCreationSuccessRate () const // in percents int GetTunnelCreationSuccessRate () const { return std::round(m_TunnelCreationSuccessRate * 100); } // in percents
{
int totalNum = m_NumSuccesiveTunnelCreations + m_NumFailedTunnelCreations;
return totalNum ? m_NumSuccesiveTunnelCreations*100/totalNum : 0;
}
}; };
extern Tunnels tunnels; extern Tunnels tunnels;

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -321,7 +321,7 @@ namespace proxy {
if (!m_Addresshelper) if (!m_Addresshelper)
{ {
LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected"); LogPrint(eLogWarning, "HTTPProxy: Addresshelper request rejected");
GenericProxyError(tr("Invalid request"), tr("addresshelper is not supported")); GenericProxyError(tr("Invalid request"), tr("Addresshelper is not supported"));
return true; return true;
} }
@@ -333,24 +333,51 @@ namespace proxy {
} }
else if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm) else if (!i2p::client::context.GetAddressBook ().FindAddress (m_RequestURL.host) || m_Confirm)
{ {
const std::string referer_raw = m_ClientRequest.GetHeader("Referer");
i2p::http::URL referer_url;
if (!referer_raw.empty ())
{
referer_url.parse (referer_raw);
}
if (m_RequestURL.host != referer_url.host)
{
if (m_Confirm) // Attempt to forced overwriting by link with "&update=true" from harmful URL
{
LogPrint (eLogWarning, "HTTPProxy: Address update from addresshelper rejected for ", m_RequestURL.host, " (referer is ", m_RequestURL.host.empty() ? "empty" : "harmful", ")");
std::string full_url = m_RequestURL.to_string();
std::stringstream ss;
ss << tr("Host %s is <font color=red>already in router's addressbook</font>. <b>Be careful: source of this URL may be harmful!</b> Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.",
m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str());
GenericProxyInfo(tr("Addresshelper forced update rejected"), ss.str());
}
else // Preventing unauthorized additions to the address book
{
LogPrint (eLogDebug, "HTTPProxy: Adding address from addresshelper for ", m_RequestURL.host, " (generate refer-base page)");
std::string full_url = m_RequestURL.to_string();
std::stringstream ss;
ss << tr("To add host <b>%s</b> in router's addressbook, click here: <a href=\"%s%s%s\">Continue</a>.",
m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str());
GenericProxyInfo(tr("Addresshelper request"), ss.str());
}
return true; /* request processed */
}
i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump); i2p::client::context.GetAddressBook ().InsertAddress (m_RequestURL.host, jump);
LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host); LogPrint (eLogInfo, "HTTPProxy: Added address from addresshelper for ", m_RequestURL.host);
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << tr("Host") <<" " << m_RequestURL.host << " " << tr("added to router's addressbook from helper") << ". "; ss << tr("Host %s added to router's addressbook from helper. Click here to proceed: <a href=\"%s\">Continue</a>.",
ss << tr("Click here to proceed:") << " <a href=\"" << full_url << "\">" << tr("Continue") << "</a>."; m_RequestURL.host.c_str(), full_url.c_str());
GenericProxyInfo(tr("Addresshelper found"), ss.str()); GenericProxyInfo(tr("Addresshelper adding"), ss.str());
return true; /* request processed */ return true; /* request processed */
} }
else else
{ {
std::string full_url = m_RequestURL.to_string(); std::string full_url = m_RequestURL.to_string();
std::stringstream ss; std::stringstream ss;
ss << tr("Host") << " " << m_RequestURL.host << " <font color=red>" << tr("already in router's addressbook") << "</font>. "; ss << tr("Host %s is <font color=red>already in router's addressbook</font>. Click here to update record: <a href=\"%s%s%s&update=true\">Continue</a>.",
ss << tr(/* tr: The "record" means addressbook's record. That message appears when domain was already added to addressbook, but helper link is opened for it. */ "Click here to update record:" ); m_RequestURL.host.c_str(), full_url.c_str(), (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="), jump.c_str());
ss << " <a href=\"" << full_url << (full_url.find('?') != std::string::npos ? "&i2paddresshelper=" : "?i2paddresshelper="); GenericProxyInfo(tr("Addresshelper update"), ss.str());
ss << jump << "&update=true\">" << tr("Continue") << "</a>.";
GenericProxyInfo(tr("Addresshelper found"), ss.str());
return true; /* request processed */ return true; /* request processed */
} }
} }
@@ -363,7 +390,7 @@ namespace proxy {
auto pos = uri.find(":"); auto pos = uri.find(":");
if(pos == std::string::npos || pos == uri.size() - 1) if(pos == std::string::npos || pos == uri.size() - 1)
{ {
GenericProxyError(tr("Invalid request"), tr("invalid request uri")); GenericProxyError(tr("Invalid request"), tr("Invalid request URI"));
return true; return true;
} }
else else
@@ -423,10 +450,10 @@ namespace proxy {
if(m_ProxyURL.parse(m_OutproxyUrl)) if(m_ProxyURL.parse(m_OutproxyUrl))
ForwardToUpstreamProxy(); ForwardToUpstreamProxy();
else else
GenericProxyError(tr("Outproxy failure"), tr("bad outproxy settings")); GenericProxyError(tr("Outproxy failure"), tr("Bad outproxy settings"));
} else { } else {
LogPrint (eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled"); LogPrint (eLogWarning, "HTTPProxy: Outproxy failure for ", dest_host, ": no outproxy enabled");
std::stringstream ss; ss << tr("Host") << " " << dest_host << " " << tr("not inside I2P network, but outproxy is not enabled"); std::stringstream ss; ss << tr("Host %s is not inside I2P network, but outproxy is not enabled", dest_host.c_str());
GenericProxyError(tr("Outproxy failure"), ss.str()); GenericProxyError(tr("Outproxy failure"), ss.str());
} }
return true; return true;
@@ -515,13 +542,13 @@ namespace proxy {
else else
{ {
/* unknown type, complain */ /* unknown type, complain */
GenericProxyError(tr("unknown outproxy url"), m_ProxyURL.to_string()); 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) void HTTPReqHandler::HandleUpstreamProxyResolved(const boost::system::error_code & ec, boost::asio::ip::tcp::resolver::iterator it, ProxyResolvedHandler handler)
{ {
if(ec) GenericProxyError(tr("cannot resolve upstream proxy"), ec.message()); if(ec) GenericProxyError(tr("Cannot resolve upstream proxy"), ec.message());
else handler(*it); else handler(*it);
} }
@@ -529,7 +556,7 @@ namespace proxy {
{ {
if(!ec) { if(!ec) {
if(m_RequestURL.host.size() > 255) { if(m_RequestURL.host.size() > 255) {
GenericProxyError(tr("hostname too long"), m_RequestURL.host); GenericProxyError(tr("Hostname is too long"), m_RequestURL.host);
return; return;
} }
uint16_t port = m_RequestURL.port; uint16_t port = m_RequestURL.port;
@@ -556,13 +583,13 @@ namespace proxy {
reqsize += host.size(); reqsize += host.size();
m_socks_buf[++reqsize] = 0; 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)); 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(tr("cannot connect to upstream socks proxy"), ec.message()); } 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) void HTTPReqHandler::HandleSocksProxySendHandshake(const boost::system::error_code & ec, std::size_t bytes_transferred)
{ {
LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent"); LogPrint(eLogDebug, "HTTPProxy: Upstream SOCKS handshake sent");
if(ec) GenericProxyError(tr("Cannot negotiate with socks proxy"), ec.message()); 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)); else m_proxysock->async_read_some(boost::asio::buffer(m_socks_buf, 8), std::bind(&HTTPReqHandler::HandleSocksProxyReply, this, std::placeholders::_1, std::placeholders::_2));
} }
@@ -604,7 +631,7 @@ namespace proxy {
} }
else else
{ {
GenericProxyError(tr("CONNECT error"), tr("Failed to Connect")); GenericProxyError(tr("CONNECT error"), tr("Failed to connect"));
} }
} }
@@ -615,7 +642,7 @@ namespace proxy {
m_send_buf = m_ClientResponse.to_string(); 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) 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(tr("socks proxy error"), ec.message()); if(ec) GenericProxyError(tr("SOCKS proxy error"), ec.message());
else HandoverToUpstreamProxy(); else HandoverToUpstreamProxy();
}); });
} else { } else {
@@ -623,7 +650,7 @@ namespace proxy {
LogPrint(eLogDebug, "HTTPProxy: Send ", m_send_buf.size(), " bytes"); 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) 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(tr("failed to send request to upstream"), ec.message()); if(ec) GenericProxyError(tr("Failed to send request to upstream"), ec.message());
else HandoverToUpstreamProxy(); else HandoverToUpstreamProxy();
}); });
} }
@@ -641,18 +668,18 @@ namespace proxy {
ss << "error code: "; ss << "error code: ";
ss << (int) m_socks_buf[1]; ss << (int) m_socks_buf[1];
std::string msg = ss.str(); std::string msg = ss.str();
GenericProxyError(tr("socks proxy error"), msg); GenericProxyError(tr("SOCKS proxy error"), msg);
} }
} }
else GenericProxyError(tr("No Reply From socks proxy"), ec.message()); else GenericProxyError(tr("No reply from SOCKS proxy"), ec.message());
} }
void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec) void HTTPReqHandler::HandleUpstreamHTTPProxyConnect(const boost::system::error_code & ec)
{ {
if(!ec) { if(!ec) {
LogPrint(eLogDebug, "HTTPProxy: Connected to http upstream"); LogPrint(eLogDebug, "HTTPProxy: Connected to http upstream");
GenericProxyError(tr("cannot connect"), tr("http out proxy not implemented")); GenericProxyError(tr("Cannot connect"), tr("HTTP out proxy not implemented"));
} else GenericProxyError(tr("cannot connect to upstream http proxy"), ec.message()); } else GenericProxyError(tr("Cannot connect to upstream HTTP proxy"), ec.message());
} }
/* will be called after some data received from client */ /* will be called after some data received from client */

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2022, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -440,18 +440,23 @@ namespace client
{ {
if (ecode != boost::asio::error::operation_aborted) if (ecode != boost::asio::error::operation_aborted)
{ {
auto session = m_Owner.FindSession(m_ID); if (m_Socket.is_open ())
if(session)
{ {
if (session->GetLocalDestination ()->IsReady ()) auto session = m_Owner.FindSession(m_ID);
SendSessionCreateReplyOk (); if(session)
else
{ {
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL)); if (session->GetLocalDestination ()->IsReady ())
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer, SendSessionCreateReplyOk ();
shared_from_this (), std::placeholders::_1)); else
{
m_Timer.expires_from_now (boost::posix_time::seconds(SAM_SESSION_READINESS_CHECK_INTERVAL));
m_Timer.async_wait (std::bind (&SAMSocket::HandleSessionReadinessCheckTimer,
shared_from_this (), std::placeholders::_1));
}
} }
} }
else
Terminate ("SAM: session socket closed");
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2013-2021, The PurpleI2P Project * Copyright (c) 2013-2023, The PurpleI2P Project
* *
* This file is part of Purple i2pd project and licensed under BSD3 * This file is part of Purple i2pd project and licensed under BSD3
* *
@@ -30,7 +30,7 @@ namespace client
{ {
const size_t SAM_SOCKET_BUFFER_SIZE = 8192; const size_t SAM_SOCKET_BUFFER_SIZE = 8192;
const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds const int SAM_SOCKET_CONNECTION_MAX_IDLE = 3600; // in seconds
const int SAM_SESSION_READINESS_CHECK_INTERVAL = 20; // in seconds const int SAM_SESSION_READINESS_CHECK_INTERVAL = 3; // in seconds
const char SAM_HANDSHAKE[] = "HELLO VERSION"; const char SAM_HANDSHAKE[] = "HELLO VERSION";
const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n"; const char SAM_HANDSHAKE_REPLY[] = "HELLO REPLY RESULT=OK VERSION=%s\n";
const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n"; const char SAM_HANDSHAKE_NOVERSION[] = "HELLO REPLY RESULT=NOVERSION\n";