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 
35 using namespace SignOn;
36 
37 static QPointer<ConnectionManager> connectionInstance = 0;
38 
39 ConnectionManager::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 
52 ConnectionManager::~ConnectionManager()
53 {
54 }
55 
56 ConnectionManager *ConnectionManager::instance()
57 {
58  if (connectionInstance == 0) {
59  connectionInstance = new ConnectionManager;
60  }
61  return connectionInstance;
62 }
63 
64 void ConnectionManager::connect()
65 {
66  if (m_connection.isConnected()) {
67  Q_EMIT connected(m_connection);
68  } else {
69  init();
70  }
71 }
72 
73 bool ConnectionManager::hasConnection() const
74 {
75  return m_connection.isConnected();
76 }
77 
78 ConnectionManager::SocketConnectionStatus
79 ConnectionManager::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 
120 void 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 
150 void 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 
164 void ConnectionManager::onDisconnected()
165 {
166  TRACE() << "Disconnected from daemon";
167  m_serviceStatus = ServiceStatusUnknown;
168  Q_EMIT disconnected();
169 }