Commit 0aed5e4eb3af56ba0bf9ceb0343c262a7427b6ba

Add an asynchronous download class

And use it to download news, updates, and the server list.
  
259259 net/adminhandler.h
260260 net/charhandler.h
261261 net/chathandler.h
262 net/download.cpp
263 net/download.h
262264 net/gamehandler.h
263265 net/generalhandler.h
264266 net/guildhandler.h
  
208208 net/adminhandler.h \
209209 net/charhandler.h \
210210 net/chathandler.h \
211 net/download.cpp \
212 net/download.h \
211213 net/gamehandler.h \
212214 net/generalhandler.h \
213215 net/guildhandler.h \
  
3636
3737#include "net/net.h"
3838
39#include "utils/xml.h"
4039#include "utils/gettext.h"
4140#include "utils/stringutils.h"
41#include "utils/xml.h"
4242
4343#include <cstdlib>
4444#include <iostream>
4545#include <string>
4646
47const short MAX_SERVERLIST = 5;
47#define MAX_SERVERLIST 5
4848
49int ServersListModel::getNumberOfElements()
49ServersListModel::ServersListModel(ServerInfos *servers, ServerDialog *parent):
50 mServers(servers),
51 mParent(parent)
5052{
51 return servers.size();
5253}
5354
54std::string ServersListModel::getElementAt(int elementIndex)
55int ServersListModel::getNumberOfElements()
5556{
56 std::string myServer = servers.at(elementIndex).name;
57 myServer += " (";
58 myServer += std::string(servers.at(elementIndex).hostname);
59 myServer += ":";
60 myServer += toString(servers.at(elementIndex).port);
61 myServer += ")";
62 return myServer;
57 MutexLocker lock = mParent->lock();
58 return mServers->size();
6359}
6460
65void ServersListModel::addFirstElement(const ServerInfo &server)
61std::string ServersListModel::getElementAt(int elementIndex)
6662{
67 // Equivalent to push_front
68 std::vector<ServerInfo>::iterator MyIterator = servers.begin();
69 servers.insert(MyIterator, 1, server);
70}
71
72void ServersListModel::addElement(const ServerInfo &server)
73{
74 servers.push_back(server);
75}
76
77void ServersListModel::mergeElement(const ServerInfo &server)
78{
79 // search through the list
80 for (int i = 0; i < getNumberOfElements(); i++)
63 MutexLocker lock = mParent->lock();
64 ServerInfo server = mServers->at(elementIndex);
65 std::string myServer;
66 if (server.name.empty())
8167 {
82 // the server is already in the list, merge its properties
83 if (servers[i] == server)
84 {
85 servers[i].name = server.name;
86 return;
87 }
68 myServer += server.hostname;
69 myServer += ":";
70 myServer += toString(server.port);
8871 }
89 // the server is not found, add it at the end of the list
90 addElement(server);
91}
92
93bool ServersListModel::contains(const ServerInfo &server)
94{
95 // search through the list
96 for (int i = 0; i < getNumberOfElements(); i++)
72 else
9773 {
98 if (servers[i] == server)
99 return true;
74 myServer += server.name;
75 myServer += " (";
76 myServer += server.hostname;
77 myServer += ":";
78 myServer += toString(server.port);
79 myServer += ")";
10080 }
101 return false;
81 return myServer;
10282}
10383
10484
105ServerDialog::ServerDialog(ServerInfo *serverInfo):
106 Window(_("Choose Your Server")), mServerInfo(serverInfo)
85ServerDialog::ServerDialog(ServerInfo *serverInfo, const std::string &dir):
86 Window(_("Choose Your Server")),
87 mDir(dir),
88 mDownloadStatus(DOWNLOADING_PREPARING),
89 mDownloadProgress(-1.0f),
90 mServers(ServerInfos()),
91 mServerInfo(serverInfo)
10792{
108 mServerDescription = new Label(std::string());
109 gcn::Label *serverLabel = new Label(_("Server:"));
110 gcn::Label *portLabel = new Label(_("Port:"));
93 Label *serverLabel = new Label(_("Server:"));
94 Label *portLabel = new Label(_("Port:"));
11195 mServerNameField = new TextField(mServerInfo->hostname);
11296 mPortField = new TextField(toString(mServerInfo->port));
11397
114 mMostUsedServersListModel = new ServersListModel;
11598 ServerInfo currentServer;
116 ServerInfo tempServer;
117
11899 // Add the most used servers from config if they are not in the online list
119100 std::string currentConfig = "";
120101 for (int i = 0; i <= MAX_SERVERLIST; i++)
110110
111111 if (!currentServer.hostname.empty() && currentServer.port != 0)
112112 {
113 if (!mMostUsedServersListModel->contains(currentServer))
114 mMostUsedServersListModel->addElement(currentServer);
113 mServers.push_back(currentServer);
115114 }
116115 }
117116
118 // load a list with online servers...
119 loadServerlist();
117 mServersListModel = new ServersListModel(&mServers, this);
120118
121 mMostUsedServersList = new ListBox(mMostUsedServersListModel);
122 ScrollArea *usedScroll = new ScrollArea(mMostUsedServersList);
119 mServersList = new ListBox(mServersListModel);
120 ScrollArea *usedScroll = new ScrollArea(mServersList);
123121 usedScroll->setHorizontalScrollPolicy(gcn::ScrollArea::SHOW_NEVER);
124122
123 mDescription = new Label(std::string());
124
125125 mQuitButton = new Button(_("Quit"), "quit", this);
126126 mConnectButton = new Button(_("Connect"), "connect", this);
127127 mManualEntryButton = new Button(_("Add Entry"), "addEntry", this);
132132 mServerNameField->addActionListener(this);
133133 mPortField->addActionListener(this);
134134 mManualEntryButton->addActionListener(this);
135 mMostUsedServersList->addSelectionListener(this);
136 mMostUsedServersList->setSelected(0);
135 mServersList->addSelectionListener(this);
136 mServersList->setSelected(0);
137137 usedScroll->setVerticalScrollAmount(0);
138138
139 place(0, 0, mServerDescription, 2);
140 place(0, 1, serverLabel);
141 place(0, 2, portLabel);
142 place(1, 1, mServerNameField, 3).setPadding(3);
143 place(1, 2, mPortField, 3).setPadding(3);
144 place(0, 3, usedScroll, 4, 5).setPadding(3);
139 place(0, 0, serverLabel);
140 place(1, 0, mServerNameField, 3).setPadding(3);
141 place(0, 1, portLabel);
142 place(1, 1, mPortField, 3).setPadding(3);
143 place(0, 2, usedScroll, 4, 5).setPadding(3);
144 place(0, 7, mDescription, 4);
145145 place(0, 8, mManualEntryButton);
146146 place(2, 8, mQuitButton);
147147 place(3, 8, mConnectButton);
166166 }
167167 }
168168
169 downloadServerList();
169170}
170171
171172ServerDialog::~ServerDialog()
172173{
173 delete mMostUsedServersListModel;
174 delete mServersListModel;
174175}
175176
176177void ServerDialog::action(const gcn::ActionEvent &event)
209209 // now add the rest of the list...
210210 std::string currentConfig = "";
211211 int configCount = 1;
212 for (int i = 0; i < mMostUsedServersListModel->getNumberOfElements(); i++)
212 for (int i = 0; i < mServersListModel->getNumberOfElements(); i++)
213213 {
214 tempServer = mMostUsedServersListModel->getServer(i);
214 tempServer = mServersListModel->getServer(i);
215215
216216 // ensure, that our server will not be added twice
217217 if (tempServer != currentServer)
244244
245245void ServerDialog::valueChanged(const gcn::SelectionEvent &event)
246246{
247 const int index = mMostUsedServersList->getSelected();
247 const int index = mServersList->getSelected();
248248 if (index == -1)
249249 return;
250250
251251 // Update the server and post fields according to the new selection
252 const ServerInfo myServer = mMostUsedServersListModel->getServer(index);
253 mServerDescription->setCaption(myServer.name);
252 const ServerInfo myServer = mServersListModel->getServer(index);
253 mDescription->setCaption(myServer.name);
254254 mServerNameField->setText(myServer.hostname);
255255 mPortField->setText(toString(myServer.port));
256256
257257 setFieldsReadOnly(true);
258258}
259259
260void ServerDialog::loadServerlist()
260void ServerDialog::logic()
261261{
262 ServerInfo currentServer;
263 currentServer.clear();
262 {
263 MutexLocker lock(&mMutex);
264 if (mDownloadStatus == DOWNLOADING_COMPLETE)
265 {
266 mDownloadStatus = DOWNLOADING_OVER;
264267
268 mDescription->setCaption(std::string());
269 }
270 else if (mDownloadStatus == DOWNLOADING_IN_PROGRESS)
271 {
272 mDescription->setCaption(strprintf(_("Downloading server list..."
273 "%2.2f%%"),
274 mDownloadProgress * 100));
275 }
276 else if (mDownloadStatus == DOWNLOADING_IDLE)
277 {
278 mDescription->setCaption(_("Waiting for server..."));
279 }
280 else if (mDownloadStatus == DOWNLOADING_PREPARING)
281 {
282 mDescription->setCaption(_("Preparing download"));
283 }
284 }
285
286 Window::logic();
287}
288
289void ServerDialog::setFieldsReadOnly(const bool readOnly)
290{
291 if (readOnly)
292 {
293 mServerNameField->setEnabled(false);
294 mPortField->setEnabled(false);
295 mManualEntryButton->setVisible(true);
296 mDescription->setVisible(true);
297 }
298 else
299 {
300 mManualEntryButton->setVisible(false);
301
302 mDescription->setVisible(false);
303 mDescription->setCaption(std::string());
304 mServersList->setSelected(-1);
305
306 mServerNameField->setText(std::string());
307 mServerNameField->setEnabled(true);
308
309 mPortField->setText(toString(DEFAULT_PORT));
310 mPortField->setEnabled(true);
311
312 mServerNameField->requestFocus();
313 }
314}
315
316void ServerDialog::downloadServerList()
317{
265318 // try to load the configuration value for the onlineServerList
266319 std::string listFile = config.getValue("onlineServerList", "void");
267320 // if there is no entry, try to load the file from the default updatehost
322322 listFile = config.getValue("updatehost", "http://updates.themanaworld.org")
323323 + "/serverlist.xml";
324324
325 xmlDocPtr doc = xmlReadFile(listFile.c_str(), NULL, 0);
326 if (doc == NULL)
327 {
328 logger->log("Failed to load online serverlist from %s", listFile.c_str());
329 return;
330 }
325 mDownload = new Net::Download(this, listFile, &downloadUpdate);
326 mDownload->setFile(mDir + "/serverlist.xml");
327 mDownload->start();
328}
331329
332 xmlNodePtr rootNode = xmlDocGetRootElement(doc);
333 int version = XML::getProperty(rootNode, "version", 3);
330void ServerDialog::loadServers()
331{
332 ServerInfo currentServer;
334333
335 if (version != 1)
336 {
337 logger->log("Online server list has wrong version");
338 return;
339 }
334 xmlDocPtr doc = xmlReadFile((mDir + "/serverlist.xml").c_str(), NULL, 0);
340335
341 for_each_xml_child_node(server, rootNode)
336 if (doc != NULL)
342337 {
343 if (xmlStrEqual(server->name, BAD_CAST "server"))
338 xmlNodePtr rootNode = xmlDocGetRootElement(doc);
339 int version = XML::getProperty(rootNode, "version", 3);
340
341 if (version != 1)
344342 {
345 //check wether the version matches
346 #ifdef TMWSERV_SUPPORT
347 if (XML::getProperty(server, "type", "unknown") != "TMWSERV")
348 continue;
349 #endif
343 logger->log("Online server list has wrong version");
344 return;
345 }
350346
351 #ifdef EATHENA_SUPPORT
352 if (XML::getProperty(server, "type", "unknown") != "EATHENA")
353 continue;
354 #endif
347 for_each_xml_child_node(server, rootNode)
348 {
349 if (xmlStrEqual(server->name, BAD_CAST "server"))
350 {
351 //check wether the version matches
352 #ifdef TMWSERV_SUPPORT
353 if (XML::getProperty(server, "type", "unknown") != "TMWSERV")
354 continue;
355 #endif
355356
356 currentServer.clear();
357 currentServer.name = XML::getProperty(server, "name", std::string());
357 #ifdef EATHENA_SUPPORT
358 if (XML::getProperty(server, "type", "unknown") != "EATHENA")
359 continue;
360 #endif
358361
359 for_each_xml_child_node(subnode, server)
360 {
361 if (xmlStrEqual(subnode->name, BAD_CAST "connection"))
362 currentServer.clear();
363 currentServer.name = XML::getProperty(server, "name", std::string());
364
365 for_each_xml_child_node(subnode, server)
362366 {
363 currentServer.hostname = XML::getProperty(subnode, "hostname", std::string());
364 currentServer.port = XML::getProperty(subnode, "port", DEFAULT_PORT);
367 if (xmlStrEqual(subnode->name, BAD_CAST "connection"))
368 {
369 currentServer.hostname = XML::getProperty(subnode, "hostname", std::string());
370 currentServer.port = XML::getProperty(subnode, "port", DEFAULT_PORT);
371 }
365372 }
366 }
367373
368 // merge the server into the local list
369 mMostUsedServersListModel->mergeElement(currentServer);
374
375 MutexLocker lock(&mMutex);
376 // add the server to the local list (if it's not already present)
377 ServerInfos::iterator it;
378 bool found = false;
379 for (it = mServers.begin(); it != mServers.end(); it++)
380 {
381 if ((*it) == currentServer)
382 {
383 (*it).name = currentServer.name;
384 found = true;
385 break;
386 }
387 }
388
389 if (!found)
390 mServers.push_back(currentServer);
391 }
370392 }
393
394 xmlFreeDoc(doc);
371395 }
372396
373 xmlFreeDoc(doc);
397 MutexLocker lock(&mMutex);
398 mDownloadStatus = DOWNLOADING_COMPLETE;
374399}
375400
376void ServerDialog::setFieldsReadOnly(const bool readOnly)
401int ServerDialog::downloadUpdate(void *ptr, DownloadStatus status,
402 size_t total, size_t remaining)
377403{
378 if (readOnly)
404 ServerDialog *sd = reinterpret_cast<ServerDialog*>(ptr);
405 bool finished = false;
406
407 if (status == DOWNLOAD_STATUS_COMPLETE)
379408 {
380 mServerNameField->setEnabled(false);
381 mPortField->setEnabled(false);
382 mManualEntryButton->setVisible(true);
383 mServerDescription->setVisible(true);
409 finished = true;
384410 }
411 else if (status < 0)
412 {
413 logger->log("Error retreiving server list: %s\n",
414 sd->mDownload->getError());
415
416 finished = true;
417 }
385418 else
386419 {
387 mManualEntryButton->setVisible(false);
420 float progress = (float) remaining / total;
388421
389 mServerDescription->setVisible(false);
390 mServerDescription->setCaption(std::string());
391 mMostUsedServersList->setSelected(-1);
422 if (progress != progress) progress = 0.0f; // check for NaN
423 if (progress < 0.0f) progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway.
424 if (progress > 1.0f) progress = 1.0f;
392425
393 mServerNameField->setText(std::string());
394 mServerNameField->setEnabled(true);
426 MutexLocker lock(&sd->mMutex);
427 sd->mDownloadStatus = DOWNLOADING_IN_PROGRESS;
428 sd->mDownloadProgress = progress;
429 }
395430
396 mPortField->setText(toString(DEFAULT_PORT));
397 mPortField->setEnabled(true);
398
399 mServerNameField->requestFocus();
431 if (finished)
432 {
433 sd->loadServers();
400434 }
435
436 return 0;
401437}
  
2424
2525#include "gui/widgets/window.h"
2626
27#include "guichanfwd.h"
28
27#include "net/download.h"
2928#include "net/serverinfo.h"
3029
30#include "utils/mutex.h"
31
3132#include <guichan/actionlistener.hpp>
3233#include <guichan/listmodel.hpp>
3334#include <guichan/selectionlistener.hpp>
3636#include <string>
3737#include <vector>
3838
39class Button;
40class Label;
3941class ListBox;
42class ServerDialog;
43class TextField;
4044
4145/**
4246 * Server and Port List Model
4848class ServersListModel : public gcn::ListModel
4949{
5050 public:
51 ServersListModel(ServerInfos *servers, ServerDialog *parent);
52
5153 /**
5254 * Used to get number of line in the list
5355 */
6464 * Used to get the corresponding Server struct
6565 */
6666 ServerInfo getServer(int elementIndex) const
67 { return servers[elementIndex]; }
67 { return mServers->at(elementIndex); }
6868
69 /**
70 * Add an Element at the end of the server list
71 */
72 void addElement(const ServerInfo &server);
73
74 /**
75 * Add an Element at the end of the server list if it
76 * doesn't exist yet. Otherwise overwrite its properties
77 * in the list.
78 *
79 * @param server ServerInfo to merge into the list.
80 */
81 void mergeElement(const ServerInfo &server);
82
83 /**
84 * Add an Element at the beginning of the server list
85 */
86 void addFirstElement(const ServerInfo &server);
87
88 /**
89 * Returns wheter the given server is already in the list.
90 * @param server Server to search in the list.
91 * @return True, if the server is in the list, false otherwise.
92 */
93 bool contains(const ServerInfo &server);
94
9569 private:
96 std::vector<ServerInfo> servers;
70 ServerInfos *mServers;
71 ServerDialog *mParent;
9772};
9873
9974/**
8686 *
8787 * @see Window::Window
8888 */
89 ServerDialog(ServerInfo *serverInfo);
89 ServerDialog(ServerInfo *serverInfo, const std::string &dir);
9090
9191 /**
9292 * Destructor
103103 */
104104 void valueChanged(const gcn::SelectionEvent &event);
105105
106 void logic();
107
108 protected:
109 friend class ServersListModel;
110 MutexLocker lock() { return MutexLocker(&mMutex); }
111
106112 private:
107113 /**
108114 * Called to load a list of available server from an online xml file.
109115 */
110 void loadServerlist();
116 void downloadServerList();
117 void loadServers();
118 static int downloadUpdate(void *ptr, DownloadStatus status,
119 size_t total, size_t remaining);
111120
112121 void setFieldsReadOnly(const bool readOnly);
113122
114 gcn::TextField *mServerNameField;
115 gcn::TextField *mPortField;
116 gcn::Label *mServerDescription;
117 gcn::Button *mQuitButton;
118 gcn::Button *mConnectButton;
119 gcn::Button *mManualEntryButton;
123 TextField *mServerNameField;
124 TextField *mPortField;
125 Label *mDescription;
126 Button *mQuitButton;
127 Button *mConnectButton;
128 Button *mManualEntryButton;
120129
121 ListBox *mMostUsedServersList;
122 ServersListModel *mMostUsedServersListModel;
130 ListBox *mServersList;
131 ServersListModel *mServersListModel;
123132
133 const std::string &mDir;
134
135 enum ServerDialogDownloadStatus
136 {
137 DOWNLOADING_ERROR,
138 DOWNLOADING_PREPARING,
139 DOWNLOADING_IDLE,
140 DOWNLOADING_IN_PROGRESS,
141 DOWNLOADING_COMPLETE,
142 DOWNLOADING_OVER
143 };
144
145 /** Status of the current download. */
146 ServerDialogDownloadStatus mDownloadStatus;
147
148 Net::Download *mDownload;
149
150 Mutex mMutex;
151 float mDownloadProgress;
152
153 ServerInfos mServers;
124154 ServerInfo *mServerInfo;
125155};
126156
  
3232#include "log.h"
3333#include "main.h"
3434
35#include "net/download.h"
36
3537#include "resources/resourcemanager.h"
3638
3739#include "utils/gettext.h"
3840#include "utils/stringutils.h"
3941
4042#include <iostream>
41#include <SDL.h>
42#include <SDL_thread.h>
43#include <zlib.h>
4443
45#include <curl/curl.h>
46
4744/**
48 * Calculates the Alder-32 checksum for the given file.
49 */
50static unsigned long fadler32(FILE *file)
51{
52 // Obtain file size
53 fseek(file, 0, SEEK_END);
54 long fileSize = ftell(file);
55 rewind(file);
56
57 // Calculate Adler-32 checksum
58 char *buffer = (char*) malloc(fileSize);
59 const size_t read = fread(buffer, 1, fileSize, file);
60 unsigned long adler = adler32(0L, Z_NULL, 0);
61 adler = adler32(adler, (Bytef*) buffer, read);
62 free(buffer);
63
64 return adler;
65}
66
67/**
6845 * Load the given file into a vector of strings.
6946 */
7047std::vector<std::string> loadTextFile(const std::string &fileName)
6666UpdaterWindow::UpdaterWindow(const std::string &updateHost,
6767 const std::string &updatesDir):
6868 Window(_("Updating...")),
69 mThread(NULL),
7069 mDownloadStatus(UPDATE_NEWS),
7170 mUpdateHost(updateHost),
7271 mUpdatesDir(updatesDir),
7372 mCurrentFile("news.txt"),
73 mDownloadProgress(0.0f),
7474 mCurrentChecksum(0),
7575 mStoreInMemory(true),
7676 mDownloadComplete(true),
7777 mUserCancel(false),
7878 mDownloadedBytes(0),
7979 mMemoryBuffer(NULL),
80 mCurlError(new char[CURL_ERROR_SIZE]),
80 mDownload(NULL),
8181 mLineIndex(0)
8282{
83 mCurlError[0] = 0;
84
8583 mBrowserBox = new BrowserBox;
8684 mScrollArea = new ScrollArea(mBrowserBox);
8785 mLabel = new Label(_("Connecting..."));
8787 mCancelButton = new Button(_("Cancel"), "cancel", this);
8888 mPlayButton = new Button(_("Play"), "play", this);
8989
90 mProgressBar->setSmoothProgress(false);
9091 mBrowserBox->setOpaque(false);
9192 mPlayButton->setEnabled(false);
9293
115115
116116UpdaterWindow::~UpdaterWindow()
117117{
118 if (mThread)
119 SDL_WaitThread(mThread, NULL);
120
121118 free(mMemoryBuffer);
122
123 // Remove possibly leftover temporary download
124 ::remove((mUpdatesDir + "/download.temp").c_str());
125
126 delete[] mCurlError;
127119}
128120
129121void UpdaterWindow::setProgress(float p)
130122{
131 mProgressBar->setProgress(p);
123 // Do delayed progress bar update, since Guichan isn't thread-safe
124 MutexLocker lock(&mDownloadMutex);
125 mDownloadProgress = p;
132126}
133127
134128void UpdaterWindow::setLabel(const std::string &str)
135129{
136130 // Do delayed label text update, since Guichan isn't thread-safe
137 MutexLocker lock(&mLabelMutex);
131 MutexLocker lock(&mDownloadMutex);
138132 mNewLabelCaption = str;
139133}
140134
148148 // Skip the updating process
149149 if (mDownloadStatus != UPDATE_COMPLETE)
150150 {
151 mDownload->cancel();
151152 mDownloadStatus = UPDATE_ERROR;
152153 }
153154 }
187187 mScrollArea->setVerticalScrollAmount(0);
188188}
189189
190int UpdaterWindow::updateProgress(void *ptr,
191 double dt, double dn, double ut, double un)
190int UpdaterWindow::updateProgress(void *ptr, DownloadStatus status,
191 size_t dt, size_t dn)
192192{
193 float progress = dn / dt;
194193 UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(ptr);
195194
195 if (status == DOWNLOAD_STATUS_COMPLETE)
196 {
197 uw->mDownloadComplete = true;
198 }
199 else if (status == DOWNLOAD_STATUS_ERROR ||
200 status == DOWNLOAD_STATUS_CANCELLED)
201 {
202 uw->mDownloadStatus = UPDATE_ERROR;
203 }
204
205 float progress = (float) dn / dt;
206
196207 if (progress != progress) progress = 0.0f; // check for NaN
197208 if (progress < 0.0f) progress = 0.0f; // no idea how this could ever happen, but why not check for it anyway.
198209 if (progress > 1.0f) progress = 1.0f;
221221 return 0;
222222}
223223
224size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
224size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, void *stream)
225225{
226226 UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(stream);
227227 size_t totalMem = size * nmemb;
236236 return totalMem;
237237}
238238
239int UpdaterWindow::downloadThread(void *ptr)
239void UpdaterWindow::download()
240240{
241 int attempts = 0;
242 UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(ptr);
243 CURL *curl;
244 CURLcode res;
245 std::string outFilename;
246 std::string url(uw->mUpdateHost + "/" + uw->mCurrentFile);
241 mDownload = new Net::Download(this, mUpdateHost + "/" + mCurrentFile,
242 updateProgress);
247243
248 while (attempts < 3 && !uw->mDownloadComplete)
244 if (mStoreInMemory)
249245 {
250 FILE *outfile = NULL;
251 FILE *newfile = NULL;
252 uw->setLabel(uw->mCurrentFile + " (0%)");
253
254 curl = curl_easy_init();
255
256 if (curl)
246 mDownload->setWriteFunction(UpdaterWindow::memoryWrite);
247 }
248 else
249 {
250 if (mDownloadStatus == UPDATE_RESOURCES)
257251 {
258 logger->log("Downloading: %s", url.c_str());
259
260 if (uw->mStoreInMemory)
261 {
262 uw->mDownloadedBytes = 0;
263 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
264 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
265 UpdaterWindow::memoryWrite);
266 curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
267 }
268 else
269 {
270 outFilename = uw->mUpdatesDir + "/download.temp";
271 outfile = fopen(outFilename.c_str(), "w+b");
272 curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
273 }
274
275#ifdef PACKAGE_VERSION
276 curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMW/" PACKAGE_VERSION);
277#else
278 curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMW");
279#endif
280 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, uw->mCurlError);
281 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
282 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
283 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,
284 UpdaterWindow::updateProgress);
285 curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ptr);
286 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
287 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
288
289 struct curl_slist *pHeaders = NULL;
290 if (uw->mDownloadStatus != UPDATE_RESOURCES)
291 {
292 // Make sure the resources2.txt and news.txt aren't cached,
293 // in order to always get the latest version.
294 pHeaders = curl_slist_append(pHeaders, "pragma: no-cache");
295 pHeaders =
296 curl_slist_append(pHeaders, "Cache-Control: no-cache");
297 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pHeaders);
298 }
299
300 if ((res = curl_easy_perform(curl)) != 0)
301 {
302 uw->mDownloadStatus = UPDATE_ERROR;
303 switch (res)
304 {
305 case CURLE_COULDNT_CONNECT:
306 default:
307 logger->log("curl error %d: %s host: %s",
308 res, uw->mCurlError, url.c_str());
309 break;
310 }
311
312 if (!uw->mStoreInMemory)
313 {
314 fclose(outfile);
315 ::remove(outFilename.c_str());
316 }
317 attempts++;
318 continue;
319 }
320
321 curl_easy_cleanup(curl);
322
323 if (uw->mDownloadStatus != UPDATE_RESOURCES)
324 {
325 curl_slist_free_all(pHeaders);
326 }
327
328 if (!uw->mStoreInMemory)
329 {
330 // Don't check resources2.txt checksum
331 if (uw->mDownloadStatus == UPDATE_RESOURCES)
332 {
333 unsigned long adler = fadler32(outfile);
334
335 if (uw->mCurrentChecksum != adler)
336 {
337 fclose(outfile);
338
339 // Remove the corrupted file
340 ::remove(outFilename.c_str());
341 logger->log(
342 "Checksum for file %s failed: (%lx/%lx)",
343 uw->mCurrentFile.c_str(),
344 adler, uw->mCurrentChecksum);
345 attempts++;
346 continue; // Bail out here to avoid the renaming
347 }
348 }
349 fclose(outfile);
350
351 // Give the file the proper name
352 const std::string newName =
353 uw->mUpdatesDir + "/" + uw->mCurrentFile;
354
355 // Any existing file with this name is deleted first, otherwise
356 // the rename will fail on Windows.
357 ::remove(newName.c_str());
358 ::rename(outFilename.c_str(), newName.c_str());
359
360 // Check if we can open it and no errors were encountered
361 // during renaming
362 newfile = fopen(newName.c_str(), "rb");
363 if (newfile)
364 {
365 fclose(newfile);
366 uw->mDownloadComplete = true;
367 }
368 }
369 else
370 {
371 // It's stored in memory, we're done
372 uw->mDownloadComplete = true;
373 }
252 mDownload->setFile(mUpdatesDir + "/" + mCurrentFile, mCurrentChecksum);
374253 }
375 attempts++;
254 else
255 {
256 mDownload->setFile(mUpdatesDir + "/" + mCurrentFile);
257 }
376258 }
377259
378 if (!uw->mDownloadComplete) {
379 uw->mDownloadStatus = UPDATE_ERROR;
260 if (mDownloadStatus != UPDATE_RESOURCES)
261 {
262 mDownload->noCache();
380263 }
381264
382 return 0;
383}
384
385void UpdaterWindow::download()
386{
265 setLabel(mCurrentFile + " (0%)");
387266 mDownloadComplete = false;
388 mThread = SDL_CreateThread(UpdaterWindow::downloadThread, this);
389267
390 if (!mThread)
391 {
392 logger->log("Unable to create mThread");
393 mDownloadStatus = UPDATE_ERROR;
394 }
268 // TODO: check return
269 mDownload->start();
395270}
396271
397272void UpdaterWindow::logic()
276276
277277 // Synchronize label caption when necessary
278278 {
279 MutexLocker lock(&mLabelMutex);
279 MutexLocker lock(&mDownloadMutex);
280280
281281 if (mLabel->getCaption() != mNewLabelCaption)
282282 {
283283 mLabel->setCaption(mNewLabelCaption);
284284 mLabel->adjustSize();
285285 }
286
287 mProgressBar->setProgress(mDownloadProgress);
286288 }
287289
290 std::string filename = mUpdatesDir + "/" + mCurrentFile;
291
288292 switch (mDownloadStatus)
289293 {
290294 case UPDATE_ERROR:
291 if (mThread)
292 {
293 if (mUserCancel) {
294 // Kill the thread, because user has canceled
295 SDL_KillThread(mThread);
296 // Set the flag to false again
297 mUserCancel = false;
298 }
299 else {
300 SDL_WaitThread(mThread, NULL);
301 }
302 mThread = NULL;
303 }
304295 // TODO: Only send complete sentences to gettext
305296 mBrowserBox->addRow("");
306297 mBrowserBox->addRow(_("##1 The update process is incomplete."));
299299 mBrowserBox->addRow(_("##1 It is strongly recommended that"));
300300 // TRANSLATORS: Begins "It is strongly recommended that".
301301 mBrowserBox->addRow(_("##1 you try again later."));
302 mBrowserBox->addRow(mCurlError);
302
303 mBrowserBox->addRow(mDownload->getError());
303304 mScrollArea->setVerticalScrollAmount(
304305 mScrollArea->getVerticalMaxScroll());
305306 mDownloadStatus = UPDATE_COMPLETE;
328328 case UPDATE_RESOURCES:
329329 if (mDownloadComplete)
330330 {
331 if (mThread)
332 {
333 SDL_WaitThread(mThread, NULL);
334 mThread = NULL;
335 }
336
337331 if (mLineIndex < mLines.size())
338332 {
339333 std::stringstream line(mLines[mLineIndex]);
  
2424
2525#include "gui/widgets/window.h"
2626
27#include "net/download.h"
28
2729#include "utils/mutex.h"
2830
2931#include <guichan/actionlistener.hpp>
3838class ProgressBar;
3939class ScrollArea;
4040
41struct SDL_Thread;
42
4341/**
4442 * Update progress window GUI
4543 *
9292 void download();
9393
9494 /**
95 * The thread function that download the files.
95 * A download callback for progress updates.
9696 */
97 static int downloadThread(void *ptr);
97 static int updateProgress(void *ptr, DownloadStatus status,
98 size_t dt, size_t dn);
9899
99100 /**
100 * A libcurl callback for progress updates.
101 */
102 static int updateProgress(void *ptr,
103 double dt, double dn, double ut, double un);
104
105 /**
106101 * A libcurl callback for writing to memory.
107102 */
108103 static size_t memoryWrite(void *ptr, size_t size, size_t nmemb,
109 FILE *stream);
104 void *stream);
110105
111 enum DownloadStatus
106 enum UpdateDownloadStatus
112107 {
113108 UPDATE_ERROR,
114109 UPDATE_IDLE,
113113 UPDATE_RESOURCES
114114 };
115115
116 /** A thread that use libcurl to download updates. */
117 SDL_Thread *mThread;
118
119116 /** Status of the current download. */
120 DownloadStatus mDownloadStatus;
117 UpdateDownloadStatus mDownloadStatus;
121118
122119 /** Host where we get the updated files. */
123120 std::string mUpdateHost;
128128 /** The new label caption to be set in the logic method. */
129129 std::string mNewLabelCaption;
130130
131 /** The mutex used to guard access to mNewLabelCaption. */
132 Mutex mLabelMutex;
131 /** The new progress value to be set in the logic method. */
132 float mDownloadProgress;
133133
134 /** The mutex used to guard access to mNewLabelCaption and mDownloadProgress. */
135 Mutex mDownloadMutex;
136
134137 /** The Adler32 checksum of the file currently downloading. */
135138 unsigned long mCurrentChecksum;
136139
152152 /** Buffer for files downloaded to memory. */
153153 char *mMemoryBuffer;
154154
155 /** Buffer to handler human readable error provided by curl. */
156 char *mCurlError;
155 /** Download handle. */
156 Net::Download *mDownload;
157157
158158 /** List of files to download. */
159159 std::vector<std::string> mLines;
  
926926 loadUpdates();
927927 }
928928
929 printf("State change: %d to %d\n", oldstate, state);
929 //printf("State change: %d to %d\n", oldstate, state);
930930
931931 oldstate = state;
932932
945945 case STATE_CHOOSE_SERVER:
946946 logger->log("State: CHOOSE SERVER");
947947
948 // Don't allow an alpha opacity
949 // lower than the default value
950 SkinLoader::instance()->setMinimumOpacity(0.8f);
951
952948 // Allow changing this using a server choice dialog
953949 // We show the dialog box only if the command-line
954950 // options weren't set.
955951 if (options.serverName.empty() && options.serverPort == 0) {
956 currentDialog = new ServerDialog(&currentServer);
952 // Don't allow an alpha opacity
953 // lower than the default value
954 SkinLoader::instance()->setMinimumOpacity(0.8f);
955
956 currentDialog = new ServerDialog(&currentServer,
957 homeDir);
957958 } else {
958959 state = STATE_CONNECT_SERVER;
959960
  
2222#ifndef LOGINHANDLER_H
2323#define LOGINHANDLER_H
2424
25#include "logindata.h"
26
25#include "net/logindata.h"
2726#include "net/serverinfo.h"
2827#include "net/worldinfo.h"
2928
  
125125namespace Net
126126{
127127 bool networkLoaded = false;
128}
128} // namespace Net
129129
130130void Net::connectToServer(const ServerInfo &server)
131131{
134134
135135 if (networkLoaded)
136136 {
137 Net::getGeneralHandler()->reload();
137 getGeneralHandler()->reload();
138138 }
139139 else
140140 {
145145#endif
146146 }
147147
148 Net::getGeneralHandler()->load();
148 getGeneralHandler()->load();
149149
150150 networkLoaded = true;
151151
152 Net::getLoginHandler()->setServer(server);
152 getLoginHandler()->setServer(server);
153153
154 Net::getLoginHandler()->connect();
154 getLoginHandler()->connect();
155155}
  
1919 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2020 */
2121
22#include "net/serverinfo.h"
23
2422#ifndef NET_H
2523#define NET_H
24
25class ServerInfo;
2626
2727namespace Net {
2828
  
2323#define SERVERINFO_H
2424
2525#include <string>
26#include <vector>
2627
2728class ServerInfo
2829{
4949 return (hostname != other.hostname || port != other.port);
5050 }
5151};
52
53typedef std::vector<ServerInfo> ServerInfos;
5254
5355#endif // SERVERINFO_H
tmw.cbp
(2 / 0)
  
459459 <Unit filename="src/net/adminhandler.h" />
460460 <Unit filename="src/net/charhandler.h" />
461461 <Unit filename="src/net/chathandler.h" />
462 <Unit filename="src/net/download.cpp" />
463 <Unit filename="src/net/download.h" />
462464 <Unit filename="src/net/ea/adminhandler.cpp">
463465 <Option target="eAthena" />
464466 <Option target="UNIX eAthena" />