#include "download_db.h" #include "util.h" #include #include #include #include #include // curl context CURL *curl = NULL; // libcrpyto context EVP_MD_CTX *mdctx = NULL; const EVP_MD *md = NULL; static size_t curl_write_cb(char* ptr, size_t size, size_t nmemb, void* usrdata) { // write the curl data to the file in usr data size_t written = fwrite(ptr, size, nmemb, (FILE *)usrdata); // update the hash with the new data EVP_DigestUpdate(mdctx, ptr, written); return written; } bool download_package(package_info_t* pkg) { if (curl == NULL) { // initialize curl if it isn't already CURLcode result = curl_global_init(CURL_GLOBAL_ALL); if (result != CURLE_OK) die("failed to initialize curl: %s", curl_easy_strerror(result)); curl = curl_easy_init(); // note(jqj): for now we reuse one easy curl object if (curl == NULL) die("failed to initialize easy curl"); /* come back to this if we have init and deinit */ /* curl_easy_cleanup(curl); */ /* curl_global_cleanup(); */ } if (mdctx == NULL) { // initialize libcrypto context if it isn't already mdctx = EVP_MD_CTX_new(); md = EVP_sha256(); /* come back to this if we have init and deinit */ /* EVP_MD_CTX_free(mdctx); */ } // initialize up sha256 hash EVP_DigestInit_ex(mdctx, md, NULL); // open file for writing and give it to the curl callback // todo(jqj): get actual path with name char* download_path = "download.tar.gz"; FILE* download_file = fopen(download_path, "wb"); if (download_file == NULL) die("failed open download file for writing (%s): %s", download_path, strerror(errno)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, download_file); // make curl request // callback writes to the file and feeds the hash curl_easy_setopt(curl, CURLOPT_URL, pkg->attrs.source_url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); CURLcode result = curl_easy_perform(curl); if(result != CURLE_OK) { log("failed to download package with curl: %s", curl_easy_strerror(result)); return false; } // get sha256 hash unsigned char hash[SHA256_DIGEST_LENGTH]; unsigned int hash_len; EVP_DigestFinal_ex(mdctx, hash, &hash_len); // get hexadecimal string of sha256 hash char hash_hex[SHA256_DIGEST_LENGTH * 2 + 1]; for(int i = 0; i < SHA256_DIGEST_LENGTH; i++) { sprintf(hash_hex + i*2,"%02x", hash[i]); } hash_hex[SHA256_DIGEST_LENGTH * 2] = '\0'; // confirm that hash matches checksum if (pkg->attrs.source_checksum != NULL) { if (strcmp(pkg->attrs.source_checksum, hash_hex) == 0) { print("checksum verified for %s: %s\n", pkg->attrs.id, hash_hex); return true; } else { log("FAIL: bad checksum for %s: %s\n", pkg->attrs.id, hash_hex); return false; } } else { // todo(jqj): not sure if we will allow packages without hashes yet log("WARNING: could not verify checksum for %s download because none was provided\n", pkg->attrs.id); return true; } return true; }