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 }