aboutsummaryrefslogtreecommitdiff
path: root/snag/src/download_db.c
diff options
context:
space:
mode:
Diffstat (limited to 'snag/src/download_db.c')
-rw-r--r--snag/src/download_db.c101
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;
+}