1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qnetworkmanagerservice.h"
6#include "qnetworkmanagernetworkinformationbackend.h"
7
8#include <QObject>
9#include <QList>
10#include <QtDBus/QDBusConnection>
11#include <QtDBus/QDBusError>
12#include <QtDBus/QDBusInterface>
13#include <QtDBus/QDBusMessage>
14#include <QtDBus/QDBusReply>
15#include <QtDBus/QDBusPendingCallWatcher>
16#include <QtDBus/QDBusObjectPath>
17#include <QtDBus/QDBusPendingCall>
18
19#define DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"_L1
20
21#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager"
22#define NM_DBUS_SERVICE NM_DBUS_INTERFACE ""_L1
23
24#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"_L1
25#define NM_CONNECTION_DBUS_INTERFACE NM_DBUS_SERVICE ".Connection.Active"_L1
26#define NM_DEVICE_DBUS_INTERFACE NM_DBUS_SERVICE ".Device"_L1
27
28QT_BEGIN_NAMESPACE
29
30using namespace Qt::StringLiterals;
31
32namespace {
33constexpr QLatin1StringView propertiesChangedKey = "PropertiesChanged"_L1;
34const QString &stateKey()
35{
36 static auto key = u"State"_s;
37 return key;
38}
39const QString &connectivityKey()
40{
41 static auto key = u"Connectivity"_s;
42 return key;
43}
44const QString &primaryConnectionKey()
45{
46 static auto key = u"PrimaryConnection"_s;
47 return key;
48}
49}
50
51QNetworkManagerInterfaceBase::QNetworkManagerInterfaceBase(QObject *parent)
52 : QDBusAbstractInterface(NM_DBUS_SERVICE, NM_DBUS_PATH,
53 NM_DBUS_INTERFACE, QDBusConnection::systemBus(), parent)
54{
55}
56
57bool QNetworkManagerInterfaceBase::networkManagerAvailable()
58{
59 return QNetworkManagerInterfaceBase().isValid();
60}
61
62QNetworkManagerInterface::QNetworkManagerInterface(QObject *parent)
63 : QNetworkManagerInterfaceBase(parent)
64{
65 if (!QDBusAbstractInterface::isValid())
66 return;
67
68 PropertiesDBusInterface managerPropertiesInterface(
69 NM_DBUS_SERVICE, NM_DBUS_PATH, DBUS_PROPERTIES_INTERFACE,
70 QDBusConnection::systemBus());
71 QList<QVariant> argumentList;
72 argumentList << NM_DBUS_SERVICE;
73 QDBusPendingReply<QVariantMap> propsReply = managerPropertiesInterface.callWithArgumentList(
74 mode: QDBus::Block, method: "GetAll"_L1, args: argumentList);
75 if (propsReply.isError()) {
76 validDBusConnection = false;
77 if (auto error = propsReply.error(); error.type() != QDBusError::AccessDenied)
78 qWarning() << "Failed to query NetworkManager properties:" << error.message();
79 return;
80 }
81 propertyMap = propsReply.value();
82
83 validDBusConnection = QDBusConnection::systemBus().connect(NM_DBUS_SERVICE, NM_DBUS_PATH,
84 DBUS_PROPERTIES_INTERFACE, name: propertiesChangedKey, receiver: this,
85 SLOT(setProperties(QString,QMap<QString,QVariant>,QList<QString>)));
86}
87
88QNetworkManagerInterface::~QNetworkManagerInterface()
89{
90 QDBusConnection::systemBus().disconnect(NM_DBUS_SERVICE, NM_DBUS_PATH,
91 DBUS_PROPERTIES_INTERFACE, name: propertiesChangedKey, receiver: this,
92 SLOT(setProperties(QString,QMap<QString,QVariant>,QList<QString>)));
93}
94
95QNetworkManagerInterface::NMState QNetworkManagerInterface::state() const
96{
97 auto it = propertyMap.constFind(key: stateKey());
98 if (it != propertyMap.cend())
99 return static_cast<QNetworkManagerInterface::NMState>(it->toUInt());
100 return QNetworkManagerInterface::NM_STATE_UNKNOWN;
101}
102
103QNetworkManagerInterface::NMConnectivityState QNetworkManagerInterface::connectivityState() const
104{
105 auto it = propertyMap.constFind(key: connectivityKey());
106 if (it != propertyMap.cend())
107 return static_cast<NMConnectivityState>(it->toUInt());
108 return QNetworkManagerInterface::NM_CONNECTIVITY_UNKNOWN;
109}
110
111static std::optional<QDBusInterface> getPrimaryDevice(const QDBusObjectPath &devicePath)
112{
113 const QDBusInterface connection(NM_DBUS_SERVICE, devicePath.path(),
114 NM_CONNECTION_DBUS_INTERFACE, QDBusConnection::systemBus());
115 if (!connection.isValid())
116 return std::nullopt;
117
118 const auto devicePaths = connection.property(name: "Devices").value<QList<QDBusObjectPath>>();
119 if (devicePaths.isEmpty())
120 return std::nullopt;
121
122 const QDBusObjectPath primaryDevicePath = devicePaths.front();
123 return std::make_optional<QDBusInterface>(NM_DBUS_SERVICE, args: primaryDevicePath.path(),
124 NM_DEVICE_DBUS_INTERFACE,
125 args: QDBusConnection::systemBus());
126}
127
128std::optional<QDBusObjectPath> QNetworkManagerInterface::primaryConnectionDevicePath() const
129{
130 auto it = propertyMap.constFind(key: primaryConnectionKey());
131 if (it != propertyMap.cend())
132 return it->value<QDBusObjectPath>();
133 return std::nullopt;
134}
135
136auto QNetworkManagerInterface::deviceType() const -> NMDeviceType
137{
138 if (const auto path = primaryConnectionDevicePath())
139 return extractDeviceType(devicePath: *path);
140 return NM_DEVICE_TYPE_UNKNOWN;
141}
142
143auto QNetworkManagerInterface::meteredState() const -> NMMetered
144{
145 if (const auto path = primaryConnectionDevicePath())
146 return extractDeviceMetered(devicePath: *path);
147 return NM_METERED_UNKNOWN;
148}
149
150auto QNetworkManagerInterface::extractDeviceType(const QDBusObjectPath &devicePath) const
151 -> NMDeviceType
152{
153 const auto primaryDevice = getPrimaryDevice(devicePath);
154 if (!primaryDevice)
155 return NM_DEVICE_TYPE_UNKNOWN;
156 if (!primaryDevice->isValid())
157 return NM_DEVICE_TYPE_UNKNOWN;
158 const QVariant deviceType = primaryDevice->property(name: "DeviceType");
159 if (!deviceType.isValid())
160 return NM_DEVICE_TYPE_UNKNOWN;
161 return static_cast<NMDeviceType>(deviceType.toUInt());
162}
163
164auto QNetworkManagerInterface::extractDeviceMetered(const QDBusObjectPath &devicePath) const
165 -> NMMetered
166{
167 const auto primaryDevice = getPrimaryDevice(devicePath);
168 if (!primaryDevice)
169 return NM_METERED_UNKNOWN;
170 if (!primaryDevice->isValid())
171 return NM_METERED_UNKNOWN;
172 const QVariant metered = primaryDevice->property(name: "Metered");
173 if (!metered.isValid())
174 return NM_METERED_UNKNOWN;
175 return static_cast<NMMetered>(metered.toUInt());
176}
177
178void QNetworkManagerInterface::setBackend(QNetworkManagerNetworkInformationBackend *ourBackend)
179{
180 backend = ourBackend;
181}
182
183void QNetworkManagerInterface::setProperties(const QString &interfaceName,
184 const QMap<QString, QVariant> &map,
185 const QStringList &invalidatedProperties)
186{
187 Q_UNUSED(interfaceName);
188 Q_UNUSED(invalidatedProperties);
189
190 for (auto i = map.cbegin(), end = map.cend(); i != end; ++i) {
191 bool valueChanged = true;
192
193 auto it = propertyMap.lowerBound(key: i.key());
194 if (it != propertyMap.end() && it.key() == i.key()) {
195 valueChanged = (it.value() != i.value());
196 *it = *i;
197 } else {
198 propertyMap.insert(pos: it, key: i.key(), value: i.value());
199 }
200
201 if (valueChanged) {
202 if (i.key() == stateKey()) {
203 quint32 state = i.value().toUInt();
204 backend->onStateChanged(state: static_cast<NMState>(state));
205 } else if (i.key() == connectivityKey()) {
206 quint32 state = i.value().toUInt();
207 backend->onConnectivityChanged(connectivityState: static_cast<NMConnectivityState>(state));
208 } else if (i.key() == primaryConnectionKey()) {
209 const QDBusObjectPath devicePath = i->value<QDBusObjectPath>();
210 backend->onDeviceTypeChanged(deviceType: extractDeviceType(devicePath));
211 backend->onMeteredChanged(metered: extractDeviceMetered(devicePath));
212 } else if (i.key() == "Metered"_L1) {
213 backend->onMeteredChanged(metered: static_cast<NMMetered>(i->toUInt()));
214 }
215 }
216 }
217}
218
219QT_END_NAMESPACE
220
221#include "moc_qnetworkmanagerservice.cpp"
222

source code of qtbase/src/plugins/networkinformation/networkmanager/qnetworkmanagerservice.cpp