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