diff --git a/src/cache.c b/src/cache.c index bdda48fd..6d2fa56b 100644 --- a/src/cache.c +++ b/src/cache.c @@ -15,9 +15,12 @@ #include +#include #include #include #include +#include +#include #include "msg.h" #include "IO/Url.h" @@ -35,6 +38,8 @@ #include "timeout.hh" #include "uicmd.hh" +#include + /* Maximum initial size for the automatically-growing data buffer */ #define MAX_INIT_BUF 1024*1024 /* Maximum filesize for a URL, before offering a download */ @@ -62,6 +67,7 @@ typedef struct { int ExpectedSize; /* Goal size of the HTTP transfer (0 if unknown)*/ int TransferSize; /* Actual length of the HTTP transfer */ uint_t Flags; /* See Flag Defines in cache.h */ + mbedtls_sha256_context Hash; /* sha256 hash state */ } CacheEntry_t; @@ -207,6 +213,7 @@ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url) NewEntry->ExpectedSize = 0; NewEntry->TransferSize = 0; NewEntry->Flags = CA_IsEmpty | CA_InProgress | CA_KeepAlive; + mbedtls_sha256_init(&NewEntry->Hash); } /* @@ -247,6 +254,7 @@ static CacheEntry_t *Cache_entry_search_with_redirect(const DilloUrl *Url) */ static CacheEntry_t *Cache_entry_add(const DilloUrl *Url) { + int rc; CacheEntry_t *old_entry, *new_entry; if ((old_entry = Cache_entry_search(Url))) { @@ -256,6 +264,10 @@ static CacheEntry_t *Cache_entry_add(const DilloUrl *Url) new_entry = dNew(CacheEntry_t, 1); Cache_entry_init(new_entry, Url); /* Set safe values */ + rc = mbedtls_sha256_starts_ret(&new_entry->Hash, 0); + if (rc < 0) { + MSG_WARN("Failed to start SHA-256 calculation.\n"); + } dList_insert_sorted(CachedURLs, new_entry, Cache_entry_cmp); return new_entry; } @@ -312,6 +324,7 @@ static void Cache_entry_free(CacheEntry_t *entry) a_Decode_transfer_free(entry->TransferDecoder); if (entry->ContentDecoder) a_Decode_free(entry->ContentDecoder); + mbedtls_sha256_init(&entry->Hash); dFree(entry); } @@ -845,6 +858,143 @@ static int Cache_get_header(CacheEntry_t *entry, return 0; } +static char *dGetblobsdir () +{ + static char *blobsdir = NULL; + + if (!blobsdir) { + blobsdir = dStrconcat(dGethomedir(), "/.ssb/blobs", NULL); + } + return blobsdir; +} + +static int hash_to_path(char *buf, size_t *lenp, unsigned char id[32]) +{ + int len = snprintf(buf, *lenp, "%s/sha256/%02x/" + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + dGetblobsdir(), + id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], + id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15], + id[16], id[17], id[18], id[19], id[20], id[21], id[22], id[23], + id[24], id[25], id[26], id[27], id[28], id[29], id[30], id[31]); + if (len < 0 || len >= (int)*lenp) { errno = EMSGSIZE; return -1; } + *lenp = len; + return 0; +} + +static void Cache_finish_write(CacheEntry_t *entry) +{ + int rc; + FILE *entry_fp, *history_fp; + unsigned char hash_result[32]; + char *hash_b64; + char filename_tmp[512], filename[512], history_filename[512]; + char history_line[512]; + char time_str[64]; + const char *url = URL_STR_(entry->Url); + int url_len = strlen(url); + size_t filename_len = sizeof(filename); + size_t history_line_len; + time_t tmp_time; + static unsigned int tmp_i = 0; + + rc = mbedtls_sha256_finish_ret(&entry->Hash, hash_result); + if (rc < 0) { + MSG_WARN("Failed to finish SHA-256 calculation.\n"); + return; + } + + hash_b64 = a_Misc_encode_base64_2((char *)hash_result, 32); + + tmp_time = time(NULL); + rc = snprintf(filename_tmp, sizeof(filename_tmp), "%s/tmp/%d-%d", + dGetblobsdir(), (int)tmp_time, tmp_i++); + if (rc < 0 || rc > (int)sizeof(filename_tmp)) { + MSG_WARN("Failed to calculate filename.\n"); + return; + } + + rc = hash_to_path(filename, &filename_len, hash_result); + if (rc < 0) { + MSG_WARN("Failed to calculate path. %s\n", dStrerror(errno)); + unlink(filename); + return; + } + + if (access(filename, F_OK) != 0) { + entry_fp = fopen(filename_tmp, "w"); + if (entry_fp == NULL) { + MSG_WARN("Unable to open file for writing. " + "Error: %s. Filename: %s URL: %s\n", + dStrerror(errno), filename_tmp, url); + return; + } + + if (fwrite(entry->Data->str, (size_t)entry->Data->len, 1, entry_fp) != 1) { + MSG_WARN("Failed to write blob file. %s\n", dStrerror(errno)); + fclose(entry_fp); + unlink(filename_tmp); + return; + } + + if (fclose(entry_fp) != 0) { + MSG_WARN("Failed to close blob file. %s\n", dStrerror(errno)); + unlink(filename_tmp); + return; + } + + rc = rename(filename_tmp, filename); + if (rc < 0) { + MSG_WARN("Failed to rename blob file. %s\n", dStrerror(errno)); + unlink(filename_tmp); + return; + } + _MSG("Saved blob %s\n", filename); + } + + /* write it to history file */ + + rc = snprintf(history_filename, sizeof(history_filename), + "%s/.dillo/history.txt", + dGethomedir()); + if (rc < 0 || rc > (int)sizeof(history_filename)) { + MSG_WARN("Failed to calculate history filename.\n"); + return; + } + + if (strftime(time_str, sizeof(time_str), "%F %T %z", gmtime(&tmp_time)) == 0) { + MSG_WARN("Failed to format timestamp.\n"); + return; + } + + history_fp = fopen(history_filename, "a"); + if (history_fp == NULL) { + MSG_WARN("Failed to open history file.\n"); + return; + } + + if (!strncmp(url, "data:", 5)) url_len = 0; + + history_line_len = snprintf(history_line, sizeof(history_line), + "%s\t&%s.sha256\t%.*s\n", + time_str, + hash_b64, + url_len, + url); + if (history_line_len <= 0 || history_line_len > (int)sizeof(history_line)) { + MSG_WARN("Failed to create history entry.\n"); + fclose(history_fp); + return; + } + + if (fwrite(history_line, history_line_len, 1, history_fp) != 1) { + MSG_WARN("Failed to write history entry. %s\n", dStrerror(errno)); + fclose(history_fp); + return; + } + fclose(history_fp); +} + static void Cache_finish_msg(CacheEntry_t *entry) { if (!(entry->Flags & CA_InProgress)) { @@ -872,6 +1022,8 @@ static void Cache_finish_msg(CacheEntry_t *entry) } dStr_fit(entry->Data); /* fit buffer size! */ + Cache_finish_write(entry); + if ((entry = Cache_process_queue(entry))) { if (entry->Flags & CA_GotHeader) { Cache_unref_data(entry); @@ -891,6 +1043,7 @@ bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, const DilloUrl *Url) { int offset, len; + int rc; const char *str; Dstr *dstr1, *dstr2, *dstr3; bool_t done = FALSE; @@ -932,6 +1085,10 @@ bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size, len = dstr2->len; } dStr_append_l(entry->Data, str, len); + rc = mbedtls_sha256_update_ret(&entry->Hash, (unsigned char *)str, len); + if (rc < 0) { + MSG_WARN("Failed to update SHA-256 calculation.\n"); + } if (entry->CharsetDecoder && entry->UTF8Data) { dstr3 = a_Decode_process(entry->CharsetDecoder, str, len); dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len); diff --git a/src/misc.c b/src/misc.c index 00589999..dfd85b50 100644 --- a/src/misc.c +++ b/src/misc.c @@ -456,6 +456,40 @@ char *a_Misc_encode_base64(const char *in) return out; } +char *a_Misc_encode_base64_2(const char *in, int len) +{ + static const char *const base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + char *out = NULL; + int i = 0; + + if (in == NULL) return NULL; + + out = (char *)dMalloc((len + 2) / 3 * 4 + 1); + + for (; len >= 3; len -= 3) { + out[i++] = base64_hex[in[0] >> 2]; + out[i++] = base64_hex[((in[0]<<4) & 0x30) | (in[1]>>4)]; + out[i++] = base64_hex[((in[1]<<2) & 0x3c) | (in[2]>>6)]; + out[i++] = base64_hex[in[2] & 0x3f]; + in += 3; + } + + if (len > 0) { + unsigned char fragment; + out[i++] = base64_hex[in[0] >> 2]; + fragment = (in[0] << 4) & 0x30; + if (len > 1) fragment |= in[1] >> 4; + out[i++] = base64_hex[fragment]; + out[i++] = (len < 2) ? '=' : base64_hex[(in[1] << 2) & 0x3c]; + out[i++] = '='; + } + out[i] = '\0'; + return out; +} + + /* * Load a local file into a dStr. * Return value: dStr on success, NULL on error. diff --git a/src/misc.h b/src/misc.h index 75f0f78a..340dd45e 100644 --- a/src/misc.h +++ b/src/misc.h @@ -19,6 +19,7 @@ int a_Misc_content_type_cmp(const char* ct1, const char *ct2); int a_Misc_parse_geometry(char *geom, int *x, int *y, int *w, int *h); int a_Misc_parse_search_url(char *source, char **label, char **urlstr); char *a_Misc_encode_base64(const char *in); +char *a_Misc_encode_base64_2(const char *in, int len); Dstr *a_Misc_file2dstr(const char *filename); #ifdef __cplusplus