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