diff --git a/daemon/HTTPServer.cpp b/daemon/HTTPServer.cpp index c2aee205..3113662f 100644 --- a/daemon/HTTPServer.cpp +++ b/daemon/HTTPServer.cpp @@ -41,72 +41,375 @@ 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='%23405' rx='5'/%3E%3Ccircle cx='32' cy='32' r='4' fill='%23e580ff'/%3E%3Cg fill='%23d42aff'%3E%3Ccircle cx='20' cy='32' r='4'/%3E%3Ccircle cx='44' cy='32' r='4'/%3E%3Ccircle cx='32' cy='20' r='4'/%3E%3Ccircle cx='32' cy='44' r='4'/%3E%3C/g%3E%3Cg fill='%2380a'%3E%3Ccircle cx='20' cy='56' r='4'/%3E%3Ccircle cx='44' cy='8' r='4'/%3E%3Ccircle cx='44' cy='56' r='4'/%3E%3Ccircle cx='8' cy='44' r='4'/%3E%3Ccircle cx='56' cy='20' r='4'/%3E%3Ccircle cx='56' cy='44' r='4'/%3E%3Ccircle cx='8' cy='20' r='4'/%3E%3Ccircle cx='20' cy='8' r='4'/%3E%3C/g%3E%3Cg fill='%23aa00d4'%3E%3Ccircle cx='32' cy='56' r='4'/%3E%3Ccircle cx='44' cy='20' r='4'/%3E%3Ccircle cx='44' cy='44' r='4'/%3E%3Ccircle cx='8' cy='32' r='4'/%3E%3Ccircle cx='56' cy='32' r='4'/%3E%3Ccircle cx='32' cy='8' r='4'/%3E%3Ccircle cx='20' cy='44' r='4'/%3E%3Ccircle cx='20' cy='20' r='4'/%3E%3C/g%3E%3Cg fill='%23660080'%3E%3Ccircle cx='8' cy='56' r='4'/%3E%3Ccircle cx='56' cy='8' r='4'/%3E%3Ccircle cx='56' cy='56' r='4'/%3E%3Ccircle cx='8' cy='8' r='4'/%3E%3C/g%3E%3C/svg%3E"; // Bundled style const std::string internalCSS = - "\r\n"; + "\r\n"; // for external style sheet std::string externalCSS; @@ -119,8 +422,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 = ""; } } @@ -132,7 +433,8 @@ namespace http { s << internalCSS; } - const char HTTP_PAGE_TUNNELS[] = "tunnels"; + const char HTTP_PAGE_TUNNEL_SUMMARY[] = "tunnel_summary"; + const char HTTP_PAGE_LOCAL_TUNNELS[] = "local_tunnels"; const char HTTP_PAGE_TRANSIT_TUNNELS[] = "transit_tunnels"; const char HTTP_PAGE_TRANSPORTS[] = "transports"; const char HTTP_PAGE_LOCAL_DESTINATIONS[] = "local_destinations"; @@ -165,7 +467,8 @@ namespace http { time_t t = divTime.quot; struct tm *tm = localtime(&t); char date[128]; - snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem); + snprintf(date, sizeof(date), "%02d/%02d/%d %02d:%02d:%02d.%03lld", + tm->tm_mday, tm->tm_mon + 1, tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec, divTime.rem); return date; } @@ -190,17 +493,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 +531,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) @@ -229,7 +553,7 @@ namespace http { if (level == "none" || level == "error" || level == "warn" || level == "info" || level == "debug") i2p::log::Logger().SetLogLevel(level); else { - LogPrint(eLogError, "HTTPServer: unknown loglevel set attempted"); + LogPrint(eLogError, "HTTPServer: Unknown loglevel set attempted"); return; } i2p::log::Logger().Reopen (); @@ -243,50 +567,77 @@ namespace http { std::string currLang = i2p::client::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 (); - s << - "\r\n" - "\r\n" - " \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"; + std::map params; + std::string page(""); + URL url; + url.parse_query(params); + page = params["page"]; + std::string token = params["token"]; + + s << "\r\n" + "\r\n" + "\r\n" /* TODO: Find something to parse html/template system. This is horrible. */ + "\r\n" + "\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"; + s << "\r\n" + "\r\n" + "
\r\n\r\n" + "\r\n" + << "\r\n"; } static void ShowPageTail (std::stringstream& s) { - s << - "\r\n\r\n" - "\r\n" - "\r\n"; + s << "
" + "" << tr("Main page") << " " + // TODO placeholder for graceful shutdown button (requires token) + "Shutdown"; + // placeholder for toggle transit (requires token) + if (i2p::context.AcceptsTunnels ()) { + s << "No transit"; + } else { + s << "Accept transit"; + } + s << "
\r\n"; if (i2p::context.IsFloodfill ()) - 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" - "
"; + s << "" << tr("LeaseSets") << "\r\n"; + s << "" << 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 +674,113 @@ namespace http { void ShowStatus (std::stringstream& s, bool includeHiddenContent, i2p::http::OutputFormatEnum outputFormat) { - s << "" << tr("Uptime") << ": "; - ShowUptime(s, i2p::context.GetUptime ()); - s << "
\r\n"; - s << "" << tr("Network status") << ": "; - ShowNetworkStatus (s, i2p::context.GetStatus ()); - 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("Shutdown") << ""; 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("Shutdown") << ""; ShowUptime(s, remains); - s << "
\r\n"; + s << "…\r\n"; } #endif - 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") << ": "; + s << "" << tr("Bandwidth") << ""; + s << std::fixed << std::setprecision(0); + 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 << 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 << ""; + + if ((i2p::context.AcceptsTunnels() || i2p::tunnel::tunnels.CountTransitTunnels()) && + (i2p::transport::transports.GetTotalReceivedBytes () > 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 << " / "; + s << (double) i2p::transport::transports.GetTransitBandwidth () / 1024; + s << " " << 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 << " (" << (double) i2p::transport::transports.GetInBandwidth () / 1024 << " " << tr(/* tr: Kibibit/s */ "KiB/s") << ")
\r\n"; - s << "" << tr("Sent") << ": "; + 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 << " (" << (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"; + s << ""; + + if ((i2p::context.AcceptsTunnels() || i2p::tunnel::tunnels.CountTransitTunnels()) && + (i2p::transport::transports.GetTotalReceivedBytes () > 0)) { + s << " / "; + 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); + s << ""; } - 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"; - 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 << "
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 << "
\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 << "\r\n"; + s << "" << tr("Build Success") << ""; + s << i2p::tunnel::tunnels.GetTunnelCreationSuccessRate () << "%\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"; + if (!(i2p::context.AcceptsTunnels () || i2p::tunnel::tunnels.CountTransitTunnels())) + s << ""; + else + s << ""; + s << "" << tr("Local 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 +789,47 @@ 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("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 << "
\r\n\r\n"; + } } + + s << "\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("Client 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\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("Server Destinations") + << "\r\n
\r\n"; for (auto& it: i2cpServer->GetSessions ()) { auto dest = it.second->GetDestination (); @@ -466,155 +838,239 @@ 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\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") << " )"; + 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"; + 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") << " )"; + 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"; + 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"; + s << "" << 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 +1094,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 +1114,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,97 +1134,307 @@ 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("Local 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 and flag individual services? + s << "
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { - s << "
"; - it->Print(s); - if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; - ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumReceivedBytes ()); - s << "
\r\n"; + if (it->GetTunnelPool () == ExplPool) { + s << "
" + << "[" << tr("In") << "] " + << ""; + it->Print(s); + 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"; + } } - s << "
\r\n
\r\n"; - s << "" << tr("Outbound tunnels") << ":
\r\n
\r\n"; for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) { - s << "
"; - it->Print(s); - if(it->LatencyIsKnown()) - s << " ( " << it->GetMeanLatency() << tr("ms") << " )"; - ShowTunnelDetails(s, it->GetState (), (it->GetTunnelPool () == ExplPool), it->GetNumSentBytes ()); - s << "
\r\n"; + if (it->GetTunnelPool () == ExplPool) { + s << "
" + << "[" << tr("Out") << "] " + << ""; + it->Print(s); + 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->GetNumSentBytes ()); + s << "\r\n
\r\n"; + } } - s << "
\r\n"; + s << "
\r\n
\r\n
\r\n"; + + + s << "
\r\n\r\n" + << "\r\n"; // TODO: flag individual services by name + s << "
\r\n
\r\n"; + for (auto & it : i2p::tunnel::tunnels.GetInboundTunnels ()) { + if (it->GetTunnelPool () != ExplPool) { + s << "
" + << "[" << tr("In") << "] " + << ""; + it->Print(s); + 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"; + } + } + for (auto & it : i2p::tunnel::tunnels.GetOutboundTunnels ()) { + if (it->GetTunnelPool () != ExplPool) { + s << "
" + << "[" << tr("Out") << "] " + << ""; + it->Print(s); + 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->GetNumSentBytes ()); + s << "\r\n
\r\n"; + } + } + s << "
\r\n
\r\n
\r\n\r\n"; + } + + void ShowTunnelSummary (std::stringstream& s) { + std::string webroot; i2p::config::GetOption("http.webroot", webroot); + size_t localInCount = i2p::tunnel::tunnels.CountInboundTunnels(); + size_t localOutCount = i2p::tunnel::tunnels.CountOutboundTunnels(); + size_t transitCount = i2p::tunnel::tunnels.CountTransitTunnels(); + s << "" << tr("Tunnel Summary") << "\r\n"; + s << "\r\n"; + s << "\r\n" + << "" + << "" + << "\r\n"; + s << "" + << "\r\n"; + if (transitCount > 0) { + s << "" + << "\r\n"; + } + s << "
" << tr("Type") << "" << tr("Inbound") << "" << tr("Outbound") << "" << tr("View Details") << "
" << tr("Local") << "" << localInCount << "" << localOutCount << "View
" << tr("Transit") << "" << transitCount << "View
\r\n"; + s << ""; + ShowI2PTunnels (s); + s << "\r\n"; } static void ShowCommands (std::stringstream& s, uint32_t token) { - std::string webroot; i2p::config::GetOption("http.webroot", webroot); + s << "\r\n"; + s << "
\r\n\r\n" + << "\r\n"; + s << "
\r\n\r\n"; + s << "\r\n"; + s << "\r\n"; + if (!i2p::context.GetRouterInfo().GetProperty("family").empty()) + s << "\r\n"; + auto family = i2p::context.GetFamily (); + if (family.length () > 0) + 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"; + s << "\r\n"; + s << "
" << tr("Router Identity") << "" + << i2p::context.GetRouterInfo().GetIdentHashBase64() << "
" << tr("Router Caps") << "" << i2p::context.GetRouterInfo().GetProperty("caps") << "
" << tr("Router Family") << "" + << i2p::context.GetRouterInfo().GetProperty("family") << "
"<< tr("Family") << "" << family << "
\r\n"; + for (const auto& address : i2p::context.GetRouterInfo().GetAddresses()) + { + s << "
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 << "
" << tr("Uptime") << ""; + ShowUptime(s, i2p::context.GetUptime ()); + s << "
" << tr("Data path") << "" << i2p::fs::GetUTF8DataDir() << "
\r\n
\r\n
\r\n\r\n"; - s << "" << tr("Router commands") << "
\r\n
\r\n
\r\n"; - s << " " << tr("Run peer test") << "
\r\n"; + + std::string webroot; i2p::config::GetOption("http.webroot", webroot); + s << "" << tr("Router Commands") << "" + << "
\r\n"; + + std::string styleFile = i2p::fs::DataDirPath ("webconsole/style.css"); + if (i2p::fs::Exists(styleFile)) { + s << "" + << tr("Reload external CSS stylesheet") << ""; + } + + s << " " + << tr("Run peer test") << "
\r\n"; // s << " Reload config
\r\n"; if (i2p::context.AcceptsTunnels ()) - s << " " << tr("Decline transit tunnels") << "
\r\n"; + s << " " + << tr("Decline transit tunnels") << "
\r\n"; else - s << " " << tr("Accept transit tunnels") << "
\r\n"; + s << " " + << tr("Accept transit tunnels") << "
\r\n"; + if (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"; - s << " " << tr("Force shutdown") << "

\r\n"; - s << " " << tr("Reload external CSS styles") << "\r\n"; - s << "
"; +/* TODO graceful shutdown button in header with .notify dialog if transit tunnels + active to offer option to shutdown immediately + only one option? displayed in the header +*/ + } else { + s << " " + << tr("Shutdown") << ""; + } + s << "
\r\n"; + 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"; + const LogLevel loglevel = i2p::log::Logger().GetLogLevel(); + s << "" << tr("Logging Level") << "\r\n"; + s << "
"; + s << "none\r\n"; + s << "error\r\n"; + s << "warn\r\n"; + s << "info\r\n"; + s << "debug" + << "
\r\n\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"; - - 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"; + s << "
\r\n"; + s << "
\r\n"; + s << " \r\n"; + s << " \r\n"; + s << " \r\n"; + s << " \r\n"; + s << "
\r\n
\r\n\r\n"; + } std::string currLang = i2p::client::context.GetLanguage ()->GetLanguage(); // get current used language - s << "" << tr("Change language") << "
\r\n"; + s << "" << tr("Console Display Language") << "\r\n"; + s << "
\r\n"; s << "
\r\n"; s << " \r\n"; s << " \r\n"; @@ -775,8 +1442,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\r\n"; } @@ -784,23 +1451,52 @@ namespace http { { if(i2p::tunnel::tunnels.CountTransitTunnels()) { - s << "" << tr("Transit Tunnels") << ":
\r\n
\r\n"; + int count = i2p::tunnel::tunnels.GetTransitTunnels().size(); + s << "" << tr("Transit Tunnels"); + s << " [" << count << "]" + << ""; + s << "\r\n"; + s << "
7) + s << "id=\"transit\" "; + s << "class=\"list\">\r\n"; for (const auto& it: i2p::tunnel::tunnels.GetTransitTunnels ()) { - s << "
\r\n"; + const auto& expiry = i2p::tunnel::tunnels.GetTransitTunnelsExpirationTimeout (); + s << "
"; + + double bytes = it->GetNumTransmittedBytes (); + s << std::fixed << std::setprecision(0); + if (bytes > 1024 * 1024 * 1024) { + s << std::fixed << std::setprecision(2); + s << "" << (double) (bytes / 1024 / 1024 / 1024) << "G "; + } else if (bytes > 1024 * 1024) { + s << std::fixed << std::setprecision(1); + s << "" << (double) (bytes / 1024 / 1024) << "M "; + } else if (bytes > 1024) { + s << "" << (int) (bytes / 1024) << "K "; + } else { + s << "" << (int) (bytes) << "B "; + } + // TODO: tunnel expiry per tunnel, not most recent + //s << "" << expiry << tr("s" /* translation: seconds */) << " "; + s << "" << it->GetTunnelID () << " "; if (std::dynamic_pointer_cast(it)) - s << it->GetTunnelID () << " ⇒ "; + s << "" + << tr("inbound gateway") << ""; else if (std::dynamic_pointer_cast(it)) - s << " ⇒ " << it->GetTunnelID (); + s << "" + << tr("outbound endpoint") << ""; else - s << " ⇒ " << it->GetTunnelID () << " ⇒ "; - s << " " << it->GetNumTransmittedBytes () << "
\r\n"; + s << "" + << tr("participant") << ""; + s << "
\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 +1508,88 @@ 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 +1603,83 @@ 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 +1694,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 +1725,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,84 +1745,106 @@ 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") << ""; + s << "\r\n"; + s << "
\r\n\r\n" + << "\r\n"; + s << "
\r\n"; + s << "
\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
\r\n
\r\n"; auto& serverTunnels = i2p::client::context.GetServerTunnels (); if (!serverTunnels.empty ()) { - s << "
\r\n" << tr("Server Tunnels") << ":
\r\n
\r\n"; + s << "\r\n\r\n\r\n"; + s << "
\r\n\r\n" + << "\r\n"; + s << "
\r\n"; + s << "
\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
\r\n
\r\n\r\n"; } auto& clientForwards = i2p::client::context.GetClientForwards (); if (!clientForwards.empty ()) { - s << "
\r\n" << tr("Client Forwards") << ":
\r\n
\r\n"; + s << "\r\n\r\n\r\n"; + s << "
\r\n\r\n" + << "\r\n"; + s << "
\r\n"; + s << "
\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
\r\n
\r\n\r\n"; } auto& serverForwards = i2p::client::context.GetServerForwards (); if (!serverForwards.empty ()) { - s << "
\r\n" << tr("Server Forwards") << ":
\r\n
\r\n"; + s << "\r\n\r\n\r\n"; + s << "
\r\n\r\n" + << "\r\n"; + s << "
\r\n"; + s << "
\r\n"; for (auto& it: serverForwards) { auto& ident = it.second->GetLocalDestination ()->GetIdentHash(); - s << ""; - s << it.second->GetName () << " ⇐ "; + s << "
"; + 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
\r\n
\r\n\r\n"; } +// s << "
\r\n"; } HTTPConnection::HTTPConnection (std::string hostname, std::shared_ptr socket): @@ -1108,7 +1916,7 @@ namespace http { if (expected == provided) return true; } - LogPrint(eLogWarning, "HTTPServer: auth failure from ", m_Socket->remote_endpoint().address ()); + LogPrint(eLogWarning, "HTTPServer: Auth failure from ", m_Socket->remote_endpoint().address ()); return false; } @@ -1118,7 +1926,7 @@ namespace http { std::string content; HTTPRes res; - LogPrint(eLogDebug, "HTTPServer: request: ", req.uri); + LogPrint(eLogDebug, "HTTPServer: Request: ", req.uri); if (needAuth && !CheckAuth(req)) { res.code = 401; @@ -1150,13 +1958,18 @@ namespace http { } // HTML head start ShowPageHead (s); + if (req.uri.find("summary") != std::string::npos || + req.uri.find("commands") != std::string::npos || + (req.uri.find("local_destinations") != std::string::npos && + req.uri.find("b32") == std::string::npos)) + res.add_header("Refresh", "10"); if (req.uri.find("page=") != std::string::npos) { HandlePage (req, res, s); } else if (req.uri.find("cmd=") != std::string::npos) { HandleCommand (req, res, s); } else { ShowStatus (s, true, i2p::http::OutputFormatEnum::forWebConsole); - res.add_header("Refresh", "10"); + res.add_header("Refresh", "5"); } ShowPageTail (s); @@ -1194,35 +2007,36 @@ namespace http { url.parse_query(params); page = params["page"]; - if (page == HTTP_PAGE_TRANSPORTS) + if (page == HTTP_PAGE_TRANSPORTS) { ShowTransports (s); - else if (page == HTTP_PAGE_TUNNELS) + } else if (page == HTTP_PAGE_TUNNEL_SUMMARY) { + ShowTunnelSummary (s); +/* ShowTunnels (s); - else if (page == HTTP_PAGE_COMMANDS) - { + ShowI2PTunnels (s); + ShowTransitTunnels (s); +*/ + } else if (page == HTTP_PAGE_COMMANDS) { uint32_t token = CreateToken (); ShowCommands (s, token); - } - else if (page == HTTP_PAGE_TRANSIT_TUNNELS) + } else if (page == HTTP_PAGE_TRANSIT_TUNNELS) { ShowTransitTunnels (s); - else if (page == HTTP_PAGE_LOCAL_DESTINATIONS) + } else if (page == HTTP_PAGE_LOCAL_DESTINATIONS) { ShowLocalDestinations (s); - else if (page == HTTP_PAGE_LOCAL_DESTINATION) - { + } else if (page == HTTP_PAGE_LOCAL_DESTINATION) { uint32_t token = CreateToken (); ShowLocalDestination (s, params["b32"], token); - } - else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION) + } else if (page == HTTP_PAGE_I2CP_LOCAL_DESTINATION) { ShowI2CPLocalDestination (s, params["i2cp_id"]); - else if (page == HTTP_PAGE_SAM_SESSIONS) + } else if (page == HTTP_PAGE_SAM_SESSIONS) { ShowSAMSessions (s); - else if (page == HTTP_PAGE_SAM_SESSION) + } else if (page == HTTP_PAGE_SAM_SESSION) { ShowSAMSession (s, params["sam_id"]); - else if (page == HTTP_PAGE_I2P_TUNNELS) - ShowI2PTunnels (s); - else if (page == HTTP_PAGE_LEASESETS) + } else if (page == HTTP_PAGE_LOCAL_TUNNELS) { + ShowTunnels (s); + } else if (page == HTTP_PAGE_LEASESETS) { ShowLeasesSets(s); - else { + } else { res.code = 400; ShowError(s, tr("Unknown page") + ": " + page); return; @@ -1238,7 +2052,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 +2115,27 @@ 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 +2145,10 @@ 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"; res.add_header("Refresh", redirect.c_str()); return; } @@ -1358,25 +2181,35 @@ 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"; 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"; } 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"; } 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"; - s << "" << tr("Return to destination page") << "\r\n"; +// s << "" << tr("Return to destination page") << "\r\n"; return; } else if (cmd == HTTP_COMMAND_SETLANGUAGE) @@ -1389,7 +2222,11 @@ namespace http { } else if (cmd == HTTP_COMMAND_RELOAD_CSS) { - LoadExtCSS(); + std::string styleFile = i2p::fs::DataDirPath ("webconsole/style.css"); + if (i2p::fs::Exists(styleFile)) + LoadExtCSS(); + else + ShowError(s, tr("No CSS file found on disk!")); } else { @@ -1398,9 +2235,28 @@ 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 << "" + << ""; + 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 << "\r\n"; res.add_header("Refresh", redirect.c_str()); } @@ -1410,6 +2266,7 @@ namespace http { reply.add_header("X-Content-Type-Options", "nosniff"); reply.add_header("X-XSS-Protection", "1; mode=block"); reply.add_header("Content-Type", "text/html"); + reply.add_header("Server", "i2pd " VERSION " webconsole" ); reply.body = content; m_SendBuffer = reply.to_string(); @@ -1446,7 +2303,7 @@ namespace http { pass[i] = alnum[random[i] % (sizeof(alnum) - 1)]; } i2p::config::SetOption("http.pass", pass); - LogPrint(eLogInfo, "HTTPServer: password set to ", pass); + LogPrint(eLogInfo, "HTTPServer: Password set to ", pass); } m_IsRunning = true; @@ -1499,7 +2356,7 @@ namespace http { if (ecode) { if(newSocket) newSocket->close(); - LogPrint(eLogError, "HTTP Server: error handling accept ", ecode.message()); + LogPrint(eLogError, "HTTP Server: Error handling accept ", ecode.message()); if(ecode != boost::asio::error::operation_aborted) Accept(); return; diff --git a/libi2pd/Tunnel.cpp b/libi2pd/Tunnel.cpp index 76504e1f..c8fdc214 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"); + 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) { @@ -148,7 +148,7 @@ namespace tunnel if (idx >= 0 && idx < msg[0]) hop->DecryptRecord (msg + 1, idx); else - LogPrint (eLogWarning, "Tunnel: hop index ", idx, " is out of range"); + LogPrint (eLogWarning, "Tunnel: Hop index ", idx, " is out of range"); hop1 = hop1->prev; } hop = hop->prev; @@ -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 << ""; } } @@ -251,8 +252,12 @@ namespace tunnel void InboundTunnel::Print (std::stringstream& s) const { + s << ""; PrintHops (s); - s << " ⇒ " << GetTunnelID () << ":me"; + s << " "; + s << " Local"; + s << "" << GetTunnelID () << ""; } ZeroHopsInboundTunnel::ZeroHopsInboundTunnel (): @@ -273,7 +278,10 @@ namespace tunnel void ZeroHopsInboundTunnel::Print (std::stringstream& s) const { - s << " ⇒ " << GetTunnelID () << ":me"; + s << ""; + s << " Local"; + s << "" << GetTunnelID () << ""; } void OutboundTunnel::SendTunnelDataMsg (const uint8_t * gwHash, uint32_t gwTunnel, std::shared_ptr msg) @@ -307,14 +315,15 @@ namespace tunnel void OutboundTunnel::HandleTunnelDataMsg (std::shared_ptr tunnelMsg) { - LogPrint (eLogError, "Tunnel: incoming message for outbound tunnel ", GetTunnelID ()); + LogPrint (eLogError, "Tunnel: Incoming message for outbound tunnel ", GetTunnelID ()); } void OutboundTunnel::Print (std::stringstream& s) const { - s << GetTunnelID () << ":me"; + s << ""; + s << "Local"; PrintHops (s); - s << " ⇒ "; + s << " " << GetTunnelID () << ""; } ZeroHopsOutboundTunnel::ZeroHopsOutboundTunnel (): @@ -348,7 +357,11 @@ namespace tunnel void ZeroHopsOutboundTunnel::Print (std::stringstream& s) const { - s << GetTunnelID () << ":me ⇒ "; + s << ""; + s << " "; + s << "" << GetTunnelID () << "\">Local"; + s << "" << GetTunnelID () << ""; } Tunnels tunnels; @@ -460,7 +473,7 @@ namespace tunnel if (m_Tunnels.emplace (tunnel->GetTunnelID (), tunnel).second) m_TransitTunnels.push_back (tunnel); else - LogPrint (eLogError, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " already exists"); + LogPrint (eLogError, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " already exists"); } void Tunnels::Start () @@ -521,7 +534,7 @@ namespace tunnel HandleTunnelGatewayMsg (tunnel, msg); } else - LogPrint (eLogWarning, "Tunnel: tunnel not found, tunnelID=", tunnelID, " previousTunnelID=", prevTunnelID, " type=", (int)typeID); + LogPrint (eLogWarning, "Tunnel: Tunnel not found, tunnelID=", tunnelID, " previousTunnelID=", prevTunnelID, " type=", (int)typeID); break; } @@ -530,11 +543,11 @@ namespace tunnel case eI2NPShortTunnelBuild: case eI2NPShortTunnelBuildReply: case eI2NPTunnelBuild: - case eI2NPTunnelBuildReply: + case eI2NPTunnelBuildReply: HandleI2NPMessage (msg->GetBuffer (), msg->GetLength ()); break; default: - LogPrint (eLogWarning, "Tunnel: unexpected message type ", (int) typeID); + LogPrint (eLogWarning, "Tunnel: Unexpected message type ", (int) typeID); } msg = m_Queue.Get (); @@ -561,12 +574,12 @@ namespace tunnel { ManageTunnelPools (ts); lastPoolsTs = ts; - } - } + } + } } catch (std::exception& ex) { - LogPrint (eLogError, "Tunnel: runtime exception: ", ex.what ()); + LogPrint (eLogError, "Tunnel: Runtime exception: ", ex.what ()); } } } @@ -575,7 +588,7 @@ namespace tunnel { if (!tunnel) { - LogPrint (eLogError, "Tunnel: missing tunnel for gateway"); + LogPrint (eLogError, "Tunnel: Missing tunnel for gateway"); return; } const uint8_t * payload = msg->GetPayload (); @@ -584,12 +597,12 @@ namespace tunnel msg->offset += I2NP_HEADER_SIZE + TUNNEL_GATEWAY_HEADER_SIZE; if (msg->offset + len > msg->len) { - LogPrint (eLogError, "Tunnel: gateway payload ", (int)len, " exceeds message length ", (int)msg->len); + LogPrint (eLogError, "Tunnel: Gateway payload ", (int)len, " exceeds message length ", (int)msg->len); return; } msg->len = msg->offset + len; auto typeID = msg->GetTypeID (); - LogPrint (eLogDebug, "Tunnel: gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID); + LogPrint (eLogDebug, "Tunnel: Gateway of ", (int) len, " bytes for tunnel ", tunnel->GetTunnelID (), ", msg type ", (int)typeID); if (IsRouterInfoMsg (msg) || typeID == eI2NPDatabaseSearchReply) // transit DatabaseStore my contain new/updated RI @@ -625,7 +638,7 @@ namespace tunnel case eTunnelStatePending: if (ts > tunnel->GetCreationTime () + TUNNEL_CREATION_TIMEOUT) { - LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " timeout, deleted"); + LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " timeout, deleted"); // update stats auto config = tunnel->GetTunnelConfig (); if (config) @@ -650,7 +663,7 @@ namespace tunnel ++it; break; case eTunnelStateBuildFailed: - LogPrint (eLogDebug, "Tunnel: pending build request ", it->first, " failed, deleted"); + LogPrint (eLogDebug, "Tunnel: Pending build request ", it->first, " failed, deleted"); it = pendingTunnels.erase (it); m_NumFailedTunnelCreations++; break; @@ -675,7 +688,7 @@ namespace tunnel auto tunnel = *it; if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) { - LogPrint (eLogDebug, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " expired"); + LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired"); auto pool = tunnel->GetTunnelPool (); if (pool) pool->TunnelExpired (tunnel); @@ -712,7 +725,7 @@ namespace tunnel i2p::transport::transports.GetRestrictedPeer() : i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); // reachable by us if (!inboundTunnel || !router) return; - LogPrint (eLogDebug, "Tunnel: creating one hop outbound tunnel"); + LogPrint (eLogDebug, "Tunnel: Creating one hop outbound tunnel"); CreateTunnel ( std::make_shared (std::vector > { router->GetRouterIdentity () }, inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()), nullptr @@ -729,7 +742,7 @@ namespace tunnel auto tunnel = *it; if (ts > tunnel->GetCreationTime () + TUNNEL_EXPIRATION_TIMEOUT) { - LogPrint (eLogDebug, "Tunnel: tunnel with id ", tunnel->GetTunnelID (), " expired"); + LogPrint (eLogDebug, "Tunnel: Tunnel with id ", tunnel->GetTunnelID (), " expired"); auto pool = tunnel->GetTunnelPool (); if (pool) pool->TunnelExpired (tunnel); @@ -763,7 +776,7 @@ namespace tunnel if (m_InboundTunnels.empty ()) { - LogPrint (eLogDebug, "Tunnel: Creating zero hops inbound tunnel"); + LogPrint (eLogDebug, "Tunnel: Creating zero hop inbound tunnel"); CreateZeroHopsInboundTunnel (nullptr); CreateZeroHopsOutboundTunnel (nullptr); if (!m_ExploratoryPool) @@ -786,10 +799,10 @@ namespace tunnel // should be reachable by us because we send build request directly i2p::data::netdb.GetRandomRouter (i2p::context.GetSharedRouterInfo (), false); if (!router) { - LogPrint (eLogWarning, "Tunnel: can't find any router, skip creating tunnel"); + LogPrint (eLogWarning, "Tunnel: Can't find any router, skipping tunnel creation"); return; } - LogPrint (eLogDebug, "Tunnel: creating one hop inbound tunnel"); + LogPrint (eLogDebug, "Tunnel: Creating one hop inbound tunnel"); CreateTunnel ( std::make_shared (std::vector > { router->GetRouterIdentity () }), nullptr ); @@ -837,7 +850,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 +862,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 +926,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);