/* * Copyright 2012 Sebastian Kügler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * QOAuth example * http://blog.ayoy.net/2009/6/24/oauth * * Twitter OAuth Docs: * https://dev.twitter.com/docs/auth/oauth * * How does OAuth work diagram: * https://dev.twitter.com/sites/default/files/images_documentation/oauth_diagram.png * * */ #include #include #include #include #include #include #include #include #include #include #include "koauthwebhelper.h" #include namespace KOAuth { class KOAuthWebHelperPrivate { public: KOAuthWebHelperPrivate() { webView = 0; dialog = 0; busy = false; } QString user; QString password; QString serviceBaseUrl; QHash authorizeUrls; QWebView *webView; KDialog *dialog; bool busy; QTimer *timer; }; KOAuthWebHelper::KOAuthWebHelper(QObject* parent) : QObject(parent), d(new KOAuthWebHelperPrivate) { setObjectName(QLatin1String("QOAuthWebHelper")); d->timer = new QTimer(); d->timer->setInterval(3000); d->timer->setSingleShot(true); connect(d->timer, SIGNAL(timeout()), SLOT(showDialog())); } KOAuthWebHelper::~KOAuthWebHelper() { kDebug(); delete d; } void KOAuthWebHelper::setUser(const QString& user) { d->user = user; } void KOAuthWebHelper::setPassword(const QString& password) { d->password = password; } void KOAuthWebHelper::setServiceBaseUrl(const QString &url) { d->serviceBaseUrl = url; } void KOAuthWebHelper::showDialog() { if (d->dialog) { kDebug() << "Showing dialog for URL: " << d->webView->page()->mainFrame()->toHtml(); d->dialog->show(); } } void KOAuthWebHelper::authorizeApp(const QString &serviceBaseUrl, const QString &authorizeUrl, const QString &pageUrl) { Q_UNUSED(serviceBaseUrl); Q_UNUSED(authorizeUrl); if (d->serviceBaseUrl == "/" || d->serviceBaseUrl.isEmpty()) return; if (!d->webView) { d->dialog = new KDialog(); d->dialog->setCaption( "authorize application" ); d->dialog->setButtons( KDialog::Ok | KDialog::Cancel); //d->webView = new KWebView(d->dialog, KWebPage::KIOIntegration); d->webView = new QWebView(d->dialog); d->dialog->setMainWidget(d->webView); // d->dialog->show(); // remove connect(d->webView->page(), SIGNAL(loadFinished(bool)), SLOT(loadFinished())); } // Set an empty cookiejar, we don't want to recycle already logged in users QNetworkAccessManager *qnam = d->webView->page()->networkAccessManager(); QNetworkCookieJar *jar = new QNetworkCookieJar(this); qnam->setCookieJar(jar); d->authorizeUrls[authorizeUrl] = d->serviceBaseUrl; d->webView->page()->mainFrame()->load(pageUrl); } bool KOAuthWebHelper::isIdentica() { return d->serviceBaseUrl.toLower().contains("identi.ca"); } void KOAuthWebHelper::loadFinished() { QWebPage *page = dynamic_cast(sender()); if (!page) { return; } QWebFrame* mf = page->mainFrame(); QString u = page->mainFrame()->url().toString(); if (d->authorizeUrls.contains(u)) { QString pin; foreach (const QWebElement &code, mf->findAllElements("CODE")) { pin = code.toPlainText(); // kDebug() << "tag:" << code.tagName() << "PIN:" << pin; }; foreach (const QWebElement &code, mf->findAllElements("#oauth_pin")) { pin = code.toPlainText(); // kDebug() << "tag:" << code.tagName() << "PIN:" << pin; }; if (!isIdentica() || !pin.isEmpty()) { // kDebug() << "We're done!" << u << pin; d->timer->stop(); // No need to show the dialog. :) emit statusUpdated(d->user, d->serviceBaseUrl, "Busy", "Application authorized"); emit appAuthSucceeded(u, pin); if (d->dialog) { d->dialog->close(); } return; } } else { // The remote service challenges app authorization, this means that the // user has to log in to the remote site to retrieve a PIN back, which // in turn will produce the accessToken and accessTokenSecret pair in // the next, final authorization step. emit statusUpdated(d->user, d->serviceBaseUrl, "Waiting", "App Authorization needed"); // we have to log in, let's see if we can do that automatically... // For Twitter, there are two cases: // - the user is already logged in, we just have to click the accept button // - the user is not logged in, in that case the user/password have to be // be filled in. We happen to know that, as it's the argument to the "auth" // operation in the dataengine. // kDebug() << "logging in " << d->user << d->password; if (!isIdentica()) { // Try to fill in user/pass into the form //kDebug() << "twitter.com hacks JavaScript hacks: " << u; QString script = "var userName = document.getElementById(\"username_or_email\"); userName.value = \"" + d->user + "\";\n"; script.append("var passWord = document.getElementById(\"password\"); passWord.value = \"" + d->password + "\";\n"); mf->evaluateJavaScript(script); //kDebug() << "ran script 1: " << script; // Evaluate the button click separately, as the above script might abort, // and not all lines are actually executed. script = QString(); script.append("var ackButton = document.getElementById(\"allow\"); ackButton.click();"); mf->evaluateJavaScript(script); //kDebug() << "ran script 2: " << script; } else if (d->serviceBaseUrl.toLower().contains("identi.ca")) { //kDebug() << "Identi.ca hacks JavaScript hacks"; QVariant r; QString script = "var userName = document.getElementById(\"nickname\"); userName.value = \"" + d->user + "\";\n"; script.append("var passWord = document.getElementById(\"password\"); passWord.value = \"" + d->password + "\";\n"); r = mf->evaluateJavaScript(script); kDebug() << "Ran script 1" << script << r; // Evaluate the button click separately, as the above script might abort, // and not all lines are actually executed. script = ""; script.append("var ackButton = document.getElementById(\"allow_submit\"); ackButton.click();"); r = mf->evaluateJavaScript(script); kDebug() << "Ran script 2" << script << r; } d->timer->start(); } } } #include "koauthwebhelper.moc"