mirror of
https://github.com/qbittorrent/qBittorrent.git
synced 2024-11-26 03:06:37 +03:00
Move some functions into Utils::OS namespace
Also remove `Utils::Misc::getUserIDString()` since there are no use of it.
This commit is contained in:
parent
f20f009b78
commit
794cce38f3
9 changed files with 277 additions and 288 deletions
|
@ -85,6 +85,7 @@
|
||||||
#include "base/torrentfileswatcher.h"
|
#include "base/torrentfileswatcher.h"
|
||||||
#include "base/utils/fs.h"
|
#include "base/utils/fs.h"
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
|
#include "base/utils/os.h"
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
#include "base/version.h"
|
#include "base/version.h"
|
||||||
#include "applicationinstancemanager.h"
|
#include "applicationinstancemanager.h"
|
||||||
|
@ -99,10 +100,6 @@
|
||||||
#include "gui/uithememanager.h"
|
#include "gui/uithememanager.h"
|
||||||
#include "gui/utils.h"
|
#include "gui/utils.h"
|
||||||
#include "gui/windowstate.h"
|
#include "gui/windowstate.h"
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include "base/utils/os.h"
|
|
||||||
#endif // Q_OS_WIN
|
|
||||||
#endif // DISABLE_GUI
|
#endif // DISABLE_GUI
|
||||||
|
|
||||||
#ifndef DISABLE_WEBUI
|
#ifndef DISABLE_WEBUI
|
||||||
|
@ -1207,12 +1204,12 @@ void Application::setProcessMemoryPriority(const MemoryPriority priority)
|
||||||
void Application::applyMemoryPriority() const
|
void Application::applyMemoryPriority() const
|
||||||
{
|
{
|
||||||
using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD);
|
using SETPROCESSINFORMATION = BOOL (WINAPI *)(HANDLE, PROCESS_INFORMATION_CLASS, LPVOID, DWORD);
|
||||||
const auto setProcessInformation = Utils::Misc::loadWinAPI<SETPROCESSINFORMATION>(u"Kernel32.dll"_s, "SetProcessInformation");
|
const auto setProcessInformation = Utils::OS::loadWinAPI<SETPROCESSINFORMATION>(u"Kernel32.dll"_s, "SetProcessInformation");
|
||||||
if (!setProcessInformation) // only available on Windows >= 8
|
if (!setProcessInformation) // only available on Windows >= 8
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using SETTHREADINFORMATION = BOOL (WINAPI *)(HANDLE, THREAD_INFORMATION_CLASS, LPVOID, DWORD);
|
using SETTHREADINFORMATION = BOOL (WINAPI *)(HANDLE, THREAD_INFORMATION_CLASS, LPVOID, DWORD);
|
||||||
const auto setThreadInformation = Utils::Misc::loadWinAPI<SETTHREADINFORMATION>(u"Kernel32.dll"_s, "SetThreadInformation");
|
const auto setThreadInformation = Utils::OS::loadWinAPI<SETTHREADINFORMATION>(u"Kernel32.dll"_s, "SetThreadInformation");
|
||||||
if (!setThreadInformation) // only available on Windows >= 8
|
if (!setThreadInformation) // only available on Windows >= 8
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1352,7 +1349,7 @@ void Application::cleanup()
|
||||||
if (m_shutdownAct != ShutdownDialogAction::Exit)
|
if (m_shutdownAct != ShutdownDialogAction::Exit)
|
||||||
{
|
{
|
||||||
qDebug() << "Sending computer shutdown/suspend/hibernate signal...";
|
qDebug() << "Sending computer shutdown/suspend/hibernate signal...";
|
||||||
Utils::Misc::shutdownComputer(m_shutdownAct);
|
Utils::OS::shutdownComputer(m_shutdownAct);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
#include "sessionimpl.h"
|
#include "sessionimpl.h"
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/os.h"
|
||||||
#endif // Q_OS_MACOS || Q_OS_WIN
|
#endif // Q_OS_MACOS || Q_OS_WIN
|
||||||
|
|
||||||
using namespace BitTorrent;
|
using namespace BitTorrent;
|
||||||
|
@ -2248,7 +2248,7 @@ void TorrentImpl::handleFileCompletedAlert(const lt::file_completed_alert *p)
|
||||||
if (isDownloading())
|
if (isDownloading())
|
||||||
{
|
{
|
||||||
const Path fullpath = actualStorageLocation() / actualPath;
|
const Path fullpath = actualStorageLocation() / actualPath;
|
||||||
Utils::Misc::applyMarkOfTheWeb(fullpath);
|
Utils::OS::applyMarkOfTheWeb(fullpath);
|
||||||
}
|
}
|
||||||
#endif // Q_OS_MACOS || Q_OS_WIN
|
#endif // Q_OS_MACOS || Q_OS_WIN
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,10 @@
|
||||||
#include "base/utils/gzip.h"
|
#include "base/utils/gzip.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||||
|
#include "base/utils/os.h"
|
||||||
|
#endif // Q_OS_MACOS || Q_OS_WIN
|
||||||
|
|
||||||
const int MAX_REDIRECTIONS = 20; // the common value for web browsers
|
const int MAX_REDIRECTIONS = 20; // the common value for web browsers
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -151,7 +155,7 @@ void Net::DownloadHandlerImpl::processFinishedDownload()
|
||||||
m_result.filePath = result.value();
|
m_result.filePath = result.value();
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||||
Utils::Misc::applyMarkOfTheWeb(m_result.filePath, m_result.url);
|
Utils::OS::applyMarkOfTheWeb(m_result.filePath, m_result.url);
|
||||||
#endif // Q_OS_MACOS || Q_OS_WIN
|
#endif // Q_OS_MACOS || Q_OS_WIN
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -167,7 +171,7 @@ void Net::DownloadHandlerImpl::processFinishedDownload()
|
||||||
m_result.filePath = destinationPath;
|
m_result.filePath = destinationPath;
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||||
Utils::Misc::applyMarkOfTheWeb(m_result.filePath, m_result.url);
|
Utils::OS::applyMarkOfTheWeb(m_result.filePath, m_result.url);
|
||||||
#endif // Q_OS_MACOS || Q_OS_WIN
|
#endif // Q_OS_MACOS || Q_OS_WIN
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -30,23 +30,6 @@
|
||||||
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <powrprof.h>
|
|
||||||
#include <shlobj.h>
|
|
||||||
#else
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
#include <Carbon/Carbon.h>
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
#include <CoreServices/CoreServices.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <boost/version.hpp>
|
#include <boost/version.hpp>
|
||||||
#include <libtorrent/version.hpp>
|
#include <libtorrent/version.hpp>
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
|
@ -60,16 +43,11 @@
|
||||||
#include <QMimeDatabase>
|
#include <QMimeDatabase>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
|
#include <QString>
|
||||||
#include <QSysInfo>
|
#include <QSysInfo>
|
||||||
#include <QVector>
|
|
||||||
|
|
||||||
#ifdef QBT_USES_DBUS
|
#include "base/path.h"
|
||||||
#include <QDBusInterface>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "base/types.h"
|
|
||||||
#include "base/unicodestrings.h"
|
#include "base/unicodestrings.h"
|
||||||
#include "base/utils/fs.h"
|
|
||||||
#include "base/utils/string.h"
|
#include "base/utils/string.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
@ -113,145 +91,6 @@ namespace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Utils::Misc::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &action)
|
|
||||||
{
|
|
||||||
#if defined(Q_OS_WIN)
|
|
||||||
HANDLE hToken; // handle to process token
|
|
||||||
TOKEN_PRIVILEGES tkp; // pointer to token structure
|
|
||||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
|
|
||||||
return;
|
|
||||||
// Get the LUID for shutdown privilege.
|
|
||||||
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
|
|
||||||
&tkp.Privileges[0].Luid);
|
|
||||||
|
|
||||||
tkp.PrivilegeCount = 1; // one privilege to set
|
|
||||||
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
||||||
|
|
||||||
// Get shutdown privilege for this process.
|
|
||||||
|
|
||||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
|
|
||||||
(PTOKEN_PRIVILEGES) NULL, 0);
|
|
||||||
|
|
||||||
// Cannot test the return value of AdjustTokenPrivileges.
|
|
||||||
|
|
||||||
if (GetLastError() != ERROR_SUCCESS)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (action == ShutdownDialogAction::Suspend)
|
|
||||||
{
|
|
||||||
::SetSuspendState(FALSE, FALSE, FALSE);
|
|
||||||
}
|
|
||||||
else if (action == ShutdownDialogAction::Hibernate)
|
|
||||||
{
|
|
||||||
::SetSuspendState(TRUE, FALSE, FALSE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const QString msg = QCoreApplication::translate("misc", "qBittorrent will shutdown the computer now because all downloads are complete.");
|
|
||||||
auto msgWchar = std::make_unique<wchar_t[]>(msg.length() + 1);
|
|
||||||
msg.toWCharArray(msgWchar.get());
|
|
||||||
::InitiateSystemShutdownW(nullptr, msgWchar.get(), 10, TRUE, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable shutdown privilege.
|
|
||||||
tkp.Privileges[0].Attributes = 0;
|
|
||||||
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES) NULL, 0);
|
|
||||||
|
|
||||||
#elif defined(Q_OS_MACOS)
|
|
||||||
AEEventID EventToSend;
|
|
||||||
if (action != ShutdownDialogAction::Shutdown)
|
|
||||||
EventToSend = kAESleep;
|
|
||||||
else
|
|
||||||
EventToSend = kAEShutDown;
|
|
||||||
AEAddressDesc targetDesc;
|
|
||||||
const ProcessSerialNumber kPSNOfSystemProcess = {0, kSystemProcess};
|
|
||||||
AppleEvent eventReply = {typeNull, NULL};
|
|
||||||
AppleEvent appleEventToSend = {typeNull, NULL};
|
|
||||||
|
|
||||||
OSStatus error = AECreateDesc(typeProcessSerialNumber, &kPSNOfSystemProcess,
|
|
||||||
sizeof(kPSNOfSystemProcess), &targetDesc);
|
|
||||||
|
|
||||||
if (error != noErr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
error = AECreateAppleEvent(kCoreEventClass, EventToSend, &targetDesc,
|
|
||||||
kAutoGenerateReturnID, kAnyTransactionID, &appleEventToSend);
|
|
||||||
|
|
||||||
AEDisposeDesc(&targetDesc);
|
|
||||||
if (error != noErr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
error = AESend(&appleEventToSend, &eventReply, kAENoReply,
|
|
||||||
kAENormalPriority, kAEDefaultTimeout, NULL, NULL);
|
|
||||||
|
|
||||||
AEDisposeDesc(&appleEventToSend);
|
|
||||||
if (error != noErr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
AEDisposeDesc(&eventReply);
|
|
||||||
|
|
||||||
#elif defined(QBT_USES_DBUS)
|
|
||||||
// Use dbus to power off / suspend the system
|
|
||||||
if (action != ShutdownDialogAction::Shutdown)
|
|
||||||
{
|
|
||||||
// Some recent systems use systemd's logind
|
|
||||||
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
|
|
||||||
u"org.freedesktop.login1.Manager"_s, QDBusConnection::systemBus());
|
|
||||||
if (login1Iface.isValid())
|
|
||||||
{
|
|
||||||
if (action == ShutdownDialogAction::Suspend)
|
|
||||||
login1Iface.call(u"Suspend"_s, false);
|
|
||||||
else
|
|
||||||
login1Iface.call(u"Hibernate"_s, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Else, other recent systems use UPower
|
|
||||||
QDBusInterface upowerIface(u"org.freedesktop.UPower"_s, u"/org/freedesktop/UPower"_s,
|
|
||||||
u"org.freedesktop.UPower"_s, QDBusConnection::systemBus());
|
|
||||||
if (upowerIface.isValid())
|
|
||||||
{
|
|
||||||
if (action == ShutdownDialogAction::Suspend)
|
|
||||||
upowerIface.call(u"Suspend"_s);
|
|
||||||
else
|
|
||||||
upowerIface.call(u"Hibernate"_s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// HAL (older systems)
|
|
||||||
QDBusInterface halIface(u"org.freedesktop.Hal"_s, u"/org/freedesktop/Hal/devices/computer"_s,
|
|
||||||
u"org.freedesktop.Hal.Device.SystemPowerManagement"_s,
|
|
||||||
QDBusConnection::systemBus());
|
|
||||||
if (action == ShutdownDialogAction::Suspend)
|
|
||||||
halIface.call(u"Suspend"_s, 5);
|
|
||||||
else
|
|
||||||
halIface.call(u"Hibernate"_s);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Some recent systems use systemd's logind
|
|
||||||
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
|
|
||||||
u"org.freedesktop.login1.Manager"_s, QDBusConnection::systemBus());
|
|
||||||
if (login1Iface.isValid())
|
|
||||||
{
|
|
||||||
login1Iface.call(u"PowerOff"_s, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Else, other recent systems use ConsoleKit
|
|
||||||
QDBusInterface consolekitIface(u"org.freedesktop.ConsoleKit"_s, u"/org/freedesktop/ConsoleKit/Manager"_s,
|
|
||||||
u"org.freedesktop.ConsoleKit.Manager"_s, QDBusConnection::systemBus());
|
|
||||||
if (consolekitIface.isValid())
|
|
||||||
{
|
|
||||||
consolekitIface.call(u"Stop"_s);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// HAL (older systems)
|
|
||||||
QDBusInterface halIface(u"org.freedesktop.Hal"_s, u"/org/freedesktop/Hal/devices/computer"_s,
|
|
||||||
u"org.freedesktop.Hal.Device.SystemPowerManagement"_s,
|
|
||||||
QDBusConnection::systemBus());
|
|
||||||
halIface.call(u"Shutdown"_s);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Utils::Misc::unitString(const SizeUnit unit, const bool isSpeed)
|
QString Utils::Misc::unitString(const SizeUnit unit, const bool isSpeed)
|
||||||
{
|
{
|
||||||
const auto &unitString = units[static_cast<int>(unit)];
|
const auto &unitString = units[static_cast<int>(unit)];
|
||||||
|
@ -413,21 +252,6 @@ QString Utils::Misc::userFriendlyDuration(const qlonglong seconds, const qlonglo
|
||||||
return QCoreApplication::translate("misc", "%1y %2d", "e.g: 2 years 10 days").arg(QString::number(years), QString::number(days));
|
return QCoreApplication::translate("misc", "%1y %2d", "e.g: 2 years 10 days").arg(QString::number(years), QString::number(days));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Utils::Misc::getUserIDString()
|
|
||||||
{
|
|
||||||
QString uid = u"0"_s;
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
const int UNLEN = 256;
|
|
||||||
WCHAR buffer[UNLEN + 1] = {0};
|
|
||||||
DWORD buffer_len = sizeof(buffer) / sizeof(*buffer);
|
|
||||||
if (::GetUserNameW(buffer, &buffer_len))
|
|
||||||
uid = QString::fromWCharArray(buffer);
|
|
||||||
#else
|
|
||||||
uid = QString::number(getuid());
|
|
||||||
#endif
|
|
||||||
return uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString Utils::Misc::languageToLocalizedString(const QString &localeStr)
|
QString Utils::Misc::languageToLocalizedString(const QString &localeStr)
|
||||||
{
|
{
|
||||||
if (localeStr.startsWith(u"eo", Qt::CaseInsensitive))
|
if (localeStr.startsWith(u"eo", Qt::CaseInsensitive))
|
||||||
|
@ -620,70 +444,3 @@ QString Utils::Misc::zlibVersionString()
|
||||||
static const auto version {QString::fromLatin1(zlibVersion())};
|
static const auto version {QString::fromLatin1(zlibVersion())};
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
Path Utils::Misc::windowsSystemPath()
|
|
||||||
{
|
|
||||||
static const Path path = []() -> Path
|
|
||||||
{
|
|
||||||
WCHAR systemPath[MAX_PATH] = {0};
|
|
||||||
GetSystemDirectoryW(systemPath, sizeof(systemPath) / sizeof(WCHAR));
|
|
||||||
return Path(QString::fromWCharArray(systemPath));
|
|
||||||
}();
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
#endif // Q_OS_WIN
|
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
|
||||||
bool Utils::Misc::applyMarkOfTheWeb(const Path &file, const QString &url)
|
|
||||||
{
|
|
||||||
Q_ASSERT(url.isEmpty() || url.startsWith(u"http:") || url.startsWith(u"https:"));
|
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
|
||||||
// References:
|
|
||||||
// https://searchfox.org/mozilla-central/rev/ffdc4971dc18e1141cb2a90c2b0b776365650270/xpcom/io/CocoaFileUtils.mm#230
|
|
||||||
// https://github.com/transmission/transmission/blob/f62f7427edb1fd5c430e0ef6956bbaa4f03ae597/macosx/Torrent.mm#L1945-L1955
|
|
||||||
|
|
||||||
CFMutableDictionaryRef properties = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0
|
|
||||||
, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
|
||||||
if (properties == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
::CFDictionarySetValue(properties, kLSQuarantineTypeKey, kLSQuarantineTypeOtherDownload);
|
|
||||||
if (!url.isEmpty())
|
|
||||||
::CFDictionarySetValue(properties, kLSQuarantineDataURLKey, url.toCFString());
|
|
||||||
|
|
||||||
const CFStringRef fileString = file.toString().toCFString();
|
|
||||||
const CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault
|
|
||||||
, fileString, kCFURLPOSIXPathStyle, false);
|
|
||||||
|
|
||||||
const Boolean success = ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey
|
|
||||||
, properties, NULL);
|
|
||||||
|
|
||||||
::CFRelease(fileURL);
|
|
||||||
::CFRelease(fileString);
|
|
||||||
::CFRelease(properties);
|
|
||||||
|
|
||||||
return success;
|
|
||||||
#elif defined(Q_OS_WIN)
|
|
||||||
const QString zoneIDStream = file.toString() + u":Zone.Identifier";
|
|
||||||
HANDLE handle = ::CreateFileW(zoneIDStream.toStdWString().c_str(), GENERIC_WRITE
|
|
||||||
, (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE)
|
|
||||||
, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// 5.6.1 Zone.Identifier Stream Name
|
|
||||||
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8
|
|
||||||
const QString hostURL = !url.isEmpty() ? url : u"about:internet"_s;
|
|
||||||
const QByteArray zoneID = QByteArrayLiteral("[ZoneTransfer]\r\nZoneId=3\r\n")
|
|
||||||
+ u"HostUrl=%1\r\n"_s.arg(hostURL).toUtf8();
|
|
||||||
|
|
||||||
DWORD written = 0;
|
|
||||||
const BOOL writeResult = ::WriteFile(handle, zoneID.constData(), zoneID.size(), &written, nullptr);
|
|
||||||
::CloseHandle(handle);
|
|
||||||
|
|
||||||
return writeResult && (written == zoneID.size());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif // Q_OS_MACOS || Q_OS_WIN
|
|
||||||
|
|
|
@ -28,20 +28,13 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <QtSystemDetection>
|
#include <QtTypes>
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
#include "base/pathfwd.h"
|
||||||
#include <windows.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <QString>
|
class QString;
|
||||||
|
|
||||||
#include "base/path.h"
|
|
||||||
|
|
||||||
enum class ShutdownDialogAction;
|
|
||||||
|
|
||||||
/* Miscellaneous functions that can be useful */
|
/* Miscellaneous functions that can be useful */
|
||||||
|
|
||||||
namespace Utils::Misc
|
namespace Utils::Misc
|
||||||
{
|
{
|
||||||
// use binary prefix standards from IEC 60027-2
|
// use binary prefix standards from IEC 60027-2
|
||||||
|
@ -68,8 +61,6 @@ namespace Utils::Misc
|
||||||
|
|
||||||
QString parseHtmlLinks(const QString &rawText);
|
QString parseHtmlLinks(const QString &rawText);
|
||||||
|
|
||||||
void shutdownComputer(const ShutdownDialogAction &action);
|
|
||||||
|
|
||||||
QString osName();
|
QString osName();
|
||||||
QString boostVersionString();
|
QString boostVersionString();
|
||||||
QString libtorrentVersionString();
|
QString libtorrentVersionString();
|
||||||
|
@ -90,22 +81,6 @@ namespace Utils::Misc
|
||||||
// Take a number of seconds and return a user-friendly
|
// Take a number of seconds and return a user-friendly
|
||||||
// time duration like "1d 2h 10m".
|
// time duration like "1d 2h 10m".
|
||||||
QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap = -1, TimeResolution resolution = TimeResolution::Minutes);
|
QString userFriendlyDuration(qlonglong seconds, qlonglong maxCap = -1, TimeResolution resolution = TimeResolution::Minutes);
|
||||||
QString getUserIDString();
|
|
||||||
|
|
||||||
QString languageToLocalizedString(const QString &localeStr);
|
QString languageToLocalizedString(const QString &localeStr);
|
||||||
|
|
||||||
#ifdef Q_OS_WIN
|
|
||||||
Path windowsSystemPath();
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
T loadWinAPI(const QString &source, const char *funcName)
|
|
||||||
{
|
|
||||||
const std::wstring path = (windowsSystemPath() / Path(source)).toString().toStdWString();
|
|
||||||
return reinterpret_cast<T>(::GetProcAddress(::LoadLibraryW(path.c_str()), funcName));
|
|
||||||
}
|
|
||||||
#endif // Q_OS_WIN
|
|
||||||
|
|
||||||
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
|
||||||
bool applyMarkOfTheWeb(const Path &file, const QString &url = {});
|
|
||||||
#endif // Q_OS_MACOS || Q_OS_WIN
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,168 @@
|
||||||
|
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <powrprof.h>
|
||||||
|
#include <shlobj.h>
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
|
#include <Carbon/Carbon.h>
|
||||||
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
#include <CoreServices/CoreServices.h>
|
#include <CoreServices/CoreServices.h>
|
||||||
#endif // Q_OS_MACOS
|
#endif // Q_OS_MACOS
|
||||||
|
|
||||||
#include <QString>
|
#ifdef QBT_USES_DBUS
|
||||||
|
#include <QDBusInterface>
|
||||||
|
#endif // QBT_USES_DBUS
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/path.h"
|
#include "base/types.h"
|
||||||
|
|
||||||
|
void Utils::OS::shutdownComputer([[maybe_unused]] const ShutdownDialogAction &action)
|
||||||
|
{
|
||||||
|
#if defined(Q_OS_WIN)
|
||||||
|
HANDLE hToken; // handle to process token
|
||||||
|
TOKEN_PRIVILEGES tkp; // pointer to token structure
|
||||||
|
if (!::OpenProcessToken(::GetCurrentProcess(), (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY), &hToken))
|
||||||
|
return;
|
||||||
|
// Get the LUID for shutdown privilege.
|
||||||
|
::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
|
||||||
|
|
||||||
|
tkp.PrivilegeCount = 1; // one privilege to set
|
||||||
|
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
|
|
||||||
|
// Get shutdown privilege for this process.
|
||||||
|
|
||||||
|
::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
|
||||||
|
|
||||||
|
// Cannot test the return value of AdjustTokenPrivileges.
|
||||||
|
|
||||||
|
if (::GetLastError() != ERROR_SUCCESS)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (action == ShutdownDialogAction::Suspend)
|
||||||
|
{
|
||||||
|
::SetSuspendState(FALSE, FALSE, FALSE);
|
||||||
|
}
|
||||||
|
else if (action == ShutdownDialogAction::Hibernate)
|
||||||
|
{
|
||||||
|
::SetSuspendState(TRUE, FALSE, FALSE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const QString msg = QCoreApplication::translate("misc"
|
||||||
|
, "qBittorrent will shutdown the computer now because all downloads are complete.");
|
||||||
|
auto msgWchar = std::make_unique<wchar_t[]>(msg.length() + 1);
|
||||||
|
msg.toWCharArray(msgWchar.get());
|
||||||
|
::InitiateSystemShutdownW(nullptr, msgWchar.get(), 10, TRUE, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable shutdown privilege.
|
||||||
|
tkp.Privileges[0].Attributes = 0;
|
||||||
|
::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
|
||||||
|
|
||||||
|
#elif defined(Q_OS_MACOS)
|
||||||
|
AEEventID EventToSend;
|
||||||
|
if (action != ShutdownDialogAction::Shutdown)
|
||||||
|
EventToSend = kAESleep;
|
||||||
|
else
|
||||||
|
EventToSend = kAEShutDown;
|
||||||
|
AEAddressDesc targetDesc;
|
||||||
|
const ProcessSerialNumber kPSNOfSystemProcess = {0, kSystemProcess};
|
||||||
|
AppleEvent eventReply = {typeNull, NULL};
|
||||||
|
AppleEvent appleEventToSend = {typeNull, NULL};
|
||||||
|
|
||||||
|
OSStatus error = ::AECreateDesc(typeProcessSerialNumber, &kPSNOfSystemProcess
|
||||||
|
, sizeof(kPSNOfSystemProcess), &targetDesc);
|
||||||
|
|
||||||
|
if (error != noErr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
error = ::AECreateAppleEvent(kCoreEventClass, EventToSend, &targetDesc, kAutoGenerateReturnID
|
||||||
|
, kAnyTransactionID, &appleEventToSend);
|
||||||
|
|
||||||
|
AEDisposeDesc(&targetDesc);
|
||||||
|
if (error != noErr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
error = ::AESend(&appleEventToSend, &eventReply, kAENoReply, kAENormalPriority, kAEDefaultTimeout
|
||||||
|
, NULL, NULL);
|
||||||
|
|
||||||
|
::AEDisposeDesc(&appleEventToSend);
|
||||||
|
if (error != noErr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
::AEDisposeDesc(&eventReply);
|
||||||
|
|
||||||
|
#elif defined(QBT_USES_DBUS)
|
||||||
|
// Use dbus to power off / suspend the system
|
||||||
|
if (action != ShutdownDialogAction::Shutdown)
|
||||||
|
{
|
||||||
|
// Some recent systems use systemd's logind
|
||||||
|
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
|
||||||
|
u"org.freedesktop.login1.Manager"_s, QDBusConnection::systemBus());
|
||||||
|
if (login1Iface.isValid())
|
||||||
|
{
|
||||||
|
if (action == ShutdownDialogAction::Suspend)
|
||||||
|
login1Iface.call(u"Suspend"_s, false);
|
||||||
|
else
|
||||||
|
login1Iface.call(u"Hibernate"_s, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Else, other recent systems use UPower
|
||||||
|
QDBusInterface upowerIface(u"org.freedesktop.UPower"_s, u"/org/freedesktop/UPower"_s,
|
||||||
|
u"org.freedesktop.UPower"_s, QDBusConnection::systemBus());
|
||||||
|
if (upowerIface.isValid())
|
||||||
|
{
|
||||||
|
if (action == ShutdownDialogAction::Suspend)
|
||||||
|
upowerIface.call(u"Suspend"_s);
|
||||||
|
else
|
||||||
|
upowerIface.call(u"Hibernate"_s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// HAL (older systems)
|
||||||
|
QDBusInterface halIface(u"org.freedesktop.Hal"_s, u"/org/freedesktop/Hal/devices/computer"_s,
|
||||||
|
u"org.freedesktop.Hal.Device.SystemPowerManagement"_s,
|
||||||
|
QDBusConnection::systemBus());
|
||||||
|
if (action == ShutdownDialogAction::Suspend)
|
||||||
|
halIface.call(u"Suspend"_s, 5);
|
||||||
|
else
|
||||||
|
halIface.call(u"Hibernate"_s);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Some recent systems use systemd's logind
|
||||||
|
QDBusInterface login1Iface(u"org.freedesktop.login1"_s, u"/org/freedesktop/login1"_s,
|
||||||
|
u"org.freedesktop.login1.Manager"_s, QDBusConnection::systemBus());
|
||||||
|
if (login1Iface.isValid())
|
||||||
|
{
|
||||||
|
login1Iface.call(u"PowerOff"_s, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Else, other recent systems use ConsoleKit
|
||||||
|
QDBusInterface consolekitIface(u"org.freedesktop.ConsoleKit"_s, u"/org/freedesktop/ConsoleKit/Manager"_s,
|
||||||
|
u"org.freedesktop.ConsoleKit.Manager"_s, QDBusConnection::systemBus());
|
||||||
|
if (consolekitIface.isValid())
|
||||||
|
{
|
||||||
|
consolekitIface.call(u"Stop"_s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// HAL (older systems)
|
||||||
|
QDBusInterface halIface(u"org.freedesktop.Hal"_s, u"/org/freedesktop/Hal/devices/computer"_s,
|
||||||
|
u"org.freedesktop.Hal.Device.SystemPowerManagement"_s,
|
||||||
|
QDBusConnection::systemBus());
|
||||||
|
halIface.call(u"Shutdown"_s);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
namespace
|
namespace
|
||||||
|
@ -104,3 +258,70 @@ void Utils::OS::setMagnetLinkAssoc()
|
||||||
::LSSetDefaultHandlerForURLScheme(magnetUrlScheme, myBundleId);
|
::LSSetDefaultHandlerForURLScheme(magnetUrlScheme, myBundleId);
|
||||||
}
|
}
|
||||||
#endif // Q_OS_MACOS
|
#endif // Q_OS_MACOS
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
Path Utils::OS::windowsSystemPath()
|
||||||
|
{
|
||||||
|
static const Path path = []() -> Path
|
||||||
|
{
|
||||||
|
WCHAR systemPath[MAX_PATH] = {0};
|
||||||
|
::GetSystemDirectoryW(systemPath, sizeof(systemPath) / sizeof(WCHAR));
|
||||||
|
return Path(QString::fromWCharArray(systemPath));
|
||||||
|
}();
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||||
|
bool Utils::OS::applyMarkOfTheWeb(const Path &file, const QString &url)
|
||||||
|
{
|
||||||
|
Q_ASSERT(url.isEmpty() || url.startsWith(u"http:") || url.startsWith(u"https:"));
|
||||||
|
|
||||||
|
#ifdef Q_OS_MACOS
|
||||||
|
// References:
|
||||||
|
// https://searchfox.org/mozilla-central/rev/ffdc4971dc18e1141cb2a90c2b0b776365650270/xpcom/io/CocoaFileUtils.mm#230
|
||||||
|
// https://github.com/transmission/transmission/blob/f62f7427edb1fd5c430e0ef6956bbaa4f03ae597/macosx/Torrent.mm#L1945-L1955
|
||||||
|
|
||||||
|
CFMutableDictionaryRef properties = ::CFDictionaryCreateMutable(kCFAllocatorDefault, 0
|
||||||
|
, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||||
|
if (properties == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
::CFDictionarySetValue(properties, kLSQuarantineTypeKey, kLSQuarantineTypeOtherDownload);
|
||||||
|
if (!url.isEmpty())
|
||||||
|
::CFDictionarySetValue(properties, kLSQuarantineDataURLKey, url.toCFString());
|
||||||
|
|
||||||
|
const CFStringRef fileString = file.toString().toCFString();
|
||||||
|
const CFURLRef fileURL = ::CFURLCreateWithFileSystemPath(kCFAllocatorDefault
|
||||||
|
, fileString, kCFURLPOSIXPathStyle, false);
|
||||||
|
|
||||||
|
const Boolean success = ::CFURLSetResourcePropertyForKey(fileURL, kCFURLQuarantinePropertiesKey
|
||||||
|
, properties, NULL);
|
||||||
|
|
||||||
|
::CFRelease(fileURL);
|
||||||
|
::CFRelease(fileString);
|
||||||
|
::CFRelease(properties);
|
||||||
|
|
||||||
|
return success;
|
||||||
|
#elif defined(Q_OS_WIN)
|
||||||
|
const QString zoneIDStream = file.toString() + u":Zone.Identifier";
|
||||||
|
HANDLE handle = ::CreateFileW(zoneIDStream.toStdWString().c_str(), GENERIC_WRITE
|
||||||
|
, (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE)
|
||||||
|
, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// 5.6.1 Zone.Identifier Stream Name
|
||||||
|
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-fscc/6e3f7352-d11c-4d76-8c39-2516a9df36e8
|
||||||
|
const QString hostURL = !url.isEmpty() ? url : u"about:internet"_s;
|
||||||
|
const QByteArray zoneID = QByteArrayLiteral("[ZoneTransfer]\r\nZoneId=3\r\n")
|
||||||
|
+ u"HostUrl=%1\r\n"_s.arg(hostURL).toUtf8();
|
||||||
|
|
||||||
|
DWORD written = 0;
|
||||||
|
const BOOL writeResult = ::WriteFile(handle, zoneID.constData(), zoneID.size(), &written, nullptr);
|
||||||
|
::CloseHandle(handle);
|
||||||
|
|
||||||
|
return writeResult && (written == zoneID.size());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif // Q_OS_MACOS || Q_OS_WIN
|
||||||
|
|
|
@ -32,12 +32,41 @@
|
||||||
|
|
||||||
#include <QtSystemDetection>
|
#include <QtSystemDetection>
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "base/path.h"
|
||||||
|
|
||||||
|
enum class ShutdownDialogAction;
|
||||||
|
|
||||||
namespace Utils::OS
|
namespace Utils::OS
|
||||||
{
|
{
|
||||||
|
void shutdownComputer(const ShutdownDialogAction &action);
|
||||||
|
|
||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
bool isTorrentFileAssocSet();
|
bool isTorrentFileAssocSet();
|
||||||
void setTorrentFileAssoc();
|
void setTorrentFileAssoc();
|
||||||
bool isMagnetLinkAssocSet();
|
bool isMagnetLinkAssocSet();
|
||||||
void setMagnetLinkAssoc();
|
void setMagnetLinkAssoc();
|
||||||
#endif // Q_OS_MACOS
|
#endif // Q_OS_MACOS
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
Path windowsSystemPath();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T loadWinAPI(const QString &source, const char *funcName)
|
||||||
|
{
|
||||||
|
const std::wstring path = (windowsSystemPath() / Path(source)).toString().toStdWString();
|
||||||
|
return reinterpret_cast<T>(::GetProcAddress(::LoadLibraryW(path.c_str()), funcName));
|
||||||
|
}
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
|
#if defined(Q_OS_MACOS) || defined(Q_OS_WIN)
|
||||||
|
bool applyMarkOfTheWeb(const Path &file, const QString &url = {});
|
||||||
|
#endif // Q_OS_MACOS || Q_OS_WIN
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,10 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#include "base/global.h"
|
#include "base/global.h"
|
||||||
#include "base/utils/misc.h"
|
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include "base/utils/os.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -56,7 +59,7 @@ namespace
|
||||||
using result_type = uint32_t;
|
using result_type = uint32_t;
|
||||||
|
|
||||||
RandomLayer()
|
RandomLayer()
|
||||||
: m_rtlGenRandom {Utils::Misc::loadWinAPI<PRTLGENRANDOM>(u"Advapi32.dll"_s, "SystemFunction036")}
|
: m_rtlGenRandom {Utils::OS::loadWinAPI<PRTLGENRANDOM>(u"Advapi32.dll"_s, "SystemFunction036")}
|
||||||
{
|
{
|
||||||
if (!m_rtlGenRandom)
|
if (!m_rtlGenRandom)
|
||||||
qFatal("Failed to load RtlGenRandom()");
|
qFatal("Failed to load RtlGenRandom()");
|
||||||
|
|
|
@ -28,6 +28,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#import "badgeview.h"
|
#import "badgeview.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
#include "base/utils/misc.h"
|
#include "base/utils/misc.h"
|
||||||
|
|
||||||
static const CGFloat kBetweenPadding = 2.0;
|
static const CGFloat kBetweenPadding = 2.0;
|
||||||
|
|
Loading…
Reference in a new issue