diff options
author | John Keeping | 2018-03-31 16:15:48 +0100 |
---|---|---|
committer | Jason A. Donenfeld | 2018-06-27 18:11:19 +0200 |
commit | c712d5ac434b9ee8cb4e63a173a2538e1878637f (patch) | |
tree | 8b4f71d0fb9ef478409109fbf485532ae90660f0 | |
parent | 71d14d9c98c39a6683780060f84429a3a7e5b348 (diff) | |
download | cgit-c712d5ac434b9ee8cb4e63a173a2538e1878637f.tar.gz cgit-c712d5ac434b9ee8cb4e63a173a2538e1878637f.tar.bz2 cgit-c712d5ac434b9ee8cb4e63a173a2538e1878637f.zip |
snapshot: support archive signatures
Read signatures from the notes refs refs/notes/signatures/$FORMAT where
FORMAT is one of our archive formats ("tar", "tar.gz", ...). The note
is expected to simply contain the signature content to be returned when
the snapshot "${filename}.asc" is requested, so the signature for
cgit-1.1.tar.xz can be stored against the v1.1 tag with:
git notes --ref=refs/notes/signatures/tar.xz add -C "$(
gpg --output - --armor --detach-sign cgit-1.1.tar.xz |
git hash-object -w --stdin
)" v1.1
and then downloaded by simply appending ".asc" to the archive URL.
Signed-off-by: John Keeping <john@keeping.me.uk>
Reviewed-by: Christian Hesse <mail@eworm.de>
-rw-r--r-- | cgit.h | 2 | ||||
-rw-r--r-- | ui-shared.c | 7 | ||||
-rw-r--r-- | ui-snapshot.c | 76 |
3 files changed, 84 insertions, 1 deletions
@@ -374,6 +374,8 @@ extern void cgit_parse_url(const char *url); | |||
374 | extern const char *cgit_repobasename(const char *reponame); | 374 | extern const char *cgit_repobasename(const char *reponame); |
375 | 375 | ||
376 | extern int cgit_parse_snapshots_mask(const char *str); | 376 | extern int cgit_parse_snapshots_mask(const char *str); |
377 | extern const struct object_id *cgit_snapshot_get_sig(const char *ref, | ||
378 | const struct cgit_snapshot_format *f); | ||
377 | 379 | ||
378 | extern int cgit_open_filter(struct cgit_filter *filter, ...); | 380 | extern int cgit_open_filter(struct cgit_filter *filter, ...); |
379 | extern int cgit_close_filter(struct cgit_filter *filter); | 381 | extern int cgit_close_filter(struct cgit_filter *filter); |
diff --git a/ui-shared.c b/ui-shared.c index 9d7ee3d..8a786e0 100644 --- a/ui-shared.c +++ b/ui-shared.c | |||
@@ -1133,6 +1133,13 @@ void cgit_print_snapshot_links(const struct cgit_repo *repo, const char *ref, | |||
1133 | strbuf_addstr(&filename, f->suffix); | 1133 | strbuf_addstr(&filename, f->suffix); |
1134 | cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, | 1134 | cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, |
1135 | filename.buf); | 1135 | filename.buf); |
1136 | if (cgit_snapshot_get_sig(ref, f)) { | ||
1137 | strbuf_addstr(&filename, ".asc"); | ||
1138 | html(" ("); | ||
1139 | cgit_snapshot_link("sig", NULL, NULL, NULL, NULL, | ||
1140 | filename.buf); | ||
1141 | html(")"); | ||
1142 | } | ||
1136 | html(separator); | 1143 | html(separator); |
1137 | } | 1144 | } |
1138 | strbuf_release(&filename); | 1145 | strbuf_release(&filename); |
diff --git a/ui-snapshot.c b/ui-snapshot.c index abf8399..c7611e8 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c | |||
@@ -94,6 +94,31 @@ const struct cgit_snapshot_format cgit_snapshot_formats[] = { | |||
94 | { NULL } | 94 | { NULL } |
95 | }; | 95 | }; |
96 | 96 | ||
97 | static struct notes_tree snapshot_sig_notes[ARRAY_SIZE(cgit_snapshot_formats)]; | ||
98 | |||
99 | const struct object_id *cgit_snapshot_get_sig(const char *ref, | ||
100 | const struct cgit_snapshot_format *f) | ||
101 | { | ||
102 | struct notes_tree *tree; | ||
103 | struct object_id oid; | ||
104 | |||
105 | if (get_oid(ref, &oid)) | ||
106 | return NULL; | ||
107 | |||
108 | tree = &snapshot_sig_notes[f - &cgit_snapshot_formats[0]]; | ||
109 | if (!tree->initialized) { | ||
110 | struct strbuf notes_ref = STRBUF_INIT; | ||
111 | |||
112 | strbuf_addf(¬es_ref, "refs/notes/signatures/%s", | ||
113 | f->suffix + 1); | ||
114 | |||
115 | init_notes(tree, notes_ref.buf, combine_notes_ignore, 0); | ||
116 | strbuf_release(¬es_ref); | ||
117 | } | ||
118 | |||
119 | return get_note(tree, &oid); | ||
120 | } | ||
121 | |||
97 | static const struct cgit_snapshot_format *get_format(const char *filename) | 122 | static const struct cgit_snapshot_format *get_format(const char *filename) |
98 | { | 123 | { |
99 | const struct cgit_snapshot_format *fmt; | 124 | const struct cgit_snapshot_format *fmt; |
@@ -129,6 +154,39 @@ static int make_snapshot(const struct cgit_snapshot_format *format, | |||
129 | return 0; | 154 | return 0; |
130 | } | 155 | } |
131 | 156 | ||
157 | static int write_sig(const struct cgit_snapshot_format *format, | ||
158 | const char *hex, const char *archive, | ||
159 | const char *filename) | ||
160 | { | ||
161 | const struct object_id *note = cgit_snapshot_get_sig(hex, format); | ||
162 | enum object_type type; | ||
163 | unsigned long size; | ||
164 | char *buf; | ||
165 | |||
166 | if (!note) { | ||
167 | cgit_print_error_page(404, "Not found", | ||
168 | "No signature for %s", archive); | ||
169 | return 0; | ||
170 | } | ||
171 | |||
172 | buf = read_sha1_file(note->hash, &type, &size); | ||
173 | if (!buf) { | ||
174 | cgit_print_error_page(404, "Not found", "Not found"); | ||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | html("X-Content-Type-Options: nosniff\n"); | ||
179 | html("Content-Security-Policy: default-src 'none'\n"); | ||
180 | ctx.page.etag = oid_to_hex(note); | ||
181 | ctx.page.mimetype = xstrdup("application/pgp-signature"); | ||
182 | ctx.page.filename = xstrdup(filename); | ||
183 | cgit_print_http_headers(); | ||
184 | |||
185 | html_raw(buf, size); | ||
186 | free(buf); | ||
187 | return 0; | ||
188 | } | ||
189 | |||
132 | /* Try to guess the requested revision from the requested snapshot name. | 190 | /* Try to guess the requested revision from the requested snapshot name. |
133 | * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become | 191 | * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become |
134 | * "cgit-0.7.2". If this is a valid commit object name we've got a winner. | 192 | * "cgit-0.7.2". If this is a valid commit object name we've got a winner. |
@@ -185,6 +243,8 @@ void cgit_print_snapshot(const char *head, const char *hex, | |||
185 | const char *filename, int dwim) | 243 | const char *filename, int dwim) |
186 | { | 244 | { |
187 | const struct cgit_snapshot_format* f; | 245 | const struct cgit_snapshot_format* f; |
246 | const char *sig_filename = NULL; | ||
247 | char *adj_filename = NULL; | ||
188 | char *prefix = NULL; | 248 | char *prefix = NULL; |
189 | 249 | ||
190 | if (!filename) { | 250 | if (!filename) { |
@@ -193,6 +253,15 @@ void cgit_print_snapshot(const char *head, const char *hex, | |||
193 | return; | 253 | return; |
194 | } | 254 | } |
195 | 255 | ||
256 | if (ends_with(filename, ".asc")) { | ||
257 | sig_filename = filename; | ||
258 | |||
259 | /* Strip ".asc" from filename for common format processing */ | ||
260 | adj_filename = xstrdup(filename); | ||
261 | adj_filename[strlen(adj_filename) - 4] = '\0'; | ||
262 | filename = adj_filename; | ||
263 | } | ||
264 | |||
196 | f = get_format(filename); | 265 | f = get_format(filename); |
197 | if (!f || !(ctx.repo->snapshots & f->bit)) { | 266 | if (!f || !(ctx.repo->snapshots & f->bit)) { |
198 | cgit_print_error_page(400, "Bad request", | 267 | cgit_print_error_page(400, "Bad request", |
@@ -216,6 +285,11 @@ void cgit_print_snapshot(const char *head, const char *hex, | |||
216 | if (!prefix) | 285 | if (!prefix) |
217 | prefix = xstrdup(cgit_snapshot_prefix(ctx.repo)); | 286 | prefix = xstrdup(cgit_snapshot_prefix(ctx.repo)); |
218 | 287 | ||
219 | make_snapshot(f, hex, prefix, filename); | 288 | if (sig_filename) |
289 | write_sig(f, hex, filename, sig_filename); | ||
290 | else | ||
291 | make_snapshot(f, hex, prefix, filename); | ||
292 | |||
220 | free(prefix); | 293 | free(prefix); |
294 | free(adj_filename); | ||
221 | } | 295 | } |