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