#include "../include/Cache.hpp" #include "../include/env.hpp" #include "../include/ResourceCache.hpp" #include "../include/FileUtil.hpp" #include "../include/Gif.hpp" #include #include #include #include #if OS_FAMILY == OS_FAMILY_POSIX #include #else #include #endif using namespace std; using namespace TinyProcessLib; namespace dchat { unordered_map 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::Err("Failed to open process token"); if (!GetUserProfileDirectory(hToken, &homeDir[0], &homeDirLen)) { CloseHandle(hToken); return Result::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; } ImageByUrlResult loadImageFromFile(const boost::filesystem::path &filepath) { try { StringView fileContent = getFileContent(filepath); if(Gif::isDataGif(fileContent)) { Gif *gif = new Gif(move(fileContent)); return { gif, ImageByUrlResult::Type::CACHED }; } else { sf::Texture *texture = new sf::Texture(); if(texture->loadFromMemory(fileContent.data, fileContent.size)) { delete fileContent.data; texture->setSmooth(true); texture->generateMipmap(); return { texture, ImageByUrlResult::Type::CACHED }; } delete fileContent.data; } } catch(FileException &e) { } catch(FailedToLoadResourceException &e) { } return { (sf::Texture*)nullptr, ImageByUrlResult::Type::FAILED_DOWNLOAD }; } Cache::Cache() { downloadWaitThread = thread([this] { while(true) { imageDownloadMutex.lock(); for(vector::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(); ImageByUrlResult imageByUrlResult = loadImageFromFile(filepath); imageUrlCache[it->url] = imageByUrlResult; switch(imageByUrlResult.type) { case ImageByUrlResult::Type::CACHED: printf("Downloaded image from url: %s\n", it->url.c_str()); break; case ImageByUrlResult::Type::FAILED_DOWNLOAD: printf("Failed to download and load image from url: %s\n", it->url.c_str()); break; } } it = imageDownloadProcesses.erase(it); } else ++it; } imageDownloadMutex.unlock(); while(imageDownloadProcesses.empty()) this_thread::sleep_for(chrono::milliseconds(20)); this_thread::sleep_for(chrono::milliseconds(20)); } }); downloadWaitThread.detach(); } const ImageByUrlResult Cache::getImageByUrl(const string &url, int downloadLimitBytes) { lock_guard lock(imageDownloadMutex); 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(); ImageByUrlResult imageByUrlResult = loadImageFromFile(filepath); if(imageByUrlResult.type == ImageByUrlResult::Type::CACHED) { imageUrlCache[url] = imageByUrlResult; printf("Loaded image from file cache: %s, is gif: %s\n", url.c_str(), imageByUrlResult.isGif ? "yes" : "no"); return imageByUrlResult; } ImageByUrlResult result((sf::Texture*)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; } }