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 m_retryCount(0)
44{
45 if (connectionInstance == 0) {
46 init();
47 connectionInstance = this;
48 } else {
49 BLAME() << "SignOn::ConnectionManager instantiated more than once!";
50 }
51}
52
53ConnectionManager::~ConnectionManager()
54{
55}
56
57ConnectionManager *ConnectionManager::instance()
58{
59 if (connectionInstance == 0) {
60 connectionInstance = new ConnectionManager;
61 }
62 return connectionInstance;
63}
64
65void ConnectionManager::connect()
66{
67 if (m_connection.isConnected()) {
68 Q_EMIT connected(m_connection);
69 } else {
70 init();
71 }
72}
73
74bool ConnectionManager::hasConnection() const
75{
76 return m_connection.isConnected();
77}
78
79ConnectionManager::SocketConnectionStatus
80ConnectionManager::setupSocketConnection()
81{
82 QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
83 QLatin1String one("1");
84 if (environment.value(QLatin1String("SSO_USE_PEER_BUS"), one) != one) {
85 return SocketConnectionUnavailable;
86 }
87
88 QString runtimeDir =
89 QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
90 if (runtimeDir.isEmpty()) return SocketConnectionUnavailable;
91
92 QString socketFileName =
93 QString::fromLatin1("unix:path=%1/" SIGNOND_SOCKET_FILENAME).arg(runtimeDir);
94 static int count = 0;
95
96 QDBusConnection connection =
97 QDBusConnection::connectToPeer(socketFileName,
98 QString(QLatin1String("libsignon-qt%1")).arg(count++));
99 if (!connection.isConnected()) {
100 QDBusError error = connection.lastError();
101 QString name = error.name();
102 TRACE() << "p2p error:" << error << error.type();
103 if (name == QLatin1String("org.freedesktop.DBus.Error.FileNotFound") &&
104 m_serviceStatus != ServiceActivated) {
105 return SocketConnectionNoService;
106 } else {
107 return SocketConnectionUnavailable;
108 }
109 }
110
111 m_connection = connection;
112 m_connection.connect(QString(),
113 QLatin1String("/org/freedesktop/DBus/Local"),
114 QLatin1String("org.freedesktop.DBus.Local"),
115 QLatin1String("Disconnected"),
116 this, SLOT(onDisconnected()));
117
118 return SocketConnectionOk;
119}
120
121void ConnectionManager::init()
122{
123 if (m_serviceStatus == ServiceActivating) return;
124
125 SocketConnectionStatus status = setupSocketConnection();
126
127 if (status == SocketConnectionUnavailable) {
128#ifdef ENABLE_P2P
129 if (m_retryCount >= 15) {
130 BLAME() << "Unable to activate p2p signond service!";
131 return;
132 }
133 TRACE() << "Unable to activate p2p signond service, trying again";
134 status = SocketConnectionNoService;
135 m_retryCount += 1;
136#else
137 TRACE() << "Unable to activate p2p signond service, falling back to session bus";
138 m_connection = SIGNOND_BUS;
139#endif
140 }
141
142 if (status == SocketConnectionNoService) {
143 TRACE() << "Peer connection unavailable, activating service";
144 QDBusConnectionInterface *interface =
145 QDBusConnection::sessionBus().interface();
146 QDBusPendingCall call =
147 interface->asyncCall(QLatin1String("StartServiceByName"),
148 SIGNOND_SERVICE, uint(0));
149 m_serviceStatus = ServiceActivating;
150 QDBusPendingCallWatcher *watcher =
151 new QDBusPendingCallWatcher(call, this);
152 QObject::connect(watcher,
153 SIGNAL(finished(QDBusPendingCallWatcher*)),
154 this,
155 SLOT(onActivationDone(QDBusPendingCallWatcher*)));
156 }
157
158 if (m_connection.isConnected()) {
159 m_retryCount = 0;
160 TRACE() << "Connected to" << m_connection.name();
161 Q_EMIT connected(m_connection);
162 }
163}
164
165void ConnectionManager::onActivationDone(QDBusPendingCallWatcher *watcher)
166{
167 QDBusPendingReply<> reply(*watcher);
168 watcher->deleteLater();
169
170 if (!reply.isError()) {
171 m_serviceStatus = ServiceActivated;
172 /* Attempt to connect again */
173 init();
174 } else {
175 BLAME() << reply.error();
176 }
177}
178
179void ConnectionManager::onDisconnected()
180{
181 TRACE() << "Disconnected from daemon";
182 m_serviceStatus = ServiceStatusUnknown;
183 Q_EMIT disconnected();
184}