Commit e1f125e9 authored by flypig's avatar flypig

[systemsettings] Update VpnModel to use libconnman-qt VPN API. Contributes to JB#45378

Functionality from VpnModel has now been moved into libconnman-qt
VpnCoreModel. This updates VpnModel to use this libconnman-qt VPN API,
by extending VpnCoreModel, rather than interacting with connman directly
through its dbus interface.
parent 19a8d7e8
#ifndef QDBUSXML2CPP_DBUS_TYPES
#define QDBUSXML2CPP_DBUS_TYPES
#include <QPair>
#include <QList>
#include <QDBusMetaType>
typedef QPair<QDBusObjectPath, QVariantMap> PathProperties;
Q_DECLARE_METATYPE(PathProperties);
typedef QList<PathProperties> PathPropertiesArray;
Q_DECLARE_METATYPE(PathPropertiesArray);
#endif
......@@ -10,9 +10,6 @@ PKGCONFIG += profile mlite5 mce timed-qt5 libshadowutils blkid libcrypto nemomod
PKGCONFIG += ssu-sysinfo nemodbus packagekitqt5
system(qdbusxml2cpp -p mceiface.h:mceiface.cpp mce.xml)
system(qdbusxml2cpp -c ConnmanVpnProxy -p connmanvpnproxy ../dbus/net.connman.vpn.xml -i qdbusxml2cpp_dbus_types.h)
system(qdbusxml2cpp -c ConnmanVpnConnectionProxy -p connmanvpnconnectionproxy ../dbus/net.connman.vpn.Connection.xml -i qdbusxml2cpp_dbus_types.h)
system(qdbusxml2cpp -c ConnmanServiceProxy -p connmanserviceproxy ../dbus/net.connman.service.xml -i qdbusxml2cpp_dbus_types.h)
SOURCES += \
languagemodel.cpp \
......@@ -26,9 +23,6 @@ SOURCES += \
aboutsettings.cpp \
certificatemodel.cpp \
vpnmodel.cpp \
connmanserviceproxy.cpp \
connmanvpnproxy.cpp \
connmanvpnconnectionproxy.cpp \
developermodesettings.cpp \
batterystatus.cpp \
diskusage.cpp \
......@@ -54,9 +48,6 @@ PUBLIC_HEADERS = \
aboutsettings.h \
certificatemodel.h \
vpnmodel.h \
connmanserviceproxy.h \
connmanvpnproxy.h \
connmanvpnconnectionproxy.h \
developermodesettings.h \
batterystatus.h \
udisks2block_p.h \
......@@ -72,7 +63,6 @@ PUBLIC_HEADERS = \
HEADERS += \
$$PUBLIC_HEADERS \
qdbusxml2cpp_dbus_types.h \
localeconfig.h \
batterystatus_p.h \
logging_p.h \
......
/*
* Copyright (C) 2016 Jolla Ltd.
* Contact: Matt Vogt <matthew.vogt@jollamobile.com>
* Copyright (c) 2016 - 2019 Jolla Ltd.
* Copyright (c) 2019 Open Mobile Platform LLC.
*
* You may use this file under the terms of the BSD license as follows:
*
......@@ -30,632 +30,395 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
*/
#include "vpnmodel.h"
#include "logging_p.h"
#include "connmanvpnconnectionproxy.h"
#include "connmanserviceproxy.h"
#include <QRegularExpression>
#include <QStandardPaths>
#include <QDataStream>
#include <QCryptographicHash>
#include <QDBusPendingCallWatcher>
#include <QDBusServiceWatcher>
#include <QRegularExpression>
#include <QQmlEngine>
#include <QDir>
#include "logging_p.h"
#include "vpnmanager.h"
#include <nemo-dbus/dbus.h>
#include "vpnmodel.h"
namespace {
const auto defaultDomain = QStringLiteral("sailfishos.org");
const auto legacyDefaultDomain(QStringLiteral("merproject.org"));
const auto connmanService = QStringLiteral("net.connman");
const auto connmanVpnService = QStringLiteral("net.connman.vpn");
const auto autoConnectKey = QStringLiteral("AutoConnect");
QString vpnServicePath(QString connectionPath)
int numericValue(VpnConnection::ConnectionState state)
{
return QString("/net/connman/service/vpn_%1").arg(connectionPath.section("/", 5));
return (state == VpnConnection::Ready ? 3 :
(state == VpnConnection::Configuration ? 2 :
(state == VpnConnection::Failure ? 1 : 0)));
}
// Conversion to/from DBus/QML
QHash<QString, QList<QPair<QVariant, QVariant> > > propertyConversions()
} // end anonymous namespace
VpnModel::VpnModel(QObject* parent)
: VpnCoreModel(parent)
, credentials_(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/system/privileged/vpn-data"))
, bestState_(VpnConnection::Idle)
, autoConnect_(false)
, orderByConnected_(true)
, provisioningOutputPath_(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/system/privileged/vpn-provisioning"))
, roles(VpnCoreModel::roleNames())
{
QHash<QString, QList<QPair<QVariant, QVariant> > > rv;
VpnManager *manager = vpnManager();
QList<QPair<QVariant, QVariant> > states;
states.push_back(qMakePair(QVariant::fromValue(QString("idle")), QVariant::fromValue(static_cast<int>(VpnModel::Idle))));
states.push_back(qMakePair(QVariant::fromValue(QString("failure")), QVariant::fromValue(static_cast<int>(VpnModel::Failure))));
states.push_back(qMakePair(QVariant::fromValue(QString("configuration")), QVariant::fromValue(static_cast<int>(VpnModel::Configuration))));
states.push_back(qMakePair(QVariant::fromValue(QString("ready")), QVariant::fromValue(static_cast<int>(VpnModel::Ready))));
states.push_back(qMakePair(QVariant::fromValue(QString("disconnect")), QVariant::fromValue(static_cast<int>(VpnModel::Disconnect))));
rv.insert(QString("state"), states);
roles.insert(ConnectedRole, "connected");
return rv;
connect(manager, &VpnManager::connectionAdded, this, &VpnModel::connectionAdded, Qt::UniqueConnection);
connect(manager, &VpnManager::connectionRemoved, this, &VpnModel::connectionRemoved, Qt::UniqueConnection);
connect(manager, &VpnManager::connectionsRefreshed, this, &VpnModel::connectionsRefreshed, Qt::UniqueConnection);
connect(manager, &VpnManager::connectionsClearingAll, this, &VpnModel::connectionsClearingAll, Qt::UniqueConnection);
}
QVariant convertValue(const QString &key, const QVariant &value, bool toDBus)
VpnModel::~VpnModel()
{
static const QHash<QString, QList<QPair<QVariant, QVariant> > > conversions(propertyConversions());
auto it = conversions.find(key.toLower());
if (it != conversions.end()) {
const QList<QPair<QVariant, QVariant> > &list(it.value());
auto lit = std::find_if(list.cbegin(), list.cend(), [value, toDBus](const QPair<QVariant, QVariant> &pair) { return value == (toDBus ? pair.second : pair.first); });
if (lit != list.end()) {
return toDBus ? (*lit).first : (*lit).second;
} else {
qCWarning(lcVpnLog) << "No conversion found for" << (toDBus ? "QML" : "DBus") << "value:" << value << key;
}
}
VpnManager *manager = vpnManager();
return value;
disconnect(manager, 0, this, 0);
}
QVariant convertToQml(const QString &key, const QVariant &value)
void VpnModel::createConnection(const QVariantMap &createProperties)
{
return convertValue(key, value, false);
QVariantMap properties(createProperties);
const QString domain(properties.value(QString("domain")).toString());
if (domain.isEmpty()) {
properties.insert(QString("domain"), QVariant::fromValue(createDefaultDomain()));
}
vpnManager()->createConnection(properties);
}
QVariant convertToDBus(const QString &key, const QVariant &value)
QHash<int, QByteArray> VpnModel::roleNames() const
{
return convertValue(key, value, true);
return roles;
}
QVariantMap propertiesToDBus(const QVariantMap &fromQml)
QVariant VpnModel::data(const QModelIndex &index, int role) const
{
QVariantMap rv;
for (QVariantMap::const_iterator it = fromQml.cbegin(), end = fromQml.cend(); it != end; ++it) {
QString key(it.key());
QVariant value(it.value());
if (key == QStringLiteral("providerProperties")) {
const QVariantMap providerProperties(value.value<QVariantMap>());
for (QVariantMap::const_iterator pit = providerProperties.cbegin(), pend = providerProperties.cend(); pit != pend; ++pit) {
rv.insert(pit.key(), pit.value());
}
continue;
if (index.isValid() && index.row() >= 0 && index.row() < connections().count()) {
switch (role) {
case ConnectedRole:
return QVariant::fromValue((bool)connections().at(index.row())->connected());
default:
return VpnCoreModel::data(index, role);
}
// The DBus properties are capitalized
QChar &initial(*key.begin());
initial = initial.toUpper();
rv.insert(key, convertToDBus(key, value));
}
return rv;
return QVariant();
}
template<typename T>
QVariant extract(const QDBusArgument &arg)
int VpnModel::bestState() const
{
T rv;
arg >> rv;
return QVariant::fromValue(rv);
return static_cast<int>(bestState_);
}
template<typename T>
QVariant extractArray(const QDBusArgument &arg)
bool VpnModel::autoConnect() const
{
QVariantList rv;
return autoConnect_;
}
arg.beginArray();
while (!arg.atEnd()) {
rv.append(extract<T>(arg));
}
arg.endArray();
bool VpnModel::orderByConnected() const
{
return orderByConnected_;
}
return QVariant::fromValue(rv);
void VpnModel::setOrderByConnected(bool orderByConnected)
{
if (orderByConnected != orderByConnected_) {
orderByConnected_ = orderByConnected;
VpnCoreModel::connectionsChanged();
emit orderByConnectedChanged();
}
}
QVariantMap propertiesToQml(const QVariantMap &fromDBus)
void VpnModel::modifyConnection(const QString &path, const QVariantMap &properties)
{
QVariantMap rv;
VpnConnection *conn = vpnManager()->connection(path);
if (conn) {
QVariantMap updatedProperties(properties);
const QString domain(updatedProperties.value(QString("domain")).toString());
QVariantMap providerProperties;
if (domain.isEmpty()) {
if (isDefaultDomain(conn->domain())) {
// The connection already has a default domain, no need to change it
updatedProperties.remove("domain");
}
else {
updatedProperties.insert(QString("domain"), QVariant::fromValue(createDefaultDomain()));
}
}
for (QVariantMap::const_iterator it = fromDBus.cbegin(), end = fromDBus.cend(); it != end; ++it) {
QString key(it.key());
QVariant value(it.value());
const QString location(CredentialsRepository::locationForObjectPath(path));
const bool couldStoreCredentials(credentials_.credentialsExist(location));
const bool canStoreCredentials(properties.value(QString("storeCredentials")).toBool());
if (key.indexOf(QChar('.')) != -1) {
providerProperties.insert(key, value);
continue;
}
vpnManager()->modifyConnection(path, updatedProperties);
// QML properties must be lowercased
QChar &initial(*key.begin());
initial = initial.toLower();
// Some properties must be extracted manually
if (key == QStringLiteral("iPv4") ||
key == QStringLiteral("iPv6")) {
value = extract<QVariantMap>(value.value<QDBusArgument>());
} else if (key == QStringLiteral("serverRoutes") ||
key == QStringLiteral("userRoutes")) {
value = extractArray<QVariantMap>(value.value<QDBusArgument>());
if (canStoreCredentials != couldStoreCredentials) {
if (canStoreCredentials) {
credentials_.storeCredentials(location, QVariantMap());
} else {
credentials_.removeCredentials(location);
}
}
rv.insert(key, convertToQml(key, value));
}
if (!providerProperties.isEmpty()) {
rv.insert(QStringLiteral("providerProperties"), QVariant::fromValue(providerProperties));
else {
qCWarning(lcVpnLog) << "VPN connection modification failed: connection doesn't exist";
}
return rv;
}
int numericValue(VpnModel::ConnectionState state)
void VpnModel::deleteConnection(const QString &path)
{
return (state == VpnModel::Ready ? 3 : (state == VpnModel::Configuration ? 2 : (state == VpnModel::Failure ? 1 : 0)));
}
if (VpnConnection *conn = vpnManager()->connection(path)) {
// Remove cached credentials
const QString location(CredentialsRepository::locationForObjectPath(path));
if (credentials_.credentialsExist(location)) {
credentials_.removeCredentials(location);
}
} // end anonymous namespace
// Remove provisioned files
if (conn->type() == QStringLiteral("openvpn")) {
QVariantMap providerProperties = conn->providerProperties();
QStringList fileProperties;
fileProperties << QStringLiteral("OpenVPN.Cert") << QStringLiteral("OpenVPN.Key") << QStringLiteral("OpenVPN.CACert") << QStringLiteral("OpenVPN.ConfigFile");
for (const QString property : fileProperties) {
const QString filename = providerProperties.value(property).toString();
// Check if the file has been provisioned
if (filename.contains(provisioningOutputPath_)) {
int timesUsed = 0;
// Check the same file is not used by other connections
for (VpnConnection *c : connections()) {
if (filename == c->providerProperties().value(property).toString()) {
timesUsed++;
if (timesUsed > 1) {
break;
}
}
}
VpnModel::CredentialsRepository::CredentialsRepository(const QString &path)
: baseDir_(path)
{
if (!baseDir_.exists() && !baseDir_.mkpath(path)) {
qCWarning(lcVpnLog) << "Unable to create base directory for VPN credentials:" << path;
}
}
if (timesUsed > 1) {
qCInfo(lcVpnLog) << "VPN provisioning file kept, used by" << timesUsed << "connections.";
continue;
}
QString VpnModel::CredentialsRepository::locationForObjectPath(const QString &path)
{
int index = path.lastIndexOf(QChar('/'));
if (index != -1) {
return path.mid(index + 1);
qCInfo(lcVpnLog) << "VPN provisioning file removed: " << filename;
if (!QFile::remove(filename)) {
qCWarning(lcVpnLog) << "VPN provisioning file could not be removed: " << filename;
}
}
}
}
vpnManager()->deleteConnection(path);
}
return QString();
}
bool VpnModel::CredentialsRepository::credentialsExist(const QString &location) const
void VpnModel::activateConnection(const QString &path)
{
// Test the FS, as another process may store/remove the credentials
return baseDir_.exists(location);
vpnManager()->activateConnection(path);
}
bool VpnModel::CredentialsRepository::storeCredentials(const QString &location, const QVariantMap &credentials)
void VpnModel::deactivateConnection(const QString &path)
{
QFile credentialsFile(baseDir_.absoluteFilePath(location));
if (!credentialsFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qCWarning(lcVpnLog) << "Unable to write credentials file:" << credentialsFile.fileName();
return false;
} else {
credentialsFile.write(encodeCredentials(credentials));
credentialsFile.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ReadOther | QFileDevice::WriteOther);
credentialsFile.close();
}
return true;
vpnManager()->deactivateConnection(path);
}
bool VpnModel::CredentialsRepository::removeCredentials(const QString &location)
VpnConnection *VpnModel::get(int index) const
{
if (baseDir_.exists(location)) {
if (!baseDir_.remove(location)) {
qCWarning(lcVpnLog) << "Unable to delete credentials file:" << location;
return false;
}
if (index >= 0 && index < connections().size()) {
VpnConnection *item(connections().at(index));
QQmlEngine::setObjectOwnership(item, QQmlEngine::CppOwnership);
return item;
}
return true;
}
return 0;
QVariantMap VpnModel::CredentialsRepository::credentials(const QString &location) const
{
QVariantMap rv;
QFile credentialsFile(baseDir_.absoluteFilePath(location));
if (!credentialsFile.open(QIODevice::ReadOnly)) {
qCWarning(lcVpnLog) << "Unable to read credentials file:" << credentialsFile.fileName();
} else {
const QByteArray encoded = credentialsFile.readAll();
credentialsFile.close();
rv = decodeCredentials(encoded);
}
return rv;
}
QByteArray VpnModel::CredentialsRepository::encodeCredentials(const QVariantMap &credentials)
{
// We can't store these values securely, but we may as well encode them to protect from grep, at least...
QByteArray encoded;
QDataStream os(&encoded, QIODevice::WriteOnly);
os.setVersion(QDataStream::Qt_5_6);
const quint32 version = 1u;
os << version;
// ==========================================================================
// QAbstractListModel Ordering
// ==========================================================================
const quint32 items = credentials.size();
os << items;
for (auto it = credentials.cbegin(), end = credentials.cend(); it != end; ++it) {
os << it.key();
os << it.value().toString();
}
return encoded.toBase64();
}
QVariantMap VpnModel::CredentialsRepository::decodeCredentials(const QByteArray &encoded)
bool VpnModel::compareConnections(const VpnConnection *i, const VpnConnection *j)
{
QVariantMap rv;
QByteArray decoded(QByteArray::fromBase64(encoded));
QDataStream is(decoded);
is.setVersion(QDataStream::Qt_5_6);
quint32 version;
is >> version;
if (version != 1u) {
qCWarning(lcVpnLog) << "Invalid version for stored credentials:" << version;
} else {
quint32 items;
is >> items;
for (quint32 i = 0; i < items; ++i) {
QString key, value;
is >> key;
is >> value;
rv.insert(key, QVariant::fromValue(value));
}
}
return rv;
return ((orderByConnected_ && (i->connected() > j->connected()))
|| ((!orderByConnected_ || (i->connected() == j->connected()))
&& (i->name().localeAwareCompare(j->name()) <= 0)));
}
VpnModel::VpnModel(QObject *parent)
: ObjectListModel(parent, true, false)
, connmanVpn_(connmanVpnService, "/", QDBusConnection::systemBus(), this)
, credentials_(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/system/privileged/vpn-data"))
, provisioningOutputPath_(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/system/privileged/vpn-provisioning"))
, bestState_(VpnModel::Idle)
, autoConnect_(false)
, orderByConnected_(false)
void VpnModel::orderConnections(QVector<VpnConnection*> &connections)
{
qDBusRegisterMetaType<PathProperties>();
qDBusRegisterMetaType<PathPropertiesArray>();
connect(&connmanVpn_, &ConnmanVpnProxy::ConnectionAdded, [this](const QDBusObjectPath &objectPath, const QVariantMap &properties) {
const QString path(objectPath.path());
VpnConnection *conn = connection(path);
if (!conn) {
qCDebug(lcVpnLog) << "Adding connection:" << path;
conn = newConnection(path);
}
QVariantMap qmlProperties(propertiesToQml(properties));
qmlProperties.insert(QStringLiteral("storeCredentials"), credentials_.credentialsExist(CredentialsRepository::locationForObjectPath(path)));
updateConnection(conn, qmlProperties);
std::sort(connections.begin(), connections.end(), [this](const VpnConnection *i, const VpnConnection *j) -> bool {
// Return true if i should appear before j in the list
return compareConnections(i, j);
});
}
connect(&connmanVpn_, &ConnmanVpnProxy::ConnectionRemoved, [this](const QDBusObjectPath &objectPath) {
const QString path(objectPath.path());
if (VpnConnection *conn = connection(path)) {
qCDebug(lcVpnLog) << "Removing obsolete connection:" << path;
removeItem(conn);
delete conn;
} else {
qCWarning(lcVpnLog) << "Unable to remove unknown connection:" << path;
}
// Remove the proxy if present
auto it = connections_.find(path);
if (it != connections_.end()) {
ConnmanVpnConnectionProxy *proxy(*it);
connections_.erase(it);
delete proxy;
}
void VpnModel::reorderConnection(VpnConnection * conn)
{
const int itemCount(connections().size());
auto vpnServiceIterator = vpnServices_.find(path);
if (vpnServiceIterator != vpnServices_.end()) {
ConnmanServiceProxy *proxy(*vpnServiceIterator);
vpnServices_.erase(vpnServiceIterator);
delete proxy;
if (itemCount > 1) {
int index = 0;
for ( ; index < itemCount; ++index) {
const VpnConnection *existing = connections().at(index);
// Scenario 1 orderByConnected == true: order first by connected, second by name
// Scenario 2 orderByConnected == false: order only by name
if (!compareConnections(existing, conn)) {
break;
}
}
});
// If connman-vpn restarts, we need to discard and re-read the state
QDBusServiceWatcher *watcher = new QDBusServiceWatcher(connmanVpnService, QDBusConnection::systemBus(), QDBusServiceWatcher::WatchForRegistration | QDBusServiceWatcher::WatchForUnregistration, this);
connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, [this](const QString &) {
for (int i = 0, n = count(); i < n; ++i) {
get(i)->deleteLater();
const int currentIndex = connections().indexOf(conn);
if (index != currentIndex && (index - 1) != currentIndex) {
moveItem(currentIndex, (currentIndex < index ? (index - 1) : index));
}
clear();
setPopulated(false);
qDeleteAll(connections_);
connections_.clear();
qDeleteAll(vpnServices_);
vpnServices_.clear();
});
connect(watcher, &QDBusServiceWatcher::serviceRegistered, this, [this](const QString &) {
fetchVpnList();
});
fetchVpnList();
}
}
VpnModel::~VpnModel()
void VpnModel::updatedConnectionPosition()
{
deleteAll();
VpnConnection *conn = qobject_cast<VpnConnection *>(sender());
reorderConnection(conn);
}
int VpnModel::bestState() const
void VpnModel::connectedChanged()
{
return static_cast<int>(bestState_);
}
VpnConnection *conn = qobject_cast<VpnConnection *>(sender());
bool VpnModel::autoConnect() const
{
return autoConnect_;
}
bool VpnModel::orderByConnected() const
{
return orderByConnected_;
int row = connections().indexOf(conn);
if (row >= 0) {
QModelIndex index = createIndex(row, 0);;
emit dataChanged(index, index);
}
reorderConnection(conn);
}
void VpnModel::setOrderByConnected(bool orderByConnected)
void VpnModel::connectionAdded(const QString &path)
{
if (orderByConnected != orderByConnected_) {
orderByConnected_ = orderByConnected;
qCDebug(lcVpnLog) << "VPN connection added";
if (VpnConnection *conn = vpnManager()->connection(path)) {
bool credentialsExist = credentials_.credentialsExist(CredentialsRepository::locationForObjectPath(path));
conn->setStoreCredentials(credentialsExist);
// Update the ordering; only the connected connections need to move
// In practice only one VPN can be connected, so full sort is overkill
const int itemCount(count());
for (int index = 0; index < itemCount; ++index) {
VpnConnection *conn = get<VpnConnection>(index);
if (conn->connected()) {
reorderConnection(conn);
}
}
emit orderByConnectedChanged();
connect(conn, &VpnConnection::nameChanged, this, &VpnModel::updatedConnectionPosition, Qt::UniqueConnection);
connect(conn, &VpnConnection::connectedChanged, this, &VpnModel::connectedChanged, Qt::UniqueConnection);
connect(conn, &VpnConnection::stateChanged, this, &VpnModel::stateChanged, Qt::UniqueConnection);
}
}
void VpnModel::createConnection(const QVariantMap &createProperties)
void VpnModel::connectionRemoved(const QString &path)
{
const QString path(createProperties.value(QString("path")).toString());
if (path.isEmpty()) {
const QString host(createProperties.value(QString("host")).toString());
const QString name(createProperties.value(QString("name")).toString());
if (!host.isEmpty() && !name.isEmpty()) {
// Connman requires a domain value, but doesn't seem to use it...
QVariantMap properties(createProperties);
const QString domain(properties.value(QString("domain")).toString());
if (domain.isEmpty()) {
properties.insert(QString("domain"), QVariant::fromValue(createDefaultDomain()));
}
QDBusPendingCall call = connmanVpn_.Create(propertiesToDBus(properties));
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<QDBusObjectPath> reply = *watcher;
watcher->deleteLater();
if (reply.isError()) {
qCWarning(lcVpnLog) << "Unable to create Connman VPN connection:" << reply.error().message();
} else {
const QDBusObjectPath &objectPath(reply.value());
qCWarning(lcVpnLog) << "Created VPN connection:" << objectPath.path();
}
});
} else {
qCWarning(lcVpnLog) << "Unable to create VPN connection without domain, host and name properties";
}
} else {
qCWarning(lcVpnLog) << "Unable to create VPN connection with pre-existing path:" << path;
qCDebug(lcVpnLog) << "VPN connection removed";
if (VpnConnection *conn = vpnManager()->connection(path)) {
disconnect(conn, 0, this, 0);
}
}
void VpnModel::modifyConnection(const QString &path, const QVariantMap &properties)
void VpnModel::connectionsClearingAll()
{
auto it = connections_.find(path);
if (it != connections_.end()) {
// ConnmanVpnConnectionProxy provides the SetProperty interface to modify a connection,
// but as far as I can tell, the only way to cause Connman to store the configuration to
// disk is to create a new connection... Work around this by removing the existing
// connection and recreating it with the updated properties.
qCWarning(lcVpnLog) << "Updating VPN connection for modification:" << path;
// Remove properties that connman doesn't know about
QVariantMap updatedProperties(properties);
updatedProperties.remove(QString("path"));
updatedProperties.remove(QString("state"));
updatedProperties.remove(QString("index"));
updatedProperties.remove(QString("immutable"));
updatedProperties.remove(QString("storeCredentials"));
const QString domain(updatedProperties.value(QString("domain")).toString());
if (domain.isEmpty()) {
updatedProperties.insert(QString("domain"), QVariant::fromValue(createDefaultDomain()));