From 0aed5e4eb3af56ba0bf9ceb0343c262a7427b6ba Mon Sep 17 00:00:00 2001 From: Jared Adams Date: Tue, 13 Oct 2009 09:00:01 -0600 Subject: Add an asynchronous download class And use it to download news, updates, and the server list. --- src/gui/serverdialog.cpp | 325 ++++++++++++++++++++++++++++------------------- src/gui/serverdialog.h | 92 ++++++++------ src/gui/updatewindow.cpp | 261 ++++++++----------------------------- src/gui/updatewindow.h | 35 +++-- 4 files changed, 316 insertions(+), 397 deletions(-) (limited to 'src/gui') diff --git a/src/gui/serverdialog.cpp b/src/gui/serverdialog.cpp index aaa0515b..f47f33ea 100644 --- a/src/gui/serverdialog.cpp +++ b/src/gui/serverdialog.cpp @@ -36,85 +36,66 @@ #include "net/net.h" -#include "utils/xml.h" #include "utils/gettext.h" #include "utils/stringutils.h" +#include "utils/xml.h" #include #include #include -const short MAX_SERVERLIST = 5; - -int ServersListModel::getNumberOfElements() -{ - return servers.size(); -} - -std::string ServersListModel::getElementAt(int elementIndex) -{ - std::string myServer = servers.at(elementIndex).name; - myServer += " ("; - myServer += std::string(servers.at(elementIndex).hostname); - myServer += ":"; - myServer += toString(servers.at(elementIndex).port); - myServer += ")"; - return myServer; -} +#define MAX_SERVERLIST 5 -void ServersListModel::addFirstElement(const ServerInfo &server) +ServersListModel::ServersListModel(ServerInfos *servers, ServerDialog *parent): + mServers(servers), + mParent(parent) { - // Equivalent to push_front - std::vector::iterator MyIterator = servers.begin(); - servers.insert(MyIterator, 1, server); } -void ServersListModel::addElement(const ServerInfo &server) +int ServersListModel::getNumberOfElements() { - servers.push_back(server); + MutexLocker lock = mParent->lock(); + return mServers->size(); } -void ServersListModel::mergeElement(const ServerInfo &server) +std::string ServersListModel::getElementAt(int elementIndex) { - // search through the list - for (int i = 0; i < getNumberOfElements(); i++) + MutexLocker lock = mParent->lock(); + ServerInfo server = mServers->at(elementIndex); + std::string myServer; + if (server.name.empty()) { - // the server is already in the list, merge its properties - if (servers[i] == server) - { - servers[i].name = server.name; - return; - } + myServer += server.hostname; + myServer += ":"; + myServer += toString(server.port); } - // the server is not found, add it at the end of the list - addElement(server); -} - -bool ServersListModel::contains(const ServerInfo &server) -{ - // search through the list - for (int i = 0; i < getNumberOfElements(); i++) + else { - if (servers[i] == server) - return true; + myServer += server.name; + myServer += " ("; + myServer += server.hostname; + myServer += ":"; + myServer += toString(server.port); + myServer += ")"; } - return false; + return myServer; } -ServerDialog::ServerDialog(ServerInfo *serverInfo): - Window(_("Choose Your Server")), mServerInfo(serverInfo) +ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir): + Window(_("Choose Your Server")), + mDir(dir), + mDownloadStatus(DOWNLOADING_PREPARING), + mDownloadProgress(-1.0f), + mServers(ServerInfos()), + mServerInfo(serverInfo) { - mServerDescription = new Label(std::string()); - gcn::Label *serverLabel = new Label(_("Server:")); - gcn::Label *portLabel = new Label(_("Port:")); + Label *serverLabel = new Label(_("Server:")); + Label *portLabel = new Label(_("Port:")); mServerNameField = new TextField(mServerInfo->hostname); mPortField = new TextField(toString(mServerInfo->port)); - mMostUsedServersListModel = new ServersListModel; ServerInfo currentServer; - ServerInfo tempServer; - // Add the most used servers from config if they are not in the online list std::string currentConfig = ""; for (int i = 0; i <= MAX_SERVERLIST; i++) @@ -129,18 +110,18 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo): if (!currentServer.hostname.empty() && currentServer.port != 0) { - if (!mMostUsedServersListModel->contains(currentServer)) - mMostUsedServersListModel->addElement(currentServer); + mServers.push_back(currentServer); } } - // load a list with online servers... - loadServerlist(); + mServersListModel = new ServersListModel(&mServers, this); - mMostUsedServersList = new ListBox(mMostUsedServersListModel); - ScrollArea *usedScroll = new ScrollArea(mMostUsedServersList); + mServersList = new ListBox(mServersListModel); + ScrollArea *usedScroll = new ScrollArea(mServersList); usedScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER); + mDescription = new Label(std::string()); + mQuitButton = new Button(_("Quit"), "quit", this); mConnectButton = new Button(_("Connect"), "connect", this); mManualEntryButton = new Button(_("Add Entry"), "addEntry", this); @@ -151,16 +132,16 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo): mServerNameField->addActionListener(this); mPortField->addActionListener(this); mManualEntryButton->addActionListener(this); - mMostUsedServersList->addSelectionListener(this); - mMostUsedServersList->setSelected(0); + mServersList->addSelectionListener(this); + mServersList->setSelected(0); usedScroll->setVerticalScrollAmount(0); - place(0, 0, mServerDescription, 2); - place(0, 1, serverLabel); - place(0, 2, portLabel); - place(1, 1, mServerNameField, 3).setPadding(3); - place(1, 2, mPortField, 3).setPadding(3); - place(0, 3, usedScroll, 4, 5).setPadding(3); + place(0, 0, serverLabel); + place(1, 0, mServerNameField, 3).setPadding(3); + place(0, 1, portLabel); + place(1, 1, mPortField, 3).setPadding(3); + place(0, 2, usedScroll, 4, 5).setPadding(3); + place(0, 7, mDescription, 4); place(0, 8, mManualEntryButton); place(2, 8, mQuitButton); place(3, 8, mConnectButton); @@ -185,11 +166,12 @@ ServerDialog::ServerDialog(ServerInfo *serverInfo): } } + downloadServerList(); } ServerDialog::~ServerDialog() { - delete mMostUsedServersListModel; + delete mServersListModel; } void ServerDialog::action(const gcn::ActionEvent &event) @@ -227,9 +209,9 @@ void ServerDialog::action(const gcn::ActionEvent &event) // now add the rest of the list... std::string currentConfig = ""; int configCount = 1; - for (int i = 0; i < mMostUsedServersListModel->getNumberOfElements(); i++) + for (int i = 0; i < mServersListModel->getNumberOfElements(); i++) { - tempServer = mMostUsedServersListModel->getServer(i); + tempServer = mServersListModel->getServer(i); // ensure, that our server will not be added twice if (tempServer != currentServer) @@ -262,24 +244,77 @@ void ServerDialog::action(const gcn::ActionEvent &event) void ServerDialog::valueChanged(const gcn::SelectionEvent &event) { - const int index = mMostUsedServersList->getSelected(); + const int index = mServersList->getSelected(); if (index == -1) return; // Update the server and post fields according to the new selection - const ServerInfo myServer = mMostUsedServersListModel->getServer(index); - mServerDescription->setCaption(myServer.name); + const ServerInfo myServer = mServersListModel->getServer(index); + mDescription->setCaption(myServer.name); mServerNameField->setText(myServer.hostname); mPortField->setText(toString(myServer.port)); setFieldsReadOnly(true); } -void ServerDialog::loadServerlist() +void ServerDialog::logic() { - ServerInfo currentServer; - currentServer.clear(); + { + MutexLocker lock(&mMutex); + if (mDownloadStatus == DOWNLOADING_COMPLETE) + { + mDownloadStatus = DOWNLOADING_OVER; + + mDescription->setCaption(std::string()); + } + else if (mDownloadStatus == DOWNLOADING_IN_PROGRESS) + { + mDescription->setCaption(strprintf(_("Downloading server list..." + "%2.2f%%"), + mDownloadProgress * 100)); + } + else if (mDownloadStatus == DOWNLOADING_IDLE) + { + mDescription->setCaption(_("Waiting for server...")); + } + else if (mDownloadStatus == DOWNLOADING_PREPARING) + { + mDescription->setCaption(_("Preparing download")); + } + } + + Window::logic(); +} + +void ServerDialog::setFieldsReadOnly(const bool readOnly) +{ + if (readOnly) + { + mServerNameField->setEnabled(false); + mPortField->setEnabled(false); + mManualEntryButton->setVisible(true); + mDescription->setVisible(true); + } + else + { + mManualEntryButton->setVisible(false); + + mDescription->setVisible(false); + mDescription->setCaption(std::string()); + mServersList->setSelected(-1); + + mServerNameField->setText(std::string()); + mServerNameField->setEnabled(true); + mPortField->setText(toString(DEFAULT_PORT)); + mPortField->setEnabled(true); + + mServerNameField->requestFocus(); + } +} + +void ServerDialog::downloadServerList() +{ // try to load the configuration value for the onlineServerList std::string listFile = config.getValue("onlineServerList", "void"); // if there is no entry, try to load the file from the default updatehost @@ -287,82 +322,116 @@ void ServerDialog::loadServerlist() listFile = config.getValue("updatehost", "http://updates.themanaworld.org") + "/serverlist.xml"; - xmlDocPtr doc = xmlReadFile(listFile.c_str(), NULL, 0); - if (doc == NULL) - { - logger->log("Failed to load online serverlist from %s", listFile.c_str()); - return; - } + mDownload = new Net::Download(this, listFile, &downloadUpdate); + mDownload->setFile(mDir + "/serverlist.xml"); + mDownload->start(); +} + +void ServerDialog::loadServers() +{ + ServerInfo currentServer; - xmlNodePtr rootNode = xmlDocGetRootElement(doc); - int version = XML::getProperty(rootNode, "version", 3); + xmlDocPtr doc = xmlReadFile((mDir + "/serverlist.xml").c_str(), NULL, 0); - if (version != 1) + if (doc != NULL) { - logger->log("Online server list has wrong version"); - return; - } + xmlNodePtr rootNode = xmlDocGetRootElement(doc); + int version = XML::getProperty(rootNode, "version", 3); - for_each_xml_child_node(server, rootNode) - { - if (xmlStrEqual(server->name, BAD_CAST "server")) + if (version != 1) { - //check wether the version matches - #ifdef TMWSERV_SUPPORT - if (XML::getProperty(server, "type", "unknown") != "TMWSERV") - continue; - #endif + logger->log("Online server list has wrong version"); + return; + } - #ifdef EATHENA_SUPPORT - if (XML::getProperty(server, "type", "unknown") != "EATHENA") - continue; - #endif + for_each_xml_child_node(server, rootNode) + { + if (xmlStrEqual(server->name, BAD_CAST "server")) + { + //check wether the version matches + #ifdef TMWSERV_SUPPORT + if (XML::getProperty(server, "type", "unknown") != "TMWSERV") + continue; + #endif - currentServer.clear(); - currentServer.name = XML::getProperty(server, "name", std::string()); + #ifdef EATHENA_SUPPORT + if (XML::getProperty(server, "type", "unknown") != "EATHENA") + continue; + #endif - for_each_xml_child_node(subnode, server) - { - if (xmlStrEqual(subnode->name, BAD_CAST "connection")) + currentServer.clear(); + currentServer.name = XML::getProperty(server, "name", std::string()); + + for_each_xml_child_node(subnode, server) { - currentServer.hostname = XML::getProperty(subnode, "hostname", std::string()); - currentServer.port = XML::getProperty(subnode, "port", DEFAULT_PORT); + if (xmlStrEqual(subnode->name, BAD_CAST "connection")) + { + currentServer.hostname = XML::getProperty(subnode, "hostname", std::string()); + currentServer.port = XML::getProperty(subnode, "port", DEFAULT_PORT); + } } - } - // merge the server into the local list - mMostUsedServersListModel->mergeElement(currentServer); + + MutexLocker lock(&mMutex); + // add the server to the local list (if it's not already present) + ServerInfos::iterator it; + bool found = false; + for (it = mServers.begin(); it != mServers.end(); it++) + { + if ((*it) == currentServer) + { + (*it).name = currentServer.name; + found = true; + break; + } + } + + if (!found) + mServers.push_back(currentServer); + } } + + xmlFreeDoc(doc); } - xmlFreeDoc(doc); + MutexLocker lock(&mMutex); + mDownloadStatus = DOWNLOADING_COMPLETE; } -void ServerDialog::setFieldsReadOnly(const bool readOnly) +int ServerDialog::downloadUpdate(void *ptr, DownloadStatus status, + size_t total, size_t remaining) { - if (readOnly) + ServerDialog *sd = reinterpret_cast(ptr); + bool finished = false; + + if (status == DOWNLOAD_STATUS_COMPLETE) { - mServerNameField->setEnabled(false); - mPortField->setEnabled(false); - mManualEntryButton->setVisible(true); - mServerDescription->setVisible(true); + finished = true; } - else + else if (status < 0) { - mManualEntryButton->setVisible(false); + logger->log("Error retreiving server list: %s\n", + sd->mDownload->getError()); - mServerDescription->setVisible(false); - mServerDescription->setCaption(std::string()); - mMostUsedServersList->setSelected(-1); + finished = true; + } + else + { + float progress = (float) remaining / total; - mServerNameField->setText(std::string()); - mServerNameField->setEnabled(true); + if (progress != progress) progress = 0.0f; // check for NaN + if (progress < 0.0f) progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway. + if (progress > 1.0f) progress = 1.0f; - mPortField->setText(toString(DEFAULT_PORT)); - mPortField->setEnabled(true); - - mServerNameField->requestFocus(); + MutexLocker lock(&sd->mMutex); + sd->mDownloadStatus = DOWNLOADING_IN_PROGRESS; + sd->mDownloadProgress = progress; } -} + if (finished) + { + sd->loadServers(); + } + return 0; +} diff --git a/src/gui/serverdialog.h b/src/gui/serverdialog.h index 4522cdad..943d69c9 100644 --- a/src/gui/serverdialog.h +++ b/src/gui/serverdialog.h @@ -24,10 +24,11 @@ #include "gui/widgets/window.h" -#include "guichanfwd.h" - +#include "net/download.h" #include "net/serverinfo.h" +#include "utils/mutex.h" + #include #include #include @@ -35,7 +36,11 @@ #include #include +class Button; +class Label; class ListBox; +class ServerDialog; +class TextField; /** * Server and Port List Model @@ -43,6 +48,8 @@ class ListBox; class ServersListModel : public gcn::ListModel { public: + ServersListModel(ServerInfos *servers, ServerDialog *parent); + /** * Used to get number of line in the list */ @@ -57,36 +64,11 @@ class ServersListModel : public gcn::ListModel * Used to get the corresponding Server struct */ ServerInfo getServer(int elementIndex) const - { return servers[elementIndex]; } - - /** - * Add an Element at the end of the server list - */ - void addElement(const ServerInfo &server); - - /** - * Add an Element at the end of the server list if it - * doesn't exist yet. Otherwise overwrite its properties - * in the list. - * - * @param server ServerInfo to merge into the list. - */ - void mergeElement(const ServerInfo &server); - - /** - * Add an Element at the beginning of the server list - */ - void addFirstElement(const ServerInfo &server); - - /** - * Returns wheter the given server is already in the list. - * @param server Server to search in the list. - * @return True, if the server is in the list, false otherwise. - */ - bool contains(const ServerInfo &server); + { return mServers->at(elementIndex); } private: - std::vector servers; + ServerInfos *mServers; + ServerDialog *mParent; }; /** @@ -104,7 +86,7 @@ class ServerDialog : public Window, * * @see Window::Window */ - ServerDialog(ServerInfo *serverInfo); + ServerDialog(ServerInfo *serverInfo, const std::string &dir); /** * Destructor @@ -121,24 +103,54 @@ class ServerDialog : public Window, */ void valueChanged(const gcn::SelectionEvent &event); + void logic(); + + protected: + friend class ServersListModel; + MutexLocker lock() { return MutexLocker(&mMutex); } + private: /** * Called to load a list of available server from an online xml file. */ - void loadServerlist(); + void downloadServerList(); + void loadServers(); + static int downloadUpdate(void *ptr, DownloadStatus status, + size_t total, size_t remaining); void setFieldsReadOnly(const bool readOnly); - gcn::TextField *mServerNameField; - gcn::TextField *mPortField; - gcn::Label *mServerDescription; - gcn::Button *mQuitButton; - gcn::Button *mConnectButton; - gcn::Button *mManualEntryButton; + TextField *mServerNameField; + TextField *mPortField; + Label *mDescription; + Button *mQuitButton; + Button *mConnectButton; + Button *mManualEntryButton; + + ListBox *mServersList; + ServersListModel *mServersListModel; + + const std::string &mDir; + + enum ServerDialogDownloadStatus + { + DOWNLOADING_ERROR, + DOWNLOADING_PREPARING, + DOWNLOADING_IDLE, + DOWNLOADING_IN_PROGRESS, + DOWNLOADING_COMPLETE, + DOWNLOADING_OVER + }; + + /** Status of the current download. */ + ServerDialogDownloadStatus mDownloadStatus; + + Net::Download *mDownload; - ListBox *mMostUsedServersList; - ServersListModel *mMostUsedServersListModel; + Mutex mMutex; + float mDownloadProgress; + ServerInfos mServers; ServerInfo *mServerInfo; }; diff --git a/src/gui/updatewindow.cpp b/src/gui/updatewindow.cpp index b39ee8c2..14ebe6b1 100644 --- a/src/gui/updatewindow.cpp +++ b/src/gui/updatewindow.cpp @@ -32,37 +32,14 @@ #include "log.h" #include "main.h" +#include "net/download.h" + #include "resources/resourcemanager.h" #include "utils/gettext.h" #include "utils/stringutils.h" #include -#include -#include -#include - -#include - -/** - * Calculates the Alder-32 checksum for the given file. - */ -static unsigned long fadler32(FILE *file) -{ - // Obtain file size - fseek(file, 0, SEEK_END); - long fileSize = ftell(file); - rewind(file); - - // Calculate Adler-32 checksum - char *buffer = (char*) malloc(fileSize); - const size_t read = fread(buffer, 1, fileSize, file); - unsigned long adler = adler32(0L, Z_NULL, 0); - adler = adler32(adler, (Bytef*) buffer, read); - free(buffer); - - return adler; -} /** * Load the given file into a vector of strings. @@ -89,22 +66,20 @@ std::vector loadTextFile(const std::string &fileName) UpdaterWindow::UpdaterWindow(const std::string &updateHost, const std::string &updatesDir): Window(_("Updating...")), - mThread(NULL), mDownloadStatus(UPDATE_NEWS), mUpdateHost(updateHost), mUpdatesDir(updatesDir), mCurrentFile("news.txt"), + mDownloadProgress(0.0f), mCurrentChecksum(0), mStoreInMemory(true), mDownloadComplete(true), mUserCancel(false), mDownloadedBytes(0), mMemoryBuffer(NULL), - mCurlError(new char[CURL_ERROR_SIZE]), + mDownload(NULL), mLineIndex(0) { - mCurlError[0] = 0; - mBrowserBox = new BrowserBox; mScrollArea = new ScrollArea(mBrowserBox); mLabel = new Label(_("Connecting...")); @@ -112,6 +87,7 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, mCancelButton = new Button(_("Cancel"), "cancel", this); mPlayButton = new Button(_("Play"), "play", this); + mProgressBar->setSmoothProgress(false); mBrowserBox->setOpaque(false); mPlayButton->setEnabled(false); @@ -139,26 +115,20 @@ UpdaterWindow::UpdaterWindow(const std::string &updateHost, UpdaterWindow::~UpdaterWindow() { - if (mThread) - SDL_WaitThread(mThread, NULL); - free(mMemoryBuffer); - - // Remove possibly leftover temporary download - ::remove((mUpdatesDir + "/download.temp").c_str()); - - delete[] mCurlError; } void UpdaterWindow::setProgress(float p) { - mProgressBar->setProgress(p); + // Do delayed progress bar update, since Guichan isn't thread-safe + MutexLocker lock(&mDownloadMutex); + mDownloadProgress = p; } void UpdaterWindow::setLabel(const std::string &str) { // Do delayed label text update, since Guichan isn't thread-safe - MutexLocker lock(&mLabelMutex); + MutexLocker lock(&mDownloadMutex); mNewLabelCaption = str; } @@ -178,6 +148,7 @@ void UpdaterWindow::action(const gcn::ActionEvent &event) // Skip the updating process if (mDownloadStatus != UPDATE_COMPLETE) { + mDownload->cancel(); mDownloadStatus = UPDATE_ERROR; } } @@ -216,12 +187,23 @@ void UpdaterWindow::loadNews() mScrollArea->setVerticalScrollAmount(0); } -int UpdaterWindow::updateProgress(void *ptr, - double dt, double dn, double ut, double un) +int UpdaterWindow::updateProgress(void *ptr, DownloadStatus status, + size_t dt, size_t dn) { - float progress = dn / dt; UpdaterWindow *uw = reinterpret_cast(ptr); + if (status == DOWNLOAD_STATUS_COMPLETE) + { + uw->mDownloadComplete = true; + } + else if (status == DOWNLOAD_STATUS_ERROR || + status == DOWNLOAD_STATUS_CANCELLED) + { + uw->mDownloadStatus = UPDATE_ERROR; + } + + float progress = (float) dn / dt; + if (progress != progress) progress = 0.0f; // check for NaN if (progress < 0.0f) progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway. if (progress > 1.0f) progress = 1.0f; @@ -239,7 +221,7 @@ int UpdaterWindow::updateProgress(void *ptr, return 0; } -size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, FILE *stream) +size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, void *stream) { UpdaterWindow *uw = reinterpret_cast(stream); size_t totalMem = size * nmemb; @@ -254,162 +236,37 @@ size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, FILE *st return totalMem; } -int UpdaterWindow::downloadThread(void *ptr) +void UpdaterWindow::download() { - int attempts = 0; - UpdaterWindow *uw = reinterpret_cast(ptr); - CURL *curl; - CURLcode res; - std::string outFilename; - std::string url(uw->mUpdateHost + "/" + uw->mCurrentFile); + mDownload = new Net::Download(this, mUpdateHost + "/" + mCurrentFile, + updateProgress); - while (attempts < 3 && !uw->mDownloadComplete) + if (mStoreInMemory) { - FILE *outfile = NULL; - FILE *newfile = NULL; - uw->setLabel(uw->mCurrentFile + " (0%)"); - - curl = curl_easy_init(); - - if (curl) + mDownload->setWriteFunction(UpdaterWindow::memoryWrite); + } + else + { + if (mDownloadStatus == UPDATE_RESOURCES) { - logger->log("Downloading: %s", url.c_str()); - - if (uw->mStoreInMemory) - { - uw->mDownloadedBytes = 0; - curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, - UpdaterWindow::memoryWrite); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr); - } - else - { - outFilename = uw->mUpdatesDir + "/download.temp"; - outfile = fopen(outFilename.c_str(), "w+b"); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile); - } - -#ifdef PACKAGE_VERSION - curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMW/" PACKAGE_VERSION); -#else - curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMW"); -#endif - curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, uw->mCurlError); - curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); - curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, - UpdaterWindow::updateProgress); - curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ptr); - curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); - curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15); - - struct curl_slist *pHeaders = NULL; - if (uw->mDownloadStatus != UPDATE_RESOURCES) - { - // Make sure the resources2.txt and news.txt aren't cached, - // in order to always get the latest version. - pHeaders = curl_slist_append(pHeaders, "pragma: no-cache"); - pHeaders = - curl_slist_append(pHeaders, "Cache-Control: no-cache"); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pHeaders); - } - - if ((res = curl_easy_perform(curl)) != 0) - { - uw->mDownloadStatus = UPDATE_ERROR; - switch (res) - { - case CURLE_COULDNT_CONNECT: - default: - logger->log("curl error %d: %s host: %s", - res, uw->mCurlError, url.c_str()); - break; - } - - if (!uw->mStoreInMemory) - { - fclose(outfile); - ::remove(outFilename.c_str()); - } - attempts++; - continue; - } - - curl_easy_cleanup(curl); - - if (uw->mDownloadStatus != UPDATE_RESOURCES) - { - curl_slist_free_all(pHeaders); - } - - if (!uw->mStoreInMemory) - { - // Don't check resources2.txt checksum - if (uw->mDownloadStatus == UPDATE_RESOURCES) - { - unsigned long adler = fadler32(outfile); - - if (uw->mCurrentChecksum != adler) - { - fclose(outfile); - - // Remove the corrupted file - ::remove(outFilename.c_str()); - logger->log( - "Checksum for file %s failed: (%lx/%lx)", - uw->mCurrentFile.c_str(), - adler, uw->mCurrentChecksum); - attempts++; - continue; // Bail out here to avoid the renaming - } - } - fclose(outfile); - - // Give the file the proper name - const std::string newName = - uw->mUpdatesDir + "/" + uw->mCurrentFile; - - // Any existing file with this name is deleted first, otherwise - // the rename will fail on Windows. - ::remove(newName.c_str()); - ::rename(outFilename.c_str(), newName.c_str()); - - // Check if we can open it and no errors were encountered - // during renaming - newfile = fopen(newName.c_str(), "rb"); - if (newfile) - { - fclose(newfile); - uw->mDownloadComplete = true; - } - } - else - { - // It's stored in memory, we're done - uw->mDownloadComplete = true; - } + mDownload->setFile(mUpdatesDir + "/" + mCurrentFile, mCurrentChecksum); + } + else + { + mDownload->setFile(mUpdatesDir + "/" + mCurrentFile); } - attempts++; } - if (!uw->mDownloadComplete) { - uw->mDownloadStatus = UPDATE_ERROR; + if (mDownloadStatus != UPDATE_RESOURCES) + { + mDownload->noCache(); } - return 0; -} - -void UpdaterWindow::download() -{ + setLabel(mCurrentFile + " (0%)"); mDownloadComplete = false; - mThread = SDL_CreateThread(UpdaterWindow::downloadThread, this); - if (!mThread) - { - logger->log("Unable to create mThread"); - mDownloadStatus = UPDATE_ERROR; - } + // TODO: check return + mDownload->start(); } void UpdaterWindow::logic() @@ -419,31 +276,22 @@ void UpdaterWindow::logic() // Synchronize label caption when necessary { - MutexLocker lock(&mLabelMutex); + MutexLocker lock(&mDownloadMutex); if (mLabel->getCaption() != mNewLabelCaption) { mLabel->setCaption(mNewLabelCaption); mLabel->adjustSize(); } + + mProgressBar->setProgress(mDownloadProgress); } + std::string filename = mUpdatesDir + "/" + mCurrentFile; + switch (mDownloadStatus) { case UPDATE_ERROR: - if (mThread) - { - if (mUserCancel) { - // Kill the thread, because user has canceled - SDL_KillThread(mThread); - // Set the flag to false again - mUserCancel = false; - } - else { - SDL_WaitThread(mThread, NULL); - } - mThread = NULL; - } // TODO: Only send complete sentences to gettext mBrowserBox->addRow(""); mBrowserBox->addRow(_("##1 The update process is incomplete.")); @@ -451,7 +299,8 @@ void UpdaterWindow::logic() mBrowserBox->addRow(_("##1 It is strongly recommended that")); // TRANSLATORS: Begins "It is strongly recommended that". mBrowserBox->addRow(_("##1 you try again later.")); - mBrowserBox->addRow(mCurlError); + + mBrowserBox->addRow(mDownload->getError()); mScrollArea->setVerticalScrollAmount( mScrollArea->getVerticalMaxScroll()); mDownloadStatus = UPDATE_COMPLETE; @@ -479,12 +328,6 @@ void UpdaterWindow::logic() case UPDATE_RESOURCES: if (mDownloadComplete) { - if (mThread) - { - SDL_WaitThread(mThread, NULL); - mThread = NULL; - } - if (mLineIndex < mLines.size()) { std::stringstream line(mLines[mLineIndex]); diff --git a/src/gui/updatewindow.h b/src/gui/updatewindow.h index 65a934dc..6e738fca 100644 --- a/src/gui/updatewindow.h +++ b/src/gui/updatewindow.h @@ -24,6 +24,8 @@ #include "gui/widgets/window.h" +#include "net/download.h" + #include "utils/mutex.h" #include @@ -36,8 +38,6 @@ class Button; class ProgressBar; class ScrollArea; -struct SDL_Thread; - /** * Update progress window GUI * @@ -92,23 +92,18 @@ private: void download(); /** - * The thread function that download the files. + * A download callback for progress updates. */ - static int downloadThread(void *ptr); - - /** - * A libcurl callback for progress updates. - */ - static int updateProgress(void *ptr, - double dt, double dn, double ut, double un); + static int updateProgress(void *ptr, DownloadStatus status, + size_t dt, size_t dn); /** * A libcurl callback for writing to memory. */ static size_t memoryWrite(void *ptr, size_t size, size_t nmemb, - FILE *stream); + void *stream); - enum DownloadStatus + enum UpdateDownloadStatus { UPDATE_ERROR, UPDATE_IDLE, @@ -118,11 +113,8 @@ private: UPDATE_RESOURCES }; - /** A thread that use libcurl to download updates. */ - SDL_Thread *mThread; - /** Status of the current download. */ - DownloadStatus mDownloadStatus; + UpdateDownloadStatus mDownloadStatus; /** Host where we get the updated files. */ std::string mUpdateHost; @@ -136,8 +128,11 @@ private: /** The new label caption to be set in the logic method. */ std::string mNewLabelCaption; - /** The mutex used to guard access to mNewLabelCaption. */ - Mutex mLabelMutex; + /** The new progress value to be set in the logic method. */ + float mDownloadProgress; + + /** The mutex used to guard access to mNewLabelCaption and mDownloadProgress. */ + Mutex mDownloadMutex; /** The Adler32 checksum of the file currently downloading. */ unsigned long mCurrentChecksum; @@ -157,8 +152,8 @@ private: /** Buffer for files downloaded to memory. */ char *mMemoryBuffer; - /** Buffer to handler human readable error provided by curl. */ - char *mCurlError; + /** Download handle. */ + Net::Download *mDownload; /** List of files to download. */ std::vector mLines; -- cgit v1.2.3-70-g09d2