mobley

C Git HTTP server
Log | Files | Refs | Submodules | README | git clone https://git.ne02ptzero.me/git/mobley

commit 5ead760019f6460ab47f710d7e7b2a5cf0b571e4
parent 7ff418e42d419a3017ea27872ebbdcc227782faa
Author: Louis Solofrizzo <lsolofrizzo@online.net>
Date:   Fri, 28 Dec 2018 21:30:59 +0100

Add commit log with GPG verification

Signed-off-by: Louis Solofrizzo <lsolofrizzo@online.net>

Diffstat:
MCMakeLists.txt | 3++-
Mhtml.c | 2+-
Mhtml.h | 10++++++++++
Mindex.c | 2+-
Mmain.c | 13+++++++++++++
Mmobley.h | 9+++++++++
Mrepository_handler.c | 23++++++++++++++++++++++-
Arepository_log.c | 142+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Arepository_log.h | 24++++++++++++++++++++++++
Mstyle/main.css | 23++++++++++++++++++++++-
Mtemplate.c | 2+-
11 files changed, 247 insertions(+), 6 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -41,7 +41,7 @@ include_directories("./contrib/sundown") add_subdirectory("./watm") -link_libraries(pthread event event_openssl crypto ssl evhtp yaml git2 shl-wrapper) +link_libraries(pthread event event_openssl crypto ssl evhtp yaml git2 shl-wrapper gpgme assuan gpg-error) add_executable(${MOBLEY_NAME} main.c args.c @@ -56,6 +56,7 @@ add_executable(${MOBLEY_NAME} main.c repository_handler.c file_format.c file.c + repository_log.c ./contrib/sundown/src/autolink.c ./contrib/sundown/src/buffer.c ./contrib/sundown/src/markdown.c diff --git a/html.c b/html.c @@ -104,7 +104,7 @@ void string_raw_format_html(mobley_t *ctx, server_req_t *r, const char *data, si size_t line_num = 1; char link[30] = { 0 }; - for (size_t i = 0; i < len; i++) + for (size_t i = 0; i < len && data[i] != '\0'; i++) { if ((i == 0 || data[i - 1] == '\n') && with_lines) { diff --git a/html.h b/html.h @@ -54,6 +54,16 @@ void _html(server_req_t *r, const mobley_t *ctx, html_raw("%s", asctime(__timeinfo)); \ } while (0) +# define html_git_date_ftime(s, f) do { \ + struct tm *__timeinfo; \ + char __buffer[150]; \ + \ + __timeinfo = localtime(s); \ + strftime(__buffer, sizeof(__buffer), f, __timeinfo); \ + html_raw("%s", __buffer); \ +} while (0) + + __PRINTF_FUNCTION(1, 2) char *__format(const char *s, ...); diff --git a/index.c b/index.c @@ -49,7 +49,7 @@ bool index_route_handler(const mobley_t *ctx, server_req_t *r) if (commit != NULL) { time = git_commit_time(commit); - html_git_date(&time); + html_git_date_ftime(&time, "%H:%M %d/%m/%Y"); } else html_raw("None"); diff --git a/main.c b/main.c @@ -16,6 +16,7 @@ #include <args.h> #include <stdio.h> #include <signal.h> +#include <locale.h> #include <event2/event.h> #include <event2/event-config.h> @@ -73,6 +74,17 @@ static void version(void) } #undef usage +static void init_gpgme(gpgme_protocol_t proto) +{ + gpgme_check_version(NULL); + setlocale(LC_ALL, ""); + gpgme_set_locale(NULL, LC_CTYPE, setlocale(LC_CTYPE, NULL)); +#ifndef HAVE_W32_SYSTEM + gpgme_set_locale(NULL, LC_MESSAGES, setlocale(LC_MESSAGES, NULL)); +#endif + gpgme_engine_check_version(proto); +} + static bool mobley_options(mobley_t *ctx) { if (IS_ARG_HERE(options, OPT_VERSION)) @@ -116,6 +128,7 @@ int main(int ac, const char **av) return 1; git_libgit2_init(); + init_gpgme(GPGME_PROTOCOL_OpenPGP); if (!mobley_options(&ctx)) goto end; diff --git a/mobley.h b/mobley.h @@ -22,6 +22,7 @@ # include <evhtp/log.h> # include <markdown.h> # include <html/html.h> +# include <gpgme.h> # include <server.h> # include <repository.h> @@ -50,6 +51,10 @@ typedef struct { struct sd_markdown *md; } format; + struct { + gpgme_ctx_t ctx; + } crypto; + struct event_base *evbase; struct evhtp *htp; } mobley_t; @@ -73,6 +78,7 @@ static inline void mobley_dtr(mobley_t *ctx) mobley_conf_dtr(ctx); sd_markdown_free(ctx->format.md); + gpgme_release(ctx->crypto.ctx); evhtp_unbind_socket(ctx->htp); evhtp_free(ctx->htp); if (ctx->evbase) @@ -110,6 +116,9 @@ static inline bool mobley_init(mobley_t *ctx) if (ctx->format.md == NULL) goto fail; + if (gpgme_new(&ctx->crypto.ctx) != GPG_ERR_NO_ERROR) + goto fail; + mobley_config(ctx); return true; fail: diff --git a/repository_handler.c b/repository_handler.c @@ -14,6 +14,7 @@ */ #include <repository_handler.h> +#include <repository_log.h> #include <html.h> #include <file_format.h> #include <file.h> @@ -62,7 +63,7 @@ static void repository_print_tree_items(mobley_t *ctx ,server_req_t *r, list_hea time = git_commit_time(commit->commit); html_open("td", .align = "right") { - html_git_date(&time); + html_git_date_ftime(&time, "%H:%M %d/%m/%Y"); } html_close("td"); git_commit_free(commit->commit); free(commit); @@ -257,6 +258,21 @@ static bool repository_route_tree_handler(mobley_t *ctx, server_req_t *r) return ret; } +static bool repository_route_log_handler(mobley_t *ctx, server_req_t *r) +{ + string_array_t *node; + bool ret = false; + + node = list_first_entry(r->uri.next, string_array_t, node); + + if (list_count(r->uri.next) == 1) + ret = repository_log_list(ctx, r); + else + ret = repository_log_commit(ctx, r, node->val); + + return ret; +} + static bool repository_show_readme(mobley_t *ctx, server_req_t *r) { git_commit *head = repository_get_last_commit(r->repo, r->branch); @@ -312,6 +328,11 @@ bool repository_route_handler(mobley_t *ctx, server_req_t *r) if (!repository_route_tree_handler(ctx, r)) goto fail; } + if (strcmp(first->val, "log") == 0) + { + if (!repository_route_log_handler(ctx, r)) + goto fail; + } } return true; diff --git a/repository_log.c b/repository_log.c @@ -0,0 +1,142 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#include <repository_log.h> +#include <html.h> + +typedef enum { + NOT_SIGNED, + SIGNATURE_OK, + SIGNATURE_WRONG +} signature_type_t; + +static signature_type_t check_commit_signature(mobley_t *ctx, server_req_t *r, git_oid *oid) +{ + git_buf sig = { 0 }, data = { 0 }; + gpgme_data_t signature; + gpgme_data_t commit_data; + signature_type_t ret = NOT_SIGNED; + + if (git_commit_extract_signature(&sig, &data, r->repo->repo, oid, NULL) != 0) + return ret; + + gpgme_data_new_from_mem(&signature, sig.ptr, sig.size, 0); + gpgme_data_new_from_mem(&commit_data, data.ptr, data.size, 0); + + if (gpgme_op_verify(ctx->crypto.ctx, signature, NULL, commit_data) == 0) + ret = SIGNATURE_OK; + else + ret = SIGNATURE_WRONG; + + gpgme_data_release(commit_data); + gpgme_data_release(signature); + free(sig.ptr); + free(data.ptr); + return ret; +} + +# define MAX_COMMIT_PER_PAGE 50 +bool repository_log_list(mobley_t *ctx, server_req_t *r) +{ + git_commit *commit = repository_get_last_commit(r->repo, r->branch); + git_commit *current; + const git_oid *oid = NULL; + git_oid tmp; + git_revwalk *walk = NULL; + git_time_t time; + unsigned int sorting = GIT_SORT_NONE; + bool ret = false; + size_t i; + + if (commit == NULL) + return false; + + oid = git_commit_id(commit); + + git_revwalk_new(&walk, r->repo->repo); + sorting = GIT_SORT_TIME | (sorting & GIT_SORT_REVERSE); + git_revwalk_push(walk, oid); + git_revwalk_sorting(walk, sorting); + + html_open("table", .class = "commit_list") { + html_open("thead") { + html_open("tr") { + html("td", "Date"); + html("td", "Commit message", .width = "80%"); + html("td", "", .align = "right"); + html("td", "Author"); + html("td", "", .align = "right"); + } html_close("tr"); + + } html_close("thead"); + + html_open("tbody") { + for (i = 0; !git_revwalk_next(&tmp, walk) && i < MAX_COMMIT_PER_PAGE; i++) + { + if (git_commit_lookup(&current, r->repo->repo, &tmp) != 0) + goto fail; + + html_open("tr") { + html_open("td") { + time = git_commit_time(current); + html_git_date_ftime(&time, "%d/%m/%Y %H:%M"); + } html_close("td"); + html_open("td", .width = "80%") { + html_open("a", .href = __format("log/%s", git_oid_tostr_s(&tmp))) { + string_raw_format_html(ctx, r, git_commit_summary(current), 150, false); + } html_close("a"); + } html_close("td"); + html_open("td") { + signature_type_t sig = check_commit_signature(ctx, r, &tmp); + if (sig == SIGNATURE_OK) + html_nd("i", .class = "fa fa-fw fa-check") + else if (sig == SIGNATURE_WRONG) + html_nd("i", .class = "fa fa-fw fa-times") + else + html_nd("i", .class = "fa fa-fw fa-lock-open"); + } html_close("td"); + + html("td", git_commit_author(current)->name); + + html_open("td", .align = "right") { + + html_open("a", .href = __format("tree?oid=%s", git_oid_tostr_s(&tmp))) { + html_nd("i", .class = "fa fa-fw fa-code"); + } html_close("a"); + html("span", __format("%.8s", git_oid_tostr_s(&tmp)), + .class = "url commit-hash" + ); + } html_close("td"); + } html_close("tr"); + + git_commit_free(current); + } + } html_close("tbody"); + } html_close("table"); + + if (i == MAX_COMMIT_PER_PAGE) + html("button", "Next!"); + ret = true; +fail: + git_commit_free(commit); + git_revwalk_free(walk); + return ret; +} + +bool repository_log_commit(mobley_t *ctx, server_req_t *r, const char *oid) +{ + html("h1", __format("Commit: %s", oid)); + return true; +} diff --git a/repository_log.h b/repository_log.h @@ -0,0 +1,24 @@ +/** + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +#ifndef REPOSITORY_LOG_H +# define REPOSITORY_LOG_H + +# include <mobley.h> + +bool repository_log_list(mobley_t *ctx, server_req_t *r); +bool repository_log_commit(mobley_t *ctx, server_req_t *r, const char *oid); + +#endif /* REPOSITORY_LOG_H */ diff --git a/style/main.css b/style/main.css @@ -44,6 +44,10 @@ a { outline: 0; } +.fa-code { + color: #999; +} + a:hover { text-decoration: underline; } @@ -139,7 +143,7 @@ table tbody .commit a { color: #999; } -li .url { +.url { padding: 1px 8px; color: white; background: black; @@ -282,3 +286,20 @@ li .url { font-weight: normal; font-style: normal; } + +.commit_list tr td { + overflow: hidden; +} + +.commit_list .fa-times { + color: red; +} + +.commit_list .fa-check { + color: #00aa0f; +} + +.commit-hash { + color: white; + background: transparent; +} diff --git a/template.c b/template.c @@ -96,7 +96,7 @@ static void template_menu(const mobley_t *ctx, server_req_t *r) menu_entry_t repo_menu[] = { { "Summary", "" }, - { "Commits", "commits" }, + { "Commits", "log" }, { "Branches", "branches" }, { "Releases", "releases" }, { "Issues", "issues" },