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 | |
| parent | c293d3cc413991e4fb18b963427a6e55afd2b5c0 (diff) | |
basic download and hash with libcurl and libcrypto
Diffstat (limited to 'snag/src')
| -rw-r--r-- | snag/src/download_db.c | 101 | ||||
| -rw-r--r-- | snag/src/download_db.h | 10 | ||||
| -rw-r--r-- | snag/src/main.c | 7 | ||||
| -rw-r--r-- | snag/src/package.h | 7 | ||||
| -rw-r--r-- | snag/src/package_db.c | 6 | ||||
| -rw-r--r-- | snag/src/package_db.h | 4 | ||||
| -rw-r--r-- | snag/src/util.c | 14 | ||||
| -rw-r--r-- | snag/src/util.h | 6 |
8 files changed, 143 insertions, 12 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; +} diff --git a/snag/src/download_db.h b/snag/src/download_db.h new file mode 100644 index 0000000..fbf7b03 --- /dev/null +++ b/snag/src/download_db.h @@ -0,0 +1,10 @@ +#ifndef DOWNLOAD_DB_H +#define DOWNLOAD_DB_H + +#include <stdbool.h> + +#include "package.h" + +bool download_package(package_info_t* pkg); + +#endif diff --git a/snag/src/main.c b/snag/src/main.c index 7d5ea58..0813032 100644 --- a/snag/src/main.c +++ b/snag/src/main.c @@ -1,8 +1,8 @@ -#include <stdio.h> +#include "libargs/args.h" #include "util.h" -#include "libargs/args.h" #include "package_db.h" +#include "download_db.h" int cmd_install(char* cmd_name, ArgParser* parser); @@ -43,8 +43,9 @@ int cmd_install(char* cmd_name, ArgParser* parser) { char* pkgid = ap_get_arg_at_index(parser, i); package_info_t info; if (load_package_info(pkgid, &info)) { - print("Installing %s - %s\n", info.attrs.name, info.attrs.desc); print("Downloading '%s'\n", info.attrs.source_url); + download_package(&info); + print("Installing %s - %s\n", info.attrs.name, info.attrs.desc); run_package_install(&info); } } diff --git a/snag/src/package.h b/snag/src/package.h index 3ff58b6..b848ce6 100644 --- a/snag/src/package.h +++ b/snag/src/package.h @@ -1,3 +1,6 @@ +#ifndef PACKAGE_H +#define PACKAGE_H + typedef struct { char* id; char* name; @@ -6,7 +9,7 @@ typedef struct { char* license; char* version; char* source_url; - char* source_hash; + char* source_checksum; char buffer[4069]; } package_attrs_t; @@ -14,3 +17,5 @@ typedef struct { char* script_path; package_attrs_t attrs; } package_info_t; + +#endif diff --git a/snag/src/package_db.c b/snag/src/package_db.c index 0bae257..3974120 100644 --- a/snag/src/package_db.c +++ b/snag/src/package_db.c @@ -80,7 +80,7 @@ bool load_package_info(char* pkgid, package_info_t* info) { // wait for child to finish int wstatus; if (waitpid(pid, &wstatus, 0) < 0) die("waitpid failed: %s", strerror(errno)); - if (WEXITSTATUS(wstatus) != 0) die("the child failed to execute get_pkg_attr script"); + if (WEXITSTATUS(wstatus) != 0) die("the child failed to execute get_pkg_attr script: %d", WEXITSTATUS(wstatus)); // read all bytes from pipe into attr buffer ssize_t bytes_read = read(pipefds[0], info->attrs.buffer, sizeof(info->attrs.buffer)); @@ -108,8 +108,8 @@ bool load_package_info(char* pkgid, package_info_t* info) { info->attrs.version = &info->attrs.buffer[i]; break; case 'u': info->attrs.source_url = &info->attrs.buffer[i]; break; - case 'h': - info->attrs.source_hash = &info->attrs.buffer[i]; break; + case 'c': + info->attrs.source_checksum = &info->attrs.buffer[i]; break; } for(;info->attrs.buffer[i] && i < bytes_read; i++); i++; diff --git a/snag/src/package_db.h b/snag/src/package_db.h index bae1cdd..17ff58d 100644 --- a/snag/src/package_db.h +++ b/snag/src/package_db.h @@ -1,5 +1,5 @@ -#ifndef PKG_DB_H -#define PKG_DB_H +#ifndef PACKAGE_DB_H +#define PACKAGE_DB_H #include <stdbool.h> diff --git a/snag/src/util.c b/snag/src/util.c index 8cc9b18..e79fd34 100644 --- a/snag/src/util.c +++ b/snag/src/util.c @@ -45,6 +45,20 @@ void* _emalloc(int line, const char* file, size_t size, char* alloc_reason) { } #endif +void printout(const char* format, ...) { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); +} + +void print(const char* format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + char* vastrcat_(int dummy, ...) { va_list args; diff --git a/snag/src/util.h b/snag/src/util.h index eca4e4d..1a05ab0 100644 --- a/snag/src/util.h +++ b/snag/src/util.h @@ -20,9 +20,9 @@ void* _emalloc(int line, const char* file, size_t size, char* alloc_reason); #endif -// functions for outputting and displaying text -#define printout(fmt, ...) fprintf(stdout, fmt, ##__VA_ARGS__) -#define print(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) +// functions for outputting and displaying text, log is used for nonroutine bad things +void printout(const char* format, ...) __attribute__((format(printf, 1, 2))); +void print(const char* format, ...) __attribute__((format(printf, 1, 2))); // string functions #define vastrcat(...) vastrcat_(0, __VA_ARGS__, NULL) |
