File indexing completed on 2024-04-21 14:45:02

0001 /*
0002     SPDX-FileCopyrightText: 2018 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "Options.h"
0008 
0009 #include "ekosliveclient.h"
0010 #include "ekos/manager.h"
0011 
0012 #include "kspaths.h"
0013 #include "Options.h"
0014 #include "ekos_debug.h"
0015 #include "QProgressIndicator.h"
0016 
0017 #include <config-kstars.h>
0018 
0019 #ifdef HAVE_KEYCHAIN
0020 #include <qt5keychain/keychain.h>
0021 #endif
0022 
0023 #include <QJsonDocument>
0024 #include <QJsonObject>
0025 #include <QNetworkAccessManager>
0026 #include <QNetworkReply>
0027 
0028 namespace EkosLive
0029 {
0030 Client::Client(Ekos::Manager *manager) : QDialog(manager), m_Manager(manager)
0031 {
0032     setupUi(this);
0033 
0034     connect(closeB, &QPushButton::clicked, this, &Client::close);
0035 
0036     QPixmap im;
0037     if (im.load(KSPaths::locate(QStandardPaths::AppLocalDataLocation, "ekoslive.png")))
0038         leftBanner->setPixmap(im);
0039 
0040     pi = new QProgressIndicator(this);
0041     bottomLayout->insertWidget(1, pi);
0042 
0043     connectionState->setPixmap(QIcon::fromTheme("state-offline").pixmap(QSize(64, 64)));
0044 
0045     username->setText(Options::ekosLiveUsername());
0046     connect(username, &QLineEdit::editingFinished, this, [this]()
0047     {
0048         Options::setEkosLiveUsername(username->text());
0049     });
0050 
0051     // Initialize node managers
0052     QSharedPointer<NodeManager> onlineManager(new NodeManager(NodeManager::Message | NodeManager::Media | NodeManager::Cloud));
0053     connect(onlineManager.get(), &NodeManager::authenticationError, this, [this](const QString & message)
0054     {
0055         onlineLabel->setToolTip(message);
0056     });
0057 
0058     QSharedPointer<NodeManager> offlineManager(new NodeManager(NodeManager::Message | NodeManager::Media));
0059 
0060     connect(offlineManager.get(), &NodeManager::authenticationError, this, [this](const QString & message)
0061     {
0062         offlineLabel->setToolTip(message);
0063     });
0064 
0065     m_NodeManagers.append(std::move(onlineManager));
0066     m_NodeManagers.append(std::move(offlineManager));
0067     syncURLs();
0068 
0069     connect(selectServersB, &QPushButton::clicked, this, &Client::showSelectServersDialog);
0070     connect(connectB, &QPushButton::clicked, this, [this]()
0071     {
0072         if (m_isConnected)
0073         {
0074             for (auto &oneManager : m_NodeManagers)
0075                 oneManager->disconnectNodes();
0076         }
0077         else
0078         {
0079             for (auto &oneManager : m_NodeManagers)
0080             {
0081                 oneManager->setCredentials(username->text(), password->text());
0082                 oneManager->authenticate();
0083             }
0084         }
0085     });
0086 
0087     connect(password, &QLineEdit::returnPressed, this, [this]()
0088     {
0089         if (!m_isConnected)
0090         {
0091             for (auto &oneManager : m_NodeManagers)
0092             {
0093                 oneManager->setCredentials(username->text(), password->text());
0094                 oneManager->authenticate();
0095             }
0096         }
0097     });
0098 
0099     rememberCredentialsCheck->setChecked(Options::rememberCredentials());
0100     connect(rememberCredentialsCheck, &QCheckBox::toggled, [ = ](bool toggled)
0101     {
0102         Options::setRememberCredentials(toggled);
0103     });
0104     autoStartCheck->setChecked(Options::autoStartEkosLive());
0105     connect(autoStartCheck, &QCheckBox::toggled, [ = ](bool toggled)
0106     {
0107         Options::setAutoStartEkosLive(toggled);
0108     });
0109 
0110 #ifdef HAVE_KEYCHAIN
0111     QKeychain::ReadPasswordJob *job = new QKeychain::ReadPasswordJob(QLatin1String("kstars"));
0112     job->setAutoDelete(false);
0113     job->setKey(QLatin1String("ekoslive"));
0114     connect(job, &QKeychain::Job::finished, this, [&](QKeychain::Job * job)
0115     {
0116         if (job->error() == false)
0117         {
0118             const auto passwordText = dynamic_cast<QKeychain::ReadPasswordJob*>(job)->textData().toLatin1();
0119 
0120             // Only set and attempt connection if the data is not empty
0121             if (passwordText.isEmpty() == false && username->text().isEmpty() == false)
0122             {
0123                 password->setText(passwordText);
0124                 if (autoStartCheck->isChecked())
0125                 {
0126                     for (auto &oneManager : m_NodeManagers)
0127                     {
0128                         oneManager->setCredentials(username->text(), password->text());
0129                         oneManager->authenticate();
0130                     }
0131                 }
0132             }
0133         }
0134         else
0135         {
0136             if (autoStartCheck->isChecked())
0137                 qCWarning(KSTARS_EKOS) << "Failed to automatically connect due to missing EkosLive credentials:" << job->errorString();
0138         }
0139         job->deleteLater();
0140     });
0141     job->start();
0142 #endif
0143 
0144     m_Message = new Message(m_Manager, m_NodeManagers);
0145     connect(m_Message, &Message::connected, this, &Client::onConnected);
0146     connect(m_Message, &Message::disconnected, this, &Client::onDisconnected);
0147     connect(m_Message, &Message::expired, this, [&](const QUrl & url)
0148     {
0149         // If token expired, disconnect and reconnect again.
0150         for (auto &oneManager : m_NodeManagers)
0151         {
0152             if (oneManager->property("websocketURL").toUrl() == url)
0153             {
0154                 oneManager->disconnectNodes();
0155                 oneManager->setCredentials(username->text(), password->text());
0156                 oneManager->authenticate();
0157             }
0158         }
0159     });
0160 
0161     m_Media = new Media(m_Manager, m_NodeManagers);
0162     connect(m_Media, &Media::connected, this, &Client::onConnected);
0163     m_Cloud = new Cloud(m_Manager, m_NodeManagers);
0164     connect(m_Cloud, &Cloud::connected, this, &Client::onConnected);
0165 }
0166 
0167 Client::~Client()
0168 {
0169     for (auto &oneManager : m_NodeManagers)
0170         oneManager->disconnectNodes();
0171 }
0172 
0173 void Client::onConnected()
0174 {
0175     pi->stopAnimation();
0176 
0177     m_isConnected = true;
0178 
0179     connectB->setText(i18n("Disconnect"));
0180     connectionState->setPixmap(QIcon::fromTheme("state-ok").pixmap(QSize(64, 64)));
0181 
0182     auto disconnected = QIcon(":/icons/AlignFailure.svg").pixmap(QSize(32, 32));
0183     auto connected = QIcon(":/icons/AlignSuccess.svg").pixmap(QSize(32, 32));
0184 
0185     onlineLabel->setStyleSheet(m_NodeManagers[0]->isConnected() ? "color:white" : "color:gray");
0186     onlineIcon->setPixmap(m_NodeManagers[0]->isConnected() ? connected : disconnected);
0187     if (m_NodeManagers[0]->isConnected())
0188         onlineLabel->setToolTip(QString());
0189 
0190     selectServersB->setEnabled(false);
0191     offlineLabel->setStyleSheet(m_NodeManagers[1]->isConnected() ? "color:white" : "color:gray");
0192     offlineIcon->setPixmap(m_NodeManagers[1]->isConnected() ? connected : disconnected);
0193     if (m_NodeManagers[1]->isConnected())
0194         offlineLabel->setToolTip(QString());
0195 
0196     if (rememberCredentialsCheck->isChecked())
0197     {
0198 #ifdef HAVE_KEYCHAIN
0199         QKeychain::WritePasswordJob *job = new QKeychain::WritePasswordJob(QLatin1String("kstars"));
0200         job->setAutoDelete(true);
0201         job->setKey(QLatin1String("ekoslive"));
0202         job->setTextData(password->text());
0203         job->start();
0204 #endif
0205     }
0206 }
0207 
0208 void Client::onDisconnected()
0209 {
0210     connectionState->setPixmap(QIcon::fromTheme("state-offline").pixmap(QSize(64, 64)));
0211     m_isConnected = false;
0212     connectB->setText(i18n("Connect"));
0213 
0214     auto disconnected = QIcon(":/icons/AlignFailure.svg").pixmap(QSize(32, 32));
0215     auto connected = QIcon(":/icons/AlignSuccess.svg").pixmap(QSize(32, 32));
0216 
0217     onlineLabel->setStyleSheet(m_NodeManagers[0]->isConnected() ? "color:white" : "color:gray");
0218     onlineIcon->setPixmap(m_NodeManagers[0]->isConnected() ? connected : disconnected);
0219 
0220     offlineLabel->setStyleSheet(m_NodeManagers[1]->isConnected() ? "color:white" : "color:gray");
0221     offlineIcon->setPixmap(m_NodeManagers[1]->isConnected() ? connected : disconnected);
0222 
0223     selectServersB->setEnabled(true);
0224 }
0225 
0226 void Client::setConnected(bool enabled)
0227 {
0228     // Return if there is no change.
0229     if (enabled == m_isConnected)
0230         return;
0231 
0232     connectB->click();
0233 }
0234 
0235 void Client::setConfig(bool rememberCredentials, bool autoConnect)
0236 {
0237     rememberCredentialsCheck->setChecked(rememberCredentials);
0238     autoStartCheck->setChecked(autoConnect);
0239 }
0240 
0241 void Client::setUser(const QString &user, const QString &pass)
0242 {
0243     username->setText(user);
0244     Options::setEkosLiveUsername(user);
0245 
0246     password->setText(pass);
0247 }
0248 
0249 void Client::showSelectServersDialog()
0250 {
0251     QDialog dialog(this);
0252     dialog.setMinimumWidth(300);
0253     dialog.setWindowTitle(i18nc("@title:window", "Select EkosLive Servers"));
0254 
0255     QLabel offline(i18n("Offline:"));
0256     QLabel online(i18n("Online:"));
0257 
0258     QLineEdit offlineEdit(&dialog);
0259     QLineEdit onlineEdit(&dialog);
0260     offlineEdit.setText(Options::ekosLiveOfflineServer());
0261     onlineEdit.setText(Options::ekosLiveOnlineServer());
0262 
0263     QFormLayout * layout = new QFormLayout;
0264     layout->addRow(&offline, &offlineEdit);
0265     layout->addRow(&online, &onlineEdit);
0266     dialog.setLayout(layout);
0267     dialog.exec();
0268 
0269     Options::setEkosLiveOfflineServer(offlineEdit.text());
0270     Options::setEkosLiveOnlineServer(onlineEdit.text());
0271 
0272     syncURLs();
0273 }
0274 
0275 void Client::syncURLs()
0276 {
0277     auto onlineSSL = QUrl(Options::ekosLiveOnlineServer()).scheme() == "https";
0278     auto onlineURL = QUrl(Options::ekosLiveOnlineServer()).url(QUrl::RemoveScheme);
0279     if (onlineSSL)
0280         m_NodeManagers[Online]->setURLs(QUrl("https:" + onlineURL), QUrl("wss:" + onlineURL));
0281     else
0282         m_NodeManagers[Online]->setURLs(QUrl("http:" + onlineURL), QUrl("ws:" + onlineURL));
0283 
0284     auto offlineSSL = QUrl(Options::ekosLiveOfflineServer()).scheme() == "https";
0285     auto offlineURL = QUrl(Options::ekosLiveOfflineServer()).url(QUrl::RemoveScheme);
0286     if (offlineSSL)
0287         m_NodeManagers[Offline]->setURLs(QUrl("https:" + offlineURL), QUrl("wss:" + offlineURL));
0288     else
0289         m_NodeManagers[Offline]->setURLs(QUrl("http:" + offlineURL), QUrl("ws:" + offlineURL));
0290 }
0291 }