/* * download.cpp by Matthias Braun * * Copyright (C) 2002 Atomic Blue (info@planeshift.it, http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation (version 2 of the License) * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #include "download.h" #include "registry.h" #include "paws/pawswidget.h" #include "paws/pawsmanager.h" #include "paws/pawsprogressbar.h" #include "pawsupdaterwindow.h" #include "updaterglobals.h" #include "updaterconfig.h" #include "fileutil.h" size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream) { size_t written = fwrite(ptr, size, nmemb, (FILE *)stream); return written; } namespace updater { Downloader::Downloader () : callback (0) { eng = psupdaterengine; curl = curl_easy_init(); if (!curl) psupdaterengine->CriticalError("Couldn't initialize libcurl downloader."); curlerror = new char[CURL_ERROR_SIZE]; curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, curlerror); curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, ProgressCallback); curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this); } Downloader::~Downloader () { delete[] curlerror; curl_easy_cleanup(curl); } void Downloader::SetProxy (bool active, const char* host, int port) { if (!active) { #ifdef WIN32 curl_easy_setopt (curl, CURLOPT_PROXY, ""); #endif } else { curl_easy_setopt (curl, CURLOPT_PROXY, host); curl_easy_setopt (curl, CURLOPT_PROXYPORT, port); } } void Downloader::SetCallback (StatusCallback* newcallback) { callback = newcallback; } bool Downloader::DownloadFile (const char* dest, const char* url, bool doprogress,bool changemirrors) { char * destpath; // Set passwords curl_easy_setopt (curl, CURLOPT_USERPWD,psupdaterengine->GetUpdater()->GetConfig()->GetMirror()->GetPassphrase()); csRef vfs = eng->GetVFS(); if (vfs) { csRef prefixpath = vfs->GetRealPath("/this/"); destpath = (char *)malloc(strlen(dest) + prefixpath->GetSize()+1); //not sure if the +1 is necessary, but paranoid. strncpy(destpath,prefixpath->GetData(),prefixpath->GetSize()); strcat(destpath,dest); } else { printf("No VFS in object registry!?\n"); return false; } FILE* file; file = fopen (destpath, "wb"); if (!file) { printf("Couldn't write to file! (%s)\n", dest); return false; } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); // do we want a progressbar? progress = doprogress; lastprogress = 0; start = csGetTicks(); CURLcode result = curl_easy_perform(curl); fclose (file); free(destpath); destpath = 0; long curlhttpcode; curl_easy_getinfo (curl, CURLINFO_HTTP_CODE, &curlhttpcode); csString error; if (result != CURLE_OK) { if (result == CURLE_COULDNT_CONNECT || result == CURLE_COULDNT_RESOLVE_HOST) error = "Couldn't connect to mirror"; else error.Format("Error while downloading: %s, file %s",curlerror,url); } // Tell the user that we failed if (curlhttpcode != 200 || error != "") { if(error == "") printf ("Server error %d (%s)\n",curlhttpcode,url); else printf ("Server error: %s (%d)\n",error.GetData(),curlhttpcode); if(changemirrors) return NextMirror(dest,url,progress); else return false; } return true; } int Downloader::ProgressCallback (void* clientp, double total, double now, double, double) { Downloader* me = (Downloader*)clientp; if (!psupdaterengine) psupdaterengine=me->GetEngine(); //Still null? if (!psupdaterengine || !psupdaterengine->GetUpdater() || !psupdaterengine->GetUpdater()->IsRunning()) return 0; int secs = (int)ceil(double(csGetTicks() - me->GetStartTick()) / 1000 ); double kb = ceil((now/double(secs)) / 1024); //Silent progress if (psupdaterengine->IsSilent()) { if (total>0 && now>0) { float percent = now/total*100; if (me->callback) me->callback->StatusUpdate (percent, ""); if (me->progress) { while ( (((int) percent+.001) - me->lastprogress > 5) && me->lastprogress<=100) { if (me->lastprogress % 25 == 0) { printf ("%d%%", me->lastprogress); } else if (me->lastprogress % 5 == 0) { printf ("."); } fflush(stdout); me->lastprogress += 1; } } } int percent = (int)(now/total*100); if (percent == 100 && me->lastprogress != 100) { if(me->progress) printf("100%% (%d KB/s)\n",(int)kb); me->lastprogress = 100; } return 0; } //Active progress //Get PAWS window pawsUpdaterWindow* updWnd = (pawsUpdaterWindow*)PawsManager::GetSingleton().FindWidget("updater"); if (!updWnd) return false; if (total>0 && now>0) { psupdaterengine->LockStatusMutex(); int percent = (int)(now/total*100); updWnd->SetProgressValue(percent); updWnd->SetSpeed(kb); psupdaterengine->UnlockStatusMutex(); } return 0; } bool Downloader::NextMirror(const char* dest, const char* url, bool progress) { unsigned int oldVersion = psupdaterengine->GetUpdater()->GetVersionFromFile("/this/version.dat"); Mirror* oldMirror = psupdaterengine->GetUpdater()->GetConfig()->GetMirror(); csString newURL = url; newURL = newURL.Slice(strlen( csString(oldMirror->GetBaseURL()) + oldMirror->GetFilesDirectory()),newURL.Length()); // Inform psupdaterengine->OutToScreen( psupdaterengine->FindRGB(255,0,0), "Couldn't connect to mirror %s!", oldMirror->GetName() ); // Action oldMirror->SetWorking(false); // set this mirror as non working so we don't enter an infinitve loop bool loop= true; while(loop) { Mirror* newMirror; Config* config = psupdaterengine->GetUpdater()->GetConfig(); if(config->SetNextWorkingMirror()) { // Get the new mirror newMirror = config->GetMirror(); psupdaterengine->OutToScreen("Trying mirror %s..",newMirror->GetName()); } else { // No mirrors left :( psupdaterengine->OutToScreen("Ran out of mirrors! Game cannot be updated."); psupdaterengine->GetUpdater()->EndUpdate(); return false; } // Compare the versions between the mirrors if (!newMirror->NoVersion()) { csString verFile; verFile.Format("updatertemp/%s.dat",newMirror->GetName()); MakeDirectory("updatertemp"); csString verFileURL = newMirror->GetBaseURL(); verFileURL += newMirror->GetVersion(); bool downloaded = DownloadFile(verFile,verFileURL,false,false); unsigned int newVersion = psupdaterengine->GetUpdater()->GetVersionFromFile("/this/" + verFile); if ( !downloaded || newVersion == 0 ) { newMirror->SetWorking(false); psupdaterengine->OutToScreen(psupdaterengine->FindRGB(255,0,0),"Mirror lacks a version file; can't use it"); continue; // Next mirror } if (oldVersion > newVersion) { // Different version newMirror->SetWorking(false); psupdaterengine->OutToScreen(psupdaterengine->FindRGB(255,0,0),"Mirror '%s' has old data", newMirror->GetName()); continue; // Next mirror } } // Make new url if(!strcmp(url, csString(oldMirror->GetBaseURL()) + oldMirror->GetRepository())) // Was this a file or the repository? { newURL = psupdaterengine->GetUpdater()->GetConfig()->GetMirror()->GetBaseURL(); newURL += psupdaterengine->GetUpdater()->GetConfig()->GetMirror()->GetRepository(); } else { newURL = csString(psupdaterengine->GetUpdater()->GetConfig()->GetMirror()->GetBaseURL()) + psupdaterengine->GetUpdater()->GetConfig()->GetMirror()->GetFilesDirectory() + newURL; } // Try again bool suc = DownloadFile(dest,newURL,progress,false); if(suc) break; else newMirror->SetWorking(false); // Next mirror if not working } return true; } } // end of namespace updater