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