1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "pastebindotcomprotocol.h"
35 #include "pastebindotcomsettings.h"
37 #include <coreplugin/icore.h>
39 #include <utils/qtcassert.h>
41 #include <QtCore/QDebug>
42 #include <QtCore/QTextStream>
43 #include <QtCore/QXmlStreamReader>
44 #include <QtCore/QXmlStreamAttributes>
45 #include <QtCore/QByteArray>
47 #include <QtNetwork/QNetworkReply>
51 static const char pastePhpScriptpC[] = "api_public.php";
52 static const char fetchPhpScriptpC[] = "raw.php";
54 namespace CodePaster {
55 PasteBinDotComProtocol::PasteBinDotComProtocol(const NetworkAccessManagerProxyPtr &nw) :
57 m_settings(new PasteBinDotComSettings),
67 QString PasteBinDotComProtocol::protocolName()
69 return QLatin1String("Pastebin.Com");
72 unsigned PasteBinDotComProtocol::capabilities() const
74 return ListCapability;
77 bool PasteBinDotComProtocol::checkConfiguration(QString *errorMessage)
79 if (m_hostChecked) // Check the host once.
81 const bool ok = httpStatus(hostName(false), errorMessage);
87 QString PasteBinDotComProtocol::hostName(bool withSubDomain) const
92 rc = m_settings->hostPrefix();
94 rc.append(QLatin1Char('.'));
96 rc.append(QLatin1String("pastebin.com"));
100 static inline QByteArray format(Protocol::ContentType ct)
106 return "paste_format=cpp";
108 case Protocol::JavaScript:
109 return "paste_format=javascript";
112 return "paste_format=dff";
115 return "paste_format=xml";
121 void PasteBinDotComProtocol::paste(const QString &text,
123 const QString &username,
124 const QString & /* comment */,
125 const QString & /* description */)
127 QTC_ASSERT(!m_pasteReply, return;)
130 QByteArray pasteData = format(ct);
131 if (!pasteData.isEmpty())
132 pasteData.append('&');
133 pasteData += "paste_name=";
134 pasteData += QUrl::toPercentEncoding(username);
136 const QString subDomain = m_settings->hostPrefix();
137 if (!subDomain.isEmpty()) {
138 pasteData += "&paste_subdomain=";
139 pasteData += QUrl::toPercentEncoding(subDomain);
142 pasteData += "&paste_code=";
143 pasteData += QUrl::toPercentEncoding(fixNewLines(text));
147 QTextStream(&link) << "http://" << hostName(false) << '/' << pastePhpScriptpC;
149 m_pasteReply = httpPost(link, pasteData);
150 connect(m_pasteReply, SIGNAL(finished()), this, SLOT(pasteFinished()));
152 qDebug() << "paste: sending " << m_pasteReply << link << pasteData;
155 void PasteBinDotComProtocol::pasteFinished()
157 if (m_pasteReply->error()) {
158 qWarning("Pastebin.com protocol error: %s", qPrintable(m_pasteReply->errorString()));
160 emit pasteDone(QString::fromAscii(m_pasteReply->readAll()));
163 m_pasteReply->deleteLater();
167 void PasteBinDotComProtocol::fetch(const QString &id)
169 const QString httpProtocolPrefix = QLatin1String("http://");
171 QTC_ASSERT(!m_fetchReply, return;)
173 // Did we get a complete URL or just an id. Insert a call to the php-script
175 if (id.startsWith(httpProtocolPrefix)) {
176 // Change "http://host/id" -> "http://host/script?i=id".
177 const int lastSlashPos = id.lastIndexOf(QLatin1Char('/'));
178 link = id.mid(0, lastSlashPos);
179 QTextStream(&link) << '/' << fetchPhpScriptpC<< "?i=" << id.mid(lastSlashPos + 1);
181 // format "http://host/script?i=id".
182 QTextStream(&link) << "http://" << hostName(true) << '/' << fetchPhpScriptpC<< "?i=" << id;
186 qDebug() << "fetch: sending " << link;
188 m_fetchReply = httpGet(link);
189 connect(m_fetchReply, SIGNAL(finished()), this, SLOT(fetchFinished()));
193 void PasteBinDotComProtocol::fetchFinished()
197 const bool error = m_fetchReply->error();
199 content = m_fetchReply->errorString();
201 qDebug() << "fetchFinished: error" << m_fetchId << content;
203 title = QString::fromLatin1("Pastebin.com: %1").arg(m_fetchId);
204 content = QString::fromAscii(m_fetchReply->readAll());
205 // Cut out from '<pre>' formatting
206 const int preEnd = content.lastIndexOf(QLatin1String("</pre>"));
208 content.truncate(preEnd);
209 const int preStart = content.indexOf(QLatin1String("<pre>"));
211 content.remove(0, preStart + 5);
212 // Make applicable as patch.
213 content = Protocol::textFromHtml(content);
214 content += QLatin1Char('\n');
217 QDebug nsp = qDebug().nospace();
218 nsp << "fetchFinished: " << content.size() << " Bytes";
223 m_fetchReply->deleteLater();
225 emit fetchDone(title, content, error);
228 void PasteBinDotComProtocol::list()
230 QTC_ASSERT(!m_listReply, return;)
233 m_listReply = httpGet(QLatin1String("http://") + hostName(true));
234 connect(m_listReply, SIGNAL(finished()), this, SLOT(listFinished()));
236 qDebug() << "list: sending " << m_listReply;
239 static inline void padString(QString *s, int len)
241 const int missing = len - s->length();
243 s->append(QString(missing, QLatin1Char(' ')));
246 /* Quick & dirty: Parse the <div>-elements with the "Recent Posts" listing
249 <div class="content_left_title">Recent Posts</div>
250 <div class="content_left_box">
251 <div class="clb_top"><a href="http://pastebin.com/id">User</a></div>
252 <div class="clb_bottom"><span>Title</div>
255 static inline QStringList parseLists(QIODevice *io)
257 enum State { OutsideRecentPostList, InsideRecentPostList,
258 InsideRecentPostBox, InsideRecentPost };
261 QXmlStreamReader reader(io);
263 const QString classAttribute = QLatin1String("class");
264 const QString divElement = QLatin1String("div");
265 const QString anchorElement = QLatin1String("a");
266 const QString spanElement = QLatin1String("span");
267 State state = OutsideRecentPostList;
268 while (!reader.atEnd()) {
269 switch(reader.readNext()) {
270 case QXmlStreamReader::StartElement:
271 // Inside a <div> of an entry: Anchor or description
272 if (state == InsideRecentPost) {
273 if (reader.name() == anchorElement) { // Anchor
274 // Strip host from link
275 QString link = reader.attributes().value(QLatin1String("href")).toString();
276 const int slashPos = link.lastIndexOf(QLatin1Char('/'));
278 link.remove(0, slashPos + 1);
279 const QString user = reader.readElementText();
280 rc.push_back(link + QLatin1Char(' ') + user);
281 } else if (reader.name() == spanElement) { // <span> with description
282 const QString description = reader.readElementText();
283 QTC_ASSERT(!rc.isEmpty(), return rc; )
284 padString(&rc.back(), 25);
285 rc.back() += QLatin1Char(' ');
286 rc.back() += description;
288 } else if (reader.name() == divElement) { // "<div>" state switching
290 // Check on the contents as there are several lists.
291 case OutsideRecentPostList:
292 if (reader.attributes().value(classAttribute) == QLatin1String("content_left_title")
293 && reader.readElementText() == QLatin1String("Recent Posts"))
294 state = InsideRecentPostList;
296 case InsideRecentPostList:
297 if (reader.attributes().value(classAttribute) == QLatin1String("content_left_box"))
298 state = InsideRecentPostBox;
300 case InsideRecentPostBox:
301 state = InsideRecentPost;
308 case QXmlStreamReader::EndElement:
309 if (reader.name() == divElement) {
311 case InsideRecentPost:
312 state = InsideRecentPostBox;
314 case InsideRecentPostBox: // Stop parsing when leaving the box.
329 void PasteBinDotComProtocol::listFinished()
331 const bool error = m_listReply->error();
334 qDebug() << "listFinished: error" << m_listReply->errorString();
336 QStringList list = parseLists(m_listReply);
337 emit listDone(name(), list);
341 m_listReply->deleteLater();
345 Core::IOptionsPage *PasteBinDotComProtocol::settingsPage() const
349 } // namespace CodePaster