File indexing completed on 2024-04-28 04:42:49
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