/* * This file is part of Remote Support Desktop * https://gitlab.das-netzwerkteam.de/RemoteWebApp/rwa.support.desktopapp * Copyright 2020-2021 Daniel Teichmann * Copyright 2020-2021 Mike Gabriel * SPDX-License-Identifier: GPL-2.0-or-later * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "session.h" Session::Session(QObject *parent, MainQMLAdaptor* main_gui) : QObject(parent) { _initDBus(); _main_gui = main_gui; // QML -> MainQMLAdaptor::handleConnectButtonClick --onConnectButtonClick--> this::handleConnectButtonClick QObject::connect(_main_gui, SIGNAL(onConnectButtonClick(bool)), this, SLOT(handleConnectButtonClick(bool))); // session::setPin --pinChanged--> MainQMLAdaptor::setPin --pinChanged--> QML QObject::connect(this, SIGNAL(pinChanged(QString)), main_gui, SLOT(setPin(QString))); // session::setURL --urlChanged--> MainQMLAdaptor::setURL --urlChanged--> QML QObject::connect(this, SIGNAL(urlChanged(QString)), main_gui, SLOT(setURL(QString))); // session::setSessionID --sessionIDChanged--> MainQMLAdaptor::setSessionID --sessionIDChanged--> QML QObject::connect(this, SIGNAL(sessionIDChanged(QString)), main_gui, SLOT(setSessionID(QString))); // QML -> MainQMLAdaptor::onCloseHandler --onCloseSignal--> session::onCloseHandler QObject::connect(main_gui, SIGNAL(onCloseSignal()), this, SLOT(onCloseHandler())); this->init_vars(); } void Session::statusTimerEvent() { qDebug() << "Status timer event triggered"; this->refresh_status_request_dbus(this->getId()); } void Session::init_vars() { setPin("-----"); setSessionID("-----"); setId("-----"); setURL(tr("Not available yet")); setStatus("unknown"); _main_gui->setConnectButtonChecked(false); _main_gui->setConnectButtonEnabled(true); _main_gui->setStatusIndicator(false); _minimizedBefore = false; } QString Session::getStatus() { return _status; } QString Session::getURL() { return _url; } QString Session::getId() { return QString(_id); } QString Session::getSessionID() { return QString(_session_id); } QString Session::getPin() { return _pin; } bool Session::isSessionAliveOrRunning(QString status) { if (status == "running" || status == "active") { return true; } else { return false; } } void Session::minimizeWindow() { if (!_minimizedBefore) { qDebug() << "Minimizing window now..."; emit _main_gui->minimizeWindow(); _minimizedBefore = true; } } void Session::setStatus(QString status) { _status = status; QString guiString = tr("Unknown state of service"); _main_gui->setStatusIndicator(false); qDebug() << "setStatus(): Setting status to " << status; if (status == "running") { /* Session is running but no one is connected yet */ guiString = tr("Remote Support session is ready to be connected to"); _main_gui->setStatusIndicator(true, QColor(255, 255, 0, 127)); } else if (status == "dead") { /* Session died */ guiString = tr("Remote Support session was stopped ungracefully"); // Clear current variables this->init_vars(); _main_gui->setStatusIndicator(true, QColor(255, 0, 0, 127)); emit _main_gui->showWindow(); _main_gui->showToast(tr("Remote Support session was stopped ungracefully"), 5000); } else if (status == "stopped") { /* Session is stopped */ guiString = tr("Remote Support session was stopped"); // Clear current variables this->init_vars(); emit _main_gui->showWindow(); _main_gui->showToast(tr("Remote Support session was stopped"), 5000); } else if (status == "active") { /* Partner is connected */ QTimer::singleShot(1000, this, &Session::minimizeWindow); guiString = tr("Your partner is connected to the Remote Support session"); _main_gui->setStatusIndicator(true, QColor(0, 255, 0, 127)); } else if (status == "waiting_start_request_answer") { /* When pressing on start button display following message while waiting */ guiString = tr("Trying to reach session service..."); } else if (status == "start_session_error") { /* Session couldn't be started */ guiString = tr("Remote Support session couldn't be started!"); _main_gui->setStatusIndicator(true, QColor(255, 0, 0, 127)); _main_gui->showToast(tr("An error occured while trying to start a session!"), 5000); } else if (status == "start_session_success") { /* Session successfully started */ guiString = tr("Remote Support session successfully started!"); _main_gui->setStatusIndicator(true, QColor(255, 255, 0, 127)); _main_gui->showToast(tr("Session was started successfully")); } else if (status == "status_session_error") { /* Session's status couldn't be refreshed */ _main_gui->showToast(tr("Session status could not be refreshed!"), 5000); guiString = tr("Session status could not be refreshed!") + "\n" + tr("remote support partner could still be connected!"); _main_gui->setStatusIndicator(true, QColor(255, 0, 0, 127)); emit _main_gui->showWindow(); } else if (status == "stop_session_error") { /* Session couldn't be stopped */ _main_gui->showToast(tr("Session could not be stopped!"), 5000); guiString = tr("Session could not be stopped!") + "\n" + tr("remote support partner could still be connected!"); _main_gui->setStatusIndicator(true, QColor(255, 0, 0, 127)); emit _main_gui->showWindow(); } _main_gui->setStatus(guiString); emit statusChanged(_status); } void Session::setURL(QString url) { _url = url; emit urlChanged(url); } void Session::setId(QString id) { _id = id; emit idChanged(id); } void Session::setSessionID(QString session_id) { _session_id = session_id; emit sessionIDChanged(session_id); } void Session::setPin(QString pin) { _pin = pin; emit pinChanged(pin); } void Session::handleConnectButtonClick(bool checked) { qDebug() << "-----Connect button handler-----" << "\nCurrent service-session #" << this->getId() << "\nCurrent support-session #" << this->getSessionID(); // Stopping even if nothing is running this->stop_request_dbus(this->getId()); if (checked) { // Start the Session again this->start_request_dbus(); } qDebug() << "-----\\Connect button handler-----"; } void Session::_initDBus() { if (!QDBusConnection::sessionBus().isConnected()) { qCritical() << "Cannot connect to the D-Bus session bus."; } // Create DBus object _dbus_rwa = new OrgArcticaProjectRWAInterface("org.ArcticaProject.RWASupportSessionService", "/RWASupportSessionService", QDBusConnection::sessionBus(), this->parent()); qDebug("Initialized DBus object!"); } void Session::start_request_dbus() { qDebug() << "Requesting D-Bus session service to start a new session"; // Make an asynchrous 'start' call (Response will be sent to 'start_dbus_replied') QDBusPendingCall async = _dbus_rwa->asyncCall("start"); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(start_dbus_replied(QDBusPendingCallWatcher*))); // Disable connect button to reduce spam. // And don't enable it as long there is no status update. // (Or something fails) _main_gui->setConnectButtonEnabled(false); this->setStatus("waiting_start_request_answer"); } void Session::start_dbus_replied(QDBusPendingCallWatcher *call) { QString result = ""; QDBusPendingReply reply = *call; if (reply.isError()) { qDebug() << "D-Bus 'start' request failed, this was the reply:"; qDebug() << reply.error(); // The user should have the oportunity to try again. this->init_vars(); qCritical() << "An error occured while creating a new session!"; this->setStatus("start_session_error"); return; } else { result = reply.argumentAt<0>(); } call->deleteLater(); qDebug() << "Raw JSON from starting session is:" << result; QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); // Get the QJsonObject QJsonObject jObject = doc.object(); QVariantMap mainMap = jObject.toVariantMap(); // Status of request QString request_status = mainMap["status"].toString(); if (request_status == "success") { qDebug() << "Successfully started a Session."; this->setStatus("start_session_success"); // Immediately ask for the status of the session QTimer::singleShot(0, this, &Session::statusTimerEvent); } else { qCritical() << "An error occured while creating a new session!"; this->init_vars(); this->setStatus("start_session_error"); return; } // Service ID == PID bool ok; long long service_id = mainMap["id"].toLongLong(&ok); this->setId(QString::number(service_id)); // Sanity Check if(ok == false){ qErrnoWarning("Unable to parse out of dbus answer!"); init_vars(); return; } // URL of remote web app frontend QString url = mainMap["url"].toString(); this->setURL(url); // PIN long long pin = mainMap["pin"].toLongLong(&ok); this->setPin(QString::number(pin)); // Sanity Check if(ok == false){ qErrnoWarning("Unable to parse out of dbus answer!"); init_vars(); return; } // session_id = remote support id from the rwa-server long long session_id = mainMap["session_id"].toLongLong(); this->setSessionID(QString::number(session_id)); // Sanity Check if(ok == false){ qErrnoWarning("Unable to parse out of dbus answer!"); init_vars(); return; } qDebug() << "Got service_id:" << service_id << "\nGot session_id:" << session_id << "\nGot url:" << url << "\nGot pin:" << pin; emit pinChanged(QString::number(pin)); emit urlChanged(url); emit idChanged(QString::number(service_id)); emit sessionIDChanged(QString::number(session_id)); } void Session::stop_request_dbus(QString id) { bool ok; if (id.toLongLong(&ok) == 0 ){ qDebug() << "Won't send a request to D-Bus service to stop a session when session ID == 0"; return; } if(ok == false){ qErrnoWarning("Unable to convert id to "); return; } // Stopping now. qDebug() << "Requesting D-Bus session service to stop session #" << id; // Make an asynchrous 'stop' call (Response will be sent to 'stop_dbus_replied') QDBusPendingCall async = _dbus_rwa->asyncCall("stop", id); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(stop_dbus_replied(QDBusPendingCallWatcher*))); // Clear current variables this->init_vars(); // Disable connect button to reduce spam. // And don't enable it as long there is no status update. // (Or something fails) _main_gui->setConnectButtonEnabled(false); } void Session::stop_dbus_replied(QDBusPendingCallWatcher *call) { QString result = ""; QDBusPendingReply reply = *call; if (reply.isError()) { qDebug() << "D-Bus 'stop' request failed, this was the reply:"; qDebug() << reply.error(); this->init_vars(); this->setStatus("stop_session_error"); return; } else { result = reply.argumentAt<0>(); } call->deleteLater(); // Get the QJsonObject QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); QJsonObject jObject = doc.object(); QVariantMap mainMap = jObject.toVariantMap(); QString new_status = mainMap["status"].toString(); qDebug() << "stop_dbus_replied(): Refreshed status:" << new_status; // Clear current variables this->init_vars(); // But the status indicator should display that the Session has stopped this->setStatus(new_status); } void Session::status_request_dbus(QString id) { bool ok; if (id.toLongLong(&ok) == 0 ){ qDebug() << "Won't send a request to D-Bus service of a session's status when session ID == 0"; return; } if(ok == false){ qErrnoWarning("Unable to convert id to "); return; } // Requesting status now. qDebug() << "Requesting status for session #" << id; // Make an asynchrous 'status' call (Response will be sent to 'status_dbus_replied') QDBusPendingCall async = _dbus_rwa->asyncCall("status", id); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(stop_dbus_replied(QDBusPendingCallWatcher*))); } void Session::refresh_status_request_dbus(QString id) { bool ok; if (id.toLongLong(&ok) == 0){ qDebug() << "Won't send a request to D-Bus service to refresh the status of a session when session ID == 0"; return; } if(ok == false){ qErrnoWarning("Unable to convert id to "); return; } // Refreshing status qDebug() << "Requesting status refresh for session #" << id; // Make an asynchrous 'refresh_status' call (Response will be sent to 'status_dbus_replied') QDBusPendingCall async = _dbus_rwa->asyncCall("refresh_status", id); QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(async, this); QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), this, SLOT(status_dbus_replied(QDBusPendingCallWatcher*))); } void Session::status_dbus_replied(QDBusPendingCallWatcher *call) { QString result = ""; QDBusPendingReply reply = *call; if (reply.isError()) { qDebug() << "D-Bus '(refresh_)status' request failed, this was the reply:"; qDebug() << reply.error(); this->init_vars(); this->setStatus("status_session_error"); return; } else { result = reply.argumentAt<0>(); } call->deleteLater(); // Get the QJsonObject QJsonDocument doc = QJsonDocument::fromJson(result.toUtf8()); QJsonObject jObject = doc.object(); QVariantMap mainMap = jObject.toVariantMap(); QString new_status = mainMap["status"].toString(); qDebug() << "status_dbus_replied(): Refreshed status:" << new_status; // Enable (dis)connect button _main_gui->setConnectButtonEnabled(true); this->setStatus(new_status); if (this->isSessionAliveOrRunning(new_status)) { // Ask status every 1000 millisecond QTimer::singleShot(1000, this, &Session::statusTimerEvent); } } void Session::onCloseHandler() { // To cleanup things here this->stop_request_dbus(this->getId()); }