git ssb

0+

cel / dillo-history



Commit b9f9b0d3e41bae8e43e88364803fdbc7f7656dc1

Release

cel committed on 11/1/2019, 2:41:50 AM

Files changed

LICENSEadded
Makefileadded
README.mdadded
dillo-patch.diffadded
history.filter.dpiadded
LICENSEView
@@ -1,0 +1,13 @@
1 +© 2019 cel @f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519
2 +
3 +Permission to use, copy, modify, and distribute this software for any
4 +purpose with or without fee is hereby granted, provided that the above
5 +copyright notice and this permission notice appear in all copies.
6 +
7 +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10 +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12 +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13 +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
MakefileView
@@ -1,0 +1,27 @@
1 +NAME = history
2 +BIN = history.filter.dpi
3 +DILLO_DIR = ~/.dillo
4 +DPI_DIR = $(DILLO_DIR)/dpi
5 +DPIDRC = $(DILLO_DIR)/dpidrc
6 +
7 +all: $(BIN)
8 +
9 +$(DPIDRC):
10 + cp /etc/dillo/dpidrc $@
11 +
12 +install-proto: $(DPIDRC)
13 + echo 'proto.history=$(NAME)/$(BIN)' >> $<
14 +
15 +install: $(BIN) install-proto
16 + mkdir -p $(DPI_DIR)/$(NAME)
17 + cp -f $(BIN) $(DPI_DIR)/$(NAME)
18 +
19 +link: $(BIN) install-proto
20 + mkdir -p $(DPI_DIR)/$(NAME)
21 + ln -frs $(BIN) $(DPI_DIR)/$(NAME)
22 +
23 +uninstall: $(BIN)
24 + rm -f $(DPI_DIR)/$(NAME)/$(BIN)
25 +
26 +.PHONY:
27 + all install link uninstall
README.mdView
@@ -1,0 +1,30 @@
1 +# dillo-history
2 +
3 +Persistent history saving for [Dillo][].
4 +
5 +This project contains:
6 +- A patch for dillo to make it save each resource from its cache to disk, and record entries in a history file.
7 +- A plugin for dillo to view the history file and its entries.
8 +
9 +The history file is `~/.dillo/history.txt` and its format is TSV. Each entry contains a timestamp, SHA256 content hash, and URL.
10 +
11 +The plugin serves requests of the form `history:[<limit>][/<pattern>]` where `<limit>` is the number of entries to show (default is 35) and `<pattern>` is a regex to grep for the history files (for URL, hash, and/or timestamp) (default is to show all entries).
12 +
13 +## Screenshot
14 +
15 +![dillo-history-screenshot.png](&REcIx+kvUF2vQt0PDyb49f2q4qDEZ08b07hlskwScMI=.sha256 "Screenshot showing dillo-history)
16 +
17 +The content hashes are in [SSB blob format][ssb links]: `&XXX.sha256`) where XXX is the content hash in base64. Their contents are saved as files in [SSB's blob store][ssb-blobs] at `~/.ssb/blobs/sha256/XX/XXXX...` where `XXXXX...` is the content hash in hexadecimal, lowercase.
18 +
19 +[Dillo]: https://www.dillo.org/
20 +[ssb links]: https://github.com/ssbc/docs/blob/master/ssb/linking.md
21 +[ssb-blobs]: https://github.com/ssbc/ssb-blobs
22 +
23 +## Install
24 +
25 +Install the plugin with `make install && dpidc stop`.
26 +Install the patch by downloading Dillo's source code, applying the patch and compiling dillo.
27 +
28 +## License
29 +
30 +MIT
dillo-patch.diffView
@@ -1,0 +1,298 @@
1 +diff --git a/src/cache.c b/src/cache.c
2 +index bdda48fd..6d2fa56b 100644
3 +--- a/src/cache.c
4 ++++ b/src/cache.c
5 +@@ -15,9 +15,12 @@
6 +
7 + #include <sys/types.h>
8 +
9 ++#include <errno.h>
10 + #include <limits.h>
11 + #include <stdlib.h>
12 + #include <string.h>
13 ++#include <time.h>
14 ++#include <unistd.h>
15 +
16 + #include "msg.h"
17 + #include "IO/Url.h"
18 +@@ -35,6 +38,8 @@
19 + #include "timeout.hh"
20 + #include "uicmd.hh"
21 +
22 ++#include <mbedtls/sha256.h>
23 ++
24 + /* Maximum initial size for the automatically-growing data buffer */
25 + #define MAX_INIT_BUF 1024*1024
26 + /* Maximum filesize for a URL, before offering a download */
27 +@@ -62,6 +67,7 @@ typedef struct {
28 + int ExpectedSize; /* Goal size of the HTTP transfer (0 if unknown)*/
29 + int TransferSize; /* Actual length of the HTTP transfer */
30 + uint_t Flags; /* See Flag Defines in cache.h */
31 ++ mbedtls_sha256_context Hash; /* sha256 hash state */
32 + } CacheEntry_t;
33 +
34 +
35 +@@ -207,6 +213,7 @@ static void Cache_entry_init(CacheEntry_t *NewEntry, const DilloUrl *Url)
36 + NewEntry->ExpectedSize = 0;
37 + NewEntry->TransferSize = 0;
38 + NewEntry->Flags = CA_IsEmpty | CA_InProgress | CA_KeepAlive;
39 ++ mbedtls_sha256_init(&NewEntry->Hash);
40 + }
41 +
42 + /*
43 +@@ -247,6 +254,7 @@ static CacheEntry_t *Cache_entry_search_with_redirect(const DilloUrl *Url)
44 + */
45 + static CacheEntry_t *Cache_entry_add(const DilloUrl *Url)
46 + {
47 ++ int rc;
48 + CacheEntry_t *old_entry, *new_entry;
49 +
50 + if ((old_entry = Cache_entry_search(Url))) {
51 +@@ -256,6 +264,10 @@ static CacheEntry_t *Cache_entry_add(const DilloUrl *Url)
52 +
53 + new_entry = dNew(CacheEntry_t, 1);
54 + Cache_entry_init(new_entry, Url); /* Set safe values */
55 ++ rc = mbedtls_sha256_starts_ret(&new_entry->Hash, 0);
56 ++ if (rc < 0) {
57 ++ MSG_WARN("Failed to start SHA-256 calculation.\n");
58 ++ }
59 + dList_insert_sorted(CachedURLs, new_entry, Cache_entry_cmp);
60 + return new_entry;
61 + }
62 +@@ -312,6 +324,7 @@ static void Cache_entry_free(CacheEntry_t *entry)
63 + a_Decode_transfer_free(entry->TransferDecoder);
64 + if (entry->ContentDecoder)
65 + a_Decode_free(entry->ContentDecoder);
66 ++ mbedtls_sha256_init(&entry->Hash);
67 + dFree(entry);
68 + }
69 +
70 +@@ -845,6 +858,143 @@ static int Cache_get_header(CacheEntry_t *entry,
71 + return 0;
72 + }
73 +
74 ++static char *dGetblobsdir ()
75 ++{
76 ++ static char *blobsdir = NULL;
77 ++
78 ++ if (!blobsdir) {
79 ++ blobsdir = dStrconcat(dGethomedir(), "/.ssb/blobs", NULL);
80 ++ }
81 ++ return blobsdir;
82 ++}
83 ++
84 ++static int hash_to_path(char *buf, size_t *lenp, unsigned char id[32])
85 ++{
86 ++ int len = snprintf(buf, *lenp, "%s/sha256/%02x/"
87 ++ "%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",
88 ++ dGetblobsdir(),
89 ++ id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7],
90 ++ id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15],
91 ++ id[16], id[17], id[18], id[19], id[20], id[21], id[22], id[23],
92 ++ id[24], id[25], id[26], id[27], id[28], id[29], id[30], id[31]);
93 ++ if (len < 0 || len >= (int)*lenp) { errno = EMSGSIZE; return -1; }
94 ++ *lenp = len;
95 ++ return 0;
96 ++}
97 ++
98 ++static void Cache_finish_write(CacheEntry_t *entry)
99 ++{
100 ++ int rc;
101 ++ FILE *entry_fp, *history_fp;
102 ++ unsigned char hash_result[32];
103 ++ char *hash_b64;
104 ++ char filename_tmp[512], filename[512], history_filename[512];
105 ++ char history_line[512];
106 ++ char time_str[64];
107 ++ const char *url = URL_STR_(entry->Url);
108 ++ int url_len = strlen(url);
109 ++ size_t filename_len = sizeof(filename);
110 ++ size_t history_line_len;
111 ++ time_t tmp_time;
112 ++ static unsigned int tmp_i = 0;
113 ++
114 ++ rc = mbedtls_sha256_finish_ret(&entry->Hash, hash_result);
115 ++ if (rc < 0) {
116 ++ MSG_WARN("Failed to finish SHA-256 calculation.\n");
117 ++ return;
118 ++ }
119 ++
120 ++ hash_b64 = a_Misc_encode_base64_2((char *)hash_result, 32);
121 ++
122 ++ tmp_time = time(NULL);
123 ++ rc = snprintf(filename_tmp, sizeof(filename_tmp), "%s/tmp/%d-%d",
124 ++ dGetblobsdir(), (int)tmp_time, tmp_i++);
125 ++ if (rc < 0 || rc > (int)sizeof(filename_tmp)) {
126 ++ MSG_WARN("Failed to calculate filename.\n");
127 ++ return;
128 ++ }
129 ++
130 ++ rc = hash_to_path(filename, &filename_len, hash_result);
131 ++ if (rc < 0) {
132 ++ MSG_WARN("Failed to calculate path. %s\n", dStrerror(errno));
133 ++ unlink(filename);
134 ++ return;
135 ++ }
136 ++
137 ++ if (access(filename, F_OK) != 0) {
138 ++ entry_fp = fopen(filename_tmp, "w");
139 ++ if (entry_fp == NULL) {
140 ++ MSG_WARN("Unable to open file for writing. "
141 ++ "Error: %s. Filename: %s URL: %s\n",
142 ++ dStrerror(errno), filename_tmp, url);
143 ++ return;
144 ++ }
145 ++
146 ++ if (fwrite(entry->Data->str, (size_t)entry->Data->len, 1, entry_fp) != 1) {
147 ++ MSG_WARN("Failed to write blob file. %s\n", dStrerror(errno));
148 ++ fclose(entry_fp);
149 ++ unlink(filename_tmp);
150 ++ return;
151 ++ }
152 ++
153 ++ if (fclose(entry_fp) != 0) {
154 ++ MSG_WARN("Failed to close blob file. %s\n", dStrerror(errno));
155 ++ unlink(filename_tmp);
156 ++ return;
157 ++ }
158 ++
159 ++ rc = rename(filename_tmp, filename);
160 ++ if (rc < 0) {
161 ++ MSG_WARN("Failed to rename blob file. %s\n", dStrerror(errno));
162 ++ unlink(filename_tmp);
163 ++ return;
164 ++ }
165 ++ _MSG("Saved blob %s\n", filename);
166 ++ }
167 ++
168 ++ /* write it to history file */
169 ++
170 ++ rc = snprintf(history_filename, sizeof(history_filename),
171 ++ "%s/.dillo/history.txt",
172 ++ dGethomedir());
173 ++ if (rc < 0 || rc > (int)sizeof(history_filename)) {
174 ++ MSG_WARN("Failed to calculate history filename.\n");
175 ++ return;
176 ++ }
177 ++
178 ++ if (strftime(time_str, sizeof(time_str), "%F %T %z", gmtime(&tmp_time)) == 0) {
179 ++ MSG_WARN("Failed to format timestamp.\n");
180 ++ return;
181 ++ }
182 ++
183 ++ history_fp = fopen(history_filename, "a");
184 ++ if (history_fp == NULL) {
185 ++ MSG_WARN("Failed to open history file.\n");
186 ++ return;
187 ++ }
188 ++
189 ++ if (!strncmp(url, "data:", 5)) url_len = 0;
190 ++
191 ++ history_line_len = snprintf(history_line, sizeof(history_line),
192 ++ "%s\t&%s.sha256\t%.*s\n",
193 ++ time_str,
194 ++ hash_b64,
195 ++ url_len,
196 ++ url);
197 ++ if (history_line_len <= 0 || history_line_len > (int)sizeof(history_line)) {
198 ++ MSG_WARN("Failed to create history entry.\n");
199 ++ fclose(history_fp);
200 ++ return;
201 ++ }
202 ++
203 ++ if (fwrite(history_line, history_line_len, 1, history_fp) != 1) {
204 ++ MSG_WARN("Failed to write history entry. %s\n", dStrerror(errno));
205 ++ fclose(history_fp);
206 ++ return;
207 ++ }
208 ++ fclose(history_fp);
209 ++}
210 ++
211 + static void Cache_finish_msg(CacheEntry_t *entry)
212 + {
213 + if (!(entry->Flags & CA_InProgress)) {
214 +@@ -872,6 +1022,8 @@ static void Cache_finish_msg(CacheEntry_t *entry)
215 + }
216 + dStr_fit(entry->Data); /* fit buffer size! */
217 +
218 ++ Cache_finish_write(entry);
219 ++
220 + if ((entry = Cache_process_queue(entry))) {
221 + if (entry->Flags & CA_GotHeader) {
222 + Cache_unref_data(entry);
223 +@@ -891,6 +1043,7 @@ bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
224 + const DilloUrl *Url)
225 + {
226 + int offset, len;
227 ++ int rc;
228 + const char *str;
229 + Dstr *dstr1, *dstr2, *dstr3;
230 + bool_t done = FALSE;
231 +@@ -932,6 +1085,10 @@ bool_t a_Cache_process_dbuf(int Op, const char *buf, size_t buf_size,
232 + len = dstr2->len;
233 + }
234 + dStr_append_l(entry->Data, str, len);
235 ++ rc = mbedtls_sha256_update_ret(&entry->Hash, (unsigned char *)str, len);
236 ++ if (rc < 0) {
237 ++ MSG_WARN("Failed to update SHA-256 calculation.\n");
238 ++ }
239 + if (entry->CharsetDecoder && entry->UTF8Data) {
240 + dstr3 = a_Decode_process(entry->CharsetDecoder, str, len);
241 + dStr_append_l(entry->UTF8Data, dstr3->str, dstr3->len);
242 +diff --git a/src/misc.c b/src/misc.c
243 +index 00589999..dfd85b50 100644
244 +--- a/src/misc.c
245 ++++ b/src/misc.c
246 +@@ -456,6 +456,40 @@ char *a_Misc_encode_base64(const char *in)
247 + return out;
248 + }
249 +
250 ++char *a_Misc_encode_base64_2(const char *in, int len)
251 ++{
252 ++ static const char *const base64_hex = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
253 ++ "abcdefghijklmnopqrstuvwxyz"
254 ++ "0123456789+/";
255 ++ char *out = NULL;
256 ++ int i = 0;
257 ++
258 ++ if (in == NULL) return NULL;
259 ++
260 ++ out = (char *)dMalloc((len + 2) / 3 * 4 + 1);
261 ++
262 ++ for (; len >= 3; len -= 3) {
263 ++ out[i++] = base64_hex[in[0] >> 2];
264 ++ out[i++] = base64_hex[((in[0]<<4) & 0x30) | (in[1]>>4)];
265 ++ out[i++] = base64_hex[((in[1]<<2) & 0x3c) | (in[2]>>6)];
266 ++ out[i++] = base64_hex[in[2] & 0x3f];
267 ++ in += 3;
268 ++ }
269 ++
270 ++ if (len > 0) {
271 ++ unsigned char fragment;
272 ++ out[i++] = base64_hex[in[0] >> 2];
273 ++ fragment = (in[0] << 4) & 0x30;
274 ++ if (len > 1) fragment |= in[1] >> 4;
275 ++ out[i++] = base64_hex[fragment];
276 ++ out[i++] = (len < 2) ? '=' : base64_hex[(in[1] << 2) & 0x3c];
277 ++ out[i++] = '=';
278 ++ }
279 ++ out[i] = '\0';
280 ++ return out;
281 ++}
282 ++
283 ++
284 + /*
285 + * Load a local file into a dStr.
286 + * Return value: dStr on success, NULL on error.
287 +diff --git a/src/misc.h b/src/misc.h
288 +index 75f0f78a..340dd45e 100644
289 +--- a/src/misc.h
290 ++++ b/src/misc.h
291 +@@ -19,6 +19,7 @@ int a_Misc_content_type_cmp(const char* ct1, const char *ct2);
292 + int a_Misc_parse_geometry(char *geom, int *x, int *y, int *w, int *h);
293 + int a_Misc_parse_search_url(char *source, char **label, char **urlstr);
294 + char *a_Misc_encode_base64(const char *in);
295 ++char *a_Misc_encode_base64_2(const char *in, int len);
296 + Dstr *a_Misc_file2dstr(const char *filename);
297 +
298 + #ifdef __cplusplus
history.filter.dpiView
@@ -1,0 +1,54 @@
1 +#!/bin/bash
2 +read -d '>' auth
3 +read -d '>' cmd
4 +case "$cmd" in
5 + "<cmd='open_url' url='"*);;
6 + *) echo $cmd; exit;;
7 +esac
8 +url=${cmd#"<cmd='open_url' url='"}
9 +url=${url%"' '"}
10 +
11 +serve_404() {
12 + printf "<cmd='start_send_page' url='' '>\n"
13 + printf "Content-type: text/plain\r\n\r\n"
14 + echo Not found
15 +}
16 +
17 +serve_history() {
18 + limit=${1:-35}
19 + grep=$2
20 + printf "<cmd='start_send_page' url='' '>\n"
21 + printf "Content-type: text/html\r\n\r\n"
22 + cat <<-EOF
23 + <!doctype html>
24 + <html>
25 + <head>
26 + <meta charset=utf-8>
27 + <title>History</title>
28 + </head>
29 + <body>
30 + EOF
31 + exec 2>&1
32 + dillo_dir=${0%/*/*/*}
33 + history_file=$dillo_dir/history.txt
34 + if [ -n "$grep" ]; then grep "$grep"; else cat
35 + fi < "$history_file" | tail -n $limit | tac | awk '
36 + {
37 + timestamp = $1 " " $2
38 + if ($3 != "+0000") {
39 + timestamp = timestamp " " $3
40 + }
41 + printf "<div>%s <a href=\"ssb:%s\"> <code>%s</code></a> <a href=\"%s\">%s</a></div>\n",
42 + timestamp, $4, $4, $5, $5
43 + }'
44 + cat <<-EOF
45 + </body>
46 + </html>
47 + EOF
48 +}
49 +
50 +case "$url" in
51 + history:*/*) a="${url#history:}"; serve_history "${a%/*}" "${a#*/}";;
52 + history:*) serve_history "${url#history:}";;
53 + *) serve_404;;
54 +esac

Built with git-ssb-web