clean up /platform/linux/ folder
This commit is contained in:
parent
ac7de6213a
commit
271e948c1f
21 changed files with 0 additions and 1611 deletions
|
|
@ -1,36 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "backendlogsobserver.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusPendingReply>
|
||||
|
||||
namespace {
|
||||
Logger logger({LOG_LINUX, LOG_CONTROLLER}, "BackendLogsObserver");
|
||||
}
|
||||
|
||||
BackendLogsObserver::BackendLogsObserver(
|
||||
QObject* parent, std::function<void(const QString&)>&& callback)
|
||||
: QObject(parent), m_callback(std::move(callback)) {
|
||||
MVPN_COUNT_CTOR(BackendLogsObserver);
|
||||
}
|
||||
|
||||
BackendLogsObserver::~BackendLogsObserver() {
|
||||
MVPN_COUNT_DTOR(BackendLogsObserver);
|
||||
}
|
||||
|
||||
void BackendLogsObserver::completed(QDBusPendingCallWatcher* call) {
|
||||
QDBusPendingReply<QString> reply = *call;
|
||||
if (reply.isError()) {
|
||||
logger.error() << "Error received from the DBus service";
|
||||
m_callback("Failed to retrieve logs from the mozillavpn linuxdaemon.");
|
||||
return;
|
||||
}
|
||||
|
||||
QString status = reply.argumentAt<0>();
|
||||
m_callback(status);
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BACKENDLOGSOBSERVER_H
|
||||
#define BACKENDLOGSOBSERVER_H
|
||||
|
||||
#include <functional>
|
||||
#include <QObject>
|
||||
|
||||
class QDBusPendingCallWatcher;
|
||||
|
||||
class BackendLogsObserver final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(BackendLogsObserver)
|
||||
|
||||
public:
|
||||
BackendLogsObserver(QObject* parent,
|
||||
std::function<void(const QString&)>&& callback);
|
||||
~BackendLogsObserver();
|
||||
|
||||
public slots:
|
||||
void completed(QDBusPendingCallWatcher* call);
|
||||
|
||||
private:
|
||||
std::function<void(const QString&)> m_callback;
|
||||
};
|
||||
|
||||
#endif // BACKENDLOGSOBSERVER_H
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "dbusclient.h"
|
||||
#include "ipaddressrange.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "models/device.h"
|
||||
#include "models/keys.h"
|
||||
#include "models/server.h"
|
||||
#include "mozillavpn.h"
|
||||
#include "settingsholder.h"
|
||||
|
||||
#include <QDBusPendingCall>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QDBusPendingReply>
|
||||
|
||||
constexpr const char* DBUS_SERVICE = "org.mozilla.vpn.dbus";
|
||||
constexpr const char* DBUS_PATH = "/";
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_LINUX, "DBusClient");
|
||||
}
|
||||
|
||||
DBusClient::DBusClient(QObject* parent) : QObject(parent) {
|
||||
MVPN_COUNT_CTOR(DBusClient);
|
||||
|
||||
m_dbus = new OrgMozillaVpnDbusInterface(DBUS_SERVICE, DBUS_PATH,
|
||||
QDBusConnection::systemBus(), this);
|
||||
|
||||
connect(m_dbus, &OrgMozillaVpnDbusInterface::connected, this,
|
||||
&DBusClient::connected);
|
||||
connect(m_dbus, &OrgMozillaVpnDbusInterface::disconnected, this,
|
||||
&DBusClient::disconnected);
|
||||
}
|
||||
|
||||
DBusClient::~DBusClient() { MVPN_COUNT_DTOR(DBusClient); }
|
||||
|
||||
QDBusPendingCallWatcher* DBusClient::version() {
|
||||
logger.debug() << "Version via DBus";
|
||||
QDBusPendingReply<QString> reply = m_dbus->version();
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher,
|
||||
&QDBusPendingCallWatcher::deleteLater);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
QDBusPendingCallWatcher* DBusClient::activate(
|
||||
const Server& server, const Device* device, const Keys* keys, int hopindex,
|
||||
const QList<IPAddressRange>& allowedIPAddressRanges,
|
||||
const QStringList& vpnDisabledApps, const QHostAddress& dnsServer) {
|
||||
QJsonObject json;
|
||||
json.insert("privateKey", QJsonValue(keys->privateKey()));
|
||||
json.insert("deviceIpv4Address", QJsonValue(device->ipv4Address()));
|
||||
json.insert("deviceIpv6Address", QJsonValue(device->ipv6Address()));
|
||||
json.insert("serverIpv4Gateway", QJsonValue(server.ipv4Gateway()));
|
||||
json.insert("serverIpv6Gateway", QJsonValue(server.ipv6Gateway()));
|
||||
json.insert("serverPublicKey", QJsonValue(server.publicKey()));
|
||||
json.insert("serverIpv4AddrIn", QJsonValue(server.ipv4AddrIn()));
|
||||
json.insert("serverIpv6AddrIn", QJsonValue(server.ipv6AddrIn()));
|
||||
json.insert("serverPort", QJsonValue((double)server.choosePort()));
|
||||
json.insert("dnsServer", QJsonValue(dnsServer.toString()));
|
||||
json.insert("hopindex", QJsonValue((double)hopindex));
|
||||
|
||||
QJsonArray allowedIPAddesses;
|
||||
for (const IPAddressRange& i : allowedIPAddressRanges) {
|
||||
QJsonObject range;
|
||||
range.insert("address", QJsonValue(i.ipAddress()));
|
||||
range.insert("range", QJsonValue((double)i.range()));
|
||||
range.insert("isIpv6", QJsonValue(i.type() == IPAddressRange::IPv6));
|
||||
allowedIPAddesses.append(range);
|
||||
};
|
||||
json.insert("allowedIPAddressRanges", allowedIPAddesses);
|
||||
|
||||
QJsonArray disabledApps;
|
||||
for (const QString& i : vpnDisabledApps) {
|
||||
disabledApps.append(QJsonValue(i));
|
||||
logger.debug() << "Disabling:" << i;
|
||||
}
|
||||
json.insert("vpnDisabledApps", disabledApps);
|
||||
|
||||
logger.debug() << "Activate via DBus";
|
||||
QDBusPendingReply<bool> reply =
|
||||
m_dbus->activate(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher,
|
||||
&QDBusPendingCallWatcher::deleteLater);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
QDBusPendingCallWatcher* DBusClient::deactivate() {
|
||||
logger.debug() << "Deactivate via DBus";
|
||||
QDBusPendingReply<bool> reply = m_dbus->deactivate();
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher,
|
||||
&QDBusPendingCallWatcher::deleteLater);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
QDBusPendingCallWatcher* DBusClient::status() {
|
||||
logger.debug() << "Status via DBus";
|
||||
QDBusPendingReply<QString> reply = m_dbus->status();
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher,
|
||||
&QDBusPendingCallWatcher::deleteLater);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
QDBusPendingCallWatcher* DBusClient::getLogs() {
|
||||
logger.debug() << "Get logs via DBus";
|
||||
QDBusPendingReply<QString> reply = m_dbus->getLogs();
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher,
|
||||
&QDBusPendingCallWatcher::deleteLater);
|
||||
return watcher;
|
||||
}
|
||||
|
||||
QDBusPendingCallWatcher* DBusClient::cleanupLogs() {
|
||||
logger.debug() << "Cleanup logs via DBus";
|
||||
QDBusPendingReply<QString> reply = m_dbus->cleanupLogs();
|
||||
QDBusPendingCallWatcher* watcher = new QDBusPendingCallWatcher(reply, this);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher,
|
||||
&QDBusPendingCallWatcher::deleteLater);
|
||||
return watcher;
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DBUSCLIENT_H
|
||||
#define DBUSCLIENT_H
|
||||
|
||||
#include "dbus_interface.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
|
||||
class Server;
|
||||
class Device;
|
||||
class Keys;
|
||||
class IPAddressRange;
|
||||
class QDBusPendingCallWatcher;
|
||||
|
||||
class DBusClient final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(DBusClient)
|
||||
|
||||
public:
|
||||
DBusClient(QObject* parent);
|
||||
~DBusClient();
|
||||
|
||||
QDBusPendingCallWatcher* version();
|
||||
|
||||
QDBusPendingCallWatcher* activate(
|
||||
const Server& server, const Device* device, const Keys* keys,
|
||||
int hopindex, const QList<IPAddressRange>& allowedIPAddressRanges,
|
||||
const QStringList& vpnDisabledApps, const QHostAddress& dnsServer);
|
||||
|
||||
QDBusPendingCallWatcher* deactivate();
|
||||
|
||||
QDBusPendingCallWatcher* status();
|
||||
|
||||
QDBusPendingCallWatcher* getLogs();
|
||||
|
||||
QDBusPendingCallWatcher* cleanupLogs();
|
||||
|
||||
signals:
|
||||
void connected(int hopindex);
|
||||
void disconnected(int hopindex);
|
||||
|
||||
private:
|
||||
OrgMozillaVpnDbusInterface* m_dbus;
|
||||
};
|
||||
|
||||
#endif // DBUSCLIENT_H
|
||||
|
|
@ -1,79 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linuxappimageprovider.h"
|
||||
#include "logger.h"
|
||||
#include "leakdetector.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QString>
|
||||
#include <QSettings>
|
||||
#include <QIcon>
|
||||
|
||||
constexpr const char* PIXMAP_FALLBACK_PATH = "/usr/share/pixmaps/";
|
||||
constexpr const char* DESKTOP_ICON_LOCATION = "/usr/share/icons/";
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_CONTROLLER, "LinuxAppImageProvider");
|
||||
}
|
||||
|
||||
LinuxAppImageProvider::LinuxAppImageProvider(QObject* parent)
|
||||
: AppImageProvider(parent, QQuickImageProvider::Image,
|
||||
QQmlImageProviderBase::ForceAsynchronousImageLoading) {
|
||||
MVPN_COUNT_CTOR(LinuxAppImageProvider);
|
||||
|
||||
QStringList searchPaths = QIcon::fallbackSearchPaths();
|
||||
|
||||
QProcessEnvironment pe = QProcessEnvironment::systemEnvironment();
|
||||
if (pe.contains("XDG_DATA_DIRS")) {
|
||||
QStringList parts = pe.value("XDG_DATA_DIRS").split(":");
|
||||
for (const QString& part : parts) {
|
||||
addFallbackPaths(part + "/icons", searchPaths);
|
||||
}
|
||||
} else {
|
||||
addFallbackPaths(DESKTOP_ICON_LOCATION, searchPaths);
|
||||
}
|
||||
|
||||
if (pe.contains("HOME")) {
|
||||
addFallbackPaths(pe.value("HOME") + "/.local/share/icons", searchPaths);
|
||||
}
|
||||
|
||||
searchPaths << PIXMAP_FALLBACK_PATH;
|
||||
QIcon::setFallbackSearchPaths(searchPaths);
|
||||
}
|
||||
|
||||
LinuxAppImageProvider::~LinuxAppImageProvider() {
|
||||
MVPN_COUNT_DTOR(LinuxAppImageProvider);
|
||||
}
|
||||
|
||||
void LinuxAppImageProvider::addFallbackPaths(const QString& iconDir,
|
||||
QStringList& searchPaths) {
|
||||
searchPaths << iconDir;
|
||||
|
||||
QDirIterator iter(iconDir, QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
while (iter.hasNext()) {
|
||||
QFileInfo fileinfo(iter.next());
|
||||
logger.debug() << "Adding QIcon fallback:" << fileinfo.absoluteFilePath();
|
||||
searchPaths << fileinfo.absoluteFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
// from QQuickImageProvider
|
||||
QImage LinuxAppImageProvider::requestImage(const QString& id, QSize* size,
|
||||
const QSize& requestedSize) {
|
||||
QSettings entry(id, QSettings::IniFormat);
|
||||
entry.beginGroup("Desktop Entry");
|
||||
QString name = entry.value("Icon").toString();
|
||||
|
||||
QIcon icon = QIcon::fromTheme(name);
|
||||
QPixmap pixmap = icon.pixmap(requestedSize);
|
||||
size->setHeight(pixmap.height());
|
||||
size->setWidth(pixmap.width());
|
||||
logger.debug() << "Loaded icon" << icon.name() << "size:" << pixmap.width()
|
||||
<< "x" << pixmap.height();
|
||||
|
||||
return pixmap.toImage();
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXAPPIMAGEPROVIDER_H
|
||||
#define LINUXAPPIMAGEPROVIDER_H
|
||||
|
||||
#include "appimageprovider.h"
|
||||
|
||||
class LinuxAppImageProvider final : public AppImageProvider {
|
||||
public:
|
||||
LinuxAppImageProvider(QObject* parent);
|
||||
~LinuxAppImageProvider();
|
||||
QImage requestImage(const QString& id, QSize* size,
|
||||
const QSize& requestedSize) override;
|
||||
|
||||
private:
|
||||
static void addFallbackPaths(const QString& dataDir,
|
||||
QStringList& fallbackPaths);
|
||||
};
|
||||
|
||||
#endif // LINUXAPPIMAGEPROVIDER_H
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linuxapplistprovider.h"
|
||||
#include "leakdetector.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
#include <QDir>
|
||||
#include <QDirIterator>
|
||||
#include <QSettings>
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include "logger.h"
|
||||
#include "leakdetector.h"
|
||||
|
||||
constexpr const char* DESKTOP_ENTRY_LOCATION = "/usr/share/applications/";
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_CONTROLLER, "LinuxAppListProvider");
|
||||
}
|
||||
|
||||
LinuxAppListProvider::LinuxAppListProvider(QObject* parent)
|
||||
: AppListProvider(parent) {
|
||||
MVPN_COUNT_CTOR(LinuxAppListProvider);
|
||||
}
|
||||
|
||||
LinuxAppListProvider::~LinuxAppListProvider() {
|
||||
MVPN_COUNT_DTOR(LinuxAppListProvider);
|
||||
}
|
||||
|
||||
void LinuxAppListProvider::fetchEntries(const QString& dataDir,
|
||||
QMap<QString, QString>& map) {
|
||||
logger.debug() << "Fetch Application list from" << dataDir;
|
||||
|
||||
QDirIterator iter(dataDir, QStringList() << "*.desktop", QDir::Files);
|
||||
while (iter.hasNext()) {
|
||||
QFileInfo fileinfo(iter.next());
|
||||
QSettings entry(fileinfo.filePath(), QSettings::IniFormat);
|
||||
entry.beginGroup("Desktop Entry");
|
||||
|
||||
/* Filter out everything except visible applications. */
|
||||
if (entry.value("Type").toString() != "Application") {
|
||||
continue;
|
||||
}
|
||||
if (entry.value("NoDisplay", QVariant(false)).toBool()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
map[fileinfo.absoluteFilePath()] = entry.value("Name").toString();
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxAppListProvider::getApplicationList() {
|
||||
logger.debug() << "Fetch Application list from Linux desktop";
|
||||
QMap<QString, QString> out;
|
||||
|
||||
QProcessEnvironment pe = QProcessEnvironment::systemEnvironment();
|
||||
if (pe.contains("XDG_DATA_DIRS")) {
|
||||
QStringList parts = pe.value("XDG_DATA_DIRS").split(":");
|
||||
for (const QString& part : parts) {
|
||||
fetchEntries(part.trimmed() + "/applications", out);
|
||||
}
|
||||
} else {
|
||||
fetchEntries(DESKTOP_ENTRY_LOCATION, out);
|
||||
}
|
||||
|
||||
if (pe.contains("HOME")) {
|
||||
fetchEntries(pe.value("HOME") + "/.local/share/applications", out);
|
||||
}
|
||||
|
||||
emit newAppList(out);
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXAPPLISTPROVIDER_H
|
||||
#define LINUXAPPLISTPROVIDER_H
|
||||
|
||||
#include <applistprovider.h>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
|
||||
class LinuxAppListProvider final : public AppListProvider {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit LinuxAppListProvider(QObject* parent);
|
||||
~LinuxAppListProvider();
|
||||
void getApplicationList() override;
|
||||
|
||||
private:
|
||||
void fetchEntries(const QString& dataDir, QMap<QString, QString>& map);
|
||||
};
|
||||
|
||||
#endif // LINUXAPPLISTPROVIDER_H
|
||||
|
|
@ -1,213 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linuxcontroller.h"
|
||||
#include "backendlogsobserver.h"
|
||||
#include "dbusclient.h"
|
||||
#include "errorhandler.h"
|
||||
#include "ipaddressrange.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "models/device.h"
|
||||
#include "models/keys.h"
|
||||
#include "models/server.h"
|
||||
#include "mozillavpn.h"
|
||||
|
||||
#include <QDBusPendingCallWatcher>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QProcess>
|
||||
#include <QString>
|
||||
|
||||
namespace {
|
||||
Logger logger({LOG_LINUX, LOG_CONTROLLER}, "LinuxController");
|
||||
}
|
||||
|
||||
LinuxController::LinuxController() {
|
||||
MVPN_COUNT_CTOR(LinuxController);
|
||||
|
||||
m_dbus = new DBusClient(this);
|
||||
connect(m_dbus, &DBusClient::connected, this, &LinuxController::hopConnected);
|
||||
connect(m_dbus, &DBusClient::disconnected, this,
|
||||
&LinuxController::hopDisconnected);
|
||||
}
|
||||
|
||||
LinuxController::~LinuxController() { MVPN_COUNT_DTOR(LinuxController); }
|
||||
|
||||
void LinuxController::initialize(const Device* device, const Keys* keys) {
|
||||
Q_UNUSED(device);
|
||||
Q_UNUSED(keys);
|
||||
|
||||
QDBusPendingCallWatcher* watcher = m_dbus->status();
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this,
|
||||
&LinuxController::initializeCompleted);
|
||||
}
|
||||
|
||||
void LinuxController::initializeCompleted(QDBusPendingCallWatcher* call) {
|
||||
QDBusPendingReply<QString> reply = *call;
|
||||
if (reply.isError()) {
|
||||
logger.error() << "Error received from the DBus service";
|
||||
emit initialized(false, false, QDateTime());
|
||||
return;
|
||||
}
|
||||
|
||||
QString status = reply.argumentAt<0>();
|
||||
logger.debug() << "Status:" << status;
|
||||
|
||||
QJsonDocument json = QJsonDocument::fromJson(status.toLocal8Bit());
|
||||
Q_ASSERT(json.isObject());
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
Q_ASSERT(obj.contains("status"));
|
||||
QJsonValue statusValue = obj.value("status");
|
||||
Q_ASSERT(statusValue.isBool());
|
||||
|
||||
emit initialized(true, statusValue.toBool(), QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
void LinuxController::activate(
|
||||
const QList<Server>& serverList, const Device* device, const Keys* keys,
|
||||
const QList<IPAddressRange>& allowedIPAddressRanges,
|
||||
const QList<QString>& vpnDisabledApps, const QHostAddress& dnsServer,
|
||||
Reason reason) {
|
||||
Q_UNUSED(reason);
|
||||
Q_UNUSED(vpnDisabledApps);
|
||||
|
||||
// Activate connections starting from the outermost tunnel
|
||||
for (int hopindex = serverList.count() - 1; hopindex > 0; hopindex--) {
|
||||
const Server& hop = serverList[hopindex];
|
||||
const Server& next = serverList[hopindex - 1];
|
||||
QList<IPAddressRange> hopAddressRanges = {
|
||||
IPAddressRange(next.ipv4AddrIn()), IPAddressRange(next.ipv6AddrIn())};
|
||||
logger.debug() << "LinuxController hopindex" << hopindex << "activated";
|
||||
connect(m_dbus->activate(hop, device, keys, hopindex, hopAddressRanges,
|
||||
QStringList(), QHostAddress(hop.ipv4Gateway())),
|
||||
&QDBusPendingCallWatcher::finished, this,
|
||||
&LinuxController::operationCompleted);
|
||||
}
|
||||
|
||||
// Activate the final hop last
|
||||
logger.debug() << "LinuxController activated";
|
||||
const Server& server = serverList[0];
|
||||
connect(m_dbus->activate(server, device, keys, 0, allowedIPAddressRanges,
|
||||
vpnDisabledApps, dnsServer),
|
||||
&QDBusPendingCallWatcher::finished, this,
|
||||
&LinuxController::operationCompleted);
|
||||
}
|
||||
|
||||
void LinuxController::deactivate(Reason reason) {
|
||||
logger.debug() << "LinuxController deactivated";
|
||||
|
||||
if (reason == ReasonSwitching) {
|
||||
logger.debug() << "No disconnect for quick server switching";
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(m_dbus->deactivate(), &QDBusPendingCallWatcher::finished, this,
|
||||
&LinuxController::operationCompleted);
|
||||
}
|
||||
|
||||
void LinuxController::operationCompleted(QDBusPendingCallWatcher* call) {
|
||||
QDBusPendingReply<bool> reply = *call;
|
||||
if (reply.isError()) {
|
||||
logger.error() << "Error received from the DBus service";
|
||||
MozillaVPN::instance()->errorHandle(ErrorHandler::ControllerError);
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
bool status = reply.argumentAt<0>();
|
||||
if (status) {
|
||||
logger.debug() << "DBus service says: all good.";
|
||||
// we will receive the connected/disconnected() signal;
|
||||
return;
|
||||
}
|
||||
|
||||
logger.error() << "DBus service says: error.";
|
||||
MozillaVPN::instance()->errorHandle(ErrorHandler::ControllerError);
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
void LinuxController::hopConnected(int hopindex) {
|
||||
if (hopindex == 0) {
|
||||
logger.debug() << "LinuxController connected";
|
||||
emit connected();
|
||||
} else {
|
||||
logger.debug() << "LinuxController hopindex" << hopindex << "connected";
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxController::hopDisconnected(int hopindex) {
|
||||
if (hopindex == 0) {
|
||||
logger.debug() << "LinuxController disconnected";
|
||||
emit disconnected();
|
||||
} else {
|
||||
logger.debug() << "LinuxController hopindex" << hopindex << "disconnected";
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxController::checkStatus() {
|
||||
logger.debug() << "Check status";
|
||||
|
||||
QDBusPendingCallWatcher* watcher = m_dbus->status();
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this,
|
||||
&LinuxController::checkStatusCompleted);
|
||||
}
|
||||
|
||||
void LinuxController::checkStatusCompleted(QDBusPendingCallWatcher* call) {
|
||||
QDBusPendingReply<QString> reply = *call;
|
||||
if (reply.isError()) {
|
||||
logger.error() << "Error received from the DBus service";
|
||||
return;
|
||||
}
|
||||
|
||||
QString status = reply.argumentAt<0>();
|
||||
logger.debug() << "Status:" << status;
|
||||
|
||||
QJsonDocument json = QJsonDocument::fromJson(status.toLocal8Bit());
|
||||
Q_ASSERT(json.isObject());
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
Q_ASSERT(obj.contains("status"));
|
||||
QJsonValue statusValue = obj.value("status");
|
||||
Q_ASSERT(statusValue.isBool());
|
||||
|
||||
if (!statusValue.toBool()) {
|
||||
logger.error() << "Unable to retrieve the status from the interface.";
|
||||
return;
|
||||
}
|
||||
|
||||
Q_ASSERT(obj.contains("serverIpv4Gateway"));
|
||||
QJsonValue serverIpv4Gateway = obj.value("serverIpv4Gateway");
|
||||
Q_ASSERT(serverIpv4Gateway.isString());
|
||||
|
||||
Q_ASSERT(obj.contains("deviceIpv4Address"));
|
||||
QJsonValue deviceIpv4Address = obj.value("deviceIpv4Address");
|
||||
Q_ASSERT(deviceIpv4Address.isString());
|
||||
|
||||
Q_ASSERT(obj.contains("txBytes"));
|
||||
QJsonValue txBytes = obj.value("txBytes");
|
||||
Q_ASSERT(txBytes.isDouble());
|
||||
|
||||
Q_ASSERT(obj.contains("rxBytes"));
|
||||
QJsonValue rxBytes = obj.value("rxBytes");
|
||||
Q_ASSERT(rxBytes.isDouble());
|
||||
|
||||
emit statusUpdated(serverIpv4Gateway.toString(), deviceIpv4Address.toString(),
|
||||
txBytes.toDouble(), rxBytes.toDouble());
|
||||
}
|
||||
|
||||
void LinuxController::getBackendLogs(
|
||||
std::function<void(const QString&)>&& a_callback) {
|
||||
std::function<void(const QString&)> callback = std::move(a_callback);
|
||||
|
||||
QDBusPendingCallWatcher* watcher = m_dbus->getLogs();
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished,
|
||||
new BackendLogsObserver(this, std::move(callback)),
|
||||
&BackendLogsObserver::completed);
|
||||
}
|
||||
|
||||
void LinuxController::cleanupBackendLogs() { m_dbus->cleanupLogs(); }
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXCONTROLLER_H
|
||||
#define LINUXCONTROLLER_H
|
||||
|
||||
#include "controllerimpl.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class DBusClient;
|
||||
class QDBusPendingCallWatcher;
|
||||
|
||||
class LinuxController final : public ControllerImpl {
|
||||
Q_DISABLE_COPY_MOVE(LinuxController)
|
||||
|
||||
public:
|
||||
LinuxController();
|
||||
~LinuxController();
|
||||
|
||||
void initialize(const Device* device, const Keys* keys) override;
|
||||
|
||||
void activate(const QList<Server>& serverList, const Device* device,
|
||||
const Keys* keys,
|
||||
const QList<IPAddressRange>& allowedIPAddressRanges,
|
||||
const QList<QString>& vpnDisabledApps,
|
||||
const QHostAddress& dnsServer, Reason reason) override;
|
||||
|
||||
void deactivate(Reason reason) override;
|
||||
|
||||
void checkStatus() override;
|
||||
|
||||
void getBackendLogs(std::function<void(const QString&)>&& callback) override;
|
||||
|
||||
void cleanupBackendLogs() override;
|
||||
|
||||
private slots:
|
||||
void checkStatusCompleted(QDBusPendingCallWatcher* call);
|
||||
void initializeCompleted(QDBusPendingCallWatcher* call);
|
||||
void operationCompleted(QDBusPendingCallWatcher* call);
|
||||
void hopConnected(int hopindex);
|
||||
void hopDisconnected(int hopindex);
|
||||
|
||||
private:
|
||||
DBusClient* m_dbus = nullptr;
|
||||
};
|
||||
|
||||
#endif // LINUXCONTROLLER_H
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "cryptosettings.h"
|
||||
|
||||
void CryptoSettings::resetKey() {}
|
||||
|
||||
bool CryptoSettings::getKey(uint8_t key[CRYPTO_SETTINGS_KEY_SIZE]) {
|
||||
Q_UNUSED(key);
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
CryptoSettings::Version CryptoSettings::getSupportedVersion() {
|
||||
return CryptoSettings::NoEncryption;
|
||||
}
|
||||
|
|
@ -1,127 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linuxdependencies.h"
|
||||
#include "dbusclient.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QMessageBox>
|
||||
|
||||
#include <mntent.h>
|
||||
|
||||
constexpr const char* WG_QUICK = "wg-quick";
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger(LOG_LINUX, "LinuxDependencies");
|
||||
|
||||
void showAlert(const QString& message) {
|
||||
logger.debug() << "Show alert:" << message;
|
||||
|
||||
QMessageBox alert;
|
||||
alert.setText(message);
|
||||
alert.exec();
|
||||
}
|
||||
|
||||
bool findInPath(const char* what) {
|
||||
char* path = getenv("PATH");
|
||||
Q_ASSERT(path);
|
||||
|
||||
QStringList parts = QString(path).split(":");
|
||||
for (const QString& part : parts) {
|
||||
QDir pathDir(part);
|
||||
QFileInfo file(pathDir.filePath(what));
|
||||
if (file.exists()) {
|
||||
logger.debug() << what << "found" << file.filePath();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool checkDaemonVersion() {
|
||||
logger.debug() << "Check Daemon Version";
|
||||
|
||||
DBusClient* dbus = new DBusClient(nullptr);
|
||||
QDBusPendingCallWatcher* watcher = dbus->version();
|
||||
|
||||
bool completed = false;
|
||||
bool value = false;
|
||||
QObject::connect(
|
||||
watcher, &QDBusPendingCallWatcher::finished,
|
||||
[completed = &completed, value = &value](QDBusPendingCallWatcher* call) {
|
||||
*completed = true;
|
||||
|
||||
QDBusPendingReply<QString> reply = *call;
|
||||
if (reply.isError()) {
|
||||
logger.error() << "DBus message received - error";
|
||||
*value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
QString version = reply.argumentAt<0>();
|
||||
*value = version == PROTOCOL_VERSION;
|
||||
|
||||
logger.debug() << "DBus message received - daemon version:" << version
|
||||
<< " - current version:" << PROTOCOL_VERSION;
|
||||
});
|
||||
|
||||
while (!completed) {
|
||||
QCoreApplication::processEvents();
|
||||
}
|
||||
|
||||
delete dbus;
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
bool LinuxDependencies::checkDependencies() {
|
||||
char* path = getenv("PATH");
|
||||
if (!path) {
|
||||
showAlert("No PATH env found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!findInPath(WG_QUICK)) {
|
||||
showAlert("Unable to locate wg-quick");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checkDaemonVersion()) {
|
||||
showAlert("mozillavpn linuxdaemon needs to be updated or restarted.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
QString LinuxDependencies::findCgroupPath(const QString& type) {
|
||||
struct mntent entry;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
FILE* fp = fopen("/etc/mtab", "r");
|
||||
if (fp == NULL) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
while (getmntent_r(fp, &entry, buf, sizeof(buf)) != NULL) {
|
||||
if (strcmp(entry.mnt_type, "cgroup") != 0) {
|
||||
continue;
|
||||
}
|
||||
if (hasmntopt(&entry, type.toLocal8Bit().constData()) != NULL) {
|
||||
fclose(fp);
|
||||
return QString(entry.mnt_dir);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXDEPENDENCIES_H
|
||||
#define LINUXDEPENDENCIES_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class LinuxDependencies final {
|
||||
public:
|
||||
static bool checkDependencies();
|
||||
static QString findCgroupPath(const QString& type);
|
||||
|
||||
private:
|
||||
LinuxDependencies() = default;
|
||||
~LinuxDependencies() = default;
|
||||
|
||||
Q_DISABLE_COPY(LinuxDependencies)
|
||||
};
|
||||
|
||||
#endif // LINUXDEPENDENCIES_H
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linuxnetworkwatcher.h"
|
||||
#include "linuxnetworkwatcherworker.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "timersingleshot.h"
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_LINUX, "LinuxNetworkWatcher");
|
||||
}
|
||||
|
||||
LinuxNetworkWatcher::LinuxNetworkWatcher(QObject* parent)
|
||||
: NetworkWatcherImpl(parent) {
|
||||
MVPN_COUNT_CTOR(LinuxNetworkWatcher);
|
||||
|
||||
m_thread.start();
|
||||
}
|
||||
|
||||
LinuxNetworkWatcher::~LinuxNetworkWatcher() {
|
||||
MVPN_COUNT_DTOR(LinuxNetworkWatcher);
|
||||
|
||||
delete m_worker;
|
||||
|
||||
m_thread.quit();
|
||||
m_thread.wait();
|
||||
}
|
||||
|
||||
void LinuxNetworkWatcher::initialize() {
|
||||
logger.debug() << "initialize";
|
||||
|
||||
m_worker = new LinuxNetworkWatcherWorker(&m_thread);
|
||||
|
||||
connect(this, &LinuxNetworkWatcher::checkDevicesInThread, m_worker,
|
||||
&LinuxNetworkWatcherWorker::checkDevices);
|
||||
|
||||
connect(m_worker, &LinuxNetworkWatcherWorker::unsecuredNetwork, this,
|
||||
&LinuxNetworkWatcher::unsecuredNetwork);
|
||||
|
||||
// Let's wait a few seconds to allow the UI to be fully loaded and shown.
|
||||
// This is not strictly needed, but it's better for user experience because
|
||||
// it makes the UI faster to appear, plus it gives a bit of delay between the
|
||||
// UI to appear and the first notification.
|
||||
TimerSingleShot::create(this, 2000, [this]() {
|
||||
QMetaObject::invokeMethod(m_worker, "initialize", Qt::QueuedConnection);
|
||||
});
|
||||
}
|
||||
|
||||
void LinuxNetworkWatcher::start() {
|
||||
logger.debug() << "actived";
|
||||
NetworkWatcherImpl::start();
|
||||
emit checkDevicesInThread();
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXNETWORKWATCHER_H
|
||||
#define LINUXNETWORKWATCHER_H
|
||||
|
||||
#include "networkwatcherimpl.h"
|
||||
|
||||
#include <QThread>
|
||||
|
||||
class LinuxNetworkWatcherWorker;
|
||||
|
||||
class LinuxNetworkWatcher final : public NetworkWatcherImpl {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit LinuxNetworkWatcher(QObject* parent);
|
||||
~LinuxNetworkWatcher();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
void start() override;
|
||||
|
||||
signals:
|
||||
void checkDevicesInThread();
|
||||
|
||||
private:
|
||||
LinuxNetworkWatcherWorker* m_worker = nullptr;
|
||||
QThread m_thread;
|
||||
};
|
||||
|
||||
#endif // LINUXNETWORKWATCHER_H
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linuxnetworkwatcherworker.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QtDBus/QtDBus>
|
||||
|
||||
// https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NMDeviceType
|
||||
#ifndef NM_DEVICE_TYPE_WIFI
|
||||
# define NM_DEVICE_TYPE_WIFI 2
|
||||
#endif
|
||||
|
||||
// https://developer.gnome.org/NetworkManager/stable/nm-dbus-types.html#NM80211ApFlags
|
||||
// Wifi network has no security
|
||||
#ifndef NM_802_11_AP_SEC_NONE
|
||||
# define NM_802_11_AP_SEC_NONE 0x00000000
|
||||
#endif
|
||||
|
||||
// Wifi network has WEP (40 bits)
|
||||
#ifndef NM_802_11_AP_SEC_PAIR_WEP40
|
||||
# define NM_802_11_AP_SEC_PAIR_WEP40 0x00000001
|
||||
#endif
|
||||
|
||||
// Wifi network has WEP (104 bits)
|
||||
#ifndef NM_802_11_AP_SEC_PAIR_WEP104
|
||||
# define NM_802_11_AP_SEC_PAIR_WEP104 0x00000002
|
||||
#endif
|
||||
|
||||
#define NM_802_11_AP_SEC_WEAK_CRYPTO \
|
||||
(NM_802_11_AP_SEC_PAIR_WEP40 | NM_802_11_AP_SEC_PAIR_WEP104)
|
||||
|
||||
constexpr const char* DBUS_NETWORKMANAGER = "org.freedesktop.NetworkManager";
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_LINUX, "LinuxNetworkWatcherWorker");
|
||||
}
|
||||
|
||||
static inline bool checkUnsecureFlags(int rsnFlags, int wpaFlags) {
|
||||
// If neither WPA nor WPA2/RSN are supported, then the network is unencrypted
|
||||
if (rsnFlags == NM_802_11_AP_SEC_NONE && wpaFlags == NM_802_11_AP_SEC_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Consider the user of weak cryptography to be unsecure
|
||||
if ((rsnFlags & NM_802_11_AP_SEC_WEAK_CRYPTO) ||
|
||||
(wpaFlags & NM_802_11_AP_SEC_WEAK_CRYPTO)) {
|
||||
return false;
|
||||
}
|
||||
// Otherwise, the network is secured with reasonable cryptography
|
||||
return true;
|
||||
}
|
||||
|
||||
LinuxNetworkWatcherWorker::LinuxNetworkWatcherWorker(QThread* thread) {
|
||||
MVPN_COUNT_CTOR(LinuxNetworkWatcherWorker);
|
||||
moveToThread(thread);
|
||||
}
|
||||
|
||||
LinuxNetworkWatcherWorker::~LinuxNetworkWatcherWorker() {
|
||||
MVPN_COUNT_DTOR(LinuxNetworkWatcherWorker);
|
||||
}
|
||||
|
||||
void LinuxNetworkWatcherWorker::initialize() {
|
||||
logger.debug() << "initialize";
|
||||
|
||||
logger.debug()
|
||||
<< "Retrieving the list of wifi network devices from NetworkManager";
|
||||
|
||||
// To know the NeworkManager DBus methods and properties, read the official
|
||||
// documentation:
|
||||
// https://developer.gnome.org/NetworkManager/stable/gdbus-org.freedesktop.NetworkManager.html
|
||||
|
||||
QDBusInterface nm(DBUS_NETWORKMANAGER, "/org/freedesktop/NetworkManager",
|
||||
DBUS_NETWORKMANAGER, QDBusConnection::systemBus());
|
||||
if (!nm.isValid()) {
|
||||
logger.error()
|
||||
<< "Failed to connect to the network manager via system dbus";
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusMessage msg = nm.call("GetDevices");
|
||||
QDBusArgument arg = msg.arguments().at(0).value<QDBusArgument>();
|
||||
if (arg.currentType() != QDBusArgument::ArrayType) {
|
||||
logger.error() << "Expected an array of devices";
|
||||
return;
|
||||
}
|
||||
|
||||
QList<QDBusObjectPath> paths = qdbus_cast<QList<QDBusObjectPath> >(arg);
|
||||
for (const QDBusObjectPath& path : paths) {
|
||||
QString devicePath = path.path();
|
||||
QDBusInterface device(DBUS_NETWORKMANAGER, devicePath,
|
||||
"org.freedesktop.NetworkManager.Device",
|
||||
QDBusConnection::systemBus());
|
||||
if (device.property("DeviceType").toInt() != NM_DEVICE_TYPE_WIFI) {
|
||||
continue;
|
||||
}
|
||||
|
||||
logger.debug() << "Found a wifi device:" << devicePath;
|
||||
m_devicePaths.append(devicePath);
|
||||
|
||||
// Here we monitor the changes.
|
||||
QDBusConnection::systemBus().connect(
|
||||
DBUS_NETWORKMANAGER, devicePath, "org.freedesktop.DBus.Properties",
|
||||
"PropertiesChanged", this,
|
||||
SLOT(propertyChanged(QString, QVariantMap, QStringList)));
|
||||
}
|
||||
|
||||
if (m_devicePaths.isEmpty()) {
|
||||
logger.warning() << "No wifi devices found";
|
||||
return;
|
||||
}
|
||||
|
||||
// We could be already be activated.
|
||||
checkDevices();
|
||||
}
|
||||
|
||||
void LinuxNetworkWatcherWorker::propertyChanged(QString interface,
|
||||
QVariantMap properties,
|
||||
QStringList list) {
|
||||
Q_UNUSED(list);
|
||||
|
||||
logger.debug() << "Properties changed for interface" << interface;
|
||||
|
||||
if (!properties.contains("ActiveAccessPoint")) {
|
||||
logger.debug() << "Access point did not changed. Ignoring the changes";
|
||||
return;
|
||||
}
|
||||
|
||||
checkDevices();
|
||||
}
|
||||
|
||||
void LinuxNetworkWatcherWorker::checkDevices() {
|
||||
logger.debug() << "Checking devices";
|
||||
|
||||
for (const QString& devicePath : m_devicePaths) {
|
||||
QDBusInterface wifiDevice(DBUS_NETWORKMANAGER, devicePath,
|
||||
"org.freedesktop.NetworkManager.Device.Wireless",
|
||||
QDBusConnection::systemBus());
|
||||
|
||||
// Check the access point path
|
||||
QString accessPointPath = wifiDevice.property("ActiveAccessPoint")
|
||||
.value<QDBusObjectPath>()
|
||||
.path();
|
||||
if (accessPointPath.isEmpty()) {
|
||||
logger.warning() << "No access point found";
|
||||
continue;
|
||||
}
|
||||
|
||||
QDBusInterface ap(DBUS_NETWORKMANAGER, accessPointPath,
|
||||
"org.freedesktop.NetworkManager.AccessPoint",
|
||||
QDBusConnection::systemBus());
|
||||
|
||||
QVariant rsnFlags = ap.property("RsnFlags");
|
||||
QVariant wpaFlags = ap.property("WpaFlags");
|
||||
if (!rsnFlags.isValid() || !wpaFlags.isValid()) {
|
||||
// We are probably not connected.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!checkUnsecureFlags(rsnFlags.toInt(), wpaFlags.toInt())) {
|
||||
QString ssid = ap.property("Ssid").toString();
|
||||
QString bssid = ap.property("HwAddress").toString();
|
||||
|
||||
// We have found 1 unsecured network. We don't need to check other wifi
|
||||
// network devices.
|
||||
logger.warning() << "Unsecured AP detected!"
|
||||
<< "rsnFlags:" << rsnFlags.toInt()
|
||||
<< "wpaFlags:" << wpaFlags.toInt() << "ssid:" << ssid;
|
||||
emit unsecuredNetwork(ssid, bssid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXNETWORKWATCHERWORKER_H
|
||||
#define LINUXNETWORKWATCHERWORKER_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QVariant>
|
||||
|
||||
class QThread;
|
||||
|
||||
class LinuxNetworkWatcherWorker final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(LinuxNetworkWatcherWorker)
|
||||
|
||||
public:
|
||||
explicit LinuxNetworkWatcherWorker(QThread* thread);
|
||||
~LinuxNetworkWatcherWorker();
|
||||
|
||||
void checkDevices();
|
||||
|
||||
signals:
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
|
||||
public slots:
|
||||
void initialize();
|
||||
|
||||
private slots:
|
||||
void propertyChanged(QString interface, QVariantMap properties,
|
||||
QStringList list);
|
||||
|
||||
private:
|
||||
// We collect the list of DBus wifi network device paths during the
|
||||
// initialization. When a property of them changes, we check if the access
|
||||
// point is active and unsecure.
|
||||
QStringList m_devicePaths;
|
||||
};
|
||||
|
||||
#endif // LINUXNETWORKWATCHERWORKER_H
|
||||
|
|
@ -1,191 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "linuxpingsender.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <linux/filter.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace {
|
||||
Logger logger({LOG_LINUX, LOG_NETWORKING}, "LinuxPingSender");
|
||||
}
|
||||
|
||||
int LinuxPingSender::createSocket() {
|
||||
// Try creating an ICMP socket. This would be the ideal choice, but it can
|
||||
// fail depending on the kernel config (see: sys.net.ipv4.ping_group_range)
|
||||
m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
if (m_socket >= 0) {
|
||||
m_ident = 0;
|
||||
return m_socket;
|
||||
}
|
||||
if ((errno != EPERM) && (errno != EACCES)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// As a fallback, create a raw socket, which requires root permissions
|
||||
// or CAP_NET_RAW to be granted to the VPN client.
|
||||
m_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
|
||||
if (m_socket < 0) {
|
||||
return -1;
|
||||
}
|
||||
m_ident = getpid() & 0xffff;
|
||||
|
||||
// Attach a BPF filter to discard everything but replies to our echo.
|
||||
struct sock_filter bpf_prog[] = {
|
||||
BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, 0), /* Skip IP header. */
|
||||
BPF_STMT(BPF_LD | BPF_H | BPF_IND, 4), /* Load icmp echo ident */
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, m_ident, 1, 0), /* Ours? */
|
||||
BPF_STMT(BPF_RET | BPF_K, 0), /* Unexpected identifier. Reject. */
|
||||
BPF_STMT(BPF_LD | BPF_B | BPF_IND, 0), /* Load icmp type */
|
||||
BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ICMP_ECHOREPLY, 1, 0), /* Echo? */
|
||||
BPF_STMT(BPF_RET | BPF_K, 0), /* Unexpected type. Reject. */
|
||||
BPF_STMT(BPF_RET | BPF_K, ~0U), /* Packet passes the filter. */
|
||||
};
|
||||
struct sock_fprog filter = {
|
||||
.len = sizeof(bpf_prog) / sizeof(struct sock_filter),
|
||||
.filter = bpf_prog,
|
||||
};
|
||||
setsockopt(m_socket, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter));
|
||||
|
||||
return m_socket;
|
||||
}
|
||||
|
||||
LinuxPingSender::LinuxPingSender(const QString& source, QObject* parent)
|
||||
: PingSender(parent), m_source(source) {
|
||||
MVPN_COUNT_CTOR(LinuxPingSender);
|
||||
logger.debug() << "LinuxPingSender(" + source + ") created";
|
||||
|
||||
m_socket = createSocket();
|
||||
if (m_socket < 0) {
|
||||
logger.error() << "Socket creation error: " << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
if (inet_aton(source.toLocal8Bit().constData(), &addr.sin_addr) == 0) {
|
||||
logger.error() << "source" << source << "error:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
if (bind(m_socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
|
||||
close(m_socket);
|
||||
m_socket = -1;
|
||||
logger.error() << "bind error:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
m_notifier = new QSocketNotifier(m_socket, QSocketNotifier::Read, this);
|
||||
if (m_ident) {
|
||||
connect(m_notifier, &QSocketNotifier::activated, this,
|
||||
&LinuxPingSender::rawSocketReady);
|
||||
} else {
|
||||
connect(m_notifier, &QSocketNotifier::activated, this,
|
||||
&LinuxPingSender::icmpSocketReady);
|
||||
}
|
||||
}
|
||||
|
||||
LinuxPingSender::~LinuxPingSender() {
|
||||
MVPN_COUNT_DTOR(LinuxPingSender);
|
||||
if (m_socket >= 0) {
|
||||
close(m_socket);
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxPingSender::sendPing(const QString& dest, quint16 sequence) {
|
||||
// QProcess is not supported on iOS. Because of this we cannot use the `ping`
|
||||
// app as fallback on this platform.
|
||||
#ifndef MVPN_IOS
|
||||
// Use the generic ping sender if we failed to open an ICMP socket.
|
||||
if (m_socket < 0) {
|
||||
QStringList args;
|
||||
args << "-c"
|
||||
<< "1";
|
||||
args << "-I" << m_source;
|
||||
args << dest;
|
||||
genericSendPing(args, sequence);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
if (inet_aton(dest.toLocal8Bit().constData(), &addr.sin_addr) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
struct icmphdr packet;
|
||||
memset(&packet, 0, sizeof(packet));
|
||||
packet.type = ICMP_ECHO;
|
||||
packet.un.echo.id = htons(m_ident);
|
||||
packet.un.echo.sequence = htons(sequence);
|
||||
packet.checksum = inetChecksum(&packet, sizeof(packet));
|
||||
|
||||
int rc = sendto(m_socket, &packet, sizeof(packet), 0, (struct sockaddr*)&addr,
|
||||
sizeof(addr));
|
||||
if (rc < 0) {
|
||||
logger.error() << "failed to send:" << strerror(errno);
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxPingSender::icmpSocketReady() {
|
||||
socklen_t slen = 0;
|
||||
unsigned char data[2048];
|
||||
int rc = recvfrom(m_socket, data, sizeof(data), MSG_DONTWAIT, NULL, &slen);
|
||||
if (rc <= 0) {
|
||||
logger.error() << "recvfrom failed:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
struct icmphdr packet;
|
||||
if (rc >= (int)sizeof(packet)) {
|
||||
memcpy(&packet, data, sizeof(packet));
|
||||
if (packet.type == ICMP_ECHOREPLY) {
|
||||
emit recvPing(htons(packet.un.echo.sequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LinuxPingSender::rawSocketReady() {
|
||||
socklen_t slen = 0;
|
||||
unsigned char data[2048];
|
||||
int rc = recvfrom(m_socket, data, sizeof(data), MSG_DONTWAIT, NULL, &slen);
|
||||
if (rc <= 0) {
|
||||
logger.error() << "recvfrom failed:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the IP header
|
||||
const struct iphdr* ip = (struct iphdr*)data;
|
||||
int iphdrlen = ip->ihl * 4;
|
||||
if (rc < iphdrlen || iphdrlen < (int)sizeof(struct iphdr)) {
|
||||
logger.error() << "malformed IP packet:" << strerror(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the ICMP packet
|
||||
struct icmphdr packet;
|
||||
if (inetChecksum(data + iphdrlen, rc - iphdrlen) != 0) {
|
||||
logger.warning() << "invalid checksum";
|
||||
return;
|
||||
}
|
||||
if (rc >= (iphdrlen + (int)sizeof(packet))) {
|
||||
memcpy(&packet, data + iphdrlen, sizeof(packet));
|
||||
quint16 id = htons(m_ident);
|
||||
if ((packet.type == ICMP_ECHOREPLY) && (packet.un.echo.id == id)) {
|
||||
emit recvPing(htons(packet.un.echo.sequence));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXPINGSENDER_H
|
||||
#define LINUXPINGSENDER_H
|
||||
|
||||
#include "pingsender.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QSocketNotifier;
|
||||
|
||||
class LinuxPingSender final : public PingSender {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(LinuxPingSender)
|
||||
|
||||
public:
|
||||
LinuxPingSender(const QString& source, QObject* parent = nullptr);
|
||||
~LinuxPingSender();
|
||||
|
||||
void sendPing(const QString& dest, quint16 sequence) override;
|
||||
|
||||
private:
|
||||
int createSocket();
|
||||
|
||||
private slots:
|
||||
void rawSocketReady();
|
||||
void icmpSocketReady();
|
||||
|
||||
private:
|
||||
QSocketNotifier* m_notifier = nullptr;
|
||||
QString m_source;
|
||||
int m_socket = 0;
|
||||
quint16 m_ident = 0;
|
||||
};
|
||||
|
||||
#endif // LINUXPINGSENDER_H
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "platforms/linux/linuxsystemtraynotificationhandler.h"
|
||||
#include "constants.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "defines.h"
|
||||
|
||||
#include <QtDBus/QtDBus>
|
||||
#include <QDesktopServices>
|
||||
|
||||
constexpr const char* DBUS_ITEM = "org.freedesktop.Notifications";
|
||||
constexpr const char* DBUS_PATH = "/org/freedesktop/Notifications";
|
||||
constexpr const char* DBUS_INTERFACE = "org.freedesktop.Notifications";
|
||||
constexpr const char* ACTION_ID = "mozilla_vpn_notification";
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_LINUX, "LinuxSystemTrayNotificationHandler");
|
||||
} // namespace
|
||||
|
||||
//static
|
||||
bool LinuxSystemTrayNotificationHandler::requiredCustomImpl() {
|
||||
//if (!QDBusConnection::sessionBus().isConnected()) {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//QDBusConnectionInterface* interface =
|
||||
// QDBusConnection::sessionBus().interface();
|
||||
//if (!interface) {
|
||||
// return false;
|
||||
//}
|
||||
|
||||
//// This custom systemTrayHandler implementation is required only on Unity.
|
||||
//QStringList registeredServices = interface->registeredServiceNames().value();
|
||||
//return registeredServices.contains("com.canonical.Unity");
|
||||
}
|
||||
|
||||
LinuxSystemTrayNotificationHandler::LinuxSystemTrayNotificationHandler(
|
||||
QObject* parent)
|
||||
: SystemTrayNotificationHandler(parent) {
|
||||
|
||||
m_systemTrayIcon.show();
|
||||
connect(&m_systemTrayIcon, &QSystemTrayIcon::activated, this, &LinuxSystemTrayNotificationHandler::onTrayActivated);
|
||||
|
||||
|
||||
m_menu.addAction(QIcon(":/images/tray/application.png"), tr("Show") + " " + APPLICATION_NAME, this, [this](){
|
||||
emit raiseRequested();
|
||||
});
|
||||
m_menu.addSeparator();
|
||||
m_trayActionConnect = m_menu.addAction(tr("Connect"), this, [this](){ emit connectRequested(); });
|
||||
m_trayActionDisconnect = m_menu.addAction(tr("Disconnect"), this, [this](){ emit disconnectRequested(); });
|
||||
|
||||
m_menu.addSeparator();
|
||||
|
||||
m_menu.addAction(QIcon(":/images/tray/link.png"), tr("Visit Website"), [&](){
|
||||
QDesktopServices::openUrl(QUrl("https://amnezia.org"));
|
||||
});
|
||||
|
||||
m_menu.addAction(QIcon(":/images/tray/cancel.png"), tr("Quit") + " " + APPLICATION_NAME, this, [&](){
|
||||
qApp->quit();
|
||||
});
|
||||
|
||||
m_systemTrayIcon.setContextMenu(&m_menu);
|
||||
setTrayState(VpnProtocol::Disconnected);
|
||||
}
|
||||
|
||||
void LinuxSystemTrayNotificationHandler::setTrayState(VpnProtocol::VpnConnectionState state)
|
||||
{
|
||||
qDebug() << "SystemTrayNotificationHandler::setTrayState" << state;
|
||||
QString resourcesPath = ":/images/tray/%1";
|
||||
|
||||
switch (state) {
|
||||
case VpnProtocol::Disconnected:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(true);
|
||||
m_trayActionDisconnect->setEnabled(false);
|
||||
break;
|
||||
case VpnProtocol::Preparing:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case VpnProtocol::Connecting:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case VpnProtocol::Connected:
|
||||
setTrayIcon(QString(resourcesPath).arg(ConnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case VpnProtocol::Disconnecting:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case VpnProtocol::Reconnecting:
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
break;
|
||||
case VpnProtocol::Error:
|
||||
setTrayIcon(QString(resourcesPath).arg(ErrorTrayIconName));
|
||||
m_trayActionConnect->setEnabled(true);
|
||||
m_trayActionDisconnect->setEnabled(false);
|
||||
break;
|
||||
case VpnProtocol::Unknown:
|
||||
default:
|
||||
m_trayActionConnect->setEnabled(false);
|
||||
m_trayActionDisconnect->setEnabled(true);
|
||||
setTrayIcon(QString(resourcesPath).arg(DisconnectedTrayIconName));
|
||||
}
|
||||
|
||||
//#ifdef Q_OS_MAC
|
||||
// // Get theme from current user (note, this app can be launched as root application and in this case this theme can be different from theme of real current user )
|
||||
// bool darkTaskBar = MacOSFunctions::instance().isMenuBarUseDarkTheme();
|
||||
// darkTaskBar = forceUseBrightIcons ? true : darkTaskBar;
|
||||
// resourcesPath = ":/images_mac/tray_icon/%1";
|
||||
// useIconName = useIconName.replace(".png", darkTaskBar ? "@2x.png" : " dark@2x.png");
|
||||
//#endif
|
||||
}
|
||||
|
||||
void LinuxSystemTrayNotificationHandler::setTrayIcon(const QString &iconPath)
|
||||
{
|
||||
QIcon trayIconMask(QPixmap(iconPath).scaled(128,128));
|
||||
trayIconMask.setIsMask(true);
|
||||
m_systemTrayIcon.setIcon(trayIconMask);
|
||||
}
|
||||
|
||||
void LinuxSystemTrayNotificationHandler::onTrayActivated(QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
#ifndef Q_OS_MAC
|
||||
if(reason == QSystemTrayIcon::DoubleClick || reason == QSystemTrayIcon::Trigger) {
|
||||
emit raiseRequested();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
LinuxSystemTrayNotificationHandler::~LinuxSystemTrayNotificationHandler() {
|
||||
MVPN_COUNT_DTOR(LinuxSystemTrayNotificationHandler);
|
||||
}
|
||||
|
||||
void LinuxSystemTrayNotificationHandler::notify(Message type,
|
||||
const QString& title,
|
||||
const QString& message,
|
||||
int timerMsec) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
void LinuxSystemTrayNotificationHandler::actionInvoked(uint actionId,
|
||||
QString action) {
|
||||
logger.debug() << "Notification clicked" << actionId << action;
|
||||
|
||||
if (action == ACTION_ID && m_lastNotificationId == actionId) {
|
||||
messageClickHandle();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,49 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LINUXNOTIFICATIONNOTIFICATIONHANDLER_H
|
||||
#define LINUXNOTIFICATIONNOTIFICATIONHANDLER_H
|
||||
|
||||
#include "./ui/systemtray_notificationhandler.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class LinuxSystemTrayNotificationHandler final
|
||||
: public SystemTrayNotificationHandler {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(LinuxSystemTrayNotificationHandler)
|
||||
|
||||
public:
|
||||
static bool requiredCustomImpl();
|
||||
|
||||
LinuxSystemTrayNotificationHandler(QObject* parent);
|
||||
~LinuxSystemTrayNotificationHandler();
|
||||
|
||||
private:
|
||||
void notify(Message type, const QString& title, const QString& message,
|
||||
int timerMsec) override;
|
||||
void setTrayState(VpnProtocol::VpnConnectionState state);
|
||||
void setTrayIcon(const QString &iconPath);
|
||||
void onTrayActivated(QSystemTrayIcon::ActivationReason reason);
|
||||
private slots:
|
||||
void actionInvoked(uint actionId, QString action);
|
||||
|
||||
private:
|
||||
QMenu m_menu;
|
||||
QSystemTrayIcon m_systemTrayIcon;
|
||||
|
||||
QAction* m_trayActionConnect = nullptr;
|
||||
QAction* m_trayActionDisconnect = nullptr;
|
||||
QAction* m_preferencesAction = nullptr;
|
||||
QAction* m_statusLabel = nullptr;
|
||||
QAction* m_separator = nullptr;
|
||||
|
||||
const QString ConnectedTrayIconName = "active.png";
|
||||
const QString DisconnectedTrayIconName = "default.png";
|
||||
const QString ErrorTrayIconName = "error.png";
|
||||
|
||||
uint m_lastNotificationId = 0;
|
||||
};
|
||||
|
||||
#endif // LINUXNOTIFICATIONNOTIFICATIONHANDLER_H
|
||||
Loading…
Add table
Add a link
Reference in a new issue