Skip to content

Commit b580884

Browse files
committed
v2.21.5
1 parent 1e2a37d commit b580884

File tree

25 files changed

+610
-320
lines changed

25 files changed

+610
-320
lines changed

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
blank_issues_enabled: false

CHANGELOG.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
2.21.5 (09/03/2026)
2+
All:
3+
* Added favourite locations to preferences import/export. #1691
4+
* Improved additional Slovak translations from GitHub user kubalav. #1684
5+
* Improved Spanish translations. #1685
6+
* Fixed DPI scaling issues in login/signup UI. #1628
7+
* Fixed a failed assertion at app start. #1582
8+
* Fixed incorrect window ordering when returning to login screen. #1673
9+
* Fixed incorrect Update button size. #1659
10+
* Fixed login not working with windscribe-cli. #1693
11+
Windows:
12+
* Improved app (helper) to support running on Windows Server 2019/2022 without requiring the Wireless LAN Service to be installed. #1681
13+
14+
115
2.21.4 (02/03/2026)
216
All:
317
* Added sound preview for sound notifications. #1582
@@ -6,7 +20,7 @@ All:
620
* Fixed more incorrect window ordering issues. #1673
721
* Fixed IP utilities sometimes not available after changing from custom config to regular location. #1677
822
Windows:
9-
* Improved adapter network identfication state detection on Windows. #1680
23+
* Improved adapter network identification state detection on Windows. #1680
1024
* Fixed IKEv2 may get stuck after waking from sleep. #1676
1125
macOS:
1226
* Fixed app may crash during launch. #1679

src/client/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ find_package(OpenSSL REQUIRED)
2222
find_package(Boost REQUIRED COMPONENTS serialization)
2323
find_package(spdlog CONFIG REQUIRED)
2424

25-
include("${CMAKE_SOURCE_DIR}/cmake/fetch_wsnet.cmake")
25+
include("${CMAKE_CURRENT_LIST_DIR}/../../cmake/fetch_wsnet.cmake")
2626

2727
# build_all.py sets this option when invoked with the '--sign' flag. Disabled by default
2828
option(DEFINE_USE_SIGNATURE_CHECK_MACRO "Add define USE_SIGNATURE_CHECK to project" OFF)
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#pragma once
2+
3+
#include <string>
4+
#include <system_error>
5+
#include <utility>
6+
7+
#include <windows.h>
8+
9+
/*
10+
11+
This class ensures a DLL is loaded from the system32, or its localized equivalent, folder
12+
to mitigate DLL hijacking attacks.
13+
14+
NOTES:
15+
- You do not need to use this class for 'known' DLLs listed in HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs.
16+
The OS loader ensures all DLLs in that list, and any DLLs loaded by them, are only loaded from system32.
17+
- One should use this class to replace any statically linked DLLs listed in cmake's target_link_libraries declaration.
18+
TL;DR your cmake file should not have a target_link_libraries declaration.
19+
20+
*/
21+
22+
namespace wsl {
23+
24+
class SystemLibLoader {
25+
public:
26+
SystemLibLoader(const SystemLibLoader&) = delete;
27+
SystemLibLoader& operator=(const SystemLibLoader&) = delete;
28+
29+
SystemLibLoader(SystemLibLoader &&other) noexcept : handle_(other.handle_)
30+
{
31+
other.handle_ = nullptr;
32+
}
33+
34+
SystemLibLoader& operator=(SystemLibLoader &&other) noexcept
35+
{
36+
if (this != &other) {
37+
std::swap(handle_, other.handle_);
38+
}
39+
return *this;
40+
}
41+
42+
explicit SystemLibLoader(const char *libName)
43+
{
44+
if (!libName) {
45+
throw std::invalid_argument("Null parameter passed to SystemLibLoader");
46+
}
47+
48+
handle_ = ::LoadLibraryExA(libName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_REQUIRE_SIGNED_TARGET);
49+
50+
if (!handle_) {
51+
throw std::system_error(::GetLastError(), std::system_category(), std::string("SystemLibLoader failed to load DLL ") + libName);
52+
}
53+
}
54+
55+
~SystemLibLoader()
56+
{
57+
if (handle_) {
58+
::FreeLibrary(handle_);
59+
}
60+
}
61+
62+
static bool isAvailable(const char *libName) noexcept
63+
{
64+
if (!libName) {
65+
return false;
66+
}
67+
68+
HMODULE handle = ::LoadLibraryExA(libName, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_REQUIRE_SIGNED_TARGET);
69+
if (handle) {
70+
::FreeLibrary(handle);
71+
return true;
72+
}
73+
74+
return false;
75+
}
76+
77+
template<typename T>
78+
T *getFunction(const char *name) const
79+
{
80+
return reinterpret_cast<T *>(getProcAddress(name));
81+
}
82+
83+
private:
84+
HMODULE handle_ = nullptr;
85+
86+
FARPROC getProcAddress(const char *name) const
87+
{
88+
if (!name) {
89+
throw std::invalid_argument("Null parameter passed to getProcAddress");
90+
}
91+
92+
if (!handle_) {
93+
throw std::logic_error("The DLL has not been loaded");
94+
}
95+
96+
auto symbol = ::GetProcAddress(handle_, name);
97+
98+
if (symbol == nullptr) {
99+
throw std::system_error(::GetLastError(), std::system_category(), std::string("SystemLibLoader failed to load function ") + name);
100+
}
101+
102+
return symbol;
103+
}
104+
};
105+
106+
}

src/client/client-common/version/windscribe_version.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
#define WINDSCRIBE_MAJOR_VERSION 2
44
#define WINDSCRIBE_MINOR_VERSION 21
5-
#define WINDSCRIBE_BUILD_VERSION 4
5+
#define WINDSCRIBE_BUILD_VERSION 5
66

77
// only one of these should be enabled; neither -> stable
88
//#define WINDSCRIBE_IS_BETA

src/client/frontend/frontend-common/locations/locationsmodel_manager.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,16 @@ void LocationsModelManager::saveFavoriteLocations()
202202
locationsModel_->saveFavoriteLocations();
203203
}
204204

205+
QJsonArray LocationsModelManager::favoriteLocationsToJson() const
206+
{
207+
return locationsModel_->favoriteLocationsToJson();
208+
}
209+
210+
void LocationsModelManager::setFavoriteLocationsFromJson(const QJsonArray &arr)
211+
{
212+
locationsModel_->setFavoriteLocationsFromJson(arr);
213+
}
214+
205215
void LocationsModelManager::onChangeConnectionSpeedTimer()
206216
{
207217
for (QHash<LocationID, PingTime>::const_iterator it = connectionSpeeds_.constBegin(); it != connectionSpeeds_.constEnd(); ++it)

src/client/frontend/frontend-common/locations/locationsmodel_manager.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class LocationsModelManager : public QObject
4343

4444
void saveFavoriteLocations();
4545

46+
QJsonArray favoriteLocationsToJson() const;
47+
void setFavoriteLocationsFromJson(const QJsonArray &arr);
48+
4649
QJsonObject renamedLocations() const;
4750
void setRenamedLocations(const QJsonObject &obj);
4851
void resetRenamedLocations();

src/client/frontend/frontend-common/locations/model/favoritelocationsstorage.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include <QDataStream>
44
#include <QDataStream>
55
#include <QIODevice>
6+
#include <QJsonArray>
7+
#include <QJsonObject>
68
#include <QSettings>
79

810
#include "utils/simplecrypt.h"
@@ -131,4 +133,40 @@ void FavoriteLocationsStorage::writeToSettings()
131133
isFavoriteLocationsSetModified_ = false;
132134
}
133135

136+
QJsonArray FavoriteLocationsStorage::toJson() const
137+
{
138+
QJsonArray arr;
139+
for (auto it = favoriteLocations_.constBegin(); it != favoriteLocations_.constEnd(); ++it) {
140+
QJsonObject entry;
141+
entry["type"] = it.key().type();
142+
entry["id"] = it.key().id();
143+
entry["city"] = it.key().city();
144+
if (!it.value().pinnedHostname.isEmpty())
145+
entry["pinnedHostname"] = it.value().pinnedHostname;
146+
if (!it.value().pinnedIp.isEmpty())
147+
entry["pinnedIp"] = it.value().pinnedIp;
148+
arr.append(entry);
149+
}
150+
return arr;
151+
}
152+
153+
void FavoriteLocationsStorage::fromJson(const QJsonArray &arr)
154+
{
155+
favoriteLocations_.clear();
156+
for (const QJsonValue &val : arr) {
157+
if (!val.isObject())
158+
continue;
159+
QJsonObject entry = val.toObject();
160+
if (!entry.contains("type") || !entry.contains("id") || !entry.contains("city"))
161+
continue;
162+
LocationID loc(entry["type"].toInt(), entry["id"].toInt(), entry["city"].toString());
163+
FavoriteData data;
164+
data.pinnedHostname = entry["pinnedHostname"].toString();
165+
data.pinnedIp = entry["pinnedIp"].toString();
166+
favoriteLocations_.insert(loc, data);
167+
}
168+
isFavoriteLocationsSetModified_ = true;
169+
writeToSettings();
170+
}
171+
134172
} //namespace gui_locations

src/client/frontend/frontend-common/locations/model/favoritelocationsstorage.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#pragma once
22

3+
#include <QJsonArray>
34
#include <QObject>
45
#include <QSet>
56
#include <QHash>
@@ -21,6 +22,9 @@ class FavoriteLocationsStorage : public QObject
2122
void readFromSettings();
2223
void writeToSettings();
2324

25+
QJsonArray toJson() const;
26+
void fromJson(const QJsonArray &arr);
27+
2428
private:
2529
struct FavoriteData {
2630
QString pinnedHostname;

src/client/frontend/frontend-common/locations/model/locationsmodel.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,18 @@ void LocationsModel::saveFavoriteLocations()
507507
favoriteLocationsStorage_.writeToSettings();
508508
}
509509

510+
QJsonArray LocationsModel::favoriteLocationsToJson() const
511+
{
512+
return favoriteLocationsStorage_.toJson();
513+
}
514+
515+
void LocationsModel::setFavoriteLocationsFromJson(const QJsonArray &arr)
516+
{
517+
beginResetModel();
518+
favoriteLocationsStorage_.fromJson(arr);
519+
endResetModel();
520+
}
521+
510522
QVariant LocationsModel::dataForLocation(int row, int role) const
511523
{
512524
if (role == Qt::DisplayRole)

0 commit comments

Comments
 (0)