}
// Display the error message and exit the process
const std::wstring errorMsgText{ reinterpret_cast<wchar_t *>(lpMsgBuf) };
- const std::wstring errorMsg = std::vformat(
+ const std::wstring errorMsg = SafeVFormat(
WMi18n::GetInstance().GetTranslationW("general.error.winapi.text"),
- std::make_wformat_args(
- functionName,
- lastError,
- errorMsgText));
+ functionName,
+ lastError,
+ errorMsgText);
TaskDialog(
nullptr, nullptr, PROGRAM_NAME, errorMsg.c_str(),
nullptr, TDCBF_OK_BUTTON, TD_ERROR_ICON, nullptr);
std::wstring ConvertStringToWideString(const std::string &ansiString)
{
+ // For CP_UTF8 the only valid flags are 0 and MB_ERR_INVALID_CHARS.
std::wstring unicodeString;
- auto wideCharSize = MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, ansiString.c_str(), -1, nullptr, 0);
+ auto wideCharSize = MultiByteToWideChar(CP_UTF8, 0, ansiString.c_str(), -1, nullptr, 0);
if (wideCharSize == 0) {
return L"";
}
unicodeString.reserve(wideCharSize);
unicodeString.resize(wideCharSize - 1);
- wideCharSize = MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, ansiString.c_str(), -1, &unicodeString[0], wideCharSize);
+ wideCharSize = MultiByteToWideChar(CP_UTF8, 0, ansiString.c_str(), -1, &unicodeString[0], wideCharSize);
return unicodeString;
}
bool LaunchBrowser(HWND hParent, const std::wstring &url)
{
+ // Only hand real web URLs to the shell. Anything else (local file paths,
+ // UNC shares, other protocol handlers) would be executed/opened directly
+ // by ShellExecute, which is dangerous for URLs that come from the
+ // update file or other external sources.
+ auto hasPrefix = [&url](const wchar_t *prefix) {
+ const size_t prefixLen = wcslen(prefix);
+ return url.length() > prefixLen
+ && _wcsnicmp(url.c_str(), prefix, prefixLen) == 0;
+ };
+ if (!hasPrefix(L"https://") && !hasPrefix(L"http://")) {
+ WMLog::GetInstance().LogError(
+ L"Refused to open \"%s\": not a http(s) URL", url.c_str());
+ return false;
+ }
+
SHELLEXECUTEINFOW sxi = { 0 };
sxi.cbSize = sizeof(sxi);
sxi.nShow = SW_SHOWNORMAL;
#pragma once
+#include <format>
#include <string>
+#include <string_view>
#include <optional>
#include <atlbase.h>
// =============================================================================
// Utility
+// Formats a runtime format string (e.g. from a translation file) without
+// letting a malformed placeholder terminate the process: on std::format_error
+// the unformatted string is returned instead.
+template<typename... Args>
+std::wstring SafeVFormat(std::wstring_view fmt, Args&&... args)
+{
+ try {
+ return std::vformat(fmt, std::make_wformat_args(args...));
+ } catch (const std::format_error &) {
+ return std::wstring{ fmt };
+ }
+}
+
void ShowWindowsError(const wchar_t *functionName, DWORD lastError = -1);
bool GetWinMuteVersion(std::wstring &versNumber);
#include "common.h"
-/* wParam = Connected = 1 | Disconnected = 0. lParam = Pointer to Wifi Name */
+/* wParam = Connected = 1 | Disconnected = 0. lParam = Pointer to Wifi Name.
+ Sent synchronously; the name is only valid for the duration of the call
+ and is owned by the sender. */
constexpr int WM_WIFISTATUSCHANGED = WM_USER + 400;
class WifiDetector {
void WlanNotificationCallback(PWLAN_NOTIFICATION_DATA notifyData);
private:
+ bool IsNetworkRelevant(const wchar_t *profileName) const;
+
HWND hNotifyWnd_;
HANDLE wlanHandle_;
// If "true" then networks_ contains all networks where the workstation should
bool isMuteList_;
bool initialized_;
std::vector<std::wstring> networks_;
+ // WlanNotificationCallback is invoked on a WLAN service thread, while
+ // SetNetworkList is called from the UI thread.
+ mutable std::mutex networksMutex_;
};