connection-manager.cpp
00001 /*
00002  * This file is part of signon
00003  *
00004  * Copyright (C) 2013 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 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
00034 #include <QStandardPaths>
00035 #endif
00036 
00037 using namespace SignOn;
00038 
00039 static QPointer<ConnectionManager> connectionInstance = 0;
00040 
00041 ConnectionManager::ConnectionManager(QObject *parent):
00042     QObject(parent),
00043     m_connection(QLatin1String("libsignon-qt-invalid")),
00044     m_serviceStatus(ServiceStatusUnknown)
00045 {
00046     if (connectionInstance == 0) {
00047         init();
00048         connectionInstance = this;
00049     } else {
00050         BLAME() << "SignOn::ConnectionManager instantiated more than once!";
00051     }
00052 }
00053 
00054 ConnectionManager::~ConnectionManager()
00055 {
00056 }
00057 
00058 ConnectionManager *ConnectionManager::instance()
00059 {
00060     if (connectionInstance == 0) {
00061         connectionInstance = new ConnectionManager;
00062     }
00063     return connectionInstance;
00064 }
00065 
00066 void ConnectionManager::connect()
00067 {
00068     if (m_connection.isConnected()) {
00069         Q_EMIT connected(m_connection);
00070     } else {
00071         init();
00072     }
00073 }
00074 
00075 bool ConnectionManager::hasConnection() const
00076 {
00077     return m_connection.isConnected();
00078 }
00079 
00080 ConnectionManager::SocketConnectionStatus
00081 ConnectionManager::setupSocketConnection()
00082 {
00083     QProcessEnvironment environment = QProcessEnvironment::systemEnvironment();
00084     QLatin1String one("1");
00085     if (environment.value(QLatin1String("SSO_USE_PEER_BUS"), one) != one) {
00086         return SocketConnectionUnavailable;
00087     }
00088 
00089 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
00090     QString runtimeDir =
00091         QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
00092 #else
00093     QString runtimeDir = environment.value(QLatin1String("XDG_RUNTIME_DIR"));
00094 #endif
00095     if (runtimeDir.isEmpty()) return SocketConnectionUnavailable;
00096 
00097     QString socketFileName =
00098         QString::fromLatin1("unix:path=%1/" SIGNOND_SOCKET_FILENAME).arg(runtimeDir);
00099     static int count = 0;
00100 
00101     QDBusConnection connection =
00102         QDBusConnection::connectToPeer(socketFileName,
00103                                        QString(QLatin1String("libsignon-qt%1")).arg(count++));
00104     if (!connection.isConnected()) {
00105         QDBusError error = connection.lastError();
00106         QString name = error.name();
00107         TRACE() << "p2p error:" << error << error.type();
00108         if (name == QLatin1String("org.freedesktop.DBus.Error.FileNotFound") &&
00109             m_serviceStatus != ServiceActivated) {
00110             return SocketConnectionNoService;
00111         } else {
00112             return SocketConnectionUnavailable;
00113         }
00114     }
00115 
00116     m_connection = connection;
00117     m_connection.connect(QString(),
00118                          QLatin1String("/org/freedesktop/DBus/Local"),
00119                          QLatin1String("org.freedesktop.DBus.Local"),
00120                          QLatin1String("Disconnected"),
00121                          this, SLOT(onDisconnected()));
00122 
00123     return SocketConnectionOk;
00124 }
00125 
00126 void ConnectionManager::init()
00127 {
00128     if (m_serviceStatus == ServiceActivating) return;
00129 
00130     SocketConnectionStatus status = setupSocketConnection();
00131 
00132     if (status == SocketConnectionNoService) {
00133         TRACE() << "Peer connection unavailable, activating service";
00134         QDBusConnectionInterface *interface =
00135             QDBusConnection::sessionBus().interface();
00136         QDBusPendingCall call =
00137             interface->asyncCall(QLatin1String("StartServiceByName"),
00138                                  SIGNOND_SERVICE, uint(0));
00139         m_serviceStatus = ServiceActivating;
00140         QDBusPendingCallWatcher *watcher =
00141             new QDBusPendingCallWatcher(call, this);
00142         QObject::connect(watcher,
00143                          SIGNAL(finished(QDBusPendingCallWatcher*)),
00144                          this,
00145                          SLOT(onActivationDone(QDBusPendingCallWatcher*)));
00146     } else if (status == SocketConnectionUnavailable) {
00147         m_connection = SIGNOND_BUS;
00148     }
00149 
00150     if (m_connection.isConnected()) {
00151         TRACE() << "Connected to" << m_connection.name();
00152         Q_EMIT connected(m_connection);
00153     }
00154 }
00155 
00156 void ConnectionManager::onActivationDone(QDBusPendingCallWatcher *watcher)
00157 {
00158     QDBusPendingReply<> reply(*watcher);
00159     watcher->deleteLater();
00160 
00161     if (!reply.isError()) {
00162         m_serviceStatus = ServiceActivated;
00163         /* Attempt to connect again */
00164         init();
00165     } else {
00166         BLAME() << reply.error();
00167     }
00168 }
00169 
00170 void ConnectionManager::onDisconnected()
00171 {
00172     TRACE() << "Disconnected from daemon";
00173     m_serviceStatus = ServiceStatusUnknown;
00174     Q_EMIT disconnected();
00175 }