summaryrefslogtreecommitdiff
path: root/src/plugins/cpaster
diff options
context:
space:
mode:
authorChristian Stenger <christian.stenger@qt.io>2017-04-07 14:29:01 +0200
committerhjk <hjk@qt.io>2017-04-12 14:17:20 +0000
commita01507e90e0a051c9b916abafe9fc01a0da00128 (patch)
tree543d55fad48b02142dab2ded62267a4f72aeb342 /src/plugins/cpaster
parentd4ca232d54a9a5dabc6955139f3d3c2dcb0875f0 (diff)
downloadqt-creator-a01507e90e0a051c9b916abafe9fc01a0da00128.tar.gz
CPaster: Fix pasting to KDE paster
Add minimal handling for credentials. Task-number: QTCREATORBUG-17942 Change-Id: I5f3821fa944fa7800bd1f51947f46d301e2b00b8 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io> Reviewed-by: hjk <hjk@qt.io>
Diffstat (limited to 'src/plugins/cpaster')
-rw-r--r--src/plugins/cpaster/authenticationdialog.cpp64
-rw-r--r--src/plugins/cpaster/authenticationdialog.h51
-rw-r--r--src/plugins/cpaster/cpaster.pro8
-rw-r--r--src/plugins/cpaster/cpaster.qbs2
-rw-r--r--src/plugins/cpaster/kdepasteprotocol.cpp126
-rw-r--r--src/plugins/cpaster/kdepasteprotocol.h27
-rw-r--r--src/plugins/cpaster/protocol.cpp19
-rw-r--r--src/plugins/cpaster/protocol.h5
8 files changed, 290 insertions, 12 deletions
diff --git a/src/plugins/cpaster/authenticationdialog.cpp b/src/plugins/cpaster/authenticationdialog.cpp
new file mode 100644
index 0000000000..326cdb2d80
--- /dev/null
+++ b/src/plugins/cpaster/authenticationdialog.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "authenticationdialog.h"
+
+#include <QDialogButtonBox>
+#include <QFormLayout>
+#include <QLabel>
+#include <QLineEdit>
+#include <QVBoxLayout>
+
+namespace CodePaster {
+
+AuthenticationDialog::AuthenticationDialog(const QString &details, QWidget *parent)
+ : QDialog(parent)
+{
+ setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ auto *mainLayout = new QVBoxLayout;
+ mainLayout->addWidget(new QLabel(details));
+ auto *formLayout = new QFormLayout;
+ formLayout->addRow(tr("Username:"), m_user = new QLineEdit);
+ formLayout->addRow(tr("Password:"), m_pass = new QLineEdit);
+ m_pass->setEchoMode(QLineEdit::Password);
+ mainLayout->addLayout(formLayout);
+ auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+ connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+ mainLayout->addWidget(buttonBox);
+ setLayout(mainLayout);
+}
+
+QString AuthenticationDialog::userName() const
+{
+ return m_user->text();
+}
+
+QString AuthenticationDialog::password() const
+{
+ return m_pass->text();
+}
+
+} // namespace CodePaster
diff --git a/src/plugins/cpaster/authenticationdialog.h b/src/plugins/cpaster/authenticationdialog.h
new file mode 100644
index 0000000000..f52dbaef24
--- /dev/null
+++ b/src/plugins/cpaster/authenticationdialog.h
@@ -0,0 +1,51 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <QDialog>
+
+QT_BEGIN_NAMESPACE
+class QLineEdit;
+QT_END_NAMESPACE
+
+namespace CodePaster {
+
+class AuthenticationDialog : public QDialog
+{
+public:
+ AuthenticationDialog(const QString &details, QWidget *parent = nullptr);
+
+ bool authenticated() const { return m_authenticated; }
+ QString userName() const;
+ QString password() const;
+
+private:
+ bool m_authenticated = false;
+ QLineEdit *m_user = nullptr;
+ QLineEdit *m_pass = nullptr;
+};
+
+} // namespace CodePaster
diff --git a/src/plugins/cpaster/cpaster.pro b/src/plugins/cpaster/cpaster.pro
index cc30094162..20c85c596b 100644
--- a/src/plugins/cpaster/cpaster.pro
+++ b/src/plugins/cpaster/cpaster.pro
@@ -14,7 +14,8 @@ HEADERS += cpasterplugin.h \
fileshareprotocolsettingspage.h \
kdepasteprotocol.h \
urlopenprotocol.h \
- codepasterservice.h
+ codepasterservice.h \
+ authenticationdialog.h
SOURCES += cpasterplugin.cpp \
settingspage.cpp \
@@ -28,7 +29,8 @@ SOURCES += cpasterplugin.cpp \
fileshareprotocol.cpp \
fileshareprotocolsettingspage.cpp \
kdepasteprotocol.cpp \
- urlopenprotocol.cpp
+ urlopenprotocol.cpp \
+ authenticationdialog.cpp
FORMS += settingspage.ui \
pasteselect.ui \
@@ -39,3 +41,5 @@ include(../../shared/cpaster/cpaster.pri)
RESOURCES += \
cpaster.qrc
+
+DEFINES *= CPASTER_PLUGIN_GUI
diff --git a/src/plugins/cpaster/cpaster.qbs b/src/plugins/cpaster/cpaster.qbs
index 4b3cb99a84..f9144b53d8 100644
--- a/src/plugins/cpaster/cpaster.qbs
+++ b/src/plugins/cpaster/cpaster.qbs
@@ -45,6 +45,8 @@ QtcPlugin {
"settingspage.ui",
"urlopenprotocol.cpp",
"urlopenprotocol.h",
+ "authenticationdialog.cpp",
+ "authenticationdialog.h"
]
Group {
diff --git a/src/plugins/cpaster/kdepasteprotocol.cpp b/src/plugins/cpaster/kdepasteprotocol.cpp
index d182005c1d..c5c09cb9bf 100644
--- a/src/plugins/cpaster/kdepasteprotocol.cpp
+++ b/src/plugins/cpaster/kdepasteprotocol.cpp
@@ -24,7 +24,11 @@
****************************************************************************/
#include "kdepasteprotocol.h"
+#ifdef CPASTER_PLUGIN_GUI
+#include "authenticationdialog.h"
+#endif
+#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <QDebug>
@@ -33,7 +37,7 @@
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
-
+#include <QRegularExpression>
#include <QNetworkReply>
#include <algorithm>
@@ -118,7 +122,7 @@ void StickyNotesPasteProtocol::paste(const QString &text,
pasteData += QUrl::toPercentEncoding(description.left(maxDescriptionLength));
}
- m_pasteReply = httpPost(m_hostUrl + QLatin1String("api/json/create"), pasteData);
+ m_pasteReply = httpPost(m_hostUrl + QLatin1String("api/json/create"), pasteData, true);
connect(m_pasteReply, &QNetworkReply::finished, this, &StickyNotesPasteProtocol::pasteFinished);
if (debug)
qDebug() << "paste: sending " << m_pasteReply << pasteData;
@@ -262,9 +266,127 @@ void StickyNotesPasteProtocol::listFinished()
m_listReply = nullptr;
}
+KdePasteProtocol::KdePasteProtocol()
+{
+ setHostUrl(QLatin1String("https://pastebin.kde.org/"));
+ connect(this, &KdePasteProtocol::authenticationFailed, this, [this] () {
+ m_loginFailed = true;
+ paste(m_text, m_contentType, m_expiryDays, QString(), QString(), m_description);
+ });
+}
+
+void KdePasteProtocol::paste(const QString &text, Protocol::ContentType ct, int expiryDays,
+ const QString &username, const QString &comment,
+ const QString &description)
+{
+ Q_UNUSED(username);
+ Q_UNUSED(comment);
+ // KDE paster needs authentication nowadays
+#ifdef CPASTER_PLUGIN_GUI
+ QString details = tr("Pasting to KDE paster needs authentication.<br/>"
+ "Enter your KDE Identity credentials to continue.");
+ if (m_loginFailed)
+ details.prepend(tr("<span style='background-color:LightYellow;color:red'>Login failed</span><br/><br/>"));
+
+ AuthenticationDialog authDialog(details, Core::ICore::dialogParent());
+ authDialog.setWindowTitle("Authenticate for KDE paster");
+ if (authDialog.exec() != QDialog::Accepted) {
+ m_loginFailed = false;
+ return;
+ }
+ const QString user = authDialog.userName();
+ const QString passwd = authDialog.password();
+#else
+ // FIXME get the credentials for the cmdline cpaster somehow
+ const QString user;
+ const QString passwd;
+ qDebug() << "KDE needs credentials for pasting";
+ return;
+#endif
+ // store input data as members to be able to use them after the authentication succeeded
+ m_text = text;
+ m_contentType = ct;
+ m_expiryDays = expiryDays;
+ m_description = description;
+ authenticate(user, passwd);
+}
+
QString KdePasteProtocol::protocolName()
{
return QLatin1String("Paste.KDE.Org");
}
+void KdePasteProtocol::authenticate(const QString &user, const QString &passwd)
+{
+ QTC_ASSERT(!m_authReply, return);
+
+ // first we need to obtain the hidden form token for logging in
+ m_authReply = httpGet(hostUrl() + "user/login");
+ connect(m_authReply, &QNetworkReply::finished, this, [this, user, passwd] () {
+ onPreAuthFinished(user, passwd);
+ });
+}
+
+void KdePasteProtocol::onPreAuthFinished(const QString &user, const QString &passwd)
+{
+ if (m_authReply->error() != QNetworkReply::NoError) {
+ m_authReply->deleteLater();
+ m_authReply = nullptr;
+ return;
+ }
+ const QByteArray page = m_authReply->readAll();
+ m_authReply->deleteLater();
+ const QRegularExpression regex("name=\"_token\"\\s+type=\"hidden\"\\s+value=\"(.*?)\">");
+ const QRegularExpressionMatch match = regex.match(QLatin1String(page));
+ if (!match.hasMatch()) {
+ m_authReply = nullptr;
+ return;
+ }
+ const QString token = match.captured(1);
+
+ QByteArray data("username=" + QUrl::toPercentEncoding(user)
+ + "&password=" + QUrl::toPercentEncoding(passwd)
+ + "&_token=" + QUrl::toPercentEncoding(token));
+ m_authReply = httpPost(hostUrl() + "user/login", data, true);
+ connect(m_authReply, &QNetworkReply::finished, this, &KdePasteProtocol::onAuthFinished);
+}
+
+void KdePasteProtocol::onAuthFinished()
+{
+ if (m_authReply->error() != QNetworkReply::NoError) {
+ m_authReply->deleteLater();
+ m_authReply = nullptr;
+ return;
+ }
+ const QVariant attribute = m_authReply->attribute(QNetworkRequest::RedirectionTargetAttribute);
+ m_redirectUrl = redirectUrl(attribute.toUrl().toString(), m_redirectUrl);
+ if (!m_redirectUrl.isEmpty()) { // we need to perform a redirect
+ QUrl url(m_redirectUrl);
+ if (url.path().isEmpty())
+ url.setPath("/"); // avoid issue inside cookiesForUrl()
+ m_authReply->deleteLater();
+ m_authReply = httpGet(url.url(), true);
+ connect(m_authReply, &QNetworkReply::finished, this, &KdePasteProtocol::onAuthFinished);
+ } else { // auth should be done now
+ const QByteArray page = m_authReply->readAll();
+ m_authReply->deleteLater();
+ m_authReply = nullptr;
+ if (page.contains("https://identity.kde.org")) // we're back on the login page
+ emit authenticationFailed();
+ else {
+ m_loginFailed = false;
+ StickyNotesPasteProtocol::paste(m_text, m_contentType, m_expiryDays, QString(),
+ QString(), m_description);
+ }
+ }
+}
+
+QString KdePasteProtocol::redirectUrl(const QString &redirect, const QString &oldRedirect) const
+{
+ QString redirectUrl;
+ if (!redirect.isEmpty() && redirect != oldRedirect)
+ redirectUrl = redirect;
+ return redirectUrl;
+}
+
} // namespace CodePaster
diff --git a/src/plugins/cpaster/kdepasteprotocol.h b/src/plugins/cpaster/kdepasteprotocol.h
index 7cd3d0d83b..e7bbd1ba1d 100644
--- a/src/plugins/cpaster/kdepasteprotocol.h
+++ b/src/plugins/cpaster/kdepasteprotocol.h
@@ -70,14 +70,33 @@ private:
class KdePasteProtocol : public StickyNotesPasteProtocol
{
+ Q_OBJECT
public:
- KdePasteProtocol()
- {
- setHostUrl(QLatin1String("https://pastebin.kde.org/"));
- }
+ KdePasteProtocol();
+
+ void paste(const QString &text, ContentType ct = Text, int expiryDays = 1,
+ const QString &username = QString(),
+ const QString &comment = QString() ,
+ const QString &description = QString()) override;
QString name() const override { return protocolName(); }
static QString protocolName();
+signals:
+ void authenticationFailed();
+private:
+ void authenticate(const QString &user, const QString &passwd);
+ void onPreAuthFinished(const QString &user, const QString &passwd);
+ void onAuthFinished();
+ QString redirectUrl(const QString &redirect, const QString &oldRedirect) const;
+
+ QNetworkReply *m_authReply = nullptr;
+ QString m_text;
+ ContentType m_contentType = Text;
+ int m_expiryDays = 1;
+ bool m_loginFailed = false;
+ QString m_description;
+ QString m_redirectUrl;
+
};
} // namespace CodePaster
diff --git a/src/plugins/cpaster/protocol.cpp b/src/plugins/cpaster/protocol.cpp
index f79bed2a46..8b745006d2 100644
--- a/src/plugins/cpaster/protocol.cpp
+++ b/src/plugins/cpaster/protocol.cpp
@@ -35,6 +35,8 @@
#include <coreplugin/icore.h>
#include <coreplugin/dialogs/ioptionspage.h>
+#include <QNetworkCookie>
+#include <QNetworkCookieJar>
#include <QNetworkRequest>
#include <QNetworkReply>
@@ -173,17 +175,30 @@ bool Protocol::showConfigurationError(const Protocol *p,
// --------- NetworkProtocol
-QNetworkReply *NetworkProtocol::httpGet(const QString &link)
+static void addCookies(QNetworkRequest &request)
+{
+ auto accessMgr = Utils::NetworkAccessManager::instance();
+ const QList<QNetworkCookie> cookies = accessMgr->cookieJar()->cookiesForUrl(request.url());
+ for (const QNetworkCookie &cookie : cookies)
+ request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookie));
+}
+
+QNetworkReply *NetworkProtocol::httpGet(const QString &link, bool handleCookies)
{
QUrl url(link);
QNetworkRequest r(url);
+ if (handleCookies)
+ addCookies(r);
return Utils::NetworkAccessManager::instance()->get(r);
}
-QNetworkReply *NetworkProtocol::httpPost(const QString &link, const QByteArray &data)
+QNetworkReply *NetworkProtocol::httpPost(const QString &link, const QByteArray &data,
+ bool handleCookies)
{
QUrl url(link);
QNetworkRequest r(url);
+ if (handleCookies)
+ addCookies(r);
r.setHeader(QNetworkRequest::ContentTypeHeader,
QVariant(QByteArray("application/x-www-form-urlencoded")));
return Utils::NetworkAccessManager::instance()->post(r, data);
diff --git a/src/plugins/cpaster/protocol.h b/src/plugins/cpaster/protocol.h
index 76891ae279..4dc2acb6b2 100644
--- a/src/plugins/cpaster/protocol.h
+++ b/src/plugins/cpaster/protocol.h
@@ -110,9 +110,10 @@ public:
~NetworkProtocol() override;
protected:
- QNetworkReply *httpGet(const QString &url);
+ QNetworkReply *httpGet(const QString &url, bool handleCookies = false);
- QNetworkReply *httpPost(const QString &link, const QByteArray &data);
+ QNetworkReply *httpPost(const QString &link, const QByteArray &data,
+ bool handleCookies = false);
// Check connectivity of host, displaying a message box.
bool httpStatus(QString url, QString *errorMessage, bool useHttps = false);