1 /* bzflag
2 * Copyright (c) 1993-2021 Tim Riker
3 *
4 * This package is free software; you can redistribute it and/or
5 * modify it under the terms of the license found in the file
6 * named COPYING that should have accompanied this file.
7 *
8 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
11 */
12
13 /* interface header */
14 #include "Downloads.h"
15
16 /* common implementation headers */
17 #include "AccessList.h"
18 #include "CacheManager.h"
19 #include "BzMaterial.h"
20 #include "AnsiCodes.h"
21 #include "TextureManager.h"
22 #include "cURLManager.h"
23
24 /* local implementation headers */
25 #include "playing.h"
26 #include "HUDDialogStack.h"
27
28
29 // local variables for file tracker
30 static int totalTex = 0;
31 static int currentTex = 0;
32 static int runs = 0;
33
34
35
36 // FIXME - someone write a better explanation
37 static const char DownloadContent[] =
38 "#\n"
39 "# This file controls the access to servers for downloads.\n"
40 "# Patterns are attempted in order against both the hostname\n"
41 "# and ip. The first matching pattern sets the state. If no\n"
42 "# patterns are matched, then the server is authorized. There\n"
43 "# are four types of matches:\n"
44 "#\n"
45 "# simple globbing (* and ?)\n"
46 "# allow\n"
47 "# deny\n"
48 "#\n"
49 "# regular expressions\n"
50 "# allow_regex\n"
51 "# deny_regex\n"
52 "#\n"
53 "\n"
54 "#\n"
55 "# To authorize all servers, remove the last 2 lines.\n"
56 "#\n"
57 "\n"
58 "allow *images.bzflag.org\n"
59 "deny *\n";
60
61 static AccessList DownloadAccessList("DownloadAccess.txt", DownloadContent);
62
63 static bool textureDownloading = false;
64
65
66 // Function Prototypes
67 static void printAuthNotice();
68 static bool checkAuthorizations(BzMaterialManager::TextureSet& set);
69
70
71 class CachedTexture : cURLManager
72 {
73 public:
74 CachedTexture(const std::string &texUrl);
75
76 virtual void finalization(char *data, unsigned int length, bool good);
77
78 static void setParams(bool check, long timeout);
79 static int activeTransfer();
80 private:
81
82 virtual void collectData(char* ptr, int len);
83
84 std::string url;
85 static bool checkForCache;
86 static long httpTimeout;
87 static int textureCounter;
88 static int byteTransferred;
89 bool timeRequest;
90 };
91 bool CachedTexture::checkForCache = false;
92 long CachedTexture::httpTimeout = 0;
93 int CachedTexture::textureCounter = 0;
94 int CachedTexture::byteTransferred = 0;
95
CachedTexture(const std::string & texUrl)96 CachedTexture::CachedTexture(const std::string &texUrl) : cURLManager()
97 {
98 CacheManager::CacheRecord oldrec;
99
100 setURL(texUrl);
101 url = texUrl;
102
103 // use the cache?
104 bool cached = CACHEMGR.findURL(texUrl, oldrec);
105 if (cached && !checkForCache)
106 {
107 // use the cached file
108 MATERIALMGR.setTextureLocal(texUrl, oldrec.name);
109 }
110 else
111 {
112 textureCounter++;
113 if (httpTimeout > 0.0)
114 setTimeout(httpTimeout);
115 setRequestFileTime(true);
116 timeRequest = cached;
117 std::string msg = ColorStrings[GreyColor];
118 msg += "downloading: " + url;
119 addMessage(NULL, msg);
120 if (cached)
121 {
122 // use the cached file -- just in case
123 MATERIALMGR.setTextureLocal(url, oldrec.name);
124 setTimeCondition(ModifiedSince, oldrec.date);
125 }
126 addHandle();
127 }
128 }
129
setParams(bool check,long timeout)130 void CachedTexture::setParams(bool check, long timeout)
131 {
132 checkForCache = check;
133 httpTimeout = timeout;
134 textureCounter = 0;
135 byteTransferred = 0;
136 }
137
finalization(char * data,unsigned int length,bool good)138 void CachedTexture::finalization(char *data, unsigned int length, bool good)
139 {
140 time_t filetime;
141
142 textureCounter--;
143 if (good)
144 {
145 if (length)
146 {
147 getFileTime(filetime);
148 // CACHEMGR generates name, usedDate, and key
149 CacheManager::CacheRecord rec;
150 rec.url = url;
151 rec.size = length;
152 rec.date = filetime;
153 CACHEMGR.addFile(rec, data);
154 const std::string localname = CACHEMGR.getLocalName(url);
155 TextureManager& TEXMGR = TextureManager::instance();
156 if (TEXMGR.isLoaded(localname))
157 {
158 TEXMGR.reloadTextureImage(localname); // reload with the new image
159 }
160 MATERIALMGR.setTextureLocal(url, localname);
161 }
162 }
163 else
164 {
165 CacheManager::CacheRecord rec;
166 if (CACHEMGR.findURL(url, rec))
167 MATERIALMGR.setTextureLocal(url, rec.name);
168 else
169 MATERIALMGR.setTextureLocal(url, "");
170 }
171 }
172
activeTransfer()173 int CachedTexture::activeTransfer()
174 {
175 return textureCounter;
176 }
177
collectData(char * ptr,int len)178 void CachedTexture::collectData(char* ptr, int len)
179 {
180 char buffer[128];
181
182 if (runs == 0)
183 totalTex = textureCounter;
184
185 cURLManager::collectData(ptr, len);
186 byteTransferred += len;
187
188 //Make it so it counts textures in reverse order (0 to max instead of max to 0)
189 currentTex = totalTex - textureCounter + 1;
190
191 //Turn bytes into kilobytes
192 sprintf (buffer, "Downloading texture (%d of %d): %d KB", currentTex, totalTex, byteTransferred/1024);
193 runs++;
194
195 HUDDialogStack::get()->setFailedMessage(buffer);
196 }
197
198 static std::vector<CachedTexture*> cachedTexVector;
199
startDownloads(bool doDownloads,bool updateDownloads,bool referencing)200 void Downloads::startDownloads(bool doDownloads, bool updateDownloads,
201 bool referencing)
202 {
203 totalTex = 0;
204 currentTex = 0;
205 runs = 0;
206
207 CACHEMGR.loadIndex();
208 CACHEMGR.limitCacheSize();
209
210 DownloadAccessList.reload();
211
212 BzMaterialManager::TextureSet set;
213 BzMaterialManager::TextureSet::iterator set_it;
214 MATERIALMGR.makeTextureList(set, referencing);
215
216 float timeout = 15;
217 if (BZDB.isSet("httpTimeout"))
218 timeout = BZDB.eval("httpTimeout");
219 CachedTexture::setParams(updateDownloads, (long)timeout);
220
221 // check hosts' access permissions
222 bool authNotice = checkAuthorizations(set);
223
224 if (!referencing)
225 {
226 // Clear old cached texture
227 // This is the first time is called after joining
228 int texNo = cachedTexVector.size();
229 for (int i = 0; i < texNo; i++)
230 delete cachedTexVector[i];
231 cachedTexVector.clear();
232 }
233
234 if (doDownloads)
235 for (set_it = set.begin(); set_it != set.end(); ++set_it)
236 {
237 const std::string& texUrl = set_it->c_str();
238 if (CACHEMGR.isCacheFileType(texUrl))
239 {
240 if (!referencing)
241 MATERIALMGR.setTextureLocal(texUrl, "");
242 cachedTexVector.push_back(new CachedTexture(texUrl));
243 }
244 }
245 else
246 for (set_it = set.begin(); set_it != set.end(); ++set_it)
247 {
248 const std::string& texUrl = set_it->c_str();
249 if (CACHEMGR.isCacheFileType(texUrl))
250 {
251
252 // use the cache?
253 CacheManager::CacheRecord oldrec;
254 if (CACHEMGR.findURL(texUrl, oldrec))
255 {
256 // use the cached file
257 MATERIALMGR.setTextureLocal(texUrl, oldrec.name);
258 }
259 else
260 {
261 // bail here if we can't download
262 MATERIALMGR.setTextureLocal(texUrl, "");
263 std::string msg = ColorStrings[GreyColor];
264 msg += "not downloading: " + texUrl;
265 addMessage(NULL, msg);
266 }
267 }
268 }
269
270 if (authNotice)
271 printAuthNotice();
272 textureDownloading = true;
273 }
274
finalizeDownloads()275 void Downloads::finalizeDownloads()
276 {
277 textureDownloading = false;
278
279 int texNo = cachedTexVector.size();
280 for (int i = 0; i < texNo; i++)
281 delete cachedTexVector[i];
282 cachedTexVector.clear();
283
284 CACHEMGR.saveIndex();
285 }
286
requestFinalized()287 bool Downloads::requestFinalized()
288 {
289 return textureDownloading && (CachedTexture::activeTransfer() == 0);
290 }
291
removeTextures()292 void Downloads::removeTextures()
293 {
294 BzMaterialManager::TextureSet set;
295 BzMaterialManager::TextureSet::iterator set_it;
296 MATERIALMGR.makeTextureList(set, false /* ignore referencing */);
297
298 TextureManager& TEXMGR = TextureManager::instance();
299
300 for (set_it = set.begin(); set_it != set.end(); ++set_it)
301 {
302 const std::string& texUrl = set_it->c_str();
303 if (CACHEMGR.isCacheFileType(texUrl))
304 {
305 const std::string& localname = CACHEMGR.getLocalName(texUrl);
306 if (TEXMGR.isLoaded(localname))
307 TEXMGR.removeTexture(localname);
308 }
309 }
310
311 return;
312 }
313
314
printAuthNotice()315 static void printAuthNotice()
316 {
317 std::string msg = ColorStrings[WhiteColor];
318 msg += "NOTE: ";
319 msg += ColorStrings[GreyColor];
320 msg += "download access is controlled by ";
321 msg += ColorStrings[YellowColor];
322 msg += DownloadAccessList.getFilePath();
323 addMessage(NULL, msg);
324 return;
325 }
326
327
authorizedServer(const std::string & hostname)328 bool authorizedServer(const std::string& hostname)
329 {
330 // Don't do here a DNS lookup, it can block the client
331 // DNS is temporary removed until someone code it unblocking
332
333 // make the list of strings to check
334 std::vector<std::string> nameAndIp;
335 if (hostname.size() > 0)
336 nameAndIp.push_back(hostname);
337
338 return DownloadAccessList.authorized(nameAndIp);
339 }
340
341
parseHostname(const std::string & url,std::string & hostname)342 bool parseHostname(const std::string& url, std::string& hostname)
343 {
344 std::string protocol, path;
345 int port;
346 if (BzfNetwork::parseURL(url, protocol, hostname, port, path))
347 {
348 if ((protocol == "http") || (protocol == "ftp"))
349 return true;
350 }
351 return false;
352 }
353
354
checkAuthorizations(BzMaterialManager::TextureSet & set)355 static bool checkAuthorizations(BzMaterialManager::TextureSet& set)
356 {
357 // avoid the DNS lookup
358 if (DownloadAccessList.alwaysAuthorized())
359 return false;
360
361 bool hostFailed = false;
362
363 BzMaterialManager::TextureSet::iterator set_it;
364
365 std::map<std::string, bool> hostAccess;
366 std::map<std::string, bool>::iterator host_it;
367
368 // get the list of hosts to check
369 for (set_it = set.begin(); set_it != set.end(); ++set_it)
370 {
371 const std::string& url = *set_it;
372 std::string hostname;
373 if (parseHostname(url, hostname))
374 hostAccess[hostname] = true;
375 }
376
377 // check the hosts
378 for (host_it = hostAccess.begin(); host_it != hostAccess.end(); ++host_it)
379 {
380 const std::string& host = host_it->first;
381 host_it->second = authorizedServer(host);
382 }
383
384 // clear any unauthorized urls
385 set_it = set.begin();
386 while (set_it != set.end())
387 {
388 BzMaterialManager::TextureSet::iterator next_it = set_it;
389 ++next_it;
390 const std::string& url = *set_it;
391 std::string hostname;
392 if (parseHostname(url, hostname) && !hostAccess[hostname])
393 {
394 hostFailed = true;
395 // send a message
396 std::string msg = ColorStrings[RedColor];
397 msg += "local denial: ";
398 msg += ColorStrings[GreyColor];
399 msg += url;
400 addMessage(NULL, msg);
401 // remove the url
402 MATERIALMGR.setTextureLocal(url, "");
403 set.erase(set_it);
404 }
405 set_it = next_it;
406 }
407
408 return hostFailed;
409 }
410
411
412 // Local Variables: ***
413 // mode: C++ ***
414 // tab-width: 4 ***
415 // c-basic-offset: 4 ***
416 // indent-tabs-mode: nil ***
417 // End: ***
418 // ex: shiftwidth=4 tabstop=4
419