1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
#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>
#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(); */
}
}
|