mirror of
https://github.com/Windscribe/Desktop-App.git
synced 2026-05-07 20:12:44 +00:00
v2.21.5
This commit is contained in:
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: false
|
||||
+15
-1
@@ -1,3 +1,17 @@
|
||||
2.21.5 (09/03/2026)
|
||||
All:
|
||||
* Added favourite locations to preferences import/export. #1691
|
||||
* Improved additional Slovak translations from GitHub user kubalav. #1684
|
||||
* Improved Spanish translations. #1685
|
||||
* Fixed DPI scaling issues in login/signup UI. #1628
|
||||
* Fixed a failed assertion at app start. #1582
|
||||
* Fixed incorrect window ordering when returning to login screen. #1673
|
||||
* Fixed incorrect Update button size. #1659
|
||||
* Fixed login not working with windscribe-cli. #1693
|
||||
Windows:
|
||||
* Improved app (helper) to support running on Windows Server 2019/2022 without requiring the Wireless LAN Service to be installed. #1681
|
||||
|
||||
|
||||
2.21.4 (02/03/2026)
|
||||
All:
|
||||
* Added sound preview for sound notifications. #1582
|
||||
@@ -6,7 +20,7 @@ All:
|
||||
* Fixed more incorrect window ordering issues. #1673
|
||||
* Fixed IP utilities sometimes not available after changing from custom config to regular location. #1677
|
||||
Windows:
|
||||
* Improved adapter network identfication state detection on Windows. #1680
|
||||
* Improved adapter network identification state detection on Windows. #1680
|
||||
* Fixed IKEv2 may get stuck after waking from sleep. #1676
|
||||
macOS:
|
||||
* Fixed app may crash during launch. #1679
|
||||
|
||||
@@ -22,7 +22,7 @@ find_package(OpenSSL REQUIRED)
|
||||
find_package(Boost REQUIRED COMPONENTS serialization)
|
||||
find_package(spdlog CONFIG REQUIRED)
|
||||
|
||||
include("${CMAKE_SOURCE_DIR}/cmake/fetch_wsnet.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../../cmake/fetch_wsnet.cmake")
|
||||
|
||||
# build_all.py sets this option when invoked with the '--sign' flag. Disabled by default
|
||||
option(DEFINE_USE_SIGNATURE_CHECK_MACRO "Add define USE_SIGNATURE_CHECK to project" OFF)
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <utility>
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
|
||||
This class ensures a DLL is loaded from the system32, or its localized equivalent, folder
|
||||
to mitigate DLL hijacking attacks.
|
||||
|
||||
NOTES:
|
||||
- You do not need to use this class for 'known' DLLs listed in HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs.
|
||||
The OS loader ensures all DLLs in that list, and any DLLs loaded by them, are only loaded from system32.
|
||||
- One should use this class to replace any statically linked DLLs listed in cmake's target_link_libraries declaration.
|
||||
TL;DR your cmake file should not have a target_link_libraries declaration.
|
||||
|
||||
*/
|
||||
|
||||
namespace wsl {
|
||||
|
||||
class SystemLibLoader {
|
||||
public:
|
||||
SystemLibLoader(const SystemLibLoader&) = delete;
|
||||
SystemLibLoader& operator=(const SystemLibLoader&) = delete;
|
||||
|
||||
SystemLibLoader(SystemLibLoader &&other) noexcept : handle_(other.handle_)
|
||||
{
|
||||
other.handle_ = nullptr;
|
||||
}
|
||||
|
||||
SystemLibLoader& operator=(SystemLibLoader &&other) noexcept
|
||||
{
|
||||
if (this != &other) {
|
||||
std::swap(handle_, other.handle_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
explicit SystemLibLoader(const char *libName)
|
||||
{
|
||||
if (!libName) {
|
||||
throw std::invalid_argument("Null parameter passed to SystemLibLoader");
|
||||
}
|
||||
|
||||
handle_ = ::LoadLibraryExA(libName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_REQUIRE_SIGNED_TARGET);
|
||||
|
||||
if (!handle_) {
|
||||
throw std::system_error(::GetLastError(), std::system_category(), std::string("SystemLibLoader failed to load DLL ") + libName);
|
||||
}
|
||||
}
|
||||
|
||||
~SystemLibLoader()
|
||||
{
|
||||
if (handle_) {
|
||||
::FreeLibrary(handle_);
|
||||
}
|
||||
}
|
||||
|
||||
static bool isAvailable(const char *libName) noexcept
|
||||
{
|
||||
if (!libName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
HMODULE handle = ::LoadLibraryExA(libName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_REQUIRE_SIGNED_TARGET);
|
||||
if (handle) {
|
||||
::FreeLibrary(handle);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T *getFunction(const char *name) const
|
||||
{
|
||||
return reinterpret_cast<T *>(getProcAddress(name));
|
||||
}
|
||||
|
||||
private:
|
||||
HMODULE handle_ = nullptr;
|
||||
|
||||
FARPROC getProcAddress(const char *name) const
|
||||
{
|
||||
if (!name) {
|
||||
throw std::invalid_argument("Null parameter passed to getProcAddress");
|
||||
}
|
||||
|
||||
if (!handle_) {
|
||||
throw std::logic_error("The DLL has not been loaded");
|
||||
}
|
||||
|
||||
auto symbol = ::GetProcAddress(handle_, name);
|
||||
|
||||
if (symbol == nullptr) {
|
||||
throw std::system_error(::GetLastError(), std::system_category(), std::string("SystemLibLoader failed to load function ") + name);
|
||||
}
|
||||
|
||||
return symbol;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#define WINDSCRIBE_MAJOR_VERSION 2
|
||||
#define WINDSCRIBE_MINOR_VERSION 21
|
||||
#define WINDSCRIBE_BUILD_VERSION 4
|
||||
#define WINDSCRIBE_BUILD_VERSION 5
|
||||
|
||||
// only one of these should be enabled; neither -> stable
|
||||
//#define WINDSCRIBE_IS_BETA
|
||||
|
||||
@@ -202,6 +202,16 @@ void LocationsModelManager::saveFavoriteLocations()
|
||||
locationsModel_->saveFavoriteLocations();
|
||||
}
|
||||
|
||||
QJsonArray LocationsModelManager::favoriteLocationsToJson() const
|
||||
{
|
||||
return locationsModel_->favoriteLocationsToJson();
|
||||
}
|
||||
|
||||
void LocationsModelManager::setFavoriteLocationsFromJson(const QJsonArray &arr)
|
||||
{
|
||||
locationsModel_->setFavoriteLocationsFromJson(arr);
|
||||
}
|
||||
|
||||
void LocationsModelManager::onChangeConnectionSpeedTimer()
|
||||
{
|
||||
for (QHash<LocationID, PingTime>::const_iterator it = connectionSpeeds_.constBegin(); it != connectionSpeeds_.constEnd(); ++it)
|
||||
|
||||
@@ -43,6 +43,9 @@ public:
|
||||
|
||||
void saveFavoriteLocations();
|
||||
|
||||
QJsonArray favoriteLocationsToJson() const;
|
||||
void setFavoriteLocationsFromJson(const QJsonArray &arr);
|
||||
|
||||
QJsonObject renamedLocations() const;
|
||||
void setRenamedLocations(const QJsonObject &obj);
|
||||
void resetRenamedLocations();
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <QDataStream>
|
||||
#include <QDataStream>
|
||||
#include <QIODevice>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QSettings>
|
||||
|
||||
#include "utils/simplecrypt.h"
|
||||
@@ -131,4 +133,40 @@ void FavoriteLocationsStorage::writeToSettings()
|
||||
isFavoriteLocationsSetModified_ = false;
|
||||
}
|
||||
|
||||
QJsonArray FavoriteLocationsStorage::toJson() const
|
||||
{
|
||||
QJsonArray arr;
|
||||
for (auto it = favoriteLocations_.constBegin(); it != favoriteLocations_.constEnd(); ++it) {
|
||||
QJsonObject entry;
|
||||
entry["type"] = it.key().type();
|
||||
entry["id"] = it.key().id();
|
||||
entry["city"] = it.key().city();
|
||||
if (!it.value().pinnedHostname.isEmpty())
|
||||
entry["pinnedHostname"] = it.value().pinnedHostname;
|
||||
if (!it.value().pinnedIp.isEmpty())
|
||||
entry["pinnedIp"] = it.value().pinnedIp;
|
||||
arr.append(entry);
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
void FavoriteLocationsStorage::fromJson(const QJsonArray &arr)
|
||||
{
|
||||
favoriteLocations_.clear();
|
||||
for (const QJsonValue &val : arr) {
|
||||
if (!val.isObject())
|
||||
continue;
|
||||
QJsonObject entry = val.toObject();
|
||||
if (!entry.contains("type") || !entry.contains("id") || !entry.contains("city"))
|
||||
continue;
|
||||
LocationID loc(entry["type"].toInt(), entry["id"].toInt(), entry["city"].toString());
|
||||
FavoriteData data;
|
||||
data.pinnedHostname = entry["pinnedHostname"].toString();
|
||||
data.pinnedIp = entry["pinnedIp"].toString();
|
||||
favoriteLocations_.insert(loc, data);
|
||||
}
|
||||
isFavoriteLocationsSetModified_ = true;
|
||||
writeToSettings();
|
||||
}
|
||||
|
||||
} //namespace gui_locations
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QHash>
|
||||
@@ -21,6 +22,9 @@ public:
|
||||
void readFromSettings();
|
||||
void writeToSettings();
|
||||
|
||||
QJsonArray toJson() const;
|
||||
void fromJson(const QJsonArray &arr);
|
||||
|
||||
private:
|
||||
struct FavoriteData {
|
||||
QString pinnedHostname;
|
||||
|
||||
@@ -507,6 +507,18 @@ void LocationsModel::saveFavoriteLocations()
|
||||
favoriteLocationsStorage_.writeToSettings();
|
||||
}
|
||||
|
||||
QJsonArray LocationsModel::favoriteLocationsToJson() const
|
||||
{
|
||||
return favoriteLocationsStorage_.toJson();
|
||||
}
|
||||
|
||||
void LocationsModel::setFavoriteLocationsFromJson(const QJsonArray &arr)
|
||||
{
|
||||
beginResetModel();
|
||||
favoriteLocationsStorage_.fromJson(arr);
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QVariant LocationsModel::dataForLocation(int row, int role) const
|
||||
{
|
||||
if (role == Qt::DisplayRole)
|
||||
|
||||
@@ -66,6 +66,9 @@ public:
|
||||
// the client of the class must explicitly save locations if required
|
||||
void saveFavoriteLocations();
|
||||
|
||||
QJsonArray favoriteLocationsToJson() const;
|
||||
void setFavoriteLocationsFromJson(const QJsonArray &arr);
|
||||
|
||||
QJsonObject renamedLocations() const;
|
||||
void setRenamedLocations(const QJsonObject &obj);
|
||||
void resetRenamedLocations();
|
||||
|
||||
@@ -64,6 +64,7 @@ void CustomMenuLineEdit::updateScaling()
|
||||
menu_->clearItems();
|
||||
menu_->clear();
|
||||
menu_->initContextMenu();
|
||||
updatePositions();
|
||||
}
|
||||
|
||||
void CustomMenuLineEdit::appendText(const QString &str)
|
||||
@@ -230,6 +231,7 @@ void CustomMenuLineEdit::updatePositions()
|
||||
|
||||
// Handle icon2 (rightmost)
|
||||
if (isCustomIcon2_ && icon2_) {
|
||||
icon2_->updateSize();
|
||||
xPos -= iconSize + iconSpacing;
|
||||
icon2_->setGeometry(xPos, height()/2 - iconSize/2, iconSize, iconSize);
|
||||
icon2_->show();
|
||||
@@ -240,6 +242,7 @@ void CustomMenuLineEdit::updatePositions()
|
||||
|
||||
// Handle icon1 (second from right, or rightmost if icon2 is not set)
|
||||
if (isCustomIcon1_ && icon1_) {
|
||||
icon1_->updateSize();
|
||||
xPos -= iconSize + iconSpacing;
|
||||
icon1_->setGeometry(xPos, height()/2 - iconSize/2, iconSize, iconSize);
|
||||
icon1_->show();
|
||||
@@ -252,6 +255,7 @@ void CustomMenuLineEdit::updatePositions()
|
||||
if ((echoMode_ == QLineEdit::Password && showRevealToggle_) && !isCustomIcon1_ && !isCustomIcon2_) {
|
||||
xPos -= iconSize + iconSpacing;
|
||||
if (icon_) {
|
||||
icon_->updateSize();
|
||||
icon_->setGeometry(xPos, height()/2 - iconSize/2, iconSize, iconSize);
|
||||
icon_->show();
|
||||
rightMargin = width() - xPos + 4*G_SCALE;
|
||||
|
||||
@@ -134,7 +134,7 @@ SignupWindowItem::SignupWindowItem(QGraphicsObject *parent, PreferencesHelper *p
|
||||
|
||||
continueButton_ = new CommonGraphics::BubbleButton(this, CommonGraphics::BubbleButton::kWelcome, 121, 38, 19);
|
||||
continueButton_->setFont(FontDescr(15, QFont::Medium));
|
||||
continueButton_->setWidth((WINDOW_WIDTH- 36)*G_SCALE);
|
||||
continueButton_->setWidth(WINDOW_WIDTH - 36);
|
||||
connect(continueButton_, &CommonGraphics::BubbleButton::clicked, this, &SignupWindowItem::onContinueClick);
|
||||
continueButton_->setClickable(true);
|
||||
continueButton_->show();
|
||||
@@ -195,7 +195,21 @@ void SignupWindowItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *
|
||||
|
||||
void SignupWindowItem::updateScaling()
|
||||
{
|
||||
// These items store pixel-valued widths that must be recomputed for the new scale
|
||||
// before ScalableGraphicsObject::updateScaling() propagates to children and
|
||||
// triggers their recalcBoundingRect() / recalcWidth() calls.
|
||||
const int hintMaxWidth = (WINDOW_WIDTH - 36) * G_SCALE;
|
||||
passwordHint_->setMaxWidth(hintMaxWidth);
|
||||
hashHint_->setMaxWidth(hintMaxWidth);
|
||||
emailHint_->setMaxWidth(hintMaxWidth);
|
||||
errorHint_->setMaxWidth(hintMaxWidth);
|
||||
|
||||
const int btnMaxWidth = (WINDOW_WIDTH - 30) * G_SCALE;
|
||||
btnVoucherCode_->setMaxWidth(btnMaxWidth);
|
||||
btnReferred_->setMaxWidth(btnMaxWidth);
|
||||
|
||||
ScalableGraphicsObject::updateScaling();
|
||||
updatePositions();
|
||||
}
|
||||
|
||||
void SignupWindowItem::setClickable(bool enabled)
|
||||
|
||||
@@ -130,7 +130,7 @@ void UsernamePasswordEntry::setWidth(int width)
|
||||
void UsernamePasswordEntry::updateScaling()
|
||||
{
|
||||
ClickableGraphicsObject::updateScaling();
|
||||
int yPos = showDescription_ ? 20 : 0;
|
||||
int yPos = showDescription_ ? 20 : 0;
|
||||
userEntryProxy_->setPos(WINDOW_MARGIN*G_SCALE, yPos*G_SCALE);
|
||||
updateFontSize();
|
||||
userEntryLine_->setFixedHeight(height_*G_SCALE - userEntryProxy_->pos().y()); // keep text box within drawing region and above line
|
||||
|
||||
@@ -1254,8 +1254,14 @@ void MainWindow::onPreferencesExportSettingsClick()
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonDocument doc(backend_->getPreferences()->toJson());
|
||||
QJsonObject json = backend_->getPreferences()->toJson();
|
||||
|
||||
// Add favourite locations
|
||||
json["favouriteLocations"] = backend_->locationsModelManager()->favoriteLocationsToJson();
|
||||
|
||||
QJsonDocument doc(json);
|
||||
file.write(doc.toJson());
|
||||
|
||||
qCDebug(LOG_BASIC) << "Exported preferences to the file.";
|
||||
}
|
||||
|
||||
@@ -1296,7 +1302,13 @@ void MainWindow::onPreferencesImportSettingsClick()
|
||||
return;
|
||||
}
|
||||
|
||||
backend_->getPreferences()->updateFromJson(jsonDoc.object());
|
||||
const QJsonObject jsonObj = jsonDoc.object();
|
||||
backend_->getPreferences()->updateFromJson(jsonObj);
|
||||
|
||||
// Read favourites
|
||||
if (jsonObj.contains("favouriteLocations") && jsonObj["favouriteLocations"].isArray()) {
|
||||
backend_->locationsModelManager()->setFavoriteLocationsFromJson(jsonObj["favouriteLocations"].toArray());
|
||||
}
|
||||
|
||||
// Preferences itself does not have any information on the current portmap. Check that connection setting ports are valid, otherwise use default port
|
||||
types::ConnectionSettings cs = backend_->getPreferences()->connectionSettings();
|
||||
|
||||
@@ -1191,6 +1191,7 @@ void MainWindowController::gotoWelcomeWindow()
|
||||
|
||||
connect(anim, &QPropertyAnimation::finished, [this]() {
|
||||
generalMessageWindow_->setVisible(false);
|
||||
connectWindow_->hide();
|
||||
welcomeWindow_->setClickable(true);
|
||||
welcomeWindow_->setFocus();
|
||||
isAtomicAnimationActive_ = false;
|
||||
@@ -1395,6 +1396,8 @@ void MainWindowController::gotoLoginWindow()
|
||||
|
||||
connect(anim, &QPropertyAnimation::finished, [this]() {
|
||||
generalMessageWindow_->setVisible(false);
|
||||
welcomeWindow_->setVisible(false);
|
||||
connectWindow_->hide();
|
||||
loginWindow_->setClickable(true);
|
||||
loginWindow_->setUsernameFocus();
|
||||
isAtomicAnimationActive_ = false;
|
||||
@@ -1595,13 +1598,39 @@ void MainWindowController::gotoEmergencyWindow()
|
||||
void MainWindowController::gotoLoggingInWindow()
|
||||
{
|
||||
WS_ASSERT(curWindow_ == WINDOW_ID_LOGIN
|
||||
|| curWindow_ == WINDOW_ID_WELCOME
|
||||
|| curWindow_ == WINDOW_ID_SIGNUP
|
||||
|| curWindow_ == WINDOW_ID_INITIALIZATION
|
||||
|| curWindow_ == WINDOW_ID_GENERAL_MESSAGE
|
||||
|| curWindow_ == WINDOW_ID_EXTERNAL_CONFIG
|
||||
|| curWindow_ == WINDOW_ID_TWO_FACTOR_AUTH);
|
||||
|
||||
if (curWindow_ == WINDOW_ID_LOGIN) {
|
||||
if (curWindow_ == WINDOW_ID_WELCOME) {
|
||||
curWindow_ = WINDOW_ID_LOGGING_IN;
|
||||
welcomeWindow_->setClickable(false);
|
||||
TooltipController::instance().hideAllTooltips();
|
||||
welcomeWindow_->stackBefore(loggingInWindow_);
|
||||
|
||||
loggingInWindow_->setOpacity(0.0);
|
||||
loggingInWindow_->setVisible(true);
|
||||
loggingInWindow_->startAnimation();
|
||||
|
||||
QPropertyAnimation *anim = new QPropertyAnimation(this);
|
||||
anim->setTargetObject(loggingInWindow_);
|
||||
anim->setPropertyName("opacity");
|
||||
anim->setStartValue(0.0);
|
||||
anim->setEndValue(1.0);
|
||||
anim->setDuration(SCREEN_SWITCH_OPACITY_ANIMATION_DURATION);
|
||||
connect(anim, &QPropertyAnimation::finished, [this]() {
|
||||
welcomeWindow_->setVisible(false);
|
||||
isAtomicAnimationActive_ = false;
|
||||
handleNextWindowChange();
|
||||
updateMainAndViewGeometry(true);
|
||||
});
|
||||
|
||||
isAtomicAnimationActive_ = true;
|
||||
anim->start(QPropertyAnimation::DeleteWhenStopped);
|
||||
} else if (curWindow_ == WINDOW_ID_LOGIN) {
|
||||
curWindow_ = WINDOW_ID_LOGGING_IN;
|
||||
loginWindow_->setClickable(false);
|
||||
TooltipController::instance().hideAllTooltips();
|
||||
|
||||
@@ -214,9 +214,7 @@ void UpdateAppItem::onUpdateClick()
|
||||
void UpdateAppItem::onLanguageChanged()
|
||||
{
|
||||
updateButton_->setText(tr("Update"));
|
||||
int width = preferences_->appSkin() == APP_SKIN_VAN_GOGH ? WIDTH_VAN_GOGH*G_SCALE : WIDTH*G_SCALE;
|
||||
int updatePosX = width - updateButton_->boundingRect().width();
|
||||
updateButton_->setPos(updatePosX, 1*G_SCALE);
|
||||
updatePositions();
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -242,7 +240,7 @@ void UpdateAppItem::updatePositions()
|
||||
{
|
||||
int width = preferences_->appSkin() == APP_SKIN_VAN_GOGH ? WIDTH_VAN_GOGH : WIDTH;
|
||||
|
||||
QFontMetrics fm(FontManager::instance().getFont(12, QFont::Normal));
|
||||
QFontMetrics fm(FontManager::instance().getFont(12, QFont::Medium));
|
||||
updateButton_->setWidth(fm.horizontalAdvance(tr("Update")) + 16*G_SCALE);
|
||||
|
||||
int updatePosX = width*G_SCALE - updateButton_->boundingRect().width();
|
||||
|
||||
@@ -88,6 +88,10 @@ void MixedComboBoxItem::updatePositions()
|
||||
|
||||
void MixedComboBoxItem::updateSecondaryItemVisibility(bool signal)
|
||||
{
|
||||
if (!hasItems()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVariant currentValue = currentItem();
|
||||
|
||||
if (currentValue == customValue_) {
|
||||
@@ -152,16 +156,16 @@ void MixedComboBoxItem::updatePreviewButtonVisibility()
|
||||
|
||||
previewButton_->show();
|
||||
|
||||
QVariant currentValue = currentItem();
|
||||
bool isActive;
|
||||
|
||||
if (currentValue == bundledValue_) {
|
||||
isActive = true;
|
||||
} else if (currentValue == customValue_) {
|
||||
const QString path = selectFileItem_->path();
|
||||
isActive = !path.isEmpty() && QFile::exists(path);
|
||||
} else {
|
||||
isActive = false;
|
||||
QVariant currentValue;
|
||||
bool isActive = false;
|
||||
if (hasItems()) {
|
||||
currentValue = currentItem();
|
||||
if (currentValue == bundledValue_) {
|
||||
isActive = true;
|
||||
} else if (currentValue == customValue_) {
|
||||
const QString path = selectFileItem_->path();
|
||||
isActive = !path.isEmpty() && QFile::exists(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isActive && isPlaying_) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -372,11 +372,11 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Standard</source>
|
||||
<translation>Štandard</translation>
|
||||
<translation>Štandardné</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Hashed</source>
|
||||
<translation>Hashed</translation>
|
||||
<translation>Hašované</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Username</source>
|
||||
@@ -396,15 +396,15 @@
|
||||
</message>
|
||||
<message>
|
||||
<source>Hash</source>
|
||||
<translation>Hash</translation>
|
||||
<translation>Haš</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Account Hash or upload file</source>
|
||||
<translation>Hash účtu alebo nahraný súbor</translation>
|
||||
<translation>Haš účtu alebo nahrať súbor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select file for hash</source>
|
||||
<translation>Vyberte súbor pre hash</translation>
|
||||
<translation>Vyberte súbor pre haš</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>All Files (*.*)</source>
|
||||
@@ -1476,19 +1476,19 @@ Ak problém pretrváva aj po reštarte, pošlite nám denník ladenia, otvorte
|
||||
</message>
|
||||
<message>
|
||||
<source>Clear Wi-Fi History</source>
|
||||
<translation>Jasná história Wi-Fi</translation>
|
||||
<translation>Vymazať históriu Wi-Fi</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure?</source>
|
||||
<translation>Si si istý?</translation>
|
||||
<translation>Ste si istý?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Are you sure you want to clear all your Wi-Fi history? This will also clear all Wi-Fi passwords except for the one you're currently connected to. This may also temporarily disable your Wi-Fi.</source>
|
||||
<translation>Si si istý, že chceš vymazať všetku svoju Wi-Fi históriu? Tým sa tiež vymažú všetky Wi-Fi heslá okrem toho, na ktoré ste momentálne pripojení. To môže tiež dočasne deaktivovať vašu Wi-Fi.</translation>
|
||||
<translation>Naozaj chcete vymazať celú históriu Wi-Fi? Tým sa tiež vymažú všetky heslá Wi-Fi okrem tej momentálne pripojenej. To môže tiež dočasne vypnúť Wi-Fi.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove Wi-Fi SSID and MAC information from your operating system to prevent location history tracking.</source>
|
||||
<translation>Odstráňte Wi-Fi SSID a MAC údaje z operačného systému, aby ste zabránili sledovaniu histórie polohy.</translation>
|
||||
<translation>Odstrániť informácie o Wi-Fi SSID a MAC z operačného systému, aby sa zabránilo sledovaniu histórie polohy.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Connect to the VPN with WireGuard even in a hostile environment.</source>
|
||||
@@ -2026,11 +2026,11 @@ Najprv sa pripojte k sieti</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Discard</source>
|
||||
<translation>Vyhodiť</translation>
|
||||
<translation>Zahodiť</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>You have unsaved changes in edit fields. Do you want to save them?</source>
|
||||
<translation>Máte neuložené zmeny v editačných poliach. Chceš ich zachrániť?</translation>
|
||||
<translation>Máte neuložené zmeny v editačných poliach. Chcete ich uložiť?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save</source>
|
||||
@@ -2509,7 +2509,7 @@ Ak preinštalovanie nepomôže, obráťte sa na podporu Windscribe a požiadajte
|
||||
</message>
|
||||
<message>
|
||||
<source>Release</source>
|
||||
<translation>Vydanie</translation>
|
||||
<translation>Stabilná verzia</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Beta</source>
|
||||
@@ -2741,7 +2741,7 @@ Ak preinštalovanie nepomôže, obráťte sa na podporu Windscribe a požiadajte
|
||||
</message>
|
||||
<message>
|
||||
<source>Show app</source>
|
||||
<translation>Aplikácia Show</translation>
|
||||
<translation>Zobraziť aplikáciu</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -10,7 +10,7 @@ set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
project(helper)
|
||||
|
||||
include("${CMAKE_SOURCE_DIR}/cmake/fetch_wsnet.cmake")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/../../cmake/fetch_wsnet.cmake")
|
||||
|
||||
if (WIN32)
|
||||
add_subdirectory(windows)
|
||||
@@ -19,5 +19,3 @@ elseif(APPLE)
|
||||
elseif(UNIX)
|
||||
add_subdirectory(linux)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -93,7 +93,6 @@ target_link_libraries(WindscribeService
|
||||
Newdev
|
||||
Shlwapi
|
||||
wevtapi
|
||||
wlanapi
|
||||
wsnet::wsnet
|
||||
Boost::serialization
|
||||
spdlog::spdlog
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
#include "clear_wifi_history.h"
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <winevt.h>
|
||||
#include <wlanapi.h>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <winreg/WinReg.hpp>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#include "utils/systemlibloader.h"
|
||||
#include "utils/wsscopeguard.h"
|
||||
|
||||
bool ClearWiFiHistory::clear()
|
||||
{
|
||||
@@ -49,62 +52,80 @@ bool ClearWiFiHistory::clear()
|
||||
std::set<std::wstring> ClearWiFiHistory::getCurrentConnectedProfiles()
|
||||
{
|
||||
std::set<std::wstring> connectedProfiles;
|
||||
HANDLE hClient = NULL;
|
||||
DWORD dwClientVersion = 2;
|
||||
DWORD dwCurVersion = 0;
|
||||
DWORD dwResult = 0;
|
||||
|
||||
// Initialize WLAN handle
|
||||
dwResult = WlanOpenHandle(dwClientVersion, NULL, &dwCurVersion, &hClient);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanOpenHandle failed with error: {}", dwResult);
|
||||
if (!wsl::SystemLibLoader::isAvailable("wlanapi.dll")) {
|
||||
return connectedProfiles;
|
||||
}
|
||||
|
||||
// Enumerate WLAN interfaces
|
||||
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
|
||||
dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanEnumInterfaces failed with error: {}", dwResult);
|
||||
WlanCloseHandle(hClient, NULL);
|
||||
return connectedProfiles;
|
||||
}
|
||||
try {
|
||||
// Load the DLL dynamically as Windows server OSes may not have the Wireless LAN Service installed. The DLL will only
|
||||
// exist on the system if said service is installed, and we don't want to block Windscribe install on the server OS
|
||||
// by statically linking to the DLL.
|
||||
wsl::SystemLibLoader wlanLib("wlanapi.dll");
|
||||
const auto wlanOpenHandle = wlanLib.getFunction<DWORD WINAPI(DWORD, PVOID, PDWORD, PHANDLE)>("WlanOpenHandle");
|
||||
const auto wlanCloseHandle = wlanLib.getFunction<DWORD WINAPI(HANDLE, PVOID)>("WlanCloseHandle");
|
||||
const auto wlanEnumInterfaces = wlanLib.getFunction<DWORD WINAPI(HANDLE, PVOID, PWLAN_INTERFACE_INFO_LIST*)>("WlanEnumInterfaces");
|
||||
const auto wlanQueryInterface = wlanLib.getFunction<DWORD WINAPI(HANDLE, const GUID*, WLAN_INTF_OPCODE, PVOID, PDWORD, PVOID*, PWLAN_OPCODE_VALUE_TYPE)>("WlanQueryInterface");
|
||||
const auto wlanFreeMemory = wlanLib.getFunction<VOID WINAPI(PVOID)>("WlanFreeMemory");
|
||||
|
||||
if (pIfList != NULL) {
|
||||
// Iterate through all interfaces
|
||||
for (DWORD i = 0; i < pIfList->dwNumberOfItems; i++) {
|
||||
PWLAN_INTERFACE_INFO pIfInfo = &pIfList->InterfaceInfo[i];
|
||||
HANDLE hClient = NULL;
|
||||
DWORD dwClientVersion = 2;
|
||||
DWORD dwCurVersion = 0;
|
||||
|
||||
// Check if interface is connected
|
||||
if (pIfInfo->isState == wlan_interface_state_connected) {
|
||||
PWLAN_CONNECTION_ATTRIBUTES pConnectInfo = NULL;
|
||||
DWORD connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES);
|
||||
// Initialize WLAN handle
|
||||
DWORD dwResult = wlanOpenHandle(dwClientVersion, NULL, &dwCurVersion, &hClient);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanOpenHandle failed with error: {}", dwResult);
|
||||
return connectedProfiles;
|
||||
}
|
||||
|
||||
// Get connection attributes
|
||||
dwResult = WlanQueryInterface(
|
||||
hClient,
|
||||
&pIfInfo->InterfaceGuid,
|
||||
wlan_intf_opcode_current_connection,
|
||||
NULL,
|
||||
&connectInfoSize,
|
||||
(PVOID*)&pConnectInfo,
|
||||
NULL
|
||||
);
|
||||
auto exitGuard = wsl::wsScopeGuard([&] {
|
||||
wlanCloseHandle(hClient, NULL);
|
||||
});
|
||||
|
||||
if (dwResult == ERROR_SUCCESS) {
|
||||
// Get profile name (this is what we need to preserve)
|
||||
std::wstring profileName(pConnectInfo->strProfileName);
|
||||
connectedProfiles.insert(profileName);
|
||||
// Enumerate WLAN interfaces
|
||||
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
|
||||
dwResult = wlanEnumInterfaces(hClient, NULL, &pIfList);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanEnumInterfaces failed with error: {}", dwResult);
|
||||
return connectedProfiles;
|
||||
}
|
||||
|
||||
WlanFreeMemory(pConnectInfo);
|
||||
if (pIfList != NULL) {
|
||||
// Iterate through all interfaces
|
||||
for (DWORD i = 0; i < pIfList->dwNumberOfItems; i++) {
|
||||
PWLAN_INTERFACE_INFO pIfInfo = &pIfList->InterfaceInfo[i];
|
||||
|
||||
// Check if interface is connected
|
||||
if (pIfInfo->isState == wlan_interface_state_connected) {
|
||||
PWLAN_CONNECTION_ATTRIBUTES pConnectInfo = NULL;
|
||||
DWORD connectInfoSize = sizeof(WLAN_CONNECTION_ATTRIBUTES);
|
||||
|
||||
// Get connection attributes
|
||||
dwResult = wlanQueryInterface(
|
||||
hClient,
|
||||
&pIfInfo->InterfaceGuid,
|
||||
wlan_intf_opcode_current_connection,
|
||||
NULL,
|
||||
&connectInfoSize,
|
||||
(PVOID*)&pConnectInfo,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (dwResult == ERROR_SUCCESS) {
|
||||
// Get profile name (this is what we need to preserve)
|
||||
std::wstring profileName(pConnectInfo->strProfileName);
|
||||
connectedProfiles.insert(profileName);
|
||||
|
||||
wlanFreeMemory(pConnectInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
wlanFreeMemory(pIfList);
|
||||
}
|
||||
WlanFreeMemory(pIfList);
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::warn("getCurrentConnectedProfiles failed: {}", e.what());
|
||||
}
|
||||
|
||||
WlanCloseHandle(hClient, NULL);
|
||||
|
||||
return connectedProfiles;
|
||||
}
|
||||
|
||||
@@ -186,85 +207,105 @@ bool ClearWiFiHistory::clearNlaCache()
|
||||
|
||||
bool ClearWiFiHistory::clearWlanProfileFiles(const std::set<std::wstring>& connectedProfiles)
|
||||
{
|
||||
HANDLE hClient = NULL;
|
||||
DWORD dwMaxClient = 2;
|
||||
DWORD dwClientVersion = 0;
|
||||
DWORD dwResult = 0;
|
||||
|
||||
// Initialize WLAN handle
|
||||
dwResult = WlanOpenHandle(dwMaxClient, NULL, &dwClientVersion, &hClient);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanOpenHandle failed with error: {}", dwResult);
|
||||
return false;
|
||||
if (!wsl::SystemLibLoader::isAvailable("wlanapi.dll")) {
|
||||
// Returning true since the OS has no Wi-Fi support without this DLL.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool overallSuccess = true;
|
||||
int totalDeleted = 0;
|
||||
int totalPreserved = 0;
|
||||
|
||||
// Enumerate WLAN interfaces
|
||||
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
|
||||
dwResult = WlanEnumInterfaces(hClient, NULL, &pIfList);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanEnumInterfaces failed with error: {}", dwResult);
|
||||
WlanCloseHandle(hClient, NULL);
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
wsl::SystemLibLoader wlanLib("wlanapi.dll");
|
||||
const auto wlanOpenHandle = wlanLib.getFunction<DWORD WINAPI(DWORD, PVOID, PDWORD, PHANDLE)>("WlanOpenHandle");
|
||||
const auto wlanCloseHandle = wlanLib.getFunction<DWORD WINAPI(HANDLE, PVOID)>("WlanCloseHandle");
|
||||
const auto wlanEnumInterfaces = wlanLib.getFunction<DWORD WINAPI(HANDLE, PVOID, PWLAN_INTERFACE_INFO_LIST*)>("WlanEnumInterfaces");
|
||||
const auto wlanGetProfileList = wlanLib.getFunction<DWORD WINAPI(HANDLE, const GUID*, PVOID, PWLAN_PROFILE_INFO_LIST*)>("WlanGetProfileList");
|
||||
const auto wlanDeleteProfile = wlanLib.getFunction<DWORD WINAPI(HANDLE, const GUID*, LPCWSTR, PVOID)>("WlanDeleteProfile");
|
||||
const auto wlanFreeMemory = wlanLib.getFunction<VOID WINAPI(PVOID)>("WlanFreeMemory");
|
||||
|
||||
if (pIfList != NULL) {
|
||||
// Iterate through all interfaces
|
||||
for (DWORD i = 0; i < pIfList->dwNumberOfItems; i++) {
|
||||
PWLAN_INTERFACE_INFO pIfInfo = &pIfList->InterfaceInfo[i];
|
||||
HANDLE hClient = NULL;
|
||||
DWORD dwMaxClient = 2;
|
||||
DWORD dwClientVersion = 0;
|
||||
|
||||
// Get list of profiles for this interface
|
||||
PWLAN_PROFILE_INFO_LIST pProfileList = NULL;
|
||||
dwResult = WlanGetProfileList(hClient, &pIfInfo->InterfaceGuid, NULL, &pProfileList);
|
||||
// Initialize WLAN handle
|
||||
DWORD dwResult = wlanOpenHandle(dwMaxClient, NULL, &dwClientVersion, &hClient);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanOpenHandle failed with error: {}", dwResult);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanGetProfileList failed for interface {} with error: {}", i, dwResult);
|
||||
overallSuccess = false;
|
||||
continue;
|
||||
}
|
||||
if (pProfileList == nullptr) {
|
||||
spdlog::error("pProfileList = nullptr in ClearWiFiHistory::clearWlanProfileFiles");
|
||||
overallSuccess = false;
|
||||
continue;
|
||||
}
|
||||
auto exitGuard = wsl::wsScopeGuard([&] {
|
||||
wlanCloseHandle(hClient, NULL);
|
||||
});
|
||||
|
||||
// Iterate through profiles
|
||||
for (DWORD j = 0; j < pProfileList->dwNumberOfItems; j++) {
|
||||
PWLAN_PROFILE_INFO pProfile = &pProfileList->ProfileInfo[j];
|
||||
std::wstring profileName(pProfile->strProfileName);
|
||||
int totalDeleted = 0;
|
||||
int totalPreserved = 0;
|
||||
|
||||
// Check if this profile is currently connected
|
||||
if (connectedProfiles.find(profileName) != connectedProfiles.end()) {
|
||||
spdlog::debug(L"Preserving connected WiFi profile: {}", profileName);
|
||||
totalPreserved++;
|
||||
// Enumerate WLAN interfaces
|
||||
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
|
||||
dwResult = wlanEnumInterfaces(hClient, NULL, &pIfList);
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanEnumInterfaces failed with error: {}", dwResult);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pIfList != NULL) {
|
||||
// Iterate through all interfaces
|
||||
for (DWORD i = 0; i < pIfList->dwNumberOfItems; i++) {
|
||||
PWLAN_INTERFACE_INFO pIfInfo = &pIfList->InterfaceInfo[i];
|
||||
|
||||
// Get list of profiles for this interface
|
||||
PWLAN_PROFILE_INFO_LIST pProfileList = NULL;
|
||||
dwResult = wlanGetProfileList(hClient, &pIfInfo->InterfaceGuid, NULL, &pProfileList);
|
||||
|
||||
if (dwResult != ERROR_SUCCESS) {
|
||||
spdlog::warn("WlanGetProfileList failed for interface {} with error: {}", i, dwResult);
|
||||
overallSuccess = false;
|
||||
continue;
|
||||
}
|
||||
if (pProfileList == nullptr) {
|
||||
spdlog::error("pProfileList = nullptr in ClearWiFiHistory::clearWlanProfileFiles");
|
||||
overallSuccess = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Delete the profile
|
||||
dwResult = WlanDeleteProfile(hClient, &pIfInfo->InterfaceGuid, profileName.c_str(), NULL);
|
||||
// Iterate through profiles
|
||||
for (DWORD j = 0; j < pProfileList->dwNumberOfItems; j++) {
|
||||
PWLAN_PROFILE_INFO pProfile = &pProfileList->ProfileInfo[j];
|
||||
std::wstring profileName(pProfile->strProfileName);
|
||||
|
||||
if (dwResult == ERROR_SUCCESS) {
|
||||
spdlog::debug(L"Deleted WiFi profile: {}", profileName);
|
||||
totalDeleted++;
|
||||
} else {
|
||||
spdlog::warn(L"Failed to delete WiFi profile '{}': error {}", profileName, dwResult);
|
||||
overallSuccess = false;
|
||||
// Check if this profile is currently connected
|
||||
if (connectedProfiles.find(profileName) != connectedProfiles.end()) {
|
||||
spdlog::debug(L"Preserving connected WiFi profile: {}", profileName);
|
||||
totalPreserved++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Delete the profile
|
||||
dwResult = wlanDeleteProfile(hClient, &pIfInfo->InterfaceGuid, profileName.c_str(), NULL);
|
||||
|
||||
if (dwResult == ERROR_SUCCESS) {
|
||||
spdlog::debug(L"Deleted WiFi profile: {}", profileName);
|
||||
totalDeleted++;
|
||||
} else {
|
||||
spdlog::warn(L"Failed to delete WiFi profile '{}': error {}", profileName, dwResult);
|
||||
overallSuccess = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (pProfileList != NULL) {
|
||||
wlanFreeMemory(pProfileList);
|
||||
}
|
||||
}
|
||||
|
||||
if (pProfileList != NULL) {
|
||||
WlanFreeMemory(pProfileList);
|
||||
}
|
||||
wlanFreeMemory(pIfList);
|
||||
}
|
||||
|
||||
WlanFreeMemory(pIfList);
|
||||
spdlog::debug("WiFi profile cleanup: {} profiles deleted, {} preserved", totalDeleted, totalPreserved);
|
||||
} catch (const std::exception& e) {
|
||||
spdlog::warn("clearWlanProfileFiles failed: {}", e.what());
|
||||
return false;
|
||||
}
|
||||
WlanCloseHandle(hClient, NULL);
|
||||
|
||||
spdlog::debug("WiFi profile cleanup: {} profiles deleted, {} preserved", totalDeleted, totalPreserved);
|
||||
|
||||
return overallSuccess;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
@@ -40,4 +41,3 @@ private:
|
||||
// Returns: true if operation succeeded, false otherwise
|
||||
static bool clearRegistry(const std::wstring &subKey);
|
||||
};
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
#include <sstream>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "../../../client/client-common/types/global_consts.h"
|
||||
#include "../../../client/client-common/utils/servicecontrolmanager.h"
|
||||
#include "../../../client/client-common/utils/win32handle.h"
|
||||
#include "../../../client/client-common/utils/wsscopeguard.h"
|
||||
#include "types/global_consts.h"
|
||||
#include "utils/servicecontrolmanager.h"
|
||||
#include "utils/win32handle.h"
|
||||
#include "utils/wsscopeguard.h"
|
||||
#include "../../common/helper_commands.h"
|
||||
#include "../executecmd.h"
|
||||
#include "../utils.h"
|
||||
|
||||
Reference in New Issue
Block a user