Author: John Keeping <john@keeping.me.uk>
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>
cgit.h | 2 + ui-shared.c | 7 ++++ ui-snapshot.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++
diff --git a/cgit.h b/cgit.h index 847cd2e075ff7efc3d71eb01e4113ab8faa580c9..a686390499b218d027cfbfdeb69397d1aa672ba3 100644 --- a/cgit.h +++ b/cgit.h @@ -374,6 +374,8 @@ extern const char *cgit_repobasename(const char *reponame); extern int cgit_parse_snapshots_mask(const char *str); +extern const struct object_id *cgit_snapshot_get_sig(const char *ref, + const struct cgit_snapshot_format *f); extern int cgit_open_filter(struct cgit_filter *filter, ...); extern int cgit_close_filter(struct cgit_filter *filter); diff --git a/ui-shared.c b/ui-shared.c index 9d7ee3d90e424bb69da1eb447bfbaa9cc19de66d..8a786e0a3e27e63d1e6f19c28b85e0fc1c326673 100644 --- a/ui-shared.c +++ b/ui-shared.c @@ -1133,6 +1133,13 @@ strbuf_setlen(&filename, prefixlen); strbuf_addstr(&filename, f->suffix); cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, filename.buf); + if (cgit_snapshot_get_sig(ref, f)) { + strbuf_addstr(&filename, ".asc"); + html(" ("); + cgit_snapshot_link("sig", NULL, NULL, NULL, NULL, + filename.buf); + html(")"); + } html(separator); } strbuf_release(&filename); diff --git a/ui-snapshot.c b/ui-snapshot.c index abf83995011075db6668810a8df3da993fc109d5..c7611e88947edfed93b497bd654703e3c8987efe 100644 --- a/ui-snapshot.c +++ b/ui-snapshot.c @@ -94,6 +94,31 @@ { ".tar.xz", "application/x-xz", write_tar_xz_archive, 0x10 }, { NULL } }; +static struct notes_tree snapshot_sig_notes[ARRAY_SIZE(cgit_snapshot_formats)]; + +const struct object_id *cgit_snapshot_get_sig(const char *ref, + const struct cgit_snapshot_format *f) +{ + struct notes_tree *tree; + struct object_id oid; + + if (get_oid(ref, &oid)) + return NULL; + + tree = &snapshot_sig_notes[f - &cgit_snapshot_formats[0]]; + if (!tree->initialized) { + struct strbuf notes_ref = STRBUF_INIT; + + strbuf_addf(¬es_ref, "refs/notes/signatures/%s", + f->suffix + 1); + + init_notes(tree, notes_ref.buf, combine_notes_ignore, 0); + strbuf_release(¬es_ref); + } + + return get_note(tree, &oid); +} + static const struct cgit_snapshot_format *get_format(const char *filename) { const struct cgit_snapshot_format *fmt; @@ -129,6 +154,39 @@ format->write_func(hex, prefix); return 0; } +static int write_sig(const struct cgit_snapshot_format *format, + const char *hex, const char *archive, + const char *filename) +{ + const struct object_id *note = cgit_snapshot_get_sig(hex, format); + enum object_type type; + unsigned long size; + char *buf; + + if (!note) { + cgit_print_error_page(404, "Not found", + "No signature for %s", archive); + return 0; + } + + buf = read_sha1_file(note->hash, &type, &size); + if (!buf) { + cgit_print_error_page(404, "Not found", "Not found"); + return 0; + } + + html("X-Content-Type-Options: nosniff\n"); + html("Content-Security-Policy: default-src 'none'\n"); + ctx.page.etag = oid_to_hex(note); + ctx.page.mimetype = xstrdup("application/pgp-signature"); + ctx.page.filename = xstrdup(filename); + cgit_print_http_headers(); + + html_raw(buf, size); + free(buf); + return 0; +} + /* Try to guess the requested revision from the requested snapshot name. * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become * "cgit-0.7.2". If this is a valid commit object name we've got a winner. @@ -185,12 +243,23 @@ void cgit_print_snapshot(const char *head, const char *hex, const char *filename, int dwim) { const struct cgit_snapshot_format* f; + const char *sig_filename = NULL; + char *adj_filename = NULL; char *prefix = NULL; if (!filename) { cgit_print_error_page(400, "Bad request", "No snapshot name specified"); return; + } + + if (ends_with(filename, ".asc")) { + sig_filename = filename; + + /* Strip ".asc" from filename for common format processing */ + adj_filename = xstrdup(filename); + adj_filename[strlen(adj_filename) - 4] = '\0'; + filename = adj_filename; } f = get_format(filename); @@ -216,6 +285,11 @@ if (!prefix) prefix = xstrdup(cgit_snapshot_prefix(ctx.repo)); - make_snapshot(f, hex, prefix, filename); + if (sig_filename) + write_sig(f, hex, filename, sig_filename); + else + make_snapshot(f, hex, prefix, filename); + free(prefix); + free(adj_filename); }