11 #define OPENSSL_API_COMPAT 10100
14 #include <openssl/md5.h>
18 #pragma comment(lib, "wldap32.lib")
19 #pragma comment(lib, "crypt32.lib")
20 #pragma comment(lib, "Ws2_32.lib")
25 #define CURL_STATICLIB
27 #include <curl/curl.h>
28 #include <curl/easy.h>
39 #include <unordered_set>
46 std::string
GetMD5(
const std::string& file_path) {
51 std::ifstream fp(file_path.c_str(), std::ios::in | std::ios::binary);
54 std::ostringstream os;
58 constexpr
const std::size_t buffer_size{1 << 12};
59 char buffer[buffer_size];
60 unsigned char hash[MD5_DIGEST_LENGTH] = {0};
66 fp.read(buffer, buffer_size);
67 MD5_Update(&ctx, buffer, fp.gcount());
70 MD5_Final(hash, &ctx);
73 std::ostringstream os;
74 os << std::hex << std::setfill(
'0');
76 for (
int i = 0; i < MD5_DIGEST_LENGTH; ++i) {
77 os << std::setw(2) << static_cast<unsigned int>(hash[i]);
85 written = fwrite(ptr,
size, nmemb, stream);
90 const std::string& md5,
91 const std::string& download_dir) {
93 if (md5.size() != MD5_DIGEST_LENGTH * 2) {
95 md5.size(), MD5_DIGEST_LENGTH * 2);
97 if (download_dir.empty()) {
102 const std::string file_name =
104 const std::string file_path = download_dir +
"/" + file_name;
111 GetMD5(file_path) == md5) {
125 curl = curl_easy_init();
129 fp = fopen(file_path.c_str(),
"wb");
133 curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
134 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
135 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER,
false);
136 curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST,
false);
137 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION,
WriteDataCb);
138 curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
139 res = curl_easy_perform(curl);
140 curl_easy_cleanup(curl);
143 if (res == CURLE_OK) {
144 const std::string actual_md5 =
GetMD5(file_path);
145 if (actual_md5 == md5) {
149 "MD5 mismatch for {}.\n- Expected: {}\n- Actual : {}",
150 file_path, md5, actual_md5);
154 curl_easy_strerror(res));
161 const std::string& md5,
162 const std::string& download_dir) {
164 if (mirrors.empty()) {
167 const std::string file_name =
169 for (
const std::string& url : mirrors) {
176 for (
const std::string& url : mirrors) {
179 }
catch (
const std::exception& ex) {
bool MakeDirectoryHierarchy(const std::string &directory)
bool DirectoryExists(const std::string &directory)
bool FileExists(const std::string &filename)
std::string GetFileNameWithoutDirectory(const std::string &filename)
std::string DownloadFromURL(const std::string &url, const std::string &md5, const std::string &download_dir)
Download a file from URL. If a file already exists and the MD5 hash matches, the download will be ski...
std::string GetMD5(const std::string &file_path)
Computes MD5 Hash for the given file.
std::string DownloadFromMirrors(const std::vector< std::string > &mirrors, const std::string &md5, const std::string &download_dir)
Download a file from list of mirror URLs. If a file already exists and the MD5 hash matches,...
static size_t WriteDataCb(void *ptr, size_t size, size_t nmemb, FILE *stream)
Generic file read and write utility for python interface.