File indexing completed on 2025-01-05 03:53:30

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2015-06-21
0007  * Description : a tool to export items to Google web services
0008  *
0009  * SPDX-FileCopyrightText: 2015      by Shourya Singh Gupta <shouryasgupta at gmail dot com>
0010  * SPDX-FileCopyrightText: 2015-2020 by Caulier Gilles <caulier dot gilles at gmail dot com>
0011  * SPDX-FileCopyrightText: 2018      by Thanh Trung Dinh <dinhthanhtrung1996 at gmail dot com>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "gstalkerbase.h"
0018 
0019 // Qt includes
0020 
0021 #include <QMap>
0022 #include <QMultiMap>
0023 #include <QDateTime>
0024 #include <QDesktopServices>
0025 #include <QOAuthHttpServerReplyHandler>
0026 
0027 // Local includes
0028 
0029 #include "digikam_debug.h"
0030 #include "networkmanager.h"
0031 #include "wstoolutils.h"
0032 
0033 using namespace Digikam;
0034 
0035 namespace DigikamGenericGoogleServicesPlugin
0036 {
0037 
0038 class Q_DECL_HIDDEN GSTalkerBase::Private
0039 {
0040 public:
0041 
0042     explicit Private()
0043       : linked    (false),
0044         authUrl   (QLatin1String("https://accounts.google.com/o/oauth2/auth")),
0045         tokenUrl  (QLatin1String("https://accounts.google.com/o/oauth2/token")),
0046         identity  (QLatin1String("258540448336-hgdegpohibcjasvk1p595fpvjor15pbc.apps.googleusercontent.com")),
0047         sharedKey (QLatin1String("iiIKTNM4ggBXiTdquAzbs2xw")),
0048         netMngr   (nullptr)
0049     {
0050     }
0051 
0052     bool                   linked;
0053 
0054     QString                authUrl;
0055     QString                tokenUrl;
0056     QString                identity;
0057     QString                sharedKey;
0058 
0059     QNetworkAccessManager* netMngr;
0060 };
0061 
0062 GSTalkerBase::GSTalkerBase(QObject* const parent, const QStringList& scope, const QString& serviceName)
0063     : QObject      (parent),
0064       m_scope      (scope),
0065       m_serviceName(serviceName),
0066       m_reply      (nullptr),
0067       m_service    (nullptr),
0068       d            (new Private)
0069 {
0070     d->netMngr = NetworkManager::instance()->getNetworkManager(this);
0071     m_service  = new QOAuth2AuthorizationCodeFlow(d->netMngr, this);
0072 
0073     m_service->setContentType(QAbstractOAuth::ContentType::Json);
0074     m_service->setClientIdentifierSharedKey(d->sharedKey);
0075     m_service->setScope(m_scope.join(QLatin1String(" ")));
0076     m_service->setAuthorizationUrl(QUrl(d->authUrl));
0077     m_service->setAccessTokenUrl(QUrl(d->tokenUrl));
0078     m_service->setClientIdentifier(d->identity);
0079 
0080 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
0081 
0082     m_service->setModifyParametersFunction([](QAbstractOAuth::Stage stage, QVariantMap* parameters)
0083         {
0084             if (stage == QAbstractOAuth::Stage::RequestingAccessToken)
0085             {
0086                 QByteArray code = parameters->value(QLatin1String("code")).toByteArray();
0087                 (*parameters)[QLatin1String("code")] = QUrl::fromPercentEncoding(code);
0088             }
0089         }
0090     );
0091 
0092 #else
0093 
0094     m_service->setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* parameters)
0095         {
0096             if (stage == QAbstractOAuth::Stage::RequestingAccessToken)
0097             {
0098                 QByteArray code = parameters->value(QLatin1String("code")).toByteArray();
0099                 (*parameters).replace(QLatin1String("code"), QUrl::fromPercentEncoding(code));
0100             }
0101         }
0102     );
0103 
0104 #endif
0105 
0106     QOAuthHttpServerReplyHandler* const replyHandler = new QOAuthHttpServerReplyHandler(8000, this);
0107     m_service->setReplyHandler(replyHandler);
0108 
0109     // OAuth configuration saved to between dk sessions
0110 
0111     m_service->setRefreshToken(WSToolUtils::readToken(m_serviceName));
0112 
0113     connect(m_service, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser,
0114             this, &GSTalkerBase::slotOpenBrowser);
0115 
0116     connect(m_service, &QOAuth2AuthorizationCodeFlow::tokenChanged,
0117             this, &GSTalkerBase::slotTokenChanged);
0118 
0119     connect(m_service, &QOAuth2AuthorizationCodeFlow::granted,
0120             this, &GSTalkerBase::slotLinkingSucceeded);
0121 
0122     connect(m_service, &QOAuth2AuthorizationCodeFlow::error,
0123             this, &GSTalkerBase::slotLinkingFailed);
0124 }
0125 
0126 GSTalkerBase::~GSTalkerBase()
0127 {
0128     if (m_reply)
0129     {
0130         m_reply->abort();
0131     }
0132 
0133     delete d;
0134 }
0135 
0136 void GSTalkerBase::link()
0137 {
0138     Q_EMIT signalBusy(true);
0139     m_service->grant();
0140 }
0141 
0142 void GSTalkerBase::unlink()
0143 {
0144     Q_EMIT signalBusy(true);
0145 
0146     d->linked = false;
0147 
0148     m_service->setToken(QString());
0149     m_service->setRefreshToken(QString());
0150 
0151     WSToolUtils::clearToken(m_serviceName);
0152 
0153     m_bearerAccessToken.clear();
0154     m_accessToken.clear();
0155 }
0156 
0157 void GSTalkerBase::slotLinkingFailed()
0158 {
0159     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to" << m_serviceName << "fail";
0160 
0161     d->linked = false;
0162 
0163     Q_EMIT signalBusy(false);
0164     Q_EMIT signalAuthenticationRefused();
0165 }
0166 
0167 void GSTalkerBase::slotLinkingSucceeded()
0168 {
0169     if (m_service->status() == QAbstractOAuth::Status::Granted)
0170     {
0171         qCDebug(DIGIKAM_WEBSERVICES_LOG) << "LINK to" << m_serviceName << "ok";
0172 
0173         d->linked = true;
0174 
0175         Q_EMIT signalAccessTokenObtained();
0176     }
0177 }
0178 
0179 void GSTalkerBase::slotOpenBrowser(const QUrl& url)
0180 {
0181     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "Open Browser... (" << url << ")";
0182 
0183     QDesktopServices::openUrl(url);
0184 }
0185 
0186 bool GSTalkerBase::authenticated() const
0187 {
0188     return d->linked;
0189 }
0190 
0191 void GSTalkerBase::slotTokenChanged(const QString& token)
0192 {
0193     m_accessToken       = token;
0194     m_bearerAccessToken = QLatin1String("Bearer ") + m_accessToken;
0195 
0196     WSToolUtils::saveToken(m_serviceName, m_service->refreshToken());
0197 }
0198 
0199 void GSTalkerBase::doOAuth()
0200 {
0201     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "current time" << QDateTime::currentDateTime();
0202     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "expires at: " << m_service->expirationAt();
0203 
0204     if (!m_service->refreshToken().isEmpty())
0205     {
0206         m_service->refreshAccessToken();
0207     }
0208     else
0209     {
0210         link();
0211     }
0212 }
0213 
0214 } // namespace DigikamGenericGoogleServicesPlugin
0215 
0216 #include "moc_gstalkerbase.cpp"