pastebindotcaprotocol.cpp 8.82 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
2
**
3
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
7
**
hjk's avatar
hjk committed
8
9
10
11
12
13
14
** 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 Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
16
**
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17
18
19
20
21
22
23
24
25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
29
30

#include "pastebindotcaprotocol.h"
31
32

#include <utils/qtcassert.h>
33

34
35
36
37
#include <QNetworkReply>
#include <QXmlStreamReader>
#include <QXmlStreamAttributes>
#include <QStringList>
38
39

static const char urlC[] = "http://pastebin.ca/";
40
static const char protocolNameC[] = "Pastebin.Ca";
41

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
static inline QByteArray expiryValue(int expiryDays)
{
    // pastebin.ca supports 1-3 days, 1-3 weeks, 1-6 months, 1 year
    const int months = expiryDays / 30;
    const int weeks = expiryDays / 7;

    if (expiryDays == 1)
        return "1 day";
    if (expiryDays < 4)
        return QByteArray::number(expiryDays) + " days";
    if (weeks <= 1)
        return "1 week";
    if (weeks <= 3)
        return QByteArray::number(weeks) + " weeks";
    if (months <= 1)
        return "1 month";
    if (months <= 6)
        return QByteArray::number(months) + " months";
    return "1 year"; // using Never makes the post expire after 1 month
}

63
namespace CodePaster {
64
PasteBinDotCaProtocol::PasteBinDotCaProtocol() :
65
    m_fetchReply(0),
66
    m_listReply(0),
67
68
    m_pasteReply(0),
    m_hostChecked(false)
69
{
70
71
}

72
73
74
75
76
QString PasteBinDotCaProtocol::protocolName()
{
    return QLatin1String(protocolNameC);
}

77
78
unsigned PasteBinDotCaProtocol::capabilities() const
{
79
    return ListCapability | PostDescriptionCapability | PostCommentCapability;
80
81
82
83
}

void PasteBinDotCaProtocol::fetch(const QString &id)
{
84
    QTC_ASSERT(!m_fetchReply, return);
85
86
87
88
89
90
91
92
93
94
95
96
97
    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);
    }
98
99
100
    m_fetchReply = httpGet(link);
    connect(m_fetchReply, SIGNAL(finished()), this, SLOT(fetchFinished()));
    m_fetchId = id;
101
102
}

103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
static QByteArray toTypeId(Protocol::ContentType ct)
{
    if (ct == Protocol::C)
        return QByteArray(1, '3');
    if (ct == Protocol::Cpp)
        return QByteArray(1, '4');
    if (ct == Protocol::JavaScript)
        return QByteArray("27");
    if (ct == Protocol::Diff)
        return QByteArray( "34");
    if (ct == Protocol::Xml)
        return QByteArray("15");
    return QByteArray(1, '1');
}

118
void PasteBinDotCaProtocol::paste(const QString &text,
119
                                  ContentType ct, int expiryDays,
120
121
                                  const QString &/* username */,
                                  const QString & comment,
122
                                  const QString &description)
123
{
124
125
126
    QTC_ASSERT(!m_pasteReply, return);
    QByteArray data = "api=+xKvWG+1UFXkr2Kn3Td4AnpYtCIjA4qt&";
    data += "content=";
127
    data += QUrl::toPercentEncoding(fixNewLines(text));
128
129
    data += "&type=";
    data += toTypeId(ct);
130
    data += "&description=";
131
    data += QUrl::toPercentEncoding(comment);
132
    data += "&expiry=";
133
134
    data += QUrl::toPercentEncoding(QLatin1String(expiryValue(expiryDays)));
    data += "&name="; // Title or name.
135
    data += QUrl::toPercentEncoding(description);
136
137
138
139
    // fire request
    const QString link = QLatin1String(urlC) + QLatin1String("quiet-paste.php");
    m_pasteReply = httpPost(link, data);
    connect(m_pasteReply, SIGNAL(finished()), this, SLOT(pasteFinished()));
140
141
}

142
void PasteBinDotCaProtocol::pasteFinished()
143
{
144
    if (m_pasteReply->error()) {
145
        qWarning("%s protocol error: %s", protocolNameC, qPrintable(m_pasteReply->errorString()));
146
147
148
    } else {
        /// returns ""SUCCESS:[id]""
        const QByteArray data = m_pasteReply->readAll();
149
        const QString link = QString::fromLatin1(urlC) + QString::fromLatin1(data).remove(QLatin1String("SUCCESS:"));
150
151
        emit pasteDone(link);
    }
152
153
    m_pasteReply->deleteLater();
    m_pasteReply = 0;
154
155
156
157
158
159
}

void PasteBinDotCaProtocol::fetchFinished()
{
    QString title;
    QString content;
160
    bool error = m_fetchReply->error();
161
    if (error) {
162
        content = m_fetchReply->errorString();
163
    } else {
164
        title = name() + QLatin1String(": ") + m_fetchId;
165
166
167
        const QByteArray data = m_fetchReply->readAll();
        content = QString::fromUtf8(data);
        content.remove(QLatin1Char('\r'));
168
    }
169
170
    m_fetchReply->deleteLater();
    m_fetchReply = 0;
171
172
    emit fetchDone(title, content, error);
}
173
174
175
176
177
178
179
180

void PasteBinDotCaProtocol::list()
{
    QTC_ASSERT(!m_listReply, return);
    m_listReply = httpGet(QLatin1String(urlC));
    connect(m_listReply, SIGNAL(finished()), this, SLOT(listFinished()));
}

181
182
183
184
185
186
187
188
189
190
bool PasteBinDotCaProtocol::checkConfiguration(QString *errorMessage)
{
    if (m_hostChecked) // Check the host once.
        return true;
    const bool ok = httpStatus(QLatin1String(urlC), errorMessage);
    if (ok)
        m_hostChecked = true;
    return ok;
}

191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/* 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()) {
224
        switch (reader.readNext()) {
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
        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();
249
    if (error)
250
        qWarning("%s list failed: %s", protocolNameC, qPrintable(m_listReply->errorString()));
251
    else
252
253
254
255
256
        emit listDone(name(), parseLists(m_listReply));
    m_listReply->deleteLater();
    m_listReply = 0;
}

257
} // namespace CodePaster