My Project
connection-manager.cpp
1/*
2 * This file is part of signon
3 *
4 * Copyright (C) 2013-2016 Canonical Ltd.
5 *
6 * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * version 2.1 as published by the Free Software Foundation.
11 *
12 * This library is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA
21 */
22
23#include "connection-manager.h"
24#include "debug.h"
25#include "libsignoncommon.h"
26#include "signond/signoncommon.h"
27
28#include <QDBusConnectionInterface>
29#include <QDBusError>
30#include <QDBusPendingCallWatcher>
31#include <QPointer>
32#include <QProcessEnvironment>
33#include <QStandardPaths>
34
35using namespace SignOn;
36
37static QPointer<ConnectionManager> connectionInstance = 0;
38
39ConnectionManager::ConnectionManager(QObject *parent):
40 QObject(parent),
41 m_connection(QLatin1String("libsignon-qt-invalid")),
42 m_serviceStatus(ServiceStatusUnknown)
43{
44 if (connectionInstance == 0) {
45 init();
46 connectionInstance = this;
47 } else {
48 BLAME() << "SignOn::ConnectionManager instantiated more than once!";
49 }
50}
51
52ConnectionManager::~ConnectionManager()
53{
54}
55
56ConnectionManager *ConnectionManager::instance()
57{
58 if (connectionInstance == 0) {
59 connectionInstance = new ConnectionManager;
60 }
61 return connectionInstance;
62}
63
64void ConnectionManager::connect()
65{
66 if (m_connection.isConnected()) {
67 Q_EMIT connected(m_connection);
68 } else {
69 init();
70 }
71}
72
73bool ConnectionManager::hasConnection() const
74{
75 return m_connection.isConnected();
76}
77
78ConnectionManager::SocketConnectionStatus
79ConnectionManager::setupSocketConnection()
80{
81 QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
82 QLatin1String one("1");
83 if (environment.value(QLatin1String("SSO_USE_PEER_BUS"), one) != one) {
84 return SocketConnectionUnavailable;
85 }
86
87 QString runtimeDir =
88 QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
89 if (runtimeDir.isEmpty()) return SocketConnectionUnavailable;
90
91 QString socketFileName =
92 QString::fromLatin1("unix:path=%1/" SIGNOND_SOCKET_FILENAME).arg(runtimeDir);
93 static int count = 0;
94
95 QDBusConnection connection =
96 QDBusConnection::connectToPeer(socketFileName,
97 QString(QLatin1String("libsignon-qt%1")).arg(count++));
98 if (!connection.isConnected()) {
99 QDBusError error = connection.lastError();
100 QString name = error.name();
101 TRACE() << "p2p error:" << error << error.type();
102 if (name == QLatin1String("org.freedesktop.DBus.Error.FileNotFound") &&
103 m_serviceStatus != ServiceActivated) {
104 return SocketConnectionNoService;
105 } else {
106 return SocketConnectionUnavailable;
107 }
108 }
109
110 m_connection = connection;
111 m_connection.connect(QString(),
112 QLatin1String("/org/freedesktop/DBus/Local"),
113 QLatin1String("org.freedesktop.DBus.Local"),
114 QLatin1String("Disconnected"),
115 this, SLOT(onDisconnected()));
116
117 return SocketConnectionOk;
118}
119
120void ConnectionManager::init()
121{
122 if (m_serviceStatus == ServiceActivating) return;
123
124 SocketConnectionStatus status = setupSocketConnection();
125
126 if (status == SocketConnectionNoService) {
127 TRACE() << "Peer connection unavailable, activating service";
128 QDBusConnectionInterface *interface =
129 QDBusConnection::sessionBus().interface();
130 QDBusPendingCall call =
131 interface->asyncCall(QLatin1String("StartServiceByName"),
132 SIGNOND_SERVICE, uint(0));
133 m_serviceStatus = ServiceActivating;
134 QDBusPendingCallWatcher *watcher =
135 new QDBusPendingCallWatcher(call, this);
136 QObject::connect(watcher,
137 SIGNAL(finished(QDBusPendingCallWatcher*)),
138 this,
139 SLOT(onActivationDone(QDBusPendingCallWatcher*)));
140 } else if (status == SocketConnectionUnavailable) {
141 m_connection = SIGNOND_BUS;
142 }
143
144 if (m_connection.isConnected()) {
145 TRACE() << "Connected to" << m_connection.name();
146 Q_EMIT connected(m_connection);
147 }
148}
149
150void ConnectionManager::onActivationDone(QDBusPendingCallWatcher *watcher)
151{
152 QDBusPendingReply<> reply(*watcher);
153 watcher->deleteLater();
154
155 if (!reply.isError()) {
156 m_serviceStatus = ServiceActivated;
157 /* Attempt to connect again */
158 init();
159 } else {
160 BLAME() << reply.error();
161 }
162}
163
164void ConnectionManager::onDisconnected()
165{
166 TRACE() << "Disconnected from daemon";
167 m_serviceStatus = ServiceStatusUnknown;
168 Q_EMIT disconnected();
169}