File indexing completed on 2024-04-14 14:55:10

0001 /** ===========================================================
0002  * @file
0003  *
0004  * This file is a part of KDE project
0005  * <a href="https://commits.kde.org/libmediawiki">libmediawiki</a>
0006  *
0007  * @date   2011-03-22
0008  * @brief  a MediaWiki C++ interface for KDE
0009  *
0010  * @author Copyright (C) 2011-2012 by Gilles Caulier
0011  *         <a href="mailto:caulier dot gilles at gmail dot com">caulier dot gilles at gmail dot com</a>
0012  * @author Copyright (C) 2011 by Manuel Campomanes
0013  *         <a href="mailto:campomanes dot manuel at gmail dot com">campomanes dot manuel at gmail dot com</a>
0014  * @author Copyright (C) 2010 by Alexandre Mendes
0015  *         <a href="mailto:alex dot mendes1988 at gmail dot com">alex dot mendes1988 at gmail dot com</a>
0016  *
0017  * This program is free software; you can redistribute it
0018  * and/or modify it under the terms of the GNU General
0019  * Public License as published by the Free Software Foundation;
0020  * either version 2, or (at your option)
0021  * any later version.
0022  *
0023  * This program is distributed in the hope that it will be useful,
0024  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0025  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0026  * GNU General Public License for more details.
0027  *
0028  * ============================================================ */
0029 
0030 #include "edit.h"
0031 
0032 
0033 // Qt includes
0034 
0035 #include <QTimer>
0036 #include <QUrl>
0037 #include <QUrlQuery>
0038 #include <QXmlStreamReader>
0039 #include <QCryptographicHash>
0040 #include <QStringList>
0041 
0042 #include <QNetworkCookie>
0043 #include <QNetworkCookieJar>
0044 #include <QNetworkReply>
0045 #include <QNetworkRequest>
0046 
0047 // Local includes
0048 
0049 #include "mediawiki.h"
0050 #include "queryinfo.h"
0051 #include "job_p.h"
0052 
0053 namespace mediawiki
0054 {
0055 
0056 class Result
0057 {
0058 public:
0059 
0060     unsigned int m_captchaId;
0061     QVariant     m_captchaQuestion;
0062     QString      m_captchaAnswer;
0063 };
0064 
0065 class EditPrivate : public JobPrivate
0066 {
0067 public:
0068 
0069     EditPrivate(MediaWiki& mediawiki)
0070         : JobPrivate(mediawiki)
0071     {
0072     }
0073 
0074     static int error(const QString& error)
0075     {
0076         QString temp = error;
0077         int ret      = 0;
0078         QStringList list;
0079         list    << QStringLiteral("notext")
0080                 << QStringLiteral("invalidsection")
0081                 << QStringLiteral("protectedtitle")
0082                 << QStringLiteral("cantcreate")
0083                 << QStringLiteral("cantcreateanon")
0084                 << QStringLiteral("articleexists")
0085                 << QStringLiteral("noimageredirectanon")
0086                 << QStringLiteral("noimageredirect")
0087                 << QStringLiteral("spamdetected")
0088                 << QStringLiteral("filtered")
0089                 << QStringLiteral("contenttoobig")
0090                 << QStringLiteral("noeditanon")
0091                 << QStringLiteral("noedit")
0092                 << QStringLiteral("pagedeleted")
0093                 << QStringLiteral("emptypage")
0094                 << QStringLiteral("emptynewsection")
0095                 << QStringLiteral("editconflict")
0096                 << QStringLiteral("revwrongpage")
0097                 << QStringLiteral("undofailure");
0098         ret = list.indexOf(temp.remove(QChar::fromLatin1('-')));
0099         if(ret == -1)
0100         {
0101             ret = 0;
0102         }
0103         return  ret + (int)Edit::TextMissing ;
0104     }
0105 
0106     QUrl                   baseUrl;
0107     QMap<QString, QString> requestParameter;
0108     Result                 result;
0109 };
0110 
0111 Edit::Edit(MediaWiki& media, QObject* parent)
0112     : Job(*new EditPrivate(media), parent)
0113 {}
0114 
0115 void Edit::setUndoAfter(int undoafter)
0116 {
0117     Q_D(Edit);
0118     d->requestParameter[QStringLiteral("undoafter")] = QString::number(undoafter);
0119 }
0120 
0121 void Edit::setUndo(int undo)
0122 {
0123     Q_D(Edit);
0124     d->requestParameter[QStringLiteral("undo")] = QString::number(undo);
0125 }
0126 
0127 void Edit::setPrependText(const QString& prependText)
0128 {
0129     Q_D(Edit);
0130     d->requestParameter[QStringLiteral("prependtext")] = prependText;
0131     d->requestParameter[QStringLiteral("md5")] = QString();
0132 }
0133 
0134 void Edit::setAppendText(const QString& appendText)
0135 {
0136     Q_D(Edit);
0137     d->requestParameter[QStringLiteral("appendtext")] = appendText;
0138     d->requestParameter[QStringLiteral("md5")] = QString();
0139 }
0140 
0141 void Edit::setPageName(const QString& pageName)
0142 {
0143     Q_D(Edit);
0144     d->requestParameter[QStringLiteral("title")] = pageName;
0145 }
0146 
0147 void Edit::setToken(const QString& token)
0148 {
0149     Q_D(Edit);
0150     d->requestParameter[QStringLiteral("token")] = token;
0151 }
0152 
0153 void Edit::setBaseTimestamp(const QDateTime& baseTimestamp)
0154 {
0155     Q_D(Edit);
0156     d->requestParameter[QStringLiteral("basetimestamp")] = baseTimestamp.toString(QStringLiteral("yyyy-MM-ddThh:mm:ssZ"));
0157 }
0158 
0159 void Edit::setStartTimestamp(const QDateTime& startTimestamp)
0160 {
0161     Q_D(Edit);
0162     d->requestParameter[QStringLiteral("starttimestamp")] = startTimestamp.toString(QStringLiteral("yyyy-MM-ddThh:mm:ssZ"));
0163 }
0164 
0165 void Edit::setText(const QString& text)
0166 {
0167     Q_D(Edit);
0168     d->requestParameter[QStringLiteral("text")] = text;
0169     d->requestParameter[QStringLiteral("md5")] = QString();
0170 }
0171 
0172 void Edit::setRecreate(bool recreate)
0173 {
0174     Q_D(Edit);
0175     if(recreate)
0176     {
0177         d->requestParameter[QStringLiteral("recreate")] = QStringLiteral("on");
0178         d->requestParameter[QStringLiteral("md5")] = QString();
0179     }
0180 }
0181 
0182 void Edit::setCreateonly(bool createonly)
0183 {
0184     Q_D(Edit);
0185     if(createonly)
0186     {
0187         d->requestParameter[QStringLiteral("createonly")] = QStringLiteral("on");
0188         d->requestParameter[QStringLiteral("md5")] = QString();
0189     }
0190 }
0191 
0192 void Edit::setNocreate(bool norecreate)
0193 {
0194     Q_D(Edit);
0195     if(norecreate)
0196     {
0197         d->requestParameter[QStringLiteral("nocreate")] = QStringLiteral("on");
0198         d->requestParameter[QStringLiteral("md5")] = QString();
0199     }
0200 }
0201 
0202 void Edit::setMinor(bool minor)
0203 {
0204     Q_D(Edit);
0205     if(minor)
0206         d->requestParameter[QStringLiteral("minor")] = QStringLiteral("on");
0207     else
0208         d->requestParameter[QStringLiteral("notminor")] = QStringLiteral("on");
0209 }
0210 
0211 void Edit::setSection(const QString& section)
0212 {
0213     Q_D(Edit);
0214     d->requestParameter[QStringLiteral("section")] = section;
0215 }
0216 
0217 void Edit::setSummary(const QString& summary)
0218 {
0219     Q_D(Edit);
0220     d->requestParameter[QStringLiteral("summary")] = summary;
0221 }
0222 
0223 void Edit::setWatchList(Edit::Watchlist watchlist)
0224 {
0225     Q_D(Edit);
0226     switch(watchlist) {
0227     case Edit::watch:
0228         d->requestParameter[QStringLiteral("watchlist")] = QString(QStringLiteral("watch"));
0229         break;
0230     case Edit::unwatch:
0231         d->requestParameter[QStringLiteral("watchlist")] = QString(QStringLiteral("unwatch"));
0232         break;
0233     case Edit::nochange:
0234         d->requestParameter[QStringLiteral("watchlist")] = QString(QStringLiteral("nochange"));
0235         break;
0236     case Edit::preferences:
0237         d->requestParameter[QStringLiteral("watchlist")] = QString(QStringLiteral("preferences"));
0238         break;
0239     }
0240 }
0241 
0242 Edit::~Edit()
0243 {
0244 }
0245 
0246 void Edit::start()
0247 {
0248     Q_D(Edit);
0249     QueryInfo* info = new QueryInfo(d->mediawiki,this);
0250     info->setPageName(d->requestParameter[QStringLiteral("title")]);
0251     info->setToken(QStringLiteral("edit"));
0252     connect(info, SIGNAL(page(Page)),
0253             this, SLOT(doWorkSendRequest(Page)));
0254     info->start();
0255 
0256 }
0257 
0258 void Edit::doWorkSendRequest(Page page)
0259 {
0260     Q_D(Edit);
0261     d->requestParameter[QStringLiteral("token")] = page.pageEditToken();
0262     // Set the url
0263     QUrl    url = d->mediawiki.url();
0264     QUrlQuery query;
0265     query.addQueryItem(QStringLiteral("format"), QStringLiteral("xml"));
0266     query.addQueryItem(QStringLiteral("action"), QStringLiteral("edit"));
0267 
0268     // Add params
0269     if(d->requestParameter.contains(QStringLiteral("md5")))
0270     {
0271         QString text;
0272         if(d->requestParameter.contains(QStringLiteral("prependtext")))
0273             text += d->requestParameter[QStringLiteral("prependtext")];
0274         if(d->requestParameter.contains(QStringLiteral("appendtext")))
0275             text += d->requestParameter[QStringLiteral("appendtext")];
0276         if(d->requestParameter.contains(QStringLiteral("text")))
0277             text = d->requestParameter[QStringLiteral("text")];
0278         QByteArray hash = QCryptographicHash::hash(text.toUtf8(),QCryptographicHash::Md5);
0279         d->requestParameter[QStringLiteral("md5")] = QString::fromLatin1(hash.toHex());
0280     }
0281 
0282     QMapIterator<QString, QString> i(d->requestParameter);
0283     while (i.hasNext())
0284     {
0285         i.next();
0286         if(i.key() != QStringLiteral("token"))
0287             query.addQueryItem(i.key(),i.value());
0288     }
0289 
0290     QByteArray cookie;
0291     QList<QNetworkCookie> mediawikiCookies = d->manager->cookieJar()->cookiesForUrl(d->mediawiki.url());
0292     for(int i = 0 ; i < mediawikiCookies.size(); ++i)
0293     {
0294         cookie += mediawikiCookies.at(i).toRawForm(QNetworkCookie::NameAndValueOnly);
0295         cookie += ';';
0296     }
0297     // Add the token
0298     query.addQueryItem(QStringLiteral("token"), d->requestParameter[QStringLiteral("token")]);
0299     url.setQuery(query);
0300     d->baseUrl = url;
0301 
0302     // Set the request
0303     QNetworkRequest request( url );
0304     request.setRawHeader("User-Agent", d->mediawiki.userAgent().toUtf8());
0305     request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
0306     request.setRawHeader( "Cookie", cookie );
0307 
0308     setPercent(25); // Request ready.
0309 
0310     // Send the request
0311     d->reply = d->manager->post( request, url.toString().toUtf8() );
0312     connectReply();
0313     connect( d->reply, SIGNAL(finished()),
0314              this, SLOT(finishedEdit()) );
0315 
0316     setPercent(50); // Request sent.
0317 }
0318 
0319 void Edit::finishedEdit()
0320 {
0321     Q_D(Edit);
0322     disconnect(d->reply, SIGNAL(finished()),
0323                this, SLOT(finishedEdit()));
0324 
0325     setPercent(75); // Response received.
0326 
0327     if ( d->reply->error() != QNetworkReply::NoError )
0328     {
0329         this->setError(this->NetworkError);
0330         d->reply->close();
0331         d->reply->deleteLater();
0332         emitResult();
0333         return;
0334     }
0335     QXmlStreamReader reader( d->reply );
0336     while ( !reader.atEnd() && !reader.hasError() )
0337     {
0338         QXmlStreamReader::TokenType token = reader.readNext();
0339         if ( token == QXmlStreamReader::StartElement )
0340         {
0341             QXmlStreamAttributes attrs = reader.attributes();
0342             if ( reader.name() == QStringLiteral("edit") )
0343             {
0344                 if ( attrs.value( QStringLiteral("result") ).toString() == QLatin1String("Success") )
0345                 {
0346                     setPercent(100); // Response parsed successfully.
0347                     this->setError(KJob::NoError);
0348                     d->reply->close();
0349                     d->reply->deleteLater();
0350                     emitResult();
0351                     return;
0352                 }
0353                 else if ( attrs.value( QStringLiteral("result") ).toString() == QLatin1String("Failure") )
0354                 {
0355                     this->setError(KJob::NoError);
0356                     reader.readNext();
0357                     attrs = reader.attributes();
0358                     d->result.m_captchaId = attrs.value( QStringLiteral("id") ).toString().toUInt() ;
0359                     if (!attrs.value( QStringLiteral("question") ).isEmpty())
0360                         d->result.m_captchaQuestion = QVariant(attrs.value( QStringLiteral("question") ).toString()) ;
0361                     else if (!attrs.value( QStringLiteral("url") ).isEmpty())
0362                         d->result.m_captchaQuestion = QVariant(attrs.value( QStringLiteral("url") ).toString()) ;
0363                 }
0364             }
0365             else if ( reader.name() == QStringLiteral("error") )
0366             {
0367                 this->setError(EditPrivate::error(attrs.value( QStringLiteral("code") ).toString()));
0368                 d->reply->close();
0369                 d->reply->deleteLater();
0370                 emitResult();
0371                 return;
0372             }
0373         }
0374         else if ( token == QXmlStreamReader::Invalid && reader.error() != QXmlStreamReader::PrematureEndOfDocumentError)
0375         {
0376             this->setError(this->XmlError);
0377             d->reply->close();
0378             d->reply->deleteLater();
0379             emitResult();
0380             return;
0381         }
0382     }
0383     d->reply->close();
0384     d->reply->deleteLater();
0385     emit resultCaptcha(d->result.m_captchaQuestion);
0386 }
0387 
0388 void Edit::finishedCaptcha(const QString& captcha)
0389 {
0390     Q_D(Edit);
0391     d->result.m_captchaAnswer = captcha;
0392     QUrl url                  = d->baseUrl;
0393     QUrlQuery query;
0394     query.addQueryItem(QStringLiteral("CaptchaId"), QString::number(d->result.m_captchaId));
0395     query.addQueryItem(QStringLiteral("CaptchaAnswer"), d->result.m_captchaAnswer);
0396     url.setQuery(query);
0397     QString data      = url.toString();
0398     QByteArray cookie;
0399     QList<QNetworkCookie> mediawikiCookies = d->manager->cookieJar()->cookiesForUrl(d->mediawiki.url());
0400     for(int i = 0 ; i < mediawikiCookies.size(); ++i)
0401     {
0402         cookie += mediawikiCookies.at(i).toRawForm(QNetworkCookie::NameAndValueOnly);
0403         cookie += ';';
0404     }
0405 
0406     // Set the request
0407     QNetworkRequest request( url );
0408     request.setRawHeader("User-Agent", d->mediawiki.userAgent().toUtf8());
0409     request.setRawHeader( "Cookie", cookie );
0410     request.setHeader(QNetworkRequest::ContentTypeHeader, QStringLiteral("application/x-www-form-urlencoded"));
0411     // Send the request
0412     d->reply = d->manager->post( request, data.toUtf8() );
0413     connect( d->reply, SIGNAL(finished()),
0414              this, SLOT(finishedEdit()) );
0415 }
0416 
0417 } // namespace mediawiki