From 939682737950f53cd5b6dc68d029b3218c2efe6a Mon Sep 17 00:00:00 2001 From: R4SAS Date: Wed, 4 Aug 2021 07:03:54 +0300 Subject: [PATCH 01/15] [makefile] build libraries on default target Signed-off-by: R4SAS --- .gitignore | 3 +++ Makefile | 49 +++++++++++++++++++++++++++---------------------- Makefile.mingw | 4 ++-- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 1fc6cefe..bafe2e18 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ netDb /libi2pd.so /libi2pdclient.so /libi2pdlang.so +/libi2pd.dll +/libi2pdclient.dll +/libi2pdlang.dll *.exe diff --git a/Makefile b/Makefile index abea5fd7..1db11c21 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,20 @@ SYS := $(shell $(CXX) -dumpmachine) -SHLIB := libi2pd.so + +ifneq (, $(findstring darwin, $(SYS))) + SHARED_PREFIX = dylib +else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) + SHARED_PREFIX = dll +else + SHARED_PREFIX = so +endif + +SHLIB := libi2pd.$(SHARED_PREFIX) ARLIB := libi2pd.a -SHLIB_LANG := libi2pdlang.so +SHLIB_LANG := libi2pdlang.$(SHARED_PREFIX) ARLIB_LANG := libi2pdlang.a -SHLIB_CLIENT := libi2pdclient.so +SHLIB_CLIENT := libi2pdclient.$(SHARED_PREFIX) ARLIB_CLIENT := libi2pdclient.a -SHLIB_WRAP := libi2pdwrapper.so +SHLIB_WRAP := libi2pdwrapper.$(SHARED_PREFIX) ARLIB_WRAP := libi2pdwrapper.a I2PD := i2pd @@ -64,22 +73,18 @@ LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC)) DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) -all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(I2PD) +## Build all code (libi2pd, libi2pdclient, libi2pdlang), link code to .a and .so (.dll on windows) and build binary +## Windows binary is not depending on output dlls +all: | api_client $(I2PD) mk_obj_dir: - @mkdir -p obj - @mkdir -p obj/Win32 - @mkdir -p obj/$(LIB_SRC_DIR) - @mkdir -p obj/$(LIB_CLIENT_SRC_DIR) - @mkdir -p obj/$(LANG_SRC_DIR) - @mkdir -p obj/$(WRAP_SRC_DIR) - @mkdir -p obj/$(DAEMON_SRC_DIR) + @mkdir -p obj/{Win32,$(LIB_SRC_DIR),$(LIB_CLIENT_SRC_DIR),$(LANG_SRC_DIR),$(WRAP_SRC_DIR),$(DAEMON_SRC_DIR)} -api: mk_obj_dir $(SHLIB) $(ARLIB) -client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT) -api_client: mk_obj_dir $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) -wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP) -lang: mk_obj_dir $(SHLIB_LANG) $(ARLIB_LANG) +api: | mk_obj_dir $(SHLIB) $(ARLIB) +client: | mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT) +lang: | mk_obj_dir $(SHLIB_LANG) $(ARLIB_LANG) +api_client: | api client lang +wrapper: | mk_obj_dir api_client $(SHLIB_WRAP) $(ARLIB_WRAP) ## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time @@ -98,14 +103,14 @@ obj/%.o: %.cpp $(I2PD): $(DAEMON_OBJS) $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(CXX) -o $@ $(LDFLAGS) $^ $(LDLIBS) -$(SHLIB): $(LIB_OBJS) +$(SHLIB): $(LIB_OBJS) $(SHLIB_LANG) ifneq ($(USE_STATIC),yes) - $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) + $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB_LANG) endif -$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) +$(SHLIB_CLIENT): $(LIB_CLIENT_OBJS) $(SHLIB) $(SHLIB_LANG) ifneq ($(USE_STATIC),yes) - $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) + $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) $(SHLIB) $(SHLIB_LANG) endif $(SHLIB_WRAP): $(WRAP_LIB_OBJS) @@ -118,7 +123,7 @@ ifneq ($(USE_STATIC),yes) $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) endif -$(ARLIB): $(LIB_OBJS) +$(ARLIB): $(LIB_OBJS) $(AR) -r $@ $^ $(ARLIB_CLIENT): $(LIB_CLIENT_OBJS) diff --git a/Makefile.mingw b/Makefile.mingw index ce1966a1..b57860b4 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -3,9 +3,9 @@ USE_WIN32_APP := yes WINDRES = windres -CXXFLAGS := $(CXX_DEBUG) -D_MT -DWIN32_LEAN_AND_MEAN -fPIC -msse +CXXFLAGS := $(CXX_DEBUG) -DWIN32_LEAN_AND_MEAN -fPIC -msse INCFLAGS = -I$(DAEMON_SRC_DIR) -IWin32 -LDFLAGS := ${LD_DEBUG} -Wl,-Bstatic -static-libgcc +LDFLAGS := ${LD_DEBUG} -static # detect proper flag for c++11 support by compilers CXXVER := $(shell $(CXX) -dumpversion) From dc9e5dc2f1479c519db5bab16a9258d025e8f99c Mon Sep 17 00:00:00 2001 From: R4SAS Date: Fri, 6 Aug 2021 20:51:25 +0300 Subject: [PATCH 02/15] [makefile] suffix, not prefix Signed-off-by: R4SAS --- Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 1db11c21..328214cd 100644 --- a/Makefile +++ b/Makefile @@ -1,20 +1,20 @@ SYS := $(shell $(CXX) -dumpmachine) ifneq (, $(findstring darwin, $(SYS))) - SHARED_PREFIX = dylib + SHARED_SUFFIX = dylib else ifneq (, $(findstring mingw, $(SYS))$(findstring cygwin, $(SYS))) - SHARED_PREFIX = dll + SHARED_SUFFIX = dll else - SHARED_PREFIX = so + SHARED_SUFFIX = so endif -SHLIB := libi2pd.$(SHARED_PREFIX) +SHLIB := libi2pd.$(SHARED_SUFFIX) ARLIB := libi2pd.a -SHLIB_LANG := libi2pdlang.$(SHARED_PREFIX) +SHLIB_LANG := libi2pdlang.$(SHARED_SUFFIX) ARLIB_LANG := libi2pdlang.a -SHLIB_CLIENT := libi2pdclient.$(SHARED_PREFIX) +SHLIB_CLIENT := libi2pdclient.$(SHARED_SUFFIX) ARLIB_CLIENT := libi2pdclient.a -SHLIB_WRAP := libi2pdwrapper.$(SHARED_PREFIX) +SHLIB_WRAP := libi2pdwrapper.$(SHARED_SUFFIX) ARLIB_WRAP := libi2pdwrapper.a I2PD := i2pd From 2f945a4fce3fafa3dc34abccf9e08c059026331d Mon Sep 17 00:00:00 2001 From: R4SAS Date: Fri, 6 Aug 2021 21:27:37 +0300 Subject: [PATCH 03/15] [makefile] dont build .so and .dll on default target Signed-off-by: R4SAS --- Makefile | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 328214cd..a4b3c3fa 100644 --- a/Makefile +++ b/Makefile @@ -73,19 +73,17 @@ LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC)) DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) -## Build all code (libi2pd, libi2pdclient, libi2pdlang), link code to .a and .so (.dll on windows) and build binary -## Windows binary is not depending on output dlls -all: | api_client $(I2PD) +## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary +all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD) mk_obj_dir: @mkdir -p obj/{Win32,$(LIB_SRC_DIR),$(LIB_CLIENT_SRC_DIR),$(LANG_SRC_DIR),$(WRAP_SRC_DIR),$(DAEMON_SRC_DIR)} -api: | mk_obj_dir $(SHLIB) $(ARLIB) -client: | mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT) -lang: | mk_obj_dir $(SHLIB_LANG) $(ARLIB_LANG) -api_client: | api client lang -wrapper: | mk_obj_dir api_client $(SHLIB_WRAP) $(ARLIB_WRAP) - +api: mk_obj_dir $(SHLIB) $(ARLIB) +client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT) +lang: mk_obj_dir $(SHLIB_LANG) $(ARLIB_LANG) +api_client: api client lang +wrapper: mk_obj_dir api_client $(SHLIB_WRAP) $(ARLIB_WRAP) ## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. @@ -138,7 +136,7 @@ $(ARLIB_LANG): $(LANG_OBJS) clean: $(RM) -r obj $(RM) -r docs/generated - $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) + $(RM) $(I2PD) $(SHLIB) $(ARLIB) $(SHLIB_CLIENT) $(ARLIB_CLIENT) $(SHLIB_LANG) $(ARLIB_LANG) $(SHLIB_WRAP) $(ARLIB_WRAP) strip: $(I2PD) $(SHLIB) $(SHLIB_CLIENT) $(SHLIB_LANG) strip $^ From a3b172bbcbdd279514e37c78ad6d8a67e315eda2 Mon Sep 17 00:00:00 2001 From: r4sas Date: Fri, 6 Aug 2021 22:18:02 +0200 Subject: [PATCH 04/15] [makefile] change back directories creation, create them before compiling object files --- Makefile | 25 +++++++++++++++---------- build/.gitignore | 3 ++- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index a4b3c3fa..d7765af7 100644 --- a/Makefile +++ b/Makefile @@ -68,22 +68,27 @@ NEEDED_CXXFLAGS += -MMD -MP -I$(LIB_SRC_DIR) -I$(LIB_CLIENT_SRC_DIR) -I$(LANG_SR LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_SRC)) LIB_CLIENT_OBJS += $(patsubst %.cpp,obj/%.o,$(LIB_CLIENT_SRC)) -WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC)) LANG_OBJS += $(patsubst %.cpp,obj/%.o,$(LANG_SRC)) DAEMON_OBJS += $(patsubst %.cpp,obj/%.o,$(DAEMON_SRC)) -DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) +WRAP_LIB_OBJS += $(patsubst %.cpp,obj/%.o,$(WRAP_LIB_SRC)) +DEPS += $(LIB_OBJS:.o=.d) $(LIB_CLIENT_OBJS:.o=.d) $(LANG_OBJS:.o=.d) $(DAEMON_OBJS:.o=.d) $(WRAP_LIB_OBJS:.o=.d) ## Build all code (libi2pd, libi2pdclient, libi2pdlang), link it to .a and build binary -all: mk_obj_dir $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD) +all: $(ARLIB) $(ARLIB_CLIENT) $(ARLIB_LANG) $(I2PD) mk_obj_dir: - @mkdir -p obj/{Win32,$(LIB_SRC_DIR),$(LIB_CLIENT_SRC_DIR),$(LANG_SRC_DIR),$(WRAP_SRC_DIR),$(DAEMON_SRC_DIR)} + @mkdir -p obj/$(LIB_SRC_DIR) + @mkdir -p obj/$(LIB_CLIENT_SRC_DIR) + @mkdir -p obj/$(LANG_SRC_DIR) + @mkdir -p obj/$(DAEMON_SRC_DIR) + @mkdir -p obj/$(WRAP_SRC_DIR) + @mkdir -p obj/Win32 -api: mk_obj_dir $(SHLIB) $(ARLIB) -client: mk_obj_dir $(SHLIB_CLIENT) $(ARLIB_CLIENT) -lang: mk_obj_dir $(SHLIB_LANG) $(ARLIB_LANG) +api: $(SHLIB) $(ARLIB) +client: $(SHLIB_CLIENT) $(ARLIB_CLIENT) +lang: $(SHLIB_LANG) $(ARLIB_LANG) api_client: api client lang -wrapper: mk_obj_dir api_client $(SHLIB_WRAP) $(ARLIB_WRAP) +wrapper: api_client $(SHLIB_WRAP) $(ARLIB_WRAP) ## NOTE: The NEEDED_CXXFLAGS are here so that CXXFLAGS can be specified at build time ## **without** overwriting the CXXFLAGS which we need in order to build. @@ -92,7 +97,7 @@ wrapper: mk_obj_dir api_client $(SHLIB_WRAP) $(ARLIB_WRAP) ## -std=c++11. If you want to remove this variable please do so in a way that allows setting ## custom FLAGS to work at build-time. -obj/%.o: %.cpp +obj/%.o: %.cpp | mk_obj_dir $(CXX) $(CXXFLAGS) $(NEEDED_CXXFLAGS) $(INCFLAGS) -c -o $@ $< # '-' is 'ignore if missing' on first run @@ -121,7 +126,7 @@ ifneq ($(USE_STATIC),yes) $(CXX) $(LDFLAGS) -shared -o $@ $^ $(LDLIBS) endif -$(ARLIB): $(LIB_OBJS) +$(ARLIB): $(LIB_OBJS) $(AR) -r $@ $^ $(ARLIB_CLIENT): $(LIB_CLIENT_OBJS) diff --git a/build/.gitignore b/build/.gitignore index b595141b..872332c5 100644 --- a/build/.gitignore +++ b/build/.gitignore @@ -3,6 +3,7 @@ /i2pd /libi2pd.a /libi2pdclient.a +/libi2pdlang.a /cmake_install.cmake /CMakeCache.txt /CPackConfig.cmake @@ -11,4 +12,4 @@ /arch.c # windows build script i2pd*.zip -build*.log \ No newline at end of file +build*.log From fcbc16f2fd8e34734cfda6c8fd3ba635863fcce9 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Sat, 7 Aug 2021 01:37:45 +0300 Subject: [PATCH 05/15] [webconsole] fix style issues, clean external style in file was not found on reload Signed-off-by: R4SAS --- daemon/HTTPServer.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 5c6a3cd8..ac83f87c 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -66,7 +66,7 @@ namespace http { " a, .slide label { text-decoration: none; color: #894C84; }\r\n" " a:hover, .slide label:hover { color: #FAFAFA; background: #894C84; }\r\n" " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n" - " color: initial; padding: 0 5px; border: 1px solid #894C84; }\r\n" + " padding: 0 5px; border: 1px solid #894C84; }\r\n" " .header { font-size: 2.5em; text-align: center; margin: 1em 0; color: #894C84; }\r\n" " .wrapper { margin: 0 auto; padding: 1em; max-width: 64em; }\r\n" " .menu { display: block; float: left; overflow: hidden; max-width: 12em; white-space: nowrap; text-overflow: ellipsis; }\r\n" @@ -97,7 +97,7 @@ namespace http { " a, .slide label { /* margin-right: 10px; */ display: block; /* font-size: 18px; */ }\r\n" " .header { margin: unset; font-size: 1.5em; } small {display: block}\r\n" " a.button { -webkit-appearance: button; -moz-appearance: button; appearance: button; text-decoration: none;\r\n" - " color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n" + " margin-top: 10px; padding: 6px; border: 1px solid #894c84; width: -webkit-fill-available; }\r\n" " input, select { width: 35%; text-align: center; padding: 5px;\r\n" " border: 2px solid #ccc; -webkit-border-radius: 5px; border-radius: 5px; font-size: 18px; }\r\n" " table.extaddr { margin: auto; text-align: unset; }\r\n" @@ -119,6 +119,8 @@ namespace http { std::ifstream f(styleFile, std::ifstream::binary); s << f.rdbuf(); externalCSS = s.str(); + } else if (externalCSS.length() != 0) { // clean up external style if file was removed + externalCSS = ""; } } From bef8587d8fa6684575939d8a2ed5f042aff1a935 Mon Sep 17 00:00:00 2001 From: R4SAS Date: Sat, 7 Aug 2021 01:38:35 +0300 Subject: [PATCH 06/15] [makefile] create object dirs on windres (race condition) Signed-off-by: R4SAS --- Makefile.mingw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.mingw b/Makefile.mingw index b57860b4..e31d895e 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -61,5 +61,5 @@ ifeq ($(USE_ASLR),yes) LDFLAGS += -Wl,--nxcompat -Wl,--high-entropy-va -Wl,--dynamicbase,--export-all-symbols endif -obj/%.o : %.rc +obj/%.o : %.rc | mk_obj_dir $(WINDRES) -i $< -o $@ From ba369d9b3064eaf01a4b9bf523fa680928e7cafa Mon Sep 17 00:00:00 2001 From: R4SAS Date: Sat, 7 Aug 2021 17:31:26 +0300 Subject: [PATCH 07/15] [webconsole] fix style in css Signed-off-by: R4SAS --- contrib/webconsole/style.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/contrib/webconsole/style.css b/contrib/webconsole/style.css index b6e56477..047839a6 100644 --- a/contrib/webconsole/style.css +++ b/contrib/webconsole/style.css @@ -21,7 +21,6 @@ a.button { -moz-appearance: button; appearance: button; text-decoration: none; - color: initial; padding: 0 5px; border: 1px solid #894C84; } @@ -198,7 +197,6 @@ textarea { -moz-appearance: button; appearance: button; text-decoration: none; - color: initial; margin-top: 10px; padding: 6px; border: 1px solid #894c84; From d124d4caceb17a0fb212b79ab258d90184b893c3 Mon Sep 17 00:00:00 2001 From: orignal Date: Tue, 10 Aug 2021 11:36:12 -0400 Subject: [PATCH 08/15] allow ipv6 adresses for UDP server tunnels --- libi2pd_client/ClientContext.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/libi2pd_client/ClientContext.cpp b/libi2pd_client/ClientContext.cpp index f8bc5667..6e9ac391 100644 --- a/libi2pd_client/ClientContext.cpp +++ b/libi2pd_client/ClientContext.cpp @@ -720,9 +720,15 @@ namespace client { // udp server tunnel // TODO: hostnames - if (address.empty ()) address = "127.0.0.1"; - auto localAddress = boost::asio::ip::address::from_string(address); boost::asio::ip::udp::endpoint endpoint(boost::asio::ip::address::from_string(host), port); + if (address.empty ()) + { + if (!endpoint.address ().is_unspecified () && endpoint.address ().is_v6 ()) + address = "::1"; + else + address = "127.0.0.1"; + } + auto localAddress = boost::asio::ip::address::from_string(address); auto serverTunnel = std::make_shared(name, localDestination, localAddress, endpoint, port, gzip); if(!isUniqueLocal) { From 49b3ac7f77431723f88bb79cf2390778ddfadac8 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 11 Aug 2021 12:23:43 -0400 Subject: [PATCH 09/15] don't reschedule resend timer for terminated streams --- libi2pd/Streaming.cpp | 16 ++++++++++------ libi2pd/Streaming.h | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/libi2pd/Streaming.cpp b/libi2pd/Streaming.cpp index bbb649df..95f6a150 100644 --- a/libi2pd/Streaming.cpp +++ b/libi2pd/Streaming.cpp @@ -104,6 +104,7 @@ namespace stream void Stream::Terminate (bool deleteFromDestination) // shoudl be called from StreamingDestination::Stop only { + m_Status = eStreamStatusTerminated; m_AckSendTimer.cancel (); m_ReceiveTimer.cancel (); m_ResendTimer.cancel (); @@ -857,12 +858,15 @@ namespace stream void Stream::ScheduleResend () { - m_ResendTimer.cancel (); - // check for invalid value - if (m_RTO <= 0) m_RTO = INITIAL_RTO; - m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); - m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, - shared_from_this (), std::placeholders::_1)); + if (m_Status != eStreamStatusTerminated) + { + m_ResendTimer.cancel (); + // check for invalid value + if (m_RTO <= 0) m_RTO = INITIAL_RTO; + m_ResendTimer.expires_from_now (boost::posix_time::milliseconds(m_RTO)); + m_ResendTimer.async_wait (std::bind (&Stream::HandleResendTimer, + shared_from_this (), std::placeholders::_1)); + } } void Stream::HandleResendTimer (const boost::system::error_code& ecode) diff --git a/libi2pd/Streaming.h b/libi2pd/Streaming.h index c40c49f5..9d206098 100644 --- a/libi2pd/Streaming.h +++ b/libi2pd/Streaming.h @@ -152,7 +152,8 @@ namespace stream eStreamStatusOpen, eStreamStatusReset, eStreamStatusClosing, - eStreamStatusClosed + eStreamStatusClosed, + eStreamStatusTerminated }; class StreamingDestination; From 38a2d45a3c24a3be48aee1b16ffec63500e374f5 Mon Sep 17 00:00:00 2001 From: orignal Date: Wed, 11 Aug 2021 12:31:46 -0400 Subject: [PATCH 10/15] close all existing streams when command SAM socket got closed --- libi2pd_client/SAM.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libi2pd_client/SAM.cpp b/libi2pd_client/SAM.cpp index af9ba6a8..c2983f43 100644 --- a/libi2pd_client/SAM.cpp +++ b/libi2pd_client/SAM.cpp @@ -1207,7 +1207,11 @@ namespace client void SAMSingleSession::StopLocalDestination () { localDestination->Release (); + // stop accepting new streams localDestination->StopAcceptingStreams (); + // terminate existing streams + auto s = localDestination->GetStreamingDestination (); // TODO: take care about datagrams + if (s) s->Stop (); } void SAMMasterSession::Close () From 4956c112b24d329b720b47156d84a456a758136b Mon Sep 17 00:00:00 2001 From: dr|z3d Date: Wed, 11 Aug 2021 05:35:21 +0000 Subject: [PATCH 11/15] UI updates --- daemon/HTTPServer.cpp | 1067 ++++++++++++++++++++++++++--------------- libi2pd/Tunnel.cpp | 57 ++- 2 files changed, 716 insertions(+), 408 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index ac83f87c..38225450 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -41,71 +41,108 @@ namespace i2p { namespace http { - const std::string itoopieFavicon = - "data:image/png;base64," - "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACx" - "jwv8YQUAAAAJcEhZcwAALiIAAC4iAari3ZIAAAAHdElNRQfgCQsUNSZrkhi1AAAAGXRFWHRTb2Z0" - "d2FyZQBwYWludC5uZXQgNC4wLjEyQwRr7AAAAoJJREFUOE9jwAUqi4Q1oEwwcDTV1+5sETaBclGB" - "vb09C5QJB6kWpvFQJoOCeLC5kmjEHCgXE2SlyETLi3h6QrkM4VL+ssWSCZUgtopITLKqaOotRTEn" - "cbAkLqAkGtOqLBLVAWLXyWSVFkkmRiqLxuaqiWb/VBYJMAYrwgckJY25VEUzniqKhjU2y+RtCRSP" - "6lUXy/1jIBV5tlYxZUaFVMq2NInwIi9hO8fSfOEAqDZUoCwal6MulvOvyS7gi69K4j9zxZT/m0ps" - "/28ptvvvquXXryIa7QYMMdTwqi0WNtVi0GIDseXl7TnUxFKfnGlxAGp0+D8j2eH/8Ub7/9e7nf7X" - "+Af/B7rwt6pI0h0l0WhQADOC9DBkhSirpImHNVZKp24ukkyoshGLnN8d5fA/y13t/44Kq/8hlnL/" - "z7fZ/58f6vcxSNpbVUVFhV1RLNBVTsQzVYZPSwhsCAhkiIfpNMrkbO6TLf071Sfk/5ZSi/+7q6z/" - "P5ns+v9mj/P/CpuI/20y+aeNGYxZoVoYGmsF3aFMBAAZlCwftnF9ke3//bU2//fXWP8/UGv731Am" - "+V+DdNblSqnUYqhSTKAiYSOqJBrVqiaa+S3UNPr/gmyH/xuKXf63hnn/B8bIP0UxHfEyyeSNQKVM" - "EB1AEB2twhcTLp+gIBJUoyKasEpVJHmqskh8qryovUG/ffCHHRU2q/Tk/YuB6eGPsbExa7ZkpLu1" - "oLEcVDtuUCgV1w60rQzElpRUE1EVSX0BYidHiInXF4nagNhYQW60EF+ApH1ktni0A1SIITSUgVlZ" - "JHYnlIsfzJjIp9xZKswL5YKBHL+coKJoRDaUSzoozxHVrygQU4JykQADAwAT5b1NHtwZugAAAABJ" - "RU5ErkJggg=="; + const std::string i2pdfavicon = + "data:image/svg+xml,%3Csvg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 64 64\'%3E%3Crect width=\'64\' height=\'64\' fill=\'%23313\' rx=\'5\'/%3E%3Ccircle cx=\'8\' cy=\'32\' r=\'4\' fill=\'%23ee6565\'/%3E%3Ccircle cx=\'20\' cy=\'32\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'32\' cy=\'8\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'32\' cy=\'20\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'32\' cy=\'32\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'32\' cy=\'44\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'32\' cy=\'56\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'44\' cy=\'32\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'56\' cy=\'32\' r=\'4\' fill=\'%23ee6565\'/%3E%3Ccircle cx=\'8\' cy=\'20\' r=\'4\' fill=\'%23ffc95e\'/%3E%3Ccircle cx=\'8\' cy=\'44\' r=\'4\' fill=\'%23ffc95e\'/%3E%3Ccircle cx=\'20\' cy=\'8\' r=\'4\' fill=\'%23ffc95e\'/%3E%3Ccircle cx=\'20\' cy=\'20\' r=\'4\' fill=\'%23ffc95e\'/%3E%3Ccircle cx=\'20\' cy=\'44\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'20\' cy=\'56\' r=\'4\' fill=\'%23ffc95e\'/%3E%3Ccircle cx=\'44\' cy=\'8\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'44\' cy=\'20\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'44\' cy=\'44\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'44\' cy=\'56\' r=\'4\' fill=\'%239ff39f\'/%3E%3Ccircle cx=\'56\' cy=\'20\' r=\'4\' fill=\'%23ffc95e\'/%3E%3Ccircle cx=\'56\' cy=\'44\' r=\'4\' fill=\'%23ffc95e\'/%3E%3Ccircle cx=\'8\' cy=\'8\' r=\'4\' fill=\'%23ee6565\'/%3E%3Ccircle cx=\'8\' cy=\'56\' r=\'4\' fill=\'%23ee6565\'/%3E%3Ccircle cx=\'56\' cy=\'8\' r=\'4\' fill=\'%23ee6565\'/%3E%3Ccircle cx=\'56\' cy=\'56\' r=\'4\' fill=\'%23ee6565\'/%3E%3C/svg%3E"; // Bundled style const std::string internalCSS = "\r\n"; // for external style sheet @@ -119,8 +156,6 @@ namespace http { std::ifstream f(styleFile, std::ifstream::binary); s << f.rdbuf(); externalCSS = s.str(); - } else if (externalCSS.length() != 0) { // clean up external style if file was removed - externalCSS = ""; } } @@ -190,17 +225,25 @@ namespace http { static void ShowTraffic (std::stringstream& s, uint64_t bytes) { - s << std::fixed << std::setprecision(2); + s << std::fixed << std::setprecision(0); auto numKBytes = (double) bytes / 1024; - if (numKBytes < 1024) - s << numKBytes << " " << tr(/* tr: Kibibit */ "KiB"); - else if (numKBytes < 1024 * 1024) - s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "MiB"); - else - s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "GiB"); + if (numKBytes < 1) { + s << std::fixed << std::setprecision(2); + s << numKBytes * 1024 << " " << tr(/* tr: Byte */ "B"); + } else if (numKBytes < 1024) { + s << numKBytes << " " << tr(/* tr: Kibibit */ "K"); + } else if (numKBytes < 1024 * 1024) { + s << std::fixed << std::setprecision(1); + s << numKBytes / 1024 << " " << tr(/* tr: Mebibit */ "M"); + } else if (numKBytes < 1024 * 1024 * 1024) { + s << std::fixed << std::setprecision(2); + s << numKBytes / 1024 / 1024 << " " << tr(/* tr: Gibibit */ "G"); + } else { + s << numKBytes / 1024 / 1024 / 1024 << " " << tr(/* tr: Tibibit */ "T"); + } } - 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, double bytes) { std::string state, stateText; switch (eState) { @@ -220,8 +263,21 @@ namespace http { else if (state == "established") stateText = tr("established"); else stateText = tr("unknown"); - s << " " << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << ", "; - s << " " << (int) (bytes / 1024) << " " << tr(/* tr: Kibibit */ "KiB") << "\r\n"; + s << "" + << stateText << ((explr) ? " (" + tr("exploratory") + ")" : "") << ""; + s << std::fixed << std::setprecision(0); + if (bytes > 1024 * 1024 * 1024) { + s << std::fixed << std::setprecision(2); + s << " " << (double) (bytes / 1024 / 1024 / 1024) << "G\r\n"; + } else if (bytes > 1024 * 1024) { + s << std::fixed << std::setprecision(1); + s << " " << (double) (bytes / 1024 / 1024) << "M\r\n"; + } else if (bytes > 1024) { + s << " " << (int) (bytes / 1024) << "K\r\n"; + } else { + s << " " << (int) (bytes) << "B\r\n"; + } } static void SetLogLevel (const std::string& level) @@ -243,6 +299,14 @@ namespace http { std::string currLang = i2p::context.GetLanguage ()->GetLanguage(); // get current used language auto it = i2p::i18n::languages.find(currLang); std::string langCode = it->second.ShortCode; + // SAM + auto sam = i2p::client::context.GetSAMBridge (); + + std::map params; + std::string page(""); + URL url; + url.parse_query(params); + page = params["page"]; s << "\r\n" @@ -250,43 +314,47 @@ namespace http { " \r\n" /* TODO: Find something to parse html/template system. This is horrible. */ " \r\n" " \r\n" - " \r\n" - " Purple I2P " VERSION " Webconsole\r\n"; + " \r\n" + " Purple I2P | " VERSION "\r\n"; GetStyles(s); s << "\r\n" "\r\n" - "
" << tr("i2pd webconsole") << "
\r\n" - "
\r\n" - "
\r\n" - " " << tr("Main page") << "

\r\n" - " " << tr("Router commands") << "
\r\n" - " " << tr("Local Destinations") << "
\r\n"; + "
\r\n\r\n" + "" + "\r\n"; } static void ShowPageTail (std::stringstream& s) { s << - "\r\n\r\n" + "
" << tr("Main page") << "
\r\n"; if (i2p::context.IsFloodfill ()) - s << " " << tr("LeaseSets") << "
\r\n"; + s << "" << tr("LeaseSets") << "\r\n"; s << - " " << tr("Tunnels") << "
\r\n" - " " << tr("Transit Tunnels") << "
\r\n" - " " << tr ("Transports") << "
\r\n" - " " << tr("I2P tunnels") << "
\r\n"; - if (i2p::client::context.GetSAMBridge ()) - s << " " << tr("SAM sessions") << "
\r\n"; - s << - "\r\n" - "
"; + "" << tr("Destinations") << "\r\n" +// "" << tr("Services") << "\r\n" +// "" << tr("Transit") << "\r\n" + "" << tr ("Transports") << "\r\n" + "" << tr("Tunnels") << "\r\n"; + if (sam && sam->GetSessions ().size ()) { + s << "" << tr("SAM Sessions") << "\r\n"; + } + s << "" << tr("Control") << "\r\n
\r\n" + "
\r\n" "\r\n" "\r\n"; } static void ShowError(std::stringstream& s, const std::string& string) { - s << "" << tr("ERROR") << ": " << string << "
\r\n"; + s << "\r\n" << tr("ERROR") + << ": " << string << "\r\n"; } static void ShowNetworkStatus (std::stringstream& s, RouterStatus status) @@ -323,106 +391,148 @@ namespace http { void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat) { - s << "" << tr("Uptime") << ": "; + s << "" << tr("Version") << "" VERSION "\r\n"; + s << "" << tr("Uptime") << ""; ShowUptime(s, i2p::context.GetUptime ()); - s << "
\r\n"; - s << "" << tr("Network status") << ": "; - ShowNetworkStatus (s, i2p::context.GetStatus ()); - s << "
\r\n"; + s << "\r\n"; + if (i2p::context.SupportsV4 ()) + { + s << "" << tr("Network Status") << ""; + ShowNetworkStatus (s, i2p::context.GetStatus ()); + s << "
\r\n"; + } if (i2p::context.SupportsV6 ()) { - s << "" << tr("Network status v6") << ": "; + s << "" << tr("Network Status (IPv6)") << ""; ShowNetworkStatus (s, i2p::context.GetStatusV6 ()); s << "
\r\n"; } #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) if (auto remains = Daemon.gracefulShutdownInterval) { - s << "" << tr("Stopping in") << ": "; + s << "" << tr("Stopping in") << ""; ShowUptime(s, remains); - s << "
\r\n"; + s << "…\r\n"; } #elif defined(WIN32_APP) if (i2p::win32::g_GracefulShutdownEndtime != 0) { uint16_t remains = (i2p::win32::g_GracefulShutdownEndtime - GetTickCount()) / 1000; - s << "" << tr("Stopping in") << ": "; + s << "" << tr("Stopping in") << ""; ShowUptime(s, remains); - s << "
\r\n"; + s << "…\r\n"; } #endif + s << "" << tr("Bandwidth") << ""; + s << std::fixed << std::setprecision(0); +// s << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s"); + if (i2p::transport::transports.GetInBandwidth () > 1024*1024*1024 || + i2p::transport::transports.GetInBandwidth () < 1024) + s << std::fixed << std::setprecision(2); + else if (i2p::transport::transports.GetInBandwidth () > 1024*1024) + s << std::fixed << std::setprecision(1); + s << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "K/s"); + s << " / "; +// s << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s"); + s << std::fixed << std::setprecision(0); + if (i2p::transport::transports.GetOutBandwidth () > 1024*1024*1024 || + i2p::transport::transports.GetOutBandwidth () < 1024) + s << std::fixed << std::setprecision(2); + else if (i2p::transport::transports.GetOutBandwidth () > 1024*1024) + s << std::fixed << std::setprecision(1); + s << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "K/s"); + s << "\r\n"; + s << "" << tr("Transferred") << ""; + s << std::fixed << std::setprecision(0); + if (i2p::transport::transports.GetTotalReceivedBytes () > 1024*1024*1024) + s << std::fixed << std::setprecision(2); + else if (i2p::transport::transports.GetTotalReceivedBytes () > 1024*1024) + s << std::fixed << std::setprecision(1); + ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); + s << " / "; + s << std::fixed << std::setprecision(0); + if (i2p::transport::transports.GetTotalSentBytes () > 1024*1024*1024) + s << std::fixed << std::setprecision(2); + else if (i2p::transport::transports.GetTotalSentBytes () > 1024*1024) + s << std::fixed << std::setprecision(1); + ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); + s << "\r\n"; + if (i2p::context.AcceptsTunnels () && i2p::tunnel::tunnels.CountTransitTunnels()) { + s << "" << tr("Transit") << ""; + s << std::fixed << std::setprecision(0); + if (i2p::transport::transports.GetTotalTransitTransmittedBytes () > 1024*1024*1024) + s << std::fixed << std::setprecision(2); + else if (i2p::transport::transports.GetTotalTransitTransmittedBytes () > 1024*1024) + s << std::fixed << std::setprecision(1); + ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); + s << std::fixed << std::setprecision(0); + if (i2p::transport::transports.GetTransitBandwidth () > 1024*1024*1024 || + i2p::transport::transports.GetTransitBandwidth () < 1024) + s << std::fixed << std::setprecision(2); + else if (i2p::transport::transports.GetTransitBandwidth () > 1024*1024) + s << std::fixed << std::setprecision(1); + s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024; +// s << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")\r\n"; + s << " " << tr(/* tr: Kibibit/s */ "K/s") << ")\r\n"; + } + s << "" << tr("Build Success") << ""; + s << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%\r\n"; +// TODO: Move to separate routerinfo page +/* + s << "" << tr("Router Caps") << "" << i2p::context.GetRouterInfo().GetProperty("caps") << "\r\n"; + s << "" << tr("Data path") << "" << i2p::fs::GetUTF8DataDir() << "\r\n"; + s << "" << tr("Router Ident") << "\r\n"; auto family = i2p::context.GetFamily (); if (family.length () > 0) - s << ""<< tr("Family") << ": " << family << "
\r\n"; - s << "" << tr("Tunnel creation success rate") << ": " << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%
\r\n"; - s << "" << tr("Received") << ": "; - ShowTraffic (s, i2p::transport::transports.GetTotalReceivedBytes ()); - s << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; - s << "" << tr("Sent") << ": "; - ShowTraffic (s, i2p::transport::transports.GetTotalSentBytes ()); - s << " (" << (double) i2p::transport::transports.GetOutBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; - s << "" << tr("Transit") << ": "; - ShowTraffic (s, i2p::transport::transports.GetTotalTransitTransmittedBytes ()); - s << " (" << (double) i2p::transport::transports.GetTransitBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; - s << "" << tr("Data path") << ": " << i2p::fs::GetUTF8DataDir() << "
\r\n"; - s << "
"; - if((outputFormat == OutputFormatEnum::forWebConsole) || !includeHiddenContent) { - s << "\r\n\r\n
\r\n"; - } - if(includeHiddenContent) { - s << "" << tr("Router Ident") << ": " << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
\r\n"; - if (!i2p::context.GetRouterInfo().GetProperty("family").empty()) - s << "" << tr("Router Family") << ": " << i2p::context.GetRouterInfo().GetProperty("family") << "
\r\n"; - s << "" << tr("Router Caps") << ": " << i2p::context.GetRouterInfo().GetProperty("caps") << "
\r\n"; - s << "" << tr("Version") << ": " VERSION "
\r\n"; - s << ""<< tr("Our external address") << ":" << "
\r\n\r\n"; - for (const auto& address : i2p::context.GetRouterInfo().GetAddresses()) + s << "\r\n"; + for (const auto& address : i2p::context.GetRouterInfo().GetAddresses()) + { + s << "\r\n"; + if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ()) { - s << "\r\n"; - if (address->IsNTCP2 () && !address->IsPublishedNTCP2 ()) - { - s << "\r\n\r\n"; - continue; - } - switch (address->transportStyle) - { - case i2p::data::RouterInfo::eTransportNTCP: - { - s << "\r\n"; - break; - } - case i2p::data::RouterInfo::eTransportSSU: - { - s << "\r\n"; - break; - } - default: - s << "\r\n"; - } - s << "\r\n\r\n"; + s << "\r\n\r\n"; + continue; } - s << "
"<< tr("Family") << "" << family << "
\r\n"; + if (!i2p::context.GetRouterInfo().GetProperty("family").empty()) + s << "
" << tr("Router Family") << "
NTCP2"; - if (address->host.is_v6 ()) s << "v6"; - s << "" << tr("supported") << "
NTCP"; - if (address->IsPublishedNTCP2 ()) s << "2"; - if (address->host.is_v6 ()) s << "v6"; - s << "SSU"; - if (address->host.is_v6 ()) - s << "v6"; - s << "" << tr("Unknown") << "" << address->host.to_string() << ":" << address->port << "
NTCP2"; + if (address->host.is_v6 ()) s << "v6"; + s << "" << tr("supported") << "
\r\n"; + switch (address->transportStyle) + { + case i2p::data::RouterInfo::eTransportNTCP: + { + s << "NTCP"; + if (address->IsPublishedNTCP2 ()) s << "2"; + if (address->host.is_v6 ()) s << "v6"; + s << "\r\n"; + break; + } + case i2p::data::RouterInfo::eTransportSSU: + { + s << "SSU"; + if (address->host.is_v6 ()) + s << "v6"; + s << "\r\n"; + break; + } + default: + s << "" << tr("Unknown") << "\r\n"; + } + s << "\r\n\r\n"; } - s << "
\r\n
\r\n"; - if(outputFormat == OutputFormatEnum::forQtUi) { - s << "
"; - } - s << "" << tr("Routers") << ": " << i2p::data::netdb.GetNumRouters () << " "; - s << "" << tr("Floodfills") << ": " << i2p::data::netdb.GetNumFloodfills () << " "; - s << "" << tr("LeaseSets") << ": " << i2p::data::netdb.GetNumLeaseSets () << "
\r\n"; +*/ + s << "" << tr("Routers") << "" << i2p::data::netdb.GetNumRouters () << "\r\n"; + s << "" << tr("Floodfills") << "" << i2p::data::netdb.GetNumFloodfills () << "\r\n"; + s << "" << tr("LeaseSets") << "" << i2p::data::netdb.GetNumLeaseSets () << "\r\n"; size_t clientTunnelCount = i2p::tunnel::tunnels.CountOutboundTunnels(); clientTunnelCount += i2p::tunnel::tunnels.CountInboundTunnels(); - size_t transitTunnelCount = i2p::tunnel::tunnels.CountTransitTunnels(); + std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("Client Tunnels") << ": " << std::to_string(clientTunnelCount) << " "; - s << "" << tr("Transit Tunnels") << ": " << std::to_string(transitTunnelCount) << "
\r\n
\r\n"; + s << "" << tr("Service Tunnels") << "" << std::to_string(clientTunnelCount) << "\r\n"; + if (i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels()) { + s << "" << tr("Transit Tunnels") << "" << std::to_string(i2p::tunnel::tunnels.CountTransitTunnels()) << "\r\n"; + } if(outputFormat==OutputFormatEnum::forWebConsole) { bool httpproxy = i2p::client::context.GetHttpProxy () ? true : false; @@ -431,33 +541,52 @@ namespace http { bool sam = i2p::client::context.GetSAMBridge () ? true : false; bool i2cp = i2p::client::context.GetI2CPServer () ? true : false; bool i2pcontrol; i2p::config::GetOption("i2pcontrol.enabled", i2pcontrol); - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "\r\n"; - s << "
" << tr("Services") << "
" << "HTTP " << tr("Proxy") << "" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "
" << "SOCKS " << tr("Proxy") << "" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "
" << "BOB" << "" << (bob ? tr("Enabled") : tr("Disabled")) << "
" << "SAM" << "" << (sam ? tr("Enabled") : tr("Disabled")) << "
" << "I2CP" << "" << (i2cp ? tr("Enabled") : tr("Disabled")) << "
" << "I2PControl" << "" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "
\r\n"; + if (httpproxy || socksproxy || bob || sam || i2cp || i2pcontrol) { + s << "" << tr("Active Router Services") << "\r\n"; + s << ""; + if (httpproxy) + s << " HTTP " << tr("Proxy") << " "; + if (socksproxy) + s << " SOCKS " << tr("Proxy") << " "; + if (bob) + s << " BOB "; + if (sam) + s << " SAM "; + if (i2cp) + s << " I2CP "; + if (i2pcontrol) + s << " I2PControl "; + s << ""; + } +/* + s << "" << "HTTP " << tr("Proxy") << "" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "\r\n"; + s << "" << "SOCKS " << tr("Proxy") << "" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "\r\n"; + s << "" << "BOB" << "" << (bob ? tr("Enabled") : tr("Disabled")) << "\r\n"; + s << "" << "SAM" << "" << (sam ? tr("Enabled") : tr("Disabled")) << "\r\n"; + s << "" << "I2CP" << "" << (i2cp ? tr("Enabled") : tr("Disabled")) << "\r\n"; + s << "" << "I2PControl" << "" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "\r\n"; +*/ } + + s << "\r\n\r\n
\r\n"; } void ShowLocalDestinations (std::stringstream& s) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("Local Destinations") << ":
\r\n
\r\n"; + s << "" << tr("Local Destinations") << "\r\n
\r\n"; for (auto& it: i2p::client::context.GetDestinations ()) { auto ident = it.second->GetIdentHash (); s << "\r\n" << std::endl; } - s << "
\r\n"; + s << "\r\n"; auto i2cpServer = i2p::client::context.GetI2CPServer (); if (i2cpServer && !(i2cpServer->GetSessions ().empty ())) { - s << "
I2CP "<< tr("Local Destinations") << ":
\r\n
\r\n"; + s << "I2CP "<< tr("Local Destinations") << "\r\n
\r\n"; for (auto& it: i2cpServer->GetSessions ()) { auto dest = it.second->GetDestination (); @@ -466,155 +595,212 @@ namespace http { auto ident = dest->GetIdentHash (); auto& name = dest->GetNickname (); s << "
[ "; - s << name << " ] ⇔ " << i2p::client::context.GetAddressBook ().ToAddress(ident) <<"
\r\n" << std::endl; + s << name << " ] " << i2p::client::context.GetAddressBook ().ToAddress(ident) <<"
\r\n" << std::endl; } } - s << "
\r\n"; + s << "\r\n"; } } static void ShowLeaseSetDestination (std::stringstream& s, std::shared_ptr dest, uint32_t token) { - s << "Base64:
\r\n
\r\n
\r\n"; + s << "\r\n"; + s << "
\r\n" + << "\r\n"; + s << "
\r\n
"; + s << dest->GetIdentity ()->ToBase64 () << "
\r\n
\r\n
\r\n\r\n"; if (dest->IsEncryptedLeaseSet ()) { i2p::data::BlindedPublicKey blinded (dest->GetIdentity (), dest->IsPerClientAuth ()); - s << "
\r\n\r\n
\r\n"; - s << blinded.ToB33 () << ".b32.i2p
\r\n"; - s << "
\r\n
\r\n"; + s << "" << tr("Encrypted B33 Address") << "\r\n"; + s << "" << blinded.ToB33 () << ".b32.i2p\r\n"; } if(dest->IsPublic()) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); auto base32 = dest->GetIdentHash ().ToBase32 (); - s << "
\r\n\r\n
\r\n" - "
\r\n" - " \r\n" - " \r\n" - " \r\n" - " " << tr("Domain") << ":\r\n\r\n" - " \r\n" - "
\r\n" << tr("Note: result string can be used only for registering 2LD domains (example.i2p). For registering subdomains please use i2pd-tools.") << "\r\n
\r\n
\r\n
\r\n"; + s << "" << tr("Address Registration String") << "\r\n" + "
\r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + " \r\n" + "
\r\n
"; + s << tr("Note: Result string can be used only for registering 2LD domains (example.i2p).") + << " " << tr("For registering subdomains, please use i2pd-tools."); + s << "
\r\n\r\n"; } if(dest->GetNumRemoteLeaseSets()) { - s << "
\r\n\r\n
\r\n"; + s << "\r\n" + << "" + << "" + << "\r\n"; + s << "\r\n
"<< tr("Address") << "" << tr("Type") << "" << tr("EncType") << "
\r\n"; + s << "
\r\n\r\n" + << "\r\n"; + s << "
\r\n\r\n\r\n" + << "" + << "" + << "" + << "\r\n\r\n"; for(auto& it: dest->GetLeaseSets ()) - s << "\r\n"; - s << "
" << tr("Address") << "" << tr("Type") << "" << tr("EncType") << "
" << it.first.ToBase32 () << "" << (int)it.second->GetStoreType () << "" << (int)it.second->GetEncryptionType () <<"
\r\n
\r\n
\r\n
\r\n"; + s << "
" << it.first.ToBase32 () << "" << (int)it.second->GetStoreType () << "" << (int)it.second->GetEncryptionType () <<"
\r\n
\r\n
\r\n\r\n"; } else - s << "" << tr("LeaseSets") << ": 0
\r\n
\r\n"; + s << "" << tr("No LeaseSets currently active") << "\r\n"; auto pool = dest->GetTunnelPool (); if (pool) { - s << "" << tr("Inbound tunnels") << ":
\r\n
\r\n"; - for (auto & it : pool->GetInboundTunnels ()) { - s << "
"; + s << "\r\n"; + s << "
\r\n\r\n" + << "\r\n"; + s << "
\r\n
\r\n"; + for (auto & it : pool->GetInboundTunnels ()) { // inbound tunnels + s << "
" + << "[" << tr("In") << "] " + << ""; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << " )"; + s << " " + << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << ""; + else // placeholder for alignment + s << " ---"; ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); - s << "
\r\n"; + s << "
\r\n"; } - s << "
\r\n"; - s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; - for (auto & it : pool->GetOutboundTunnels ()) { - s << "
"; + for (auto & it : pool->GetOutboundTunnels ()) { // outbound tunnels + s << "
" + << "[" << tr("Out") << "] " + << ""; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; + s << " " + << it->GetMeanLatency() << tr("ms") << ""; + else // placeholder for alignment + s << " ---"; ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); - s << "
\r\n"; + s << "
\r\n"; } } - s << "
\r\n"; + s << "
\r\n
\r\n
\r\n\r\n"; - s << "" << tr("Tags") << "
\r\n" << tr("Incoming") << ": " << dest->GetNumIncomingTags () << "
\r\n"; + if (dest->GetNumIncomingTags () > 0) { + s << "" << tr("Incoming Session Tags") << " [" << dest->GetNumIncomingTags () << "]\r\n"; + } else { + s << "" << tr("No Incoming Session Tags") << "\r\n"; + } if (!dest->GetSessions ().empty ()) { std::stringstream tmp_s; uint32_t out_tags = 0; + s << ""; for (const auto& it: dest->GetSessions ()) { - tmp_s << "" << i2p::client::context.GetAddressBook ().ToAddress(it.first) << "" << it.second->GetNumOutgoingTags () << "\r\n"; + tmp_s << "" << i2p::client::context.GetAddressBook ().ToAddress(it.first) + << "" << it.second->GetNumOutgoingTags () << "\r\n"; out_tags += it.second->GetNumOutgoingTags (); } - s << "
\r\n\r\n" - << "
\r\n\r\n\r\n\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Amount") << "
\r\n
\r\n
\r\n"; + s << "" << tr("Outgoing Session Tags") << " [" << out_tags << "]\r\n" + << "\r\n" + << "\r\n\r\n\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Count") + << "
\r\n\r\n"; } else - s << tr("Outgoing") << ": 0
\r\n"; - s << "
\r\n"; + s << "" << tr("No Outgoing Session Tags") << "\r\n"; auto numECIESx25519Tags = dest->GetNumIncomingECIESx25519Tags (); if (numECIESx25519Tags > 0) { - s << "ECIESx25519
\r\n" << tr("Incoming Tags") << ": " << numECIESx25519Tags << "
\r\n"; + s << "ECIESx25519
\r\n" << tr("Incoming Tags") << " [" << numECIESx25519Tags << "]\r\n"; if (!dest->GetECIESx25519Sessions ().empty ()) { std::stringstream tmp_s; uint32_t ecies_sessions = 0; for (const auto& it: dest->GetECIESx25519Sessions ()) { - tmp_s << "" << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) << "" << it.second->GetState () << "\r\n"; + tmp_s << "" + << i2p::client::context.GetAddressBook ().ToAddress(it.second->GetDestination ()) + << "" << it.second->GetState () << "\r\n"; ecies_sessions++; } - s << "
\r\n\r\n" - << "
\r\n\r\n\r\n\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Status") << "
\r\n
\r\n
\r\n"; + s << "\r\n" + << "
\r\n" + << "\r\n" + << "
\r\n\r\n\r\n\r\n" << tmp_s.str () << "
" << tr("Destination") << "" + << tr("Status") << "
\r\n
\r\n
\r\n"; } else - s << tr("Tags sessions") << ": 0
\r\n"; - s << "
\r\n"; + s << "" << tr("No Tag Sessions") << "\r\n"; } } void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token) { - s << "" << tr("Local Destination") << ":
\r\n
\r\n"; i2p::data::IdentHash ident; ident.FromBase32 (b32); auto dest = i2p::client::context.FindLocalDestination (ident); + if (dest) { + std::string b32Short = b32.substr(0,6); + s << "" << tr("Local Destination") << " [" << b32Short << "]\r\n"; + } else + s << "" << tr("Local Destination") << " [" << tr("Not Found") << "]\r\n"; if (dest) { ShowLeaseSetDestination (s, dest, token); - // Print table with streams information - s << "\r\n\r\n\r\n"; - s << ""; - s << "
" << tr("Streams") << "
StreamID"; // Stream closing button column + s << "
\r\n"; + s << "
\r\n\r\n" + << "\r\n"; + s << "
\r\n\r\n\r\n"; + s << ""; s << ""; - s << ""; - s << ""; + s << ""; + s << ""; s << ""; s << ""; s << ""; s << ""; - s << ""; + s << ""; s << ""; - s << "\r\n\r\n\r\n"; + s << "\r\n\r\n"; + s << "\r\n"; for (const auto& it: dest->GetAllStreams ()) { auto streamDest = i2p::client::context.GetAddressBook ().ToAddress(it->GetRemoteIdentity ()); - std::string streamDestShort = streamDest.substr(0,12) + "….b32.i2p"; + std::string streamDestShort = streamDest.substr(0,10) + "…b32.i2p"; s << ""; - s << ""; - if (it->GetRecvStreamID ()) { - s << ""; - } else { - s << ""; s << ""; - s << ""; - s << ""; + s << std::fixed << std::setprecision(0); + if (it->GetNumSentBytes () > 1024 * 1024 * 1024) { + s << std::fixed << std::setprecision(2); + s << ""; + } else if (it->GetNumSentBytes () > 1024 * 1024) { + s << std::fixed << std::setprecision(2); + s << ""; + } else { + s << ""; + } + if (it->GetNumReceivedBytes () > 1024 * 1024 * 1024) { + s << std::fixed << std::setprecision(2); + s << ""; + } else if (it->GetNumReceivedBytes () > 1024 * 1024) { + s << std::fixed << std::setprecision(1); + s << ""; + } else { + s << ""; + } s << ""; s << ""; s << ""; s << ""; s << ""; - s << ""; + s << ""; s << "\r\n"; } - s << "\r\n
IDDestinationSentReceivedTXRXOutInBufRTTWindowWinStatus
" << it->GetRecvStreamID () << ""; - } + s << "" + << " " << it->GetRecvStreamID () << "" << streamDestShort << "" << it->GetNumSentBytes () << "" << it->GetNumReceivedBytes () << "" << (double) it->GetNumSentBytes () / 1024 / 1024 / 1024 << "G" << (double) it->GetNumSentBytes () / 1024 / 1024 << "M" << it->GetNumSentBytes () / 1024 << "K" << (double) it->GetNumReceivedBytes () / 1024 / 1024 / 1024 << "G" << (double) it->GetNumReceivedBytes () / 1024 / 1024 << "M" << it->GetNumReceivedBytes () / 1024 << "K" << it->GetSendQueueSize () << "" << it->GetReceiveQueueSize () << "" << it->GetSendBufferSize () << "" << it->GetRTT () << "" << it->GetWindowSize () << "" << (int)it->GetStatus () << "" << (int) it->GetStatus () << "
"; + s << "\r\n
\r\n
\r\n
\r\n"; } } @@ -638,7 +824,7 @@ namespace http { { if (i2p::data::netdb.GetNumLeaseSets ()) { - s << "" << tr("LeaseSets") << ":
\r\n
\r\n"; + s << "" << tr("LeaseSets") << "\r\n
\r\n"; int counter = 1; // for each lease set i2p::data::netdb.VisitLeaseSets( @@ -658,8 +844,9 @@ namespace http { s << "\">\r\n"; if (!ls->IsValid()) s << "
!! " << tr("Invalid") << " !!
\r\n"; - s << "
\r\n"; - s << "\r\n
\r\n"; + s << "
\r\n" + << "\r\n"; + s << "
\r\n"; s << "" << tr("Store type") << ": " << (int)storeType << "
\r\n"; s << "" << tr("Expires") << ": " << ConvertTime(ls->GetExpirationTime()) << "
\r\n"; if (storeType == i2p::data::NETDB_STORE_TYPE_LEASESET || storeType == i2p::data::NETDB_STORE_TYPE_STANDARD_LEASESET2) @@ -677,53 +864,63 @@ namespace http { s << "
\r\n
\r\n
\r\n"; } ); + s << "\r\n"; // end for each lease set } else if (!i2p::context.IsFloodfill ()) { - s << "" << tr("LeaseSets") << ": " << tr("not floodfill") << ".
\r\n"; + s << "" << tr("No LeaseSets") << " (" << tr("not floodfill") << ")\r\n"; } else { - s << "" << tr("LeaseSets") << ": 0
\r\n"; + s << "" << tr("No LeaseSets") << "\r\n"; } } void ShowTunnels (std::stringstream& s) { - s << "" << tr("Tunnels") << ":
\r\n"; - s << "" << tr("Queue size") << ": " << i2p::tunnel::tunnels.GetQueueSize () << "
\r\n
\r\n"; + s << "" << tr("Tunnels") << "\r\n"; + s << "" << tr("Queue size") << ": " << i2p::tunnel::tunnels.GetQueueSize () << "\r\n"; auto ExplPool = i2p::tunnel::tunnels.GetExploratoryPool (); - s << "" << tr("Inbound tunnels") << ":
\r\n
\r\n"; + s << "\r\n"; + s << "
\r\n\r\n" + << "\r\n"; // TODO: separate client & exploratory tunnels into sections + s << "
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { - s << "
"; + s << "
" + << "[" << tr("In") << "] " + << ""; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; + s << " " << it->GetMeanLatency() << tr("ms") << ""; + else // placeholder for alignment + s << " ---"; ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); - s << "
\r\n"; + s << "
\r\n"; } - s << "
\r\n
\r\n"; - s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) { - s << "
"; + s << "
" + << "[" << tr("Out") << "] " + << ""; it->Print(s); if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; + s << " " << it->GetMeanLatency() << tr("ms") << ""; + else // placeholder for alignment + s << " ---"; ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); - s << "
\r\n"; + s << "\r\n
\r\n"; } - s << "
\r\n"; + s << "
\r\n
\r\n
\r\n\r\n"; } static void ShowCommands (std::stringstream& s, uint32_t token) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("Router commands") << "
\r\n
\r\n
\r\n"; - s << " " << tr("Run peer test") << "
\r\n"; + s << "" << tr("Router Commands") << "\r\n"; + s << " " << tr("Run peer test") << "
\r\n"; // s << " Reload config
\r\n"; @@ -732,42 +929,47 @@ namespace http { else s << " " << tr("Accept transit tunnels") << "
\r\n"; + if (i2p::tunnel::tunnels.CountTransitTunnels()) { #if ((!defined(WIN32) && !defined(QT_GUI_LIB) && !defined(ANDROID)) || defined(ANDROID_BINARY)) - if (Daemon.gracefulShutdownInterval) - s << " " << tr("Cancel graceful shutdown") << "
\r\n"; - else - s << " " << tr("Start graceful shutdown") << "
\r\n"; + if (Daemon.gracefulShutdownInterval) + s << " " << tr("Cancel graceful shutdown") << "
\r\n"; + else + s << " " << tr("Start graceful shutdown") << "
\r\n"; #elif defined(WIN32_APP) - if (i2p::util::DaemonWin32::Instance().isGraceful) - s << " " << tr("Cancel graceful shutdown") << "
\r\n"; - else - s << " " << tr("Start graceful shutdown") << "
\r\n"; + if (i2p::util::DaemonWin32::Instance().isGraceful) + s << " " << tr("Cancel graceful shutdown") << "
\r\n"; + else + s << " " << tr("Start graceful shutdown") << "
\r\n"; #endif + s << " " << tr("Force shutdown") << "\r\n"; + } else { + s << " " << tr("Shutdown") << "\r\n"; + } + s << "" + << tr("Reload external CSS styles") << "\r\n"; - s << " " << tr("Force shutdown") << "

\r\n"; - s << " " << tr("Reload external CSS styles") << "\r\n"; - s << "
"; + s << "\r\n
" << tr("Note: Configuration changes made here persist for the duration of the router session and will not be saved to your config file.") << "
\r\n"; - s << "
\r\n" << tr("Note: any action done here are not persistent and not changes your config files.") << "\r\n
\r\n"; - - s << "" << tr("Logging level") << "
\r\n"; + s << "" << tr("Logging Level") << "\r\n"; s << " none \r\n"; s << " error \r\n"; s << " warn \r\n"; s << " info \r\n"; - s << " debug
\r\n
\r\n"; + s << " debug \r\n"; - uint16_t maxTunnels = GetMaxNumTransitTunnels (); - s << "" << tr("Transit tunnels limit") << "
\r\n"; - s << "
\r\n"; - s << " \r\n"; - s << " \r\n"; - s << " \r\n"; - s << " \r\n"; - s << "
\r\n
\r\n"; + if (i2p::context.AcceptsTunnels ()) { + uint16_t maxTunnels = GetMaxNumTransitTunnels (); + s << "" << tr("Maximum Transit Tunnels") << "\r\n\r\n"; + s << "
\r\n"; + s << " \r\n"; + s << " \r\n"; + s << " \r\n"; + s << " \r\n"; + s << "
\r\n\r\n"; + } std::string currLang = i2p::context.GetLanguage ()->GetLanguage(); // get current used language - s << "" << tr("Change language") << "
\r\n"; + s << "" << tr("Console Display Language") << "\r\n\r\n"; s << "
\r\n"; s << " \r\n"; s << " \r\n"; @@ -775,8 +977,8 @@ namespace http { for (const auto& it: i2p::i18n::languages) s << " \r\n"; s << " \r\n"; - s << " \r\n"; - s << "
\r\n
\r\n"; + s << " \r\n"; + s << "\r\n\r\n"; } @@ -784,23 +986,24 @@ namespace http { { if(i2p::tunnel::tunnels.CountTransitTunnels()) { - s << "" << tr("Transit Tunnels") << ":
\r\n
\r\n"; + s << "" << tr("Transit Tunnels") << "\r\n
\r\n"; for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) { - s << "
\r\n"; + s << "
"; if (std::dynamic_pointer_cast(it)) - s << it->GetTunnelID () << " ⇒ "; + s << it->GetTunnelID () << " "; else if (std::dynamic_pointer_cast(it)) - s << " ⇒ " << it->GetTunnelID (); + s << " " << "" << it->GetTunnelID () << ""; else - s << " ⇒ " << it->GetTunnelID () << " ⇒ "; - s << " " << it->GetNumTransmittedBytes () << "
\r\n"; + s << " " << it->GetTunnelID () + << " "; + s << " " << it->GetNumTransmittedBytes () << "B
\r\n"; } - s << "
\r\n"; + s << "
\r\n"; } else { - s << "" << tr("Transit Tunnels") << ": " << tr("no transit tunnels currently built") << ".
\r\n"; + s << "" << tr("No active transit tunnels") << "\r\n"; } } @@ -812,44 +1015,86 @@ namespace http { { if (it.second && it.second->IsEstablished () && !it.second->GetRemoteEndpoint ().address ().is_v6 ()) { - tmp_s << "
\r\n"; - if (it.second->IsOutgoing ()) tmp_s << " ⇒ "; - tmp_s << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << it.second->GetRemoteEndpoint ().address ().to_string (); - if (!it.second->IsOutgoing ()) tmp_s << " ⇒ "; - tmp_s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - tmp_s << "
\r\n" << std::endl; + tmp_s << "
"; + if (it.second->IsOutgoing ()) + tmp_s << ""; + else + tmp_s << ""; + tmp_s << " "; + tmp_s << "" << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << "" + << " GetRemoteEndpoint ().address ().to_string () << "\" data-tooltip=\"" + << tr("Lookup address on gwhois.org") << "\">" + << it.second->GetRemoteEndpoint ().address ().to_string () << ""; + tmp_s << std::fixed << std::setprecision(0); + if (it.second->GetNumSentBytes () > 1024 * 1024) { + tmp_s << std::fixed << std::setprecision(1); + tmp_s << " " << (double) it.second->GetNumSentBytes () / 1024 / 1024 << "M"; + } else { + tmp_s << " " << (double) it.second->GetNumSentBytes () / 1024 << "K"; + } + tmp_s << std::fixed << std::setprecision(0); + if (it.second->GetNumReceivedBytes () > 1024 * 1024) { + tmp_s << std::fixed << std::setprecision(1); + tmp_s << " " << (double) it.second->GetNumReceivedBytes () / 1024 / 1024 << "M"; + } else { + tmp_s << " " << (double) it.second->GetNumReceivedBytes () / 1024 << "K"; + } + tmp_s << "
\r\n" << std::endl; cnt++; } if (it.second && it.second->IsEstablished () && it.second->GetRemoteEndpoint ().address ().is_v6 ()) { - tmp_s6 << "
\r\n"; - if (it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; - tmp_s6 << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << ": " - << "[" << it.second->GetRemoteEndpoint ().address ().to_string () << "]"; - if (!it.second->IsOutgoing ()) tmp_s6 << " ⇒ "; - tmp_s6 << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; - tmp_s6 << "
\r\n" << std::endl; + tmp_s6 << "
"; + if (it.second->IsOutgoing ()) + tmp_s6 << ""; + else + tmp_s6 << ""; + tmp_s6 << " "; + tmp_s6 << "" << i2p::data::GetIdentHashAbbreviation (it.second->GetRemoteIdentity ()->GetIdentHash ()) << "" + << " GetRemoteEndpoint ().address ().to_string () << "\" data-tooltip=\"" + << tr("Lookup address on gwhois.org") << "\">" + << it.second->GetRemoteEndpoint ().address ().to_string () << ""; + tmp_s6 << std::fixed << std::setprecision(0); + if (it.second->GetNumSentBytes () > 1024 * 1024) { + tmp_s6 << std::fixed << std::setprecision(1); + tmp_s6 << " " << (double) it.second->GetNumSentBytes () / 1024 / 1024 << "M"; + } else { + tmp_s6 << " " << (double) it.second->GetNumSentBytes () / 1024 << "K"; + } + tmp_s6 << " /"; + tmp_s6 << std::fixed << std::setprecision(0); + if (it.second->GetNumReceivedBytes () > 1024 * 1024) { + tmp_s6 << std::fixed << std::setprecision(1); + tmp_s6 << " " << (double) it.second->GetNumReceivedBytes () / 1024 / 1024 << "M"; + } else { + tmp_s6 << " " << (double) it.second->GetNumReceivedBytes () / 1024 << "K"; + } + tmp_s6 << "
\r\n" << std::endl; cnt6++; } } if (!tmp_s.str ().empty ()) { - s << "
\r\n\r\n
" + s << "
\r\n\r\n
" << tmp_s.str () << "
\r\n
\r\n"; } if (!tmp_s6.str ().empty ()) { - s << "
\r\n\r\n
" + s << "
\r\n" + << "\r\n
" << tmp_s6.str () << "
\r\n
\r\n"; } } void ShowTransports (std::stringstream& s) { - s << "" << tr("Transports") << ":
\r\n"; + s << "" << tr("Transports") << "\r\n" + << ""; auto ntcp2Server = i2p::transport::transports.GetNTCP2Server (); if (ntcp2Server) { @@ -863,38 +1108,81 @@ namespace http { auto sessions = ssuServer->GetSessions (); if (!sessions.empty ()) { - s << "
\r\n\r\n
"; + s << "
\r\n" + << "\r\n" + << "
\r\n"; for (const auto& it: sessions) { - s << "
\r\n"; + s << "
"; + if (it.second->IsOutgoing ()) + s << ""; + else + s << ""; + s << " "; auto endpoint = it.second->GetRemoteEndpoint (); - if (it.second->IsOutgoing ()) s << " ⇒ "; - s << endpoint.address ().to_string () << ":" << endpoint.port (); - if (!it.second->IsOutgoing ()) s << " ⇒ "; - s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; +// s << " " << endpoint.address ().to_string () << ":" << endpoint.port () << ""; + s << " " + << endpoint.address ().to_string () << ":" << endpoint.port () << ""; + s << std::fixed << std::setprecision(0); + if (it.second->GetNumSentBytes () > 1024 * 1024) { + s << std::fixed << std::setprecision(1); + s << " " << (double) it.second->GetNumSentBytes () / 1024 / 1024 << "M"; + } else { + s << " " << (double) it.second->GetNumSentBytes () / 1024 << "K"; + } + s << " /"; + s << std::fixed << std::setprecision(0); + if (it.second->GetNumReceivedBytes () > 1024 * 1024) { + s << std::fixed << std::setprecision(1); + s << " " << (double) it.second->GetNumReceivedBytes () / 1024 / 1024 << "M"; + } else { + s << " " << (double) it.second->GetNumReceivedBytes () / 1024 << "K"; + } if (it.second->GetRelayTag ()) - s << " [itag:" << it.second->GetRelayTag () << "]"; - s << "
\r\n" << std::endl; + s << " " << it.second->GetRelayTag () << ""; + s << "
\r\n" << std::endl; } s << "
\r\n
\r\n"; } auto sessions6 = ssuServer->GetSessionsV6 (); if (!sessions6.empty ()) { - s << "
\r\n\r\n
"; + s << "
\r\n\r\n" + << "\r\n" + << "
\r\n"; for (const auto& it: sessions6) { - s << "
\r\n"; + s << "
"; + if (it.second->IsOutgoing ()) + s << ""; + else + s << ""; + s << " "; auto endpoint = it.second->GetRemoteEndpoint (); - if (it.second->IsOutgoing ()) s << " ⇒ "; - s << "[" << endpoint.address ().to_string () << "]:" << endpoint.port (); - if (!it.second->IsOutgoing ()) s << " ⇒ "; - s << " [" << it.second->GetNumSentBytes () << ":" << it.second->GetNumReceivedBytes () << "]"; + s << " " << endpoint.address ().to_string () << ":" << endpoint.port () << ""; + s << std::fixed << std::setprecision(0); + + if (it.second->GetNumSentBytes () > 1024 * 1024) { + s << std::fixed << std::setprecision(1); + s << " " << (double) it.second->GetNumSentBytes () / 1024 / 1024 << "M"; + } else { + s << " " << (double) it.second->GetNumSentBytes () / 1024 << "K"; + } + s << " /"; + s << std::fixed << std::setprecision(0); + if (it.second->GetNumReceivedBytes () > 1024 * 1024) { + s << std::fixed << std::setprecision(1); + s << " " << (double) it.second->GetNumReceivedBytes () / 1024 / 1024 << "M"; + } else { + s << " " << (double) it.second->GetNumReceivedBytes () / 1024 << "K"; + } if (it.second->GetRelayTag ()) - s << " [itag:" << it.second->GetRelayTag () << "]"; - s << "
\r\n" << std::endl; + s << " " << it.second->GetRelayTag () << ""; + s << "\r\n
\r\n" << std::endl; } - s << "
\r\n
\r\n"; + s << "
\r\n
\r\n\r\n"; } } } @@ -909,19 +1197,20 @@ namespace http { return; } - if(sam->GetSessions ().size ()) + if (sam->GetSessions ().size ()) { - s << "" << tr("SAM sessions") << ":
\r\n
\r\n"; + s << "" << tr("SAM sessions") + << "\r\n\r\n
\r\n"; for (auto& it: sam->GetSessions ()) { auto& name = it.second->GetLocalDestination ()->GetNickname (); s << "\r\n" << std::endl; } - s << "
\r\n"; + s << "
\r\n\r\n"; } else - s << "" << tr("SAM sessions") << ": " << tr("no sessions currently running") << ".
\r\n"; + s << "" << tr("No active SAM sessions") << "\r\n"; } void ShowSAMSession (std::stringstream& s, const std::string& id) @@ -939,12 +1228,12 @@ namespace http { } std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("SAM Session") << ":
\r\n
\r\n"; + s << "" << tr("SAM Session") << "\r\n\r\n
\r\n"; auto& ident = session->GetLocalDestination ()->GetIdentHash(); s << "\r\n"; s << "
\r\n"; - s << "" << tr("Streams") << ":
\r\n
\r\n"; + s << "" << tr("Streams") << "\r\n
\r\n"; for (const auto& it: sam->ListSockets(id)) { s << "
"; @@ -959,83 +1248,85 @@ namespace http { s << " [" << it->GetSocket ().remote_endpoint() << "]"; s << "
\r\n"; } - s << "
\r\n"; + s << "
\r\n"; } void ShowI2PTunnels (std::stringstream& s) { std::string webroot; i2p::config::GetOption("http.webroot", webroot); - s << "" << tr("Client Tunnels") << ":
\r\n
\r\n"; + s << "" << tr("Service Tunnels") << "" + << "\r\n
\r\n"; for (auto& it: i2p::client::context.GetClientTunnels ()) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); s << "
"; - s << it.second->GetName () << " ⇐ "; + s << it.second->GetName () << " "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; + s << "
\r\n"<< std::endl; } auto httpProxy = i2p::client::context.GetHttpProxy (); if (httpProxy) { auto& ident = httpProxy->GetLocalDestination ()->GetIdentHash(); s << "
"; - s << "HTTP " << tr("Proxy") << " ⇐ "; + s << "HTTP " << tr("Proxy") << " "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; + s << "
\r\n"<< std::endl; } auto socksProxy = i2p::client::context.GetSocksProxy (); if (socksProxy) { auto& ident = socksProxy->GetLocalDestination ()->GetIdentHash(); s << "
"; - s << "SOCKS " << tr("Proxy") << " ⇐ "; + s << "SOCKS " << tr("Proxy") << " "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; + s << "
\r\n" << std::endl; } - s << "
\r\n"; + s << "
\r\n"; auto& serverTunnels = i2p::client::context.GetServerTunnels (); if (!serverTunnels.empty ()) { - s << "
\r\n" << tr("Server Tunnels") << ":
\r\n
\r\n"; + s << "" << tr("Server Tunnels") << "\r\n
\r\n"; for (auto& it: serverTunnels) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); s << "
"; - s << it.second->GetName () << " ⇒ "; + s << it.second->GetName () << " "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); s << ":" << it.second->GetLocalPort (); - s << "
\r\n"<< std::endl; + s << "
\r\n" << std::endl; } - s << "
\r\n"; + s << "
\r\n"; } auto& clientForwards = i2p::client::context.GetClientForwards (); if (!clientForwards.empty ()) { - s << "
\r\n" << tr("Client Forwards") << ":
\r\n
\r\n"; + s << "" << tr("Client Forwards") << "\r\n
\r\n"; for (auto& it: clientForwards) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); s << "
"; - s << it.second->GetName () << " ⇐ "; + s << it.second->GetName () << " "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; + s << "
\r\n"<< std::endl; } - s << "
\r\n"; + s << "
\r\n"; } auto& serverForwards = i2p::client::context.GetServerForwards (); if (!serverForwards.empty ()) { - s << "
\r\n" << tr("Server Forwards") << ":
\r\n
\r\n"; + s << "" << tr("Server Forwards") << "\r\n" + << "\r\n
\r\n"; for (auto& it: serverForwards) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); s << ""; - s << it.second->GetName () << " ⇐ "; + s << it.second->GetName () << " "; s << i2p::client::context.GetAddressBook ().ToAddress(ident); - s << "
\r\n"<< std::endl; + s << "
\r\n"<< std::endl; } - s << "
\r\n"; + s << "
\r\n"; } } @@ -1238,7 +1529,7 @@ namespace http { url.parse_query(params); std::string webroot; i2p::config::GetOption("http.webroot", webroot); - std::string redirect = "5; url=" + webroot + "?page=commands"; + std::string redirect = "2; url=" + webroot + "?page=commands"; std::string token = params["token"]; if (token.empty () || m_Tokens.find (std::stoi (token)) == m_Tokens.end ()) @@ -1301,19 +1592,21 @@ namespace http { if (dest) { if(dest->DeleteStream (streamID)) - s << "" << tr("SUCCESS") << ": " << tr("Stream closed") << "
\r\n
\r\n"; + s << "" << tr("SUCCESS") << ": " + << tr("Stream closed") << "\r\n"; else - s << "" << tr("ERROR") << ": " << tr("Stream not found or already was closed") << "
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " + << tr("Stream not found or already was closed") << "\r\n"; } else - s << "" << tr("ERROR") << ": " << tr("Destination not found") << "
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " + << tr("Destination not found") << "\r\n"; } else - s << "" << tr("ERROR") << ": " << tr("StreamID can't be null") << "
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " + << tr("StreamID can't be null") << "\r\n"; - s << "" << tr("Return to destination page") << "
\r\n"; - s << "

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

" << tr("You will be redirected in 5 seconds") << ""; + s << "" << tr("ERROR") << ": " + << tr("Transit tunnels count must not exceed 65535") << "\r\n\r\n"; res.add_header("Refresh", redirect.c_str()); return; } @@ -1358,25 +1650,31 @@ namespace http { auto len = i2p::data::ByteStreamToBase64 (signature, signatureLen, sig, signatureLen*2); sig[len] = 0; out << "#!sig=" << sig; - s << "" << tr("SUCCESS") << ":
\r\n

\r\n" - "\r\n
\r\n
\r\n" - "" << tr("Register at reg.i2p") << ":\r\n
\r\n" - "" << tr("Description") << ":\r\n\r\n" - "\r\n" - "
\r\n
\r\n"; + s << "" << tr("SUCCESS") + << ":
\r\n
\r\n" + << "\r\n
\r\n
\r\n" + << "" << tr("Register at reg.i2p") << ":\r\n
\r\n" + << "" << tr("Description") << ":\r\n\r\n" + << "\r\n" + << "
\r\n\r\n"; delete[] signature; delete[] sig; } else - s << "" << tr("ERROR") << ": " << tr("Domain can't end with .b32.i2p") << "\r\n
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " + << tr("Domain can't end with .b32.i2p") << "\r\n
\r\n\r\n"; } else - s << "" << tr("ERROR") << ": " << tr("Domain must end with .i2p") << "\r\n
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " + << tr("Domain must end with .i2p") << "\r\n
\r\n\r\n"; } else - s << "" << tr("ERROR") << ": " << tr("Such destination is not found") << "\r\n
\r\n
\r\n"; + s << "" << tr("ERROR") << ": " + << tr("No such destination found") << "\r\n
\r\n<\r\n"; - s << "" << tr("Return to destination page") << "\r\n"; +// s << "" << tr("Return to destination page") << "\r\n"; return; } else if (cmd == HTTP_COMMAND_SETLANGUAGE) @@ -1398,9 +1696,10 @@ namespace http { return; } - s << "" << tr("SUCCESS") << ": " << tr("Command accepted") << "

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

" << tr("You will be redirected in 5 seconds") << ""; + s << "" << tr("SUCCESS") << ": " + << tr("Command accepted") << "\r\n"; +// s << "" << tr("Back to commands list") << "
\r\n"; +// s << "

" << tr("You will be redirected in 5 seconds") << ""; res.add_header("Refresh", redirect.c_str()); } diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index e7b38686..3c7c0a54 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -46,7 +46,7 @@ namespace tunnel const int numRecords = numHops <= STANDARD_NUM_RECORDS ? STANDARD_NUM_RECORDS : MAX_NUM_RECORDS; auto msg = numRecords <= STANDARD_NUM_RECORDS ? NewI2NPShortMessage () : NewI2NPMessage (); *msg->GetPayload () = numRecords; - const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; + const size_t recordSize = m_Config->IsShort () ? SHORT_TUNNEL_BUILD_RECORD_SIZE : TUNNEL_BUILD_RECORD_SIZE; msg->len += numRecords*recordSize + 1; // shuffle records std::vector recordIndicies; @@ -97,13 +97,13 @@ namespace tunnel { auto ident = m_Config->GetFirstHop () ? m_Config->GetFirstHop ()->ident : nullptr; if (ident && ident->GetIdentHash () != outboundTunnel->GetNextIdentHash ()) // don't encrypt if IBGW = OBEP - { + { auto msg1 = i2p::garlic::WrapECIESX25519MessageForRouter (msg, ident->GetEncryptionPublicKey ()); if (msg1) msg = msg1; - } - } + } + } outboundTunnel->SendTunnelDataMsg (GetNextIdentHash (), 0, msg); - } + } else { if (m_Config->IsShort () && m_Config->GetLastHop () && @@ -114,11 +114,11 @@ namespace tunnel uint64_t tag = m_Config->GetLastHop ()->GetGarlicKey (key); if (m_Pool && m_Pool->GetLocalDestination ()) m_Pool->GetLocalDestination ()->AddECIESx25519Key (key, tag); - else + else i2p::context.AddECIESx25519Key (key, tag); - } + } i2p::transport::transports.SendMessage (GetNextIdentHash (), msg); - } + } } bool Tunnel::HandleTunnelBuildResponse (uint8_t * msg, size_t len) @@ -133,14 +133,14 @@ namespace tunnel { if (!hop->DecryptBuildResponseRecord (msg + 1)) return false; - } + } else { LogPrint (eLogWarning, "Tunnel: hop index ", hop->recordIndex, " is out of range"); return false; - } - - // decrypt records before current hop + } + + // decrypt records before current hop TunnelHopConfig * hop1 = hop->prev; while (hop1) { @@ -235,8 +235,9 @@ namespace tunnel // hops are in inverted order, we must print in direct order for (auto it = m_Hops.rbegin (); it != m_Hops.rend (); it++) { - s << " ⇒ "; + s << " "; s << i2p::data::GetIdentHashAbbreviation ((*it)->ident->GetIdentHash ()); + s << ""; } } @@ -252,7 +253,10 @@ namespace tunnel void InboundTunnel::Print (std::stringstream& s) const { PrintHops (s); - s << " ⇒ " << GetTunnelID () << ":me"; + s << " "; + s << " Local"; + s << "" << GetTunnelID () << ""; } ZeroHopsInboundTunnel::ZeroHopsInboundTunnel (): @@ -273,7 +277,9 @@ namespace tunnel void ZeroHopsInboundTunnel::Print (std::stringstream& s) const { - s << " ⇒ " << GetTunnelID () << ":me"; + s << " Local"; + s << "" << GetTunnelID () << ""; } void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) @@ -312,9 +318,9 @@ namespace tunnel void OutboundTunnel::Print (std::stringstream& s) const { - s << GetTunnelID () << ":me"; + s << "Local"; PrintHops (s); - s << " ⇒ "; + s << "" << GetTunnelID () << ""; } ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel (): @@ -348,7 +354,10 @@ namespace tunnel void ZeroHopsOutboundTunnel::Print (std::stringstream& s) const { - s << GetTunnelID () << ":me ⇒ "; + s << " "; + s << "" << GetTunnelID () << "\">Local"; + s << "" << GetTunnelID () << ""; } Tunnels tunnels; @@ -530,7 +539,7 @@ namespace tunnel case eI2NPShortTunnelBuild: case eI2NPShortTunnelBuildReply: case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: + case eI2NPTunnelBuildReply: HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); break; default: @@ -561,8 +570,8 @@ namespace tunnel { ManageTunnelPools (ts); lastPoolsTs = ts; - } - } + } + } } catch (std::exception& ex) { @@ -837,7 +846,7 @@ namespace tunnel } template - std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, + std::shared_ptr Tunnels::CreateTunnel (std::shared_ptr config, std::shared_ptr pool, std::shared_ptr outboundTunnel) { auto newTunnel = std::make_shared (config); @@ -849,7 +858,7 @@ namespace tunnel return newTunnel; } - std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, + std::shared_ptr Tunnels::CreateInboundTunnel (std::shared_ptr config, std::shared_ptr pool, std::shared_ptr outboundTunnel) { if (config) @@ -913,7 +922,7 @@ namespace tunnel } - std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr pool) + std::shared_ptr Tunnels::CreateZeroHopsInboundTunnel (std::shared_ptr pool) { auto inboundTunnel = std::make_shared (); inboundTunnel->SetTunnelPool (pool); From b6d478977b10fa36d6c6ba1fa3677f0860957e94 Mon Sep 17 00:00:00 2001 From: dr|z3d Date: Wed, 11 Aug 2021 05:36:38 +0000 Subject: [PATCH 12/15] purple theme for new ui --- webconsole/style.css | 1043 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1043 insertions(+) create mode 100644 webconsole/style.css diff --git a/webconsole/style.css b/webconsole/style.css new file mode 100644 index 00000000..3a3c4b98 --- /dev/null +++ b/webconsole/style.css @@ -0,0 +1,1043 @@ +:root { + --bodyfont: Open Sans, Noto Sans, Ubuntu, Segoe UI, sans-serif; + --monospaced: Droid Sans Mono, Noto Mono, Lucida Console, DejaVu Sans Mono, monospace; + --logo: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 58 16'%3E%3Cg fill='%23525'%3E%3Ccircle cx='3.1' cy='2.8' r='1.2'/%3E%3Ccircle cx='3.1' cy='6.3' r='1.2'/%3E%3Ccircle cx='3.1' cy='9.7' r='1.2'/%3E%3Ccircle cx='3.1' cy='13.2' r='1.2'/%3E%3Ccircle cx='13.5' cy='2.8' r='1.2'/%3E%3Ccircle cx='13.5' cy='6.3' r='1.2'/%3E%3Ccircle cx='13.5' cy='9.7' r='1.2'/%3E%3Ccircle cx='13.5' cy='13.2' r='1.2'/%3E%3Ccircle cx='16.9' cy='6.3' r='1.2'/%3E%3Ccircle cx='20.4' cy='6.3' r='1.2'/%3E%3Ccircle cx='23.8' cy='9.7' r='1.2'/%3E%3Ccircle cx='27.3' cy='2.8' r='1.2'/%3E%3Ccircle cx='27.3' cy='6.3' r='1.2'/%3E%3Ccircle cx='27.3' cy='9.7' r='1.2'/%3E%3Ccircle cx='27.3' cy='13.2' r='1.2'/%3E%3Ccircle cx='34.2' cy='6.3' r='1.2'/%3E%3Ccircle cx='34.2' cy='13.2' r='1.2'/%3E%3Ccircle cx='37.6' cy='13.2' r='1.2'/%3E%3Ccircle cx='41.1' cy='2.8' r='1.2'/%3E%3Ccircle cx='41.1' cy='6.3' r='1.2'/%3E%3Ccircle cx='41.1' cy='9.7' r='1.2'/%3E%3Ccircle cx='41.1' cy='13.2' r='1.2'/%3E%3Ccircle cx='48' cy='6.3' r='1.2'/%3E%3Ccircle cx='48' cy='9.7' r='1.2'/%3E%3Ccircle cx='51.4' cy='2.8' r='1.2'/%3E%3Ccircle cx='51.4' cy='13.2' r='1.2'/%3E%3C/g%3E%3Cg fill='%23f0f'%3E%3Ccircle cx='6.6' cy='2.8' r='1.2'/%3E%3Ccircle cx='6.6' cy='6.3' r='1.2'/%3E%3Ccircle cx='6.6' cy='9.7' r='1.2'/%3E%3Ccircle cx='6.6' cy='13.2' r='1.2'/%3E%3Ccircle cx='10' cy='2.8' r='1.2'/%3E%3Ccircle cx='10' cy='6.3' r='1.2'/%3E%3Ccircle cx='10' cy='9.7' r='1.2'/%3E%3Ccircle cx='10' cy='13.2' r='1.2'/%3E%3Ccircle cx='16.9' cy='2.8' r='1.2'/%3E%3Ccircle cx='16.9' cy='9.7' r='1.2'/%3E%3Ccircle cx='16.9' cy='13.2' r='1.2'/%3E%3Ccircle cx='20.4' cy='2.8' r='1.2'/%3E%3Ccircle cx='20.4' cy='9.7' r='1.2'/%3E%3Ccircle cx='20.4' cy='13.2' r='1.2'/%3E%3Ccircle cx='23.8' cy='2.8' r='1.2'/%3E%3Ccircle cx='23.8' cy='6.3' r='1.2'/%3E%3Ccircle cx='23.8' cy='13.2' r='1.2'/%3E%3Ccircle cx='30.7' cy='2.8' r='1.2'/%3E%3Ccircle cx='30.7' cy='6.3' r='1.2'/%3E%3Ccircle cx='30.7' cy='9.7' r='1.2'/%3E%3Ccircle cx='30.7' cy='13.2' r='1.2'/%3E%3Ccircle cx='34.2' cy='2.8' r='1.2'/%3E%3Ccircle cx='34.2' cy='9.7' r='1.2'/%3E%3Ccircle cx='37.6' cy='2.8' r='1.2'/%3E%3Ccircle cx='37.6' cy='6.3' r='1.2'/%3E%3Ccircle cx='37.6' cy='9.7' r='1.2'/%3E%3Ccircle cx='44.5' cy='2.8' r='1.2'/%3E%3Ccircle cx='44.5' cy='6.3' r='1.2'/%3E%3Ccircle cx='44.5' cy='9.7' r='1.2'/%3E%3Ccircle cx='44.5' cy='13.2' r='1.2'/%3E%3Ccircle cx='48' cy='2.8' r='1.2'/%3E%3Ccircle cx='48' cy='13.2' r='1.2'/%3E%3Ccircle cx='51.4' cy='6.3' r='1.2'/%3E%3Ccircle cx='51.4' cy='9.7' r='1.2'/%3E%3C/g%3E%3Cg fill='%23525'%3E%3Ccircle cx='54.9' cy='2.8' r='1.2'/%3E%3Ccircle cx='54.9' cy='6.3' r='1.2'/%3E%3Ccircle cx='54.9' cy='9.7' r='1.2'/%3E%3Ccircle cx='54.9' cy='13.2' r='1.2'/%3E%3C/g%3E%3C/svg%3E"); + --dropdown: url("data:image/svg+xml,%3Csvg viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m5.29 17.93 26.71 28.14 26.71-28.14' fill='none' stroke='%23ae6ba8' stroke-linecap='round' stroke-linejoin='round' stroke-width='10'/%3E%3C/svg%3E"); + --dropdown_hover: url("data:image/svg+xml,%3Csvg viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m5.29 17.93 26.71 28.14 26.71-28.14' fill='none' stroke='%23fafafa' stroke-linecap='round' stroke-linejoin='round' stroke-width='10'/%3E%3C/svg%3E"); + --yes: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%2371c837' d='M55.9 8.6a4.3 4.3 0 00-3 1.3l-31 30.8L11.3 30a4.4 4.4 0 00-6 0l-4 4.2a4.4 4.4 0 000 6L19 57.7a4.4 4.4 0 006 0l37.8-37.9a4.4 4.4 0 000-6l-4-4a4.3 4.3 0 00-3-1.3z'/%3E%3C/svg%3E"); + --no: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='red' d='M9.7 0c-1 0-2.1.4-3 1.2L1.3 7a4.2 4.2 0 000 5.8L20.6 32 1.3 51.3a4.2 4.2 0 000 5.9l5.6 5.6a4.2 4.2 0 005.9 0L32 43.5l19.2 19.3a4.2 4.2 0 005.9 0l5.6-5.6a4.2 4.2 0 000-5.9L43.5 32l19.2-19.3a4.1 4.1 0 000-5.9l-5.6-5.6a4.2 4.2 0 00-5.8 0L32 20.5 12.6 1.2A4.2 4.2 0 009.7 0z'/%3E%3C/svg%3E"); + --info: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23fcf' stroke='%23313' d='M31.4 3a28.8 28.8 0 00-1.6.1 28.8 28.8 0 00-26.6 29 28.8 28.8 0 1057.6 0A28.8 28.8 0 0031.4 3zm.6 9.3a4.5 4.5 0 014.5 4.5 4.5 4.5 0 01-4.5 4.4 4.5 4.5 0 01-4.5-4.4 4.5 4.5 0 014.5-4.5zm-4.5 13.1h9v26.3h-9V25.4z'/%3E%3C/svg%3E"); + --eye: url("data:image/svg+xml,%3Csvg viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m63.95 33.1a2.03 2.03 0 0 0 0-1.97c-6.13-11.3-18.1-18.95-31.85-18.95s-25.7 7.66-31.85 18.94a2.03 2.03 0 0 0 0 1.97c6.13 11.3 18.1 18.95 31.85 18.95s25.7-7.67 31.85-18.95z' fill='%23894c84'/%3E%3Cpath d='m32.1 47.4c-8.45 0-15.3-6.85-15.3-15.3s6.85-15.3 15.3-15.3 15.3 6.85 15.3 15.3-6.85 15.3-15.3 15.3z' fill='%23313'/%3E%3Cpath d='m32.1 24.3a7.72 7.72 0 0 0 -1.87.22 4.05 4.05 0 0 1 .99 2.65c0 2.24-1.8 4.04-4.04 4.04-1 0-1.93-.37-2.65-1a7.66 7.66 0 0 0 -.22 1.87 7.79 7.79 0 0 0 7.79 7.79c4.3 0 7.8-3.5 7.8-7.8s-3.5-7.8-7.8-7.8z' fill='%23894c84'/%3E%3C/svg%3E"); + --eye_hover: url("data:image/svg+xml,%3Csvg viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m63.95 33.1a2.03 2.03 0 0 0 0-1.97c-6.13-11.3-18.1-18.95-31.85-18.95s-25.7 7.66-31.85 18.94a2.03 2.03 0 0 0 0 1.97c6.13 11.3 18.1 18.95 31.85 18.95s25.7-7.67 31.85-18.95z' fill='%23dbd'/%3E%3Cpath d='m32.1 47.4c-8.45 0-15.3-6.85-15.3-15.3s6.85-15.3 15.3-15.3 15.3 6.85 15.3 15.3-6.85 15.3-15.3 15.3z' fill='%23313'/%3E%3Cpath d='m32.1 24.3a7.72 7.72 0 0 0 -1.87.22 4.05 4.05 0 0 1 .99 2.65c0 2.24-1.8 4.04-4.04 4.04-1 0-1.93-.37-2.65-1a7.66 7.66 0 0 0 -.22 1.87 7.79 7.79 0 0 0 7.79 7.79c4.3 0 7.8-3.5 7.8-7.8s-3.5-7.8-7.8-7.8z' fill='%23dbd'/%3E%3C/svg%3E"); + --arrow_left: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23dbd' viewBox='0 0 64 64'%3E%3Cpath d='M4.5 32l30-30v20.2h25v19.6h-25V62z'/%3E%3C/svg%3E"); + --arrow_right: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23dbd' viewBox='0 0 64 64'%3E%3Cpath d='M59.5 32l-30-30v20.2h-25v19.6h25V62z'/%3E%3C/svg%3E"); + --arrow_up: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23dbd' viewBox='0 0 64 64'%3E%3Cpath d='M32 4.5l-30 30h20.2v25h19.6v-25H62z'/%3E%3C/svg%3E"); + --arrow_down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23dbd' viewBox='0 0 64 64'%3E%3Cpath d='M32 59.5l-30-30h20.2v-25h19.6v25H62z'/%3E%3C/svg%3E"); + --arrow_double: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23dbd' viewBox='0 0 64 64'%3E%3Cpath d='M2.4 32l20.9-20.9v14h6.3v13.7h-6.3v14zM61.6 32L40.7 11.1v14h-6.3v13.7h6.3v14z'/%3E%3C/svg%3E"); + --error: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cg stroke-linejoin='round'%3E%3Cpath fill='none' stroke='%23300' stroke-width='10' d='M58 54.6H6l26-45z'/%3E%3Cpath fill='%23fff' stroke='%23b00' stroke-width='3' d='M58 54.6H6l26-45z'/%3E%3C/g%3E%3Cpath d='M29.5 24.5h5v14.7h-5zm0 18.6h5v5.6h-5z'/%3E%3C/svg%3E"); + --success: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Ccircle cx='32' cy='32' r='32' fill='%2371c837'/%3E%3Ccircle cx='32' cy='32' r='27.1' fill='%23fff'/%3E%3Ccircle cx='32' cy='32' r='22.2' fill='%2371c837'/%3E%3Cpath fill='%23fff' d='M44 19.4a2.2 2.2 0 00-1.5.6L27 35.5 21.6 30c-.8-.8-2.3-.8-3 0l-2.1 2.1c-.8.8-.7 2.2 0 3l9 8.9c.8.8 2.2.8 3 0l19-19c.8-.8.8-2.3 0-3l-2-2a2.2 2.2 0 00-1.5-.7z'/%3E%3C/svg%3E"); + --planet: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Ccircle cx='32' cy='32' r='32' fill='%231ea6c6'/%3E%3Cpath fill='%23f7cf52' d='M59.5 15.6c-5-3.6-6.9-1.8-7.6-.3a2 2 0 01-1.8 1.1h-.2a2 2 0 01-1.9-2c0-4.2 2.7-8.4 2.7-8.4a32.1 32.1 0 018.8 9.6zM64 32a32 32 0 01-1.9 10.8c-1-1.7-1.4-3.8-1.5-5.6-.1-2-2-3.5-4-3.2a5 5 0 01-5.7-4.3 19.4 19.4 0 01-.2-3.4s.4-4.9 2.8-7.2a4 4 0 011.2-1.1 2.5 2.5 0 011.8-.5c2.3.3 4 0 4 0A31.9 31.9 0 0164 32zM37.2 5.3l-3.9 5c-.5-3.8-5-7.1-3.9-7 6 .5 7.8 2 7.8 2zm-8 5.3a3.2 3.2 0 01-1.3 4.8 26.1 26.1 0 00-8.5 5.6 3 3 0 01-3 1c-1.5-.3-3.4-.3-4.5 1.6-2 3.6 5.8 7.6 5.4 12a3.6 3.6 0 00-2.1-2.4c-2.8-1.2-5.2-3-6.8-5.9C5 21.1 7.4 13.7 9.8 9a32 32 0 0110.3-6.6s5.3 3.3 9 8.3zm7.5 31c3 1.7 3.7 5.8 1.4 8.5l-4 4.7-5 5.3-3.3 3.3c-3-4.3-2-12.5-2-12.5l-3-2.2a7.6 7.6 0 01-3.3-7 17 17 0 00-.2-6c2.6-.8 5.3-1.2 7.2-1.4a5 5 0 014 1.3c2 2 5.4 4.2 8.2 6z'/%3E%3C/svg%3E"); + --tunnel: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 64 64'%3E%3Cdefs%3E%3Cpath id='A' fill-opacity='.8' d='M0 0h32v32H0z'/%3E%3C/defs%3E%3ClinearGradient id='B' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23ff0'/%3E%3Cstop offset='.6' stop-color='%23f7cc22'/%3E%3Cstop offset='1' stop-color='%23d4aa00'/%3E%3C/linearGradient%3E%3CradialGradient id='C' cx='282.7' cy='938.5' r='184.6' gradientTransform='matrix(-.19558 -.1369 -.05868 .07823 153.4 13)' xlink:href='%23B'/%3E%3Cfilter id='D' width='1' height='1' x='0' y='0'%3E%3CfeColorMatrix in='SourceGraphic' values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0'/%3E%3C/filter%3E%3CradialGradient id='E' cx='413' cy='807.7' r='151.1' gradientTransform='matrix(-.17603 0 0 .19558 97.6 -124.9)' xlink:href='%23B'/%3E%3CradialGradient id='F' cx='306.1' cy='1055.1' r='184.6' gradientTransform='matrix(-.21514 0 0 .07823 107.6 -30.2)' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23a80'/%3E%3Cstop offset='.8' stop-color='%23a28100'/%3E%3Cstop offset='1' stop-color='%23540'/%3E%3C/radialGradient%3E%3Cmask id='G'%3E%3Cg filter='url(%23D)'%3E%3Cuse fill-opacity='.5' xlink:href='%23A'/%3E%3C/g%3E%3C/mask%3E%3CradialGradient id='H' cx='478.2' cy='713.7' r='76.4' gradientTransform='matrix(-.12 .21 .13 .07 -27.3 -142.6)' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23fff'/%3E%3Cstop offset='1' stop-color='%23fff' stop-opacity='0'/%3E%3C/radialGradient%3E%3CclipPath id='I'%3E%3Cuse xlink:href='%23A'/%3E%3C/clipPath%3E%3Cpath d='M50.3 24.7c1 3.3 1.5 6 2 8.6 4 3.8 10.2 10 10.5 12.4.2 2.4-9.7 11-14.1 12-3 1-10.8.4-19.8-2.5-8.2-2.5-16-6-23.2-10.7-2.8-2-4.5-4.4-4.5-7 0-2.4 1.2-4.6 3.1-6L4.1 29A22.1 22.1 0 0125.8 6.1c14.2.5 21.6 9.6 24.5 18.6z'/%3E%3Cg transform='translate(.2 .1)'%3E%3Cpath fill='url(%23C)' d='M49 25.1c1 3.2 1.4 5.9 1.8 8.2l2.1 1.8c2 1.6 8.4 8 8.4 10.2 0 2.7-5.8 6.4-8.2 8.4-3.3 2.5-4.3 3-11.5 3A91 91 0 016.9 43.4c-2.6-2-4-3.9-4-6.4 0-2 1-4 3-5.5L5.5 29a21 21 0 0120-21.7A24.8 24.8 0 0149 25z'/%3E%3Cpath fill='url(%23E)' d='M50.8 33.3C50.7 45 35.4 47.8 28.4 48 18.2 48 5.7 40.8 5.7 31.6V29c9.2-34.5 39.1-17.5 45.1 4.3z'/%3E%3Cpath fill='url(%23F)' d='M3 36.5c.2 2.1 2.4 4.4 4.8 6.2a101 101 0 0033.7 13c4.2.3 8-.6 11.3-3.1 3.4-2.7 8-5 8.6-7.5.4 2.8-5.7 6.6-8.1 8.6a15 15 0 01-11.8 3A89 89 0 016.9 43.5c-2.7-2-4.1-4.3-4.1-6.4v-.6z'/%3E%3C/g%3E%3Cpath d='M46.9 29.7v1.4C45.7 19.6 31.3 4.8 18.3 8.6c2-.6 4.1-1 6.4-1C36.3 7.8 47 19.4 47 29.7z'/%3E%3Cpath d='M20.3 8.2c8.6 2 16 11.3 17.6 23.8.4 3.4.4 6.7 0 9.8 0-2.5 0-5-.4-7.8-2-13.7-10.3-24-19.5-24.9zm0 0'/%3E%3Cg clip-path='url(%23I)' mask='url(%23G)' transform='matrix(1.95584 0 0 1.95584 .6 .8)'%3E%3Cpath fill='url(%23H)' d='M11 5.6c2.1 1.8 3.8 4.3 5 7.3a28.5 28.5 0 00-12.3 1c-.2-4.7 3-8.2 7.4-8.3zm0 0'/%3E%3C/g%3E%3C/svg%3E"); + /* tunnel status */ + --established: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%233b3' d='M17.4 2.5l3.4 7c.3.3.7.7 1.3.8l7.5 1c1.3.2 1.9 2 1 2.8L25 19.4c-.4.4-.5 1-.4 1.5l1.2 7.5a1.6 1.6 0 01-2.3 1.7l-6.7-3.5c-.5-.3-1.1-.3-1.5 0L8.5 30c-1.3.7-2.6-.3-2.4-1.7L7.4 21c0-.6 0-1.1-.5-1.5l-5.4-5.3a1.6 1.6 0 01.9-2.8l7.5-1c.5 0 1-.4 1.3-.9l3.4-6.9c.5-1.1 2.2-1.1 2.8 0z'/%3E%3C/svg%3E"); + --building: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%23dd0' d='M17.4 2.5l3.4 7c.3.3.7.7 1.3.8l7.5 1c1.3.2 1.9 2 1 2.8L25 19.4c-.4.4-.5 1-.4 1.5l1.2 7.5a1.6 1.6 0 01-2.3 1.7l-6.7-3.5c-.5-.3-1.1-.3-1.5 0L8.5 30c-1.3.7-2.6-.3-2.4-1.7L7.4 21c0-.6 0-1.1-.5-1.5l-5.4-5.3a1.6 1.6 0 01.9-2.8l7.5-1c.5 0 1-.4 1.3-.9l3.4-6.9c.5-1.1 2.2-1.1 2.8 0z'/%3E%3C/svg%3E"); + --failed: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%23f00' d='M17.4 2.5l3.4 7c.3.3.7.7 1.3.8l7.5 1c1.3.2 1.9 2 1 2.8L25 19.4c-.4.4-.5 1-.4 1.5l1.2 7.5a1.6 1.6 0 01-2.3 1.7l-6.7-3.5c-.5-.3-1.1-.3-1.5 0L8.5 30c-1.3.7-2.6-.3-2.4-1.7L7.4 21c0-.6 0-1.1-.5-1.5l-5.4-5.3a1.6 1.6 0 01.9-2.8l7.5-1c.5 0 1-.4 1.3-.9l3.4-6.9c.5-1.1 2.2-1.1 2.8 0z'/%3E%3C/svg%3E"); + --expiring: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%23999999dd' d='M17.4 2.5l3.4 7c.3.3.7.7 1.3.8l7.5 1c1.3.2 1.9 2 1 2.8L25 19.4c-.4.4-.5 1-.4 1.5l1.2 7.5a1.6 1.6 0 01-2.3 1.7l-6.7-3.5c-.5-.3-1.1-.3-1.5 0L8.5 30c-1.3.7-2.6-.3-2.4-1.7L7.4 21c0-.6 0-1.1-.5-1.5l-5.4-5.3a1.6 1.6 0 01.9-2.8l7.5-1c.5 0 1-.4 1.3-.9l3.4-6.9c.5-1.1 2.2-1.1 2.8 0z'/%3E%3C/svg%3E"); + --exploratory: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3CradialGradient id='a' cx='-23' cy='27.6' r='15.6' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%2364b5f6'/%3E%3Cstop offset='1' stop-color='%237bc9ff'/%3E%3C/radialGradient%3E%3Cg transform='matrix(.62496 0 0 .62496 1 1)'%3E%3Cg fill='%23616161' transform='matrix(-1.1993 0 0 1.1993 52.8 -4.8)'%3E%3Cpath d='M29.2 32l2.8-2.8 12 12-2.8 2.8z'/%3E%3Ccircle cx='20' cy='20' r='16'/%3E%3C/g%3E%3Cpath fill='%2337474f' d='M9.7 41.6l-3.3-3.3L0 44.7 3.3 48z'/%3E%3Ccircle cx='-28.8' cy='19.2' r='15.6' fill='url(%23a)' transform='scale(-1 1)'/%3E%3Cpath fill='%23bbdefb' fill-opacity='.9' d='M20.5 9.9a10.8 10.8 0 0116.6 0c.4.4.3 1.3-.2 1.6-.4.5-1.3.4-1.6 0a8.5 8.5 0 00-13 0c-.2.2-.6.4-1 .4l-.7-.2c-.5-.5-.5-1.4-.1-1.8z'/%3E%3C/g%3E%3C/svg%3E"); + --local: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3ClinearGradient id='a' x1='7.8' x2='23.1' y1='10.4' y2='33.3' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0' stop-color='%23ffd5f6'/%3E%3Cstop offset='1' stop-color='%23fae'/%3E%3C/linearGradient%3E%3Cpath fill='%239d93ac' d='M35.2 44.9l1 7.6h-8.4l1-7.6z'/%3E%3Cpath fill='%23beb7c8' d='M60 46c0 1.3-1.1 2.4-2.4 2.4H6.4A2.4 2.4 0 014 46V10c0-1.3 1.1-2.4 2.4-2.4h51.2c1.3 0 2.4 1 2.4 2.4zm-37 6.2h18a2 2 0 012 2v.2a2 2 0 01-2 2H23a2 2 0 01-2-2v-.2a2 2 0 012-2z'/%3E%3Ccircle cx='32' cy='44.8' r='1.3' fill='%23dedbe3'/%3E%3Cpath fill='%23de87cd' d='M8.1 12v29.3h48.1V12z'/%3E%3Cpath fill='url(%23a)' d='M7.5 12.5v29l49-29z' transform='matrix(.98 0 0 1.01 .7 -.6)'/%3E%3Cpath fill='none' stroke='%23442178' stroke-width='2' d='M8 12v29.3h48V12z'/%3E%3C/svg%3E"); + --time: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cg transform='translate(1 1)'%3E%3Ccircle cx='31.1' cy='31.1' r='28.1' stroke='%23dbd' stroke-width='6'/%3E%3Cpath fill='none' stroke='%23dbd' stroke-linecap='round' stroke-linejoin='round' stroke-width='5' d='M30.7 13.2v18.5h16.5'/%3E%3Ccircle cx='31.1' cy='31.1' r='4.5' fill='%23dbd'/%3E%3C/g%3E%3C/svg%3E"); + --tag: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%23dbd' d='M63 37.9v20.3c-.2 2.3-2.6 4.5-4.9 4.7l-20.3.1a4.3 4.3 0 01-2.9-1.4L2.3 29.2a4.3 4.3 0 010-6.1L23.1 2.3a4.3 4.3 0 016.1 0l32.5 32.5c.9.8 1.3 1.9 1.3 3.2zm-9.3 5.5a7.3 7.3 0 10-10.3 10.2 7.3 7.3 0 0010.3 0 7.1 7.1 0 000-10.2z'/%3E%3C/svg%3E"); + --shutdown: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='red' d='M16 1a3 3 0 00-3 3v9.3a3 3 0 002.9 2.9 3 3 0 003-2.8V4a3 3 0 00-3-3zm7.2 3.2a3 3 0 00-2.8 3 3 3 0 001.1 2.2 8.8 8.8 0 013.3 6.9 8.8 8.8 0 01-9.9 8.8 8.8 8.8 0 01-4.5-15.7 3 3 0 001-2.2c0-2.4-2.7-3.8-4.6-2.3a14.6 14.6 0 00-5.5 12.9 14.7 14.7 0 1023.9-13 2.8 2.8 0 00-1.9-.6z'/%3E%3C/svg%3E"); + --shutdown-graceful: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Cpath fill='%2313ff00' d='M16 1a3 3 0 00-3 3v9.3a3 3 0 002.9 2.9 3 3 0 003-2.8V4a3 3 0 00-3-3zm7.2 3.2a3 3 0 00-2.8 3 3 3 0 001.1 2.2 8.8 8.8 0 013.3 6.9 8.8 8.8 0 01-9.9 8.8 8.8 8.8 0 01-4.5-15.7 3 3 0 001-2.2c0-2.4-2.7-3.8-4.6-2.3a14.6 14.6 0 00-5.5 12.9 14.7 14.7 0 1023.9-13 2.8 2.8 0 00-1.9-.6z'/%3E%3C/svg%3E"); + /* theme colors */ + --scrollbar: #414 #101; + --ink: #dbd; + --ink-faded: rgba(221,187,221, .5); + --notify: #5f5; + --page: #140014; + --main-boxshadow: 0 0 0 1px #000, 0 0 0 4px #313, 0 0 0 5px #000; + --link: #ae6ba8; + --link_hover: #fafafa; + --border: #515; + --border2: #404; + --button-border: #313; + --button: linear-gradient(#303, #202 50%, #202 50%, #101); + --button_hover: linear-gradient(to bottom, #94518e, #733f6f 50%, #733f6f 50%, #42243f); + --button_active: linear-gradient(to bottom, #202, #303); + --active_shadow: inset 3px 3px 3px rgba(0,0,0,.6); + --hr: linear-gradient(to right, #313, #414, #313); + --highlight: inset 0 0 0 1px #101; + --tr: #1a001a; + --tr-alt: #202; + --tr-inner: #240024; + --header: linear-gradient(to bottom, #202, #101 50%, #101 50%, #000); + --th: linear-gradient(to bottom, #180018, #000); + --th_multicolumn: linear-gradient(to bottom, #202, #101); + --sectiontitle: linear-gradient(to bottom, #202, #101 50%, #101 50%, #000); + --b64: #101; + --b64-ink: #2475c2; + --b64-boxshadow: 0 0 0 1px #000, inset 0 0 0 1px #202; + --input_text: var(--button-border); + --menu: #303; + --menu-ink: #fff; + --textshadow: 0 1px 1px rgba(0,0,0.7); +} + +html, body { + min-height: 100%; + background: var(--page); +} + +html, body, textarea { + scrollbar-color: var(--scrollbar); +} + +body { + margin: 0; + padding: 0; + width: 100%; + height: 100%; + display: table; + line-height: 1.6; + position: absolute; + top: 0; + left: 0; + text-align: center; + font: 14pt var(--bodyfont); + color: var(--ink); +} + +.header { + display: none; +} + +.wrapper { + margin: 0 auto; + padding: 5px; + width: 100%; + display: table-cell; + vertical-align: middle; + text-align: center; +} + +b { + font-weight: 600; +} + +#main { + margin: 0 auto; + min-width: 300px; + max-width: 700px; + font-size: 85%; + border: 2px solid var(--border); + border-spacing: 1px; + box-shadow: var(--main-boxshadow); +} + +.center, .center form, .register { + text-align: center !important; +} + +.left { + text-align: left !important; +} + +.right { + text-align: right !important; +} + +form { + margin: 5px 0; +} + +a, .slide label { + text-decoration: none; + color: var(--link); + font-weight: 600; +} + +a { + padding: 1px 8px; + display: inline-block; + border-radius: 2px; +} + +.listitem a { + padding: 0 1px; +} + +a#home { + width: calc(100% - 20px); + height: 44px; + display: inline-block; + font-size: 0; + background: var(--logo) no-repeat center center / auto 40px; + opacity: .5; + vertical-align: top; +} + +a#home:hover { + opacity: 1; +} + +a.view { + padding-left: 0; + color: var(--ink); + width: 100%; +} + +a.view:hover, a.view:focus, tr:active .view { + padding-left: 22px; + color: var(--link); + background: var(--eye) no-repeat left center / 16px; +} + +a:hover, .slide label:hover, button:hover, select:hover, input[type=number]:focus, td.streamid:hover { + color: var(--link_hover); + background: var(--link); +} + +a.button, button, input, select { + vertical-align: middle; +} + +select, input, button { + margin: 4px 2px; + padding: 6px 8px; + font-family: var(--bodyfont); + font-size: 90% !important; + font-weight: 600; + color: var(--link); + border: 1px solid var(--button-border); + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; +} + +a, select, button, label { + text-shadow: var(--textshadow); + cursor: pointer; +} + +a.button, button { + margin: 4px 2px; + padding: 2px 8px 4px; + min-width: 64px; + display: inline-block; + font-size: 90% !important; + font-weight: 700; + text-align: center; + text-decoration: none; + border: 1px solid var(--button-border); + border-radius: 2px; + box-shadow: var(--highlight); + background: var(--button); + appearance: none; +} + +a.button { + margin: 8px 2px; +} + +button { + padding: 6px 12px; + min-width: 120px; +} + +a.button:hover, a.button:focus { + color: var(--link_hover); + background: var(--button_hover) !important; +} + +button:active, a.button:active, .slide label:active, td.streamid:active { + box-shadow: var(--highlight), var(--active_shadow); + background: var(--button_active) !important; +} + +.streamid:hover a { + color: var(--link_hover); +} + +button.apply { + color: transparent; + text-shadow: none !important; + background: var(--yes) no-repeat center center / 14px, var(--button); + background-blend-mode: luminosity; +} + +button.apply:hover, button.apply:focus { + color: transparent; + background: var(--yes) no-repeat center center / 14px, var(--button_hover) !important; + background-blend-mode: normal; +} + +button.apply:active { + color: transparent; + background: var(--yes) no-repeat center center / 14px, var(--button_active) !important; + background-blend-mode: luminosity; +} + +select, input[type=number] { + width: 150px; + box-sizing: border-box; + font-size: 90% !important; + background: var(--input_text); +} + +input[type=number] { + box-shadow: var(--highlight), var(--active_shadow); + outline: none; + appearance: none; + -moz-appearance: textfield; +} + +input[type=number]::-webkit-inner-spin-button { + -webkit-appearance: none; +} + +select { + padding: 6px 20px 6px 8px; + line-height: 1.5; + background: var(--dropdown) no-repeat right 8px center / 10px, var(--button); + box-shadow: var(--highlight); +} + +select:hover, select:focus, select:active { + color: var(--link_hover); + background: var(--dropdown_hover) no-repeat right 8px center / 10px, var(--button_hover); +} + +select option { + color: var(--menu-ink); + background: var(--menu); +} + +select, option:hover, option:focus, option:active { + outline: none; +} + +.note { + padding: 15px 10px !important; +} + +.note::before { + margin-top: -2px; + width: 24px; + height: 18px; + display: inline-block; + vertical-align: middle; + background: var(--info) no-repeat center center / 16px; + content: ""; +} + +.routerservice { + display: inline-block; + margin: 4px 2px; + padding: 0 10px 0 25px; + background: #212 var(--yes) no-repeat 8px center / 10px; + border-radius: 2px; + text-align: left; + font-size: 90%; +} + +tr { + background: var(--tr); + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); +} + +tr tr, tr#version, tr#version ~ tr:nth-child(odd), .tableitem tr:nth-child(odd) { + background: var(--tr-inner) !important; +} + +.tableitem tr:nth-child(even) { + background: var(--tr) !important; +} + +th, td, .listitem { + box-shadow: var(--highlight); + font-size: 97%; +} + +th, td { + padding: 5px 12px; + border: 1px solid var(--button-border); +} + +th { + padding: 6px 12px; + font-weight: 600; + background: var(--th_multicolumn); +} + +th[colspan="2"], .slide label, #routerservices { + background: var(--th); +} + +th.sectiontitle { + padding: 0 0 10px !important; + font-weight: 700; + border-bottom: none; + background: none; +} + +.sectiontitle span { + padding: 4px 12px; + min-width: 50%; + display: inline-block; + white-space: nowrap; + line-height: 1.6; + font-size: 98%; + border: 1px solid var(--button-border); + border-top: none; + border-radius: 0 0 4px 4px; + box-shadow: var(--highlight), 0 2px 2px rgba(0,0,0,.4); + background: var(--sectiontitle); +} + +table table th { + font-size: 80%; +} + +tr:first-child { + background: var(--header); +} + +td:first-child { + width: 50%; + text-align: right; + font-weight: 600; +} + +td td:first-child { + width: auto; +} + +td:last-child { + text-align: left; +} + +.error, .notify { + padding: 30px 12px; + font-size: 110%; + color: #fff; + box-shadow: var(--highlight), inset 0 0 3px 3px rgba(0,0,0,.6); + text-align: center; +} + +#warning, #success { + display: block; + width: 100%; + height: 48px; + background: var(--error) no-repeat center top / 40px; +} + +#success { + background: var(--success) no-repeat center top / 40px; +} + +.thin { + width: 1%; + white-space: nowrap; +} + +#navlinks { + padding: 10px 4px !important; + font-size: 100%; + background: var(--header); +} + +.enabled, .disabled { + font-size: 0; + display: inline-block; + width: 10px; + height: 10px; + vertical-align: middle; +} + +#main .enabled { + background: var(--yes) no-repeat left 12px center / 10px; +} + +#main .disabled { + background: var(--no) no-repeat left 12px center / 10px; +} + +.sensitive { + filter: blur(8px); + display: inline-block !important; + max-width: 120px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + line-height: 1.05; + font-size: 75%; +} + +.sensitive:hover, td:active .sensitive { + max-width: 300px; + white-space: pre-wrap; + word-break: break-all; + filter: blur(0); +} + +.arrowright, .arrowleft, .arrowleftright, .arrowup, .arrowdown { + width: 12px; + height: 12px; + display: inline-block; + vertical-align: middle; + font-size: 0 !important; +} + +.arrowleft { + background: var(--arrow_left) no-repeat center center / 11px; +} + +.arrowright { + background: var(--arrow_right) no-repeat center center / 11px; +} + +.arrowleftright { + background: var(--arrow_double) no-repeat center center / 11px; +} + +.arrowup { + background: var(--arrow_up) no-repeat center center / 11px; +} + +.arrowdown { + background: var(--arrow_down) no-repeat center center / 11px; +} + +.listitem, .tableitem { + padding: 5px 0; + white-space: nowrap; + font-size: 80%; + font-family: var(--monospaced); +} + +.listitem { + display: inline-block; + width: 100%; + vertical-align: middle; + border-top: 1px solid var(--button-border); +} + +.listitem:nth-child(odd) { + background: var(--tr-alt); +} + +.listitem:last-child { + border-bottom: 1px solid var(--button-border); +} + +.listitem.out .arrowup, .listitem.in .arrowdown { + margin: 0 4px; +} + +#transports .listitem .arrowup, #transports .listitem .arrowdown { + margin: 0 10px; +} + +.tableitem .button { + margin: 0 !important; + padding: 1px 4px !important; + font-size: 100% !important; + border: none; + background: none; + box-shadow: none; +} + +.streamid .button, .streamid .button:hover, .streamid .button:focus, .streamid .button:active { + background: none !important; + box-shadow: none !important; +} + +.tableitem a.button .close { + margin: -2px -6px 0 0; + width: 11px; + height: 11px; + display: inline-block; + vertical-align: middle; + color: transparent !important; + text-shadow: none !important; + background: var(--no) no-repeat center center / 9px !important; + opacity: .8; +} + +.tableitem a.button:hover .close, .tableitem a.button:focus .close { + opacity: 1; +} + +.tunnel.established { + color: #56B734; +} + +.tunnel.expiring { + color: #D3AE3F; +} + +.tunnel.failed { + color: #D33F3F; +} + +.tunnel.building { + color: #434343; +} + +caption { + font-size: 1.5em; + text-align: center; + color: var(--link); +} + +table { + display: table; + border-collapse: collapse; + text-align: center; +} + +td table { + width: 100% !important; +} + +table.extaddr { + text-align: left; +} + +table.services { + width: 100%; +} + +#b64 { + margin: 2px -4px; + padding: 3px 4px; + width: calc(100% + 8px); + word-break: break-all; + color: var(--b64-ink); + border: 1px solid var(--button-border); + background: var(--b64); + font-family: var(--monospaced); + font-size: 80%; + display: inline-block; + line-height: 1; + box-sizing: border-box; + user-select: all; + box-shadow: var(--b64-boxshadow); + white-space: pre-wrap; +} + +#b64 { + margin: 4px; + width: calc(100% - 8px); + text-align: justify; +} + +.streamdest { + width: 120px; + max-width: 240px; + overflow: hidden; + text-overflow: ellipsis; +} + +.slide div.slidecontent, .slide [type=checkbox] { + display: none; +} + +.slide [type=checkbox]:checked ~ div.slidecontent { + margin-top: 0; + padding: 0; + display: block; +} + +.disabled { + color: #D33F3F; +} + +.enabled { + color: #56B734; +} + +.nopadding { + padding: 0 !important; +} + +.nopadding table { + border: none !important; +} + +.tunnelid.local, .tunnel { + display: inline-block; + width: 16px; + height: 16px; + vertical-align: middle; + font-size: 0; + background: var(--local) no-repeat center center / 15px; +} + +.tunnelid:not(.local) { + padding: 2px 4px 0 22px; + display: inline-block; + width: auto; + height: 16px; + vertical-align: middle; + border-radius: 2px; + box-shadow: 0 0 0 1px #000; + background: #303 var(--tunnel) no-repeat 4px center / 14px; +} + +.tunnelid:not(.local) { + text-align: left; + min-width: 86px; + border-left: 5px solid var(--border2); + border-radius: 0 2px 2px 0; +} + +.tunnel { + margin: 3px 5px 0; + width: 26px; + height: 16px; + float: left; + vertical-align: middle; + background: var(--established) no-repeat left center / 12px; +} + +.tunnelid.local + .tunnel { + margin-left: 4px; +} + +.tunnel.building { + background: var(--building) no-repeat left center / 12px; +} + +.tunnel.failed { + background: var(--failed) no-repeat left center / 12px; +} + +.tunnel.expiring { + background: var(--expiring) no-repeat left center / 12px; +} + +.tunnel.exploratory { + background: var(--established) no-repeat left center / 12px, var(--exploratory) no-repeat right 3px / 12px; +} + +.tunnel.building.exploratory { + background: var(--building) no-repeat left center / 12px, var(--exploratory) no-repeat right 3px / 12px; +} + +.tunnel.expiring.exploratory { + background: var(--expiring) no-repeat left center / 12px, var(--exploratory) no-repeat right 3px / 12px; +} + +.tunnel.failed.exploratory { + background: var(--failed) no-repeat left center / 12px, var(--exploratory) no-repeat right 3px / 12px; +} + +span[data-tooltip] { + position: relative; +} + +.hop, .host { + padding: 1px 4px; + display: inline-block; + vertical-align: middle; + border-radius: 2px; + box-shadow: 0 0 0 1px #000; + background: #303; +} + +.chain.inbound .arrowright:not(.zerohop):first-of-type { + display: none !important; +} + +.host { + padding-left: 17px; + background: #303 var(--planet) no-repeat 4px center / 9px; +} + +a:hover .host, a:focus .host, a:active .host { + background: #505 var(--exploratory) no-repeat 2px center / 13px; +} + +.transferred { + display: inline-block; + vertical-align: middle; + text-align: right; +} + +.latency { + padding: 2px 5px 2px 18px; + min-width: 52px; + display: inline-block; + vertical-align: middle; + text-align: right; + float: right; + background: var(--page) var(--time) no-repeat 3px center / 13px; + border-radius: 2px; +} + +.latency.unknown { + color: var(--ink-faded); +} + +.sent, .recvd { + padding-right: 16px; + display: inline-block; + vertical-align: middle; + text-align: right; + background: var(--arrow_up) no-repeat right center / 12px; +} + +.recvd { + background: var(--arrow_down) no-repeat right center / 12px; +} + +.hide { + display: none; +} + +.router.sent, .router.recvd, .transit.sent { + padding-left: 17px; + padding-right: 0; + text-align: left; + background-size: 14px; + background-position: left center; +} + +.router.sent { + margin-left: 6px; +} + +.itag { + padding-left: 13px; + display: inline-block; + vertical-align: middle; + background: var(--tag) no-repeat left center / 10px; +} + +a[href^="https://gwhois"] { + position: relative; +} + +span[data-tooltip]:hover::after, span[data-tooltip]:active::after, +.itag[data-tooltip]:hover::after, .itag[data-tooltip]:active::after, +/*a[href^="https://gwhois"][data-tooltip]:hover::after, a[href^="https://gwhois"][data-tooltip]:active::after*/ { + padding: 3px 6px; + display: inline-block; + position: absolute; + top: -32px; + left: -10px; + font-size: 12px; + font-weight: 600; + color: #444; + border: 1px solid #444; + box-shadow: 0 0 1px 1px rgba(0, 0, 0, .2); + background: #fff !important; + content: attr(data-tooltip); + text-shadow: none !important; +} + +.slide label { + margin: 0; + padding: 6px 0 6px 20px; + width: 100%; + display: block; + border: 1px solid var(--button-border); + border-left: none; + border-right: none; + box-shadow: var(--highlight); + background: var(--button); + background: var(--th); + box-sizing: border-box; + color: var(--ink); +} + +input[type=checkbox] + label::after { + content: "+"; + display: inline-block; + vertical-align: middle; + float: right; + margin: -6px 10px 2px 0; + font-size: 16pt; + font-weight: 700; + color: var(--ink); +} + +input[type=checkbox]:checked + label::after { + content: "–"; +} + +.slide label:hover { + color: var(--link_hover); + background: var(--button_hover); +} + +.slide table { + width: 100%; +} + +@media screen and (max-width: 1000px) { + body { + font-size: 13pt !important; + } + + .listitem { + font-size: 90%; + } + + a { + padding: 1px 3px; + } + + .b32, .listitem a[href*="local_destination&b32"] { + max-width: 300px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + vertical-align: middle; + } + + .router.sent, .router.recvd, .transit.sent { + padding-left: 15px; + background-size: 12px; + } + + .tunnelid:not(.local) { + display: none; + } + + .tunnel, .latency { + margin: 0 4px; + } + + .tunnel { + margin-left: 8px; + } +} + + @media screen and (-webkit-min-device-pixel-ratio: 1.5) { + body { + font-size: 12pt !important; + } + + .i2ptunnels .b32, .i2cp .b32 { + max-width: 200px !important; + } + } +} + +@media screen and (max-width: 800px) { + #main { + width: 400px; + } + + td { + padding: 5px 10px; + } + + td:first-child { + width: auto; + } + + .b32, .listitem a[href*="local_destination&b32"] { + max-width: 300px; + } + + .hop { + margin: 0 -3px; + } + + @media screen and (-webkit-min-device-pixel-ratio: 1.5) { + #main { + width: 300px; + } + } +} + +@media screen and (min-width: 1000px) { + .tunnelid[data-tooltip]:hover::after, .tunnelid[data-tooltip]:active::after { + display: none; + } +} + +@media screen and (min-width: 1200px) { + #main { + width: 700px; + } + + .tunnelid { + background-size: 16px; + } + + .tunnelid:not(.local) { + margin-left: 12px; + float: right; + } + + .chain { + min-width: 560px; + display: inline-block; + vertical-align: middle; + text-align: right; + } + + #transports .chain { + min-width: 540px; + text-align: left; + } + + .chain.transit { + min-width: 0; + text-align: center; + } + + .recvd, .sent { + min-width: 64px; + } + + .router.recvd, .router.sent { + min-width: 80px; + } + + .host { + min-width: 144px; + } + + .SSU .host { + min-width: 190px; + } + + .i2ptunnels .listitem a { + padding: 2px 10px; + min-width: 100px; + text-align: right; + } + + .i2ptunnels .listitem a:hover, .i2ptunnels .listitem a:focus { + text-align: center; + } + + .listitem.out .arrowup, .listitem.in .arrowdown { + background-size: 14px; + } + + .i2ptunnels .b32 { + margin-right: 10px; + } + + .itag { + float: right; + } + + .latency { + padding-top: 3px; + padding-bottom: 3px; + margin-left: 12px; + } + + .transferred { + min-width: 48px; + } +} + +@media screen and (min-width: 1200px) and (min-height: 600px) { + .wrapper { + padding: 2%; + } + + td, .listitem, .tableitem { + padding-top: 6px; + padding-bottom: 6px; + } + + .host, .hop { + padding-top: 2px; + padding-bottom: 2px; + } + + .tunnelid:not(.local) { + padding-top: 3px; + padding-bottom: 1px; + } +} \ No newline at end of file From 693a78fbe6692bb0681dfcecd315a4446fecbfb2 Mon Sep 17 00:00:00 2001 From: dr|z3d Date: Wed, 11 Aug 2021 19:41:54 +0000 Subject: [PATCH 13/15] More webconsole UI refinements --- daemon/HTTPServer.cpp | 94 ++++++++++++++++++++---------- libi2pd/Tunnel.cpp | 12 ++-- webconsole/style.css | 132 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 178 insertions(+), 60 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 38225450..c9f477e4 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -559,12 +559,12 @@ namespace http { s << ""; } /* - s << "" << "HTTP " << tr("Proxy") << "" << (httpproxy ? tr("Enabled") : tr("Disabled")) << "\r\n"; - s << "" << "SOCKS " << tr("Proxy") << "" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "\r\n"; - s << "" << "BOB" << "" << (bob ? tr("Enabled") : tr("Disabled")) << "\r\n"; - s << "" << "SAM" << "" << (sam ? tr("Enabled") : tr("Disabled")) << "\r\n"; - s << "" << "I2CP" << "" << (i2cp ? tr("Enabled") : tr("Disabled")) << "\r\n"; - s << "" << "I2PControl" << "" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "\r\n"; + s << "" << "HTTP " << tr("Proxy") << "" << (socksproxy ? tr("Enabled") : tr("Disabled")) << "\r\n"; + s << "" << "BOB" << "" << (sam ? tr("Enabled") : tr("Disabled")) << "\r\n"; + s << "" << "I2CP" << "" << (i2pcontrol ? tr("Enabled") : tr("Disabled")) << "\r\n"; */ } @@ -606,7 +606,7 @@ namespace http { { s << "\r\n"; s << "

\r\n" - << "\r\n"; + << "\r\n"; s << "
\r\n
"; s << dest->GetIdentity ()->ToBase64 () << "
\r\n
\r\n
\r\n\r\n"; if (dest->IsEncryptedLeaseSet ()) @@ -637,7 +637,9 @@ namespace http { { s << "\r\n"; s << "
\r\n\r\n" - << "\r\n"; + << "\r\n"; s << "
\r\n\r\n\r\n" << "" << "" @@ -657,18 +659,24 @@ namespace http { { s << "\r\n"; out_tags += it.second->GetNumOutgoingTags (); } - s << "\r\n" + s << "\r\n" << "\r\n"; @@ -710,7 +726,9 @@ namespace http { auto numECIESx25519Tags = dest->GetNumIncomingECIESx25519Tags (); if (numECIESx25519Tags > 0) { - s << "\r\n"; + s << "\r\n"; if (!dest->GetECIESx25519Sessions ().empty ()) { std::stringstream tmp_s; uint32_t ecies_sessions = 0; @@ -722,8 +740,9 @@ namespace http { } s << "
" << tr("Address") << "" << tr("Type") << "
\r\n"; s << "
\r\n\r\n" - << "\r\n"; + << "\r\n"; s << "
\r\n
\r\n"; for (auto & it : pool->GetInboundTunnels ()) { // inbound tunnels s << "
" << "[" << tr("In") << "] " << ""; it->Print(s); - if(it->LatencyIsKnown()) - s << " " - << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << ""; - else // placeholder for alignment + if(it->LatencyIsKnown()) { + s << " "; + if (it->GetMeanLatency() >= 1000) { + s << std::fixed << std::setprecision(2); + s << (double) it->GetMeanLatency() / 1000 << tr(/* tr: seconds */ "s") << ""; + } else { + s << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << ""; + } + } else { // placeholder for alignment s << " ---"; + } ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); s << "
\r\n"; } @@ -677,11 +685,17 @@ namespace http { << "[" << tr("Out") << "] " << ""; it->Print(s); - if(it->LatencyIsKnown()) - s << " " - << it->GetMeanLatency() << tr("ms") << ""; - else // placeholder for alignment + if(it->LatencyIsKnown()) { + s << " "; + if (it->GetMeanLatency() >= 1000) { + s << std::fixed << std::setprecision(2); + s << (double) it->GetMeanLatency() / 1000 << tr(/* tr: seconds */ "s") << ""; + } else { + s << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << ""; + } + } else { // placeholder for alignment s << " ---"; + } ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); s << "
\r\n"; } @@ -701,7 +715,9 @@ namespace http { << "
" << it.second->GetNumOutgoingTags () << "
" << tr("Outgoing Session Tags") << " [" << out_tags << "]
" << tr("Outgoing Session Tags") + << " [" << out_tags + << "]
\r\n" << "\r\n\r\n\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Count") << "
\r\n
ECIESx25519
\r\n" << tr("Incoming Tags") << " [" << numECIESx25519Tags << "]
ECIESx25519
\r\n" << tr("Incoming Tags") + << " [" << numECIESx25519Tags + << "]
\r\n" << "
\r\n" - << "\r\n" + << "\r\n" << "
\r\n\r\n\r\n\r\n" << tmp_s.str () << "
" << tr("Destination") << "" << tr("Status") << "
\r\n
\r\n
\r\n"; } else @@ -748,7 +767,7 @@ namespace http { // Print table with streams information s << "
\r\n"; s << "
\r\n\r\n" - << "\r\n"; + << "\r\n"; s << "
\r\n\r\n\r\n"; s << ""; s << ""; @@ -886,17 +905,24 @@ namespace http { s << "\r\n"; if (dest->GetNumIncomingTags () > 0) { - s << "\r\n"; + s << "\r\n"; } else { s << "\r\n"; } @@ -921,7 +923,7 @@ namespace http { s << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << ""; } } else { // placeholder for alignment - s << " ---"; + s << " --- "; } ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); s << "\r\n"; @@ -934,7 +936,7 @@ namespace http { if(it->LatencyIsKnown()) s << " " << it->GetMeanLatency() << tr("ms") << ""; else // placeholder for alignment - s << " ---"; + s << " --- "; ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); s << "\r\n\r\n"; } From f4d7d37557e63f0750187989673f357e9dea14e4 Mon Sep 17 00:00:00 2001 From: dr|z3d Date: Wed, 11 Aug 2021 23:55:00 +0000 Subject: [PATCH 15/15] Add command-specific notification text; logo tweak --- daemon/HTTPServer.cpp | 24 ++++++++++++++++++++---- webconsole/style.css | 23 +++++++++++++++++++---- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index 1b1e503a..cec986ab 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -1728,10 +1728,26 @@ namespace http { return; } - s << "\r\n"; -// s << "" << tr("Back to commands list") << "
\r\n"; -// s << "

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

\r\n"; res.add_header("Refresh", redirect.c_str()); } diff --git a/webconsole/style.css b/webconsole/style.css index 95dc8efb..176ec4eb 100644 --- a/webconsole/style.css +++ b/webconsole/style.css @@ -1,7 +1,7 @@ :root { --bodyfont: Open Sans, Noto Sans, Ubuntu, Segoe UI, sans-serif; --monospaced: Droid Sans Mono, Noto Mono, Lucida Console, DejaVu Sans Mono, monospace; - --logo: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 58 16'%3E%3Cg fill='%23525'%3E%3Ccircle cx='3.1' cy='2.8' r='1.2'/%3E%3Ccircle cx='3.1' cy='6.3' r='1.2'/%3E%3Ccircle cx='3.1' cy='9.7' r='1.2'/%3E%3Ccircle cx='3.1' cy='13.2' r='1.2'/%3E%3Ccircle cx='13.5' cy='2.8' r='1.2'/%3E%3Ccircle cx='13.5' cy='6.3' r='1.2'/%3E%3Ccircle cx='13.5' cy='9.7' r='1.2'/%3E%3Ccircle cx='13.5' cy='13.2' r='1.2'/%3E%3Ccircle cx='16.9' cy='6.3' r='1.2'/%3E%3Ccircle cx='20.4' cy='6.3' r='1.2'/%3E%3Ccircle cx='23.8' cy='9.7' r='1.2'/%3E%3Ccircle cx='27.3' cy='2.8' r='1.2'/%3E%3Ccircle cx='27.3' cy='6.3' r='1.2'/%3E%3Ccircle cx='27.3' cy='9.7' r='1.2'/%3E%3Ccircle cx='27.3' cy='13.2' r='1.2'/%3E%3Ccircle cx='34.2' cy='6.3' r='1.2'/%3E%3Ccircle cx='34.2' cy='13.2' r='1.2'/%3E%3Ccircle cx='37.6' cy='13.2' r='1.2'/%3E%3Ccircle cx='41.1' cy='2.8' r='1.2'/%3E%3Ccircle cx='41.1' cy='6.3' r='1.2'/%3E%3Ccircle cx='41.1' cy='9.7' r='1.2'/%3E%3Ccircle cx='41.1' cy='13.2' r='1.2'/%3E%3Ccircle cx='48' cy='6.3' r='1.2'/%3E%3Ccircle cx='48' cy='9.7' r='1.2'/%3E%3Ccircle cx='51.4' cy='2.8' r='1.2'/%3E%3Ccircle cx='51.4' cy='13.2' r='1.2'/%3E%3C/g%3E%3Cg fill='%23f0f'%3E%3Ccircle cx='6.6' cy='2.8' r='1.2'/%3E%3Ccircle cx='6.6' cy='6.3' r='1.2'/%3E%3Ccircle cx='6.6' cy='9.7' r='1.2'/%3E%3Ccircle cx='6.6' cy='13.2' r='1.2'/%3E%3Ccircle cx='10' cy='2.8' r='1.2'/%3E%3Ccircle cx='10' cy='6.3' r='1.2'/%3E%3Ccircle cx='10' cy='9.7' r='1.2'/%3E%3Ccircle cx='10' cy='13.2' r='1.2'/%3E%3Ccircle cx='16.9' cy='2.8' r='1.2'/%3E%3Ccircle cx='16.9' cy='9.7' r='1.2'/%3E%3Ccircle cx='16.9' cy='13.2' r='1.2'/%3E%3Ccircle cx='20.4' cy='2.8' r='1.2'/%3E%3Ccircle cx='20.4' cy='9.7' r='1.2'/%3E%3Ccircle cx='20.4' cy='13.2' r='1.2'/%3E%3Ccircle cx='23.8' cy='2.8' r='1.2'/%3E%3Ccircle cx='23.8' cy='6.3' r='1.2'/%3E%3Ccircle cx='23.8' cy='13.2' r='1.2'/%3E%3Ccircle cx='30.7' cy='2.8' r='1.2'/%3E%3Ccircle cx='30.7' cy='6.3' r='1.2'/%3E%3Ccircle cx='30.7' cy='9.7' r='1.2'/%3E%3Ccircle cx='30.7' cy='13.2' r='1.2'/%3E%3Ccircle cx='34.2' cy='2.8' r='1.2'/%3E%3Ccircle cx='34.2' cy='9.7' r='1.2'/%3E%3Ccircle cx='37.6' cy='2.8' r='1.2'/%3E%3Ccircle cx='37.6' cy='6.3' r='1.2'/%3E%3Ccircle cx='37.6' cy='9.7' r='1.2'/%3E%3Ccircle cx='44.5' cy='2.8' r='1.2'/%3E%3Ccircle cx='44.5' cy='6.3' r='1.2'/%3E%3Ccircle cx='44.5' cy='9.7' r='1.2'/%3E%3Ccircle cx='44.5' cy='13.2' r='1.2'/%3E%3Ccircle cx='48' cy='2.8' r='1.2'/%3E%3Ccircle cx='48' cy='13.2' r='1.2'/%3E%3Ccircle cx='51.4' cy='6.3' r='1.2'/%3E%3Ccircle cx='51.4' cy='9.7' r='1.2'/%3E%3C/g%3E%3Cg fill='%23525'%3E%3Ccircle cx='54.9' cy='2.8' r='1.2'/%3E%3Ccircle cx='54.9' cy='6.3' r='1.2'/%3E%3Ccircle cx='54.9' cy='9.7' r='1.2'/%3E%3Ccircle cx='54.9' cy='13.2' r='1.2'/%3E%3C/g%3E%3C/svg%3E"); + --logo: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 19'%3E%3Cg fill='purple'%3E%3Ccircle cx='2.7' cy='2.6' r='1.2'/%3E%3Ccircle cx='2.7' cy='6' r='1.2'/%3E%3Ccircle cx='2.7' cy='9.5' r='1.2'/%3E%3Ccircle cx='2.7' cy='13' r='1.2'/%3E%3Ccircle cx='2.7' cy='16.4' r='1.2'/%3E%3Ccircle cx='9.6' cy='2.6' r='1.2'/%3E%3Ccircle cx='9.6' cy='6' r='1.2'/%3E%3Ccircle cx='9.6' cy='9.5' r='1.2'/%3E%3Ccircle cx='9.6' cy='13' r='1.2'/%3E%3Ccircle cx='9.6' cy='16.4' r='1.2'/%3E%3Ccircle cx='13' cy='6' r='1.2'/%3E%3Ccircle cx='16.5' cy='6' r='1.2'/%3E%3Ccircle cx='16.5' cy='13' r='1.2'/%3E%3Ccircle cx='19.9' cy='6' r='1.2'/%3E%3Ccircle cx='19.9' cy='13' r='1.2'/%3E%3Ccircle cx='23.4' cy='13' r='1.2'/%3E%3Ccircle cx='26.8' cy='2.6' r='1.2'/%3E%3Ccircle cx='26.8' cy='6' r='1.2'/%3E%3Ccircle cx='26.8' cy='9.5' r='1.2'/%3E%3Ccircle cx='26.8' cy='13' r='1.2'/%3E%3Ccircle cx='26.8' cy='16.4' r='1.2'/%3E%3Ccircle cx='33.7' cy='6' r='1.2'/%3E%3Ccircle cx='33.7' cy='13' r='1.2'/%3E%3Ccircle cx='33.7' cy='16.4' r='1.2'/%3E%3Ccircle cx='37.2' cy='6' r='1.2'/%3E%3Ccircle cx='37.2' cy='13' r='1.2'/%3E%3Ccircle cx='37.2' cy='16.4' r='1.2'/%3E%3Ccircle cx='40.6' cy='13' r='1.2'/%3E%3Ccircle cx='40.6' cy='16.4' r='1.2'/%3E%3Ccircle cx='44.1' cy='2.6' r='1.2'/%3E%3Ccircle cx='44.1' cy='6' r='1.2'/%3E%3Ccircle cx='44.1' cy='9.5' r='1.2'/%3E%3Ccircle cx='44.1' cy='13' r='1.2'/%3E%3Ccircle cx='44.1' cy='16.4' r='1.2'/%3E%3Ccircle cx='47.5' cy='2.6' r='1.2'/%3E%3Ccircle cx='47.5' cy='6' r='1.2'/%3E%3Ccircle cx='51' cy='2.6' r='1.2'/%3E%3Ccircle cx='51' cy='6' r='1.2'/%3E%3Ccircle cx='51' cy='13' r='1.2'/%3E%3Ccircle cx='54.4' cy='2.6' r='1.2'/%3E%3Ccircle cx='54.4' cy='6' r='1.2'/%3E%3Ccircle cx='54.4' cy='13' r='1.2'/%3E%3Ccircle cx='61.3' cy='2.6' r='1.2'/%3E%3Ccircle cx='61.3' cy='6' r='1.2'/%3E%3Ccircle cx='61.3' cy='9.5' r='1.2'/%3E%3Ccircle cx='61.3' cy='13' r='1.2'/%3E%3Ccircle cx='61.3' cy='16.4' r='1.2'/%3E%3C/g%3E%3Cg fill='%23f0f'%3E%3Ccircle cx='6.1' cy='2.6' r='1.2'/%3E%3Ccircle cx='6.1' cy='6' r='1.2'/%3E%3Ccircle cx='6.1' cy='9.5' r='1.2'/%3E%3Ccircle cx='6.1' cy='13' r='1.2'/%3E%3Ccircle cx='6.1' cy='16.4' r='1.2'/%3E%3Ccircle cx='13' cy='2.6' r='1.2'/%3E%3Ccircle cx='13' cy='9.5' r='1.2'/%3E%3Ccircle cx='13' cy='13' r='1.2'/%3E%3Ccircle cx='13' cy='16.4' r='1.2'/%3E%3Ccircle cx='16.5' cy='2.6' r='1.2'/%3E%3Ccircle cx='16.5' cy='9.5' r='1.2'/%3E%3Ccircle cx='16.5' cy='16.4' r='1.2'/%3E%3Ccircle cx='19.9' cy='2.6' r='1.2'/%3E%3Ccircle cx='19.9' cy='9.5' r='1.2'/%3E%3Ccircle cx='19.9' cy='16.4' r='1.2'/%3E%3Ccircle cx='23.4' cy='2.6' r='1.2'/%3E%3Ccircle cx='23.4' cy='6' r='1.2'/%3E%3Ccircle cx='23.4' cy='9.5' r='1.2'/%3E%3Ccircle cx='23.4' cy='16.4' r='1.2'/%3E%3Ccircle cx='30.3' cy='2.6' r='1.2'/%3E%3Ccircle cx='30.3' cy='6' r='1.2'/%3E%3Ccircle cx='30.3' cy='9.5' r='1.2'/%3E%3Ccircle cx='30.3' cy='13' r='1.2'/%3E%3Ccircle cx='30.3' cy='16.4' r='1.2'/%3E%3Ccircle cx='33.7' cy='2.6' r='1.2'/%3E%3Ccircle cx='33.7' cy='9.5' r='1.2'/%3E%3Ccircle cx='37.2' cy='2.6' r='1.2'/%3E%3Ccircle cx='37.2' cy='9.5' r='1.2'/%3E%3Ccircle cx='40.6' cy='2.6' r='1.2'/%3E%3Ccircle cx='40.6' cy='6' r='1.2'/%3E%3Ccircle cx='40.6' cy='9.5' r='1.2'/%3E%3Ccircle cx='47.5' cy='9.5' r='1.2'/%3E%3Ccircle cx='47.5' cy='13' r='1.2'/%3E%3Ccircle cx='47.5' cy='16.4' r='1.2'/%3E%3Ccircle cx='51' cy='9.5' r='1.2'/%3E%3Ccircle cx='51' cy='16.4' r='1.2'/%3E%3Ccircle cx='54.4' cy='9.5' r='1.2'/%3E%3Ccircle cx='54.4' cy='16.4' r='1.2'/%3E%3Ccircle cx='57.9' cy='2.6' r='1.2'/%3E%3Ccircle cx='57.9' cy='6' r='1.2'/%3E%3Ccircle cx='57.9' cy='9.5' r='1.2'/%3E%3Ccircle cx='57.9' cy='13' r='1.2'/%3E%3Ccircle cx='57.9' cy='16.4' r='1.2'/%3E%3C/g%3E%3C/svg%3E"); --dropdown: url("data:image/svg+xml,%3Csvg viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m5.29 17.93 26.71 28.14 26.71-28.14' fill='none' stroke='%23ae6ba8' stroke-linecap='round' stroke-linejoin='round' stroke-width='10'/%3E%3C/svg%3E"); --dropdown_hover: url("data:image/svg+xml,%3Csvg viewBox='0 0 64 64' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m5.29 17.93 26.71 28.14 26.71-28.14' fill='none' stroke='%23fafafa' stroke-linecap='round' stroke-linejoin='round' stroke-width='10'/%3E%3C/svg%3E"); --yes: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cpath fill='%2371c837' d='M55.9 8.6a4.3 4.3 0 00-3 1.3l-31 30.8L11.3 30a4.4 4.4 0 00-6 0l-4 4.2a4.4 4.4 0 000 6L19 57.7a4.4 4.4 0 006 0l37.8-37.9a4.4 4.4 0 000-6l-4-4a4.3 4.3 0 00-3-1.3z'/%3E%3C/svg%3E"); @@ -152,7 +152,7 @@ a, .slide label { text-shadow: none; } -.count:hover { +label:hover .count { background: var(--ink); } @@ -446,11 +446,21 @@ td:last-child { } #navlinks { - padding: 10px 4px !important; + padding: 10px 2px !important; font-size: 100%; background: var(--header); } +#navlinks a:hover { + background: var(--button_hover); +} + +#navlinks a:active { + color: var(--ink-faded); + box-shadow: var(--highlight), var(--active_shadow), 0 0 0 1px var(--button-border); + background: var(--button_active); +} + .enabled, .disabled { font-size: 0; display: inline-block; @@ -677,7 +687,7 @@ table.services { height: 16px; vertical-align: middle; font-size: 0; - background: var(--local) no-repeat center center / 15px; + background: var(--local) no-repeat center center / 16px; } .tunnelid:not(.local) { @@ -994,6 +1004,11 @@ input[type=checkbox]:checked + label::after { width: 700px; } + #navlinks a { + margin-top: 0; + margin-bottom: 0; + } + .tunnelid { background-size: 16px; }
IDDestination
\r\n"; s << "
\r\n\r\n" - << "\r\n"; // TODO: separate client & exploratory tunnels into sections + << "\r\n"; // TODO: separate client & exploratory tunnels into sections s << "
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { s << "
" << "[" << tr("In") << "] " << ""; it->Print(s); - if(it->LatencyIsKnown()) - s << " " << it->GetMeanLatency() << tr("ms") << ""; - else // placeholder for alignment + if(it->LatencyIsKnown()) { + s << " "; + if (it->GetMeanLatency() >= 1000) { + s << std::fixed << std::setprecision(2); + s << (double) it->GetMeanLatency() / 1000 << tr(/* tr: seconds */ "s") << ""; + } else { + s << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << ""; + } + } else { // placeholder for alignment s << " ---"; + } ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); s << "
\r\n"; } @@ -1078,15 +1104,17 @@ namespace http { if (!tmp_s.str ().empty ()) { s << "
\r\n\r\n
" + << "\" />\r\n\r\n
" << tmp_s.str () << "
\r\n
\r\n"; } if (!tmp_s6.str ().empty ()) { s << "
\r\n" - << "\r\n
" + << "\r\n
" << tmp_s6.str () << "
\r\n
\r\n"; } } @@ -1109,7 +1137,8 @@ namespace http { if (!sessions.empty ()) { s << "
\r\n" - << "\r\n" + << "\r\n" << "
\r\n"; for (const auto& it: sessions) { @@ -1150,7 +1179,8 @@ namespace http { if (!sessions6.empty ()) { s << "
\r\n\r\n" - << "\r\n" + << "\r\n" << "
\r\n"; for (const auto& it: sessions6) { diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 3c7c0a54..fa6a73db 100644 --- a/libi2pd/Tunnel.cpp +++ b/libi2pd/Tunnel.cpp @@ -252,10 +252,11 @@ namespace tunnel void InboundTunnel::Print (std::stringstream& s) const { + s << ""; PrintHops (s); s << " "; s << " Local"; + s << GetTunnelID () << "\">Local"; s << "" << GetTunnelID () << ""; } @@ -277,8 +278,9 @@ namespace tunnel void ZeroHopsInboundTunnel::Print (std::stringstream& s) const { + s << ""; s << " Local"; + << GetTunnelID () << "\">Local"; s << "" << GetTunnelID () << ""; } @@ -318,9 +320,10 @@ namespace tunnel void OutboundTunnel::Print (std::stringstream& s) const { + s << ""; s << "Local"; PrintHops (s); - s << "" << GetTunnelID () << ""; + s << " " << GetTunnelID () << ""; } ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel (): @@ -354,9 +357,10 @@ namespace tunnel void ZeroHopsOutboundTunnel::Print (std::stringstream& s) const { + s << ""; s << " "; s << "" << GetTunnelID () << "\">Local"; + << GetTunnelID () << "\">" << GetTunnelID () << "\">Local"; s << "" << GetTunnelID () << ""; } diff --git a/webconsole/style.css b/webconsole/style.css index 3a3c4b98..95dc8efb 100644 --- a/webconsole/style.css +++ b/webconsole/style.css @@ -32,9 +32,10 @@ /* theme colors */ --scrollbar: #414 #101; --ink: #dbd; + --ink-darker: #b9b; --ink-faded: rgba(221,187,221, .5); --notify: #5f5; - --page: #140014; + --page: #120012; --main-boxshadow: 0 0 0 1px #000, 0 0 0 4px #313, 0 0 0 5px #000; --link: #ae6ba8; --link_hover: #fafafa; @@ -47,7 +48,7 @@ --active_shadow: inset 3px 3px 3px rgba(0,0,0,.6); --hr: linear-gradient(to right, #313, #414, #313); --highlight: inset 0 0 0 1px #101; - --tr: #1a001a; + --tr: #180818; --tr-alt: #202; --tr-inner: #240024; --header: linear-gradient(to bottom, #202, #101 50%, #101 50%, #000); @@ -136,6 +137,25 @@ a, .slide label { font-weight: 600; } +.slide label { + font-weight: 700; +} + +.count { + margin: -1px 0 -1px 3px; + padding: 0 10px; + display: inline-block; + vertical-align: baseline; + border-radius: 2px; + background: var(--ink-darker); + color: var(--page); + text-shadow: none; +} + +.count:hover { + background: var(--ink); +} + a { padding: 1px 8px; display: inline-block; @@ -354,6 +374,10 @@ th[colspan="2"], .slide label, #routerservices { background: var(--th); } +th:not(.sectiontitle)[colspan="2"], .slide label { + font-size: 95%; +} + th.sectiontitle { padding: 0 0 10px !important; font-weight: 700; @@ -398,7 +422,7 @@ td:last-child { } .error, .notify { - padding: 30px 12px; + padding: 30px 12px 40px; font-size: 110%; color: #fff; box-shadow: var(--highlight), inset 0 0 3px 3px rgba(0,0,0,.6); @@ -409,7 +433,7 @@ td:last-child { display: block; width: 100%; height: 48px; - background: var(--error) no-repeat center top / 40px; + background: var(--error) no-repeat center top / 44px; } #success { @@ -463,7 +487,7 @@ td:last-child { .arrowright, .arrowleft, .arrowleftright, .arrowup, .arrowdown { width: 12px; - height: 12px; + height: 16px; display: inline-block; vertical-align: middle; font-size: 0 !important; @@ -482,11 +506,11 @@ td:last-child { } .arrowup { - background: var(--arrow_up) no-repeat center center / 11px; + background: var(--arrow_up) no-repeat center center / 12px; } .arrowdown { - background: var(--arrow_down) no-repeat center center / 11px; + background: var(--arrow_down) no-repeat center center / 12px; } .listitem, .tableitem { @@ -512,13 +536,14 @@ td:last-child { } .listitem.out .arrowup, .listitem.in .arrowdown { - margin: 0 4px; + margin: 3px 8px 0 16px; + float: left; } - +/* #transports .listitem .arrowup, #transports .listitem .arrowdown { - margin: 0 10px; + margin: 3px 10px 0 16px; } - +*/ .tableitem .button { margin: 0 !important; padding: 1px 4px !important; @@ -674,7 +699,7 @@ table.services { } .tunnel { - margin: 3px 5px 0; + margin: 1px 5px 0; width: 26px; height: 16px; float: left; @@ -718,6 +743,10 @@ span[data-tooltip] { position: relative; } +.hops { + text-align: right; +} + .hop, .host { padding: 1px 4px; display: inline-block; @@ -736,6 +765,10 @@ span[data-tooltip] { background: #303 var(--planet) no-repeat 4px center / 9px; } +a[href^="https://gwhois"]:hover, a[href^="https://gwhois"]:focus { + background: none !important +} + a:hover .host, a:focus .host, a:active .host { background: #505 var(--exploratory) no-repeat 2px center / 13px; } @@ -747,13 +780,13 @@ a:hover .host, a:focus .host, a:active .host { } .latency { - padding: 2px 5px 2px 18px; - min-width: 52px; + padding: 2px 5px 2px 20px; + min-width: 40px; display: inline-block; vertical-align: middle; text-align: right; float: right; - background: var(--page) var(--time) no-repeat 3px center / 13px; + background: var(--page) var(--time) no-repeat 5px center / 13px; border-radius: 2px; } @@ -801,8 +834,7 @@ a[href^="https://gwhois"] { } span[data-tooltip]:hover::after, span[data-tooltip]:active::after, -.itag[data-tooltip]:hover::after, .itag[data-tooltip]:active::after, -/*a[href^="https://gwhois"][data-tooltip]:hover::after, a[href^="https://gwhois"][data-tooltip]:active::after*/ { +.itag[data-tooltip]:hover::after, .itag[data-tooltip]:active::after { padding: 3px 6px; display: inline-block; position: absolute; @@ -888,11 +920,13 @@ input[type=checkbox]:checked + label::after { } .tunnel, .latency { - margin: 0 4px; + margin: 1px 6px 0 4px; } - .tunnel { - margin-left: 8px; + .tunnel, .hops { + margin-top: 2px; + display: inline-block; + vertical-align: middle; } } @@ -905,7 +939,6 @@ input[type=checkbox]:checked + label::after { max-width: 200px !important; } } -} @media screen and (max-width: 800px) { #main { @@ -924,6 +957,16 @@ input[type=checkbox]:checked + label::after { max-width: 300px; } + .arrowup, .arrowdown, .tunnel { + float: none; + } + + .latency { + min-width: 0; + background-size: 11px; + background-color: transparent !important; + } + .hop { margin: 0 -3px; } @@ -939,6 +982,11 @@ input[type=checkbox]:checked + label::after { .tunnelid[data-tooltip]:hover::after, .tunnelid[data-tooltip]:active::after { display: none; } + + .hops { + display: inline-block; + min-width: 240px; + } } @media screen and (min-width: 1200px) { @@ -950,6 +998,11 @@ input[type=checkbox]:checked + label::after { background-size: 16px; } + .tunnelid:not(.local), .latency, .hops { + margin-top: 1px; + margin-bottom: -1px; + } + .tunnelid:not(.local) { margin-left: 12px; float: right; @@ -959,11 +1012,10 @@ input[type=checkbox]:checked + label::after { min-width: 560px; display: inline-block; vertical-align: middle; - text-align: right; } #transports .chain { - min-width: 540px; + min-width: 580px; text-align: left; } @@ -972,6 +1024,12 @@ input[type=checkbox]:checked + label::after { text-align: center; } + .hops { + min-width: 280px; + display: inline-block; + text-align: right; + } + .recvd, .sent { min-width: 64px; } @@ -984,6 +1042,10 @@ input[type=checkbox]:checked + label::after { min-width: 144px; } + .host a { + margin-bottom: -1px; + } + .SSU .host { min-width: 190px; } @@ -999,6 +1061,7 @@ input[type=checkbox]:checked + label::after { } .listitem.out .arrowup, .listitem.in .arrowdown { + margin-top: 2px; background-size: 14px; } @@ -1006,8 +1069,18 @@ input[type=checkbox]:checked + label::after { margin-right: 10px; } + .itag, .host { + margin-top: 1px; + } + .itag { + padding: 2px 5px 2px 20px; float: right; + min-width: 100px; + display: inline-block; + border-radius: 2px; + background-color: var(--menu); + background-position: 5px center; } .latency { @@ -1019,6 +1092,10 @@ input[type=checkbox]:checked + label::after { .transferred { min-width: 48px; } + + .tunnel { + margin: 2px 0 0 -48px; + } } @media screen and (min-width: 1200px) and (min-height: 600px) { @@ -1040,4 +1117,11 @@ input[type=checkbox]:checked + label::after { padding-top: 3px; padding-bottom: 1px; } -} \ No newline at end of file +} + +/* enable for screenshots */ +/* +.hop { + filter: blur(8px); +} +*/ \ No newline at end of file From 65019fab9bf6aa3995ebbd9f1c343bf932ba55cf Mon Sep 17 00:00:00 2001 From: dr|z3d Date: Wed, 11 Aug 2021 21:46:59 +0000 Subject: [PATCH 14/15] Better alignment for --- latency; boxify session tag count (cherry picked from commit 329d600c64c86f5f4650078ea8f24ebeface77bd) --- daemon/HTTPServer.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index c9f477e4..1b1e503a 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -675,7 +675,7 @@ namespace http { s << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << ""; } } else { // placeholder for alignment - s << " ---"; + s << " --- "; } ShowTunnelDetails(s, it->GetState (), false, it->GetNumReceivedBytes ()); s << "
\r\n"; @@ -694,7 +694,7 @@ namespace http { s << it->GetMeanLatency() << tr(/* tr: Milliseconds */ "ms") << ""; } } else { // placeholder for alignment - s << " ---"; + s << " --- "; } ShowTunnelDetails(s, it->GetState (), false, it->GetNumSentBytes ()); s << "
\r\n"; @@ -703,7 +703,9 @@ namespace http { s << "
\r\n
\r\n
\r\n
" << tr("Incoming Session Tags") << " [" << dest->GetNumIncomingTags () << "]
" << tr("Incoming Session Tags") + << " [" + << dest->GetNumIncomingTags () << "]
" << tr("No Incoming Session Tags") << "
" << tr("SUCCESS") << ": " - << tr("Command accepted") << "
"; + if (cmd == HTTP_COMMAND_SHUTDOWN_NOW) + s << tr("Immediate router shutdown initiated"); + else if (cmd == HTTP_COMMAND_SHUTDOWN_CANCEL) + s << tr("Router shutdown cancelled"); + else if (cmd == HTTP_COMMAND_RELOAD_CSS) + s << tr("Console CSS stylesheet reloaded"); + else if (cmd == HTTP_COMMAND_LIMITTRANSIT) + s << tr("Maximum transit tunnels configured for session"); + else if (cmd == HTTP_COMMAND_ENABLE_TRANSIT) + s << tr("Transit tunnels enabled for session"); + else if (cmd == HTTP_COMMAND_DISABLE_TRANSIT) + s << tr("Transit tunnels disabled for session"); + else if (cmd == HTTP_COMMAND_SETLANGUAGE) + s << tr("Console language updated"); + else if (cmd == HTTP_COMMAND_LOGLEVEL) + s << tr("Log level updated for session"); + else + s << "" << tr("SUCCESS") << ": " << tr("Command accepted"); + s << "