00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "gui/updatewindow.h"
00023
00024 #include "gui/widgets/browserbox.h"
00025 #include "gui/widgets/button.h"
00026 #include "gui/widgets/label.h"
00027 #include "gui/widgets/layout.h"
00028 #include "gui/widgets/progressbar.h"
00029 #include "gui/widgets/scrollarea.h"
00030
00031 #include "configuration.h"
00032 #include "log.h"
00033 #include "main.h"
00034
00035 #include "resources/resourcemanager.h"
00036
00037 #include "utils/gettext.h"
00038 #include "utils/stringutils.h"
00039
00040 #include <iostream>
00041 #include <SDL.h>
00042 #include <SDL_thread.h>
00043 #include <zlib.h>
00044
00045 #include <curl/curl.h>
00046
00050 static unsigned long fadler32(FILE *file)
00051 {
00052
00053 fseek(file, 0, SEEK_END);
00054 long fileSize = ftell(file);
00055 rewind(file);
00056
00057
00058 char *buffer = (char*) malloc(fileSize);
00059 const size_t read = fread(buffer, 1, fileSize, file);
00060 unsigned long adler = adler32(0L, Z_NULL, 0);
00061 adler = adler32(adler, (Bytef*) buffer, read);
00062 free(buffer);
00063
00064 return adler;
00065 }
00066
00070 std::vector<std::string> loadTextFile(const std::string &fileName)
00071 {
00072 std::vector<std::string> lines;
00073 std::ifstream fin(fileName.c_str());
00074
00075 if (!fin) {
00076 logger->log("Couldn't load text file: %s", fileName.c_str());
00077 return lines;
00078 }
00079
00080 std::string line;
00081
00082 while (getline(fin, line))
00083 lines.push_back(line);
00084
00085 return lines;
00086 }
00087
00088
00089 UpdaterWindow::UpdaterWindow(const std::string &updateHost,
00090 const std::string &updatesDir):
00091 Window(_("Updating...")),
00092 mThread(NULL),
00093 mDownloadStatus(UPDATE_NEWS),
00094 mUpdateHost(updateHost),
00095 mUpdatesDir(updatesDir),
00096 mCurrentFile("news.txt"),
00097 mCurrentChecksum(0),
00098 mStoreInMemory(true),
00099 mDownloadComplete(true),
00100 mUserCancel(false),
00101 mDownloadedBytes(0),
00102 mMemoryBuffer(NULL),
00103 mCurlError(new char[CURL_ERROR_SIZE]),
00104 mLineIndex(0)
00105 {
00106 mCurlError[0] = 0;
00107
00108 mBrowserBox = new BrowserBox;
00109 mScrollArea = new ScrollArea(mBrowserBox);
00110 mLabel = new Label(_("Connecting..."));
00111 mProgressBar = new ProgressBar(0.0, 310, 20, 168, 116, 31);
00112 mCancelButton = new Button(_("Cancel"), "cancel", this);
00113 mPlayButton = new Button(_("Play"), "play", this);
00114
00115 mBrowserBox->setOpaque(false);
00116 mPlayButton->setEnabled(false);
00117
00118 ContainerPlacer place;
00119 place = getPlacer(0, 0);
00120
00121 place(0, 0, mScrollArea, 5, 3).setPadding(3);
00122 place(0, 3, mLabel, 5);
00123 place(0, 4, mProgressBar, 5);
00124 place(3, 5, mCancelButton);
00125 place(4, 5, mPlayButton);
00126
00127 reflowLayout(320, 240);
00128
00129 Layout &layout = getLayout();
00130 layout.setRowHeight(0, Layout::AUTO_SET);
00131
00132 center();
00133 setVisible(true);
00134 mCancelButton->requestFocus();
00135
00136
00137 download();
00138 }
00139
00140 UpdaterWindow::~UpdaterWindow()
00141 {
00142 if (mThread)
00143 SDL_WaitThread(mThread, NULL);
00144
00145 free(mMemoryBuffer);
00146
00147
00148 ::remove((mUpdatesDir + "/download.temp").c_str());
00149
00150 delete[] mCurlError;
00151 }
00152
00153 void UpdaterWindow::setProgress(float p)
00154 {
00155 mProgressBar->setProgress(p);
00156 }
00157
00158 void UpdaterWindow::setLabel(const std::string &str)
00159 {
00160
00161 MutexLocker lock(&mLabelMutex);
00162 mNewLabelCaption = str;
00163 }
00164
00165 void UpdaterWindow::enable()
00166 {
00167 mCancelButton->setEnabled(false);
00168 mPlayButton->setEnabled(true);
00169 mPlayButton->requestFocus();
00170 }
00171
00172 void UpdaterWindow::action(const gcn::ActionEvent &event)
00173 {
00174 if (event.getId() == "cancel")
00175 {
00176
00177 mUserCancel = true;
00178
00179 if (mDownloadStatus != UPDATE_COMPLETE)
00180 {
00181 mDownloadStatus = UPDATE_ERROR;
00182 }
00183 }
00184 else if (event.getId() == "play")
00185 {
00186 state = STATE_LOADDATA;
00187 }
00188 }
00189
00190 void UpdaterWindow::loadNews()
00191 {
00192 if (!mMemoryBuffer)
00193 {
00194 logger->log("Couldn't load news");
00195 return;
00196 }
00197
00198
00199 mMemoryBuffer = (char*)realloc(mMemoryBuffer, mDownloadedBytes + 1);
00200 mMemoryBuffer[mDownloadedBytes] = '\0';
00201
00202 mBrowserBox->clearRows();
00203
00204
00205 char *line = strtok(mMemoryBuffer, "\n");
00206 while (line)
00207 {
00208 mBrowserBox->addRow(line);
00209 line = strtok(NULL, "\n");
00210 }
00211
00212
00213 free(mMemoryBuffer);
00214 mMemoryBuffer = NULL;
00215
00216 mScrollArea->setVerticalScrollAmount(0);
00217 }
00218
00219 int UpdaterWindow::updateProgress(void *ptr,
00220 double dt, double dn, double ut, double un)
00221 {
00222 float progress = dn / dt;
00223 UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(ptr);
00224
00225 if (progress != progress) progress = 0.0f;
00226 if (progress < 0.0f) progress = 0.0f;
00227 if (progress > 1.0f) progress = 1.0f;
00228
00229 uw->setLabel(
00230 uw->mCurrentFile + " (" + toString((int) (progress * 100)) + "%)");
00231 uw->setProgress(progress);
00232
00233 if (state != STATE_UPDATE || uw->mDownloadStatus == UPDATE_ERROR)
00234 {
00235
00236 return -1;
00237 }
00238
00239 return 0;
00240 }
00241
00242 size_t UpdaterWindow::memoryWrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
00243 {
00244 UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(stream);
00245 size_t totalMem = size * nmemb;
00246 uw->mMemoryBuffer = (char*) realloc(uw->mMemoryBuffer,
00247 uw->mDownloadedBytes + totalMem);
00248 if (uw->mMemoryBuffer)
00249 {
00250 memcpy(&(uw->mMemoryBuffer[uw->mDownloadedBytes]), ptr, totalMem);
00251 uw->mDownloadedBytes += totalMem;
00252 }
00253
00254 return totalMem;
00255 }
00256
00257 int UpdaterWindow::downloadThread(void *ptr)
00258 {
00259 int attempts = 0;
00260 UpdaterWindow *uw = reinterpret_cast<UpdaterWindow *>(ptr);
00261 CURL *curl;
00262 CURLcode res;
00263 std::string outFilename;
00264 std::string url(uw->mUpdateHost + "/" + uw->mCurrentFile);
00265
00266 while (attempts < 3 && !uw->mDownloadComplete)
00267 {
00268 FILE *outfile = NULL;
00269 FILE *newfile = NULL;
00270 uw->setLabel(uw->mCurrentFile + " (0%)");
00271
00272 curl = curl_easy_init();
00273
00274 if (curl)
00275 {
00276 logger->log("Downloading: %s", url.c_str());
00277
00278 if (uw->mStoreInMemory)
00279 {
00280 uw->mDownloadedBytes = 0;
00281 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
00282 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
00283 UpdaterWindow::memoryWrite);
00284 curl_easy_setopt(curl, CURLOPT_WRITEDATA, ptr);
00285 }
00286 else
00287 {
00288 outFilename = uw->mUpdatesDir + "/download.temp";
00289 outfile = fopen(outFilename.c_str(), "w+b");
00290 curl_easy_setopt(curl, CURLOPT_WRITEDATA, outfile);
00291 }
00292
00293 #ifdef PACKAGE_VERSION
00294 curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMW/" PACKAGE_VERSION);
00295 #else
00296 curl_easy_setopt(curl, CURLOPT_USERAGENT, "TMW");
00297 #endif
00298 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, uw->mCurlError);
00299 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
00300 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
00301 curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION,
00302 UpdaterWindow::updateProgress);
00303 curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, ptr);
00304 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
00305 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 15);
00306
00307 struct curl_slist *pHeaders = NULL;
00308 if (uw->mDownloadStatus != UPDATE_RESOURCES)
00309 {
00310
00311
00312 pHeaders = curl_slist_append(pHeaders, "pragma: no-cache");
00313 pHeaders =
00314 curl_slist_append(pHeaders, "Cache-Control: no-cache");
00315 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, pHeaders);
00316 }
00317
00318 if ((res = curl_easy_perform(curl)) != 0)
00319 {
00320 uw->mDownloadStatus = UPDATE_ERROR;
00321 switch (res)
00322 {
00323 case CURLE_COULDNT_CONNECT:
00324 default:
00325 std::cerr << _("curl error ") << res << ": "
00326 << uw->mCurlError << _(" host: ") << url.c_str()
00327 << std::endl;
00328 break;
00329 }
00330
00331 if (!uw->mStoreInMemory)
00332 {
00333 fclose(outfile);
00334 ::remove(outFilename.c_str());
00335 }
00336 attempts++;
00337 continue;
00338 }
00339
00340 curl_easy_cleanup(curl);
00341
00342 if (uw->mDownloadStatus != UPDATE_RESOURCES)
00343 {
00344 curl_slist_free_all(pHeaders);
00345 }
00346
00347 if (!uw->mStoreInMemory)
00348 {
00349
00350 if (uw->mDownloadStatus == UPDATE_RESOURCES)
00351 {
00352 unsigned long adler = fadler32(outfile);
00353
00354 if (uw->mCurrentChecksum != adler)
00355 {
00356 fclose(outfile);
00357
00358
00359 ::remove(outFilename.c_str());
00360 logger->log(
00361 "Checksum for file %s failed: (%lx/%lx)",
00362 uw->mCurrentFile.c_str(),
00363 adler, uw->mCurrentChecksum);
00364 attempts++;
00365 continue;
00366 }
00367 }
00368 fclose(outfile);
00369
00370
00371 const std::string newName =
00372 uw->mUpdatesDir + "/" + uw->mCurrentFile;
00373
00374
00375
00376 ::remove(newName.c_str());
00377 ::rename(outFilename.c_str(), newName.c_str());
00378
00379
00380
00381 newfile = fopen(newName.c_str(), "rb");
00382 if (newfile)
00383 {
00384 fclose(newfile);
00385 uw->mDownloadComplete = true;
00386 }
00387 }
00388 else
00389 {
00390
00391 uw->mDownloadComplete = true;
00392 }
00393 }
00394 attempts++;
00395 }
00396
00397 if (!uw->mDownloadComplete) {
00398 uw->mDownloadStatus = UPDATE_ERROR;
00399 }
00400
00401 return 0;
00402 }
00403
00404 void UpdaterWindow::download()
00405 {
00406 mDownloadComplete = false;
00407 mThread = SDL_CreateThread(UpdaterWindow::downloadThread, this);
00408
00409 if (!mThread)
00410 {
00411 logger->log("Unable to create mThread");
00412 mDownloadStatus = UPDATE_ERROR;
00413 }
00414 }
00415
00416 void UpdaterWindow::logic()
00417 {
00418
00419 mScrollArea->logic();
00420
00421
00422 {
00423 MutexLocker lock(&mLabelMutex);
00424
00425 if (mLabel->getCaption() != mNewLabelCaption)
00426 {
00427 mLabel->setCaption(mNewLabelCaption);
00428 mLabel->adjustSize();
00429 }
00430 }
00431
00432 switch (mDownloadStatus)
00433 {
00434 case UPDATE_ERROR:
00435 if (mThread)
00436 {
00437 if (mUserCancel) {
00438
00439 SDL_KillThread(mThread);
00440
00441 mUserCancel = false;
00442 }
00443 else {
00444 SDL_WaitThread(mThread, NULL);
00445 }
00446 mThread = NULL;
00447 }
00448 mBrowserBox->addRow("");
00449 mBrowserBox->addRow(_("##1 The update process is incomplete."));
00450 mBrowserBox->addRow(_("##1 It is strongly recommended that"));
00451 mBrowserBox->addRow(_("##1 you try again later"));
00452 mBrowserBox->addRow(mCurlError);
00453 mScrollArea->setVerticalScrollAmount(
00454 mScrollArea->getVerticalMaxScroll());
00455 mDownloadStatus = UPDATE_COMPLETE;
00456 break;
00457 case UPDATE_NEWS:
00458 if (mDownloadComplete)
00459 {
00460
00461 loadNews();
00462
00463 mCurrentFile = "resources2.txt";
00464 mStoreInMemory = false;
00465 mDownloadStatus = UPDATE_LIST;
00466 download();
00467 }
00468 break;
00469 case UPDATE_LIST:
00470 if (mDownloadComplete)
00471 {
00472 mLines = loadTextFile(mUpdatesDir + "/resources2.txt");
00473 mStoreInMemory = false;
00474 mDownloadStatus = UPDATE_RESOURCES;
00475 }
00476 break;
00477 case UPDATE_RESOURCES:
00478 if (mDownloadComplete)
00479 {
00480 if (mThread)
00481 {
00482 SDL_WaitThread(mThread, NULL);
00483 mThread = NULL;
00484 }
00485
00486 if (mLineIndex < mLines.size())
00487 {
00488 std::stringstream line(mLines[mLineIndex]);
00489 line >> mCurrentFile;
00490 std::string checksum;
00491 line >> checksum;
00492 std::stringstream ss(checksum);
00493 ss >> std::hex >> mCurrentChecksum;
00494
00495 std::ifstream temp(
00496 (mUpdatesDir + "/" + mCurrentFile).c_str());
00497
00498 if (!temp.is_open())
00499 {
00500 temp.close();
00501 download();
00502 }
00503 else
00504 {
00505 logger->log("%s already here", mCurrentFile.c_str());
00506 }
00507 mLineIndex++;
00508 }
00509 else
00510 {
00511
00512 mDownloadStatus = UPDATE_COMPLETE;
00513 }
00514 }
00515 break;
00516 case UPDATE_COMPLETE:
00517 enable();
00518 setLabel(_("Completed"));
00519 break;
00520 case UPDATE_IDLE:
00521 break;
00522 }
00523 }