From 05cec243c28f50bd5ee7a167067501f7bd8db3ff Mon Sep 17 00:00:00 2001 From: Andrei Karas Date: Mon, 5 May 2014 17:23:21 +0300 Subject: Validate xml downloads, and if failed, use another mirror. --- src/commands.cpp | 2 +- src/gui/windows/serverdialog.cpp | 2 +- src/gui/windows/updaterwindow.cpp | 20 ++++++++++++++++---- src/gui/windows/updaterwindow.h | 2 ++ src/net/download.cpp | 21 ++++++++++++++++++--- src/net/download.h | 8 ++++++-- src/utils/xml.cpp | 38 +++++++++++++++++++++++++++++++++++++- src/utils/xml.h | 6 ++++++ 8 files changed, 87 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/commands.cpp b/src/commands.cpp index aeb1f3f53..508c78e4e 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -1338,7 +1338,7 @@ static void uploadFile(const std::string &str, Net::Download *const upload = new Net::Download(info, "http://sprunge.us", &uploadUpdate, - false, true); + false, true, false); info->upload = upload; info->text = str; info->addStr = addStr; diff --git a/src/gui/windows/serverdialog.cpp b/src/gui/windows/serverdialog.cpp index 653d62567..015c94f00 100644 --- a/src/gui/windows/serverdialog.cpp +++ b/src/gui/windows/serverdialog.cpp @@ -522,7 +522,7 @@ void ServerDialog::downloadServerList() } mDownload = new Net::Download(this, listFile, - &downloadUpdate, false, false); + &downloadUpdate, false, false, true); mDownload->setFile(std::string(mDir).append("/").append( branding.getStringValue("onlineServerFile"))); mDownload->start(); diff --git a/src/gui/windows/updaterwindow.cpp b/src/gui/windows/updaterwindow.cpp index 842176793..dab1a6ebc 100644 --- a/src/gui/windows/updaterwindow.cpp +++ b/src/gui/windows/updaterwindow.cpp @@ -193,7 +193,8 @@ UpdaterWindow::UpdaterWindow(const std::string &restrict updateHost, mStoreInMemory(true), mDownloadComplete(true), mUserCancel(false), - mLoadUpdates(applyUpdates) + mLoadUpdates(applyUpdates), + mValidateXml(false) { setWindowName("UpdaterWindow"); setResizable(true); @@ -532,13 +533,16 @@ void UpdaterWindow::download() { mDownload = new Net::Download(this, "http://manaplus.org/update/" + mCurrentFile, - &updateProgress, true, false); + &updateProgress, + true, false, mValidateXml); mDownload->addMirror("http://www.manaplus.org/update/" + mCurrentFile); } else { - mDownload = new Net::Download(this, std::string(mUpdateHost).append( - "/").append(mCurrentFile), &updateProgress, false, false); + mDownload = new Net::Download(this, + std::string(mUpdateHost).append("/").append(mCurrentFile), + &updateProgress, + false, false, mValidateXml); const std::vector &mirrors = client->getMirrors(); FOR_EACH (std::vector::const_iterator, it, mirrors) @@ -788,6 +792,7 @@ void UpdaterWindow::logic() // Parse current memory buffer as news and dispose of the data loadNews(); + mValidateXml = true; mCurrentFile = xmlUpdateFile; mStoreInMemory = false; mDownloadStatus = UPDATE_LIST; @@ -803,6 +808,7 @@ void UpdaterWindow::logic() mUpdateHost = updateServer2 + mUpdateServerPath; mUpdatesDir.append("/fix"); mCurrentFile = xmlUpdateFile; + mValidateXml = true; mStoreInMemory = false; mDownloadStatus = UPDATE_LIST2; download(); @@ -827,6 +833,7 @@ void UpdaterWindow::logic() // If the resources.xml file fails, // fall back onto a older version mCurrentFile = txtUpdateFile; + mValidateXml = false; mStoreInMemory = false; mDownloadStatus = UPDATE_LIST; download(); @@ -835,6 +842,7 @@ void UpdaterWindow::logic() } else if (mCurrentFile == txtUpdateFile) { + mValidateXml = true; mUpdateFiles = loadTxtFile(std::string(mUpdatesDir).append( "/").append(txtUpdateFile)); } @@ -863,6 +871,7 @@ void UpdaterWindow::logic() std::ifstream temp((std::string(mUpdatesDir).append( "/").append(mCurrentFile)).c_str()); + mValidateXml = false; if (!temp.is_open() || !validateFile(std::string( mUpdatesDir).append("/").append(mCurrentFile), mCurrentChecksum)) @@ -883,6 +892,7 @@ void UpdaterWindow::logic() mCurrentFile = "latest.txt"; mStoreInMemory = true; mDownloadStatus = UPDATE_PATCH; + mValidateXml = false; download(); // download() changes // mDownloadComplete to false } @@ -898,6 +908,7 @@ void UpdaterWindow::logic() } mUpdateIndexOffset = mUpdateIndex; mUpdateIndex = 0; + mValidateXml = true; mStoreInMemory = false; mDownloadStatus = UPDATE_RESOURCES2; download(); @@ -906,6 +917,7 @@ void UpdaterWindow::logic() case UPDATE_RESOURCES2: if (mDownloadComplete) { + mValidateXml = false; if (mUpdateIndex < mTempUpdateFiles.size()) { const UpdateFile thisFile = mTempUpdateFiles[mUpdateIndex]; diff --git a/src/gui/windows/updaterwindow.h b/src/gui/windows/updaterwindow.h index 4874ba8ba..d73da1e3e 100644 --- a/src/gui/windows/updaterwindow.h +++ b/src/gui/windows/updaterwindow.h @@ -268,6 +268,8 @@ private: /** Tells ~UpdaterWindow() if it should load updates */ bool mLoadUpdates; + + bool mValidateXml; }; #endif // GUI_WINDOWS_UPDATERWINDOW_H diff --git a/src/net/download.cpp b/src/net/download.cpp index 26a3cfe58..e1b5012fd 100644 --- a/src/net/download.cpp +++ b/src/net/download.cpp @@ -60,9 +60,12 @@ namespace Net std::string Download::mUploadResponse = ""; -Download::Download(void *const ptr, const std::string &url, +Download::Download(void *const ptr, + const std::string &url, const DownloadUpdate updateFunction, - const bool ignoreError, const bool isUpload) : + const bool ignoreError, + const bool isUpload, + const bool isXml) : mPtr(ptr), mUrl(url), mOptions(), @@ -77,7 +80,8 @@ Download::Download(void *const ptr, const std::string &url, mFormPost(nullptr), mError(static_cast(calloc(CURL_ERROR_SIZE + 1, 1))), mIgnoreError(ignoreError), - mUpload(isUpload) + mUpload(isUpload), + mIsXml(isXml) { if (mError) mError[0] = 0; @@ -417,6 +421,7 @@ int Download::downloadThread(void *ptr) continue; // Bail out here to avoid the renaming } } + if (file) { fclose(file); @@ -427,6 +432,16 @@ int Download::downloadThread(void *ptr) // otherwise the rename will fail on Windows. if (!d->mOptions.cancel) { + if (d->mIsXml) + { + if (!XML::Document::validateXml(outFilename)) + { + logger->log_r("Xml file validation error"); + attempts++; + continue; + } + } + ::remove(d->mFileName.c_str()); Files::renameFile(outFilename, d->mFileName); diff --git a/src/net/download.h b/src/net/download.h index 2201aaff2..bc12a8f7c 100644 --- a/src/net/download.h +++ b/src/net/download.h @@ -55,9 +55,12 @@ namespace Net class Download final { public: - Download(void *const ptr, const std::string &url, + Download(void *const ptr, + const std::string &url, const DownloadUpdate updateFunction, - const bool ignoreError, const bool isUpload); + const bool ignoreError, + const bool isUpload, + const bool isXml); A_DELETE_COPY(Download) @@ -141,6 +144,7 @@ class Download final char *mError; bool mIgnoreError; bool mUpload; + bool mIsXml; }; } // namespace Net diff --git a/src/utils/xml.cpp b/src/utils/xml.cpp index 3d98fda61..eae3567ef 100644 --- a/src/utils/xml.cpp +++ b/src/utils/xml.cpp @@ -27,6 +27,7 @@ #include "resources/resourcemanager.h" #include "utils/fuzzer.h" +#include "utils/stringutils.h" #include "utils/translation/podict.h" @@ -34,6 +35,11 @@ #include "debug.h" +namespace +{ + bool valid = false; +} // namespace + static void xmlErrorLogger(void *ctx A_UNUSED, const char *msg A_UNUSED, ...) #ifdef __GNUC__ #ifdef __OpenBSD__ @@ -61,12 +67,13 @@ static void xmlErrorLogger(void *ctx A_UNUSED, const char *msg, ...) va_end(ap); if (logger) - logger->log1(buf); + logger->log_r("%s", buf); else puts(buf); // Delete temporary buffer delete [] buf; + valid = false; } namespace XML @@ -80,6 +87,7 @@ namespace XML #endif int size = 0; char *data = nullptr; + valid = true; if (useResman) { const ResourceManager *const resman @@ -122,6 +130,7 @@ namespace XML { logger->log("Error loading %s", filename.c_str()); } + mIsValid = valid; } Document::Document(const char *const data, const int size) : @@ -261,4 +270,31 @@ namespace XML xmlCleanupParser(); } + bool Document::validateXml(const std::string &fileName) + { + xmlDocPtr doc = xmlReadFile(fileName.c_str(), + nullptr, XML_PARSE_PEDANTIC); + const bool valid(doc); + xmlFreeDoc(doc); + if (!valid) + return false; + + std::ifstream file; + file.open(fileName.c_str(), std::ios::in); + if (!file.is_open()) + { + file.close(); + return false; + } + char line[101]; + if (!file.getline(line, 100)) + return false; + file.close(); + + const std::string str = line; + if (!strStartWith(str, "