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