diff options
| author | Jack Jamison <jackqjamison@gmail.com> | 2026-07-22 14:54:42 -0400 |
|---|---|---|
| committer | Jack Jamison <jackqjamison@gmail.com> | 2026-07-22 14:54:42 -0400 |
| commit | dfff353cf081024f9e145629dd51ef08526bb54d (patch) | |
| tree | 143ea3f1d33edf5d0527768fa9461750e417578a /snag/src/download_db.c | |
| parent | c293d3cc413991e4fb18b963427a6e55afd2b5c0 (diff) | |
basic download and hash with libcurl and libcrypto
Diffstat (limited to 'snag/src/download_db.c')
| -rw-r--r-- | snag/src/download_db.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/snag/src/download_db.c b/snag/src/download_db.c new file mode 100644 index 0000000..3f51347 --- /dev/null +++ b/snag/src/download_db.c @@ -0,0 +1,101 @@ +#include "download_db.h" + +#include "util.h" + +#include <curl/curl.h> +#include <openssl/evp.h> +#include <openssl/sha.h> +#include <string.h> +#include <errno.h> + +// 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; +} |
