diff options
Diffstat (limited to 'src/Cache.cpp')
-rw-r--r-- | src/Cache.cpp | 158 |
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; + } +} |