connection-manager.cpp
00001 /*
00002  * This file is part of signon
00003  *
00004  * Copyright (C) 2013-2016 Canonical Ltd.
00005  *
00006  * Contact: Alberto Mardegan <alberto.mardegan@canonical.com>
00007  *
00008  * This library is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU Lesser General Public License
00010  * version 2.1 as published by the Free Software Foundation.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
00020  * 02110-1301 USA
00021  */
00022 
00023 #include "connection-manager.h"
00024 #include "debug.h"
00025 #include "libsignoncommon.h"
00026 #include "signond/signoncommon.h"
00027 
00028 #include <QDBusConnectionInterface>
00029 #include <QDBusError>
00030 #include <QDBusPendingCallWatcher>
00031 #include <QPointer>
00032 #include <QProcessEnvironment>
00033 #include <QStandardPaths>
00034 
00035 using namespace SignOn;
00036 
00037 static QPointer<ConnectionManager> connectionInstance = 0;
00038 
00039 ConnectionManager::ConnectionManager(QObject *parent):
00040     QObject(parent),
00041     m_connection(QLatin1String("libsignon-qt-invalid")),
00042     m_serviceStatus(ServiceStatusUnknown)
00043 {
00044     if (connectionInstance == 0) {
00045         init();
00046         connectionInstance = this;
00047     } else {
00048         BLAME() << "SignOn::ConnectionManager instantiated more than once!";
00049     }
00050 }
00051 
00052 ConnectionManager::~ConnectionManager()
00053 {
00054 }
00055 
00056 ConnectionManager *ConnectionManager::instance()
00057 {
00058     if (connectionInstance == 0) {
00059         connectionInstance = new ConnectionManager;
00060     }
00061     return connectionInstance;
00062 }
00063 
00064 void ConnectionManager::connect()
00065 {
00066     if (m_connection.isConnected()) {
00067         Q_EMIT connected(m_connection);
00068     } else {
00069         init();
00070     }
00071 }
00072 
00073 bool ConnectionManager::hasConnection() const
00074 {
00075     return m_connection.isConnected();
00076 }
00077 
00078 ConnectionManager::SocketConnectionStatus
00079 ConnectionManager::setupSocketConnection()
00080 {
00081     QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
00082     QLatin1String one("1");
00083     if (environment.value(QLatin1String("SSO_USE_PEER_BUS"), one) != one) {
00084         return SocketConnectionUnavailable;
00085     }
00086 
00087     QString runtimeDir =
00088         QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
00089     if (runtimeDir.isEmpty()) return SocketConnectionUnavailable;
00090 
00091     QString socketFileName =
00092         QString::fromLatin1("unix:path=%1/" SIGNOND_SOCKET_FILENAME).arg(runtimeDir);
00093     static int count = 0;
00094 
00095     QDBusConnection connection =
00096         QDBusConnection::connectToPeer(socketFileName,
00097                                        QString(QLatin1String("libsignon-qt%1")).arg(count++));
00098     if (!connection.isConnected()) {
00099         QDBusError error = connection.lastError();
00100         QString name = error.name();
00101         TRACE() << "p2p error:" << error << error.type();
00102         if (name == QLatin1String("org.freedesktop.DBus.Error.FileNotFound") &&
00103             m_serviceStatus != ServiceActivated) {
00104             return SocketConnectionNoService;
00105         } else {
00106             return SocketConnectionUnavailable;
00107         }
00108     }
00109 
00110     m_connection = connection;
00111     m_connection.connect(QString(),
00112                          QLatin1String("/org/freedesktop/DBus/Local"),
00113                          QLatin1String("org.freedesktop.DBus.Local"),
00114                          QLatin1String("Disconnected"),
00115                          this, SLOT(onDisconnected()));
00116 
00117     return SocketConnectionOk;
00118 }
00119 
00120 void ConnectionManager::init()
00121 {
00122     if (m_serviceStatus == ServiceActivating) return;
00123 
00124     SocketConnectionStatus status = setupSocketConnection();
00125 
00126     if (status == SocketConnectionNoService) {
00127         TRACE() << "Peer connection unavailable, activating service";
00128         QDBusConnectionInterface *interface =
00129             QDBusConnection::sessionBus().interface();
00130         QDBusPendingCall call =
00131             interface->asyncCall(QLatin1String("StartServiceByName"),
00132                                  SIGNOND_SERVICE, uint(0));
00133         m_serviceStatus = ServiceActivating;
00134         QDBusPendingCallWatcher *watcher =
00135             new QDBusPendingCallWatcher(call, this);
00136         QObject::connect(watcher,
00137                          SIGNAL(finished(QDBusPendingCallWatcher*)),
00138                          this,
00139                          SLOT(onActivationDone(QDBusPendingCallWatcher*)));
00140     } else if (status == SocketConnectionUnavailable) {
00141         m_connection = SIGNOND_BUS;
00142     }
00143 
00144     if (m_connection.isConnected()) {
00145         TRACE() << "Connected to" << m_connection.name();
00146         Q_EMIT connected(m_connection);
00147     }
00148 }
00149 
00150 void ConnectionManager::onActivationDone(QDBusPendingCallWatcher *watcher)
00151 {
00152     QDBusPendingReply<> reply(*watcher);
00153     watcher->deleteLater();
00154 
00155     if (!reply.isError()) {
00156         m_serviceStatus = ServiceActivated;
00157         /* Attempt to connect again */
00158         init();
00159     } else {
00160         BLAME() << reply.error();
00161     }
00162 }
00163 
00164 void ConnectionManager::onDisconnected()
00165 {
00166     TRACE() << "Disconnected from daemon";
00167     m_serviceStatus = ServiceStatusUnknown;
00168     Q_EMIT disconnected();
00169 }