Preparation to remove network_http (its work will be

taken over by the reqeust manager).
Initialise news and addons manager in a separate thread
and not from the network_http thread anymore. Icons are
also downloaded now from the request manager instead
of the network_http.


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@15006 178a84e3-b1eb-0310-8ba1-8eac791a3b58
This commit is contained in:
hikerstk 2014-01-12 11:20:32 +00:00
parent 9804cbddeb
commit b136e16f7a
14 changed files with 460 additions and 201 deletions

View File

@ -20,6 +20,21 @@
#include "addons/addons_manager.hpp"
#include "addons/inetwork_http.hpp"
#include "addons./news_manager.hpp"
#include "addons/zip.hpp"
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "online/http_request.hpp"
#include "online/request_manager.hpp"
#include "states_screens/kart_selection.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/string_utils.hpp"
#include <fstream>
#include <iostream>
#include <map>
@ -27,18 +42,6 @@
#include <string.h>
#include <vector>
#include "addons/inetwork_http.hpp"
#include "addons/request.hpp"
#include "addons/zip.hpp"
#include "io/file_manager.hpp"
#include "io/xml_node.hpp"
#include "karts/kart_properties.hpp"
#include "karts/kart_properties_manager.hpp"
#include "states_screens/kart_selection.hpp"
#include "tracks/track.hpp"
#include "tracks/track_manager.hpp"
#include "utils/string_utils.hpp"
AddonsManager* addons_manager = 0;
// ----------------------------------------------------------------------------
@ -68,6 +71,59 @@ AddonsManager::~AddonsManager()
saveInstalled();
} // ~AddonsManager
// ----------------------------------------------------------------------------
void AddonsManager::init(const XMLNode *xml,
bool force_refresh)
{
std::string addon_list_url("");
StkTime::TimeType mtime(0);
const XMLNode *include = xml->getNode("include");
std::string filename=file_manager->getAddonsFile("addons.xml");
if(!include)
{
file_manager->removeFile(filename);
NewsManager::get()->addNewsMessage(_("Can't access stkaddons server..."));
// Use a curl error code here:
//return CURLE_COULDNT_CONNECT;
return;
}
include->get("file", &addon_list_url);
int64_t tmp;
include->get("mtime", &tmp);
mtime = tmp;
bool download = mtime > UserConfigParams::m_addons_last_updated ||
force_refresh ||
!file_manager->fileExists(filename);
if (download)
{
Log::info("NetworkHttp", "Downloading updated addons.xml");
Online::HTTPRequest *download_request = new Online::HTTPRequest("addons.xml");
download_request->setURL(addon_list_url);
download_request->executeNow();
if(download_request->hadDownloadError())
{
Log::error("addons", "Error on download addons.xml: %s\n",
download_request->getDownloadErrorMessage());
delete download_request;
return;
}
delete download_request;
UserConfigParams::m_addons_last_updated=StkTime::getTimeSinceEpoch();
}
else
Log::info("NetworkHttp", "Using cached addons.xml");
const XMLNode *xml_addons = new XMLNode(filename);
addons_manager->initAddons(xml_addons); // will free xml_addons
if(UserConfigParams::logAddons())
Log::info("addons", "Addons manager list downloaded");
}
// ----------------------------------------------------------------------------
/** This initialises the online portion of the addons manager. It uses the
* downloaded list of available addons. This is called by network_http before
@ -76,7 +132,7 @@ AddonsManager::~AddonsManager()
* main GUI is not blocked anyway). This function will update the state
* variable
*/
void AddonsManager::initOnline(const XMLNode *xml)
void AddonsManager::initAddons(const XMLNode *xml)
{
m_addons_list.lock();
// Clear the list in case that a reinit is being done.
@ -204,7 +260,7 @@ void AddonsManager::initOnline(const XMLNode *xml)
if (UserConfigParams::m_internet_status == INetworkHttp::IPERM_ALLOWED)
downloadIcons();
} // initOnline
} // initAddons
// ----------------------------------------------------------------------------
/** Reinitialises the addon manager, which happens when the user selects
@ -297,12 +353,25 @@ void AddonsManager::downloadIcons()
addon.getId().c_str());
continue;
}
std::string save = "icons/"+icon;
Request *r = INetworkHttp::get()->downloadFileAsynchron(url, save,
/*priority*/1,
/*manage_mem*/true);
if (r != NULL)
r->setAddonIconNotification(&addon);
// A simple class that will notify the addon via a callback
class IconRequest : public Online::HTTPRequest
{
Addon *m_addon; // stores this addon object
void afterOperation()
{
m_addon->setIconReady();
} // callback
public:
IconRequest(const std::string &filename,
const std::string &url,
Addon *addon ) : HTTPRequest(filename, true, 1)
{
m_addon = addon; setURL(url);
} // IconRequest
};
IconRequest *r = new IconRequest("icons/"+icon, url, &addon);
r->queue();
}
else
m_addons_list.getData()[i].setIconReady();

View File

@ -39,8 +39,6 @@ private:
Synchronised<std::vector<Addon> > m_addons_list;
/** Full filename of the addons_installed.xml file. */
std::string m_file_installed;
std::string m_type;
int m_download_state;
/** List of loaded icons. */
std::vector<std::string> m_icon_list;
@ -60,7 +58,8 @@ private:
public:
AddonsManager();
~AddonsManager();
void initOnline(const XMLNode *xml);
void init(const XMLNode *xml, bool force_refresh);
void initAddons(const XMLNode *xml);
void checkInstalledAddons();
const Addon* getAddon(const std::string &id) const;
int getAddonIndex(const std::string &id) const;

View File

@ -256,102 +256,19 @@ NetworkHttp::~NetworkHttp()
*/
CURLcode NetworkHttp::init(bool forceRefresh)
{
news_manager->clearErrorMessage();
core::stringw error_message("");
// The news message must be updated if either it has never been updated,
// or if the time of the last update was more than news_frequency ago.
bool download = UserConfigParams::m_news_last_updated==0 ||
UserConfigParams::m_news_last_updated
+UserConfigParams::m_news_frequency
< StkTime::getTimeSinceEpoch() || forceRefresh;
if(!download)
{
// If there is no old news message file, force a new download
std::string xml_file = file_manager->getAddonsFile("news.xml");
if(!file_manager->fileExists(xml_file))
download=true;
}
// Initialise the online portion of the addons manager.
if(download && UserConfigParams::logAddons())
Log::info("addons", "Downloading list.");
Request r(Request::HC_DOWNLOAD_FILE, 9999, false,
"news.xml", "news.xml");
CURLcode status = download ? downloadFileInternal(&r)
: CURLE_OK;
if(download &&
status==CURLE_COULDNT_RESOLVE_HOST)
{
// Assume that the server address is wrong. And retry
// with the default server address again (just in case
// that a redirect went wrong, or a wrong/incorrect
// address somehow made its way into the config file.
UserConfigParams::m_server_addons.revertToDefaults();
status = downloadFileInternal(&r);
}
if(status==CURLE_OK)
{
std::string xml_file = file_manager->getAddonsFile("news.xml");
if(download)
UserConfigParams::m_news_last_updated = StkTime::getTimeSinceEpoch();
const XMLNode *xml = new XMLNode(xml_file);
// A proper news file has at least a version number, mtime, and
// frequency defined. If this is not the case, assume that
// it's an invalid download. Try downloading again after
// resetting the news server back to the default.
int version=-1;
if( !xml->get("version", &version) || version!=1 ||
!xml->get("mtime", &version) ||
!xml->get("frequency", &version) )
{
UserConfigParams::m_server_addons.revertToDefaults();
status = downloadFileInternal(&r);
if(status==CURLE_OK)
UserConfigParams::m_news_last_updated =
StkTime::getTimeSinceEpoch();
delete xml;
xml = new XMLNode(xml_file);
}
news_manager->init();
#ifdef xx
status = loadAddonsList(xml, xml_file, forceRefresh);
delete xml;
if(status==CURLE_OK)
{
return status;
}
else
{
// This message must be translated dynamically in the main menu.
// If it would be translated here, it wouldn't be translated
// if the language is changed in the menu!
error_message=
N_("Can't download addons list, check terminal for details.");
}
// Now fall through to error handling.
}
else
{
// This message must be translated dynamically in the main menu.
// If it would be translated here, it wouldn't be translated
// if the language is changed in the menu!
error_message=
N_("Can't download news file, check terminal for details.");
}
// Abort requested by stk -> display no error message and return
// Abort requested by stk -> display no error message and return
if(status==CURLE_ABORTED_BY_CALLBACK)
return status;
addons_manager->setErrorState();
news_manager->setErrorMessage(error_message);
if(UserConfigParams::logAddons())
Log::error("addons", "Error raised in NetworkHttp::init : %s", core::stringc(error_message).c_str());
return status;
#endif
return CURLE_OK;
} // init
// ---------------------------------------------------------------------------
@ -422,7 +339,7 @@ CURLcode NetworkHttp::loadAddonsList(const XMLNode *xml,
if(addon_list_url.size()==0)
{
file_manager->removeFile(filename);
news_manager->addNewsMessage(_("Can't access stkaddons server..."));
NewsManager::get()->addNewsMessage(_("Can't access stkaddons server..."));
// Use a curl error code here:
return CURLE_COULDNT_CONNECT;
}
@ -451,7 +368,7 @@ CURLcode NetworkHttp::loadAddonsList(const XMLNode *xml,
if(download)
UserConfigParams::m_addons_last_updated=StkTime::getTimeSinceEpoch();
const XMLNode *xml = new XMLNode(xml_file);
addons_manager->initOnline(xml);
// addons_manager->initOnline(xml);
if(UserConfigParams::logAddons())
Log::info("addons", "Addons manager list downloaded");
return status;

View File

@ -66,9 +66,6 @@ private:
static void *mainLoop(void *obj);
CURLcode init(bool forceRefresh);
CURLcode loadAddonsList(const XMLNode *xml,
const std::string &filename,
bool forceRefresh);
CURLcode downloadFileInternal(Request *request);
static int progressDownload(void *clientp, double dltotal, double dlnow,
double ultotal, double ulnow);
@ -85,6 +82,9 @@ public:
int priority = 1,
bool manage_memory=true);
void cancelAllDownloads();
CURLcode loadAddonsList(const XMLNode *xml,
const std::string &filename,
bool forceRefresh);
}; // NetworkHttp
#endif

View File

@ -19,6 +19,7 @@
#include "config/user_config.hpp"
#include "io/file_manager.hpp"
#include "online/http_request.hpp"
#include "states_screens/addons_screen.hpp"
#include "states_screens/main_menu_screen.hpp"
#include "utils/string_utils.hpp"
@ -27,13 +28,14 @@
#include <iostream>
NewsManager *news_manager=NULL;
NewsManager *NewsManager::m_news_manager=NULL;
// ----------------------------------------------------------------------------
NewsManager::NewsManager() : m_news(std::vector<NewsMessage>())
{
m_current_news_message = -1;
m_error_message = "";
m_error_message.setAtomic("");
init(false);
} // NewsManage
// ---------------------------------------------------------------------------
@ -41,6 +43,149 @@ NewsManager::~NewsManager()
{
} // ~NewsManager
// ---------------------------------------------------------------------------
/** This function initialises the data for the news manager. If necessary,
* it will use a separate thread to download the news.xml file.
*/
void NewsManager::init(bool force_refresh)
{
// The rest (which potentially involves downloading news.xml) is handled
// in a separate thread, so that the GUI remains responsive.
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// Should be the default, but just in case:
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
//pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
pthread_t thread_id;
int error = pthread_create(&thread_id, &attr,
&NewsManager::downloadNews, this);
if(error)
{
Log::warn("news", "Could not create thread, error=%d", errno);
// In this case just execute the downloading code with this thread
downloadNews(this);
}
pthread_attr_destroy(&attr);
} //init
// ---------------------------------------------------------------------------
/** This function submits request which will download the news.xml file
* if necessary. It is running in its own thread, so we can use blocking
* download calls without blocking the GUI.
* \param obj This is 'this' object, passed on during pthread creation.
*/
void* NewsManager::downloadNews(void *obj)
{
bool force_refresh = false;
NewsManager *me = (NewsManager*)obj;
me->clearErrorMessage();
std::string xml_file = file_manager->getAddonsFile("news.xml");
bool news_exists = file_manager->fileExists(xml_file);
// The news message must be updated if either it has never been updated,
// or if the time of the last update was more than news_frequency ago,
// or because a 'refresh' was explicitly requested by the user, or no
// news.xml file exists.
bool download = UserConfigParams::m_news_last_updated==0 ||
UserConfigParams::m_news_last_updated
+UserConfigParams::m_news_frequency
< StkTime::getTimeSinceEpoch() ||
force_refresh ||
!news_exists;
const XMLNode *xml = NULL;
if(!download)
{
// If (so far) we don't need to download, there should be an existing
// file. Try to read this, and do some basic checks
xml = new XMLNode(xml_file);
// A proper news file has at least a version number, mtime, frequency
// and an include node (which contains addon data) defined. If this is
// not the case, assume that it is an invalid download, or a corrupt
// local file. Try downloading again after resetting the news server
// back to the default.
int version=-1;
if( !xml->get("version", &version) || version!=1 ||
!xml->get("mtime", &version) ||
!xml->getNode("include") ||
!xml->get("frequency", &version) )
{
delete xml;
xml = NULL;
download = true;
} // if xml not consistemt
} // if !download
if(download)
{
core::stringw error_message("");
Online::HTTPRequest *download_req = new Online::HTTPRequest("news.xml");
download_req->setAddonsURL("news.xml");
// Initialise the online portion of the addons manager.
if(UserConfigParams::logAddons())
Log::info("addons", "Downloading news.");
download_req->executeNow();
if(download_req->hadDownloadError())
{
// Assume that the server address is wrong. And retry
// with the default server address again (just in case
// that a redirect went wrong, or a wrong/incorrect
// address somehow made its way into the config file.
delete download_req;
// We need a new object, since the state of the old
// download request is now done.
download_req = new Online::HTTPRequest("news.xml");
UserConfigParams::m_server_addons.revertToDefaults();
// make sure the new server address is actually used
download_req->setAddonsURL("news.xml");
download_req->executeNow();
if(download_req->hadDownloadError())
{
// This message must be translated dynamically in the main menu.
// If it would be translated here, it wouldn't be translated
// if the language is changed in the menu!
error_message = N_("Error downloading news: '%s'.");
const char *const curl_error = download_req->getDownloadErrorMessage();
error_message = StringUtils::insertValues(error_message, curl_error);
addons_manager->setErrorState();
me->setErrorMessage(error_message);
Log::error("news", core::stringc(error_message).c_str());
} // hadDownloadError
} // hadDownloadError
if(!download_req->hadDownloadError())
UserConfigParams::m_news_last_updated = StkTime::getTimeSinceEpoch();
delete download_req;
// No download error, update the last_updated time value, and delete
// the potentially loaded xml file
delete xml;
xml = NULL;
} // hadDownloadError
// Process new.xml now.
if(file_manager->fileExists(xml_file))
{
xml = new XMLNode(xml_file);
me->checkRedirect(xml);
me->updateNews(xml, xml_file);
addons_manager->init(xml, force_refresh);
delete xml;
}
pthread_exit(NULL);
return 0; // prevent warning
} // downloadNews
// ---------------------------------------------------------------------------
/** Initialises the online part of the network manager. It downloads the
* news.xml file from the server (if the frequency of downloads makes this
@ -48,16 +193,6 @@ NewsManager::~NewsManager()
* \return 0 if an error happened and no online connection will be available,
* 1 otherwise.
*/
void NewsManager::init()
{
UserConfigParams::m_news_last_updated = StkTime::getTimeSinceEpoch();
std::string xml_file = file_manager->getAddonsFile("news.xml");
const XMLNode *xml = new XMLNode(xml_file);
checkRedirect(xml);
updateNews(xml, xml_file);
delete xml;
} // init
// ---------------------------------------------------------------------------
/** Checks if a redirect is received, causing a new server to be used for
@ -210,8 +345,8 @@ const core::stringw NewsManager::getImportantMessage()
const core::stringw NewsManager::getNextNewsMessage()
{
// Only display error message in case of a problem.
if(m_error_message.size()>0)
return _(m_error_message.c_str());
if(m_error_message.getAtomic().size()>0)
return _(m_error_message.getAtomic().c_str());
m_news.lock();
if(m_all_news_messages.size()>0)

View File

@ -35,6 +35,8 @@ class XMLNode;
class NewsManager
{
private:
static NewsManager *m_news_manager;
// A wrapper class to store news message together with
// a message id and a display count.
class NewsMessage
@ -87,28 +89,50 @@ private:
/** A high priority error message that is shown instead of
* any news message (usually indicating connection problems). */
core::stringw m_error_message;
Synchronised<core::stringw> m_error_message;
void checkRedirect(const XMLNode *xml);
void updateNews(const XMLNode *xml,
const std::string &filename);
bool conditionFulfilled(const std::string &cond);
static void* downloadNews(void *obj);
NewsManager();
~NewsManager();
public:
NewsManager();
~NewsManager();
/** Singleton: if necessary create and get the news managers */
static NewsManager* get()
{
if(!m_news_manager)
m_news_manager = new NewsManager();
return m_news_manager;
} //
// ------------------------------------------------------------------------
static void deallocate()
{
if(m_news_manager)
{
delete m_news_manager;
m_news_manager = NULL;
}
} // deallocate
// ------------------------------------------------------------------------
const core::stringw
getNextNewsMessage();
const core::stringw
getImportantMessage();
void init();
void init(bool force_refresh);
void addNewsMessage(const core::stringw &s);
// ------------------------------------------------------------------------
/** Sets an error message that is displayed instead of any news message. */
void setErrorMessage(const core::stringw &s) { m_error_message=s;}
void setErrorMessage(const core::stringw &s)
{
m_error_message.setAtomic(s);
} // setErrorMessage
// ------------------------------------------------------------------------
/** Clears the error message. */
void clearErrorMessage() {m_error_message=""; }
void clearErrorMessage() {m_error_message.setAtomic(""); }
// ------------------------------------------------------------------------
}; // NewsManager

View File

@ -1018,10 +1018,10 @@ void initRest()
// This only initialises the non-network part of the addons manager. The
// online section of the addons manager will be initialised from a
// separate thread running in network http.
news_manager = new NewsManager();
addons_manager = new AddonsManager();
INetworkHttp::create();
NewsManager::get(); // this will create the news manager
// Note that the network thread must be started after the assignment
// to network_http (since the thread might use network_http, otherwise
@ -1099,7 +1099,7 @@ static void cleanSuperTuxKart()
if(ReplayPlay::get()) ReplayPlay::destroy();
if(race_manager) delete race_manager;
INetworkHttp::destroy();
if(news_manager) delete news_manager;
NewsManager::deallocate();
if(addons_manager) delete addons_manager;
NetworkManager::kill();
@ -1252,7 +1252,7 @@ int main(int argc, char *argv[] )
std::string xml_file = file_manager->getAddonsFile("addons.xml");
if (file_manager->fileExists(xml_file)) {
const XMLNode *xml = new XMLNode (xml_file);
addons_manager->initOnline(xml);
addons_manager->initAddons(xml);
}
}
@ -1354,7 +1354,7 @@ int main(int argc, char *argv[] )
// If an important news message exists it is shown in a popup dialog.
const core::stringw important_message =
news_manager->getImportantMessage();
NewsManager::get()->getImportantMessage();
if(important_message!="")
{
new MessageDialog(important_message,

View File

@ -30,32 +30,66 @@
#include <assert.h>
namespace Online{
namespace Online
{
/** Creates a HTTP(S) request that will have a raw string as result. (Can
* of course be used if the result doesn't matter.)
* \param manage_memory whether or not the HTTPManager should take care of
* \param manage_memory whether or not the RequestManager should take care of
* deleting the object after all callbacks have been done.
* \param priority by what priority should the HTTPManager take care of
* \param priority by what priority should the RequestManager take care of
* this request.
*/
HTTPRequest::HTTPRequest(bool manage_memory, int priority)
: Request(manage_memory, priority, 0)
{
m_url = "";
m_string_buffer = "";
m_parameters = new Parameters();
m_progress.setAtomic(0);
init();
} // HTTPRequest
// ------------------------------------------------------------------------
HTTPRequest::~HTTPRequest()
/** This constructor configures this request to save the data in a flie.
* \param filename Name of the file to save the data to.
* \param manage_memory whether or not the RequestManager should take care of
* deleting the object after all callbacks have been done.
* \param priority by what priority should the RequestManager take care of
* this request.
*/
HTTPRequest::HTTPRequest(const std::string &filename, bool manage_memory,
int priority)
: Request(manage_memory, priority, 0)
{
delete m_parameters;
} // ~HTTPRequest
assert(filename.size()>0);
init();
m_filename = file_manager->getAddonsFile(filename);
} // HTTPRequest(filename ...)
// ------------------------------------------------------------------------
/** A handy shortcut that appends the given path to the URL of the server.
/** Char * needs a separate constructor, otherwise it will be considered
* to be the no-filename constructor (char* -> bool).
*/
HTTPRequest::HTTPRequest(const char* const filename, bool manage_memory,
int priority)
: Request(manage_memory, priority, 0)
{
init();
m_filename = file_manager->getAddonsFile(filename);
} // HTTPRequest(filename ...)
// ------------------------------------------------------------------------
/** Initialises all member variables.
*/
void HTTPRequest::init()
{
m_url = "";
m_string_buffer = "";
m_filename = "";
m_parameters = "";
m_curl_code = CURLE_OK;
m_progress.setAtomic(0);
} // init
// ------------------------------------------------------------------------
/** A handy shortcut that appends the given path to the URL of the
* mutiplayer server.
* \param path The path to add to the server.
*/
void HTTPRequest::setServerURL(const std::string& path)
@ -63,6 +97,16 @@ namespace Online{
setURL((std::string)UserConfigParams::m_server_multiplayer+path);
} // setServerURL
// ------------------------------------------------------------------------
/** A handy shortcut that appends the given path to the URL of the addons
* server.
* \param path The path to add to the server.
*/
void HTTPRequest::setAddonsURL(const std::string& path)
{
setURL((std::string)UserConfigParams::m_server_addons
+ "/" + path);
} // set AddonsURL
// ------------------------------------------------------------------------
/** Checks the request if it has enough (correct) information to be
* executed (and thus allowed to add to the queue).
@ -84,10 +128,9 @@ namespace Online{
"LibCurl session not initialized.");
return;
}
curl_easy_setopt(m_curl_session, CURLOPT_URL, m_url.c_str());
curl_easy_setopt(m_curl_session, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(m_curl_session, CURLOPT_WRITEFUNCTION,
&HTTPRequest::writeCallback);
curl_easy_setopt(m_curl_session, CURLOPT_NOPROGRESS, 0);
curl_easy_setopt(m_curl_session, CURLOPT_PROGRESSDATA, this);
curl_easy_setopt(m_curl_session, CURLOPT_PROGRESSFUNCTION,
@ -95,15 +138,17 @@ namespace Online{
curl_easy_setopt(m_curl_session, CURLOPT_CONNECTTIMEOUT, 20);
curl_easy_setopt(m_curl_session, CURLOPT_LOW_SPEED_LIMIT, 10);
curl_easy_setopt(m_curl_session, CURLOPT_LOW_SPEED_TIME, 20);
//https
struct curl_slist *chunk = NULL;
chunk = curl_slist_append(chunk, "Host: api.stkaddons.net");
curl_easy_setopt(m_curl_session, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(m_curl_session, CURLOPT_CAINFO,
(file_manager->getAsset("web.tuxfamily.org.pem")).c_str());
curl_easy_setopt(m_curl_session, CURLOPT_SSL_VERIFYPEER, 0L);
//curl_easy_setopt(m_curl_session, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(m_curl_session, CURLOPT_WRITEDATA, &m_string_buffer);
if(m_filename.size()==0)
{
//https
struct curl_slist *chunk = NULL;
chunk = curl_slist_append(chunk, "Host: api.stkaddons.net");
curl_easy_setopt(m_curl_session, CURLOPT_HTTPHEADER, chunk);
curl_easy_setopt(m_curl_session, CURLOPT_CAINFO,
file_manager->getAsset("web.tuxfamily.org.pem").c_str());
curl_easy_setopt(m_curl_session, CURLOPT_SSL_VERIFYPEER, 0L);
//curl_easy_setopt(m_curl_session, CURLOPT_VERBOSE, 1L);
}
} // prepareOperation
// ------------------------------------------------------------------------
@ -113,28 +158,38 @@ namespace Online{
{
if(!m_curl_session)
return;
Parameters::iterator iter;
std::string postString("");
for (iter = m_parameters->begin(); iter != m_parameters->end(); ++iter)
FILE *fout = NULL;
if(m_filename.size()>0)
{
if(iter != m_parameters->begin())
postString.append("&");
char* escaped = curl_easy_escape(m_curl_session ,
iter->first.c_str(),
iter->first.size());
postString.append(escaped);
curl_free(escaped);
postString.append("=");
escaped = curl_easy_escape(m_curl_session,
iter->second.c_str(),
iter->second.size());
postString.append(escaped);
curl_free(escaped);
fout = fopen((m_filename+".part").c_str(), "wb");
if(!fout)
{
Log::error("HTTPRequest",
"Can't open '%s' for writing, ignored.",
(m_filename+".part").c_str());
return;
}
curl_easy_setopt(m_curl_session, CURLOPT_WRITEDATA, fout );
curl_easy_setopt(m_curl_session, CURLOPT_WRITEFUNCTION, fwrite);
}
Log::info("HTTPRequest::operation", "Sending : %s",
postString.c_str());
else
{
curl_easy_setopt(m_curl_session, CURLOPT_WRITEDATA,
&m_string_buffer);
curl_easy_setopt(m_curl_session, CURLOPT_WRITEFUNCTION,
&HTTPRequest::writeCallback);
}
// All parameters added have a '&' added
if(m_parameters.size()>0)
m_parameters.pop_back();
Log::info("HTTPRequest", "Sending %s to %s",
m_parameters.c_str(), m_url.c_str());
curl_easy_setopt(m_curl_session, CURLOPT_POSTFIELDS,
postString.c_str());
m_parameters.c_str());
std::string uagent( std::string("SuperTuxKart/") + STK_VERSION );
#ifdef WIN32
uagent += (std::string)" (Windows)";
@ -151,6 +206,28 @@ namespace Online{
m_curl_code = curl_easy_perform(m_curl_session);
Request::operation();
if(fout)
{
fclose(fout);
if(m_curl_code==CURLE_OK)
{
if(UserConfigParams::logAddons())
Log::info("HTTPRequest", "Download successful.");
// The behaviour of rename is unspecified if the target
// file should already exist - so remove it.
file_manager->removeFile(m_filename);
int ret = rename((m_filename+".part").c_str(),
m_filename.c_str() );
// In case of an error, set the status to indicate this
if(ret!=0)
{
if(UserConfigParams::logAddons())
Log::error("addons", "Could not rename downloaded file!");
m_curl_code = CURLE_WRITE_ERROR;
}
} // m_curl_code ==CURLE_OK
} // if fout
} // operation
// ------------------------------------------------------------------------
@ -229,4 +306,5 @@ namespace Online{
} // progressDownload
} // namespace Online

View File

@ -39,8 +39,6 @@ namespace Online
class HTTPRequest : public Request
{
private:
typedef std::map<std::string, std::string> Parameters;
/** The progress indicator. 0 untill it is started and the first
* packet is downloaded. Guaranteed to be <1 while the download
* is in progress, it will be set to either -1 (error) or 1
@ -51,7 +49,11 @@ namespace Online
std::string m_url;
/** The POST parameters that will be send with the request. */
Parameters *m_parameters;
std::string m_parameters;
/** Contains a filename if the data should be saved into a file
* instead of being kept in in memory. Otherwise this is "". */
std::string m_filename;
/** Pointer to the curl data structure for this request. */
CURL *m_curl_session;
@ -73,18 +75,35 @@ namespace Online
static size_t writeCallback(void *contents, size_t size,
size_t nmemb, void *userp);
void init();
public :
HTTPRequest(bool manage_memory = false,
int priority = 1);
virtual ~HTTPRequest();
HTTPRequest(const std::string &filename,
bool manage_memory = false,
int priority = 1);
HTTPRequest(const char * const filename,
bool manage_memory = false,
int priority = 1);
virtual ~HTTPRequest() {};
virtual bool isAllowedToAdd() OVERRIDE;
void setServerURL(const std::string& url);
void setAddonsURL(const std::string& path);
// ------------------------------------------------------------------------
/** Returns the curl error status of the request.
/** Returns true if there was an error downloading the file.
*/
CURLcode getResult() const { return m_curl_code; }
bool hadDownloadError() const { return m_curl_code!=CURLE_OK; }
// ------------------------------------------------------------------------
/** Returns the curl error message if an error has occurred.
* \pre m_curl_code!=CURLE_OK
*/
const char* getDownloadErrorMessage() const
{
assert(hadDownloadError());
return curl_easy_strerror(m_curl_code);
} // getDownloadErrorMessage
// ------------------------------------------------------------------------
/** Returns the downloaded string.
* \pre request has to be done
@ -101,8 +120,8 @@ namespace Online
*/
void addParameter(const std::string & name, const std::string &value)
{
assert(isPreparing());
(*m_parameters)[name] = value;
// Call the template, so that the strings are escaped properly
addParameter(name, value.c_str());
}; // addParameter
// --------------------------------------------------------------------
/** Sets a parameter to 'value' (stringw).
@ -110,16 +129,25 @@ namespace Online
void addParameter(const std::string & name,
const irr::core::stringw &value)
{
assert(isPreparing());
(*m_parameters)[name] = irr::core::stringc(value.c_str()).c_str();
core::stringc s = core::stringc(value.c_str());
// Call the template to escape strings properly
addParameter(name, s.c_str());
} // addParameter
// --------------------------------------------------------------------
/** Sets a parameter to 'value' (arbitrary types).
*/
template <typename T>
void addParameter(const std::string & name, const T& value){
void addParameter(const std::string & name, const T& value)
{
assert(isPreparing());
(*m_parameters)[name] = StringUtils::toString(value);
std::string s = StringUtils::toString(value);
char *s1 = curl_easy_escape(m_curl_session, name.c_str(),
name.size() );
char *s2 = curl_easy_escape(m_curl_session, s.c_str(), s.size());
m_parameters.append(std::string(s1)+"="+s2+"&");
curl_free(s1);
curl_free(s2);
} // addParameter
// --------------------------------------------------------------------
/** Returns the current progress. */

View File

@ -46,6 +46,14 @@ namespace Online
m_state.setAtomic(S_PREPARING);
} // Request
// ------------------------------------------------------------------------
/** Inserts this request into the RequestManager's queue for executing.
*/
void Request::queue()
{
RequestManager::get()->addRequest(this);
} // queue
// ------------------------------------------------------------------------
/** Executes the request. This calles prepareOperation, operation, and
* afterOperation.

View File

@ -124,6 +124,7 @@ namespace Online
virtual ~Request() {}
void execute();
void executeNow();
void queue();
// --------------------------------------------------------------------
/** Executed when a request has finished. */
virtual void callback() {}

View File

@ -1,8 +1,8 @@
//
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2010 Lucas Baudin
// 2011 Joerg Henrichs
// 2013 Glenn De Jonghe
// Copyright (C) 2010-2014 Lucas Baudin
// 2011-201 Joerg Henrichs
// 2013-2014 Glenn De Jonghe
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License

View File

@ -61,10 +61,10 @@ namespace Online
void XMLRequest::afterOperation()
{
m_xml_data = file_manager->createXMLTreeFromString(getData());
if(getResult() != CURLE_OK)
if(hadDownloadError())
Log::error("XMLRequest::afterOperation",
"curl_easy_perform() failed: %s",
curl_easy_strerror(getResult()));
getDownloadErrorMessage());
m_success = false;
std::string rec_success;
if(m_xml_data->get("success", &rec_success))

View File

@ -112,7 +112,7 @@ void MainMenuScreen::init()
LabelWidget* w = getWidget<LabelWidget>("info_addons");
const core::stringw &news_text = news_manager->getNextNewsMessage();
const core::stringw &news_text = NewsManager::get()->getNextNewsMessage();
w->setText(news_text, true);
w->update(0.01f);
@ -165,7 +165,7 @@ void MainMenuScreen::onUpdate(float delta, irr::video::IVideoDriver* driver)
w->update(delta);
if(w->scrolledOff())
{
const core::stringw &news_text = news_manager->getNextNewsMessage();
const core::stringw &news_text = NewsManager::get()->getNextNewsMessage();
w->setText(news_text, true);
}
} // onUpdate