From 9282991429a92d6df17213acdc49fe687f42da6c Mon Sep 17 00:00:00 2001 From: Jack Jamison Date: Thu, 23 Jul 2026 15:34:43 -0400 Subject: download db is now source db, ready to start build --- snag/src/source_db.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 snag/src/source_db.c (limited to 'snag/src/source_db.c') diff --git a/snag/src/source_db.c b/snag/src/source_db.c new file mode 100644 index 0000000..ab7ec99 --- /dev/null +++ b/snag/src/source_db.c @@ -0,0 +1,123 @@ +#include "source_db.h" + +#include "util.h" + +#include +#include +#include +#include +#include +#include + +// libcrpyto context +EVP_MD_CTX *mdctx = NULL; +const EVP_MD *md = NULL; +void ensure_libcrypto(); + +// curl context +CURL *curl = NULL; +void ensure_libcurl(); + +// note: fixed location of source database for now +const char source_db_path[] = "./var/snag/sources"; +char* get_source_path(source_info_t* source) { return join_path(source_db_path, source->name); } + +bool verify_source(source_info_t* source) { + // verify source exists + char* source_path = get_source_path(source); + bool verified = access(source_path, F_OK) == 0; + + // todo(jqj): actually verify checksum + free(source_path); + return verified; +} + +static size_t download_write_callback(char* ptr, size_t size, size_t nmemb, void* usrdata); +bool download_source(source_info_t* source) { + + char* source_path = get_source_path(source); + print("Downloading %s\n", source->url); + + // initialize libcurl and libcrypto if they aren't already + ensure_libcurl(); + ensure_libcrypto(); + + // initialize sha256 hash + EVP_DigestInit_ex(mdctx, md, NULL); + + // open file for writing + FILE* download_file = fopen(source_path, "wb"); + if (download_file == NULL) die("failed open download file for writing (%s): %s", source_path, strerror(errno)); + + // make curl request with url and output file + // callback writes to the file and feeds the hash + curl_easy_setopt(curl, CURLOPT_WRITEDATA, download_file); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_write_callback); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(curl, CURLOPT_URL, source->url); + CURLcode result = curl_easy_perform(curl); + fclose(download_file); + if(result != CURLE_OK) { + // todo(jqj): handle better, maybe try again + print("failed to download package with curl: %s", curl_easy_strerror(result)); + goto fail; + } + + // get sha256 hash + unsigned char hash[SHA256_DIGEST_LENGTH]; + unsigned int hash_len; + EVP_DigestFinal_ex(mdctx, hash, &hash_len); + + // make 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 checksum matches + assert(source->checksum != NULL); + if (strcmp(source->checksum, hash_hex) == 0) { + print("checksum verified for download: %s\n", hash_hex); + return true; + } else { + print("FAIL: bad checksum for download: %s\n", hash_hex); + goto fail; + } + +fail: + if (access(source_path, F_OK) == 0) remove(source_path); + free(source_path); + return false; +} + +static size_t download_write_callback(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; +} + +void ensure_libcrypto() { + if (mdctx == NULL) { + mdctx = EVP_MD_CTX_new(); + md = EVP_sha256(); + /* come back to this if we have init and deinit */ + /* EVP_MD_CTX_free(mdctx); */ + } +} + +void ensure_libcurl() { + if (curl == NULL) { + 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(); */ + } +} -- cgit v1.2.3