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
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
endif
endif

View File

@ -64,7 +64,7 @@ call :BUILDING_XP
echo.
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

View File

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

View File

@ -11,6 +11,7 @@ ENV REPO_URL=${REPO_URL}
ENV I2PD_HOME="/home/i2pd"
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" \
&& adduser -S -h "$I2PD_HOME" i2pd \

View File

@ -2,7 +2,6 @@
COMMAND=/usr/local/bin/i2pd
# To make ports exposeable
# 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
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 ();
if (i2cpServer)
@ -820,7 +820,7 @@ namespace http {
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 ();
if (!sam) {
@ -1312,6 +1312,8 @@ namespace http {
void HTTPServer::Run ()
{
i2p::util::SetThreadName("Webconsole");
while (m_IsRunning)
{
try

View File

@ -98,6 +98,8 @@ namespace http
void ShowSAMSessions (std::stringstream& s);
void ShowI2PTunnels (std::stringstream& s);
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
} // i2p

View File

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

View File

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

View File

@ -638,7 +638,7 @@ namespace crypto
{
uint64_t buf[256];
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)
{
__asm__
@ -1316,6 +1316,8 @@ namespace crypto
#endif
}
// Noise
void NoiseSymmetricState::MixHash (const uint8_t * buf, size_t len)
{
SHA256_CTX ctx;
@ -1331,6 +1333,59 @@ namespace crypto
// 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
/* std::vector <std::unique_ptr<std::mutex> > m_OpenSSLMutexes;

View File

@ -318,6 +318,10 @@ namespace crypto
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
void InitCrypto (bool precomputation, bool aesni, bool avx, bool force);
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;
for (auto& it: m_PendingRoutingSessions)
if (it->GetOwner ()) // found established session
if (it->GetOwner () && m_RoutingSession->IsReadyToSend ()) // found established session
{
m_RoutingSession = it;
m_PendingRoutingSessions.clear ();
@ -309,7 +309,7 @@ namespace datagram
if (!found)
{
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);
}
}

View File

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

View File

@ -9,6 +9,7 @@
#include <string.h>
#include <openssl/sha.h>
#include "Log.h"
#include "util.h"
#include "Crypto.h"
#include "Elligator.h"
#include "Tag.h"
@ -45,13 +46,13 @@ namespace garlic
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++;
if (m_NextIndex >= 65535)
{
LogPrint (eLogError, "Garlic: Tagset ", GetTagSetID (), " is empty");
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 ();
}
@ -153,29 +154,12 @@ namespace garlic
GarlicRoutingSession (owner, attachLeaseSet)
{
RAND_bytes (m_PaddingSizes, 32); m_NextPaddingSize = 0;
ResetKeys ();
}
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)
{
memset (nonce, 0, 4);
@ -236,7 +220,7 @@ namespace garlic
if (!GetOwner ()) return false;
// we are Bob
// 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))
{
@ -315,7 +299,10 @@ namespace garlic
break;
case eECIESx25519BlkNextKey:
LogPrint (eLogDebug, "Garlic: next key");
if (receiveTagset)
HandleNextKey (buf + offset, size, receiveTagset);
else
LogPrint (eLogError, "Garlic: Unexpected next key block");
break;
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)
{
ResetKeys ();
// we are Alice, bpk is m_RemoteStaticKey
size_t offset = 0;
if (!GenerateEphemeralKeysAndEncode (out + offset))
@ -459,7 +445,7 @@ namespace garlic
offset += 32;
// 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)
uint8_t sharedSecret[32];
m_EphemeralKeys->Agree (m_RemoteStaticKey, sharedSecret); // x25519(aesk, bpk)
@ -508,6 +494,31 @@ namespace garlic
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)
{
// we are Bob
@ -609,18 +620,15 @@ namespace garlic
}
buf += 32; len -= 32;
// 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 (bepk, 32); // h = SHA256(h || bepk)
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)
MixKey (sharedSecret);
GetOwner ()->Decrypt (bepk, sharedSecret, nullptr, i2p::data::CRYPTO_KEY_TYPE_ECIES_X25519_AEAD); // x25519 (ask, bepk)
MixKey (sharedSecret);
}
uint8_t nonce[12];
CreateNonce (0, nonce);
// calculate hash for zero length
@ -636,6 +644,7 @@ namespace garlic
i2p::crypto::HKDF (m_CK, nullptr, 0, "", keydata); // keydata = HKDF(chainKey, ZEROLEN, "", 64)
if (m_State == eSessionStateNewSessionSent)
{
// only first time, then we keep using existing tagsets
// k_ab = keydata[0:31], k_ba = keydata[32:63]
m_SendTagset = std::make_shared<RatchetTagSet>(shared_from_this ());
m_SendTagset->DHInitialize (m_CK, keydata); // tagset_ab = DH_INITIALIZE(chainKey, k_ab)
@ -657,11 +666,10 @@ namespace garlic
if (m_State == eSessionStateNewSessionSent)
{
m_State = eSessionStateEstablished;
m_EphemeralKeys = nullptr;
//m_EphemeralKeys = nullptr; // TODO: delete after a while
m_SessionCreatedTimestamp = i2p::util::GetSecondsSinceEpoch ();
GetOwner ()->AddECIESx25519Session (m_RemoteStaticKey, shared_from_this ());
}
memcpy (m_H, h, 32); // restore m_H
HandlePayload (buf, len - 16, nullptr, 0);
// we have received reply to NS with LeaseSet in it
@ -677,6 +685,13 @@ namespace garlic
auto index = m_SendTagset->GetNextIndex ();
CreateNonce (index, nonce); // tag's index
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);
// ad = The session tag, 8 bytes
// ciphertext = ENCRYPT(k, n, payload, ad)
@ -714,21 +729,20 @@ namespace garlic
{
if (receiveTagset->GetNextIndex () - index < GetOwner ()->GetNumRatchetInboundTags ()/2)
moreTags = GetOwner ()->GetNumRatchetInboundTags ();
index -= GetOwner ()->GetNumRatchetInboundTags (); // trim behind
}
else
{
moreTags = ECIESX25519_MIN_NUM_GENERATED_TAGS + (index >> 2); // N/4
if (moreTags > ECIESX25519_MAX_NUM_GENERATED_TAGS) moreTags = ECIESX25519_MAX_NUM_GENERATED_TAGS;
moreTags -= (receiveTagset->GetNextIndex () - index);
index -= ECIESX25519_MAX_NUM_GENERATED_TAGS; // trim behind
}
if (moreTags > 0)
{
GenerateMoreReceiveTags (receiveTagset, moreTags);
index -= (moreTags >> 1); // /2
if (index > 0)
receiveTagset->SetTrimBehind (index);
}
}
return true;
}
@ -746,12 +760,16 @@ namespace garlic
[[fallthrough]];
#endif
case eSessionStateEstablished:
if (HandleExistingSessionMessage (buf, len, receiveTagset, index)) return true;
// check NSR just in case
if (receiveTagset->IsNS ())
{
// our of sequence NSR
LogPrint (eLogDebug, "Garlic: check for out of order NSR with index ", index);
if (receiveTagset->GetNextIndex () - index < ECIESX25519_NSR_NUM_GENERATED_TAGS/2)
GenerateMoreReceiveTags (receiveTagset, ECIESX25519_NSR_NUM_GENERATED_TAGS);
return HandleNewOutgoingSessionReply (buf, len);
}
else
return HandleExistingSessionMessage (buf, len, receiveTagset, index);
case eSessionStateNew:
return HandleNewIncomingSession (buf, len);
case eSessionStateNewSessionSent:
@ -763,6 +781,28 @@ namespace garlic
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)
{
auto payload = CreatePayload (msg, m_State != eSessionStateEstablished);
@ -799,6 +839,11 @@ namespace garlic
return nullptr;
len += 96;
break;
case eSessionStateForRouter:
if (!NewOutgoingMessageForRouter (payload.data (), payload.size (), buf, m->maxLen))
return nullptr;
len += 48;
break;
default:
return nullptr;
}
@ -809,9 +854,9 @@ namespace garlic
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);
}
@ -1014,7 +1059,14 @@ namespace garlic
if (GetOwner ())
{
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_TAGSET_MAX_NUM_TAGS = 8192; // number of tags we request new tagset after
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 size_t ECIESX25519_OPTIMAL_PAYLOAD_SIZE = 1912; // 1912 = 1956 /* to fit 2 tunnel messages */
@ -45,6 +45,7 @@ namespace garlic
public:
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 NextSessionTagRatchet ();
@ -92,6 +93,8 @@ namespace garlic
NSRatchetTagSet (std::shared_ptr<ECIESX25519AEADRatchetSession> session):
RatchetTagSet (session), m_DummySession (session) {};
bool IsNS () const { return true; };
private:
std::shared_ptr<ECIESX25519AEADRatchetSession> m_DummySession; // we need a strong pointer for NS
@ -140,7 +143,8 @@ namespace garlic
eSessionStateNewSessionSent,
eSessionStateNewSessionReplySent,
eSessionStateEstablished,
eSessionStateOneTime
eSessionStateOneTime,
eSessionStateForRouter
};
struct DHRatchet
@ -157,8 +161,9 @@ namespace garlic
~ECIESX25519AEADRatchetSession ();
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> 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; }
void SetRemoteStaticKey (const uint8_t * key) { memcpy (m_RemoteStaticKey, key, 32); }
@ -178,7 +183,6 @@ namespace garlic
private:
void ResetKeys ();
void CreateNonce (uint64_t seqn, uint8_t * nonce);
bool GenerateEphemeralKeysAndEncode (uint8_t * buf); // buf is 32 bytes
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 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 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);
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
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
// 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
auto decryption = it->second;
@ -499,28 +519,11 @@ namespace garlic
decryption->SetIV (iv);
decryption->Decrypt (buf + 32, length - 32, buf + 32);
HandleAESBlock (buf + 32, length - 32, decryption, msg->from);
found = true;
}
else
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
{
// AES tag not found. Handle depending on encryption type
@ -541,8 +544,33 @@ namespace garlic
// otherwise ECIESx25519
auto session = std::make_shared<ECIESX25519AEADRatchetSession> (this, false); // incoming
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");
}
}
else
LogPrint (eLogError, "Garlic: Failed to decrypt message");
}
@ -716,7 +744,7 @@ namespace garlic
{
auto session = std::make_shared<ECIESX25519AEADRatchetSession>(this, false);
session->SetRemoteStaticKey (router->GetIdentity ()->GetEncryptionPublicKey ());
return session->WrapOneTimeMessage (msg);
return session->WrapOneTimeMessage (msg, true);
}
else
{
@ -844,6 +872,8 @@ namespace garlic
}
if (numExpiredTags > 0)
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)
@ -1029,11 +1059,13 @@ namespace garlic
}
}
void GarlicDestination::AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset)
uint64_t GarlicDestination::AddECIESx25519SessionNextTag (RatchetTagSetPtr tagset)
{
auto index = tagset->GetNextIndex ();
uint64_t tag = tagset->GetNextSessionTag ();
if (tag)
m_ECIESx25519Tags.emplace (tag, ECIESX25519AEADRatchetIndexTagset{index, tagset});
return tag;
}
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
virtual bool SubmitSessionKey (const uint8_t * key, const uint8_t * tag); // from different thread
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 RemoveECIESx25519Session (const uint8_t * staticKey);
void HandleECIESx25519GarlicClove (const uint8_t * buf, size_t len);
@ -285,6 +285,7 @@ namespace garlic
int m_NumRatchetInboundTags;
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
RatchetTagSetPtr m_LastTagset; // tagset last message came for
// DeliveryStatus
std::mutex m_DeliveryStatusSessionsMutex;
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 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)
{
__asm__

View File

@ -7,6 +7,7 @@
*/
#include "Log.h"
#include "util.h"
//for std::transform
#include <algorithm>
@ -179,6 +180,8 @@ namespace log {
void Log::Run ()
{
i2p::util::SetThreadName("Logging");
Reopen ();
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)
{
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)
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);
i2p::crypto::InitNoiseXKState (*this, rs);
// h = SHA256(h || epub)
MixHash (epub, 32);
// x25519 between pub and priv

View File

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

View File

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

View File

@ -19,7 +19,7 @@
#include "version.h"
#include "Log.h"
#include "Family.h"
#include "TunnelConfig.h"
#include "ECIESX25519AEADRatchetSession.h"
#include "RouterContext.h"
namespace i2p
@ -45,15 +45,15 @@ namespace i2p
if (IsECIES ())
{
auto initState = new i2p::crypto::NoiseSymmetricState ();
i2p::tunnel::InitBuildRequestRecordNoiseState (*initState);
initState->MixHash (GetIdentity ()->GetEncryptionPublicKey (), 32); // h = SHA256(h || hepk)
i2p::crypto::InitNoiseNState (*initState, GetIdentity ()->GetEncryptionPublicKey ());
m_InitialNoiseState.reset (initState);
}
}
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 ();
NewRouterInfo ();
}
@ -591,7 +591,8 @@ namespace i2p
// update keys
LogPrint (eLogInfo, "Router: router keys are obsolete. Creating new");
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 ();
}
// read NTCP2 keys if available
@ -674,6 +675,20 @@ namespace i2p
void RouterContext::ProcessGarlicMessage (std::shared_ptr<I2NPMessage> msg)
{
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);
}

View File

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

View File

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

View File

@ -75,9 +75,11 @@ namespace transport
std::shared_ptr<I2NPMessage> msg;
int nextFragmentNum;
uint32_t lastFragmentInsertTime; // in seconds
uint64_t receivedFragmentsBits;
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);
};
@ -109,7 +111,7 @@ namespace transport
private:
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 ProcessFragments (uint8_t * buf);
void ProcessSentMessageAck (uint32_t msgID);

View File

@ -701,7 +701,7 @@ namespace stream
size++; // resend delay
htobe16buf (packet + size, PACKET_FLAG_CLOSE | PACKET_FLAG_SIGNATURE_INCLUDED);
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
size += 2; // options size
uint8_t * signature = packet + size;

View File

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

View File

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

View File

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

View File

@ -127,10 +127,9 @@ namespace tunnel
void TunnelHopConfig::EncryptECIES (std::shared_ptr<i2p::crypto::CryptoKeyEncryptor>& encryptor,
const uint8_t * plainText, uint8_t * encrypted, BN_CTX * ctx)
{
InitBuildRequestRecordNoiseState (*this);
uint8_t hepk[32];
encryptor->Encrypt (nullptr, hepk, nullptr, false);
MixHash (hepk, 32); // h = SHA256(h || hepk)
i2p::crypto::InitNoiseNState (*this, hepk);
auto ephemeralKeys = i2p::transport::transports.GetNextX25519KeysPair ();
memcpy (encrypted, ephemeralKeys->GetPublicKey (), 32);
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)
}
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);
};
void InitBuildRequestRecordNoiseState (i2p::crypto::NoiseSymmetricState& state);
class TunnelConfig
{
public:

View File

@ -235,7 +235,7 @@ namespace tunnel
for (const auto& it : m_InboundTunnels)
if (it->IsEstablished ()) num++;
}
if (!num && !m_OutboundTunnels.empty ())
if (!num && !m_OutboundTunnels.empty () && m_NumOutboundHops > 0)
{
for (auto it: m_OutboundTunnels)
{
@ -559,7 +559,7 @@ namespace tunnel
{
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);
newTunnel->SetTunnelPool (shared_from_this ());
@ -574,8 +574,12 @@ namespace tunnel
void TunnelPool::CreatePairedInboundTunnel (std::shared_ptr<OutboundTunnel> outboundTunnel)
{
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 ());
if (tunnel->IsEstablished ()) // zero hops
TunnelCreated (tunnel);
}
void TunnelPool::SetCustomPeerSelector(ITunnelPeerSelector * selector)

View File

@ -13,6 +13,15 @@
#include "util.h"
#include "Log.h"
#if not defined (__FreeBSD__)
#include <pthread.h>
#endif
#if defined(__OpenBSD__) || defined(__FreeBSD__)
#include <pthread_np.h>
#endif
#ifdef _WIN32
#include <stdlib.h>
#include <string.h>
@ -32,7 +41,7 @@
// inet_pton exists Windows since Vista, but XP doesn't have that function!
// This function was written by Petar Korponai?. See http://stackoverflow.com/questions/15660203/inet-pton-identifier-not-found
int inet_pton_xp(int af, const char *src, void *dst)
int inet_pton_xp (int af, const char *src, void *dst)
{
struct sockaddr_storage ss;
int size = sizeof (ss);
@ -94,6 +103,8 @@ namespace util
void RunnableService::Run ()
{
SetThreadName(m_Name.c_str());
while (m_IsRunning)
{
try
@ -107,10 +118,20 @@ 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
{
#ifdef _WIN32
bool IsWindowsXPorLater()
bool IsWindowsXPorLater ()
{
static bool isRequested = false;
static bool isXP = false;
@ -129,7 +150,7 @@ namespace net
return isXP;
}
int GetMTUWindowsIpv4(sockaddr_in inputAddress, int fallback)
int GetMTUWindowsIpv4 (sockaddr_in inputAddress, int fallback)
{
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
@ -183,7 +204,7 @@ namespace net
return fallback;
}
int GetMTUWindowsIpv6(sockaddr_in6 inputAddress, int fallback)
int GetMTUWindowsIpv6 (sockaddr_in6 inputAddress, int fallback)
{
ULONG outBufLen = 0;
PIP_ADAPTER_ADDRESSES pAddresses = nullptr;
@ -248,7 +269,7 @@ namespace net
return fallback;
}
int GetMTUWindows(const boost::asio::ip::address& localAddress, int fallback)
int GetMTUWindows (const boost::asio::ip::address& localAddress, int fallback)
{
#ifdef UNICODE
string localAddress_temporary = localAddress.to_string();
@ -280,7 +301,7 @@ namespace net
}
}
#else // assume unix
int GetMTUUnix(const boost::asio::ip::address& localAddress, int fallback)
int GetMTUUnix (const boost::asio::ip::address& localAddress, int fallback)
{
ifaddrs* ifaddr, *ifa = nullptr;
if(getifaddrs(&ifaddr) == -1)
@ -335,7 +356,7 @@ namespace net
}
#endif // _WIN32
int GetMTU(const boost::asio::ip::address& localAddress)
int GetMTU (const boost::asio::ip::address& localAddress)
{
int fallback = localAddress.is_v6 () ? 1280 : 620; // fallback MTU
@ -347,7 +368,7 @@ namespace net
return fallback;
}
const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6)
const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6)
{
#ifdef _WIN32
LogPrint(eLogError, "NetIface: cannot get address by interface name, not implemented on WIN32");
@ -395,7 +416,7 @@ namespace net
#endif
}
bool IsInReservedRange(const boost::asio::ip::address& host) {
bool IsInReservedRange (const boost::asio::ip::address& host) {
// https://en.wikipedia.org/wiki/Reserved_IP_addresses
if(host.is_v4())
{

View File

@ -168,11 +168,27 @@ namespace util
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
{
int GetMTU (const boost::asio::ip::address& localAddress);
const boost::asio::ip::address GetInterfaceAddress(const std::string & ifname, bool ipv6=false);
bool IsInReservedRange(const boost::asio::ip::address& host);
const boost::asio::ip::address GetInterfaceAddress (const std::string & ifname, bool ipv6=false);
bool IsInReservedRange (const boost::asio::ip::address& host);
}
}
}

View File

@ -748,6 +748,8 @@ namespace client
void AddressBookSubscription::CheckUpdates ()
{
i2p::util::SetThreadName("Addressbook");
bool result = MakeRequest ();
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,
std::shared_ptr<const i2p::data::IdentityEx> identity, bool isPublic, const std::map<std::string, std::string>& 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 ();
m_Owner = nullptr;
m_LeaseSetCreationTimer.cancel ();
}
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)
{
if (m_IsCreatingLeaseSet)
{
LogPrint (eLogInfo, "I2CP: LeaseSet is being created");
return;
}
uint8_t priv[256] = {0};
i2p::data::LocalLeaseSet ls (m_Identity, priv, tunnels); // we don't care about encryption key, we need leases only
m_LeaseSetExpirationTime = ls.GetExpirationTime ();
@ -94,15 +101,28 @@ namespace client
uint16_t sessionID = m_Owner->GetSessionID ();
if (sessionID != 0xFFFF)
{
m_IsCreatingLeaseSet = true;
htobe16buf (leases - 3, sessionID);
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_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)
{
m_IsCreatingLeaseSet = false;
m_LeaseSetCreationTimer.cancel ();
auto ls = std::make_shared<i2p::data::LocalLeaseSet> (m_Identity, buf, len);
ls->SetExpirationTime (m_LeaseSetExpirationTime);
SetLeaseSet (ls);
@ -110,6 +130,8 @@ namespace client
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) ?
std::make_shared<i2p::data::LocalEncryptedLeaseSet2> (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_MAX_MESSAGE_LENGTH = 65535;
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_TYPE_OFFSET = I2CP_HEADER_LENGTH_OFFSET + 4;
@ -109,6 +110,8 @@ namespace client
std::shared_ptr<i2p::crypto::ECIESX25519AEADRatchetDecryptor> m_ECIESx25519Decryptor;
uint8_t m_ECIESx25519PrivateKey[32];
uint64_t m_LeaseSetExpirationTime;
bool m_IsCreatingLeaseSet;
boost::asio::deadline_timer m_LeaseSetCreationTimer;
};
class RunnableI2CPDestination: private i2p::util::RunnableService, public I2CPDestination

View File

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

View File

@ -708,16 +708,16 @@ namespace client
auto session = m_Owner.FindSession(m_ID);
auto dest = session == nullptr ? context.GetSharedLocalDestination() : session->localDestination;
if (name == "ME")
SendNamingLookupReply (dest->GetIdentity ());
SendNamingLookupReply (name, dest->GetIdentity ());
else if ((identity = context.GetAddressBook ().GetFullAddress (name)) != nullptr)
SendNamingLookupReply (identity);
SendNamingLookupReply (name, identity);
else if ((addr = context.GetAddressBook ().GetAddress (name)))
{
if (addr->IsIdentHash ())
{
auto leaseSet = dest->FindLeaseSet (addr->identHash);
if (leaseSet)
SendNamingLookupReply (leaseSet->GetIdentity ());
SendNamingLookupReply (name, leaseSet->GetIdentity ());
else
dest->RequestDestination (addr->identHash,
std::bind (&SAMSocket::HandleNamingLookupLeaseSetRequestComplete,
@ -756,7 +756,7 @@ namespace client
if (leaseSet)
{
context.GetAddressBook ().InsertFullAddress (leaseSet->GetIdentity ());
SendNamingLookupReply (leaseSet->GetIdentity ());
SendNamingLookupReply (name, leaseSet->GetIdentity ());
}
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 ();
#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
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
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_I2P_ERROR[] = "DEST REPLY RESULT=I2P_ERROR\n";
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_RAW_RECEIVED[] = "RAW RECEIVED SIZE=%lu\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 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 HandleSessionReadinessCheckTimer (const boost::system::error_code& ecode);
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();
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
ctc->setdest(destinationLineEdit->text().toStdString());

View File

@ -2,6 +2,7 @@
#define DELAYEDSAVEMANAGER_H
#include "Saver.h"
#include "I2pdQtTypes.h"
class DelayedSaveManager
{
@ -12,13 +13,14 @@ public:
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
virtual bool appExiting()=0;
virtual bool needsFocusOnTunnel()=0;
virtual FocusEnum getFocusOn()=0;
virtual std::string& getTunnelNameToFocus()=0;
virtual QWidget* getWidgetToFocus()=0;
};
#endif // DELAYEDSAVEMANAGER_H

View File

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

View File

@ -7,14 +7,14 @@
#include <QMutex>
#include <QDateTime>
#include "mainwindow.h"
#include "I2pdQtTypes.h"
#include "DelayedSaveManager.h"
#include "Saver.h"
class DelayedSaveManagerImpl;
class Saver;
class DelayedSaveThread : public QThread
{
class DelayedSaveThread : public QThread {
Q_OBJECT
public:
@ -42,14 +42,17 @@ private:
volatile TIMESTAMP_TYPE wakeTime;
};
class DelayedSaveManagerImpl : public DelayedSaveManager
{
class DelayedSaveManagerImpl : public DelayedSaveManager {
FocusEnum focusOn;
std::string tunnelNameToFocus;
QWidget* widgetToFocus;
bool reloadAfterSave;
public:
DelayedSaveManagerImpl();
virtual ~DelayedSaveManagerImpl();
virtual void setSaver(Saver* saver);
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();
typedef DelayedSaveThread::TIMESTAMP_TYPE TIMESTAMP_TYPE;
@ -59,8 +62,10 @@ public:
Saver* getSaver();
static TIMESTAMP_TYPE getTime();
bool needsFocusOnTunnel();
std::string& getTunnelNameToFocus();
bool isReloadAfterSave() { return reloadAfterSave; }
FocusEnum getFocusOn() { return focusOn; }
std::string& getTunnelNameToFocus() { return tunnelNameToFocus; }
QWidget* getWidgetToFocus() { return widgetToFocus; }
private:
Saver* saver;
@ -74,9 +79,6 @@ private:
bool exiting;
DelayedSaveThread* thread;
void wakeThreadAndJoinThread();
bool focusOnTunnel;
std::string tunnelNameToFocus;
};
#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 <QObject>
#include <QString>
class QWidget;
#include "I2pdQtTypes.h"
class Saver : public QObject
{
@ -11,8 +14,8 @@ class Saver : public QObject
public:
Saver();
//false iff failures
virtual bool save(const bool focusOnTunnel, const std::string& tunnelNameToFocus)=0;
//FocusEnum::focusNone iff failures //??? wtf
virtual bool save(bool reloadAfterSave, const FocusEnum focusOn, const std::string& tunnelNameToFocus="", QWidget* widgetToFocus=nullptr)=0;
signals:
void reloadTunnelsConfigAndUISignal(const QString);

View File

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

View File

@ -19,7 +19,7 @@ class SaverImpl : public Saver
public:
SaverImpl(MainWindow *mainWindowPtr_, QList<MainWindowItem*> * configItems_, std::map<std::string,TunnelConfig*>* tunnelConfigs_);
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 setTunnelsConfPath(QString& tunconfpath_);
private:

View File

@ -124,6 +124,17 @@ protected:
if(!ok)return false;
ServerTunnelConfig* stc=tunnelConfig->asServerTunnelConfig();
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());
auto portStr=portLineEdit->text();

View File

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

View File

@ -119,6 +119,21 @@ protected:
public:
//returns false when invalid data at UI
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->setType(readTunnelTypeComboboxData());
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_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

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>
<widget class="QComboBox" name="logLevelComboBox">
<item>
<property name="text">
<string>None</string>
</property>
</item>
<item>
<property name="text">
<string>Error</string>

View File

@ -9,10 +9,15 @@ CONFIG += strict_c++ c++11
CONFIG(debug, debug|release) {
message(Debug build)
# do not redirect logging to std::ostream and to Log pane
DEFINES += DEBUG_WITH_DEFAULT_LOGGING
DEFINES += I2PD_QT_DEBUG
I2PDMAKE += DEBUG=yes
} else {
message(Release build)
DEFINES += I2PD_QT_RELEASE
I2PDMAKE += DEBUG=no
}
@ -36,7 +41,9 @@ SOURCES += DaemonQT.cpp mainwindow.cpp \
../../daemon/HTTPServer.cpp \
../../daemon/I2PControl.cpp \
../../daemon/i2pd.cpp \
../../daemon/UPnP.cpp
../../daemon/UPnP.cpp \
AboutDialog.cpp \
I2pdQtUtil.cpp
HEADERS += DaemonQT.h mainwindow.h \
ClientTunnelPane.h \
@ -59,8 +66,11 @@ HEADERS += DaemonQT.h mainwindow.h \
../../daemon/Daemon.h \
../../daemon/HTTPServer.h \
../../daemon/I2PControl.h \
../../daemon/UPnP.h
../../daemon/UPnP.h \
AboutDialog.h \
BuildDateTimeQt.h \
I2pdQtUtil.h \
I2pdQtTypes.h
INCLUDEPATH += ../../libi2pd
INCLUDEPATH += ../../libi2pd_client
@ -71,7 +81,8 @@ FORMS += mainwindow.ui \
tunnelform.ui \
statusbuttons.ui \
routercommandswidget.ui \
generalsettingswidget.ui
generalsettingswidget.ui \
AboutDialog.ui
LIBS += $$PWD/../../libi2pd.a $$PWD/../../libi2pdclient.a -lz
@ -79,13 +90,23 @@ libi2pd.commands = @echo Building i2pd libraries
libi2pd.target = $$PWD/../../libi2pd.a
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.depends = FORCE
cleani2pd.commands = cd $$PWD/../../ && CC=$$QMAKE_CC CXX=$$QMAKE_CXX $(MAKE) 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
QMAKE_EXTRA_TARGETS += cleani2pd i2pd libi2pd
CLEAN_DEPS += cleani2pd

View File

@ -1,4 +1,6 @@
#include "mainwindow.h"
#include "I2pdQtUtil.h"
#include "AboutDialog.h"
#include "ui_mainwindow.h"
#include "ui_statusbuttons.h"
#include "ui_routercommandswidget.h"
@ -8,6 +10,15 @@
#include <QTimer>
#include <QFile>
#include <QFileDialog>
#include <QApplication>
#include <QScreen>
#include <QStyleHints>
#include <QScreen>
#include <QWindow>
#include <assert.h>
#include "RouterContext.h"
#include "Config.h"
#include "FS.h"
@ -29,10 +40,12 @@
#include "DelayedSaveManagerImpl.h"
#include "SaverImpl.h"
std::string programOptionsWriterCurrentSection;
MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *parent) :
QMainWindow(parent)
,currentLocalDestinationB32("")
,logStream(logStream_)
,delayedSaveManagerPtr(new DelayedSaveManagerImpl())
,dataSerial(DelayedSaveManagerImpl::INITIAL_DATA_SERIAL)
@ -65,6 +78,10 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
statusButtonsUI->setupUi(ui->statusButtonsPane);
routerCommandsUI->setupUi(routerCommandsParent);
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();
ui->verticalLayout_2->addWidget(routerCommandsParent);
//,statusHtmlUI(new Ui::StatusHtmlPaneForm)
@ -76,15 +93,16 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
setWindowTitle(QApplication::translate("AppTitle","I2PD"));
//TODO handle resizes and change the below into resize() call
setFixedHeight(550);
ui->centralWidget->setFixedHeight(550);
constexpr auto WINDOW_HEIGHT = 610;
setFixedHeight(WINDOW_HEIGHT);
ui->centralWidget->setFixedHeight(WINDOW_HEIGHT);
onResize();
ui->stackedWidget->setCurrentIndex(0);
ui->settingsScrollArea->resize(uiSettings->settingsContentsQVBoxLayout->sizeHint().width()+10,380);
//QScrollBar* const barSett = ui->settingsScrollArea->verticalScrollBar();
int w = 683;
int h = 4550;
constexpr auto w = 683;
constexpr auto h = 4550;
ui->settingsContents->setFixedSize(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->setOpenLinks(false);
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);
ui->verticalLayout_2->addWidget(pageWithBackButton);
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->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->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))
initFileChooser( OPTION("","conf",[](){return "";}), uiSettings->configFileLineEdit, uiSettings->configFileBrowsePushButton);
initFileChooser( OPTION("","tunconf",[](){return "";}), uiSettings->tunnelsConfigFileLineEdit, uiSettings->tunnelsConfigFileBrowsePushButton);
initFileChooser( OPTION("","pidfile",[]{return "";}), uiSettings->pidFileLineEdit, uiSettings->pidFileBrowsePushButton);
initFileChooser( OPTION("","conf",[](){return "";}), uiSettings->configFileLineEdit, uiSettings->configFileBrowsePushButton, false, true);
initFileChooser( OPTION("","tunconf",[](){return "";}), uiSettings->tunnelsConfigFileLineEdit, uiSettings->tunnelsConfigFileBrowsePushButton, false);
initFileChooser( OPTION("","pidfile",[]{return "";}), uiSettings->pidFileLineEdit, uiSettings->pidFileBrowsePushButton, false);
uiSettings->logDestinationComboBox->clear();
uiSettings->logDestinationComboBox->insertItems(0, QStringList()
@ -169,9 +190,15 @@ MainWindow::MainWindow(std::shared_ptr<std::iostream> logStream_, QWidget *paren
<< QApplication::translate("MainWindow", "file", 0)
);
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);
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)"
initFolderChooser( OPTION("","datadir",[]{return "";}), uiSettings->dataFolderLineEdit, uiSettings->dataFolderBrowsePushButton);
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"));
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
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);
initStringBox( OPTION("httpproxy","inbound.length",[]{return "3";}), uiSettings->httpProxyInboundTunnelsLenLineEdit);
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);
initIPAddressBox( OPTION("socksproxy","address",[]{return "";}), uiSettings->socksProxyAddressLineEdit, tr("Socks proxy -> IP address"));
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);
initStringBox( OPTION("socksproxy","inbound.length",[]{return "";}), uiSettings->socksProxyInboundTunnelsLenLineEdit);
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"));
initTCPPortBox( OPTION("i2pcontrol","port",[]{return "7650";}), uiSettings->i2pControlPortLineEdit, tr("I2PControl -> Port"));
initStringBox( OPTION("i2pcontrol","password",[]{return "";}), uiSettings->i2pControlPasswordLineEdit);
initFileChooser( OPTION("i2pcontrol","cert",[]{return "i2pcontrol.crt.pem";}), uiSettings->i2pControlCertFileLineEdit, uiSettings->i2pControlCertFileBrowsePushButton);
initFileChooser( OPTION("i2pcontrol","key",[]{return "i2pcontrol.key.pem";}), uiSettings->i2pControlKeyFileLineEdit, uiSettings->i2pControlKeyFileBrowsePushButton);
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, true);
initCheckBox( OPTION("upnp","enabled",[]{return "true";}), uiSettings->enableUPnPCheckBox);
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("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);
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
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
//widgetlocks.add(new widgetlock(widget,lockbtn));
// #1593
#ifdef I2PD_QT_RELEASE
uiSettings->logDestComboEditPushButton->setEnabled(false);
#else
widgetlocks.add(new widgetlock(uiSettings->logDestinationComboBox,uiSettings->logDestComboEditPushButton));
#endif
widgetlocks.add(new widgetlock(uiSettings->logLevelComboBox,uiSettings->logLevelComboEditPushButton));
widgetlocks.add(new widgetlock(uiSettings->comboBox_httpPorxySignatureType,uiSettings->httpProxySignTypeComboEditPushButton));
widgetlocks.add(new widgetlock(uiSettings->comboBox_socksProxySignatureType,uiSettings->socksProxySignTypeComboEditPushButton));
@ -400,6 +435,29 @@ void MainWindow::showStatusPage(StatusPage newStatusPage){
}
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::showSettingsPage(){ui->stackedWidget->setCurrentIndex(2);setStatusButtonsVisible(false);}
void MainWindow::showTunnelsPage(){ui->stackedWidget->setCurrentIndex(3);setStatusButtonsVisible(false);}
@ -607,15 +665,15 @@ MainWindow::~MainWindow()
//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;
retVal=new FileChooserItem(option, fileNameLineEdit, fileBrowsePushButton);
retVal=new FileChooserItem(option, fileNameLineEdit, fileBrowsePushButton, this, requireExistingFile, readOnly);
MainWindowItem* super=retVal;
configItems.append(super);
return retVal;
}
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){
configItems.append(new ComboBoxItem(option, comboBox));
@ -631,25 +689,25 @@ void MainWindow::initSignatureTypeCombobox(ConfigOption option, QComboBox* combo
configItems.append(new SignatureTypeComboBoxItem(option, comboBox));
}
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){
configItems.append(new TCPPortStringItem(option, portLineEdit, fieldNameTranslated));
configItems.append(new TCPPortStringItem(option, portLineEdit, fieldNameTranslated, this));
}
void MainWindow::initCheckBox(ConfigOption option, QCheckBox* checkBox) {
configItems.append(new CheckBoxItem(option, checkBox));
}
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){
configItems.append(new UInt32StringItem(option, numberLineEdit, fieldNameTranslated));
configItems.append(new UInt32StringItem(option, numberLineEdit, fieldNameTranslated, this));
}
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){
configItems.append(new BaseStringItem(option, lineEdit, QString()));
configItems.append(new BaseStringItem(option, lineEdit, QString(), this));
}
NonGUIOptionItem* MainWindow::initNonGUIOption(ConfigOption option) {
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 */
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.");
programOptionsWriterCurrentSection="";
/*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) {
MainWindowItem* item = *it;
if(!item->isValid()){
highlightWrongInput(QApplication::tr("Invalid value for")+" "+item->getConfigOption().section+"::"+item->getConfigOption().option+". "+item->getRequirementToBeValid()+" "+cannotSaveSettings, item->getWidgetToFocus());
bool alreadyDisplayedIfWrong=false;
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;
}
}
delayedSaveManagerPtr->delayedSave(++dataSerial, focusOnTunnel, tunnelNameToFocus);
delayedSaveManagerPtr->delayedSave(reloadAfterSave, ++dataSerial, focusOn, tunnelNameToFocus, widgetToFocus);//TODO does dataSerial work? //FIXME
//onLoggingOptionsChange();
return true;
@ -764,11 +827,14 @@ bool MainWindow::saveAllConfigs(bool focusOnTunnel, std::string tunnelNameToFocu
void FileChooserItem::pushButtonReleased() {
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);
}
void FolderChooserItem::pushButtonReleased() {
QString fileName = lineEdit->text().trimmed();
assert(requireExistingFile);
fileName = QFileDialog::getExistingDirectory(nullptr, tr("Open Folder"), fileName);
if(fileName.length()>0)lineEdit->setText(fileName);
}
@ -776,6 +842,11 @@ void FolderChooserItem::pushButtonReleased() {
void BaseStringItem::installListeners(MainWindow *mainWindow) {
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) {
QObject::connect(comboBox, SIGNAL(currentIndexChanged(int)), mainWindow, SLOT(updated()));
}
@ -787,8 +858,9 @@ void MainWindow::updated() {
ui->wrongInputLabel->setVisible(false);
adjustSizesAccordingToWrongLabel();
applyTunnelsUiToConfigs();
saveAllConfigs(false);
bool correct = applyTunnelsUiToConfigs();
if(!correct) return;
saveAllConfigs(false, FocusEnum::noFocus);
}
void MainWindowItem::installListeners(MainWindow *mainWindow) {}
@ -863,11 +935,11 @@ bool MainWindow::applyTunnelsUiToConfigs() {
return true;
}
void MainWindow::reloadTunnelsConfigAndUI_QString(const QString tunnelNameToFocus) {
reloadTunnelsConfigAndUI(tunnelNameToFocus.toStdString());
void MainWindow::reloadTunnelsConfigAndUI_QString(QString tunnelNameToFocus) {
reloadTunnelsConfigAndUI(tunnelNameToFocus.toStdString(), nullptr);
}
void MainWindow::reloadTunnelsConfigAndUI(std::string tunnelNameToFocus) {
void MainWindow::reloadTunnelsConfigAndUI(std::string tunnelNameToFocus, QWidget* widgetToFocus) {
deleteTunnelForms();
for (std::map<std::string,TunnelConfig*>::iterator it=tunnelConfigs.begin(); it!=tunnelConfigs.end(); ++it) {
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);
if(it!=mainWindow->tunnelConfigs.end())mainWindow->tunnelConfigs.erase(it);
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){
@ -923,17 +997,68 @@ void MainWindow::anchorClickedHandler(const QUrl & link) {
qDebug()<<debugStr;
//QMessageBox::information(this, "", debugStr);
/* /?page=local_destination&b32=xx...xx */
QString str=link.toString();
#define LOCAL_DEST_B32_PREFIX "/?page=local_destination&b32="
static size_t LOCAL_DEST_B32_PREFIX_SZ=QString(LOCAL_DEST_B32_PREFIX).size();
if(str.startsWith(LOCAL_DEST_B32_PREFIX)) {
str = str.right(str.size()-LOCAL_DEST_B32_PREFIX_SZ);
qDebug () << "b32:" << str;
std::map<std::string, std::string> params;
i2p::http::URL url;
url.parse(str.toStdString());
url.parse_query(params);
const std::string page = params["page"];
const std::string cmd = params["cmd"];
if (page == "sam_session") {
pageWithBackButton->show();
textBrowser->hide();
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);
childTextBrowser->setHtml(QString::fromStdString(s.str()));
}
@ -944,39 +1069,58 @@ void MainWindow::backClickedFromChild() {
}
void MainWindow::adjustSizesAccordingToWrongLabel() {
constexpr auto HEIGHT = 581;
constexpr auto WIDTH = 707;
if(ui->wrongInputLabel->isVisible()) {
int dh = ui->wrongInputLabel->height()+ui->verticalLayout_7->layout()->spacing();
ui->verticalLayout_7->invalidate();
ui->wrongInputLabel->adjustSize();
ui->stackedWidget->adjustSize();
ui->stackedWidget->setFixedHeight(531-dh);
ui->settingsPage->setFixedHeight(531-dh);
ui->verticalLayoutWidget_4->setGeometry(QRect(0, 0, 711, 531-dh));
ui->stackedWidget->setFixedHeight(531-dh);
ui->settingsScrollArea->setFixedHeight(531-dh-settingsTitleLabelNominalHeight-ui->verticalLayout_4->spacing());
const auto height = HEIGHT - dh;
ui->stackedWidget->setFixedHeight(height);
ui->settingsPage->setFixedHeight(height);
ui->verticalLayoutWidget_4->setGeometry(QRect(0, 0, WIDTH, height));
ui->stackedWidget->setFixedHeight(height);
ui->settingsScrollArea->setFixedHeight(height-settingsTitleLabelNominalHeight-ui->verticalLayout_4->spacing());
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);
}else{
ui->verticalLayout_7->invalidate();
ui->wrongInputLabel->adjustSize();
ui->stackedWidget->adjustSize();
ui->stackedWidget->setFixedHeight(531);
ui->settingsPage->setFixedHeight(531);
ui->verticalLayoutWidget_4->setGeometry(QRect(0, 0, 711, 531));
ui->stackedWidget->setFixedHeight(531);
ui->settingsScrollArea->setFixedHeight(531-settingsTitleLabelNominalHeight-ui->verticalLayout_4->spacing());
ui->stackedWidget->setFixedHeight(HEIGHT);
ui->settingsPage->setFixedHeight(HEIGHT);
ui->verticalLayoutWidget_4->setGeometry(QRect(0, 0, WIDTH, HEIGHT));
ui->stackedWidget->setFixedHeight(HEIGHT);
ui->settingsScrollArea->setFixedHeight(HEIGHT-settingsTitleLabelNominalHeight-ui->verticalLayout_4->spacing());
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);
}
}
void MainWindow::highlightWrongInput(QString warningText, QWidget* widgetToFocus) {
void MainWindow::highlightWrongInput(QString warningText, WrongInputPageEnum inputPage, QWidget* widgetToFocus) {
bool redVisible = ui->wrongInputLabel->isVisible();
ui->wrongInputLabel->setVisible(true);
ui->wrongInputLabel->setText(warningText);
if(!redVisible)adjustSizesAccordingToWrongLabel();
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 "SaverImpl.h"
#include "I2pdQtUtil.h"
class SaverImpl;
class LogViewerManager;
@ -100,12 +102,14 @@ class MainWindow;
class MainWindowItem : public QObject {
Q_OBJECT
private:
ConfigOption option;
QWidget* widgetToFocus;
QString requirementToBeValid;
const bool readOnly;
public:
MainWindowItem(ConfigOption option_, QWidget* widgetToFocus_, QString requirementToBeValid_) :
option(option_), widgetToFocus(widgetToFocus_), requirementToBeValid(requirementToBeValid_) {}
MainWindowItem(ConfigOption option_, QWidget* widgetToFocus_, QString requirementToBeValid_, bool readOnly_=false) :
option(option_), widgetToFocus(widgetToFocus_), requirementToBeValid(requirementToBeValid_), readOnly(readOnly_) {}
QWidget* getWidgetToFocus(){return widgetToFocus;}
QString& getRequirementToBeValid() { return requirementToBeValid; }
ConfigOption& getConfigOption() { return option; }
@ -116,13 +120,14 @@ public:
std::string optName="";
if(!option.section.isEmpty())optName=option.section.toStdString()+std::string(".");
optName+=option.option.toStdString();
qDebug() << "loadFromConfigOption[" << optName.c_str() << "]";
//qDebug() << "loadFromConfigOption[" << optName.c_str() << "]";
boost::any programOption;
i2p::config::GetOptionAsAny(optName, programOption);
optionValue=programOption.empty()?boost::any(std::string(""))
:boost::any_cast<boost::program_options::variable_value>(programOption).value();
}
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)) {
std::string v = boost::any_cast<std::string>(optionValue);
if(v.empty())return;
@ -132,7 +137,7 @@ public:
std::string optName="";
if(!option.section.isEmpty())optName=option.section.toStdString()+std::string(".");
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();
if(!option.section.isEmpty() &&
sectionAsStdStr!=programOptionsWriterCurrentSection) {
@ -155,19 +160,24 @@ public:
}else out << boost::any_cast<std::string>(optionValue); //let it throw
out << "\n\n";
}
virtual bool isValid(){return true;}
virtual bool isValid(bool & alreadyDisplayedIfWrong){alreadyDisplayedIfWrong=false;return true;}
};
class NonGUIOptionItem : public MainWindowItem {
public:
NonGUIOptionItem(ConfigOption option_) : MainWindowItem(option_, nullptr, QString()) {};
NonGUIOptionItem(ConfigOption option_) : MainWindowItem(option_, nullptr, QString()) {}
virtual ~NonGUIOptionItem(){}
virtual bool isValid() { return true; }
//virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
};
class BaseStringItem : public MainWindowItem {
Q_OBJECT
public:
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 void installListeners(MainWindow *mainWindow);
virtual QString toString(){
@ -183,13 +193,15 @@ public:
optionValue=fromString(lineEdit->text());
MainWindowItem::saveToStringStream(out);
}
virtual bool isValid() { return true; }
virtual bool isValid(bool & alreadyDisplayedIfWrong);
};
class FileOrFolderChooserItem : public BaseStringItem {
protected:
const bool requireExistingFile;
public:
QPushButton* browsePushButton;
FileOrFolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_) :
BaseStringItem(option_, lineEdit_, QString()), browsePushButton(browsePushButton_) {}
FileOrFolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_, MainWindow* mw, bool requireExistingFile_, bool readOnly) :
BaseStringItem(option_, lineEdit_, QString(), mw, readOnly), requireExistingFile(requireExistingFile_), browsePushButton(browsePushButton_) {}
virtual ~FileOrFolderChooserItem(){}
};
class FileChooserItem : public FileOrFolderChooserItem {
@ -197,8 +209,8 @@ class FileChooserItem : public FileOrFolderChooserItem {
private slots:
void pushButtonReleased();
public:
FileChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_) :
FileOrFolderChooserItem(option_, lineEdit_, browsePushButton_) {
FileChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_, MainWindow* mw, bool requireExistingFile, bool readOnly) :
FileOrFolderChooserItem(option_, lineEdit_, browsePushButton_, mw, requireExistingFile, readOnly) {
QObject::connect(browsePushButton, SIGNAL(released()), this, SLOT(pushButtonReleased()));
}
};
@ -207,20 +219,20 @@ class FolderChooserItem : public FileOrFolderChooserItem{
private slots:
void pushButtonReleased();
public:
FolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_) :
FileOrFolderChooserItem(option_, lineEdit_, browsePushButton_) {
FolderChooserItem(ConfigOption option_, QLineEdit* lineEdit_, QPushButton* browsePushButton_, MainWindow* mw, bool requireExistingFolder) :
FileOrFolderChooserItem(option_, lineEdit_, browsePushButton_, mw, requireExistingFolder, false) {
QObject::connect(browsePushButton, SIGNAL(released()), this, SLOT(pushButtonReleased()));
}
};
class ComboBoxItem : public MainWindowItem {
public:
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 void installListeners(MainWindow *mainWindow);
virtual void loadFromConfigOption()=0;
virtual void saveToStringStream(std::stringstream& out)=0;
virtual bool isValid() { return true; }
//virtual bool isValid(bool & alreadyDisplayedIfWrong) { return ; }
};
class LogDestinationComboBoxItem : public ComboBoxItem {
public:
@ -237,13 +249,13 @@ public:
optionValue=logDest;
MainWindowItem::saveToStringStream(out);
}
virtual bool isValid() { return true; }
//virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
Q_OBJECT
};
class LogLevelComboBoxItem : public ComboBoxItem {
public:
LogLevelComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {};
LogLevelComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {}
virtual ~LogLevelComboBoxItem(){}
virtual void loadFromConfigOption(){
MainWindowItem::loadFromConfigOption();
@ -254,11 +266,11 @@ public:
optionValue=comboBox->currentText().toStdString();
MainWindowItem::saveToStringStream(out);
}
virtual bool isValid() { return true; }
//virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
};
class SignatureTypeComboBoxItem : public ComboBoxItem {
public:
SignatureTypeComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {};
SignatureTypeComboBoxItem(ConfigOption option_, QComboBox* comboBox_) : ComboBoxItem(option_, comboBox_) {}
virtual ~SignatureTypeComboBoxItem(){}
virtual void loadFromConfigOption(){
MainWindowItem::loadFromConfigOption();
@ -271,39 +283,42 @@ public:
optionValue=(unsigned short)selected;
MainWindowItem::saveToStringStream(out);
}
virtual bool isValid() { return true; }
//virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
};
class CheckBoxItem : public MainWindowItem {
public:
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 void installListeners(MainWindow *mainWindow);
virtual void loadFromConfigOption(){
MainWindowItem::loadFromConfigOption();
qDebug() << "setting value for checkbox " << checkBox->text();
//qDebug() << "setting value for checkbox " << checkBox->text();
checkBox->setChecked(boost::any_cast<bool>(optionValue));
}
virtual void saveToStringStream(std::stringstream& out){
optionValue=checkBox->isChecked();
MainWindowItem::saveToStringStream(out);
}
virtual bool isValid() { return true; }
//virtual bool isValid(bool & alreadyDisplayedIfWrong) { return true; }
};
class BaseFormattedStringItem : public BaseStringItem {
public:
QString fieldNameTranslated;
BaseFormattedStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, QString requirementToBeValid_) :
BaseStringItem(option_, lineEdit_, requirementToBeValid_), fieldNameTranslated(fieldNameTranslated_) {};
BaseFormattedStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, QString requirementToBeValid_, MainWindow* mw) :
BaseStringItem(option_, lineEdit_, requirementToBeValid_, mw), fieldNameTranslated(fieldNameTranslated_) {}
virtual ~BaseFormattedStringItem(){}
virtual bool isValid()=0;
//virtual bool isValid(bool & alreadyDisplayedIfWrong)=0;
};
class IntegerStringItem : public BaseFormattedStringItem {
public:
IntegerStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be a valid integer.")) {};
IntegerStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be a valid integer."), mw) {}
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();
bool ok;
str.toInt(&ok);
@ -314,10 +329,13 @@ public:
};
class UShortStringItem : public BaseFormattedStringItem {
public:
UShortStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned short integer.")) {};
UShortStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned short integer."), mw) {}
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();
bool ok;
str.toUShort(&ok);
@ -328,10 +346,13 @@ public:
};
class UInt32StringItem : public BaseFormattedStringItem {
public:
UInt32StringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned 32-bit integer.")) {};
UInt32StringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned 32-bit integer."), mw) {}
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();
bool ok;
str.toUInt(&ok);
@ -342,10 +363,13 @@ public:
};
class UInt16StringItem : public BaseFormattedStringItem {
public:
UInt16StringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned 16-bit integer.")) {};
UInt16StringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be unsigned 16-bit integer."), mw) {}
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();
bool ok;
str.toUShort(&ok);
@ -356,14 +380,14 @@ public:
};
class IPAddressStringItem : public BaseFormattedStringItem {
public:
IPAddressStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be an IPv4 address")) {};
virtual bool isValid(){return true;}//todo
IPAddressStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
BaseFormattedStringItem(option_, lineEdit_, fieldNameTranslated_, QApplication::tr("Must be an IPv4 address"), mw) {}
//virtual bool isValid(bool & alreadyDisplayedIfWrong){return true;}//todo
};
class TCPPortStringItem : public UShortStringItem {
public:
TCPPortStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_) :
UShortStringItem(option_, lineEdit_, fieldNameTranslated_) {};
TCPPortStringItem(ConfigOption option_, QLineEdit* lineEdit_, QString fieldNameTranslated_, MainWindow* mw) :
UShortStringItem(option_, lineEdit_, fieldNameTranslated_,mw) {}
};
namespace Ui {
@ -386,6 +410,7 @@ class DelayedSaveManagerImpl;
class MainWindow : public QMainWindow {
Q_OBJECT
private:
std::string currentLocalDestinationB32;
std::shared_ptr<std::iostream> logStream;
DelayedSaveManagerImpl* delayedSaveManagerPtr;
DelayedSaveManager::DATA_SERIAL_TYPE dataSerial;
@ -395,7 +420,7 @@ public:
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;
@ -428,7 +453,10 @@ private slots:
void runPeerTest();
void enableTransit();
void disableTransit();
public slots:
void syncLogLevel (int comboBoxIndex);
void showStatus_local_destinations_Page();
void showStatus_leasesets_Page();
void showStatus_tunnels_Page();
@ -442,6 +470,7 @@ public slots:
void showTunnelsPage();
void showRestartPage();
void showQuitPage();
void showAboutBox(const QString & href);
private:
StatusPage statusPage;
@ -499,7 +528,7 @@ protected:
//LogDestinationComboBoxItem* logOption;
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 initCombobox(ConfigOption option, QComboBox* comboBox);
void initLogDestinationCombobox(ConfigOption option, QComboBox* comboBox);
@ -519,12 +548,12 @@ protected:
public slots:
/** returns false iff not valid items present and save was aborted */
bool saveAllConfigs(bool focusOnTunnel, std::string tunnelNameToFocus="");
void reloadTunnelsConfigAndUI(std::string tunnelNameToFocus);
bool saveAllConfigs(bool reloadAfterSave, FocusEnum focusOn, std::string tunnelNameToFocus="", QWidget* widgetToFocus=nullptr);
void reloadTunnelsConfigAndUI(std::string tunnelNameToFocus, QWidget* widgetToFocus);
void reloadTunnelsConfigAndUI() { reloadTunnelsConfigAndUI("", nullptr); }
//focus none
void reloadTunnelsConfigAndUI() { reloadTunnelsConfigAndUI(""); }
void reloadTunnelsConfigAndUI_QString(const QString tunnelNameToFocus);
void reloadTunnelsConfigAndUI_QString(QString tunnelNameToFocus);
void addServerTunnelPushButtonReleased();
void addClientTunnelPushButtonReleased();
@ -629,7 +658,7 @@ private:
tunnelConfigs.erase(it);
delete tc;
}
saveAllConfigs(false);
saveAllConfigs(true, FocusEnum::noFocus);
}
std::string GenerateNewTunnelName() {
@ -666,7 +695,7 @@ private:
sigType,
cryptoType);
saveAllConfigs(true, name);
saveAllConfigs(true, FocusEnum::focusOnTunnelName, name);
}
void CreateDefaultServerTunnel() {//TODO dedup default values with ReadTunnelsConfig() and with ClientContext.cpp::ReadTunnels ()
@ -704,7 +733,7 @@ private:
cryptoType);
saveAllConfigs(true, name);
saveAllConfigs(true, FocusEnum::focusOnTunnelName, name);
}
void ReadTunnelsConfig() //TODO deduplicate the code with ClientContext.cpp::ReadTunnels ()
@ -747,16 +776,13 @@ private:
std::string dest;
if (type == I2P_TUNNELS_SECTION_TYPE_CLIENT || type == I2P_TUNNELS_SECTION_TYPE_UDPCLIENT) {
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);
std::cout << "had read tunnel port: " << port << std::endl;
// optional params
std::string keys = section.second.get (I2P_CLIENT_TUNNEL_KEYS, "");
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 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);
// I2CP
std::map<std::string, std::string> options;

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>908</width>
<height>554</height>
<height>604</height>
</rect>
</property>
<property name="minimumSize">
@ -35,13 +35,13 @@
<property name="minimumSize">
<size>
<width>908</width>
<height>550</height>
<height>600</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>908</width>
<height>550</height>
<height>600</height>
</size>
</property>
<widget class="QWidget" name="horizontalLayoutWidget">
@ -50,7 +50,7 @@
<x>10</x>
<y>10</y>
<width>888</width>
<height>531</height>
<height>596</height>
</rect>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
@ -58,7 +58,7 @@
<enum>QLayout::SetMaximumSize</enum>
</property>
<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">
<enum>QLayout::SetMinimumSize</enum>
</property>
@ -67,7 +67,7 @@
<x>0</x>
<y>0</y>
<width>170</width>
<height>496</height>
<height>596</height>
</rect>
</property>
<item>
@ -172,6 +172,33 @@
</property>
</spacer>
</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>
</item>
<item>
@ -629,7 +656,7 @@
</property>
<property name="maximumSize">
<size>
<width>713</width>
<width>707</width>
<height>713</height>
</size>
</property>
@ -648,8 +675,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>713</width>
<height>531</height>
<width>707</width>
<height>586</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
@ -690,8 +717,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>711</width>
<height>531</height>
<width>707</width>
<height>586</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4_logViewer">
@ -753,8 +780,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>711</width>
<height>531</height>
<width>707</width>
<height>586</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
@ -798,8 +825,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>81</width>
<height>28</height>
<width>693</width>
<height>498</height>
</rect>
</property>
<property name="sizePolicy">
@ -820,8 +847,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>711</width>
<height>531</height>
<width>707</width>
<height>586</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
@ -903,8 +930,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>711</width>
<height>531</height>
<width>707</width>
<height>586</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
@ -958,7 +985,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>711</width>
<width>707</width>
<height>531</height>
</rect>
</property>

View File

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