aboutsummaryrefslogtreecommitdiff
path: root/src/Cache.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Cache.cpp')
-rw-r--r--src/Cache.cpp158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/Cache.cpp b/src/Cache.cpp
new file mode 100644
index 0000000..7e3272a
--- /dev/null
+++ b/src/Cache.cpp
@@ -0,0 +1,158 @@
+#include "../include/Cache.hpp"
+#include "../include/env.hpp"
+#include "../include/ResourceCache.hpp"
+#include <boost/filesystem/convenience.hpp>
+#include <unordered_map>
+#include <process.hpp>
+#include <odhtdb/Hash.hpp>
+
+#if OS_FAMILY == OS_FAMILY_POSIX
+#include <pwd.h>
+#else
+#include <string>
+#endif
+
+using namespace std;
+using namespace TinyProcessLib;
+
+namespace dchat
+{
+ unordered_map<string, ImageByUrlResult> imageUrlCache;
+
+ boost::filesystem::path getHomeDir()
+ {
+ #if OS_FAMILY == OS_FAMILY_POSIX
+ const char *homeDir = getenv("HOME");
+ if(!homeDir)
+ {
+ passwd *pw = getpwuid(getuid());
+ homeDir = pw->pw_dir;
+ }
+ return boost::filesystem::path(homeDir);
+ #elif OS_FAMILY == OS_FAMILY_WINDOWS
+ BOOL ret;
+ HANDLE hToken;
+ std::wstring homeDir;
+ DWORD homeDirLen = MAX_PATH;
+ homeDir.resize(homeDirLen);
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken))
+ return Result<FileString>::Err("Failed to open process token");
+
+ if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen))
+ {
+ CloseHandle(hToken);
+ return Result<FileString>::Err("Failed to get home directory");
+ }
+
+ CloseHandle(hToken);
+ homeDir.resize(wcslen(homeDir.c_str()));
+ return boost::filesystem::path(homeDir);
+ #endif
+ }
+
+ boost::filesystem::path Cache::getDchatDir()
+ {
+ boost::filesystem::path dchatHomeDir = getHomeDir() / ".dchat";
+ boost::filesystem::create_directories(dchatHomeDir);
+ return dchatHomeDir;
+ }
+
+ Cache::Cache()
+ {
+ downloadWaitThread = thread([this]
+ {
+ while(true)
+ {
+ imageDownloadMutex.lock();
+ for(vector<ImageDownloadInfo>::iterator it = imageDownloadProcesses.begin(); it != imageDownloadProcesses.end();)
+ {
+ int exitStatus;
+ if(it->process->try_get_exit_status(exitStatus))
+ {
+ bool failed = exitStatus != 0;
+
+ if(!failed)
+ {
+ boost::filesystem::path filepath = getDchatDir();
+ odhtdb::Hash urlHash(it->url.data(), it->url.size());
+ filepath /= urlHash.toString();
+
+ try
+ {
+ const sf::Texture *texture = ResourceCache::getTexture(filepath.string());
+ ImageByUrlResult &imageByUrlResult = imageUrlCache[it->url];
+ imageByUrlResult.texture = texture;
+ imageByUrlResult.type = ImageByUrlResult::Type::CACHED;
+ }
+ catch(FailedToLoadResourceException &e)
+ {
+ fprintf(stderr, "%s\n", e.what());
+ failed = true;
+ }
+ }
+
+ if(failed)
+ {
+ imageUrlCache[it->url].type = ImageByUrlResult::Type::FAILED_DOWNLOAD;
+ fprintf(stderr, "Image download failed for url: %s\n", it->url.c_str());
+ }
+
+ it = imageDownloadProcesses.erase(it);
+ }
+ else
+ ++it;
+ }
+ imageDownloadMutex.unlock();
+
+ while(imageDownloadProcesses.empty())
+ this_thread::sleep_for(chrono::milliseconds(20));
+ }
+ });
+ downloadWaitThread.detach();
+ }
+
+ const ImageByUrlResult Cache::getImageByUrl(const string &url, int downloadLimitBytes)
+ {
+ auto it = imageUrlCache.find(url);
+ if(it != imageUrlCache.end())
+ return it->second;
+
+ // TODO: Verify hashed url is not too long for filepath on windows
+ boost::filesystem::path filepath = getDchatDir();
+ odhtdb::Hash urlHash(url.data(), url.size());
+ filepath /= urlHash.toString();
+
+ // Check if file exists because we dont want sfml spam with "Failed to load image""...
+ if(boost::filesystem::exists(filepath))
+ {
+ try
+ {
+ const sf::Texture *texture = ResourceCache::getTexture(filepath.string());
+ lock_guard<mutex> lock(imageDownloadMutex);
+ ImageByUrlResult result { texture, ImageByUrlResult::Type::CACHED };
+ imageUrlCache[url] = result;
+ return result;
+ }
+ catch(FailedToLoadResourceException &e)
+ {
+
+ }
+ }
+
+ lock_guard<mutex> lock(imageDownloadMutex);
+ ImageByUrlResult result { nullptr, ImageByUrlResult::Type::DOWNLOADING };
+ imageUrlCache[url] = result;
+
+ string downloadLimitBytesStr = to_string(downloadLimitBytes);
+
+ Process::string_type cmd = "curl -L --silent -o '";
+ cmd += filepath.native();
+ cmd += "' --max-filesize " + downloadLimitBytesStr + " --range 0-" + downloadLimitBytesStr + " --url '" + url + "'";
+ // certutil.exe -urlcache -split -f "https://url/to/file" path/and/name/to/save/as/file
+ Process *process = new Process(cmd, "", nullptr, nullptr, false);
+ ImageDownloadInfo imageDownloadInfo { process, url };
+ imageDownloadProcesses.emplace_back(imageDownloadInfo);
+ return result;
+ }
+}