File indexing completed on 2025-01-05 03:53:45
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2018-07-08 0007 * Description : Base class for web service talkers. 0008 * 0009 * SPDX-FileCopyrightText: 2018 by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "wstalker.h" 0016 0017 // Qt includes 0018 0019 #include <QApplication> 0020 #include <QMessageBox> 0021 #include <QObject> 0022 #include <QDateTime> 0023 0024 // KDE includes 0025 0026 #include <klocalizedstring.h> 0027 0028 // Local includes 0029 0030 #include "digikam_debug.h" 0031 #include "o0globals.h" 0032 #include "wstoolutils.h" 0033 0034 using namespace Digikam; 0035 0036 namespace Digikam 0037 { 0038 0039 bool operator< (const WSAlbum& first, const WSAlbum& second) 0040 { 0041 return first.title < second.title; 0042 } 0043 0044 } // namespace Digikam 0045 0046 namespace DigikamGenericUnifiedPlugin 0047 { 0048 0049 WSTalker::WSTalker(QWidget* const parent) 0050 : QObject(parent), 0051 m_netMngr(new QNetworkAccessManager(this)), 0052 m_reply(0), 0053 m_state(WSTalker::DEFAULT), 0054 m_settings(0), 0055 m_store(0), 0056 m_userName(QString()), 0057 m_wizard(0) 0058 { 0059 m_wizard = dynamic_cast<WSWizard*>(parent); 0060 0061 if (m_wizard != nullptr) 0062 { 0063 m_settings = m_wizard->oauthSettings(); 0064 m_store = m_wizard->oauthSettingsStore(); 0065 m_userName = m_wizard->settings()->userName; 0066 0067 connect(this, SIGNAL(signalBusy(bool)), 0068 m_wizard, SLOT(slotBusy(bool))); 0069 } 0070 else 0071 { 0072 m_settings = WSToolUtils::getOauthSettings(parent); 0073 m_store = new O0SettingsStore(m_settings, QLatin1String(O2_ENCRYPTION_KEY), this); 0074 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Parent of talker is not an instance of WSWizard"; 0075 } 0076 0077 connect(m_netMngr, SIGNAL(finished(QNetworkReply*)), 0078 this, SLOT(slotFinished(QNetworkReply*))); 0079 } 0080 0081 WSTalker::~WSTalker() 0082 { 0083 if (m_reply) 0084 { 0085 m_reply->abort(); 0086 } 0087 0088 delete m_reply; 0089 delete m_netMngr; 0090 0091 /* Verify if m_settings is initialized by wstalker constructor or it is already initialized in wizard. 0092 * if not by wizard, we have to delete it. 0093 */ 0094 if (!m_wizard) 0095 { 0096 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "delete m_settings"; 0097 delete m_settings; 0098 delete m_store; 0099 } 0100 } 0101 0102 void WSTalker::cancel() 0103 { 0104 if (m_reply) 0105 { 0106 m_reply->abort(); 0107 m_reply = 0; 0108 } 0109 0110 Q_EMIT signalBusy(false); 0111 } 0112 0113 QString WSTalker::getUserID(const QString& userName) 0114 { 0115 QString userID; 0116 0117 if (!userName.isEmpty()) 0118 { 0119 m_settings->beginGroup(m_store->groupKey()); 0120 m_settings->beginGroup(QLatin1String("users")); 0121 userID = m_settings->value(userName).toString(); 0122 m_settings->endGroup(); 0123 m_settings->endGroup(); 0124 } 0125 0126 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "ID of user " << userName << " : " << userID; 0127 return userID; 0128 } 0129 0130 void WSTalker::link() 0131 { 0132 } 0133 0134 void WSTalker::unlink() 0135 { 0136 } 0137 0138 bool WSTalker::linked() const 0139 { 0140 return false; 0141 } 0142 0143 void WSTalker::authenticate() 0144 { 0145 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "username chosen: " << m_userName; 0146 bool authenticateValide = loadUserAccount(m_userName); 0147 0148 /* If user account already exists and doesn't expire yet (authenticateValide == true), linking to his account 0149 * Otherwise, unlink() current account and link to new account 0150 */ 0151 if (authenticateValide) 0152 { 0153 link(); 0154 } 0155 else 0156 { 0157 reauthenticate(); 0158 } 0159 } 0160 0161 void WSTalker::reauthenticate() 0162 { 0163 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "reauthenticate"; 0164 0165 unlink(); 0166 0167 // Wait until user account is unlinked completely 0168 while(linked()); 0169 0170 link(); 0171 } 0172 0173 QMap<QString, QVariant> WSTalker::getUserAccountInfo(const QString& userName) 0174 { 0175 QString userID = getUserID(userName); 0176 0177 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "getUserAccountInfo with userID: " << userID; 0178 0179 QMap<QString, QVariant> map; 0180 0181 if (userID.isEmpty()) 0182 { 0183 return map; 0184 } 0185 0186 m_settings->beginGroup(m_store->groupKey()); 0187 m_settings->beginGroup(userID); 0188 QStringList keys = m_settings->allKeys(); 0189 0190 Q_FOREACH (const QString& key, keys) 0191 { 0192 QVariant value = m_settings->value(key); 0193 map.insert(key, value); 0194 } 0195 0196 m_settings->endGroup(); 0197 m_settings->endGroup(); 0198 0199 return map; 0200 } 0201 0202 void WSTalker::saveUserAccount(const QString& userName, 0203 const QString& userID, 0204 long long int expire, 0205 const QString& accessToken, 0206 const QString& refreshToken) 0207 { 0208 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "saveUserAccount with username : " << userName << ", userID: " << userID; 0209 0210 if (userID.isEmpty()) 0211 { 0212 return; 0213 } 0214 0215 m_settings->beginGroup(m_store->groupKey()); 0216 0217 m_settings->beginGroup(QLatin1String("users")); 0218 m_settings->setValue(userName, userID); 0219 m_settings->endGroup(); 0220 0221 m_settings->beginGroup(userID); 0222 m_settings->setValue(QLatin1String("expiration_time"), expire); 0223 m_settings->setValue(QLatin1String("access_token"), accessToken); 0224 m_settings->setValue(QLatin1String("refresh_token"), refreshToken); 0225 m_settings->endGroup(); 0226 0227 m_settings->endGroup(); 0228 0229 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "current " << QDateTime::currentMSecsSinceEpoch() / 1000; 0230 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "expire " << expire; 0231 } 0232 0233 bool WSTalker::loadUserAccount(const QString& userName) 0234 { 0235 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "loadUserAccount with user name : " << userName; 0236 0237 /* User logins using new account, return false so that we can unlink() current account, 0238 * before link() to new account 0239 */ 0240 if (userName.isEmpty()) 0241 { 0242 return false; 0243 } 0244 0245 QMap<QString, QVariant> map = getUserAccountInfo(userName); 0246 0247 /* 0248 * if getUserAccountInfo(userName) return empty with a non empty userName, there must 0249 * be some kind of errors. So, the condition below is a security check, which assures 0250 * user to relogin anyway. 0251 * 0252 * However, if it happens, INSPECTATION is obligated!!! 0253 */ 0254 if (map.isEmpty()) 0255 { 0256 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "WARNING: Something strange happens with getUserAccountInfo"; 0257 return false; 0258 } 0259 0260 QString expire = map[QLatin1String("expiration_time")].toString(); 0261 QString accessToken = map[QLatin1String("access_token")].toString(); 0262 QString refreshToken = map[QLatin1String("refresh_token")].toString(); 0263 0264 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "expired moment : " << expire; 0265 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "current time : " << QDateTime::currentMSecsSinceEpoch() / 1000; 0266 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "access_token: " << accessToken; 0267 0268 /* If access token is not expired yet, retrieve all tokens and return true so that we can link() 0269 * directly to new account. 0270 * Otherwise, return false so that user can relogin. 0271 */ 0272 0273 if (expire.toLongLong() > QDateTime::currentMSecsSinceEpoch() / 1000) 0274 { 0275 resetTalker(expire, accessToken, refreshToken); 0276 return true; 0277 } 0278 0279 return false; 0280 0281 } 0282 0283 void WSTalker::resetTalker(const QString& /*expire*/, const QString& /*accessToken*/, const QString& /*refreshToken*/) 0284 { 0285 } 0286 0287 void WSTalker::getLoggedInUser() 0288 { 0289 } 0290 0291 void WSTalker::listAlbums(long long /*userID*/) 0292 { 0293 } 0294 0295 void WSTalker::createNewAlbum() 0296 { 0297 } 0298 0299 void WSTalker::addPhoto(const QString& /*imgPath*/, const QString& /*albumID*/, const QString& /*caption*/) 0300 { 0301 } 0302 0303 void WSTalker::removeUserAccount(const QString& userName) 0304 { 0305 QString userID = getUserID(userName); 0306 0307 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "removeUserAccount with userID: " << userID; 0308 0309 if (userID.isEmpty()) 0310 { 0311 return; 0312 } 0313 0314 m_settings->beginGroup(m_store->groupKey()); 0315 0316 m_settings->beginGroup(userID); 0317 m_settings->remove(QString()); 0318 m_settings->endGroup(); 0319 0320 m_settings->beginGroup(QLatin1String("users")); 0321 m_settings->remove(userName); 0322 m_settings->endGroup(); 0323 0324 m_settings->endGroup(); 0325 } 0326 0327 void WSTalker::removeAllAccounts() 0328 { 0329 m_settings->beginGroup(m_store->groupKey()); 0330 m_settings->remove(QString()); 0331 m_settings->endGroup(); 0332 } 0333 0334 void WSTalker::sortAlbumsList(QList<WSAlbum>& albumsList) 0335 { 0336 std::sort(albumsList.begin(), albumsList.end()); 0337 } 0338 0339 /* 0340 * saveUserAccount(...) must be called inside this method when it is reimplemented 0341 * in derived class, because saveUserAccount(...) can be called only in derived class. 0342 */ 0343 void WSTalker::authenticationDone(int errCode, const QString& errMsg) 0344 { 0345 if (errCode != 0) 0346 { 0347 QMessageBox::critical(QApplication::activeWindow(), 0348 i18nc("@title:window", "Error"), 0349 i18n("Code: %1. %2", errCode, errMsg)); 0350 } 0351 0352 Q_EMIT signalBusy(false); 0353 } 0354 0355 void WSTalker::parseResponseGetLoggedInUser(const QByteArray& /*data*/) 0356 { 0357 } 0358 0359 void WSTalker::parseResponseListAlbums(const QByteArray& /*data*/) 0360 { 0361 } 0362 0363 void WSTalker::parseResponseCreateAlbum(const QByteArray& /*data*/) 0364 { 0365 } 0366 0367 void WSTalker::parseResponseAddPhoto(const QByteArray& /*data*/) 0368 { 0369 } 0370 0371 void WSTalker::slotFinished(QNetworkReply* reply) 0372 { 0373 if (reply != m_reply) 0374 { 0375 return; 0376 } 0377 0378 m_reply = 0; 0379 0380 if (reply->error() != QNetworkReply::NoError) 0381 { 0382 qCDebug(DIGIKAM_WEBSERVICES_LOG) << reply->error() << " text :"<< QLatin1String(reply->readAll()); 0383 authenticationDone(reply->error(), reply->errorString()); 0384 reply->deleteLater(); 0385 return; 0386 } 0387 0388 QByteArray buffer = reply->readAll(); 0389 0390 switch (m_state) 0391 { 0392 case WSTalker::GETUSER : 0393 parseResponseGetLoggedInUser(buffer); 0394 authenticationDone(0, QLatin1String("")); 0395 break; 0396 case WSTalker::LISTALBUMS : 0397 parseResponseListAlbums(buffer); 0398 break; 0399 case WSTalker::CREATEALBUM : 0400 parseResponseCreateAlbum(buffer); 0401 break; 0402 case WSTalker::ADDPHOTO : 0403 parseResponseAddPhoto(buffer); 0404 break; 0405 case WSTalker::DEFAULT : 0406 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "slotFinished at state = default"; 0407 break; 0408 } 0409 0410 reply->deleteLater(); 0411 } 0412 0413 void WSTalker::slotOpenBrowser(const QUrl& url) 0414 { 0415 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Open Browser..."; 0416 Q_EMIT signalOpenBrowser(url); 0417 } 0418 0419 void WSTalker::slotCloseBrowser() 0420 { 0421 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Close Browser..."; 0422 Q_EMIT signalCloseBrowser(); 0423 } 0424 0425 void WSTalker::slotLinkingFailed() 0426 { 0427 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK fail"; 0428 authenticationDone(-1, QLatin1String("Account link failed.")); 0429 0430 Q_EMIT signalBusy(false); 0431 Q_EMIT signalAuthenticationComplete(linked()); 0432 } 0433 0434 void WSTalker::slotLinkingSucceeded() 0435 { 0436 if (!linked()) 0437 { 0438 Q_EMIT signalBusy(false); 0439 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "UNLINK ok"; 0440 0441 return; 0442 } 0443 0444 // Get user account information 0445 getLoggedInUser(); 0446 0447 Q_EMIT signalAuthenticationComplete(linked()); 0448 qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK ok"; 0449 } 0450 0451 void WSTalker::slotResponseTokenReceived(const QMap<QString, QString>& /*rep*/) 0452 { 0453 } 0454 0455 } // namespace DigikamGenericUnifiedPlugin 0456 0457 #include "moc_wstalker.cpp"