aboutsummaryrefslogtreecommitdiff
path: root/snag/src/source_db.c
diff options
context:
space:
mode:
authorJack Jamison <jackqjamison@gmail.com>2026-07-23 15:34:43 -0400
committerJack Jamison <jackqjamison@gmail.com>2026-07-23 15:34:43 -0400
commit9282991429a92d6df17213acdc49fe687f42da6c (patch)
tree0e7ca7f74ba0c8b445956eab4d47545e0adcf407 /snag/src/source_db.c
parentd9bc99629dd3f8624df98ead06f400c4ffb3880b (diff)
download db is now source db, ready to start build
Diffstat (limited to 'snag/src/source_db.c')
-rw-r--r--snag/src/source_db.c123
1 files changed, 123 insertions, 0 deletions
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 <curl/curl.h>
+#include <openssl/evp.h>
+#include <openssl/sha.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+// 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(); */
+ }
+}