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"