2
* Copyright (C) 2014 Canonical, Ltd.
5
* Jussi Pakkanen <jussi.pakkanen@canonical.com>
7
* This program is free software: you can redistribute it and/or modify it
8
* under the terms of the GNU General Public License version 3, as published
9
* by the Free Software Foundation.
11
* This library is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16
* You should have received a copy of the GNU General Public License
17
* along with this program. If not, see <http://www.gnu.org/licenses/>.
20
#include "hotspotmanager.h"
22
#include "nm_manager_proxy.h"
23
#include "nm_settings_proxy.h"
24
#include "nm_settings_connection_proxy.h"
25
#include <QStringList>
28
#include <QDBusInterface>
29
#include <QDBusMetaType>
31
typedef QMap<QString, QVariantMap> nmConnectionArg;
32
Q_DECLARE_METATYPE(nmConnectionArg)
36
const QString nm_service("org.freedesktop.NetworkManager");
37
const QString nm_settings_object("/org/freedesktop/NetworkManager/Settings");
38
const QString nm_settings_interface("org.freedesktop.NetworkManager.Settings");
39
const QString nm_connection_interface("org.freedesktop.NetworkManager.Settings.Connection");
40
const QString nm_dbus_path("/org/freedesktop/NetworkManager");
41
const QString nm_device_interface("org.freedesktop.NetworkManager.Device");
43
#define NM_METHOD_NAME "AddAndActivateConnection"
45
void startAdhoc(const QByteArray &ssid, const QString &password, const QDBusObjectPath &devicePath) {
46
nmConnectionArg connection;
48
QDBusObjectPath specific("/");
51
wireless[QStringLiteral("security")] = QVariant(QStringLiteral("802-11-wireless-security"));
52
wireless[QStringLiteral("ssid")] = QVariant(ssid);
53
wireless[QStringLiteral("mode")] = QVariant(QStringLiteral("adhoc"));
54
connection["802-11-wireless"] = wireless;
56
QVariantMap connsettings;
57
connsettings[QStringLiteral("autoconnect")] = QVariant(false);
58
connsettings[QStringLiteral("uuid")] = QVariant(QStringLiteral("aab22b5d-7342-48dc-8920-1b7da31d6829"));
59
connsettings[QStringLiteral("type")] = QVariant(QStringLiteral("802-11-wireless"));
60
connection["connection"] = connsettings;
63
ipv4[QStringLiteral("addressess")] = QVariant(QStringList());
64
ipv4[QStringLiteral("dns")] = QVariant(QStringList());
65
ipv4[QStringLiteral("method")] = QVariant(QStringLiteral("shared"));
66
ipv4[QStringLiteral("routes")] = QVariant(QStringList());
67
connection["ipv4"] = ipv4;
70
security[QStringLiteral("proto")] = QVariant(QStringList{"rsn"});
71
security[QStringLiteral("pairwise")] = QVariant(QStringList{"ccmp"});
72
security[QStringLiteral("group")] = QVariant(QStringList{"ccmp"});
73
security[QStringLiteral("key-mgmt")] = QVariant(QStringLiteral("wpa-psk"));
74
security[QStringLiteral("psk")] = QVariant(password);
75
connection["802-11-wireless-security"] = security;
77
OrgFreedesktopNetworkManagerInterface mgr(nm_service,
79
QDBusConnection::systemBus());
80
auto reply = mgr.AddAndActivateConnection(connection, devicePath, specific);
81
reply.waitForFinished();
82
if(!reply.isValid()) {
83
qWarning() << "Failed to start adhoc network: " << reply.error().message() << "\n";
87
bool detectAdhoc(QString &dbusPath, QByteArray &ssid, QString &password, bool &isActive) {
88
static const QString activeIface("org.freedesktop.NetworkManager.Connection.Active");
89
static const QString connProp("Connection");
90
OrgFreedesktopNetworkManagerInterface mgr(nm_service,
92
QDBusConnection::systemBus());
93
auto activeConnections = mgr.activeConnections();
94
OrgFreedesktopNetworkManagerSettingsInterface settings(nm_service, nm_settings_object,
95
QDBusConnection::systemBus());
96
QSet<QDBusObjectPath> actives;
97
auto r = settings.ListConnections();
99
for(const auto &conn : activeConnections) {
100
QDBusInterface iface(nm_service, conn.path(), "org.freedesktop.DBus.Properties",
101
QDBusConnection::systemBus());
102
QDBusReply<QVariant> conname = iface.call("Get", activeIface, connProp);
103
if(!conname.isValid()) {
104
qWarning() << "Error getting connamd: " << conname.error().message() << "\n";
107
QDBusObjectPath mainConnection = qvariant_cast<QDBusObjectPath>(conname.value());
108
actives.insert(mainConnection);
110
const char wifiKey[] = "802-11-wireless";
111
for(const auto &i : r.value()) {
112
OrgFreedesktopNetworkManagerSettingsConnectionInterface conn(nm_service,
114
QDBusConnection::systemBus());
115
auto reply = conn.GetSettings();
116
reply.waitForFinished();
117
auto s = reply.value();
118
if(s.find(wifiKey) != s.end()) {
119
auto wsetup = s[wifiKey];
120
if(wsetup["mode"] == "adhoc") {
122
ssid = wsetup["ssid"].toByteArray();
123
auto pwdReply = conn.GetSecrets("802-11-wireless-security");
124
pwdReply.waitForFinished();
125
password = pwdReply.value()["802-11-wireless-security"]["psk"].toString();
127
for(const auto &ac : actives) {
140
QDBusObjectPath detectWirelessDevice() {
141
OrgFreedesktopNetworkManagerInterface mgr(nm_service,
143
QDBusConnection::systemBus());
144
auto devices = mgr.GetDevices();
145
devices.waitForFinished();
146
for(const auto &dpath : devices.value()) {
147
QDBusInterface iface(nm_service, dpath.path(), "org.freedesktop.DBus.Properties",
148
QDBusConnection::systemBus());
149
QDBusReply<QVariant> typeReply = iface.call("Get", "org.freedesktop.NetworkManager.Device", "DeviceType");
150
auto typeInt = qvariant_cast<int>(typeReply.value());
152
return dpath; // Assumptions are that there is only one wifi device and it is not hotpluggable.
155
qWarning() << "Wireless device not found, hotspot functionality is inoperative.\n";
156
return QDBusObjectPath();
159
std::string generate_password() {
160
static const std::string items("abcdefghijklmnopqrstuvwxyz01234567890");
161
const int passwordLength = 8;
163
for(int i=0; i<passwordLength; i++) {
164
result.push_back(items[std::rand() % items.length()]);
171
HotspotManager::HotspotManager(QObject *parent) : QObject(parent),
172
m_devicePath(detectWirelessDevice()) {
173
static bool isRegistered = false;
175
qDBusRegisterMetaType<nmConnectionArg>();
178
if(!detectAdhoc(m_settingsPath, m_ssid, m_password, m_isActive)) {
180
m_ssid = "Ubuntu hotspot";
181
m_password = generate_password().c_str();
186
QByteArray HotspotManager::getHotspotName() {
190
QString HotspotManager::getHotspotPassword() {
194
void HotspotManager::setupHotspot(QByteArray ssid_, QString password_) {
196
m_password = password_;
199
void HotspotManager::enableHotspot() {
200
if(!m_settingsPath.isEmpty()) {
201
// Prints a warning message if the connection has disappeared already.
203
// NM returns from the dbus call immediately but only destroys the
204
// connection some time later. There is no callback for when this happens.
205
// So this is the best we can do with reasonable effort.
208
startAdhoc(m_ssid, m_password, m_devicePath);
209
detectAdhoc(m_settingsPath, m_ssid, m_password, m_isActive);
212
bool HotspotManager::isHotspotActive() {
216
void HotspotManager::disableHotspot() {
217
static const QString activeIface("org.freedesktop.NetworkManager.Connection.Active");
218
static const QString connProp("Connection");
219
OrgFreedesktopNetworkManagerInterface mgr(nm_service,
221
QDBusConnection::systemBus());
222
auto activeConnections = mgr.activeConnections();
223
for(const auto &aConn : activeConnections) {
224
QDBusInterface iface(nm_service, aConn.path(), "org.freedesktop.DBus.Properties",
225
QDBusConnection::systemBus());
226
QDBusReply<QVariant> conname = iface.call("Get", activeIface, connProp);
227
QDBusObjectPath backingConnection = qvariant_cast<QDBusObjectPath>(conname.value());
228
if(backingConnection.path() == m_settingsPath) {
229
mgr.DeactivateConnection(aConn);
233
qWarning() << "Could not find a hotspot setup to disable.\n";
236
void HotspotManager::destroyHotspot() {
237
if(m_settingsPath.isEmpty()) {
238
qWarning() << "Tried to destroy nonexisting hotspot.\n";
241
QDBusInterface control(nm_service, m_settingsPath, nm_connection_interface,
242
QDBusConnection::systemBus());
243
QDBusReply<void> reply = control.call("Delete");
244
if(!reply.isValid()) {
245
qWarning() << "Could not disconnect adhoc network: " << reply.error().message() << "\n";