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-02
0007  * Description : embedded web browser for web service authentication
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 "wsauthenticationpage.h"
0016 
0017 // Qt includes
0018 
0019 #include <QFile>
0020 #include <QFileInfo>
0021 #include <QTimer>
0022 #include <QWidget>
0023 #include <QApplication>
0024 #include <QStandardPaths>
0025 #include <QLabel>
0026 #include <QStringList>
0027 
0028 // KDE includes
0029 
0030 #include <klocalizedstring.h>
0031 
0032 // Local includes
0033 
0034 #include "digikam_debug.h"
0035 #include "digikam_version.h"
0036 #include "dlayoutbox.h"
0037 #include "wswizard.h"
0038 #include "wssettings.h"
0039 
0040 namespace DigikamGenericUnifiedPlugin
0041 {
0042 
0043 #ifdef HAVE_QWEBENGINE
0044 
0045 WSAuthenticationPage::WSAuthenticationPage(QObject* const parent, QWebEngineProfile* profile, const QString& callbackUrl)
0046     : QWebEnginePage(profile, parent),
0047       m_callbackUrl(callbackUrl)
0048 {
0049 }
0050 
0051 #else
0052 
0053 WSAuthenticationPage::WSAuthenticationPage(QObject* const parent, const QString& callbackUrl)
0054     : QWebPage(parent),
0055       m_callbackUrl(callbackUrl)
0056 {
0057     connect(mainFrame(), SIGNAL(urlChanged(QUrl)),
0058             this, SLOT(slotUrlChanged(QUrl)));
0059 }
0060 
0061 #endif // #ifdef HAVE_QWEBENGINE
0062 
0063 WSAuthenticationPage::~WSAuthenticationPage()
0064 {
0065 }
0066 
0067 void WSAuthenticationPage::setCallbackUrl(const QString& url)
0068 {
0069     m_callbackUrl = url;
0070 }
0071 
0072 #ifdef HAVE_QWEBENGINE
0073 
0074 bool WSAuthenticationPage::acceptNavigationRequest(const QUrl& url, QWebEnginePage::NavigationType /*type*/, bool /*isMainFrame*/)
0075 
0076 #else
0077 
0078 bool WSAuthenticationPage::slotUrlChanged(const QUrl& url)
0079 
0080 #endif // #ifdef HAVE_QWEBENGINE
0081 
0082 {
0083     QString urlString = url.toString();
0084     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "urlString: " << urlString;
0085 
0086     /*
0087      * Condition to verify that the url loaded on page is the one containing access token
0088      */
0089     if (m_callbackUrl.length() > 0                   &&
0090         urlString.length() >= m_callbackUrl.length() &&
0091         urlString.left(m_callbackUrl.length()) == m_callbackUrl)
0092     {
0093         Q_EMIT callbackCatched(urlString);
0094         return false;
0095     }
0096 
0097     return true;
0098 }
0099 
0100 // ----------------------------------------------------------------------------
0101 
0102 #ifdef HAVE_QWEBENGINE
0103 
0104 WSAuthenticationPageView::WSAuthenticationPageView(QWidget* const parent,
0105                                                    WSAuthentication* const wsAuth,
0106                                                    const QString& callbackUrl)
0107     : QWebEngineView(parent),
0108       m_WSAuthentication(wsAuth)
0109 
0110 #else
0111 
0112 WSAuthenticationPageView::WSAuthenticationPageView(QWidget* const parent,
0113                                                    WSAuthentication* const wsAuth,
0114                                                    const QString& callbackUrl)
0115     : QWebView(parent),
0116       m_WSAuthentication(wsAuth)
0117 
0118 #endif // #ifdef HAVE_QWEBENGINE
0119 
0120 {
0121     adjustSize();
0122     setMinimumSize(QSize(850, 800));
0123 
0124 #ifdef HAVE_QWEBENGINE
0125 
0126     WSAuthenticationPage* const wpage = new WSAuthenticationPage(this, new QWebEngineProfile, callbackUrl);
0127 
0128 #else
0129 
0130     WSAuthenticationPage* const wpage = new WSAuthenticationPage(this, callbackUrl);
0131 
0132 #endif // #ifdef HAVE_QWEBENGINE
0133 
0134     setPage(wpage);
0135 
0136     connect(wpage, SIGNAL(callbackCatched(QString)),
0137             this, SLOT(slotCallbackCatched(QString)));
0138 
0139     connect(m_WSAuthentication, SIGNAL(signalOpenBrowser(QUrl)),
0140             this, SLOT(slotOpenBrowser(QUrl)));
0141 
0142     connect(m_WSAuthentication, SIGNAL(signalCloseBrowser()),
0143             this, SLOT(slotCloseBrowser()));
0144 
0145     /*
0146      * Here we hide the web browser immediately after creation.
0147      * If user has to login, we will show the browser again. Otherwise,
0148      * we will keep it hiding to improve page's looking.
0149      */
0150     hide();
0151 }
0152 
0153 WSAuthenticationPageView::~WSAuthenticationPageView()
0154 {
0155 }
0156 
0157 bool WSAuthenticationPageView::authenticationComplete() const
0158 {
0159     return m_WSAuthentication->authenticated();
0160 }
0161 
0162 QMap<QString, QString> WSAuthenticationPageView::parseUrlFragment(const QString& urlFragment)
0163 {
0164     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "parseUrlFragment: " << urlFragment;
0165 
0166     QMap<QString, QString> result;
0167     QStringList listArgs = urlFragment.split(QLatin1Char('&'));
0168 
0169     Q_FOREACH (const QString& arg, listArgs)
0170     {
0171         QStringList pair = arg.split(QLatin1Char('='));
0172         result.insert(pair.first(), pair.last());
0173     }
0174 
0175     return result;
0176 }
0177 
0178 void WSAuthenticationPageView::slotOpenBrowser(const QUrl& url)
0179 {
0180     WSAuthenticationPage* const page = dynamic_cast<WSAuthenticationPage*>(this->page());
0181 
0182 #ifdef HAVE_QWEBENGINE
0183 
0184     page->setUrl(url);
0185 
0186 #else
0187 
0188     page->mainFrame()->setUrl(url);
0189 
0190 #endif
0191 
0192     /*
0193      * Here we show the web browser again after creation, because when this slot is triggered,
0194      * user has to login. Therefore the login page has to be shown.
0195      */
0196     show();
0197 }
0198 
0199 void WSAuthenticationPageView::slotCloseBrowser()
0200 {
0201     close();
0202 }
0203 
0204 void WSAuthenticationPageView::slotCallbackCatched(const QString& callbackUrl)
0205 {
0206     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "slotCallbackCatched url: " << callbackUrl;
0207 
0208     QUrl url(callbackUrl);
0209     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "url fragment: " << url.fragment();
0210 
0211     QMap<QString, QString> res = parseUrlFragment(url.fragment());
0212     Q_EMIT m_WSAuthentication->signalResponseTokenReceived(res);
0213 }
0214 
0215 // ----------------------------------------------------------------------------
0216 
0217 class Q_DECL_HIDDEN WSAuthenticationWizard::Private
0218 {
0219 public:
0220 
0221     explicit Private(QWizard* const dialog, const QString& callback)
0222       : wizard(0),
0223         iface(0),
0224         wsAuth(0),
0225         wsAuthView(0),
0226         vbox(0),
0227         text(0),
0228         callbackUrl(callback)
0229     {
0230         wizard = dynamic_cast<WSWizard*>(dialog);
0231 
0232         if (wizard)
0233         {
0234             iface = wizard->iface();
0235 
0236             if (wizard->settings()->webService == WSSettings::WebService::FACEBOOK)
0237             {
0238                 callbackUrl = QLatin1String("https://www.facebook.com/connect/login_success.html");
0239             }
0240         }
0241     }
0242 
0243     WSWizard*                   wizard;
0244     DInfoInterface*             iface;
0245     WSAuthentication*           wsAuth;
0246     WSAuthenticationPageView*   wsAuthView;
0247     DVBox*                      vbox;
0248     QLabel*                     text;
0249     QString                     callbackUrl;
0250 };
0251 
0252 WSAuthenticationWizard::WSAuthenticationWizard(QWizard* const dialog,
0253                                                const QString& title,
0254                                                const QString& callback)
0255     : DWizardPage(dialog, title),
0256       d(new Private(dialog, callback))
0257 {
0258     d->wsAuth  = d->wizard->wsAuth();
0259 
0260     connect(d->wsAuth, SIGNAL(signalAuthenticationComplete(bool)),
0261             this, SLOT(slotAuthenticationComplete(bool)));
0262 
0263     d->vbox    = new DVBox(this);
0264 
0265     d->text    = new QLabel(d->vbox);
0266     d->text->setWordWrap(true);
0267     d->text->setOpenExternalLinks(true);
0268 
0269     d->vbox->setStretchFactor(d->text, 1);
0270     d->vbox->setStretchFactor(d->wsAuthView, 4);
0271 
0272     setPageWidget(d->vbox);
0273 }
0274 
0275 WSAuthenticationWizard::~WSAuthenticationWizard()
0276 {
0277     delete d;
0278 }
0279 
0280 bool WSAuthenticationWizard::isComplete() const
0281 {
0282     return d->wsAuthView->authenticationComplete();
0283 }
0284 
0285 void WSAuthenticationWizard::initializePage()
0286 {
0287     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "initPage WSAuthenticationWizard";
0288 
0289     /*
0290      * Init WebView of WSAuthenticationWizard every time initializePage is called.
0291      *
0292      * This guarantees an appropriate authentication page, even when user goes back to
0293      * intro page and choose a different account or different web service.
0294      */
0295     d->text->hide();
0296     d->wsAuthView                                 = new WSAuthenticationPageView(d->vbox, d->wsAuth, d->callbackUrl);
0297     QMap<WSSettings::WebService, QString> wsNames = WSSettings::webServiceNames();
0298     WSSettings::WebService ws                     = d->wizard->settings()->webService;
0299     d->wsAuth->createTalker(ws, wsNames[ws]);
0300 
0301     d->wsAuth->authenticate();
0302 }
0303 
0304 bool WSAuthenticationWizard::validatePage()
0305 {
0306     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "validatePage";
0307     return true;
0308 }
0309 
0310 void WSAuthenticationWizard::cleanupPage()
0311 {
0312     qCDebug(DIGIKAM_WEBSERVICES_LOG) << "cleanupPage WSAuthenticationWizard";
0313     d->wsAuth->cancelTalker();
0314 
0315     delete d->wsAuthView;
0316     d->wsAuthView = 0;
0317 }
0318 
0319 void WSAuthenticationWizard::slotAuthenticationComplete(bool isLinked)
0320 {
0321     d->text->show();
0322 
0323     if (isLinked)
0324     {
0325         d->text->setText(i18n("<qt>"
0326                               "<p><h1><b>Authentication done!</b></h1></p>"
0327                               "<p><h3>Account linking succeeded!</h3></p>"
0328                               "</qt>"));
0329     }
0330     else
0331     {
0332         d->text->setText(i18n("<qt>"
0333                               "<p><h1><b>Authentication done!</b></h1></p>"
0334                               "<p><h3>Account linking failed!</h3></p>"
0335                               "</qt>"));
0336     }
0337 
0338     Q_EMIT completeChanged();
0339 }
0340 
0341 } // namespace DigikamGenericUnifiedPlugin
0342 
0343 #include "moc_wsauthenticationpage.cpp"