1 ///////////////////////////////////////// 2 // 3 // OpenLieroX 4 // 5 // Auxiliary Software class library 6 // 7 // based on the work of JasonB 8 // enhanced by Dark Charlie and Albert Zeyer 9 // 10 // code under LGPL 11 // 12 ///////////////////////////////////////// 13 14 15 // File downloading over HTTP 16 // Created 13/10/07 17 // By Karel Petranek, Albert Zeyer and Martin Griffin 18 19 20 #ifndef __FILEDOWNLOAD_H__ 21 #define __FILEDOWNLOAD_H__ 22 23 #include <list> 24 #include <vector> 25 #include <string> 26 #include <map> 27 #include <stdio.h> // for FILE 28 #include "ThreadPool.h" 29 #include <SDL_mutex.h> 30 #include "HTTP.h" 31 #include "types.h" 32 #include "CBytestream.h" 33 34 35 // File download states 36 enum { 37 FILEDL_NO_FILE = -1, 38 FILEDL_INITIALIZING = 0, 39 FILEDL_RECEIVING, 40 FILEDL_FINISHED, 41 FILEDL_ERROR 42 }; 43 44 // File download errors 45 // NOTE: if you change this, change also error strings in FileDownload.cpp 46 enum { 47 FILEDL_ERROR_NO_ERROR = 0, 48 FILEDL_ERROR_NO_FILE, 49 FILEDL_ERROR_NO_DEST, 50 FILEDL_ERROR_NO_SERVER, 51 FILEDL_ERROR_HTTP, 52 FILEDL_ERROR_SAVING, 53 }; 54 55 // File download error structure 56 class DownloadError { public: 57 std::string sErrorMsg; 58 int iError; 59 HttpError tHttpError; 60 61 DownloadError& operator= (const DownloadError& oth) { 62 if (&oth != this) { 63 sErrorMsg = oth.sErrorMsg; 64 iError = oth.iError; 65 tHttpError = oth.tHttpError; 66 } 67 return *this; 68 } 69 }; 70 71 72 // Single file download handling class 73 class CHttpDownloader { 74 public: 75 // Constructors and destructors CHttpDownloader()76 CHttpDownloader() : 77 tFile(NULL), 78 tDownloadServers(NULL), 79 iState(FILEDL_NO_FILE), 80 iID(0) 81 { tMutex = SDL_CreateMutex(); } 82 CHttpDownloader(std::vector<std::string> * download_servers,size_t id)83 CHttpDownloader(std::vector<std::string> *download_servers, size_t id) : 84 tFile(NULL), 85 tDownloadServers(download_servers), 86 iState(FILEDL_NO_FILE), 87 iID(id) 88 { tMutex = SDL_CreateMutex(); } 89 ~CHttpDownloader()90 ~CHttpDownloader() { Stop(); SDL_DestroyMutex(tMutex); } 91 92 93 private: CHttpDownloader(const CHttpDownloader &)94 CHttpDownloader(const CHttpDownloader&) { assert(false); } 95 CHttpDownloader& operator=(const CHttpDownloader& dl) { 96 assert(false); /* 97 if (&dl == this) 98 return *this; 99 100 sFileName = dl.sFileName; 101 sDestPath = dl.sDestPath; 102 tFile = dl.tFile; 103 tDownloadServers = dl.tDownloadServers; // HINT: this is a pointer 104 iCurrentServer = dl.iCurrentServer; 105 iState = dl.iState; 106 iID = dl.iID; 107 tHttp = dl.tHttp; // HINT: safe, CHttp has a copy operator defined 108 tError = dl.tError; */ 109 return *this; 110 } 111 112 std::string sFileName; 113 std::string sDestPath; 114 FILE *tFile; 115 std::vector<std::string> *tDownloadServers; 116 int iCurrentServer; 117 int iState; 118 size_t iID; 119 CHttp tHttp; 120 DownloadError tError; 121 SDL_mutex *tMutex; 122 123 private: 124 void SetHttpError(HttpError err); 125 void SetDlError(int id); 126 127 public: 128 void Start(const std::string& filename, const std::string& dest_dir); 129 void Stop(); 130 void ProcessDownload(); 131 std::string GetFileName(); 132 int GetState(); 133 size_t GetID(); 134 int GetProgress(); 135 DownloadError GetError(); Lock()136 void Lock() { SDL_LockMutex(tMutex); } Unlock()137 void Unlock() { SDL_UnlockMutex(tMutex); } 138 }; 139 140 141 // File downloader class (multiple downloads at once, a set of CFileDownload) 142 class CHttpDownloadManager { 143 public: 144 CHttpDownloadManager(); 145 ~CHttpDownloadManager(); 146 147 private: 148 std::list<CHttpDownloader *> tDownloads; 149 std::vector<std::string> tDownloadServers; 150 ThreadPoolItem *tThread; 151 SDL_mutex *tMutex; 152 size_t iActiveDownloads; 153 154 friend int ManagerMain(void *param); 155 bool bBreakThread; 156 157 public: 158 void ProcessDownloads(); 159 void StartFileDownload(const std::string& filename, const std::string& dest_dir); 160 void CancelFileDownload(const std::string& filename); 161 void RemoveFileDownload(const std::string& filename); 162 bool IsFileDownloaded(const std::string& filename); 163 DownloadError FileDownloadError(const std::string& filename); 164 int GetFileProgress(const std::string& filename); Lock()165 void Lock() { SDL_LockMutex(tMutex); } Unlock()166 void Unlock() { SDL_UnlockMutex(tMutex); } GetDownloads()167 std::list<CHttpDownloader *> *GetDownloads() { return &tDownloads; } 168 }; 169 170 // In-lobby or in-game file downloader over unreliable protocol - send packets of 256 bytes 171 // Actually we're using reliable CChannel to send packets so this downloader 172 // doesn't contain any checks on packet lost / received in wrong order, 173 // only Adler32 checksum which is calculated by zlib. 174 class CUdpFileDownloader 175 { 176 public: CUdpFileDownloader()177 CUdpFileDownloader() { reset(); bAllowFileRequest = true; }; ~CUdpFileDownloader()178 ~CUdpFileDownloader() { }; 179 180 enum State_t { S_SEND, S_RECEIVE, S_FINISHED }; 181 182 // Basic functions for file download getFilename()183 const std::string & getFilename() const { return isReceiving() ? sLastFileRequested : sFilename; }; 184 // getData() contains garbage when download not finished yet, or when uploading a file getData()185 const std::string & getData() const { return sData; } ; 186 187 // Should be called when received S2C_SENDFILE or C2S_SENDFILE msg - read needed amount of bytes from bytestream 188 // Returns true if download finished or error occured 189 // If file request received automatically start sending requested file if bAllowFileRequest is true 190 bool receive( CBytestream * bs ); 191 // Should be called to append file data to S2C_SENDFILE or C2S_SENDFILE messages when sending in a loop 192 // Returns true if download finished or error occured 193 bool send( CBytestream * bs ); 194 195 // Does not change any variables, just pings server with zero-sized packet to un-freeze it, else download speed sucks 196 void sendPing( CBytestream * bs ) const; 197 getState()198 State_t getState() const { return tState; }; 199 isSending()200 bool isSending() const { return tState == S_SEND; }; isReceiving()201 bool isReceiving() const { return tState == S_RECEIVE; }; isFinished()202 bool isFinished() const { return tState == S_FINISHED; }; wasSending()203 bool wasSending() const { return tPrevState == S_SEND; }; wasReceiving()204 bool wasReceiving() const { return tPrevState == S_RECEIVE; }; 205 wasError()206 bool wasError() const { return bWasError; }; clearError()207 void clearError() { bWasError = false; }; 208 wasAborted()209 bool wasAborted() const { return bWasAborted; }; clearAborted()210 void clearAborted() { bWasAborted = false; }; 211 212 void setDataToSend( const std::string & name, const std::string & data, bool noCompress = false ); 213 void setFileToSend( const std::string & path ); 214 215 void reset(); 216 217 void abortDownload(); // Aborts both downloading and uploading, data to send is in one packet less than 256 bytes 218 219 // Functions that will trigger remote CFileDownloaderInGame to do something like send some file or list some dir 220 void allowFileRequest( bool allow ); 221 void requestFile( const std::string & path, bool retryIfFail ); 222 bool requestFilesPending(); // Re-send file request if downloading fails 223 void removeFileFromRequest( const std::string & path ); 224 static bool isPathValid( const std::string & path ); // Check if someone tries to access /etc/shadow to get system passwords 225 226 struct StatInfo 227 { 228 std::string filename; 229 uint size; 230 uint compressedSize; // Approximate! Use only for progressbars 231 uint checksum; 232 StatInfo( const std::string & _filename="", uint _size=0, uint _compressedSize=0, uint _checksum=0 ): filenameStatInfo233 filename(_filename), size(_size), compressedSize(_compressedSize), checksum(_checksum) {}; 234 }; 235 236 // For dir returns recursive list of all files in dir, clears previous file info 237 void requestFileInfo( const std::string & path, bool retryIfFail ); 238 // File statistics from requestFileInfo() is saved in array returned by this func getFileInfo()239 const std::vector< StatInfo > & getFileInfo() const { return cStatInfo; }; 240 241 // Additional functionality to show download progress - inexact, server may send any file and we should accept it, 242 // yet current implementation will send only files we will request 243 float getFileDownloadingProgress() const; // Downloading progress of current file, in range 0.0-1.0 244 size_t getFileDownloadingProgressBytes() const; 245 size_t getFilesPendingAmount() const; 246 size_t getFilesPendingSize() const; // Calculates compressed size of all pending files, incluing the one currently downloading 247 248 private: 249 void processFileRequests(); 250 251 // TODO: should use intern-pointer here 252 std::string sFilename; 253 std::string sData; 254 size_t iPos; 255 256 State_t tPrevState; 257 State_t tState; 258 bool bWasError; 259 bool bWasAborted; 260 261 bool bAllowFileRequest; 262 263 std::vector< std::string > tRequestedFiles; 264 265 std::vector< StatInfo > cStatInfo; 266 267 // For progressbar and download percentage 268 std::map< std::string, StatInfo > cStatInfoCache; 269 std::string sLastFileRequested; 270 271 }; 272 273 #endif // __FILEDOWNLOAD_H__ 274