Skip to content

Commit

Permalink
[nemo-qml-plugin-systemsettings] Add support for automatically enable…
Browse files Browse the repository at this point in the history
…d VPNs
  • Loading branch information
matthewvogt committed Nov 4, 2016
1 parent a8337b7 commit 9e33326
Show file tree
Hide file tree
Showing 6 changed files with 176 additions and 6 deletions.
3 changes: 3 additions & 0 deletions rpm/nemo-qml-plugin-systemsettings.spec
Expand Up @@ -53,6 +53,7 @@ make %{?jobs:-j%jobs}
%install
rm -rf %{buildroot}
%qmake5_install
chmod +x %{buildroot}/%{_bindir}/vpn-updown.sh

%post -p /sbin/ldconfig

Expand All @@ -63,6 +64,8 @@ rm -rf %{buildroot}
%{_libdir}/qt5/qml/org/nemomobile/systemsettings/libnemosystemsettings.so
%{_libdir}/qt5/qml/org/nemomobile/systemsettings/qmldir
%{_libdir}/libsystemsettings.so.*
%{_libdir}/systemd/user/vpn-updown.service
%{_bindir}/vpn-updown.sh

%files devel
%defattr(-,root,root,-)
Expand Down
8 changes: 7 additions & 1 deletion src/src.pro
Expand Up @@ -67,11 +67,17 @@ target.path = $$[QT_INSTALL_LIBS]
pkgconfig.files = $$PWD/pkgconfig/systemsettings.pc
pkgconfig.path = $$target.path/pkgconfig

scripts.path = /usr/bin/
scripts.files = vpn-updown.sh

servicefiles.path = /usr/lib/systemd/user/
servicefiles.files = vpn-updown.service

QMAKE_PKGCONFIG_NAME = lib$$TARGET
QMAKE_PKGCONFIG_DESCRIPTION = System settings application development files
QMAKE_PKGCONFIG_LIBDIR = $$target.path
QMAKE_PKGCONFIG_INCDIR = $$develheaders.path
QMAKE_PKGCONFIG_DESTDIR = pkgconfig
QMAKE_PKGCONFIG_REQUIRES = Qt5Core Qt5DBus profile nemomodels-qt5

INSTALLS += target develheaders pkgconfig
INSTALLS += target develheaders pkgconfig scripts servicefiles
8 changes: 8 additions & 0 deletions src/vpn-updown.service
@@ -0,0 +1,8 @@
[Unit]
Description=Automatic up/down for configured VPN connections

[Service]
Type=oneshot
ExecStart=/usr/bin/vpn-updown.sh up
ExecStop=/usr/bin/vpn-updown.sh down
RemainAfterExit=yes
25 changes: 25 additions & 0 deletions src/vpn-updown.sh
@@ -0,0 +1,25 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Usage: $0 up|down"
exit 1
fi

METHOD=
if [ "$1" = "up" ]; then
METHOD=net.connman.vpn.Connection.Connect
elif [ "$1" = "down" ]; then
METHOD=net.connman.vpn.Connection.Disconnect
else
echo "Usage: $0 up|down"
exit 2
fi

logger "$0 $1"
for FILE in $(find /home/nemo/.local/share/system/vpn -mindepth 1 -maxdepth 1); do
TOKEN=$(basename $FILE)
OBJECTPATH=/net/connman/vpn/connection/$TOKEN
/bin/dbus-send --system --dest=net.connman.vpn --print-reply $OBJECTPATH $METHOD
logger "Invoked $METHOD for $OBJECTPATH"
done

exit 0
111 changes: 106 additions & 5 deletions src/vpnmodel.cpp
Expand Up @@ -179,9 +179,83 @@ QVariantMap propertiesToQml(const QVariantMap &fromDBus)

}


VpnModel::TokenFileRepository::TokenFileRepository(const QString &path)
: baseDir_(path)
{
if (!baseDir_.exists() && !baseDir_.mkpath(path)) {
qWarning() << "Unable to create base directory for VPN token files:" << path;
} else {
foreach (const QFileInfo &info, baseDir_.entryInfoList()) {
if (info.isFile() && info.size() == 0) {
// This is a token file
tokens_.append(info.fileName());
}
}
}
}

QString VpnModel::TokenFileRepository::tokenForObjectPath(const QString &path)
{
int index = path.lastIndexOf(QChar('/'));
if (index != -1) {
return path.mid(index + 1);
}

return QString();
}

bool VpnModel::TokenFileRepository::tokenExists(const QString &token) const
{
return tokens_.contains(token);
}

void VpnModel::TokenFileRepository::ensureToken(const QString &token)
{
if (!tokens_.contains(token)) {
QFile tokenFile(baseDir_.absoluteFilePath(token));
if (!tokenFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qWarning() << "Unable to write token file:" << tokenFile.fileName();
} else {
tokenFile.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ReadOther | QFileDevice::WriteOther);
tokenFile.close();
tokens_.append(token);
}
}
}

void VpnModel::TokenFileRepository::removeToken(const QString &token)
{
QStringList::iterator it = std::find(tokens_.begin(), tokens_.end(), token);
if (it != tokens_.end()) {
if (!baseDir_.remove(token)) {
qWarning() << "Unable to delete token file:" << token;
} else {
tokens_.erase(it);
}
}
}

void VpnModel::TokenFileRepository::removeUnknownTokens(const QStringList &knownConnections)
{
for (QStringList::iterator it = tokens_.begin(); it != tokens_.end(); ) {
const QString &token(*it);
if (knownConnections.contains(token)) {
// This token pertains to an extant connection
++it;
} else {
// Remove this token
baseDir_.remove(token);
it = tokens_.erase(it);
}
}
}


VpnModel::VpnModel(QObject *parent)
: ObjectListModel(parent, true, false)
, connmanVpn_("net.connman.vpn", "/", QDBusConnection::systemBus(), this)
, tokenFiles_("/home/nemo/.local/share/system/vpn")
{
qDBusRegisterMetaType<PathProperties>();
qDBusRegisterMetaType<PathPropertiesArray>();
Expand All @@ -193,7 +267,10 @@ VpnModel::VpnModel(QObject *parent)
qWarning() << "Adding connection:" << path;
conn = newConnection(path);
}
updateConnection(conn, propertiesToQml(properties));

QVariantMap qmlProperties(propertiesToQml(properties));
qmlProperties.insert(QStringLiteral("automaticUpDown"), tokenFiles_.tokenExists(TokenFileRepository::tokenForObjectPath(path)));
updateConnection(conn, qmlProperties);
});

connect(&connmanVpn_, &ConnmanVpnProxy::ConnectionRemoved, [this](const QDBusObjectPath &objectPath) {
Expand Down Expand Up @@ -243,8 +320,9 @@ void VpnModel::createConnection(const QVariantMap &properties)
if (path.isEmpty()) {
const QString host(properties.value(QString("host")).toString());
const QString name(properties.value(QString("name")).toString());
const QString domain(properties.value(QString("domain")).toString());

if (!host.isEmpty() && !name.isEmpty()) {
if (!host.isEmpty() && !name.isEmpty() && !domain.isEmpty()) {
QDBusPendingCall call = connmanVpn_.Create(propertiesToDBus(properties));

QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
Expand All @@ -260,7 +338,7 @@ void VpnModel::createConnection(const QVariantMap &properties)
}
});
} else {
qWarning() << "Unable to create VPN connection without host and name properties";
qWarning() << "Unable to create VPN connection without domain, host and name properties";
}
} else {
qWarning() << "Unable to create VPN connection with pre-existing path:" << path;
Expand All @@ -277,16 +355,22 @@ void VpnModel::modifyConnection(const QString &path, const QVariantMap &properti
qWarning() << "Removing VPN connection for modification:" << conn->path();
deleteConnection(conn->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("automaticUpDown"));

const QString token(TokenFileRepository::tokenForObjectPath(path));
const bool wasAutomatic(tokenFiles_.tokenExists(token));
const bool automatic(properties.value(QString("automaticUpDown")).toBool());

QDBusPendingCall call = connmanVpn_.Create(propertiesToDBus(updatedProperties));

QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, conn](QDBusPendingCallWatcher *watcher) {
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, conn, token, automatic, wasAutomatic](QDBusPendingCallWatcher *watcher) {
QDBusPendingReply<QDBusObjectPath> reply = *watcher;
watcher->deleteLater();

Expand All @@ -295,6 +379,14 @@ void VpnModel::modifyConnection(const QString &path, const QVariantMap &properti
} else {
const QDBusObjectPath &objectPath(reply.value());
qWarning() << "Modified VPN connection:" << objectPath.path();

if (automatic != wasAutomatic) {
if (automatic) {
tokenFiles_.ensureToken(token);
} else {
tokenFiles_.removeToken(token);
}
}
}
});
} else {
Expand Down Expand Up @@ -391,13 +483,22 @@ void VpnModel::fetchVpnList()
qWarning() << "Unable to fetch Connman VPN connections:" << reply.error().message();
} else {
const PathPropertiesArray &connections(reply.value());

QStringList tokens;
for (const PathProperties &connection : connections) {
const QString &path(connection.first.path());
const QVariantMap &properties(connection.second);

QVariantMap qmlProperties(propertiesToQml(properties));
qmlProperties.insert(QStringLiteral("automaticUpDown"), tokenFiles_.tokenExists(TokenFileRepository::tokenForObjectPath(path)));

VpnConnection *conn = newConnection(path);
updateConnection(conn, propertiesToQml(properties));
updateConnection(conn, qmlProperties);

tokens.append(TokenFileRepository::tokenForObjectPath(path));
}

tokenFiles_.removeUnknownTokens(tokens);
}

setPopulated(true);
Expand Down
27 changes: 27 additions & 0 deletions src/vpnmodel.h
Expand Up @@ -40,6 +40,7 @@

#include <objectlistmodel.h>

#include <QDir>
#include <QVariantMap>


Expand Down Expand Up @@ -87,8 +88,28 @@ class SYSTEMSETTINGS_EXPORT VpnModel : public ObjectListModel
VpnConnection *newConnection(const QString &path);
void updateConnection(VpnConnection *conn, const QVariantMap &properties);

class TokenFileRepository
{
public:
TokenFileRepository(const QString &path);

static QString tokenForObjectPath(const QString &path);

bool tokenExists(const QString &token) const;

void ensureToken(const QString &token);
void removeToken(const QString &token);

void removeUnknownTokens(const QStringList &knownConnections);

private:
QDir baseDir_;
QStringList tokens_;
};

ConnmanVpnProxy connmanVpn_;
QHash<QString, ConnmanVpnConnectionProxy *> connections_;
TokenFileRepository tokenFiles_;
};

class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject
Expand All @@ -99,6 +120,7 @@ class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject
Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged)
Q_PROPERTY(QString domain READ domain WRITE setDomain NOTIFY domainChanged)
Q_PROPERTY(QString networks READ networks WRITE setNetworks NOTIFY networksChanged)
Q_PROPERTY(bool automaticUpDown READ automaticUpDown WRITE setAutomaticUpDown NOTIFY automaticUpDownChanged)
Q_PROPERTY(int state READ state WRITE setState NOTIFY stateChanged)
Q_PROPERTY(int type READ type WRITE setType NOTIFY typeChanged)
Q_PROPERTY(bool immutable READ immutable WRITE setImmutable NOTIFY immutableChanged)
Expand Down Expand Up @@ -127,6 +149,9 @@ class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject
QString networks() const { return networks_; }
void setNetworks(const QString &networks) { updateMember(&VpnConnection::networks_, networks, &VpnConnection::networksChanged); }

bool automaticUpDown() const { return automaticUpDown_; }
void setAutomaticUpDown(bool automaticUpDown) { updateMember(&VpnConnection::automaticUpDown_, automaticUpDown, &VpnConnection::automaticUpDownChanged); }

int state() const { return state_; }
void setState(int state) { updateMember(&VpnConnection::state_, state, &VpnConnection::stateChanged); }

Expand Down Expand Up @@ -164,6 +189,7 @@ class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject
void hostChanged();
void domainChanged();
void networksChanged();
void automaticUpDownChanged();
void immutableChanged();
void indexChanged();
void iPv4Changed();
Expand Down Expand Up @@ -191,6 +217,7 @@ class SYSTEMSETTINGS_EXPORT VpnConnection : public QObject
QString host_;
QString domain_;
QString networks_;
bool automaticUpDown_;
bool immutable_;
int index_;
QVariantMap ipv4_;
Expand Down

0 comments on commit 9e33326

Please sign in to comment.