Merge pull request #1610 from PurpleI2P/openssl

recent chamges
This commit is contained in:
orignal 2020-12-30 08:27:57 -05:00 committed by GitHub
commit a2c28d0837
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1200 additions and 389 deletions

20
.github/workflows/build-freebsd.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: Build on FreeBSD
on: [push, pull_request]
jobs:
build:
runs-on: macos-latest
name: with UPnP
steps:
- uses: actions/checkout@v2
- name: Test in FreeBSD
id: test
uses: vmactions/freebsd-vm@v0.0.9
with:
usesh: true
prepare: pkg install -y devel/cmake devel/gmake devel/boost-libs security/openssl net/miniupnpc
run: |
cd build
cmake -DWITH_UPNP=ON -DCMAKE_BUILD_TYPE=Release .
gmake -j2

View File

@ -58,7 +58,7 @@ ifeq ($(USE_UPNP),yes)
endif endif
ifeq ($(USE_AESNI),yes) ifeq ($(USE_AESNI),yes)
ifeq (, $(findstring arm, $(SYS))$(findstring aarch64, $(SYS))) # no arm and aarch64 in dumpmachine ifneq (, $(findstring i386, $(SYS))$(findstring i686, $(SYS))$(findstring x86_64, $(SYS))) # only x86-based CPU supports that
NEEDED_CXXFLAGS += -D__AES__ -maes NEEDED_CXXFLAGS += -D__AES__ -maes
endif endif
endif endif

View File

@ -64,7 +64,7 @@ call :BUILDING_XP
echo. echo.
REM compile installer REM compile installer
C:\PROGRA~2\INNOSE~1\ISCC.exe /dI2Pd_ver="%tag%" build\win_installer.iss >> build\build.log 2>&1 C:\PROGRA~2\INNOSE~1\ISCC.exe /dI2Pd_TextVer="%tag%" /dI2Pd_Ver="%tag%.0" build\win_installer.iss >> build\build.log 2>&1
del README.txt i2pd_x32.exe i2pd_x64.exe i2pd_xp.exe >> nul del README.txt i2pd_x32.exe i2pd_x64.exe i2pd_xp.exe >> nul

View File

@ -6,25 +6,35 @@
[Setup] [Setup]
AppName={#I2Pd_AppName} AppName={#I2Pd_AppName}
AppVersion={#I2Pd_ver} AppVersion={#I2Pd_TextVer}
AppPublisher={#I2Pd_Publisher} AppPublisher={#I2Pd_Publisher}
DefaultDirName={pf}\I2Pd DefaultDirName={pf}\I2Pd
DefaultGroupName=I2Pd DefaultGroupName=I2Pd
UninstallDisplayIcon={app}\I2Pd.exe UninstallDisplayIcon={app}\I2Pd.exe
OutputDir=. OutputDir=.
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_TextVer}
LicenseFile=..\LICENSE LicenseFile=..\LICENSE
OutputBaseFilename=setup_{#I2Pd_AppName}_v{#I2Pd_ver}
SetupIconFile=..\Win32\mask.ico SetupIconFile=..\Win32\mask.ico
InternalCompressLevel=ultra64 InternalCompressLevel=ultra64
Compression=lzma/ultra64 Compression=lzma/ultra64
SolidCompression=true SolidCompression=true
ArchitecturesInstallIn64BitMode=x64 ArchitecturesInstallIn64BitMode=x64
AppVerName={#I2Pd_AppName}
ExtraDiskSpaceRequired=15 ExtraDiskSpaceRequired=15
AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2} AppID={{621A23E0-3CF4-4BD6-97BC-4835EA5206A2}
AppVerName={#I2Pd_AppName}
AppCopyright=Copyright (c) 2013-2020, The PurpleI2P Project
AppPublisherURL=http://i2pd.website/ AppPublisherURL=http://i2pd.website/
AppSupportURL=https://github.com/PurpleI2P/i2pd/issues AppSupportURL=https://github.com/PurpleI2P/i2pd/issues
AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases AppUpdatesURL=https://github.com/PurpleI2P/i2pd/releases
VersionInfoProductVersion={#I2Pd_Ver}
VersionInfoVersion={#I2Pd_Ver}
CloseApplications=yes CloseApplications=yes
[Files] [Files]

View File

@ -11,6 +11,7 @@ ENV REPO_URL=${REPO_URL}
ENV I2PD_HOME="/home/i2pd" ENV I2PD_HOME="/home/i2pd"
ENV DATA_DIR="${I2PD_HOME}/data" ENV DATA_DIR="${I2PD_HOME}/data"
ENV DEFAULT_ARGS=" --datadir=$DATA_DIR --reseed.verify=true --upnp.enabled=false --http.enabled=true --http.address=0.0.0.0 --httpproxy.enabled=true --httpproxy.address=0.0.0.0 --socksproxy.enabled=true --socksproxy.address=0.0.0.0 --sam.enabled=true --sam.address=0.0.0.0"
RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \ RUN mkdir -p "$I2PD_HOME" "$DATA_DIR" \
&& adduser -S -h "$I2PD_HOME" i2pd \ && adduser -S -h "$I2PD_HOME" i2pd \

View File

@ -2,7 +2,6 @@
COMMAND=/usr/local/bin/i2pd COMMAND=/usr/local/bin/i2pd
# To make ports exposeable # To make ports exposeable
# Note: $DATA_DIR is defined in /etc/profile # Note: $DATA_DIR is defined in /etc/profile
DEFAULT_ARGS=" --datadir=$DATA_DIR --reseed.verify=true --upnp.enabled=false --http.enabled=true --http.address=0.0.0.0 --httpproxy.enabled=true --httpproxy.address=0.0.0.0 --socksproxy.enabled=true --socksproxy.address=0.0.0.0 --sam.enabled=true --sam.address=0.0.0.0"
if [ "$1" = "--help" ]; then if [ "$1" = "--help" ]; then
set -- $COMMAND --help set -- $COMMAND --help

View File

@ -532,7 +532,7 @@ namespace http {
} }
} }
static void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id) void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id)
{ {
auto i2cpServer = i2p::client::context.GetI2CPServer (); auto i2cpServer = i2p::client::context.GetI2CPServer ();
if (i2cpServer) if (i2cpServer)
@ -820,7 +820,7 @@ namespace http {
s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n"; s << "<b>SAM Sessions:</b> no sessions currently running.<br>\r\n";
} }
static void ShowSAMSession (std::stringstream& s, const std::string& id) void ShowSAMSession (std::stringstream& s, const std::string& id)
{ {
auto sam = i2p::client::context.GetSAMBridge (); auto sam = i2p::client::context.GetSAMBridge ();
if (!sam) { if (!sam) {
@ -1312,6 +1312,8 @@ namespace http {
void HTTPServer::Run () void HTTPServer::Run ()
{ {
i2p::util::SetThreadName("Webconsole");
while (m_IsRunning) while (m_IsRunning)
{ {
try try

View File

@ -98,6 +98,8 @@ namespace http
void ShowSAMSessions (std::stringstream& s); void ShowSAMSessions (std::stringstream& s);
void ShowI2PTunnels (std::stringstream& s); void ShowI2PTunnels (std::stringstream& s);
void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token); void ShowLocalDestination (std::stringstream& s, const std::string& b32, uint32_t token);
void ShowSAMSession (std::stringstream& s, const std::string& id);
void ShowI2CPLocalDestination (std::stringstream& s, const std::string& id);
} // http } // http
} // i2p } // i2p

View File

@ -131,6 +131,8 @@ namespace client
void I2PControlService::Run () void I2PControlService::Run ()
{ {
i2p::util::SetThreadName("I2PC");
while (m_IsRunning) while (m_IsRunning)
{ {
try { try {

View File

@ -60,6 +60,8 @@ namespace transport
void UPnP::Run () void UPnP::Run ()
{ {
i2p::util::SetThreadName("UPnP");
while (m_IsRunning) while (m_IsRunning)
{ {
try try

View File

@ -638,7 +638,7 @@ namespace crypto
{ {
uint64_t buf[256]; uint64_t buf[256];
uint64_t hash[12]; // 96 bytes uint64_t hash[12]; // 96 bytes
#if defined(__x86_64__) || defined(__i386__) #if (defined(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600)
if(i2p::cpu::avx) if(i2p::cpu::avx)
{ {
__asm__ __asm__
@ -1316,6 +1316,8 @@ namespace crypto
#endif #endif
} }
// Noise
void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len) void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len)
{ {
SHA256_CTX ctx; SHA256_CTX ctx;
@ -1331,6 +1333,59 @@ namespace crypto
// new ck is m_CK[0:31], key is m_CK[32:63] // new ck is m_CK[0:31], key is m_CK[32:63]
} }
static void InitNoiseState (NoiseSymmetricState& state, const uint8_t * ck,
const uint8_t * hh, const uint8_t * pub)
{
// pub is Bob's public static key, hh = SHA256(h)
memcpy (state.m_CK, ck, 32);
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, hh, 32);
SHA256_Update (&ctx, pub, 32);
SHA256_Final (state.m_H, &ctx); // h = MixHash(pub) = SHA256(hh || pub)
}
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub)
{
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
static const uint8_t hh[32] =
{
0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1,
0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54
}; // hh = SHA256(protocol_name || 0)
InitNoiseState (state, (const uint8_t *)protocolName, hh, pub); // ck = protocol_name || 0
}
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub)
{
static const uint8_t protocolNameHash[] =
{
0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed,
0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71
}; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256")
static const uint8_t hh[32] =
{
0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5,
0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e
}; // SHA256 (protocolNameHash)
InitNoiseState (state, protocolNameHash, hh, pub);
}
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub)
{
static const uint8_t protocolNameHash[32] =
{
0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba,
0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c
}; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes
static const uint8_t hh[32] =
{
0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32,
0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c
}; // SHA256 (protocolNameHash)
InitNoiseState (state, protocolNameHash, hh, pub);
}
// init and terminate // init and terminate
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes; /* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;

View File

@ -318,6 +318,10 @@ namespace crypto
void MixKey (const uint8_t * sharedSecret); void MixKey (const uint8_t * sharedSecret);
}; };
void InitNoiseNState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_N (tunnels, router)
void InitNoiseXKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_XK (NTCP2)
void InitNoiseIKState (NoiseSymmetricState& state, const uint8_t * pub); // Noise_IK (ratchets)
// init and terminate // init and terminate
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force); void InitCrypto (bool precomputation, bool aesni, bool avx, bool force);
void TerminateCrypto (); void TerminateCrypto ();

View File

@ -295,11 +295,11 @@ namespace datagram
} }
} }
if (!m_RoutingSession || !m_RoutingSession->GetOwner ()) if (!m_RoutingSession || !m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ())
{ {
bool found = false; bool found = false;
for (auto& it: m_PendingRoutingSessions) for (auto& it: m_PendingRoutingSessions)
if (it->GetOwner ()) // found established session if (it->GetOwner () && m_RoutingSession->IsReadyToSend ()) // found established session
{ {
m_RoutingSession = it; m_RoutingSession = it;
m_PendingRoutingSessions.clear (); m_PendingRoutingSessions.clear ();
@ -309,7 +309,7 @@ namespace datagram
if (!found) if (!found)
{ {
m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true); m_RoutingSession = m_LocalDestination->GetRoutingSession(m_RemoteLeaseSet, true);
if (!m_RoutingSession->GetOwner ()) if (!m_RoutingSession->GetOwner () || !m_RoutingSession->IsReadyToSend ())
m_PendingRoutingSessions.push_back (m_RoutingSession); m_PendingRoutingSessions.push_back (m_RoutingSession);
} }
} }

View File

@ -379,7 +379,8 @@ namespace client
LogPrint (eLogDebug, "Destination: Remote LeaseSet"); LogPrint (eLogDebug, "Destination: Remote LeaseSet");
std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex); std::lock_guard<std::mutex> lock(m_RemoteLeaseSetsMutex);
auto it = m_RemoteLeaseSets.find (key); auto it = m_RemoteLeaseSets.find (key);
if (it != m_RemoteLeaseSets.end ()) if (it != m_RemoteLeaseSets.end () &&
it->second->GetStoreType () == buf[DATABASE_STORE_TYPE_OFFSET]) // update only if same type
{ {
leaseSet = it->second; leaseSet = it->second;
if (leaseSet->IsNewer (buf + offset, len - offset)) if (leaseSet->IsNewer (buf + offset, len - offset))
@ -399,6 +400,7 @@ namespace client
} }
else else
{ {
// add or replace
if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET) if (buf[DATABASE_STORE_TYPE_OFFSET] == i2p::data::NETDB_STORE_TYPE_LEASESET)
leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet leaseSet = std::make_shared<i2p::data::LeaseSet> (buf + offset, len - offset); // LeaseSet
else else
@ -559,9 +561,7 @@ namespace client
m_ExcludedFloodfills.insert (floodfill->GetIdentHash ()); m_ExcludedFloodfills.insert (floodfill->GetIdentHash ());
LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ()); LogPrint (eLogDebug, "Destination: Publish LeaseSet of ", GetIdentHash ().ToBase32 ());
RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4); RAND_bytes ((uint8_t *)&m_PublishReplyToken, 4);
auto msg = i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound); auto msg = WrapMessageForRouter (floodfill, i2p::CreateDatabaseStoreMsg (leaseSet, m_PublishReplyToken, inbound));
if (floodfill->GetIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // TODO: remove whan implemented
msg = WrapMessageForRouter (floodfill, msg);
m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT)); m_PublishConfirmationTimer.expires_from_now (boost::posix_time::seconds(PUBLISH_CONFIRMATION_TIMEOUT));
m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer, m_PublishConfirmationTimer.async_wait (std::bind (&LeaseSetDestination::HandlePublishConfirmationTimer,
shared_from_this (), std::placeholders::_1)); shared_from_this (), std::placeholders::_1));
@ -755,10 +755,8 @@ namespace client
AddECIESx25519Key (replyKey, replyTag); AddECIESx25519Key (replyKey, replyTag);
else else
AddSessionKey (replyKey, replyTag); AddSessionKey (replyKey, replyTag);
auto msg = WrapMessageForRouter (nextFloodfill, CreateLeaseSetDatabaseLookupMsg (dest,
auto msg = CreateLeaseSetDatabaseLookupMsg (dest, request->excluded, request->replyTunnel, replyKey, replyTag, isECIES); request->excluded, request->replyTunnel, replyKey, replyTag, isECIES));
if (nextFloodfill->GetIdentity ()->GetCryptoKeyType () != i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD) // TODO: remove whan implemented
msg = WrapMessageForRouter (nextFloodfill, msg);
request->outboundTunnel->SendTunnelDataMsg ( request->outboundTunnel->SendTunnelDataMsg (
{ {
i2p::tunnel::TunnelMessageBlock i2p::tunnel::TunnelMessageBlock

View File

@ -9,6 +9,7 @@
#include <string.h> #include <string.h>
#include <openssl/sha.h> #include <openssl/sha.h>
#include "Log.h" #include "Log.h"
#include "util.h"
#include "Crypto.h" #include "Crypto.h"
#include "Elligator.h" #include "Elligator.h"
#include "Tag.h" #include "Tag.h"
@ -45,13 +46,13 @@ namespace garlic
uint64_t RatchetTagSet::GetNextSessionTag () uint64_t RatchetTagSet::GetNextSessionTag ()
{ {
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
m_NextIndex++; m_NextIndex++;
if (m_NextIndex >= 65535) if (m_NextIndex >= 65535)
{ {
LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty"); LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty");
return 0; return 0;
} }
i2p::crypto::HKDF (m_KeyData.GetSessTagCK (), m_SessTagConstant, 32, "SessionTagKeyGen", m_KeyData.buf); // [sessTag_ck, tag] = HKDF(sessTag_chainkey, SESSTAG_CONSTANT, "SessionTagKeyGen", 64)
return m_KeyData.GetTag (); return m_KeyData.GetTag ();
} }
@ -153,29 +154,12 @@ namespace garlic
GarlicRoutingSession (owner, attachLeaseSet) GarlicRoutingSession (owner, attachLeaseSet)
{ {
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0; RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
ResetKeys ();
} }
ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession () ECIESX25519AEADRatchetSession::~ECIESX25519AEADRatchetSession ()
{ {
} }
void ECIESX25519AEADRatchetSession::ResetKeys ()
{
static const uint8_t protocolNameHash[32] =
{
0x4c, 0xaf, 0x11, 0xef, 0x2c, 0x8e, 0x36, 0x56, 0x4c, 0x53, 0xe8, 0x88, 0x85, 0x06, 0x4d, 0xba,
0xac, 0xbe, 0x00, 0x54, 0xad, 0x17, 0x8f, 0x80, 0x79, 0xa6, 0x46, 0x82, 0x7e, 0x6e, 0xe4, 0x0c
}; // SHA256("Noise_IKelg2+hs2_25519_ChaChaPoly_SHA256"), 40 bytes
static const uint8_t hh[32] =
{
0x9c, 0xcf, 0x85, 0x2c, 0xc9, 0x3b, 0xb9, 0x50, 0x44, 0x41, 0xe9, 0x50, 0xe0, 0x1d, 0x52, 0x32,
0x2e, 0x0d, 0x47, 0xad, 0xd1, 0xe9, 0xa5, 0x55, 0xf7, 0x55, 0xb5, 0x69, 0xae, 0x18, 0x3b, 0x5c
}; // SHA256 (protocolNameHash)
memcpy (m_CK, protocolNameHash, 32);
memcpy (m_H, hh, 32);
}
void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce) void ECIESX25519AEADRatchetSession::CreateNonce (uint64_t seqn, uint8_t * nonce)
{ {
memset (nonce, 0, 4); memset (nonce, 0, 4);
@ -236,7 +220,7 @@ namespace garlic
if (!GetOwner ()) return false; if (!GetOwner ()) return false;
// we are Bob // we are Bob
// KDF1 // KDF1
MixHash (GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD), 32); // h = SHA256(h || bpk) i2p::crypto::InitNoiseIKState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk)) if (!i2p::crypto::GetElligator ()->Decode (buf, m_Aepk))
{ {
@ -315,7 +299,10 @@ namespace garlic
break; break;
case eECIESx25519BlkNextKey: case eECIESx25519BlkNextKey:
LogPrint (eLogDebug, "Garlic: next key"); LogPrint (eLogDebug, "Garlic: next key");
if (receiveTagset)
HandleNextKey (buf + offset, size, receiveTagset); HandleNextKey (buf + offset, size, receiveTagset);
else
LogPrint (eLogError, "Garlic: Unexpected next key block");
break; break;
case eECIESx25519BlkAck: case eECIESx25519BlkAck:
{ {
@ -448,7 +435,6 @@ namespace garlic
bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic) bool ECIESX25519AEADRatchetSession::NewOutgoingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen, bool isStatic)
{ {
ResetKeys ();
// we are Alice, bpk is m_RemoteStaticKey // we are Alice, bpk is m_RemoteStaticKey
size_t offset = 0; size_t offset = 0;
if (!GenerateEphemeralKeysAndEncode (out + offset)) if (!GenerateEphemeralKeysAndEncode (out + offset))
@ -459,7 +445,7 @@ namespace garlic
offset += 32; offset += 32;
// KDF1 // KDF1
MixHash (m_RemoteStaticKey, 32); // h = SHA256(h || bpk) i2p::crypto::InitNoiseIKState (*this, m_RemoteStaticKey); // bpk
MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk) MixHash (m_EphemeralKeys->GetPublicKey (), 32); // h = SHA256(h || aepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk) m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
@ -508,6 +494,31 @@ namespace garlic
return true; return true;
} }
bool ECIESX25519AEADRatchetSession::NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{
// we are Alice, router's bpk is m_RemoteStaticKey
i2p::crypto::InitNoiseNState (*this, m_RemoteStaticKey);
size_t offset = 0;
m_EphemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (out + offset, m_EphemeralKeys->GetPublicKey (), 32);
MixHash (out + offset, 32); // h = SHA256(h || aepk)
offset += 32;
uint8_t sharedSecret[32];
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
MixKey (sharedSecret);
uint8_t nonce[12];
CreateNonce (0, nonce);
// encrypt payload
if (!i2p::crypto::AEADChaCha20Poly1305 (payload, len, m_H, 32, m_CK + 32, nonce, out + offset, len + 16, true)) // encrypt
{
LogPrint (eLogWarning, "Garlic: Payload for router AEAD encryption failed");
return false;
}
m_State = eSessionStateNewSessionSent;
return true;
}
bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen) bool ECIESX25519AEADRatchetSession::NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen)
{ {
// we are Bob // we are Bob
@ -609,18 +620,15 @@ namespace garlic
} }
buf += 32; len -= 32; buf += 32; len -= 32;
// KDF for Reply Key Section // KDF for Reply Key Section
uint8_t h[32]; memcpy (h, m_H, 32); // save m_H i2p::util::SaveStateHelper<i2p::crypto::NoiseSymmetricState> s(*this); // restore noise state on exit
MixHash (tag, 8); // h = SHA256(h || tag) MixHash (tag, 8); // h = SHA256(h || tag)
MixHash (bepk, 32); // h = SHA256(h || bepk) MixHash (bepk, 32); // h = SHA256(h || bepk)
uint8_t sharedSecret[32]; uint8_t sharedSecret[32];
if (m_State == eSessionStateNewSessionSent)
{
// only fist time, we assume ephemeral keys the same
m_EphemeralKeys->Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk) m_EphemeralKeys->Agree (bepk, sharedSecret); // sharedSecret = x25519(aesk, bepk)
MixKey (sharedSecret); MixKey (sharedSecret);
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk) GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk)
MixKey (sharedSecret); MixKey (sharedSecret);
}
uint8_t nonce[12]; uint8_t nonce[12];
CreateNonce (0, nonce); CreateNonce (0, nonce);
// calculate hash for zero length // calculate hash for zero length
@ -636,6 +644,7 @@ namespace garlic
i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64) i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
if (m_State == eSessionStateNewSessionSent) if (m_State == eSessionStateNewSessionSent)
{ {
// only first time, then we keep using existing tagsets
// k_ab = keydata[0:31], k_ba = keydata[32:63] // k_ab = keydata[0:31], k_ba = keydata[32:63]
m_SendTagset = std::make_shared<RatchetTagSet>(shared_from_this ()); m_SendTagset = std::make_shared<RatchetTagSet>(shared_from_this ());
m_SendTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab) m_SendTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
@ -657,11 +666,10 @@ namespace garlic
if (m_State == eSessionStateNewSessionSent) if (m_State == eSessionStateNewSessionSent)
{ {
m_State = eSessionStateEstablished; m_State = eSessionStateEstablished;
m_EphemeralKeys = nullptr; //m_EphemeralKeys = nullptr; // TODO: delete after a while
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch (); m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ()); GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
} }
memcpy (m_H, h, 32); // restore m_H
HandlePayload (buf, len - 16, nullptr, 0); HandlePayload (buf, len - 16, nullptr, 0);
// we have received reply to NS with LeaseSet in it // we have received reply to NS with LeaseSet in it
@ -677,6 +685,13 @@ namespace garlic
auto index = m_SendTagset->GetNextIndex (); auto index = m_SendTagset->GetNextIndex ();
CreateNonce (index, nonce); // tag's index CreateNonce (index, nonce); // tag's index
uint64_t tag = m_SendTagset->GetNextSessionTag (); uint64_t tag = m_SendTagset->GetNextSessionTag ();
if (!tag)
{
LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for send tagset");
if (GetOwner ())
GetOwner ()->RemoveECIESx25519Session (m_RemoteStaticKey);
return false;
}
memcpy (out, &tag, 8); memcpy (out, &tag, 8);
// ad = The session tag, 8 bytes // ad = The session tag, 8 bytes
// ciphertext = ENCRYPT(k, n, payload, ad) // ciphertext = ENCRYPT(k, n, payload, ad)
@ -714,21 +729,20 @@ namespace garlic
{ {
if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2) if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2)
moreTags = GetOwner ()->GetNumRatchetInboundTags (); moreTags = GetOwner ()->GetNumRatchetInboundTags ();
index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind
} }
else else
{ {
moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4 moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4
if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS; if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS;
moreTags -= (receiveTagset->GetNextIndex () - index); moreTags -= (receiveTagset->GetNextIndex () - index);
index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind
} }
if (moreTags > 0) if (moreTags > 0)
{
GenerateMoreReceiveTags (receiveTagset, moreTags); GenerateMoreReceiveTags (receiveTagset, moreTags);
index -= (moreTags >> 1); // /2
if (index > 0) if (index > 0)
receiveTagset->SetTrimBehind (index); receiveTagset->SetTrimBehind (index);
} }
}
return true; return true;
} }
@ -746,12 +760,16 @@ namespace garlic
[[fallthrough]]; [[fallthrough]];
#endif #endif
case eSessionStateEstablished: case eSessionStateEstablished:
if (HandleExistingSessionMessage (buf, len, receiveTagset, index)) return true; if (receiveTagset->IsNS ())
// check NSR just in case {
// our of sequence NSR
LogPrint (eLogDebug, "Garlic: check for out of order NSR with index ", index); LogPrint (eLogDebug, "Garlic: check for out of order NSR with index ", index);
if (receiveTagset->GetNextIndex () - index < ECIESX25519_NSR_NUM_GENERATED_TAGS/2) if (receiveTagset->GetNextIndex () - index < ECIESX25519_NSR_NUM_GENERATED_TAGS/2)
GenerateMoreReceiveTags (receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS); GenerateMoreReceiveTags (receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS);
return HandleNewOutgoingSessionReply (buf, len); return HandleNewOutgoingSessionReply (buf, len);
}
else
return HandleExistingSessionMessage (buf, len, receiveTagset, index);
case eSessionStateNew: case eSessionStateNew:
return HandleNewIncomingSession (buf, len); return HandleNewIncomingSession (buf, len);
case eSessionStateNewSessionSent: case eSessionStateNewSessionSent:
@ -763,6 +781,28 @@ namespace garlic
return true; return true;
} }
bool ECIESX25519AEADRatchetSession::HandleNextMessageForRouter (const uint8_t * buf, size_t len)
{
if (!GetOwner ()) return false;
// we are Bob
i2p::crypto::InitNoiseNState (*this, GetOwner ()->GetEncryptionPublicKey (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD)); // bpk
MixHash (buf, 32);
uint8_t sharedSecret[32];
GetOwner ()->Decrypt (buf, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519(bsk, aepk)
MixKey (sharedSecret);
buf += 32; len -= 32;
uint8_t nonce[12];
CreateNonce (0, nonce);
std::vector<uint8_t> payload (len - 16);
if (!i2p::crypto::AEADChaCha20Poly1305 (buf, len - 16, m_H, 32, m_CK + 32, nonce, payload.data (), len - 16, false)) // decrypt
{
LogPrint (eLogWarning, "Garlic: Payload for router AEAD verification failed");
return false;
}
HandlePayload (payload.data (), len - 16, nullptr, 0);
return true;
}
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg) std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg)
{ {
auto payload = CreatePayload (msg, m_State != eSessionStateEstablished); auto payload = CreatePayload (msg, m_State != eSessionStateEstablished);
@ -799,6 +839,11 @@ namespace garlic
return nullptr; return nullptr;
len += 96; len += 96;
break; break;
case eSessionStateForRouter:
if (!NewOutgoingMessageForRouter (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 48;
break;
default: default:
return nullptr; return nullptr;
} }
@ -809,9 +854,9 @@ namespace garlic
return m; return m;
} }
std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg) std::shared_ptr<I2NPMessage> ECIESX25519AEADRatchetSession::WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter)
{ {
m_State = eSessionStateOneTime; m_State = isForRouter ? eSessionStateForRouter : eSessionStateOneTime;
return WrapSingleMessage (msg); return WrapSingleMessage (msg);
} }
@ -1014,7 +1059,14 @@ namespace garlic
if (GetOwner ()) if (GetOwner ())
{ {
for (int i = 0; i < numTags; i++) for (int i = 0; i < numTags; i++)
GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset); {
auto tag = GetOwner ()->AddECIESx25519SessionNextTag (receiveTagset);
if (!tag)
{
LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for receive tagset");
break;
}
}
} }
} }

View File

@ -33,7 +33,7 @@ namespace garlic
const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180 const int ECIESX25519_PREVIOUS_TAGSET_EXPIRATION_TIMEOUT = 180; // 180
const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after const int ECIESX25519_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24; const int ECIESX25519_MIN_NUM_GENERATED_TAGS = 24;
const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 160; const int ECIESX25519_MAX_NUM_GENERATED_TAGS = 320;
const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12; const int ECIESX25519_NSR_NUM_GENERATED_TAGS = 12;
const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */ const size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */
@ -45,6 +45,7 @@ namespace garlic
public: public:
RatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session): m_Session (session) {}; RatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session): m_Session (session) {};
virtual bool IsNS () const { return false; };
void DHInitialize (const uint8_t * rootKey, const uint8_t * k); void DHInitialize (const uint8_t * rootKey, const uint8_t * k);
void NextSessionTagRatchet (); void NextSessionTagRatchet ();
@ -92,6 +93,8 @@ namespace garlic
NSRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session): NSRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session):
RatchetTagSet (session), m_DummySession (session) {}; RatchetTagSet (session), m_DummySession (session) {};
bool IsNS () const { return true; };
private: private:
std::shared_ptr<ECIESX25519AEADRatchetSession> m_DummySession; // we need a strong pointer for NS std::shared_ptr<ECIESX25519AEADRatchetSession> m_DummySession; // we need a strong pointer for NS
@ -140,7 +143,8 @@ namespace garlic
eSessionStateNewSessionSent, eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent, eSessionStateNewSessionReplySent,
eSessionStateEstablished, eSessionStateEstablished,
eSessionStateOneTime eSessionStateOneTime,
eSessionStateForRouter
}; };
struct DHRatchet struct DHRatchet
@ -157,8 +161,9 @@ namespace garlic
~ECIESX25519AEADRatchetSession (); ~ECIESX25519AEADRatchetSession ();
bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<RatchetTagSet> receiveTagset, int index = 0); bool HandleNextMessage (uint8_t * buf, size_t len, std::shared_ptr<RatchetTagSet> receiveTagset, int index = 0);
bool HandleNextMessageForRouter (const uint8_t * buf, size_t len);
std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<I2NPMessage> WrapSingleMessage (std::shared_ptr<const I2NPMessage> msg);
std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg); std::shared_ptr<I2NPMessage> WrapOneTimeMessage (std::shared_ptr<const I2NPMessage> msg, bool isForRouter = false);
const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; } const uint8_t * GetRemoteStaticKey () const { return m_RemoteStaticKey; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); } void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
@ -178,7 +183,6 @@ namespace garlic
private: private:
void ResetKeys ();
void CreateNonce (uint64_t seqn, uint8_t * nonce); void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
std::shared_ptr<RatchetTagSet> CreateNewSessionTagset (); std::shared_ptr<RatchetTagSet> CreateNewSessionTagset ();
@ -193,6 +197,7 @@ namespace garlic
bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NextNewSessionReplyMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen); bool NewExistingSessionMessage (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
bool NewOutgoingMessageForRouter (const uint8_t * payload, size_t len, uint8_t * out, size_t outLen);
std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first); std::vector<uint8_t> CreatePayload (std::shared_ptr<const I2NPMessage> msg, bool first);
size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len); size_t CreateGarlicClove (std::shared_ptr<const I2NPMessage> msg, uint8_t * buf, size_t len);

View File

@ -485,9 +485,29 @@ namespace garlic
} }
auto mod = length & 0x0f; // %16 auto mod = length & 0x0f; // %16
buf += 4; // length buf += 4; // length
bool found = false;
uint64_t tag;
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{
// try ECIESx25519 tag
memcpy (&tag, buf, 8);
auto it1 = m_ECIESx25519Tags.find (tag);
if (it1 != m_ECIESx25519Tags.end ())
{
found = true;
if (it1->second.tagset->HandleNextMessage (buf, length, it1->second.index))
m_LastTagset = it1->second.tagset;
else
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
m_ECIESx25519Tags.erase (it1);
}
}
if (!found)
{
auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16 auto it = !mod ? m_Tags.find (SessionTag(buf)) : m_Tags.end (); // AES block is multiple of 16
// AES tag might be used even if encryption type is not ElGamal/AES // AES tag might be used even if encryption type is not ElGamal/AES
if (it != m_Tags.end ()) if (it != m_Tags.end ()) // try AES tag
{ {
// tag found. Use AES // tag found. Use AES
auto decryption = it->second; auto decryption = it->second;
@ -499,28 +519,11 @@ namespace garlic
decryption->SetIV (iv); decryption->SetIV (iv);
decryption->Decrypt (buf + 32, length - 32, buf + 32); decryption->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, decryption, msg->from); HandleAESBlock (buf + 32, length - 32, decryption, msg->from);
found = true;
} }
else else
LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes"); LogPrint (eLogWarning, "Garlic: message length ", length, " is less than 32 bytes");
} }
else
{
bool found = false;
if (SupportsEncryptionType (i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD))
{
// try ECIESx25519 tag
uint64_t tag;
memcpy (&tag, buf, 8);
auto it1 = m_ECIESx25519Tags.find (tag);
if (it1 != m_ECIESx25519Tags.end ())
{
found = true;
if (!it1->second.tagset->HandleNextMessage (buf, length, it1->second.index))
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
m_ECIESx25519Tags.erase (it1);
}
}
if (!found) // assume new session if (!found) // assume new session
{ {
// AES tag not found. Handle depending on encryption type // AES tag not found. Handle depending on encryption type
@ -541,8 +544,33 @@ namespace garlic
// otherwise ECIESx25519 // otherwise ECIESx25519
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
if (!session->HandleNextMessage (buf, length, nullptr, 0)) if (!session->HandleNextMessage (buf, length, nullptr, 0))
{
// try to gererate more tags for last tagset
if (m_LastTagset && m_LastTagset->GetNextIndex () < 2*ECIESX25519_TAGSET_MAX_NUM_TAGS)
{
auto maxTags = std::max (m_NumRatchetInboundTags, ECIESX25519_MAX_NUM_GENERATED_TAGS);
for (int i = 0; i < maxTags; i++)
{
auto nextTag = AddECIESx25519SessionNextTag (m_LastTagset);
if (!nextTag)
{
LogPrint (eLogError, "Garlic: can't create new ECIES-X25519-AEAD-Ratchet tag for last tagset");
break;
}
if (nextTag == tag)
{
LogPrint (eLogDebug, "Garlic: Missing ECIES-X25519-AEAD-Ratchet tag was generated");
if (m_LastTagset->HandleNextMessage (buf, length, m_ECIESx25519Tags[tag].index))
found = true;
break;
}
}
if (!found) m_LastTagset = nullptr;
}
if (!found)
LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message"); LogPrint (eLogError, "Garlic: can't handle ECIES-X25519-AEAD-Ratchet message");
} }
}
else else
LogPrint (eLogError, "Garlic: Failed to decrypt message"); LogPrint (eLogError, "Garlic: Failed to decrypt message");
} }
@ -716,7 +744,7 @@ namespace garlic
{ {
auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false); auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false);
session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ()); session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ());
return session->WrapOneTimeMessage (msg); return session->WrapOneTimeMessage (msg, true);
} }
else else
{ {
@ -844,6 +872,8 @@ namespace garlic
} }
if (numExpiredTags > 0) if (numExpiredTags > 0)
LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ()); LogPrint (eLogDebug, "Garlic: ", numExpiredTags, " ECIESx25519 tags expired for ", GetIdentHash().ToBase64 ());
if (m_LastTagset && m_LastTagset->IsExpired (ts))
m_LastTagset = nullptr;
} }
void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID) void GarlicDestination::RemoveDeliveryStatusSession (uint32_t msgID)
@ -1029,11 +1059,13 @@ namespace garlic
} }
} }
void GarlicDestination::AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset) uint64_t GarlicDestination::AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset)
{ {
auto index = tagset->GetNextIndex (); auto index = tagset->GetNextIndex ();
uint64_t tag = tagset->GetNextSessionTag (); uint64_t tag = tagset->GetNextSessionTag ();
if (tag)
m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset}); m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset});
return tag;
} }
void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session) void GarlicDestination::AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session)

View File

@ -245,7 +245,7 @@ namespace garlic
void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag void AddECIESx25519Key (const uint8_t * key, const uint8_t * tag); // one tag
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID); void DeliveryStatusSent (GarlicRoutingSessionPtr session, uint32_t msgID);
void AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset); uint64_t AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset);
void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session); void AddECIESx25519Session (const uint8_t * staticKey, ECIESX25519AEADRatchetSessionPtr session);
void RemoveECIESx25519Session (const uint8_t * staticKey); void RemoveECIESx25519Session (const uint8_t * staticKey);
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len); void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
@ -285,6 +285,7 @@ namespace garlic
int m_NumRatchetInboundTags; int m_NumRatchetInboundTags;
std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags; std::unordered_map<SessionTag, std::shared_ptr<AESDecryption>, std::hash<i2p::data::Tag<32> > > m_Tags;
std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session std::unordered_map<uint64_t, ECIESX25519AEADRatchetIndexTagset> m_ECIESx25519Tags; // session tag -> session
RatchetTagSetPtr m_LastTagset; // tagset last message came for
// DeliveryStatus // DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex; std::mutex m_DeliveryStatusSessionsMutex;
std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session std::unordered_map<uint32_t, GarlicRoutingSessionPtr> m_DeliveryStatusSessions; // msgID -> session

View File

@ -828,7 +828,7 @@ namespace data
XORMetric operator^(const IdentHash& key1, const IdentHash& key2) XORMetric operator^(const IdentHash& key1, const IdentHash& key2)
{ {
XORMetric m; XORMetric m;
#if defined(__x86_64__) || defined(__i386__) #if (defined(__x86_64__) || defined(__i386__)) && defined(__AVX__) // not all X86 targets supports AVX (like old Pentium, see #1600)
if(i2p::cpu::avx) if(i2p::cpu::avx)
{ {
__asm__ __asm__

View File

@ -7,6 +7,7 @@
*/ */
#include "Log.h" #include "Log.h"
#include "util.h"
//for std::transform //for std::transform
#include <algorithm> #include <algorithm>
@ -179,6 +180,8 @@ namespace log {
void Log::Run () void Log::Run ()
{ {
i2p::util::SetThreadName("Logging");
Reopen (); Reopen ();
while (m_IsRunning) while (m_IsRunning)
{ {

View File

@ -41,23 +41,7 @@ namespace transport
void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub) void NTCP2Establisher::KeyDerivationFunction1 (const uint8_t * pub, i2p::crypto::X25519Keys& priv, const uint8_t * rs, const uint8_t * epub)
{ {
static const uint8_t protocolNameHash[] = i2p::crypto::InitNoiseXKState (*this, rs);
{
0x72, 0xe8, 0x42, 0xc5, 0x45, 0xe1, 0x80, 0x80, 0xd3, 0x9c, 0x44, 0x93, 0xbb, 0x91, 0xd7, 0xed,
0xf2, 0x28, 0x98, 0x17, 0x71, 0x21, 0x8c, 0x1f, 0x62, 0x4e, 0x20, 0x6f, 0x28, 0xd3, 0x2f, 0x71
}; // SHA256 ("Noise_XKaesobfse+hs2+hs3_25519_ChaChaPoly_SHA256")
static const uint8_t hh[32] =
{
0x49, 0xff, 0x48, 0x3f, 0xc4, 0x04, 0xb9, 0xb2, 0x6b, 0x11, 0x94, 0x36, 0x72, 0xff, 0x05, 0xb5,
0x61, 0x27, 0x03, 0x31, 0xba, 0x89, 0xb8, 0xfc, 0x33, 0x15, 0x93, 0x87, 0x57, 0xdd, 0x3d, 0x1e
}; // SHA256 (protocolNameHash)
memcpy (m_CK, protocolNameHash, 32);
// h = SHA256(hh || rs)
SHA256_CTX ctx;
SHA256_Init (&ctx);
SHA256_Update (&ctx, hh, 32);
SHA256_Update (&ctx, rs, 32);
SHA256_Final (m_H, &ctx);
// h = SHA256(h || epub) // h = SHA256(h || epub)
MixHash (epub, 32); MixHash (epub, 32);
// x25519 between pub and priv // x25519 between pub and priv

View File

@ -11,7 +11,6 @@
#include <inttypes.h> #include <inttypes.h>
#include <memory> #include <memory>
#include <thread>
#include <list> #include <list>
#include <map> #include <map>
#include <array> #include <array>

View File

@ -26,6 +26,7 @@
#include "ECIESX25519AEADRatchetSession.h" #include "ECIESX25519AEADRatchetSession.h"
#include "Config.h" #include "Config.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "util.h"
using namespace i2p::transport; using namespace i2p::transport;
@ -88,6 +89,8 @@ namespace data
void NetDb::Run () void NetDb::Run ()
{ {
i2p::util::SetThreadName("NetDB");
uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0; uint32_t lastSave = 0, lastPublish = 0, lastExploratory = 0, lastManageRequest = 0, lastDestinationCleanup = 0;
while (m_IsRunning) while (m_IsRunning)
{ {

View File

@ -19,7 +19,7 @@
#include "version.h" #include "version.h"
#include "Log.h" #include "Log.h"
#include "Family.h" #include "Family.h"
#include "TunnelConfig.h" #include "ECIESX25519AEADRatchetSession.h"
#include "RouterContext.h" #include "RouterContext.h"
namespace i2p namespace i2p
@ -45,15 +45,15 @@ namespace i2p
if (IsECIES ()) if (IsECIES ())
{ {
auto initState = new i2p::crypto::NoiseSymmetricState (); auto initState = new i2p::crypto::NoiseSymmetricState ();
i2p::tunnel::InitBuildRequestRecordNoiseState (*initState); i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ());
initState->MixHash (GetIdentity ()->GetEncryptionPublicKey (), 32); // h = SHA256(h || hepk)
m_InitialNoiseState.reset (initState); m_InitialNoiseState.reset (initState);
} }
} }
void RouterContext::CreateNewRouter () void RouterContext::CreateNewRouter ()
{ {
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
SaveKeys (); SaveKeys ();
NewRouterInfo (); NewRouterInfo ();
} }
@ -591,7 +591,8 @@ namespace i2p
// update keys // update keys
LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new"); LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
oldIdentity = m_Keys.GetPublic (); oldIdentity = m_Keys.GetPublic ();
m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519); m_Keys = i2p::data::PrivateKeys::CreateRandomKeys (i2p::data::SIGNING_KEY_TYPE_EDDSA_SHA512_ED25519,
i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD);
SaveKeys (); SaveKeys ();
} }
// read NTCP2 keys if available // read NTCP2 keys if available
@ -674,6 +675,20 @@ namespace i2p
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg) void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{ {
std::unique_lock<std::mutex> l(m_GarlicMutex); std::unique_lock<std::mutex> l(m_GarlicMutex);
if (IsECIES ())
{
uint8_t * buf = msg->GetPayload ();
uint32_t len = bufbe32toh (buf);
if (len > msg->GetLength ())
{
LogPrint (eLogWarning, "Router: garlic message length ", len, " exceeds I2NP message length ", msg->GetLength ());
return;
}
buf += 4;
auto session = std::make_shared<i2p::garlic::ECIESX25519AEADRatchetSession>(this, false);
session->HandleNextMessageForRouter (buf, len);
}
else
i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg); i2p::garlic::GarlicDestination::ProcessGarlicMessage (msg);
} }

View File

@ -12,6 +12,7 @@
#include "RouterContext.h" #include "RouterContext.h"
#include "NetDb.hpp" #include "NetDb.hpp"
#include "SSU.h" #include "SSU.h"
#include "util.h"
#ifdef _WIN32 #ifdef _WIN32
#include <boost/winapi/error_codes.hpp> #include <boost/winapi/error_codes.hpp>
@ -142,6 +143,8 @@ namespace transport
void SSUServer::Run () void SSUServer::Run ()
{ {
i2p::util::SetThreadName("SSU");
while (m_IsRunning) while (m_IsRunning)
{ {
try try
@ -157,6 +160,8 @@ namespace transport
void SSUServer::RunReceivers () void SSUServer::RunReceivers ()
{ {
i2p::util::SetThreadName("SSUv4");
while (m_IsRunning) while (m_IsRunning)
{ {
try try
@ -179,6 +184,8 @@ namespace transport
void SSUServer::RunReceiversV6 () void SSUServer::RunReceiversV6 ()
{ {
i2p::util::SetThreadName("SSUv6");
while (m_IsRunning) while (m_IsRunning)
{ {
try try

View File

@ -185,6 +185,11 @@ namespace transport
std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first; std::unique_ptr<IncompleteMessage>(new IncompleteMessage (msg)))).first;
} }
std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second; std::unique_ptr<IncompleteMessage>& incompleteMessage = it->second;
// mark fragment as received
if (fragmentNum < 64)
incompleteMessage->receivedFragmentsBits |= (0x01 << fragmentNum);
else
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
// handle current fragment // handle current fragment
if (fragmentNum == incompleteMessage->nextFragmentNum) if (fragmentNum == incompleteMessage->nextFragmentNum)
@ -266,7 +271,7 @@ namespace transport
} }
} }
else else
SendFragmentAck (msgID, fragmentNum); SendFragmentAck (msgID, incompleteMessage->receivedFragmentsBits);
buf += fragmentSize; buf += fragmentSize;
} }
} }
@ -394,13 +399,9 @@ namespace transport
m_Session.Send (buf, 48); m_Session.Send (buf, 48);
} }
void SSUData::SendFragmentAck (uint32_t msgID, int fragmentNum) void SSUData::SendFragmentAck (uint32_t msgID, uint64_t bits)
{ {
if (fragmentNum > 64) if (!bits) return;
{
LogPrint (eLogWarning, "SSU: Fragment number ", fragmentNum, " exceeds 64");
return;
}
uint8_t buf[64 + 18] = {0}; uint8_t buf[64 + 18] = {0};
uint8_t * payload = buf + sizeof (SSUHeader); uint8_t * payload = buf + sizeof (SSUHeader);
*payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag *payload = DATA_FLAG_ACK_BITFIELDS_INCLUDED; // flag
@ -410,14 +411,16 @@ namespace transport
// one ack // one ack
*(uint32_t *)(payload) = htobe32 (msgID); // msgID *(uint32_t *)(payload) = htobe32 (msgID); // msgID
payload += 4; payload += 4;
div_t d = div (fragmentNum, 7); size_t len = 0;
memset (payload, 0x80, d.quot); // 0x80 means non-last while (bits)
payload += d.quot; {
*payload = 0x01 << d.rem; // set corresponding bit *payload = (bits & 0x7F); // next 7 bits
payload++; bits >>= 7;
if (bits) *payload &= 0x80; // 0x80 means non-last
payload++; len++;
}
*payload = 0; // number of fragments *payload = 0; // number of fragments
len = (len <= 4) ? 48 : 64; // 48 = 37 + 7 + 4
size_t len = d.quot < 4 ? 48 : 64; // 48 = 37 + 7 + 4 (3+1)
// encrypt message with session key // encrypt message with session key
m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len); m_Session.FillHeaderAndEncrypt (PAYLOAD_TYPE_DATA, buf, len);
m_Session.Send (buf, len); m_Session.Send (buf, len);

View File

@ -75,9 +75,11 @@ namespace transport
std::shared_ptr<I2NPMessage> msg; std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum; int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds uint32_t lastFragmentInsertTime; // in seconds
uint64_t receivedFragmentsBits;
std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments; std::set<std::unique_ptr<Fragment>, FragmentCmp> savedFragments;
IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0), lastFragmentInsertTime (0) {}; IncompleteMessage (std::shared_ptr<I2NPMessage> m): msg (m), nextFragmentNum (0),
lastFragmentInsertTime (0), receivedFragmentsBits (0) {};
void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize); void AttachNextFragment (const uint8_t * fragment, size_t fragmentSize);
}; };
@ -109,7 +111,7 @@ namespace transport
private: private:
void SendMsgAck (uint32_t msgID); void SendMsgAck (uint32_t msgID);
void SendFragmentAck (uint32_t msgID, int fragmentNum); void SendFragmentAck (uint32_t msgID, uint64_t bits);
void ProcessAcks (uint8_t *& buf, uint8_t flag); void ProcessAcks (uint8_t *& buf, uint8_t flag);
void ProcessFragments (uint8_t * buf); void ProcessFragments (uint8_t * buf);
void ProcessSentMessageAck (uint32_t msgID); void ProcessSentMessageAck (uint32_t msgID);

View File

@ -701,7 +701,7 @@ namespace stream
size++; // resend delay size++; // resend delay
htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED); htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
size += 2; // flags size += 2; // flags
size_t signatureLen = m_LocalDestination.GetOwner ()->GetIdentity ()->GetSignatureLen (); size_t signatureLen = m_LocalDestination.GetOwner ()->GetPrivateKeys ().GetSignatureLen ();
htobe16buf (packet + size, signatureLen); // signature only htobe16buf (packet + size, signatureLen); // signature only
size += 2; // options size size += 2; // options size
uint8_t * signature = packet + size; uint8_t * signature = packet + size;

View File

@ -18,6 +18,7 @@
#include "Log.h" #include "Log.h"
#include "I2PEndian.h" #include "I2PEndian.h"
#include "Timestamp.h" #include "Timestamp.h"
#include "util.h"
#ifdef _WIN32 #ifdef _WIN32
#ifndef _WIN64 #ifndef _WIN64
@ -148,6 +149,8 @@ namespace util
void NTPTimeSync::Run () void NTPTimeSync::Run ()
{ {
i2p::util::SetThreadName("Timesync");
while (m_IsRunning) while (m_IsRunning)
{ {
try try

View File

@ -14,6 +14,7 @@
#include "Transports.h" #include "Transports.h"
#include "Config.h" #include "Config.h"
#include "HTTP.h" #include "HTTP.h"
#include "util.h"
using namespace i2p::data; using namespace i2p::data;
@ -59,6 +60,8 @@ namespace transport
template<typename Keys> template<typename Keys>
void EphemeralKeysSupplier<Keys>::Run () void EphemeralKeysSupplier<Keys>::Run ()
{ {
i2p::util::SetThreadName("Ephemerals");
while (m_IsRunning) while (m_IsRunning)
{ {
int num, total = 0; int num, total = 0;
@ -272,6 +275,8 @@ namespace transport
void Transports::Run () void Transports::Run ()
{ {
i2p::util::SetThreadName("Transports");
while (m_IsRunning && m_Service) while (m_IsRunning && m_Service)
{ {
try try

View File

@ -22,6 +22,7 @@
#include "Config.h" #include "Config.h"
#include "Tunnel.h" #include "Tunnel.h"
#include "TunnelPool.h" #include "TunnelPool.h"
#include "util.h"
namespace i2p namespace i2p
{ {
@ -472,6 +473,7 @@ namespace tunnel
void Tunnels::Run () void Tunnels::Run ()
{ {
i2p::util::SetThreadName("Tunnels");
std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready std::this_thread::sleep_for (std::chrono::seconds(1)); // wait for other parts are ready
uint64_t lastTs = 0, lastPoolsTs = 0; uint64_t lastTs = 0, lastPoolsTs = 0;

View File

@ -127,10 +127,9 @@ namespace tunnel
void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor, void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx) const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx)
{ {
InitBuildRequestRecordNoiseState (*this);
uint8_t hepk[32]; uint8_t hepk[32];
encryptor->Encrypt (nullptr, hepk, nullptr, false); encryptor->Encrypt (nullptr, hepk, nullptr, false);
MixHash (hepk, 32); // h = SHA256(h || hepk) i2p::crypto::InitNoiseNState (*this, hepk);
auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair (); auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32); memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32);
MixHash (encrypted, 32); // h = SHA256(h || sepk) MixHash (encrypted, 32); // h = SHA256(h || sepk)
@ -148,17 +147,5 @@ namespace tunnel
} }
MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext) MixHash (encrypted, ECIES_BUILD_REQUEST_RECORD_CLEAR_TEXT_SIZE + 16); // h = SHA256(h || ciphertext)
} }
void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state)
{
static const char protocolName[] = "Noise_N_25519_ChaChaPoly_SHA256"; // 31 chars
static const uint8_t hh[32] =
{
0x69, 0x4d, 0x52, 0x44, 0x5a, 0x27, 0xd9, 0xad, 0xfa, 0xd2, 0x9c, 0x76, 0x32, 0x39, 0x5d, 0xc1,
0xe4, 0x35, 0x4c, 0x69, 0xb4, 0xf9, 0x2e, 0xac, 0x8a, 0x1e, 0xe4, 0x6a, 0x9e, 0xd2, 0x15, 0x54
}; // SHA256 (protocol_name || 0)
memcpy (state.m_CK, protocolName, 32); // ck = h = protocol_name || 0
memcpy (state.m_H, hh, 32); // h = SHA256(h)
}
} }
} }

View File

@ -45,8 +45,6 @@ namespace tunnel
const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx); const uint8_t * clearText, uint8_t * encrypted, BN_CTX * ctx);
}; };
void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state);
class TunnelConfig class TunnelConfig
{ {
public: public:

View File

@ -235,7 +235,7 @@ namespace tunnel
for (const auto& it : m_InboundTunnels) for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++; if (it->IsEstablished ()) num++;
} }
if (!num && !m_OutboundTunnels.empty ()) if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0)
{ {
for (auto it: m_OutboundTunnels) for (auto it: m_OutboundTunnels)
{ {
@ -559,7 +559,7 @@ namespace tunnel
{ {
config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ()); config = std::make_shared<TunnelConfig>(tunnel->GetPeers (), inboundTunnel->GetNextTunnelID (), inboundTunnel->GetNextIdentHash ());
} }
if(m_NumOutboundHops == 0 || config) if (!m_NumOutboundHops || config)
{ {
auto newTunnel = tunnels.CreateOutboundTunnel (config); auto newTunnel = tunnels.CreateOutboundTunnel (config);
newTunnel->SetTunnelPool (shared_from_this ()); newTunnel->SetTunnelPool (shared_from_this ());
@ -574,8 +574,12 @@ namespace tunnel
void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel) void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
{ {
LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel..."); LogPrint (eLogDebug, "Tunnels: Creating paired inbound tunnel...");
auto tunnel = tunnels.CreateInboundTunnel (std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()), outboundTunnel); auto tunnel = tunnels.CreateInboundTunnel (
m_NumOutboundHops > 0 ? std::make_shared<TunnelConfig>(outboundTunnel->GetInvertedPeers ()) : nullptr,
outboundTunnel);
tunnel->SetTunnelPool (shared_from_this ()); tunnel->SetTunnelPool (shared_from_this ());
if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
} }
void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector * selector) void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector * selector)

View File

@ -13,6 +13,15 @@
#include "util.h" #include "util.h"
#include "Log.h" #include "Log.h"
#if not defined (__FreeBSD__)
#include <pthread.h>
#endif
#if defined(__OpenBSD__) || defined(__FreeBSD__)
#include <pthread_np.h>
#endif
#ifdef _WIN32 #ifdef _WIN32
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -94,6 +103,8 @@ namespace util
void RunnableService::Run () void RunnableService::Run ()
{ {
SetThreadName(m_Name.c_str());
while (m_IsRunning) while (m_IsRunning)
{ {
try try
@ -107,6 +118,16 @@ namespace util
} }
} }
void SetThreadName (const char *name) {
#if defined (__APPLE__)
pthread_setname_np(name);
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
pthread_set_name_np(pthread_self(), name);
#else
pthread_setname_np(pthread_self(), name);
#endif
}
namespace net namespace net
{ {
#ifdef _WIN32 #ifdef _WIN32

View File

@ -168,6 +168,22 @@ namespace util
boost::asio::io_service::work m_Work; boost::asio::io_service::work m_Work;
}; };
void SetThreadName (const char *name);
template<typename T>
class SaveStateHelper
{
public:
SaveStateHelper (T& orig): m_Original (orig), m_Copy (orig) {};
~SaveStateHelper () { m_Original = m_Copy; };
private:
T& m_Original;
T m_Copy;
};
namespace net namespace net
{ {
int GetMTU (const boost::asio::ip::address& localAddress); int GetMTU (const boost::asio::ip::address& localAddress);

View File

@ -748,6 +748,8 @@ namespace client
void AddressBookSubscription::CheckUpdates () void AddressBookSubscription::CheckUpdates ()
{ {
i2p::util::SetThreadName("Addressbook");
bool result = MakeRequest (); bool result = MakeRequest ();
m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified); m_Book.DownloadComplete (result, m_Ident, m_Etag, m_LastModified);
} }

View File

@ -26,7 +26,8 @@ namespace client
I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner, I2CPDestination::I2CPDestination (boost::asio::io_service& service, std::shared_ptr<I2CPSession> owner,
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params): std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& params):
LeaseSetDestination (service, isPublic, &params), LeaseSetDestination (service, isPublic, &params),
m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()) m_Owner (owner), m_Identity (identity), m_EncryptionKeyType (m_Identity->GetCryptoKeyType ()),
m_IsCreatingLeaseSet (false), m_LeaseSetCreationTimer (service)
{ {
} }
@ -34,6 +35,7 @@ namespace client
{ {
LeaseSetDestination::Stop (); LeaseSetDestination::Stop ();
m_Owner = nullptr; m_Owner = nullptr;
m_LeaseSetCreationTimer.cancel ();
} }
void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key) void I2CPDestination::SetEncryptionPrivateKey (const uint8_t * key)
@ -84,6 +86,11 @@ namespace client
void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels) void I2CPDestination::CreateNewLeaseSet (std::vector<std::shared_ptr<i2p::tunnel::InboundTunnel> > tunnels)
{ {
if (m_IsCreatingLeaseSet)
{
LogPrint (eLogInfo, "I2CP: LeaseSet is being created");
return;
}
uint8_t priv[256] = {0}; uint8_t priv[256] = {0};
i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only
m_LeaseSetExpirationTime = ls.GetExpirationTime (); m_LeaseSetExpirationTime = ls.GetExpirationTime ();
@ -94,15 +101,28 @@ namespace client
uint16_t sessionID = m_Owner->GetSessionID (); uint16_t sessionID = m_Owner->GetSessionID ();
if (sessionID != 0xFFFF) if (sessionID != 0xFFFF)
{ {
m_IsCreatingLeaseSet = true;
htobe16buf (leases - 3, sessionID); htobe16buf (leases - 3, sessionID);
size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size (); size_t l = 2/*sessionID*/ + 1/*num leases*/ + i2p::data::LEASE_SIZE*tunnels.size ();
m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l); m_Owner->SendI2CPMessage (I2CP_REQUEST_VARIABLE_LEASESET_MESSAGE, leases - 3, l);
m_LeaseSetCreationTimer.expires_from_now (boost::posix_time::seconds (I2CP_LEASESET_CREATION_TIMEOUT));
auto s = GetSharedFromThis ();
m_LeaseSetCreationTimer.async_wait ([s](const boost::system::error_code& ecode)
{
if (ecode != boost::asio::error::operation_aborted)
{
LogPrint (eLogInfo, "I2CP: LeaseSet creation timeout expired. Terminate");
if (s->m_Owner) s->m_Owner->Stop ();
}
});
} }
} }
} }
void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len) void I2CPDestination::LeaseSetCreated (const uint8_t * buf, size_t len)
{ {
m_IsCreatingLeaseSet = false;
m_LeaseSetCreationTimer.cancel ();
auto ls = std::make_shared<i2p::data::LocalLeaseSet> (m_Identity, buf, len); auto ls = std::make_shared<i2p::data::LocalLeaseSet> (m_Identity, buf, len);
ls->SetExpirationTime (m_LeaseSetExpirationTime); ls->SetExpirationTime (m_LeaseSetExpirationTime);
SetLeaseSet (ls); SetLeaseSet (ls);
@ -110,6 +130,8 @@ namespace client
void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len) void I2CPDestination::LeaseSet2Created (uint8_t storeType, const uint8_t * buf, size_t len)
{ {
m_IsCreatingLeaseSet = false;
m_LeaseSetCreationTimer.cancel ();
auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ? auto ls = (storeType == i2p::data::NETDB_STORE_TYPE_ENCRYPTED_LEASESET2) ?
std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (m_Identity, buf, len): std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (m_Identity, buf, len):
std::make_shared<i2p::data::LocalLeaseSet2> (storeType, m_Identity, buf, len); std::make_shared<i2p::data::LocalLeaseSet2> (storeType, m_Identity, buf, len);

View File

@ -27,6 +27,7 @@ namespace client
const size_t I2CP_SESSION_BUFFER_SIZE = 4096; const size_t I2CP_SESSION_BUFFER_SIZE = 4096;
const size_t I2CP_MAX_MESSAGE_LENGTH = 65535; const size_t I2CP_MAX_MESSAGE_LENGTH = 65535;
const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M const size_t I2CP_MAX_SEND_QUEUE_SIZE = 1024*1024; // in bytes, 1M
const int I2CP_LEASESET_CREATION_TIMEOUT = 10; // in seconds
const size_t I2CP_HEADER_LENGTH_OFFSET = 0; const size_t I2CP_HEADER_LENGTH_OFFSET = 0;
const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4; const size_t I2CP_HEADER_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;
@ -109,6 +110,8 @@ namespace client
std::shared_ptr<i2p::crypto::ECIESX25519AEADRatchetDecryptor> m_ECIESx25519Decryptor; std::shared_ptr<i2p::crypto::ECIESX25519AEADRatchetDecryptor> m_ECIESx25519Decryptor;
uint8_t m_ECIESx25519PrivateKey[32]; uint8_t m_ECIESx25519PrivateKey[32];
uint64_t m_LeaseSetExpirationTime; uint64_t m_LeaseSetExpirationTime;
bool m_IsCreatingLeaseSet;
boost::asio::deadline_timer m_LeaseSetCreationTimer;
}; };
class RunnableI2CPDestination: private i2p::util::RunnableService, public I2CPDestination class RunnableI2CPDestination: private i2p::util::RunnableService, public I2CPDestination

View File

@ -12,6 +12,7 @@
#include "Destination.h" #include "Destination.h"
#include "ClientContext.h" #include "ClientContext.h"
#include "I2PTunnel.h" #include "I2PTunnel.h"
#include "util.h"
namespace i2p namespace i2p
{ {
@ -940,6 +941,7 @@ namespace client
} }
void I2PUDPClientTunnel::TryResolving() { void I2PUDPClientTunnel::TryResolving() {
i2p::util::SetThreadName("UDP Resolver");
LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest); LogPrint(eLogInfo, "UDP Tunnel: Trying to resolve ", m_RemoteDest);
std::shared_ptr<const Address> addr; std::shared_ptr<const Address> addr;

View File

@ -708,16 +708,16 @@ namespace client
auto session = m_Owner.FindSession(m_ID); auto session = m_Owner.FindSession(m_ID);
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination; auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination;
if (name == "ME") if (name == "ME")
SendNamingLookupReply (dest->GetIdentity ()); SendNamingLookupReply (name, dest->GetIdentity ());
else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr) else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr)
SendNamingLookupReply (identity); SendNamingLookupReply (name, identity);
else if ((addr = context.GetAddressBook ().GetAddress (name))) else if ((addr = context.GetAddressBook ().GetAddress (name)))
{ {
if (addr->IsIdentHash ()) if (addr->IsIdentHash ())
{ {
auto leaseSet = dest->FindLeaseSet (addr->identHash); auto leaseSet = dest->FindLeaseSet (addr->identHash);
if (leaseSet) if (leaseSet)
SendNamingLookupReply (leaseSet->GetIdentity ()); SendNamingLookupReply (name, leaseSet->GetIdentity ());
else else
dest->RequestDestination (addr->identHash, dest->RequestDestination (addr->identHash,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete, std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
@ -756,7 +756,7 @@ namespace client
if (leaseSet) if (leaseSet)
{ {
context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ()); context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ());
SendNamingLookupReply (leaseSet->GetIdentity ()); SendNamingLookupReply (name, leaseSet->GetIdentity ());
} }
else else
{ {
@ -770,13 +770,13 @@ namespace client
} }
} }
void SAMSocket::SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity) void SAMSocket::SendNamingLookupReply (const std::string& name, std::shared_ptr<const i2p::data::IdentityEx> identity)
{ {
auto base64 = identity->ToBase64 (); auto base64 = identity->ToBase64 ();
#ifdef _MSC_VER #ifdef _MSC_VER
size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); size_t l = sprintf_s (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ());
#else #else
size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, base64.c_str ()); size_t l = snprintf (m_Buffer, SAM_SOCKET_BUFFER_SIZE, SAM_NAMING_REPLY, name.c_str (), base64.c_str ());
#endif #endif
SendMessageReply (m_Buffer, l, false); SendMessageReply (m_Buffer, l, false);
} }

View File

@ -55,7 +55,7 @@ namespace client
const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n"; const char SAM_DEST_REPLY[] = "DEST REPLY PUB=%s PRIV=%s\n";
const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n"; const char SAM_DEST_REPLY_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP"; const char SAM_NAMING_LOOKUP[] = "NAMING LOOKUP";
const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=ME VALUE=%s\n"; const char SAM_NAMING_REPLY[] = "NAMING REPLY RESULT=OK NAME=%s VALUE=%s\n";
const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n"; const char SAM_DATAGRAM_RECEIVED[] = "DATAGRAM RECEIVED DESTINATION=%s SIZE=%lu\n";
const char SAM_RAW_RECEIVED[] = "RAW RECEIVED SIZE=%lu\n"; const char SAM_RAW_RECEIVED[] = "RAW RECEIVED SIZE=%lu\n";
const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n"; const char SAM_NAMING_REPLY_INVALID_KEY[] = "NAMING REPLY RESULT=INVALID_KEY NAME=%s\n";
@ -140,7 +140,7 @@ namespace client
void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote, std::shared_ptr<SAMSession> session = nullptr); void Connect (std::shared_ptr<const i2p::data::LeaseSet> remote, std::shared_ptr<SAMSession> session = nullptr);
void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet); void HandleConnectLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet);
void SendNamingLookupReply (std::shared_ptr<const i2p::data::IdentityEx> identity); void SendNamingLookupReply (const std::string& name, std::shared_ptr<const i2p::data::IdentityEx> identity);
void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name); void HandleNamingLookupLeaseSetRequestComplete (std::shared_ptr<i2p::data::LeaseSet> leaseSet, std::string name);
void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode); void HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
void SendSessionCreateReplyOk (); void SendSessionCreateReplyOk ();

View File

@ -0,0 +1,21 @@
#include "AboutDialog.h"
#include "ui_AboutDialog.h"
#include <QDebug>
#include "version.h"
#include "BuildDateTimeQt.h"
AboutDialog::AboutDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AboutDialog)
{
ui->setupUi(this);
ui->i2pdVersionLabel->setText(I2PD_VERSION);
ui->i2pVersionLabel->setText(I2P_VERSION);
ui->buildDateTimeLabel->setText(BUILD_DATE_TIME_QT);
ui->vcsCommitInfoLabel->setText(VCS_COMMIT_INFO);
}
AboutDialog::~AboutDialog()
{
delete ui;
}

22
qt/i2pd_qt/AboutDialog.h Normal file
View File

@ -0,0 +1,22 @@
#ifndef ABOUTDIALOG_H
#define ABOUTDIALOG_H
#include <QDialog>
namespace Ui {
class AboutDialog;
}
class AboutDialog : public QDialog
{
Q_OBJECT
public:
explicit AboutDialog(QWidget *parent = 0);
~AboutDialog();
private:
Ui::AboutDialog *ui;
};
#endif // ABOUTDIALOG_H

194
qt/i2pd_qt/AboutDialog.ui Normal file
View File

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AboutDialog</class>
<widget class="QDialog" name="AboutDialog">
<property name="windowModality">
<enum>Qt::WindowModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>199</height>
</rect>
</property>
<property name="windowTitle">
<string>About i2pd_qt</string>
</property>
<property name="windowIcon">
<iconset resource="i2pd.qrc">
<normaloff>:/icons/mask</normaloff>:/icons/mask</iconset>
</property>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
<x>10</x>
<y>160</y>
<width>381</width>
<height>32</height>
</rect>
</property>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>381</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600;&quot;&gt;About i2pd_qt&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
<widget class="QLabel" name="label_2">
<property name="geometry">
<rect>
<x>10</x>
<y>40</y>
<width>131</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>i2pd Version:</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
<property name="geometry">
<rect>
<x>10</x>
<y>70</y>
<width>131</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Build Date/Time:</string>
</property>
</widget>
<widget class="QLabel" name="i2pdVersionLabel">
<property name="geometry">
<rect>
<x>150</x>
<y>40</y>
<width>241</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>I2PD_VERSION_LABEL</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>10</x>
<y>130</y>
<width>131</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>I2P Version:</string>
</property>
</widget>
<widget class="QLabel" name="i2pVersionLabel">
<property name="geometry">
<rect>
<x>150</x>
<y>130</y>
<width>241</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>I2P_VERSION_LABEL</string>
</property>
</widget>
<widget class="QLabel" name="buildDateTimeLabel">
<property name="geometry">
<rect>
<x>150</x>
<y>70</y>
<width>241</width>
<height>20</height>
</rect>
</property>
<property name="text">
<string>BUILD_DATE_TIME_LABEL</string>
</property>
</widget>
<widget class="QLabel" name="label_5">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>131</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>Version Control:</string>
</property>
</widget>
<widget class="QLabel" name="vcsCommitInfoLabel">
<property name="geometry">
<rect>
<x>150</x>
<y>100</y>
<width>241</width>
<height>17</height>
</rect>
</property>
<property name="text">
<string>VCS_COMMIT_INFO_LABEL</string>
</property>
</widget>
</widget>
<resources>
<include location="i2pd.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,7 @@
#ifndef BUILDDATETIMEQT_H
#define BUILDDATETIMEQT_H
#include <QString>
const QString BUILD_DATE_TIME_QT = QStringLiteral(__DATE__ " " __TIME__);
#endif // BUILDDATETIMEQT_H

View File

@ -79,6 +79,13 @@ protected:
ClientTunnelConfig* ctc=tunnelConfig->asClientTunnelConfig(); ClientTunnelConfig* ctc=tunnelConfig->asClientTunnelConfig();
assert(ctc!=nullptr); assert(ctc!=nullptr);
if(!isValidSingleLine(destinationLineEdit))return false;
if(!isValidSingleLine(portLineEdit))return false;
if(!isValidSingleLine(cryptoTypeLineEdit))return false;
if(!isValidSingleLine(keysLineEdit))return false;
if(!isValidSingleLine(addressLineEdit))return false;
if(!isValidSingleLine(destinationPortLineEdit))return false;
//destination //destination
ctc->setdest(destinationLineEdit->text().toStdString()); ctc->setdest(destinationLineEdit->text().toStdString());

View File

@ -2,6 +2,7 @@
#define DELAYEDSAVEMANAGER_H #define DELAYEDSAVEMANAGER_H
#include "Saver.h" #include "Saver.h"
#include "I2pdQtTypes.h"
class DelayedSaveManager class DelayedSaveManager
{ {
@ -12,13 +13,14 @@ public:
typedef unsigned int DATA_SERIAL_TYPE; typedef unsigned int DATA_SERIAL_TYPE;
virtual void delayedSave(DATA_SERIAL_TYPE dataSerial, bool needsTunnelFocus, std::string tunnelNameToFocus)=0; virtual void delayedSave(bool reloadAfterSave, DATA_SERIAL_TYPE dataSerial, FocusEnum focusOn, std::string tunnelNameToFocus, QWidget* widgetToFocus)=0;
//returns false iff save failed //returns false iff save failed
virtual bool appExiting()=0; virtual bool appExiting()=0;
virtual bool needsFocusOnTunnel()=0; virtual FocusEnum getFocusOn()=0;
virtual std::string& getTunnelNameToFocus()=0; virtual std::string& getTunnelNameToFocus()=0;
virtual QWidget* getWidgetToFocus()=0;
}; };
#endif // DELAYEDSAVEMANAGER_H #endif // DELAYEDSAVEMANAGER_H

View File

@ -1,6 +1,9 @@
#include "DelayedSaveManagerImpl.h" #include "DelayedSaveManagerImpl.h"
#include <assert.h>
DelayedSaveManagerImpl::DelayedSaveManagerImpl() : DelayedSaveManagerImpl::DelayedSaveManagerImpl() :
widgetToFocus(nullptr),
saver(nullptr), saver(nullptr),
lastDataSerialSeen(DelayedSaveManagerImpl::INITIAL_DATA_SERIAL), lastDataSerialSeen(DelayedSaveManagerImpl::INITIAL_DATA_SERIAL),
lastSaveStartedTimestamp(A_VERY_OBSOLETE_TIMESTAMP), lastSaveStartedTimestamp(A_VERY_OBSOLETE_TIMESTAMP),
@ -21,10 +24,12 @@ bool DelayedSaveManagerImpl::isSaverValid() {
return saver != nullptr; return saver != nullptr;
} }
void DelayedSaveManagerImpl::delayedSave(DATA_SERIAL_TYPE dataSerial, bool focusOnTunnel, std::string tunnelNameToFocus_) { void DelayedSaveManagerImpl::delayedSave(bool reloadAfterSave, DATA_SERIAL_TYPE dataSerial, FocusEnum focusOn, std::string tunnelNameToFocus, QWidget* widgetToFocus) {
if(lastDataSerialSeen==dataSerial)return; if(lastDataSerialSeen==dataSerial)return;
this->focusOnTunnel = focusOnTunnel; this->reloadAfterSave = reloadAfterSave;
tunnelNameToFocus = tunnelNameToFocus_; this->focusOn = focusOn;
this->tunnelNameToFocus = tunnelNameToFocus;
this->widgetToFocus = widgetToFocus;
lastDataSerialSeen=dataSerial; lastDataSerialSeen=dataSerial;
assert(isSaverValid()); assert(isSaverValid());
TIMESTAMP_TYPE now = getTime(); TIMESTAMP_TYPE now = getTime();
@ -42,7 +47,7 @@ bool DelayedSaveManagerImpl::appExiting() {
exiting=true; exiting=true;
thread->wakeThreadAndJoinThread(); thread->wakeThreadAndJoinThread();
assert(isSaverValid()); assert(isSaverValid());
saver->save(false, ""); saver->save(false, FocusEnum::noFocus);
return true; return true;
} }
@ -71,9 +76,10 @@ void DelayedSaveThread::run() {
assert(saver!=nullptr); assert(saver!=nullptr);
if(saveNow) { if(saveNow) {
saveNow = false; saveNow = false;
const bool focusOnTunnel = delayedSaveManagerImpl->needsFocusOnTunnel(); const FocusEnum focusOn = delayedSaveManagerImpl->getFocusOn();
const std::string tunnelNameToFocus = delayedSaveManagerImpl->getTunnelNameToFocus(); const std::string tunnelNameToFocus = delayedSaveManagerImpl->getTunnelNameToFocus();
saver->save(focusOnTunnel, tunnelNameToFocus); QWidget* widgetToFocus = delayedSaveManagerImpl->getWidgetToFocus();
saver->save(delayedSaveManagerImpl->isReloadAfterSave(), focusOn, tunnelNameToFocus, widgetToFocus);
continue; continue;
} }
if(defer) { if(defer) {
@ -87,9 +93,10 @@ void DelayedSaveThread::run() {
if(delayedSaveManagerImpl->isExiting())return; if(delayedSaveManagerImpl->isExiting())return;
continue; continue;
} }
const bool focusOnTunnel = delayedSaveManagerImpl->needsFocusOnTunnel(); const FocusEnum focusOn = delayedSaveManagerImpl->getFocusOn();
const std::string tunnelNameToFocus = delayedSaveManagerImpl->getTunnelNameToFocus(); const std::string tunnelNameToFocus = delayedSaveManagerImpl->getTunnelNameToFocus();
saver->save(focusOnTunnel, tunnelNameToFocus); QWidget* widgetToFocus = delayedSaveManagerImpl->getWidgetToFocus();
saver->save(delayedSaveManagerImpl->isReloadAfterSave(), focusOn, tunnelNameToFocus, widgetToFocus);
break; //break inner loop break; //break inner loop
} }
} }
@ -131,10 +138,3 @@ Saver* DelayedSaveManagerImpl::getSaver() {
return saver; return saver;
} }
bool DelayedSaveManagerImpl::needsFocusOnTunnel() {
return focusOnTunnel;
}
std::string& DelayedSaveManagerImpl::getTunnelNameToFocus() {
return tunnelNameToFocus;
}

View File

@ -7,14 +7,14 @@
#include <QMutex> #include <QMutex>
#include <QDateTime> #include <QDateTime>
#include "mainwindow.h" #include "I2pdQtTypes.h"
#include "DelayedSaveManager.h" #include "DelayedSaveManager.h"
#include "Saver.h" #include "Saver.h"
class DelayedSaveManagerImpl; class DelayedSaveManagerImpl;
class Saver;
class DelayedSaveThread : public QThread class DelayedSaveThread : public QThread {
{
Q_OBJECT Q_OBJECT
public: public:
@ -42,14 +42,17 @@ private:
volatile TIMESTAMP_TYPE wakeTime; volatile TIMESTAMP_TYPE wakeTime;
}; };
class DelayedSaveManagerImpl : public DelayedSaveManager class DelayedSaveManagerImpl : public DelayedSaveManager {
{ FocusEnum focusOn;
std::string tunnelNameToFocus;
QWidget* widgetToFocus;
bool reloadAfterSave;
public: public:
DelayedSaveManagerImpl(); DelayedSaveManagerImpl();
virtual ~DelayedSaveManagerImpl(); virtual ~DelayedSaveManagerImpl();
virtual void setSaver(Saver* saver); virtual void setSaver(Saver* saver);
virtual void start(); virtual void start();
virtual void delayedSave(DATA_SERIAL_TYPE dataSerial, bool focusOnTunnel, std::string tunnelNameToFocus); virtual void delayedSave(bool reloadAfterSave, DATA_SERIAL_TYPE dataSerial, FocusEnum focusOn, std::string tunnelNameToFocus, QWidget* widgetToFocus);
virtual bool appExiting(); virtual bool appExiting();
typedef DelayedSaveThread::TIMESTAMP_TYPE TIMESTAMP_TYPE; typedef DelayedSaveThread::TIMESTAMP_TYPE TIMESTAMP_TYPE;
@ -59,8 +62,10 @@ public:
Saver* getSaver(); Saver* getSaver();
static TIMESTAMP_TYPE getTime(); static TIMESTAMP_TYPE getTime();
bool needsFocusOnTunnel(); bool isReloadAfterSave() { return reloadAfterSave; }
std::string& getTunnelNameToFocus(); FocusEnum getFocusOn() { return focusOn; }
std::string& getTunnelNameToFocus() { return tunnelNameToFocus; }
QWidget* getWidgetToFocus() { return widgetToFocus; }
private: private:
Saver* saver; Saver* saver;
@ -74,9 +79,6 @@ private:
bool exiting; bool exiting;
DelayedSaveThread* thread; DelayedSaveThread* thread;
void wakeThreadAndJoinThread(); void wakeThreadAndJoinThread();
bool focusOnTunnel;
std::string tunnelNameToFocus;
}; };
#endif // DELAYEDSAVEMANAGERIMPL_H #endif // DELAYEDSAVEMANAGERIMPL_H

7
qt/i2pd_qt/I2pdQtTypes.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef I2PDQTTYPES_H
#define I2PDQTTYPES_H
enum WrongInputPageEnum { generalSettingsPage, tunnelsSettingsPage };
enum FocusEnum { noFocus, focusOnTunnelName, focusOnWidget };
#endif // I2PDQTTYPES_H

12
qt/i2pd_qt/I2pdQtUtil.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "I2pdQtUtil.h"
bool isValidSingleLine(QLineEdit* widget, WrongInputPageEnum inputPage, MainWindow* mainWindow) {
bool correct = !widget->text().contains(QRegularExpression("[\r\n]"), nullptr);
if(!correct) {
mainWindow->highlightWrongInput(
QApplication::tr("Single line input expected, but it's multiline"),
inputPage,
widget);
}
return correct;
}

9
qt/i2pd_qt/I2pdQtUtil.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef I2pdQtUtil_H
#define I2pdQtUtil_H
class QLineEdit;
#include "mainwindow.h"
bool isValidSingleLine(QLineEdit* widget, WrongInputPageEnum inputPage, MainWindow* mainWindow);
#endif // I2pdQtUtil_H

View File

@ -4,6 +4,9 @@
#include <string> #include <string>
#include <QObject> #include <QObject>
#include <QString> #include <QString>
class QWidget;
#include "I2pdQtTypes.h"
class Saver : public QObject class Saver : public QObject
{ {
@ -11,8 +14,8 @@ class Saver : public QObject
public: public:
Saver(); Saver();
//false iff failures //FocusEnum::focusNone iff failures //??? wtf
virtual bool save(const bool focusOnTunnel, const std::string& tunnelNameToFocus)=0; virtual bool save(bool reloadAfterSave, const FocusEnum focusOn, const std::string& tunnelNameToFocus="", QWidget* widgetToFocus=nullptr)=0;
signals: signals:
void reloadTunnelsConfigAndUISignal(const QString); void reloadTunnelsConfigAndUISignal(const QString);

View File

@ -15,7 +15,7 @@ SaverImpl::SaverImpl(MainWindow *mainWindowPtr_, QList<MainWindowItem*> * config
SaverImpl::~SaverImpl() {} SaverImpl::~SaverImpl() {}
bool SaverImpl::save(const bool focusOnTunnel, const std::string& tunnelNameToFocus) { bool SaverImpl::save(bool reloadAfterSave, const FocusEnum focusOn, const std::string& tunnelNameToFocus, QWidget* widgetToFocus) {
//save main config //save main config
{ {
std::stringstream out; std::stringstream out;
@ -59,12 +59,14 @@ bool SaverImpl::save(const bool focusOnTunnel, const std::string& tunnelNameToFo
outfile.close(); outfile.close();
} }
if(reloadAfterSave) {
//reload saved configs //reload saved configs
#if 0 #if 0
i2p::client::context.ReloadConfig(); i2p::client::context.ReloadConfig();
#endif #endif
if(focusOnTunnel) emit reloadTunnelsConfigAndUISignal(QString::fromStdString(tunnelNameToFocus)); if(reloadAfterSave) emit reloadTunnelsConfigAndUISignal(focusOn==FocusEnum::focusOnTunnelName?QString::fromStdString(tunnelNameToFocus):"");
}
return true; return true;
} }

View File

@ -19,7 +19,7 @@ class SaverImpl : public Saver
public: public:
SaverImpl(MainWindow *mainWindowPtr_, QList<MainWindowItem*> * configItems_, std::map<std::string,TunnelConfig*>* tunnelConfigs_); SaverImpl(MainWindow *mainWindowPtr_, QList<MainWindowItem*> * configItems_, std::map<std::string,TunnelConfig*>* tunnelConfigs_);
virtual ~SaverImpl(); virtual ~SaverImpl();
virtual bool save(const bool focusOnTunnel, const std::string& tunnelNameToFocus); virtual bool save(bool reloadAfterSave, const FocusEnum focusOn, const std::string& tunnelNameToFocus, QWidget* widgetToFocus);
void setConfPath(QString& confpath_); void setConfPath(QString& confpath_);
void setTunnelsConfPath(QString& tunconfpath_); void setTunnelsConfPath(QString& tunconfpath_);
private: private:

View File

@ -124,6 +124,17 @@ protected:
if(!ok)return false; if(!ok)return false;
ServerTunnelConfig* stc=tunnelConfig->asServerTunnelConfig(); ServerTunnelConfig* stc=tunnelConfig->asServerTunnelConfig();
assert(stc!=nullptr); assert(stc!=nullptr);
if(!isValidSingleLine(hostLineEdit))return false;
if(!isValidSingleLine(portLineEdit))return false;
if(!isValidSingleLine(cryptoTypeLineEdit))return false;
if(!isValidSingleLine(keysLineEdit))return false;
if(!isValidSingleLine(inPortLineEdit))return false;
if(!isValidSingleLine(accessListLineEdit))return false;
if(!isValidSingleLine(hostOverrideLineEdit))return false;
if(!isValidSingleLine(webIRCPassLineEdit))return false;
if(!isValidSingleLine(addressLineEdit))return false;
stc->sethost(hostLineEdit->text().toStdString()); stc->sethost(hostLineEdit->text().toStdString());
auto portStr=portLineEdit->text(); auto portStr=portLineEdit->text();

View File

@ -4,6 +4,8 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "I2pdQtUtil.h"
TunnelPane::TunnelPane(TunnelsPageUpdateListener* tunnelsPageUpdateListener_, TunnelConfig* tunnelConfig_, QWidget* wrongInputPane_, QLabel* wrongInputLabel_, MainWindow* mainWindow_): TunnelPane::TunnelPane(TunnelsPageUpdateListener* tunnelsPageUpdateListener_, TunnelConfig* tunnelConfig_, QWidget* wrongInputPane_, QLabel* wrongInputLabel_, MainWindow* mainWindow_):
QObject(), QObject(),
mainWindow(mainWindow_), mainWindow(mainWindow_),
@ -47,8 +49,6 @@ void TunnelPane::setupTunnelPane(
nameLineEdit->setText(tunnelName); nameLineEdit->setText(tunnelName);
setGroupBoxTitle(tunnelName); setGroupBoxTitle(tunnelName);
QObject::connect(nameLineEdit, SIGNAL(textChanged(const QString &)),
this, SLOT(setGroupBoxTitle(const QString &)));
QObject::connect(nameLineEdit, SIGNAL(textChanged(const QString &)), QObject::connect(nameLineEdit, SIGNAL(textChanged(const QString &)),
this, SLOT(updated())); this, SLOT(updated()));
@ -399,3 +399,7 @@ void TunnelPane::hideWrongInputLabel() const {
wrongInputPane->setVisible(false); wrongInputPane->setVisible(false);
mainWindow->adjustSizesAccordingToWrongLabel(); mainWindow->adjustSizesAccordingToWrongLabel();
} }
bool TunnelPane::isValidSingleLine(QLineEdit* widget) {
return ::isValidSingleLine(widget, WrongInputPageEnum::tunnelsSettingsPage, mainWindow);
}

View File

@ -119,6 +119,21 @@ protected:
public: public:
//returns false when invalid data at UI //returns false when invalid data at UI
virtual bool applyDataFromUIToTunnelConfig() { virtual bool applyDataFromUIToTunnelConfig() {
if(!isValidSingleLine(nameLineEdit)){
setGroupBoxTitle(QApplication::translate("tunPage", "invalid_tunnel_name"));
return false;
}
if(!isValidSingleLine(inbound_lengthLineEdit))return false;
if(!isValidSingleLine(inbound_quantityLineEdit))return false;
if(!isValidSingleLine(outbound_lengthLineEdit))return false;
if(!isValidSingleLine(outbound_quantityLineEdit))return false;
if(!isValidSingleLine(crypto_tagsToSendLineEdit))return false;
if(!isValidSingleLine(i2cp_leaseSetAuthTypeLineEdit))return false;
if(!isValidSingleLine(i2cp_leaseSetEncTypeLineEdit))return false;
if(!isValidSingleLine(i2cp_leaseSetPrivKeyLineEdit))return false;
if(!isValidSingleLine(i2cp_leaseSetTypeLineEdit))return false;
if(!isValidSingleLine(i2p_streaming_initialAckDelayLineEdit))return false;
setGroupBoxTitle(nameLineEdit->text());
tunnelConfig->setName(nameLineEdit->text().toStdString()); tunnelConfig->setName(nameLineEdit->text().toStdString());
tunnelConfig->setType(readTunnelTypeComboboxData()); tunnelConfig->setType(readTunnelTypeComboboxData());
I2CPParameters& i2cpParams=tunnelConfig->getI2cpParameters(); I2CPParameters& i2cpParams=tunnelConfig->getI2cpParameters();
@ -169,6 +184,8 @@ private:
i2cp_leaseSetPrivKeyLabel->setText(QApplication::translate("tunForm", "Decryption key for encrypted LeaseSet in base64. PSK or private DH:", 0)); i2cp_leaseSetPrivKeyLabel->setText(QApplication::translate("tunForm", "Decryption key for encrypted LeaseSet in base64. PSK or private DH:", 0));
i2cp_leaseSetAuthTypeLabel->setText(QApplication::translate("tunForm", "Auth type for encrypted LeaseSet. 0 - no auth, 1 - DH, 2 - PSK:", 0)); i2cp_leaseSetAuthTypeLabel->setText(QApplication::translate("tunForm", "Auth type for encrypted LeaseSet. 0 - no auth, 1 - DH, 2 - PSK:", 0));
} }
protected:
bool isValidSingleLine(QLineEdit* widget);
}; };
#endif // TUNNELPANE_H #endif // TUNNELPANE_H

2
qt/i2pd_qt/data/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.tmp.xml

View File

@ -495,6 +495,11 @@ QGroupBox::title {
</item> </item>
<item> <item>
<widget class="QComboBox" name="logLevelComboBox"> <widget class="QComboBox" name="logLevelComboBox">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item> <item>
<property name="text"> <property name="text">
<string>Error</string> <string>Error</string>

View File

@ -9,10 +9,15 @@ CONFIG += strict_c++ c++11
CONFIG(debug, debug|release) { CONFIG(debug, debug|release) {
message(Debug build) message(Debug build)
# do not redirect logging to std::ostream and to Log pane
DEFINES += DEBUG_WITH_DEFAULT_LOGGING DEFINES += DEBUG_WITH_DEFAULT_LOGGING
DEFINES += I2PD_QT_DEBUG
I2PDMAKE += DEBUG=yes I2PDMAKE += DEBUG=yes
} else { } else {
message(Release build) message(Release build)
DEFINES += I2PD_QT_RELEASE
I2PDMAKE += DEBUG=no I2PDMAKE += DEBUG=no
} }
@ -36,7 +41,9 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../daemon/HTTPServer.cpp \ ../../daemon/HTTPServer.cpp \
../../daemon/I2PControl.cpp \ ../../daemon/I2PControl.cpp \
../../daemon/i2pd.cpp \ ../../daemon/i2pd.cpp \
../../daemon/UPnP.cpp ../../daemon/UPnP.cpp \
AboutDialog.cpp \
I2pdQtUtil.cpp
HEADERS += DaemonQT.h mainwindow.h \ HEADERS += DaemonQT.h mainwindow.h \
ClientTunnelPane.h \ ClientTunnelPane.h \
@ -59,8 +66,11 @@ HEADERS += DaemonQT.h mainwindow.h \
../../daemon/Daemon.h \ ../../daemon/Daemon.h \
../../daemon/HTTPServer.h \ ../../daemon/HTTPServer.h \
../../daemon/I2PControl.h \ ../../daemon/I2PControl.h \
../../daemon/UPnP.h ../../daemon/UPnP.h \
AboutDialog.h \
BuildDateTimeQt.h \
I2pdQtUtil.h \
I2pdQtTypes.h
INCLUDEPATH += ../../libi2pd INCLUDEPATH += ../../libi2pd
INCLUDEPATH += ../../libi2pd_client INCLUDEPATH += ../../libi2pd_client
@ -71,7 +81,8 @@ FORMS += mainwindow.ui \
tunnelform.ui \ tunnelform.ui \
statusbuttons.ui \ statusbuttons.ui \
routercommandswidget.ui \ routercommandswidget.ui \
generalsettingswidget.ui generalsettingswidget.ui \
AboutDialog.ui
LIBS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a -lz LIBS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a -lz
@ -79,13 +90,23 @@ libi2pd.commands = @echo Building i2pd libraries
libi2pd.target = $$PWD/../../libi2pd.a libi2pd.target = $$PWD/../../libi2pd.a
libi2pd.depends = i2pd FORCE libi2pd.depends = i2pd FORCE
i2pd.commands = cd $$PWD/../../ && mkdir -p obj/libi2pd_client && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) USE_UPNP=yes $$I2PDMAKE api_client i2pd.commands = cd $$PWD/../../ && mkdir -p obj/libi2pd obj/libi2pd_client && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) USE_UPNP=yes $$I2PDMAKE mk_obj_dir api_client
i2pd.target += $$PWD/../../libi2pdclient.a i2pd.target += $$PWD/../../libi2pdclient.a
i2pd.depends = FORCE i2pd.depends = FORCE
cleani2pd.commands = cd $$PWD/../../ && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) clean cleani2pd.commands = cd $$PWD/../../ && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) clean
cleani2pd.depends = clean cleani2pd.depends = clean
BuildDateTimeQtTarget.target = BuildDateTimeQt.h
BuildDateTimeQtTarget.depends = FORCE
# 'touch' is unix-only; will probably break on non-unix, TBD
BuildDateTimeQtTarget.commands = touch $$PWD/BuildDateTimeQt.h
PRE_TARGETDEPS += BuildDateTimeQt.h
QMAKE_EXTRA_TARGETS += BuildDateTimeQtTarget
# git only, port to other VCS, too. TBD
DEFINES += VCS_COMMIT_INFO="\\\"git:$(shell git -C \""$$_PRO_FILE_PWD_"\" describe)\\\""
PRE_TARGETDEPS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a PRE_TARGETDEPS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a
QMAKE_EXTRA_TARGETS += cleani2pd i2pd libi2pd QMAKE_EXTRA_TARGETS += cleani2pd i2pd libi2pd
CLEAN_DEPS += cleani2pd CLEAN_DEPS += cleani2pd

View File

@ -1,4 +1,6 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "I2pdQtUtil.h"
#include "AboutDialog.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "ui_statusbuttons.h" #include "ui_statusbuttons.h"
#include "ui_routercommandswidget.h" #include "ui_routercommandswidget.h"
@ -8,6 +10,15 @@
#include <QTimer> #include <QTimer>
#include <QFile> #include <QFile>
#include <QFileDialog> #include <QFileDialog>
#include <QApplication>
#include <QScreen>
#include <QStyleHints>
#include <QScreen>
#include <QWindow>
#include <assert.h>
#include "RouterContext.h" #include "RouterContext.h"
#include "Config.h" #include "Config.h"
#include "FS.h" #include "FS.h"
@ -29,10 +40,12 @@
#include "DelayedSaveManagerImpl.h" #include "DelayedSaveManagerImpl.h"
#include "SaverImpl.h" #include "SaverImpl.h"
std::string programOptionsWriterCurrentSection; std::string programOptionsWriterCurrentSection;
MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *parent) : MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *parent) :
QMainWindow(parent) QMainWindow(parent)
,currentLocalDestinationB32("")
,logStream(logStream_) ,logStream(logStream_)
,delayedSaveManagerPtr(new DelayedSaveManagerImpl()) ,delayedSaveManagerPtr(new DelayedSaveManagerImpl())
,dataSerial(DelayedSaveManagerImpl::INITIAL_DATA_SERIAL) ,dataSerial(DelayedSaveManagerImpl::INITIAL_DATA_SERIAL)
@ -65,6 +78,10 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
statusButtonsUI->setupUi(ui->statusButtonsPane); statusButtonsUI->setupUi(ui->statusButtonsPane);
routerCommandsUI->setupUi(routerCommandsParent); routerCommandsUI->setupUi(routerCommandsParent);
uiSettings->setupUi(ui->settingsContents); uiSettings->setupUi(ui->settingsContents);
ui->aboutHrefLabel->setText("<html><head/><body><p><a href='about:i2pd_qt'><span style='text-decoration:none;color:#a0a0a0;'>"
"<span style='font-weight:500;'>i2pd_qt</span><br/>Version " I2PD_VERSION " · About...</span></a></p></body></html>");
routerCommandsParent->hide(); routerCommandsParent->hide();
ui->verticalLayout_2->addWidget(routerCommandsParent); ui->verticalLayout_2->addWidget(routerCommandsParent);
//,statusHtmlUI(new Ui::StatusHtmlPaneForm) //,statusHtmlUI(new Ui::StatusHtmlPaneForm)
@ -76,15 +93,16 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
setWindowTitle(QApplication::translate("AppTitle","I2PD")); setWindowTitle(QApplication::translate("AppTitle","I2PD"));
//TODO handle resizes and change the below into resize() call //TODO handle resizes and change the below into resize() call
setFixedHeight(550); constexpr auto WINDOW_HEIGHT = 610;
ui->centralWidget->setFixedHeight(550); setFixedHeight(WINDOW_HEIGHT);
ui->centralWidget->setFixedHeight(WINDOW_HEIGHT);
onResize(); onResize();
ui->stackedWidget->setCurrentIndex(0); ui->stackedWidget->setCurrentIndex(0);
ui->settingsScrollArea->resize(uiSettings->settingsContentsQVBoxLayout->sizeHint().width()+10,380); ui->settingsScrollArea->resize(uiSettings->settingsContentsQVBoxLayout->sizeHint().width()+10,380);
//QScrollBar* const barSett = ui->settingsScrollArea->verticalScrollBar(); //QScrollBar* const barSett = ui->settingsScrollArea->verticalScrollBar();
int w = 683; constexpr auto w = 683;
int h = 4550; constexpr auto h = 4550;
ui->settingsContents->setFixedSize(w, h); ui->settingsContents->setFixedSize(w, h);
ui->settingsContents->setGeometry(QRect(0,0,w,h)); ui->settingsContents->setGeometry(QRect(0,0,w,h));
@ -118,6 +136,7 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
//childTextBrowser->setOpenExternalLinks(false); //childTextBrowser->setOpenExternalLinks(false);
childTextBrowser->setOpenLinks(false); childTextBrowser->setOpenLinks(false);
connect(textBrowser, SIGNAL(anchorClicked(const QUrl&)), this, SLOT(anchorClickedHandler(const QUrl&))); connect(textBrowser, SIGNAL(anchorClicked(const QUrl&)), this, SLOT(anchorClickedHandler(const QUrl&)));
connect(childTextBrowser, SIGNAL(anchorClicked(const QUrl&)), this, SLOT(anchorClickedHandler(const QUrl&)));
pageWithBackButton = new PageWithBackButton(this, childTextBrowser); pageWithBackButton = new PageWithBackButton(this, childTextBrowser);
ui->verticalLayout_2->addWidget(pageWithBackButton); ui->verticalLayout_2->addWidget(pageWithBackButton);
pageWithBackButton->hide(); pageWithBackButton->hide();
@ -143,6 +162,8 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
QObject::connect(routerCommandsUI->acceptTransitTunnelsPushButton, SIGNAL(released()), this, SLOT(enableTransit())); QObject::connect(routerCommandsUI->acceptTransitTunnelsPushButton, SIGNAL(released()), this, SLOT(enableTransit()));
QObject::connect(routerCommandsUI->declineTransitTunnelsPushButton, SIGNAL(released()), this, SLOT(disableTransit())); QObject::connect(routerCommandsUI->declineTransitTunnelsPushButton, SIGNAL(released()), this, SLOT(disableTransit()));
QObject::connect(ui->aboutHrefLabel, SIGNAL(linkActivated(const QString &)), this, SLOT(showAboutBox(const QString &)));
QObject::connect(ui->logViewerPushButton, SIGNAL(released()), this, SLOT(showLogViewerPage())); QObject::connect(ui->logViewerPushButton, SIGNAL(released()), this, SLOT(showLogViewerPage()));
QObject::connect(ui->settingsPagePushButton, SIGNAL(released()), this, SLOT(showSettingsPage())); QObject::connect(ui->settingsPagePushButton, SIGNAL(released()), this, SLOT(showSettingsPage()));
@ -158,9 +179,9 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
# define OPTION(section,option,defaultValueGetter) ConfigOption(QString(section),QString(option)) # define OPTION(section,option,defaultValueGetter) ConfigOption(QString(section),QString(option))
initFileChooser( OPTION("","conf",[](){return "";}), uiSettings->configFileLineEdit, uiSettings->configFileBrowsePushButton); initFileChooser( OPTION("","conf",[](){return "";}), uiSettings->configFileLineEdit, uiSettings->configFileBrowsePushButton, false, true);
initFileChooser( OPTION("","tunconf",[](){return "";}), uiSettings->tunnelsConfigFileLineEdit, uiSettings->tunnelsConfigFileBrowsePushButton); initFileChooser( OPTION("","tunconf",[](){return "";}), uiSettings->tunnelsConfigFileLineEdit, uiSettings->tunnelsConfigFileBrowsePushButton, false);
initFileChooser( OPTION("","pidfile",[]{return "";}), uiSettings->pidFileLineEdit, uiSettings->pidFileBrowsePushButton); initFileChooser( OPTION("","pidfile",[]{return "";}), uiSettings->pidFileLineEdit, uiSettings->pidFileBrowsePushButton, false);
uiSettings->logDestinationComboBox->clear(); uiSettings->logDestinationComboBox->clear();
uiSettings->logDestinationComboBox->insertItems(0, QStringList() uiSettings->logDestinationComboBox->insertItems(0, QStringList()
@ -169,9 +190,15 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
<< QApplication::translate("MainWindow", "file", 0) << QApplication::translate("MainWindow", "file", 0)
); );
initLogDestinationCombobox( OPTION("","log",[]{return "";}), uiSettings->logDestinationComboBox); initLogDestinationCombobox( OPTION("","log",[]{return "";}), uiSettings->logDestinationComboBox);
#ifdef I2PD_QT_RELEASE
uiSettings->logDestinationComboBox->setEnabled(false); // #1593
#endif
logFileNameOption=initFileChooser( OPTION("","logfile",[]{return "";}), uiSettings->logFileLineEdit, uiSettings->logFileBrowsePushButton); logFileNameOption=initFileChooser( OPTION("","logfile",[]{return "";}), uiSettings->logFileLineEdit, uiSettings->logFileBrowsePushButton, false);
initLogLevelCombobox(OPTION("","loglevel",[]{return "";}), uiSettings->logLevelComboBox); initLogLevelCombobox(OPTION("","loglevel",[]{return "";}), uiSettings->logLevelComboBox);
QObject::connect(uiSettings->logLevelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(syncLogLevel(int)));
initCheckBox( OPTION("","logclftime",[]{return "false";}), uiSettings->logclftimeCheckBox);//"Write full CLF-formatted date and time to log (default: write only time)" initCheckBox( OPTION("","logclftime",[]{return "false";}), uiSettings->logclftimeCheckBox);//"Write full CLF-formatted date and time to log (default: write only time)"
initFolderChooser( OPTION("","datadir",[]{return "";}), uiSettings->dataFolderLineEdit, uiSettings->dataFolderBrowsePushButton); initFolderChooser( OPTION("","datadir",[]{return "";}), uiSettings->dataFolderLineEdit, uiSettings->dataFolderBrowsePushButton);
initIPAddressBox( OPTION("","host",[]{return "";}), uiSettings->routerExternalHostLineEdit, tr("Router external address -> Host")); initIPAddressBox( OPTION("","host",[]{return "";}), uiSettings->routerExternalHostLineEdit, tr("Router external address -> Host"));
@ -213,7 +240,7 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
initIPAddressBox( OPTION("httpproxy","address",[]{return "";}), uiSettings->httpProxyAddressLineEdit, tr("HTTP proxy -> IP address")); initIPAddressBox( OPTION("httpproxy","address",[]{return "";}), uiSettings->httpProxyAddressLineEdit, tr("HTTP proxy -> IP address"));
initTCPPortBox( OPTION("httpproxy","port",[]{return "4444";}), uiSettings->httpProxyPortLineEdit, tr("HTTP proxy -> Port")); initTCPPortBox( OPTION("httpproxy","port",[]{return "4444";}), uiSettings->httpProxyPortLineEdit, tr("HTTP proxy -> Port"));
initCheckBox( OPTION("httpproxy","addresshelper",[]{return "true";}), uiSettings->httpProxyAddressHelperCheckBox);//Enable address helper (jump). true by default initCheckBox( OPTION("httpproxy","addresshelper",[]{return "true";}), uiSettings->httpProxyAddressHelperCheckBox);//Enable address helper (jump). true by default
initFileChooser( OPTION("httpproxy","keys",[]{return "";}), uiSettings->httpProxyKeyFileLineEdit, uiSettings->httpProxyKeyFilePushButton); initFileChooser( OPTION("httpproxy","keys",[]{return "";}), uiSettings->httpProxyKeyFileLineEdit, uiSettings->httpProxyKeyFilePushButton, false);
initSignatureTypeCombobox(OPTION("httpproxy","signaturetype",[]{return "7";}), uiSettings->comboBox_httpPorxySignatureType); initSignatureTypeCombobox(OPTION("httpproxy","signaturetype",[]{return "7";}), uiSettings->comboBox_httpPorxySignatureType);
initStringBox( OPTION("httpproxy","inbound.length",[]{return "3";}), uiSettings->httpProxyInboundTunnelsLenLineEdit); initStringBox( OPTION("httpproxy","inbound.length",[]{return "3";}), uiSettings->httpProxyInboundTunnelsLenLineEdit);
initStringBox( OPTION("httpproxy","inbound.quantity",[]{return "5";}), uiSettings->httpProxyInboundTunnQuantityLineEdit); initStringBox( OPTION("httpproxy","inbound.quantity",[]{return "5";}), uiSettings->httpProxyInboundTunnQuantityLineEdit);
@ -226,7 +253,7 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
initCheckBox( OPTION("socksproxy","enabled",[]{return "";}), uiSettings->socksProxyEnabledCheckBox); initCheckBox( OPTION("socksproxy","enabled",[]{return "";}), uiSettings->socksProxyEnabledCheckBox);
initIPAddressBox( OPTION("socksproxy","address",[]{return "";}), uiSettings->socksProxyAddressLineEdit, tr("Socks proxy -> IP address")); initIPAddressBox( OPTION("socksproxy","address",[]{return "";}), uiSettings->socksProxyAddressLineEdit, tr("Socks proxy -> IP address"));
initTCPPortBox( OPTION("socksproxy","port",[]{return "4447";}), uiSettings->socksProxyPortLineEdit, tr("Socks proxy -> Port")); initTCPPortBox( OPTION("socksproxy","port",[]{return "4447";}), uiSettings->socksProxyPortLineEdit, tr("Socks proxy -> Port"));
initFileChooser( OPTION("socksproxy","keys",[]{return "";}), uiSettings->socksProxyKeyFileLineEdit, uiSettings->socksProxyKeyFilePushButton); initFileChooser( OPTION("socksproxy","keys",[]{return "";}), uiSettings->socksProxyKeyFileLineEdit, uiSettings->socksProxyKeyFilePushButton, false);
initSignatureTypeCombobox(OPTION("socksproxy","signaturetype",[]{return "7";}), uiSettings->comboBox_socksProxySignatureType); initSignatureTypeCombobox(OPTION("socksproxy","signaturetype",[]{return "7";}), uiSettings->comboBox_socksProxySignatureType);
initStringBox( OPTION("socksproxy","inbound.length",[]{return "";}), uiSettings->socksProxyInboundTunnelsLenLineEdit); initStringBox( OPTION("socksproxy","inbound.length",[]{return "";}), uiSettings->socksProxyInboundTunnelsLenLineEdit);
initStringBox( OPTION("socksproxy","inbound.quantity",[]{return "";}), uiSettings->socksProxyInboundTunnQuantityLineEdit); initStringBox( OPTION("socksproxy","inbound.quantity",[]{return "";}), uiSettings->socksProxyInboundTunnQuantityLineEdit);
@ -256,8 +283,8 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
initIPAddressBox( OPTION("i2pcontrol","address",[]{return "";}), uiSettings->i2pControlAddressLineEdit, tr("I2PControl -> IP address")); initIPAddressBox( OPTION("i2pcontrol","address",[]{return "";}), uiSettings->i2pControlAddressLineEdit, tr("I2PControl -> IP address"));
initTCPPortBox( OPTION("i2pcontrol","port",[]{return "7650";}), uiSettings->i2pControlPortLineEdit, tr("I2PControl -> Port")); initTCPPortBox( OPTION("i2pcontrol","port",[]{return "7650";}), uiSettings->i2pControlPortLineEdit, tr("I2PControl -> Port"));
initStringBox( OPTION("i2pcontrol","password",[]{return "";}), uiSettings->i2pControlPasswordLineEdit); initStringBox( OPTION("i2pcontrol","password",[]{return "";}), uiSettings->i2pControlPasswordLineEdit);
initFileChooser( OPTION("i2pcontrol","cert",[]{return "i2pcontrol.crt.pem";}), uiSettings->i2pControlCertFileLineEdit, uiSettings->i2pControlCertFileBrowsePushButton); initFileChooser( OPTION("i2pcontrol","cert",[]{return "i2pcontrol.crt.pem";}), uiSettings->i2pControlCertFileLineEdit, uiSettings->i2pControlCertFileBrowsePushButton, true);
initFileChooser( OPTION("i2pcontrol","key",[]{return "i2pcontrol.key.pem";}), uiSettings->i2pControlKeyFileLineEdit, uiSettings->i2pControlKeyFileBrowsePushButton); initFileChooser( OPTION("i2pcontrol","key",[]{return "i2pcontrol.key.pem";}), uiSettings->i2pControlKeyFileLineEdit, uiSettings->i2pControlKeyFileBrowsePushButton, true);
initCheckBox( OPTION("upnp","enabled",[]{return "true";}), uiSettings->enableUPnPCheckBox); initCheckBox( OPTION("upnp","enabled",[]{return "true";}), uiSettings->enableUPnPCheckBox);
initStringBox( OPTION("upnp","name",[]{return "I2Pd";}), uiSettings->upnpNameLineEdit); initStringBox( OPTION("upnp","name",[]{return "I2Pd";}), uiSettings->upnpNameLineEdit);
@ -265,9 +292,9 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
initCheckBox( OPTION("precomputation","elgamal",[]{return "false";}), uiSettings->useElGamalPrecomputedTablesCheckBox); initCheckBox( OPTION("precomputation","elgamal",[]{return "false";}), uiSettings->useElGamalPrecomputedTablesCheckBox);
initCheckBox( OPTION("reseed","verify",[]{return "";}), uiSettings->reseedVerifyCheckBox); initCheckBox( OPTION("reseed","verify",[]{return "";}), uiSettings->reseedVerifyCheckBox);
initFileChooser( OPTION("reseed","file",[]{return "";}), uiSettings->reseedFileLineEdit, uiSettings->reseedFileBrowsePushButton); initFileChooser( OPTION("reseed","file",[]{return "";}), uiSettings->reseedFileLineEdit, uiSettings->reseedFileBrowsePushButton, true);
initStringBox( OPTION("reseed","urls",[]{return "";}), uiSettings->reseedURLsLineEdit); initStringBox( OPTION("reseed","urls",[]{return "";}), uiSettings->reseedURLsLineEdit);
initFileChooser( OPTION("reseed","zipfile",[]{return "";}), uiSettings->reseedZipFileLineEdit, uiSettings->reseedZipFileBrowsePushButton); //Path to local .zip file to reseed from initFileChooser( OPTION("reseed","zipfile",[]{return "";}), uiSettings->reseedZipFileLineEdit, uiSettings->reseedZipFileBrowsePushButton, true); //Path to local .zip file to reseed from
initUInt16Box( OPTION("reseed","threshold",[]{return "25";}), uiSettings->reseedThresholdNumberLineEdit, tr("reseedThreshold")); //Minimum number of known routers before requesting reseed. 25 by default initUInt16Box( OPTION("reseed","threshold",[]{return "25";}), uiSettings->reseedThresholdNumberLineEdit, tr("reseedThreshold")); //Minimum number of known routers before requesting reseed. 25 by default
initStringBox( OPTION("reseed","proxy",[]{return "";}), uiSettings->reseedProxyLineEdit);//URL for https/socks reseed proxy initStringBox( OPTION("reseed","proxy",[]{return "";}), uiSettings->reseedProxyLineEdit);//URL for https/socks reseed proxy
@ -306,7 +333,15 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
# undef OPTION # undef OPTION
//widgetlocks.add(new widgetlock(widget,lockbtn)); //widgetlocks.add(new widgetlock(widget,lockbtn));
// #1593
#ifdef I2PD_QT_RELEASE
uiSettings->logDestComboEditPushButton->setEnabled(false);
#else
widgetlocks.add(new widgetlock(uiSettings->logDestinationComboBox,uiSettings->logDestComboEditPushButton)); widgetlocks.add(new widgetlock(uiSettings->logDestinationComboBox,uiSettings->logDestComboEditPushButton));
#endif
widgetlocks.add(new widgetlock(uiSettings->logLevelComboBox,uiSettings->logLevelComboEditPushButton)); widgetlocks.add(new widgetlock(uiSettings->logLevelComboBox,uiSettings->logLevelComboEditPushButton));
widgetlocks.add(new widgetlock(uiSettings->comboBox_httpPorxySignatureType,uiSettings->httpProxySignTypeComboEditPushButton)); widgetlocks.add(new widgetlock(uiSettings->comboBox_httpPorxySignatureType,uiSettings->httpProxySignTypeComboEditPushButton));
widgetlocks.add(new widgetlock(uiSettings->comboBox_socksProxySignatureType,uiSettings->socksProxySignTypeComboEditPushButton)); widgetlocks.add(new widgetlock(uiSettings->comboBox_socksProxySignatureType,uiSettings->socksProxySignTypeComboEditPushButton));
@ -400,6 +435,29 @@ void MainWindow::showStatusPage(StatusPage newStatusPage){
} }
wasSelectingAtStatusMainPage=false; wasSelectingAtStatusMainPage=false;
} }
void MainWindow::showAboutBox(const QString & href) {
AboutDialog dialog(this);
/*
//doesn't work on older qt5: class QStyleHints has no member named showIsMaximized
if (!QGuiApplication::styleHints()->showIsFullScreen() && !QGuiApplication::styleHints()->showIsMaximized()) {
const QWindow * windowHandle = dialog.windowHandle();
qDebug()<<"AboutDialog windowHandle ptr: "<<(size_t)windowHandle<<endl;
const QScreen * screen = windowHandle?windowHandle->screen():nullptr; //Qt 5.14+: dialog.screen()
qDebug()<<"AboutDialog screen ptr: "<<(size_t)screen<<endl;
if (screen) {
const QRect availableGeometry = screen->availableGeometry();
//dialog.resize(availableGeometry.width() / 3, availableGeometry.height() * 2 / 3);
dialog.move((availableGeometry.width() - dialog.width()) / 2,
(availableGeometry.height() - dialog.height()) / 2);
}
}
*/
(void) dialog.exec();
}
void MainWindow::showLogViewerPage(){ui->stackedWidget->setCurrentIndex(1);setStatusButtonsVisible(false);} void MainWindow::showLogViewerPage(){ui->stackedWidget->setCurrentIndex(1);setStatusButtonsVisible(false);}
void MainWindow::showSettingsPage(){ui->stackedWidget->setCurrentIndex(2);setStatusButtonsVisible(false);} void MainWindow::showSettingsPage(){ui->stackedWidget->setCurrentIndex(2);setStatusButtonsVisible(false);}
void MainWindow::showTunnelsPage(){ui->stackedWidget->setCurrentIndex(3);setStatusButtonsVisible(false);} void MainWindow::showTunnelsPage(){ui->stackedWidget->setCurrentIndex(3);setStatusButtonsVisible(false);}
@ -607,15 +665,15 @@ MainWindow::~MainWindow()
//QMessageBox::information(0, "Debug", "mw destructor 2"); //QMessageBox::information(0, "Debug", "mw destructor 2");
} }
FileChooserItem* MainWindow::initFileChooser(ConfigOption option, QLineEdit* fileNameLineEdit, QPushButton* fileBrowsePushButton){ FileChooserItem* MainWindow::initFileChooser(ConfigOption option, QLineEdit* fileNameLineEdit, QPushButton* fileBrowsePushButton, bool requireExistingFile, bool readOnly){
FileChooserItem* retVal; FileChooserItem* retVal;
retVal=new FileChooserItem(option, fileNameLineEdit, fileBrowsePushButton); retVal=new FileChooserItem(option, fileNameLineEdit, fileBrowsePushButton, this, requireExistingFile, readOnly);
MainWindowItem* super=retVal; MainWindowItem* super=retVal;
configItems.append(super); configItems.append(super);
return retVal; return retVal;
} }
void MainWindow::initFolderChooser(ConfigOption option, QLineEdit* folderLineEdit, QPushButton* folderBrowsePushButton){ void MainWindow::initFolderChooser(ConfigOption option, QLineEdit* folderLineEdit, QPushButton* folderBrowsePushButton){
configItems.append(new FolderChooserItem(option, folderLineEdit, folderBrowsePushButton)); configItems.append(new FolderChooserItem(option, folderLineEdit, folderBrowsePushButton, this, true));
} }
/*void MainWindow::initCombobox(ConfigOption option, QComboBox* comboBox){ /*void MainWindow::initCombobox(ConfigOption option, QComboBox* comboBox){
configItems.append(new ComboBoxItem(option, comboBox)); configItems.append(new ComboBoxItem(option, comboBox));
@ -631,25 +689,25 @@ void MainWindow::initSignatureTypeCombobox(ConfigOption option, QComboBox* combo
configItems.append(new SignatureTypeComboBoxItem(option, comboBox)); configItems.append(new SignatureTypeComboBoxItem(option, comboBox));
} }
void MainWindow::initIPAddressBox(ConfigOption option, QLineEdit* addressLineEdit, QString fieldNameTranslated){ void MainWindow::initIPAddressBox(ConfigOption option, QLineEdit* addressLineEdit, QString fieldNameTranslated){
configItems.append(new IPAddressStringItem(option, addressLineEdit, fieldNameTranslated)); configItems.append(new IPAddressStringItem(option, addressLineEdit, fieldNameTranslated, this));
} }
void MainWindow::initTCPPortBox(ConfigOption option, QLineEdit* portLineEdit, QString fieldNameTranslated){ void MainWindow::initTCPPortBox(ConfigOption option, QLineEdit* portLineEdit, QString fieldNameTranslated){
configItems.append(new TCPPortStringItem(option, portLineEdit, fieldNameTranslated)); configItems.append(new TCPPortStringItem(option, portLineEdit, fieldNameTranslated, this));
} }
void MainWindow::initCheckBox(ConfigOption option, QCheckBox* checkBox) { void MainWindow::initCheckBox(ConfigOption option, QCheckBox* checkBox) {
configItems.append(new CheckBoxItem(option, checkBox)); configItems.append(new CheckBoxItem(option, checkBox));
} }
void MainWindow::initIntegerBox(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){ void MainWindow::initIntegerBox(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){
configItems.append(new IntegerStringItem(option, numberLineEdit, fieldNameTranslated)); configItems.append(new IntegerStringItem(option, numberLineEdit, fieldNameTranslated, this));
} }
void MainWindow::initUInt32Box(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){ void MainWindow::initUInt32Box(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){
configItems.append(new UInt32StringItem(option, numberLineEdit, fieldNameTranslated)); configItems.append(new UInt32StringItem(option, numberLineEdit, fieldNameTranslated, this));
} }
void MainWindow::initUInt16Box(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){ void MainWindow::initUInt16Box(ConfigOption option, QLineEdit* numberLineEdit, QString fieldNameTranslated){
configItems.append(new UInt16StringItem(option, numberLineEdit, fieldNameTranslated)); configItems.append(new UInt16StringItem(option, numberLineEdit, fieldNameTranslated, this));
} }
void MainWindow::initStringBox(ConfigOption option, QLineEdit* lineEdit){ void MainWindow::initStringBox(ConfigOption option, QLineEdit* lineEdit){
configItems.append(new BaseStringItem(option, lineEdit, QString())); configItems.append(new BaseStringItem(option, lineEdit, QString(), this));
} }
NonGUIOptionItem* MainWindow::initNonGUIOption(ConfigOption option) { NonGUIOptionItem* MainWindow::initNonGUIOption(ConfigOption option) {
NonGUIOptionItem * retValue; NonGUIOptionItem * retValue;
@ -741,7 +799,7 @@ void MainWindow::deleteTunnelFromUI(std::string tunnelName, TunnelConfig* cnf) {
} }
/** returns false iff not valid items present and save was aborted */ /** returns false iff not valid items present and save was aborted */
bool MainWindow::saveAllConfigs(bool focusOnTunnel, std::string tunnelNameToFocus){ bool MainWindow::saveAllConfigs(bool reloadAfterSave, FocusEnum focusOn, std::string tunnelNameToFocus, QWidget* widgetToFocus){
QString cannotSaveSettings = QApplication::tr("Cannot save settings."); QString cannotSaveSettings = QApplication::tr("Cannot save settings.");
programOptionsWriterCurrentSection=""; programOptionsWriterCurrentSection="";
/*if(!logFileNameOption->lineEdit->text().trimmed().isEmpty())logOption->optionValue=boost::any(std::string("file")); /*if(!logFileNameOption->lineEdit->text().trimmed().isEmpty())logOption->optionValue=boost::any(std::string("file"));
@ -751,12 +809,17 @@ bool MainWindow::saveAllConfigs(bool focusOnTunnel, std::string tunnelNameToFocu
for(QList<MainWindowItem*>::iterator it = configItems.begin(); it!= configItems.end(); ++it) { for(QList<MainWindowItem*>::iterator it = configItems.begin(); it!= configItems.end(); ++it) {
MainWindowItem* item = *it; MainWindowItem* item = *it;
if(!item->isValid()){ bool alreadyDisplayedIfWrong=false;
highlightWrongInput(QApplication::tr("Invalid value for")+" "+item->getConfigOption().section+"::"+item->getConfigOption().option+". "+item->getRequirementToBeValid()+" "+cannotSaveSettings, item->getWidgetToFocus()); if(!item->isValid(alreadyDisplayedIfWrong)){
if(!alreadyDisplayedIfWrong)
highlightWrongInput(
QApplication::tr("Invalid value for")+" "+item->getConfigOption().section+"::"+item->getConfigOption().option+". "+item->getRequirementToBeValid()+" "+cannotSaveSettings,
WrongInputPageEnum::generalSettingsPage,
item->getWidgetToFocus());
return false; return false;
} }
} }
delayedSaveManagerPtr->delayedSave(++dataSerial, focusOnTunnel, tunnelNameToFocus); delayedSaveManagerPtr->delayedSave(reloadAfterSave, ++dataSerial, focusOn, tunnelNameToFocus, widgetToFocus);//TODO does dataSerial work? //FIXME
//onLoggingOptionsChange(); //onLoggingOptionsChange();
return true; return true;
@ -764,11 +827,14 @@ bool MainWindow::saveAllConfigs(bool focusOnTunnel, std::string tunnelNameToFocu
void FileChooserItem::pushButtonReleased() { void FileChooserItem::pushButtonReleased() {
QString fileName = lineEdit->text().trimmed(); QString fileName = lineEdit->text().trimmed();
fileName = QFileDialog::getOpenFileName(nullptr, tr("Open File"), fileName, tr("All Files (*.*)")); fileName = requireExistingFile ?
QFileDialog::getOpenFileName(nullptr, tr("Open File"), fileName, tr("All Files (*.*)")) :
QFileDialog::getSaveFileName(nullptr, tr("Open File"), fileName, tr("All Files (*.*)"));
if(fileName.length()>0)lineEdit->setText(fileName); if(fileName.length()>0)lineEdit->setText(fileName);
} }
void FolderChooserItem::pushButtonReleased() { void FolderChooserItem::pushButtonReleased() {
QString fileName = lineEdit->text().trimmed(); QString fileName = lineEdit->text().trimmed();
assert(requireExistingFile);
fileName = QFileDialog::getExistingDirectory(nullptr, tr("Open Folder"), fileName); fileName = QFileDialog::getExistingDirectory(nullptr, tr("Open Folder"), fileName);
if(fileName.length()>0)lineEdit->setText(fileName); if(fileName.length()>0)lineEdit->setText(fileName);
} }
@ -776,6 +842,11 @@ void FolderChooserItem::pushButtonReleased() {
void BaseStringItem::installListeners(MainWindow *mainWindow) { void BaseStringItem::installListeners(MainWindow *mainWindow) {
QObject::connect(lineEdit, SIGNAL(textChanged(const QString &)), mainWindow, SLOT(updated())); QObject::connect(lineEdit, SIGNAL(textChanged(const QString &)), mainWindow, SLOT(updated()));
} }
bool BaseStringItem::isValid(bool & alreadyDisplayedIfWrong) {
alreadyDisplayedIfWrong=true;
return ::isValidSingleLine(lineEdit, WrongInputPageEnum::generalSettingsPage, mainWindow);
}
void ComboBoxItem::installListeners(MainWindow *mainWindow) { void ComboBoxItem::installListeners(MainWindow *mainWindow) {
QObject::connect(comboBox, SIGNAL(currentIndexChanged(int)), mainWindow, SLOT(updated())); QObject::connect(comboBox, SIGNAL(currentIndexChanged(int)), mainWindow, SLOT(updated()));
} }
@ -787,8 +858,9 @@ void MainWindow::updated() {
ui->wrongInputLabel->setVisible(false); ui->wrongInputLabel->setVisible(false);
adjustSizesAccordingToWrongLabel(); adjustSizesAccordingToWrongLabel();
applyTunnelsUiToConfigs(); bool correct = applyTunnelsUiToConfigs();
saveAllConfigs(false); if(!correct) return;
saveAllConfigs(false, FocusEnum::noFocus);
} }
void MainWindowItem::installListeners(MainWindow *mainWindow) {} void MainWindowItem::installListeners(MainWindow *mainWindow) {}
@ -863,11 +935,11 @@ bool MainWindow::applyTunnelsUiToConfigs() {
return true; return true;
} }
void MainWindow::reloadTunnelsConfigAndUI_QString(const QString tunnelNameToFocus) { void MainWindow::reloadTunnelsConfigAndUI_QString(QString tunnelNameToFocus) {
reloadTunnelsConfigAndUI(tunnelNameToFocus.toStdString()); reloadTunnelsConfigAndUI(tunnelNameToFocus.toStdString(), nullptr);
} }
void MainWindow::reloadTunnelsConfigAndUI(std::string tunnelNameToFocus) { void MainWindow::reloadTunnelsConfigAndUI(std::string tunnelNameToFocus, QWidget* widgetToFocus) {
deleteTunnelForms(); deleteTunnelForms();
for (std::map<std::string,TunnelConfig*>::iterator it=tunnelConfigs.begin(); it!=tunnelConfigs.end(); ++it) { for (std::map<std::string,TunnelConfig*>::iterator it=tunnelConfigs.begin(); it!=tunnelConfigs.end(); ++it) {
TunnelConfig* tunconf = it->second; TunnelConfig* tunconf = it->second;
@ -884,8 +956,10 @@ void MainWindow::TunnelsPageUpdateListenerMainWindowImpl::updated(std::string ol
std::map<std::string,TunnelConfig*>::const_iterator it=mainWindow->tunnelConfigs.find(oldName); std::map<std::string,TunnelConfig*>::const_iterator it=mainWindow->tunnelConfigs.find(oldName);
if(it!=mainWindow->tunnelConfigs.end())mainWindow->tunnelConfigs.erase(it); if(it!=mainWindow->tunnelConfigs.end())mainWindow->tunnelConfigs.erase(it);
mainWindow->tunnelConfigs[tunConf->getName()]=tunConf; mainWindow->tunnelConfigs[tunConf->getName()]=tunConf;
mainWindow->saveAllConfigs(true, FocusEnum::focusOnTunnelName, tunConf->getName());
} }
mainWindow->saveAllConfigs(true, tunConf->getName()); else
mainWindow->saveAllConfigs(false, FocusEnum::noFocus);
} }
void MainWindow::TunnelsPageUpdateListenerMainWindowImpl::needsDeleting(std::string oldName){ void MainWindow::TunnelsPageUpdateListenerMainWindowImpl::needsDeleting(std::string oldName){
@ -923,17 +997,68 @@ void MainWindow::anchorClickedHandler(const QUrl & link) {
qDebug()<<debugStr; qDebug()<<debugStr;
//QMessageBox::information(this, "", debugStr); //QMessageBox::information(this, "", debugStr);
/* /?page=local_destination&b32=xx...xx */
QString str=link.toString(); QString str=link.toString();
#define LOCAL_DEST_B32_PREFIX "/?page=local_destination&b32=" std::map<std::string, std::string> params;
static size_t LOCAL_DEST_B32_PREFIX_SZ=QString(LOCAL_DEST_B32_PREFIX).size(); i2p::http::URL url;
if(str.startsWith(LOCAL_DEST_B32_PREFIX)) { url.parse(str.toStdString());
str = str.right(str.size()-LOCAL_DEST_B32_PREFIX_SZ); url.parse_query(params);
qDebug () << "b32:" << str; const std::string page = params["page"];
const std::string cmd = params["cmd"];
if (page == "sam_session") {
pageWithBackButton->show(); pageWithBackButton->show();
textBrowser->hide(); textBrowser->hide();
std::stringstream s; std::stringstream s;
std::string strstd = str.toStdString(); i2p::http::ShowSAMSession (s, params["sam_id"]);
childTextBrowser->setHtml(QString::fromStdString(s.str()));
} else if (page == "local_destination") {
std::string b32 = params["b32"];
currentLocalDestinationB32 = b32;
pageWithBackButton->show();
textBrowser->hide();
std::stringstream s;
std::string strstd = currentLocalDestinationB32;
i2p::http::ShowLocalDestination(s,strstd,0);
childTextBrowser->setHtml(QString::fromStdString(s.str()));
} else if (page == "i2cp_local_destination") {
pageWithBackButton->show();
textBrowser->hide();
std::stringstream s;
i2p::http::ShowI2CPLocalDestination (s, params["i2cp_id"]);
childTextBrowser->setHtml(QString::fromStdString(s.str()));
} else if(cmd == "closestream") {
std::string b32 = params["b32"];
uint32_t streamID = std::stoul(params["streamID"], nullptr);
i2p::data::IdentHash ident;
ident.FromBase32 (b32);
auto dest = i2p::client::context.FindLocalDestination (ident);
if (streamID) {
if (dest) {
if(dest->DeleteStream (streamID))
QMessageBox::information(
this,
QApplication::tr("Success"),
QApplication::tr("<HTML><b>SUCCESS</b>: Stream closed"));
else
QMessageBox::critical(
this,
QApplication::tr("Error"),
QApplication::tr("<HTML><b>ERROR</b>: Stream not found or already was closed"));
}
else
QMessageBox::critical(
this,
QApplication::tr("Error"),
QApplication::tr("<HTML><b>ERROR</b>: Destination not found"));
}
else
QMessageBox::critical(
this,
QApplication::tr("Error"),
QApplication::tr("<HTML><b>ERROR</b>: StreamID is null"));
std::stringstream s;
std::string strstd = currentLocalDestinationB32;
i2p::http::ShowLocalDestination(s,strstd,0); i2p::http::ShowLocalDestination(s,strstd,0);
childTextBrowser->setHtml(QString::fromStdString(s.str())); childTextBrowser->setHtml(QString::fromStdString(s.str()));
} }
@ -944,39 +1069,58 @@ void MainWindow::backClickedFromChild() {
} }
void MainWindow::adjustSizesAccordingToWrongLabel() { void MainWindow::adjustSizesAccordingToWrongLabel() {
constexpr auto HEIGHT = 581;
constexpr auto WIDTH = 707;
if(ui->wrongInputLabel->isVisible()) { if(ui->wrongInputLabel->isVisible()) {
int dh = ui->wrongInputLabel->height()+ui->verticalLayout_7->layout()->spacing(); int dh = ui->wrongInputLabel->height()+ui->verticalLayout_7->layout()->spacing();
ui->verticalLayout_7->invalidate(); ui->verticalLayout_7->invalidate();
ui->wrongInputLabel->adjustSize(); ui->wrongInputLabel->adjustSize();
ui->stackedWidget->adjustSize(); ui->stackedWidget->adjustSize();
ui->stackedWidget->setFixedHeight(531-dh); const auto height = HEIGHT - dh;
ui->settingsPage->setFixedHeight(531-dh); ui->stackedWidget->setFixedHeight(height);
ui->verticalLayoutWidget_4->setGeometry(QRect(0, 0, 711, 531-dh)); ui->settingsPage->setFixedHeight(height);
ui->stackedWidget->setFixedHeight(531-dh); ui->verticalLayoutWidget_4->setGeometry(QRect(0, 0, WIDTH, height));
ui->settingsScrollArea->setFixedHeight(531-dh-settingsTitleLabelNominalHeight-ui->verticalLayout_4->spacing()); ui->stackedWidget->setFixedHeight(height);
ui->settingsScrollArea->setFixedHeight(height-settingsTitleLabelNominalHeight-ui->verticalLayout_4->spacing());
ui->settingsTitleLabel->setFixedHeight(settingsTitleLabelNominalHeight); ui->settingsTitleLabel->setFixedHeight(settingsTitleLabelNominalHeight);
ui->tunnelsScrollArea->setFixedHeight(531-dh-settingsTitleLabelNominalHeight-ui->horizontalLayout_42->geometry().height()-2*ui->verticalLayout_4->spacing()); ui->tunnelsScrollArea->setFixedHeight(height-settingsTitleLabelNominalHeight-ui->horizontalLayout_42->geometry().height()-2*ui->verticalLayout_4->spacing());
ui->tunnelsTitleLabel->setFixedHeight(settingsTitleLabelNominalHeight); ui->tunnelsTitleLabel->setFixedHeight(settingsTitleLabelNominalHeight);
}else{ }else{
ui->verticalLayout_7->invalidate(); ui->verticalLayout_7->invalidate();
ui->wrongInputLabel->adjustSize(); ui->wrongInputLabel->adjustSize();
ui->stackedWidget->adjustSize(); ui->stackedWidget->adjustSize();
ui->stackedWidget->setFixedHeight(531); ui->stackedWidget->setFixedHeight(HEIGHT);
ui->settingsPage->setFixedHeight(531); ui->settingsPage->setFixedHeight(HEIGHT);
ui->verticalLayoutWidget_4->setGeometry(QRect(0, 0, 711, 531)); ui->verticalLayoutWidget_4->setGeometry(QRect(0, 0, WIDTH, HEIGHT));
ui->stackedWidget->setFixedHeight(531); ui->stackedWidget->setFixedHeight(HEIGHT);
ui->settingsScrollArea->setFixedHeight(531-settingsTitleLabelNominalHeight-ui->verticalLayout_4->spacing()); ui->settingsScrollArea->setFixedHeight(HEIGHT-settingsTitleLabelNominalHeight-ui->verticalLayout_4->spacing());
ui->settingsTitleLabel->setFixedHeight(settingsTitleLabelNominalHeight); ui->settingsTitleLabel->setFixedHeight(settingsTitleLabelNominalHeight);
ui->tunnelsScrollArea->setFixedHeight(531-settingsTitleLabelNominalHeight-ui->horizontalLayout_42->geometry().height()-2*ui->verticalLayout_4->spacing()); ui->tunnelsScrollArea->setFixedHeight(HEIGHT-settingsTitleLabelNominalHeight-ui->horizontalLayout_42->geometry().height()-2*ui->verticalLayout_4->spacing());
ui->tunnelsTitleLabel->setFixedHeight(settingsTitleLabelNominalHeight); ui->tunnelsTitleLabel->setFixedHeight(settingsTitleLabelNominalHeight);
} }
} }
void MainWindow::highlightWrongInput(QString warningText, QWidget* widgetToFocus) { void MainWindow::highlightWrongInput(QString warningText, WrongInputPageEnum inputPage, QWidget* widgetToFocus) {
bool redVisible = ui->wrongInputLabel->isVisible(); bool redVisible = ui->wrongInputLabel->isVisible();
ui->wrongInputLabel->setVisible(true); ui->wrongInputLabel->setVisible(true);
ui->wrongInputLabel->setText(warningText); ui->wrongInputLabel->setText(warningText);
if(!redVisible)adjustSizesAccordingToWrongLabel(); if(!redVisible)adjustSizesAccordingToWrongLabel();
if(widgetToFocus){ui->settingsScrollArea->ensureWidgetVisible(widgetToFocus);widgetToFocus->setFocus();} if(widgetToFocus){ui->settingsScrollArea->ensureWidgetVisible(widgetToFocus);widgetToFocus->setFocus();}
showSettingsPage(); switch(inputPage) {
case WrongInputPageEnum::generalSettingsPage: showSettingsPage(); break;
case WrongInputPageEnum::tunnelsSettingsPage: showTunnelsPage(); break;
default: assert(false); break;
} }
}
void MainWindow::syncLogLevel (int /*comboBoxIndex*/) {
std::string level = uiSettings->logLevelComboBox->currentText().toLower().toStdString();
if (level == "none" || level == "error" || level == "warn" || level == "info" || level == "debug")
i2p::log::Logger().SetLogLevel(level);
else {
LogPrint(eLogError, "unknown loglevel set attempted");
return;
}
i2p::log::Logger().Reopen ();
}

View File

@ -66,6 +66,8 @@
#include "DelayedSaveManagerImpl.h" #include "DelayedSaveManagerImpl.h"
#include "SaverImpl.h" #include "SaverImpl.h"
#include "I2pdQtUtil.h"
class SaverImpl; class SaverImpl;
class LogViewerManager; class LogViewerManager;
@ -100,12 +102,14 @@ class MainWindow;
class MainWindowItem : public QObject { class MainWindowItem : public QObject {
Q_OBJECT Q_OBJECT
private:
ConfigOption option; ConfigOption option;
QWidget* widgetToFocus; QWidget* widgetToFocus;
QString requirementToBeValid; QString requirementToBeValid;
const bool readOnly;
public: public:
MainWindowItem(ConfigOption option_, QWidget* widgetToFocus_, QString requirementToBeValid_) : MainWindowItem(ConfigOption option_, QWidget* widgetToFocus_, QString requirementToBeValid_, bool readOnly_=false) :
option(option_), widgetToFocus(widgetToFocus_), requirementToBeValid(requirementToBeValid_) {} option(option_), widgetToFocus(widgetToFocus_), requirementToBeValid(requirementToBeValid_), readOnly(readOnly_) {}
QWidget* getWidgetToFocus(){return widgetToFocus;} QWidget* getWidgetToFocus(){return widgetToFocus;}
QString& getRequirementToBeValid() { return requirementToBeValid; } QString& getRequirementToBeValid() { return requirementToBeValid; }
ConfigOption& getConfigOption() { return option; } ConfigOption& getConfigOption() { return option; }
@ -116,13 +120,14 @@ public:
std::string optName=""; std::string optName="";
if(!option.section.isEmpty())optName=option.section.toStdString()+std::string("."); if(!option.section.isEmpty())optName=option.section.toStdString()+std::string(".");
optName+=option.option.toStdString(); optName+=option.option.toStdString();
qDebug() << "loadFromConfigOption[" << optName.c_str() << "]"; //qDebug() << "loadFromConfigOption[" << optName.c_str() << "]";
boost::any programOption; boost::any programOption;
i2p::config::GetOptionAsAny(optName, programOption); i2p::config::GetOptionAsAny(optName, programOption);
optionValue=programOption.empty()?boost::any(std::string("")) optionValue=programOption.empty()?boost::any(std::string(""))
:boost::any_cast<boost::program_options::variable_value>(programOption).value(); :boost::any_cast<boost::program_options::variable_value>(programOption).value();
} }
virtual void saveToStringStream(std::stringstream& out){ virtual void saveToStringStream(std::stringstream& out){
if(readOnly)return; //should readOnly items (conf=) error somewhere, instead of silently skipping save?
if(isType<std::string>(optionValue)) { if(isType<std::string>(optionValue)) {
std::string v = boost::any_cast<std::string>(optionValue); std::string v = boost::any_cast<std::string>(optionValue);
if(v.empty())return; if(v.empty())return;
@ -132,7 +137,7 @@ public:
std::string optName=""; std::string optName="";
if(!option.section.isEmpty())optName=option.section.toStdString()+std::string("."); if(!option.section.isEmpty())optName=option.section.toStdString()+std::string(".");
optName+=option.option.toStdString(); optName+=option.option.toStdString();
qDebug() << "Writing option" << optName.c_str() << "of type" << rtti.c_str(); //qDebug() << "Writing option" << optName.c_str() << "of type" << rtti.c_str();
std::string sectionAsStdStr = option.section.toStdString(); std::string sectionAsStdStr = option.section.toStdString();
if(!option.section.isEmpty() && if(!option.section.isEmpty() &&
sectionAsStdStr!=programOptionsWriterCurrentSection) { sectionAsStdStr!=programOptionsWriterCurrentSection) {
@ -155,19 +160,24 @@ public:
}else out << boost::any_cast<std::string>(optionValue); //let it throw }else out << boost::any_cast<std::string>(optionValue); //let it throw
out << "\n\n"; out << "\n\n";
} }
virtual bool isValid(){return true;} virtual bool isValid(bool & alreadyDisplayedIfWrong){alreadyDisplayedIfWrong=false;return true;}
}; };
class NonGUIOptionItem : public MainWindowItem { class NonGUIOptionItem : public MainWindowItem {
public: public:
NonGUIOptionItem(ConfigOption option_) : MainWindowItem(option_, nullptr, QString()) {}; NonGUIOptionItem(ConfigOption option_) : MainWindowItem(option_, nullptr, QString()) {}
virtual ~NonGUIOptionItem(){} virtual ~NonGUIOptionItem(){}
virtual bool isValid() { return true; } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
}; };
class BaseStringItem : public MainWindowItem { class BaseStringItem : public MainWindowItem {
Q_OBJECT Q_OBJECT
public: public:
QLineEdit* lineEdit; QLineEdit* lineEdit;
BaseStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString requirementToBeValid_) : MainWindowItem(option_, lineEdit_, requirementToBeValid_), lineEdit(lineEdit_){}; MainWindow *mainWindow;
BaseStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString requirementToBeValid_, MainWindow* mainWindow_, bool readOnly=false):
MainWindowItem(option_, lineEdit_, requirementToBeValid_, readOnly),
lineEdit(lineEdit_),
mainWindow(mainWindow_)
{};
virtual ~BaseStringItem(){} virtual ~BaseStringItem(){}
virtual void installListeners(MainWindow *mainWindow); virtual void installListeners(MainWindow *mainWindow);
virtual QString toString(){ virtual QString toString(){
@ -183,13 +193,15 @@ public:
optionValue=fromString(lineEdit->text()); optionValue=fromString(lineEdit->text());
MainWindowItem::saveToStringStream(out); MainWindowItem::saveToStringStream(out);
} }
virtual bool isValid() { return true; } virtual bool isValid(bool & alreadyDisplayedIfWrong);
}; };
class FileOrFolderChooserItem : public BaseStringItem { class FileOrFolderChooserItem : public BaseStringItem {
protected:
const bool requireExistingFile;
public: public:
QPushButton* browsePushButton; QPushButton* browsePushButton;
FileOrFolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_) : FileOrFolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_, MainWindow* mw, bool requireExistingFile_, bool readOnly) :
BaseStringItem(option_, lineEdit_, QString()), browsePushButton(browsePushButton_) {} BaseStringItem(option_, lineEdit_, QString(), mw, readOnly), requireExistingFile(requireExistingFile_), browsePushButton(browsePushButton_) {}
virtual ~FileOrFolderChooserItem(){} virtual ~FileOrFolderChooserItem(){}
}; };
class FileChooserItem : public FileOrFolderChooserItem { class FileChooserItem : public FileOrFolderChooserItem {
@ -197,8 +209,8 @@ class FileChooserItem : public FileOrFolderChooserItem {
private slots: private slots:
void pushButtonReleased(); void pushButtonReleased();
public: public:
FileChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_) : FileChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_, MainWindow* mw, bool requireExistingFile, bool readOnly) :
FileOrFolderChooserItem(option_, lineEdit_, browsePushButton_) { FileOrFolderChooserItem(option_, lineEdit_, browsePushButton_, mw, requireExistingFile, readOnly) {
QObject::connect(browsePushButton, SIGNAL(released()), this, SLOT(pushButtonReleased())); QObject::connect(browsePushButton, SIGNAL(released()), this, SLOT(pushButtonReleased()));
} }
}; };
@ -207,20 +219,20 @@ class FolderChooserItem : public FileOrFolderChooserItem{
private slots: private slots:
void pushButtonReleased(); void pushButtonReleased();
public: public:
FolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_) : FolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_, MainWindow* mw, bool requireExistingFolder) :
FileOrFolderChooserItem(option_, lineEdit_, browsePushButton_) { FileOrFolderChooserItem(option_, lineEdit_, browsePushButton_, mw, requireExistingFolder, false) {
QObject::connect(browsePushButton, SIGNAL(released()), this, SLOT(pushButtonReleased())); QObject::connect(browsePushButton, SIGNAL(released()), this, SLOT(pushButtonReleased()));
} }
}; };
class ComboBoxItem : public MainWindowItem { class ComboBoxItem : public MainWindowItem {
public: public:
QComboBox* comboBox; QComboBox* comboBox;
ComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : MainWindowItem(option_,comboBox_,QString()), comboBox(comboBox_){}; ComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : MainWindowItem(option_,comboBox_,QString()), comboBox(comboBox_){}
virtual ~ComboBoxItem(){} virtual ~ComboBoxItem(){}
virtual void installListeners(MainWindow *mainWindow); virtual void installListeners(MainWindow *mainWindow);
virtual void loadFromConfigOption()=0; virtual void loadFromConfigOption()=0;
virtual void saveToStringStream(std::stringstream& out)=0; virtual void saveToStringStream(std::stringstream& out)=0;
virtual bool isValid() { return true; } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return ; }
}; };
class LogDestinationComboBoxItem : public ComboBoxItem { class LogDestinationComboBoxItem : public ComboBoxItem {
public: public:
@ -237,13 +249,13 @@ public:
optionValue=logDest; optionValue=logDest;
MainWindowItem::saveToStringStream(out); MainWindowItem::saveToStringStream(out);
} }
virtual bool isValid() { return true; } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
Q_OBJECT Q_OBJECT
}; };
class LogLevelComboBoxItem : public ComboBoxItem { class LogLevelComboBoxItem : public ComboBoxItem {
public: public:
LogLevelComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {}; LogLevelComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {}
virtual ~LogLevelComboBoxItem(){} virtual ~LogLevelComboBoxItem(){}
virtual void loadFromConfigOption(){ virtual void loadFromConfigOption(){
MainWindowItem::loadFromConfigOption(); MainWindowItem::loadFromConfigOption();
@ -254,11 +266,11 @@ public:
optionValue=comboBox->currentText().toStdString(); optionValue=comboBox->currentText().toStdString();
MainWindowItem::saveToStringStream(out); MainWindowItem::saveToStringStream(out);
} }
virtual bool isValid() { return true; } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
}; };
class SignatureTypeComboBoxItem : public ComboBoxItem { class SignatureTypeComboBoxItem : public ComboBoxItem {
public: public:
SignatureTypeComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {}; SignatureTypeComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {}
virtual ~SignatureTypeComboBoxItem(){} virtual ~SignatureTypeComboBoxItem(){}
virtual void loadFromConfigOption(){ virtual void loadFromConfigOption(){
MainWindowItem::loadFromConfigOption(); MainWindowItem::loadFromConfigOption();
@ -271,39 +283,42 @@ public:
optionValue=(unsigned short)selected; optionValue=(unsigned short)selected;
MainWindowItem::saveToStringStream(out); MainWindowItem::saveToStringStream(out);
} }
virtual bool isValid() { return true; } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
}; };
class CheckBoxItem : public MainWindowItem { class CheckBoxItem : public MainWindowItem {
public: public:
QCheckBox* checkBox; QCheckBox* checkBox;
CheckBoxItem(ConfigOption option_, QCheckBox* checkBox_) : MainWindowItem(option_,checkBox_,QString()), checkBox(checkBox_){}; CheckBoxItem(ConfigOption option_, QCheckBox* checkBox_) : MainWindowItem(option_,checkBox_,QString()), checkBox(checkBox_){}
virtual ~CheckBoxItem(){} virtual ~CheckBoxItem(){}
virtual void installListeners(MainWindow *mainWindow); virtual void installListeners(MainWindow *mainWindow);
virtual void loadFromConfigOption(){ virtual void loadFromConfigOption(){
MainWindowItem::loadFromConfigOption(); MainWindowItem::loadFromConfigOption();
qDebug() << "setting value for checkbox " << checkBox->text(); //qDebug() << "setting value for checkbox " << checkBox->text();
checkBox->setChecked(boost::any_cast<bool>(optionValue)); checkBox->setChecked(boost::any_cast<bool>(optionValue));
} }
virtual void saveToStringStream(std::stringstream& out){ virtual void saveToStringStream(std::stringstream& out){
optionValue=checkBox->isChecked(); optionValue=checkBox->isChecked();
MainWindowItem::saveToStringStream(out); MainWindowItem::saveToStringStream(out);
} }
virtual bool isValid() { return true; } //virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
}; };
class BaseFormattedStringItem : public BaseStringItem { class BaseFormattedStringItem : public BaseStringItem {
public: public:
QString fieldNameTranslated; QString fieldNameTranslated;
BaseFormattedStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, QString requirementToBeValid_) : BaseFormattedStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, QString requirementToBeValid_, MainWindow* mw) :
BaseStringItem(option_, lineEdit_, requirementToBeValid_), fieldNameTranslated(fieldNameTranslated_) {}; BaseStringItem(option_, lineEdit_, requirementToBeValid_, mw), fieldNameTranslated(fieldNameTranslated_) {}
virtual ~BaseFormattedStringItem(){} virtual ~BaseFormattedStringItem(){}
virtual bool isValid()=0; //virtual bool isValid(bool & alreadyDisplayedIfWrong)=0;
}; };
class IntegerStringItem : public BaseFormattedStringItem { class IntegerStringItem : public BaseFormattedStringItem {
public: public:
IntegerStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) : IntegerStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be a valid integer.")) {}; BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be a valid integer."), mw) {}
virtual ~IntegerStringItem(){} virtual ~IntegerStringItem(){}
virtual bool isValid(){ virtual bool isValid(bool & alreadyDisplayedIfWrong){
bool correct = BaseFormattedStringItem::isValid(alreadyDisplayedIfWrong);
if(!correct)return false;
alreadyDisplayedIfWrong = false;
auto str=lineEdit->text(); auto str=lineEdit->text();
bool ok; bool ok;
str.toInt(&ok); str.toInt(&ok);
@ -314,10 +329,13 @@ public:
}; };
class UShortStringItem : public BaseFormattedStringItem { class UShortStringItem : public BaseFormattedStringItem {
public: public:
UShortStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) : UShortStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned short integer.")) {}; BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned short integer."), mw) {}
virtual ~UShortStringItem(){} virtual ~UShortStringItem(){}
virtual bool isValid(){ virtual bool isValid(bool & alreadyDisplayedIfWrong){
bool correct = BaseFormattedStringItem::isValid(alreadyDisplayedIfWrong);
if(!correct)return false;
alreadyDisplayedIfWrong = false;
auto str=lineEdit->text(); auto str=lineEdit->text();
bool ok; bool ok;
str.toUShort(&ok); str.toUShort(&ok);
@ -328,10 +346,13 @@ public:
}; };
class UInt32StringItem : public BaseFormattedStringItem { class UInt32StringItem : public BaseFormattedStringItem {
public: public:
UInt32StringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) : UInt32StringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned 32-bit integer.")) {}; BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned 32-bit integer."), mw) {}
virtual ~UInt32StringItem(){} virtual ~UInt32StringItem(){}
virtual bool isValid(){ virtual bool isValid(bool & alreadyDisplayedIfWrong){
bool correct = BaseFormattedStringItem::isValid(alreadyDisplayedIfWrong);
if(!correct)return false;
alreadyDisplayedIfWrong = false;
auto str=lineEdit->text(); auto str=lineEdit->text();
bool ok; bool ok;
str.toUInt(&ok); str.toUInt(&ok);
@ -342,10 +363,13 @@ public:
}; };
class UInt16StringItem : public BaseFormattedStringItem { class UInt16StringItem : public BaseFormattedStringItem {
public: public:
UInt16StringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) : UInt16StringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned 16-bit integer.")) {}; BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned 16-bit integer."), mw) {}
virtual ~UInt16StringItem(){} virtual ~UInt16StringItem(){}
virtual bool isValid(){ virtual bool isValid(bool & alreadyDisplayedIfWrong){
bool correct = BaseFormattedStringItem::isValid(alreadyDisplayedIfWrong);
if(!correct)return false;
alreadyDisplayedIfWrong = false;
auto str=lineEdit->text(); auto str=lineEdit->text();
bool ok; bool ok;
str.toUShort(&ok); str.toUShort(&ok);
@ -356,14 +380,14 @@ public:
}; };
class IPAddressStringItem : public BaseFormattedStringItem { class IPAddressStringItem : public BaseFormattedStringItem {
public: public:
IPAddressStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) : IPAddressStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be an IPv4 address")) {}; BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be an IPv4 address"), mw) {}
virtual bool isValid(){return true;}//todo //virtual bool isValid(bool & alreadyDisplayedIfWrong){return true;}//todo
}; };
class TCPPortStringItem : public UShortStringItem { class TCPPortStringItem : public UShortStringItem {
public: public:
TCPPortStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) : TCPPortStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
UShortStringItem(option_, lineEdit_, fieldNameTranslated_) {}; UShortStringItem(option_, lineEdit_, fieldNameTranslated_,mw) {}
}; };
namespace Ui { namespace Ui {
@ -386,6 +410,7 @@ class DelayedSaveManagerImpl;
class MainWindow : public QMainWindow { class MainWindow : public QMainWindow {
Q_OBJECT Q_OBJECT
private: private:
std::string currentLocalDestinationB32;
std::shared_ptr<std::iostream> logStream; std::shared_ptr<std::iostream> logStream;
DelayedSaveManagerImpl* delayedSaveManagerPtr; DelayedSaveManagerImpl* delayedSaveManagerPtr;
DelayedSaveManager::DATA_SERIAL_TYPE dataSerial; DelayedSaveManager::DATA_SERIAL_TYPE dataSerial;
@ -395,7 +420,7 @@ public:
void setI2PController(i2p::qt::Controller* controller_); void setI2PController(i2p::qt::Controller* controller_);
void highlightWrongInput(QString warningText, QWidget* widgetToFocus); void highlightWrongInput(QString warningText, WrongInputPageEnum inputPage, QWidget* widgetToFocus);
//typedef std::function<QString ()> DefaultValueGetter; //typedef std::function<QString ()> DefaultValueGetter;
@ -428,7 +453,10 @@ private slots:
void runPeerTest(); void runPeerTest();
void enableTransit(); void enableTransit();
void disableTransit(); void disableTransit();
public slots: public slots:
void syncLogLevel (int comboBoxIndex);
void showStatus_local_destinations_Page(); void showStatus_local_destinations_Page();
void showStatus_leasesets_Page(); void showStatus_leasesets_Page();
void showStatus_tunnels_Page(); void showStatus_tunnels_Page();
@ -442,6 +470,7 @@ public slots:
void showTunnelsPage(); void showTunnelsPage();
void showRestartPage(); void showRestartPage();
void showQuitPage(); void showQuitPage();
void showAboutBox(const QString & href);
private: private:
StatusPage statusPage; StatusPage statusPage;
@ -499,7 +528,7 @@ protected:
//LogDestinationComboBoxItem* logOption; //LogDestinationComboBoxItem* logOption;
FileChooserItem* logFileNameOption; FileChooserItem* logFileNameOption;
FileChooserItem* initFileChooser(ConfigOption option, QLineEdit* fileNameLineEdit, QPushButton* fileBrowsePushButton); FileChooserItem* initFileChooser(ConfigOption option, QLineEdit* fileNameLineEdit, QPushButton* fileBrowsePushButton, bool requireExistingFile, bool readOnly=false);
void initFolderChooser(ConfigOption option, QLineEdit* folderLineEdit, QPushButton* folderBrowsePushButton); void initFolderChooser(ConfigOption option, QLineEdit* folderLineEdit, QPushButton* folderBrowsePushButton);
//void initCombobox(ConfigOption option, QComboBox* comboBox); //void initCombobox(ConfigOption option, QComboBox* comboBox);
void initLogDestinationCombobox(ConfigOption option, QComboBox* comboBox); void initLogDestinationCombobox(ConfigOption option, QComboBox* comboBox);
@ -519,12 +548,12 @@ protected:
public slots: public slots:
/** returns false iff not valid items present and save was aborted */ /** returns false iff not valid items present and save was aborted */
bool saveAllConfigs(bool focusOnTunnel, std::string tunnelNameToFocus=""); bool saveAllConfigs(bool reloadAfterSave, FocusEnum focusOn, std::string tunnelNameToFocus="", QWidget* widgetToFocus=nullptr);
void reloadTunnelsConfigAndUI(std::string tunnelNameToFocus); void reloadTunnelsConfigAndUI(std::string tunnelNameToFocus, QWidget* widgetToFocus);
void reloadTunnelsConfigAndUI() { reloadTunnelsConfigAndUI("", nullptr); }
//focus none //focus none
void reloadTunnelsConfigAndUI() { reloadTunnelsConfigAndUI(""); } void reloadTunnelsConfigAndUI_QString(QString tunnelNameToFocus);
void reloadTunnelsConfigAndUI_QString(const QString tunnelNameToFocus);
void addServerTunnelPushButtonReleased(); void addServerTunnelPushButtonReleased();
void addClientTunnelPushButtonReleased(); void addClientTunnelPushButtonReleased();
@ -629,7 +658,7 @@ private:
tunnelConfigs.erase(it); tunnelConfigs.erase(it);
delete tc; delete tc;
} }
saveAllConfigs(false); saveAllConfigs(true, FocusEnum::noFocus);
} }
std::string GenerateNewTunnelName() { std::string GenerateNewTunnelName() {
@ -666,7 +695,7 @@ private:
sigType, sigType,
cryptoType); cryptoType);
saveAllConfigs(true, name); saveAllConfigs(true, FocusEnum::focusOnTunnelName, name);
} }
void CreateDefaultServerTunnel() {//TODO dedup default values with ReadTunnelsConfig() and with ClientContext.cpp::ReadTunnels () void CreateDefaultServerTunnel() {//TODO dedup default values with ReadTunnelsConfig() and with ClientContext.cpp::ReadTunnels ()
@ -704,7 +733,7 @@ private:
cryptoType); cryptoType);
saveAllConfigs(true, name); saveAllConfigs(true, FocusEnum::focusOnTunnelName, name);
} }
void ReadTunnelsConfig() //TODO deduplicate the code with ClientContext.cpp::ReadTunnels () void ReadTunnelsConfig() //TODO deduplicate the code with ClientContext.cpp::ReadTunnels ()
@ -747,16 +776,13 @@ private:
std::string dest; std::string dest;
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) { if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION); dest = section.second.get<std::string> (I2P_CLIENT_TUNNEL_DESTINATION);
std::cout << "had read tunnel dest: " << dest << std::endl;
} }
int port = section.second.get<int> (I2P_CLIENT_TUNNEL_PORT); int port = section.second.get<int> (I2P_CLIENT_TUNNEL_PORT);
std::cout << "had read tunnel port: " << port << std::endl;
// optional params // optional params
std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, ""); std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");
std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1"); std::string address = section.second.get (I2P_CLIENT_TUNNEL_ADDRESS, "127.0.0.1");
int cryptoType = section.second.get<int>(I2P_CLIENT_TUNNEL_CRYPTO_TYPE, 0); int cryptoType = section.second.get<int>(I2P_CLIENT_TUNNEL_CRYPTO_TYPE, 0);
int destinationPort = section.second.get<int>(I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0); int destinationPort = section.second.get<int>(I2P_CLIENT_TUNNEL_DESTINATION_PORT, 0);
std::cout << "had read tunnel destinationPort: " << destinationPort << std::endl;
i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256); i2p::data::SigningKeyType sigType = section.second.get (I2P_CLIENT_TUNNEL_SIGNATURE_TYPE, i2p::data::SIGNING_KEY_TYPE_ECDSA_SHA256_P256);
// I2CP // I2CP
std::map<std::string, std::string> options; std::map<std::string, std::string> options;

View File

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>908</width> <width>908</width>
<height>554</height> <height>604</height>
</rect> </rect>
</property> </property>
<property name="minimumSize"> <property name="minimumSize">
@ -35,13 +35,13 @@
<property name="minimumSize"> <property name="minimumSize">
<size> <size>
<width>908</width> <width>908</width>
<height>550</height> <height>600</height>
</size> </size>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>908</width> <width>908</width>
<height>550</height> <height>600</height>
</size> </size>
</property> </property>
<widget class="QWidget" name="horizontalLayoutWidget"> <widget class="QWidget" name="horizontalLayoutWidget">
@ -50,7 +50,7 @@
<x>10</x> <x>10</x>
<y>10</y> <y>10</y>
<width>888</width> <width>888</width>
<height>531</height> <height>596</height>
</rect> </rect>
</property> </property>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
@ -58,7 +58,7 @@
<enum>QLayout::SetMaximumSize</enum> <enum>QLayout::SetMaximumSize</enum>
</property> </property>
<item> <item>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0,0,0,0"> <layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0,0,0,0,0">
<property name="sizeConstraint"> <property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum> <enum>QLayout::SetMinimumSize</enum>
</property> </property>
@ -67,7 +67,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>170</width> <width>170</width>
<height>496</height> <height>596</height>
</rect> </rect>
</property> </property>
<item> <item>
@ -172,6 +172,33 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLabel" name="aboutHrefLabel">
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="contextMenuPolicy">
<enum>Qt::NoContextMenu</enum>
</property>
<property name="toolTip">
<string>Show app name, version and build date</string>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;about:i2pd_qt&quot;&gt;&lt;span style=&quot;text-decoration: none; color:#a0a0a0;&quot;&gt;&lt;span style=&quot;font-weight: 500;&quot;&gt;i2pd_qt&lt;/span&gt;&lt;br/&gt;Version SHORT_VERSION · About...&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="margin">
<number>6</number>
</property>
<property name="indent">
<number>0</number>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -629,7 +656,7 @@
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
<width>713</width> <width>707</width>
<height>713</height> <height>713</height>
</size> </size>
</property> </property>
@ -648,8 +675,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>713</width> <width>707</width>
<height>531</height> <height>586</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_5"> <layout class="QVBoxLayout" name="verticalLayout_5">
@ -690,8 +717,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>711</width> <width>707</width>
<height>531</height> <height>586</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4_logViewer"> <layout class="QVBoxLayout" name="verticalLayout_4_logViewer">
@ -753,8 +780,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>711</width> <width>707</width>
<height>531</height> <height>586</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_4"> <layout class="QVBoxLayout" name="verticalLayout_4">
@ -798,8 +825,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>81</width> <width>693</width>
<height>28</height> <height>498</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -820,8 +847,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>711</width> <width>707</width>
<height>531</height> <height>586</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_6"> <layout class="QVBoxLayout" name="verticalLayout_6">
@ -903,8 +930,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>711</width> <width>707</width>
<height>531</height> <height>586</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_3"> <layout class="QVBoxLayout" name="verticalLayout_3">
@ -958,7 +985,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>711</width> <width>707</width>
<height>531</height> <height>531</height>
</rect> </rect>
</property> </property>

View File

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>711</width> <width>707</width>
<height>300</height> <height>300</height>
</rect> </rect>
</property> </property>
@ -24,7 +24,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>711</width> <width>707</width>
<height>301</height> <height>301</height>
</rect> </rect>
</property> </property>