#include "source_db.h" #include "util.h" #include #include #include #include #include #include #define EXTRACT_TAR_SCRIPT "./scripts/extract_tar_contents.sh" // 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(const 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; } bool unpack_source(const source_info_t* source, char* dest_path) { char* source_path = get_source_path(source); char* command = vastrcat(EXTRACT_TAR_SCRIPT, " ", source_path, " ", dest_path); // just run the unpack script. we can rewrite in c in the future int ret = system(command); if (ret == -1) { die("failed to execute unpack command '%s': %s", command, strerror(errno)); } else if (!WIFEXITED(ret)) { die("failed to execute unpack command '%s' (didn't exit)", command); } else if (WEXITSTATUS(ret) != 0) { die("unpack command '%s' failed: exit status %d", command, WEXITSTATUS(ret)); } free(command); free(source_path); return true; } 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(); */ } }