From 8c20130563a5d1d5aa322826ac1ce1560ba42ad0 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Thu, 8 Apr 2010 10:39:26 +0200 Subject: [PATCH] CodePaster: Make pastebin.ca protocol work. Replace deprecated QHttp by QNetworkReply. Implement listing functionality, fix fetch encoding. --- src/plugins/cpaster/codepasterprotocol.cpp | 9 +- src/plugins/cpaster/pastebindotcaprotocol.cpp | 153 ++++++++++++++---- src/plugins/cpaster/pastebindotcaprotocol.h | 29 ++-- 3 files changed, 143 insertions(+), 48 deletions(-) diff --git a/src/plugins/cpaster/codepasterprotocol.cpp b/src/plugins/cpaster/codepasterprotocol.cpp index 39b21af8e64..f86c9f800b6 100644 --- a/src/plugins/cpaster/codepasterprotocol.cpp +++ b/src/plugins/cpaster/codepasterprotocol.cpp @@ -30,7 +30,6 @@ #include "codepasterprotocol.h" #include "codepastersettings.h" #include "cpasterplugin.h" -#include "cgi.h" #include <coreplugin/coreconstants.h> #include <coreplugin/editormanager/editormanager.h> @@ -130,13 +129,13 @@ void CodePasterProtocol::paste(const QString &text, return; QByteArray data = "command=processcreate&submit=submit&highlight_type=0&description="; - data += CGI::encodeURL(description).toLatin1(); + data += QUrl::toPercentEncoding(description); data += "&comment="; - data += CGI::encodeURL(comment).toLatin1(); + data += QUrl::toPercentEncoding(comment); data += "&code="; - data += CGI::encodeURL(fixNewLines(text)).toLatin1(); + data += QUrl::toPercentEncoding(fixNewLines(text)); data += "&poster="; - data += CGI::encodeURL(username).toLatin1(); + data += QUrl::toPercentEncoding(username); m_pasteReply = httpPost(QLatin1String("http://") + hostName, data); connect(m_pasteReply, SIGNAL(finished()), this, SLOT(pasteFinished())); diff --git a/src/plugins/cpaster/pastebindotcaprotocol.cpp b/src/plugins/cpaster/pastebindotcaprotocol.cpp index 7984ee64c8a..76bad682df7 100644 --- a/src/plugins/cpaster/pastebindotcaprotocol.cpp +++ b/src/plugins/cpaster/pastebindotcaprotocol.cpp @@ -28,24 +28,46 @@ **************************************************************************/ #include "pastebindotcaprotocol.h" -#include "cgi.h" + +#include <utils/qtcassert.h> #include <QtNetwork/QNetworkReply> +#include <QtCore/QXmlStreamReader> +#include <QtCore/QXmlStreamAttributes> +#include <QtCore/QStringList> + +static const char urlC[] = "http://pastebin.ca/"; namespace CodePaster { PasteBinDotCaProtocol::PasteBinDotCaProtocol(const NetworkAccessManagerProxyPtr &nw) : NetworkProtocol(nw), m_fetchReply(0), - m_postId(-1) + m_listReply(0), + m_pasteReply(0) { - connect(&m_http, SIGNAL(requestFinished(int,bool)), - this, SLOT(postRequestFinished(int,bool))); +} + +unsigned PasteBinDotCaProtocol::capabilities() const +{ + return ListCapability | PostDescriptionCapability; } void PasteBinDotCaProtocol::fetch(const QString &id) { - QString link = QLatin1String("http://pastebin.ca/raw/"); - link.append(id); + QTC_ASSERT(!m_fetchReply, return) + const QString url = QLatin1String(urlC); + const QString rawPostFix = QLatin1String("raw/"); + // Create link as ""http://pastebin.ca/raw/[id]" + // If we get a complete URL, just insert 'raw', else build URL. + QString link = id; + if (link.startsWith(url)) { + const int lastSlashPos = link.lastIndexOf(QLatin1Char('/')); + if (lastSlashPos != -1) + link.insert(lastSlashPos + 1, rawPostFix); + } else { + link.insert(0, rawPostFix); + link.insert(0, url); + } m_fetchReply = httpGet(link); connect(m_fetchReply, SIGNAL(finished()), this, SLOT(fetchFinished())); m_fetchId = id; @@ -54,36 +76,34 @@ void PasteBinDotCaProtocol::fetch(const QString &id) void PasteBinDotCaProtocol::paste(const QString &text, ContentType /* ct */, const QString &username, - const QString &comment, + const QString & /* comment */, const QString &description) { - Q_UNUSED(comment); - Q_UNUSED(description); - QString data = "content="; - data += CGI::encodeURL(fixNewLines(text)); + QTC_ASSERT(!m_pasteReply, return) + QByteArray data = "content="; + data += QUrl::toPercentEncoding(fixNewLines(text)); data += "&description="; - data += CGI::encodeURL(description); + data += QUrl::toPercentEncoding(description); data += "&type=1&expiry=1%20day&name="; - data += CGI::encodeURL(username); - QHttpRequestHeader header("POST", "/quiet-paste.php"); - header.setValue("host", "pastebin.ca" ); - header.setContentType("application/x-www-form-urlencoded"); - m_http.setHost("pastebin.ca", QHttp::ConnectionModeHttp); - header.setValue("User-Agent", "CreatorPastebin"); - m_postId = m_http.request(header, data.toAscii()); + data += QUrl::toPercentEncoding(username); + // fire request + const QString link = QLatin1String(urlC) + QLatin1String("quiet-paste.php"); + m_pasteReply = httpPost(link, data); + connect(m_pasteReply, SIGNAL(finished()), this, SLOT(pasteFinished())); } -void PasteBinDotCaProtocol::postRequestFinished(int id, bool error) +void PasteBinDotCaProtocol::pasteFinished() { - QString link; - if (id == m_postId) { - if (!error) { - QByteArray data = m_http.readAll(); - link = QString::fromLatin1("http://pastebin.ca/") + QString(data).remove("SUCCESS:"); - } else - link = m_http.errorString(); + if (m_pasteReply->error()) { + qWarning("Pastebin.ca protocol error: %s", qPrintable(m_pasteReply->errorString())); + } else { + /// returns ""SUCCESS:[id]"" + const QByteArray data = m_pasteReply->readAll(); + const QString link = QString::fromLatin1(urlC) + QString::fromAscii(data).remove(QLatin1String("SUCCESS:")); emit pasteDone(link); } + m_pasteReply->deleteLater(); + m_pasteReply = 0; } void PasteBinDotCaProtocol::fetchFinished() @@ -95,10 +115,87 @@ void PasteBinDotCaProtocol::fetchFinished() content = m_fetchReply->errorString(); } else { title = QString::fromLatin1("Pastebin.ca: %1").arg(m_fetchId); - content = m_fetchReply->readAll(); + const QByteArray data = m_fetchReply->readAll(); + content = QString::fromUtf8(data); + content.remove(QLatin1Char('\r')); } m_fetchReply->deleteLater(); m_fetchReply = 0; emit fetchDone(title, content, error); } + +void PasteBinDotCaProtocol::list() +{ + QTC_ASSERT(!m_listReply, return); + m_listReply = httpGet(QLatin1String(urlC)); + connect(m_listReply, SIGNAL(finished()), this, SLOT(listFinished())); +} + +/* Quick & dirty: Parse the <div>-elements with the "Recent Posts" listing + * out of the page. +\code +<div class="menutitle"><h2>Recent Posts</h2></div> + <div class="items" id="idmenurecent-collapse"> + <div class='recentlink'> + <a href="/[id]" class="rjt" rel="/preview.php?id=[id]">[nameTitle]</a> + <div class='recentdetail'>[time spec]</div> + </div> + ...<h2>Create a New Pastebin Post</h2> +\endcode */ + +static inline QStringList parseLists(QIODevice *io) +{ + enum State { OutsideRecentLink, InsideRecentLink }; + + QStringList rc; + + const QString classAttribute = QLatin1String("class"); + const QString divElement = QLatin1String("div"); + const QString anchorElement = QLatin1String("a"); + + // Start parsing at the 'recent posts' entry as the HTML above is not well-formed + // as of 8.4.2010. This will then terminate with an error. + QByteArray data = io->readAll(); + const QByteArray recentPosts("<h2>Recent Posts</h2></div>"); + const int recentPostsPos = data.indexOf(recentPosts); + if (recentPostsPos == -1) + return rc; + data.remove(0, recentPostsPos + recentPosts.size()); + QXmlStreamReader reader(data); + State state = OutsideRecentLink; + while (!reader.atEnd()) { + switch(reader.readNext()) { + case QXmlStreamReader::StartElement: + // Inside a <div> of an entry: Anchor or description + if (state == InsideRecentLink && reader.name() == anchorElement) { // Anchor + // Strip host from link + QString link = reader.attributes().value(QLatin1String("href")).toString(); + if (link.startsWith(QLatin1Char('/'))) + link.remove(0, 1); + const QString nameTitle = reader.readElementText(); + rc.push_back(link + QLatin1Char(' ') + nameTitle); + } else if (state == OutsideRecentLink && reader.name() == divElement) { // "<div>" state switching + if (reader.attributes().value(classAttribute) == QLatin1String("recentlink")) + state = InsideRecentLink; + } // divElement + break; + default: + break; + } // switch reader + } // while reader.atEnd() + return rc; +} + +void PasteBinDotCaProtocol::listFinished() +{ + const bool error = m_listReply->error(); + if (error) { + qWarning("pastebin.ca list failed: %s", qPrintable(m_listReply->errorString())); + } else { + emit listDone(name(), parseLists(m_listReply)); + } + m_listReply->deleteLater(); + m_listReply = 0; +} + } // namespace CodePaster diff --git a/src/plugins/cpaster/pastebindotcaprotocol.h b/src/plugins/cpaster/pastebindotcaprotocol.h index 4cc766d4b01..38e1e12abe2 100644 --- a/src/plugins/cpaster/pastebindotcaprotocol.h +++ b/src/plugins/cpaster/pastebindotcaprotocol.h @@ -32,8 +32,6 @@ #include "protocol.h" -#include <QtNetwork/QHttp> - namespace CodePaster { class PasteBinDotCaProtocol : public NetworkProtocol { @@ -42,26 +40,27 @@ public: explicit PasteBinDotCaProtocol(const NetworkAccessManagerProxyPtr &nw); QString name() const { return QLatin1String("Pastebin.Ca"); } - bool hasSettings() const { return false; } - virtual unsigned capabilities() const { return 0; } + virtual bool hasSettings() const { return false; } + virtual unsigned capabilities() const; + + virtual void fetch(const QString &id); + virtual void paste(const QString &text, + ContentType ct = Text, + const QString &username = QString(), + const QString &comment = QString(), + const QString &description = QString()); + virtual void list(); - void fetch(const QString &id); - void paste(const QString &text, - ContentType ct = Text, - const QString &username = QString(), - const QString &comment = QString(), - const QString &description = QString()); public slots: void fetchFinished(); - - void postRequestFinished(int id, bool error); + void listFinished(); + void pasteFinished(); private: QNetworkReply *m_fetchReply; + QNetworkReply *m_listReply; + QNetworkReply *m_pasteReply; QString m_fetchId; - - QHttp m_http; - int m_postId; }; } // namespace CodePaster -- GitLab