Commit a01507e9 authored by Christian Stenger's avatar Christian Stenger Committed by hjk
Browse files

CPaster: Fix pasting to KDE paster



Add minimal handling for credentials.

Task-number: QTCREATORBUG-17942
Change-Id: I5f3821fa944fa7800bd1f51947f46d301e2b00b8
Reviewed-by: Riitta-Leena Miettinen's avatarLeena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: default avatarhjk <hjk@qt.io>
parent d4ca232d
/****************************************************************************
**
** 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
/****************************************************************************
**
** 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
......@@ -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
......@@ -45,6 +45,8 @@ QtcPlugin {
"settingspage.ui",
"urlopenprotocol.cpp",
"urlopenprotocol.h",
"authenticationdialog.cpp",
"authenticationdialog.h"
]
Group {
......
......@@ -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
......@@ -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
......@@ -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);
......
......@@ -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);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment