diff options
Diffstat (limited to 'third_party')
39 files changed, 184 insertions, 1615 deletions
diff --git a/third_party/cgit/AUTHORS b/third_party/cgit/AUTHORS index 031de338f904..256ea6b3bc7d 100644 --- a/third_party/cgit/AUTHORS +++ b/third_party/cgit/AUTHORS @@ -1,5 +1,5 @@ Maintainer: - Jason A. Donenfeld <Jason@zx2c4.com> + June McEnroe <june@causal.agency> Contributors: Jason A. Donenfeld <Jason@zx2c4.com> diff --git a/third_party/cgit/Makefile b/third_party/cgit/Makefile index 2509d21775bd..5967a327bb2e 100644 --- a/third_party/cgit/Makefile +++ b/third_party/cgit/Makefile @@ -1,6 +1,6 @@ all:: -CGIT_VERSION = v1.2.3 +CGIT_VERSION = 1.4.1 CGIT_SCRIPT_NAME = cgit.cgi CGIT_SCRIPT_PATH = /var/www/htdocs/cgit CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH) @@ -14,7 +14,7 @@ htmldir = $(docdir) pdfdir = $(docdir) mandir = $(prefix)/share/man SHA1_HEADER = <openssl/sha.h> -GIT_VER = 2.36.0 +GIT_VER = 2.36.1 GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.xz INSTALL = install COPYTREE = cp -r @@ -88,7 +88,6 @@ install: all $(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH) $(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css $(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png - $(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico $(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt $(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir) $(COPYTREE) filters/* $(DESTDIR)$(filterdir) @@ -111,7 +110,6 @@ uninstall: rm -f $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME) rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css rm -f $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png - rm -f $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico uninstall-doc: uninstall-man uninstall-html uninstall-pdf diff --git a/third_party/cgit/README b/third_party/cgit/README index 7a6b4a40ca57..2094b87df057 100644 --- a/third_party/cgit/README +++ b/third_party/cgit/README @@ -1,8 +1,9 @@ -cgit - CGI for Git -================== +cgit-pink - CGI for Git +======================= -This is an attempt to create a fast web interface for the Git SCM, using a -built-in cache to decrease server I/O pressure. +This is a fork of cgit, an attempt to create a fast web interface +for the Git SCM, using a built-in cache to decrease server I/O +pressure. Installation ------------ @@ -32,18 +33,6 @@ This will install `cgit.cgi` and `cgit.css` into `/var/www/htdocs/cgit`. You can configure this location (and a few other things) by providing a `cgit.conf` file (see the Makefile for details). -If you'd like to compile without Lua support, you may use: - - $ make NO_LUA=1 - -And if you'd like to specify a Lua implementation, you may use: - - $ make LUA_PKGCONFIG=lua5.1 - -If this is not specified, the Lua implementation will be auto-detected, -preferring LuaJIT if many are present. Acceptable values are generally "lua", -"luajit", "lua5.1", and "lua5.2". - Dependencies ------------ @@ -51,7 +40,6 @@ Dependencies * libzip * libcrypto (OpenSSL) * libssl (OpenSSL) -* optional: luajit or lua, most reliably used when pkg-config is available Apache configuration -------------------- @@ -92,8 +80,9 @@ the HTTP headers `Modified` and `Expires`. Online presence --------------- -* The cgit homepage is hosted by cgit at <https://git.zx2c4.com/cgit/about/> +* The cgit-pink homepage is hosted by cgit at + <https://git.causal.agency/cgit-pink/about> -* Patches, bug reports, discussions and support should go to the cgit - mailing list: <cgit@lists.zx2c4.com>. To sign up, visit - <https://lists.zx2c4.com/mailman/listinfo/cgit> +* Patches, bug reports, discussions and support should go to the cgit-pink + mailing list: <list+cgit@causal.agency>. Archives are available at: + <https://causal.agency/list/cgit.html> diff --git a/third_party/cgit/cache.c b/third_party/cgit/cache.c index 55199e8fe555..59372541cbc9 100644 --- a/third_party/cgit/cache.c +++ b/third_party/cgit/cache.c @@ -85,40 +85,45 @@ static int close_slot(struct cache_slot *slot) /* Print the content of the active cache slot (but skip the key). */ static int print_slot(struct cache_slot *slot) { + off_t off; #ifdef HAVE_LINUX_SENDFILE - off_t start_off; - int ret; + off_t size; +#endif + + off = slot->keylen + 1; - start_off = slot->keylen + 1; +#ifdef HAVE_LINUX_SENDFILE + size = slot->cache_st.st_size; do { - ret = sendfile(STDOUT_FILENO, slot->cache_fd, &start_off, - slot->cache_st.st_size - start_off); + ssize_t ret; + ret = sendfile(STDOUT_FILENO, slot->cache_fd, &off, size - off); if (ret < 0) { if (errno == EAGAIN || errno == EINTR) continue; + /* Fall back to read/write on EINVAL or ENOSYS */ + if (errno == EINVAL || errno == ENOSYS) + break; return errno; } - return 0; + if (off == size) + return 0; } while (1); -#else - ssize_t i, j; +#endif - i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET); - if (i != slot->keylen + 1) + if (lseek(slot->cache_fd, off, SEEK_SET) != off) return errno; do { - i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); - if (i > 0) - j = xwrite(STDOUT_FILENO, slot->buf, i); - } while (i > 0 && j == i); - - if (i < 0 || j != i) - return errno; - else - return 0; -#endif + ssize_t ret; + ret = xread(slot->cache_fd, slot->buf, sizeof(slot->buf)); + if (ret < 0) + return errno; + if (ret == 0) + return 0; + if (write_in_full(STDOUT_FILENO, slot->buf, ret) < 0) + return errno; + } while (1); } /* Check if the slot has expired */ @@ -265,6 +270,13 @@ static int process_slot(struct cache_slot *slot) { int err; + /* + * Make sure any buffered data is flushed before we redirect, + * do sendfile(2) or write(2) + */ + if (fflush(stdout)) + return errno; + err = open_slot(slot); if (!err && slot->match) { if (is_expired(slot)) { diff --git a/third_party/cgit/cgit.c b/third_party/cgit/cgit.c index 4b2d86c88129..dd28a79315ac 100644 --- a/third_party/cgit/cgit.c +++ b/third_party/cgit/cgit.c @@ -378,7 +378,7 @@ static void prepare_context(void) ctx.cfg.commit_sort = 0; ctx.cfg.css = "/cgit.css"; ctx.cfg.logo = "/cgit.png"; - ctx.cfg.favicon = "/favicon.ico"; + ctx.cfg.favicon = NULL; ctx.cfg.local_time = 0; ctx.cfg.enable_http_clone = 1; ctx.cfg.enable_index_owner = 1; @@ -507,9 +507,11 @@ static inline void parse_readme(const char *readme, char **filename, char **ref, /* Check if the readme is tracked in the git repo. */ colon = strchr(readme, ':'); if (colon && strlen(colon) > 1) { - /* If it starts with a colon, we want to use - * the default branch */ - if (colon == readme && repo->defbranch) + /* If it starts with a colon, we want to use head given + * from query or the default branch */ + if (colon == readme && ctx.qry.head) + *ref = xstrdup(ctx.qry.head); + else if (colon == readme && repo->defbranch) *ref = xstrdup(repo->defbranch); else *ref = xstrndup(readme, colon - readme); @@ -674,7 +676,7 @@ static inline void authenticate_post(void) len = MAX_AUTHENTICATION_POST_BYTES; if ((len = read(STDIN_FILENO, buffer, len)) < 0) die_errno("Could not read POST from stdin"); - if (write(STDOUT_FILENO, buffer, len) < 0) + if (fwrite(buffer, 1, len, stdout) < len) die_errno("Could not write POST to stdout"); cgit_close_filter(ctx.cfg.auth_filter); exit(0); @@ -963,13 +965,7 @@ static void cgit_parse_args(int argc, const char **argv) for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "--version")) { - printf("CGit %s | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION); -#ifdef NO_LUA - printf("[-] "); -#else - printf("[+] "); -#endif - printf("Lua scripting\n"); + printf("CGit-pink %s | https://git.causal.agency/cgit-pink/\n\nCompiled in features:\n", CGIT_VERSION); #ifndef HAVE_LINUX_SENDFILE printf("[-] "); #else @@ -1051,7 +1047,6 @@ int cmd_main(int argc, const char **argv) const char *path; int err, ttl; - cgit_init_filters(); atexit(cgit_cleanup_filters); prepare_context(); diff --git a/third_party/cgit/cgit.css b/third_party/cgit/cgit.css index dfa144d05dd5..51ddbf833754 100644 --- a/third_party/cgit/cgit.css +++ b/third_party/cgit/cgit.css @@ -75,7 +75,7 @@ div#cgit table.tabs td { } div#cgit table.tabs td a { - padding: 2px 0.75em; + padding: 2px 0.25em; color: #777; font-size: 110%; } @@ -363,6 +363,10 @@ div#cgit table.blame td.lines > div > pre { top: 0; } +div#cgit table.blame .oid { + font-size: 100%; +} + div#cgit table.bin-blob { margin-top: 0.5em; border: solid 1px black; @@ -437,11 +441,6 @@ div#cgit div.commit-subject { padding: 0em; } -div#cgit div.commit-msg { - white-space: pre; - font-family: monospace; -} - div#cgit div.notes-header { font-weight: bold; padding-top: 1.5em; @@ -538,26 +537,20 @@ div#cgit table.diff { width: 100%; } -div#cgit table.diff td { - font-family: monospace; - white-space: pre; -} - -div#cgit table.diff td div.head { +div#cgit table.diff td span.head { font-weight: bold; - margin-top: 1em; color: black; } -div#cgit table.diff td div.hunk { +div#cgit table.diff td span.hunk { color: #009; } -div#cgit table.diff td div.add { +div#cgit table.diff td span.add { color: green; } -div#cgit table.diff td div.del { +div#cgit table.diff td span.del { color: red; } @@ -581,7 +574,6 @@ div#cgit table.list td.reposection { div#cgit a.button { font-size: 80%; - padding: 0em 0.5em; } div#cgit a.primary { @@ -671,7 +663,6 @@ div#cgit div.footer a:hover { div#cgit a.branch-deco { color: #000; - margin: 0px 0.5em; padding: 0px 0.25em; background-color: #88ff88; border: solid 1px #007700; @@ -679,7 +670,6 @@ div#cgit a.branch-deco { div#cgit a.tag-deco { color: #000; - margin: 0px 0.5em; padding: 0px 0.25em; background-color: #ffff88; border: solid 1px #777700; @@ -687,7 +677,6 @@ div#cgit a.tag-deco { div#cgit a.tag-annotated-deco { color: #000; - margin: 0px 0.5em; padding: 0px 0.25em; background-color: #ffcc88; border: solid 1px #777700; @@ -695,7 +684,6 @@ div#cgit a.tag-annotated-deco { div#cgit a.remote-deco { color: #000; - margin: 0px 0.5em; padding: 0px 0.25em; background-color: #ccccff; border: solid 1px #000077; @@ -703,7 +691,6 @@ div#cgit a.remote-deco { div#cgit a.deco { color: #000; - margin: 0px 0.5em; padding: 0px 0.25em; background-color: #ff8888; border: solid 1px #770000; @@ -714,7 +701,6 @@ div#cgit div.commit-subject a.tag-deco, div#cgit div.commit-subject a.tag-annotated-deco, div#cgit div.commit-subject a.remote-deco, div#cgit div.commit-subject a.deco { - margin-left: 1em; font-size: 75%; } diff --git a/third_party/cgit/cgit.h b/third_party/cgit/cgit.h index 69b5c13232e7..72fcd8498a07 100644 --- a/third_party/cgit/cgit.h +++ b/third_party/cgit/cgit.h @@ -385,7 +385,6 @@ extern void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char extern void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **argv); extern struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype); extern void cgit_cleanup_filters(void); -extern void cgit_init_filters(void); extern void cgit_prepare_repo_env(struct cgit_repo * repo); diff --git a/third_party/cgit/cgit.mk b/third_party/cgit/cgit.mk index 3fcc1ca31440..5b9ed5be8e46 100644 --- a/third_party/cgit/cgit.mk +++ b/third_party/cgit/cgit.mk @@ -27,32 +27,6 @@ ifdef NO_C99_FORMAT CFLAGS += -DNO_C99_FORMAT endif -ifdef NO_LUA - LUA_MESSAGE := linking without specified Lua support - CGIT_CFLAGS += -DNO_LUA -else -ifeq ($(LUA_PKGCONFIG),) - LUA_PKGCONFIG := $(shell for pc in luajit lua lua5.2 lua5.1; do \ - $(PKG_CONFIG) --exists $$pc 2>/dev/null && echo $$pc && break; \ - done) - LUA_MODE := autodetected -else - LUA_MODE := specified -endif -ifneq ($(LUA_PKGCONFIG),) - LUA_MESSAGE := linking with $(LUA_MODE) $(LUA_PKGCONFIG) - LUA_LIBS := $(shell $(PKG_CONFIG) --libs $(LUA_PKGCONFIG) 2>/dev/null) - LUA_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(LUA_PKGCONFIG) 2>/dev/null) - CGIT_LIBS += $(LUA_LIBS) - CGIT_CFLAGS += $(LUA_CFLAGS) -else - LUA_MESSAGE := linking without autodetected Lua support - NO_LUA := YesPlease - CGIT_CFLAGS += -DNO_LUA -endif - -endif - # Add -ldl to linker flags on systems that commonly use GNU libc. ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD)) CGIT_LIBS += -ldl @@ -130,7 +104,6 @@ $(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs) $(QUIET_CC)$(CC) -o $*.o -c $(dep_args) $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $< $(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS) - @echo 1>&1 " * $(LUA_MESSAGE)" $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS) CGIT_SP_OBJS := $(patsubst %.o,%.sp,$(CGIT_OBJS)) diff --git a/third_party/cgit/cgitrc.5.txt b/third_party/cgit/cgitrc.5.txt index 33a6a8c0c758..cafb5355ea25 100644 --- a/third_party/cgit/cgitrc.5.txt +++ b/third_party/cgit/cgitrc.5.txt @@ -219,7 +219,7 @@ enable-tree-linenumbers:: favicon:: Url used as link to a shortcut icon for cgit. It is suggested to use the value "/favicon.ico" since certain browsers will ignore other - values. Default value: "/favicon.ico". + values. Default value: none. footer:: The content of the file specified with this option will be included @@ -579,11 +579,11 @@ repo.readme:: verbatim as the "About" page for this repo. You may also specify a git refspec by head or by hash by prepending the refspec followed by a colon. For example, "master:docs/readme.mkd". If the value begins - with a colon, i.e. ":docs/readme.rst", the default branch of the - repository will be used. Sharing any file will expose that entire - directory tree to the "/about/PATH" endpoints, so be sure that there - are no non-public files located in the same directory as the readme - file. Default value: <readme>. + with a colon, i.e. ":docs/readme.rst", the head giving in query or + the default branch of the repository will be used. Sharing any file + will expose that entire directory tree to the "/about/PATH" endpoints, + so be sure that there are no non-public files located in the same + directory as the readme file. Default value: <readme>. repo.section:: Override the current section name for this repository. Default value: @@ -632,37 +632,6 @@ specification with the relevant string; available values are: 'exec:':: The default "one process per filter" mode. -'lua:':: - Executes the script using a built-in Lua interpreter. The script is - loaded once per execution of cgit, and may be called multiple times - during cgit's lifetime, making it a good choice for repeated filters - such as the 'email filter'. It responds to three functions: - - 'filter_open(argument1, argument2, argument3, ...)':: - This is called upon activation of the filter for a particular - set of data. - 'filter_write(buffer)':: - This is called whenever cgit writes data to the webpage. - 'filter_close()':: - This is called when the current filtering operation is - completed. It must return an integer value. Usually 0 - indicates success. - - Additionally, cgit exposes to the Lua the following built-in functions: - - 'html(str)':: - Writes 'str' to the webpage. - 'html_txt(str)':: - HTML escapes and writes 'str' to the webpage. - 'html_attr(str)':: - HTML escapes for an attribute and writes "str' to the webpage. - 'html_url_path(str)':: - URL escapes for a path and writes 'str' to the webpage. - 'html_url_arg(str)':: - URL escapes for an argument and writes 'str' to the webpage. - 'html_include(file)':: - Includes 'file' in webpage. - Parameters are provided to filters as follows. @@ -696,9 +665,6 @@ auth filter:: with a 302 redirect, and write to output one or more "Set-Cookie" HTTP headers, each followed by a newline. - Please see `filters/simple-authentication.lua` for a clear example - script that may be modified. - commit filter:: This filter is given no arguments. The commit message text that is to be filtered is available on standard input and the filtered text is diff --git a/third_party/cgit/default.nix b/third_party/cgit/default.nix index 55aaa02f32d4..47c0de0d6518 100644 --- a/third_party/cgit/default.nix +++ b/third_party/cgit/default.nix @@ -4,12 +4,11 @@ let inherit (pkgs) stdenv gzip bzip2 xz luajit zlib autoconf openssl pkgconfig; in stdenv.mkDerivation rec { - pname = "cgit"; + pname = "cgit-pink"; version = "master"; src = ./.; - nativeBuildInputs = [ autoconf pkgconfig ]; - buildInputs = [ openssl zlib luajit ]; + buildInputs = [ openssl zlib ]; enableParallelBuilding = true; @@ -34,11 +33,9 @@ stdenv.mkDerivation rec { ''; meta = { - homepage = https://git.zx2c4.com/cgit/about/; - repositories.git = git://git.zx2c4.com/cgit; - description = "Web frontend for git repositories"; + hompepage = "https://git.causal.agency/cgit-pink/"; + description = "cgit fork aiming for better maintenance"; license = lib.licenses.gpl2; platforms = lib.platforms.linux; - maintainers = with lib.maintainers; [ bjornfor ]; }; } diff --git a/third_party/cgit/favicon.ico b/third_party/cgit/favicon.ico deleted file mode 100644 index 56ff59384f2c..000000000000 --- a/third_party/cgit/favicon.ico +++ /dev/null Binary files differdiff --git a/third_party/cgit/filter.c b/third_party/cgit/filter.c index 181d9a892f0b..190fb5501b49 100644 --- a/third_party/cgit/filter.c +++ b/third_party/cgit/filter.c @@ -8,12 +8,6 @@ #include "cgit.h" #include "html.h" -#ifndef NO_LUA -#include <dlfcn.h> -#include <lua.h> -#include <lualib.h> -#include <lauxlib.h> -#endif static inline void reap_filter(struct cgit_filter *filter) { @@ -48,6 +42,7 @@ static int open_exec_filter(struct cgit_filter *base, va_list ap) for (i = 0; i < filter->base.argument_count; i++) filter->argv[i + 1] = va_arg(ap, char *); + chk_zero(fflush(stdout), "unable to flush STDOUT"); filter->old_stdout = chk_positive(dup(STDOUT_FILENO), "Unable to duplicate STDOUT"); chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess"); @@ -71,6 +66,7 @@ static int close_exec_filter(struct cgit_filter *base) struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base; int i, exit_status = 0; + chk_zero(fflush(stdout), "unable to flush STDOUT"); chk_non_negative(dup2(filter->old_stdout, STDOUT_FILENO), "Unable to restore STDOUT"); close(filter->old_stdout); @@ -136,234 +132,6 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar filter->base.argument_count = 0; } -#ifdef NO_LUA -void cgit_init_filters(void) -{ -} -#endif - -#ifndef NO_LUA -static ssize_t (*libc_write)(int fd, const void *buf, size_t count); -static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL; -static struct cgit_filter *current_write_filter = NULL; - -void cgit_init_filters(void) -{ - libc_write = dlsym(RTLD_NEXT, "write"); - if (!libc_write) - die("Could not locate libc's write function"); -} - -ssize_t write(int fd, const void *buf, size_t count) -{ - if (fd != STDOUT_FILENO || !filter_write) - return libc_write(fd, buf, count); - return filter_write(current_write_filter, buf, count); -} - -static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count)) -{ - /* We want to avoid buggy nested patterns. */ - assert(filter_write == NULL); - assert(current_write_filter == NULL); - current_write_filter = filter; - filter_write = new_write; -} - -static inline void unhook_write(void) -{ - assert(filter_write != NULL); - assert(current_write_filter != NULL); - filter_write = NULL; - current_write_filter = NULL; -} - -struct lua_filter { - struct cgit_filter base; - char *script_file; - lua_State *lua_state; -}; - -static void error_lua_filter(struct lua_filter *filter) -{ - die("Lua error in %s: %s", filter->script_file, lua_tostring(filter->lua_state, -1)); - lua_pop(filter->lua_state, 1); -} - -static ssize_t write_lua_filter(struct cgit_filter *base, const void *buf, size_t count) -{ - struct lua_filter *filter = (struct lua_filter *)base; - - lua_getglobal(filter->lua_state, "filter_write"); - lua_pushlstring(filter->lua_state, buf, count); - if (lua_pcall(filter->lua_state, 1, 0, 0)) { - error_lua_filter(filter); - errno = EIO; - return -1; - } - return count; -} - -static inline int hook_lua_filter(lua_State *lua_state, void (*fn)(const char *txt)) -{ - const char *str; - ssize_t (*save_filter_write)(struct cgit_filter *base, const void *buf, size_t count); - struct cgit_filter *save_filter; - - str = lua_tostring(lua_state, 1); - if (!str) - return 0; - - save_filter_write = filter_write; - save_filter = current_write_filter; - unhook_write(); - fn(str); - hook_write(save_filter, save_filter_write); - - return 0; -} - -static int html_lua_filter(lua_State *lua_state) -{ - return hook_lua_filter(lua_state, html); -} - -static int html_txt_lua_filter(lua_State *lua_state) -{ - return hook_lua_filter(lua_state, html_txt); -} - -static int html_attr_lua_filter(lua_State *lua_state) -{ - return hook_lua_filter(lua_state, html_attr); -} - -static int html_url_path_lua_filter(lua_State *lua_state) -{ - return hook_lua_filter(lua_state, html_url_path); -} - -static int html_url_arg_lua_filter(lua_State *lua_state) -{ - return hook_lua_filter(lua_state, html_url_arg); -} - -static int html_include_lua_filter(lua_State *lua_state) -{ - return hook_lua_filter(lua_state, (void (*)(const char *))html_include); -} - -static void cleanup_lua_filter(struct cgit_filter *base) -{ - struct lua_filter *filter = (struct lua_filter *)base; - - if (!filter->lua_state) - return; - - lua_close(filter->lua_state); - filter->lua_state = NULL; - if (filter->script_file) { - free(filter->script_file); - filter->script_file = NULL; - } -} - -static int init_lua_filter(struct lua_filter *filter) -{ - if (filter->lua_state) - return 0; - - if (!(filter->lua_state = luaL_newstate())) - return 1; - - luaL_openlibs(filter->lua_state); - - lua_pushcfunction(filter->lua_state, html_lua_filter); - lua_setglobal(filter->lua_state, "html"); - lua_pushcfunction(filter->lua_state, html_txt_lua_filter); - lua_setglobal(filter->lua_state, "html_txt"); - lua_pushcfunction(filter->lua_state, html_attr_lua_filter); - lua_setglobal(filter->lua_state, "html_attr"); - lua_pushcfunction(filter->lua_state, html_url_path_lua_filter); - lua_setglobal(filter->lua_state, "html_url_path"); - lua_pushcfunction(filter->lua_state, html_url_arg_lua_filter); - lua_setglobal(filter->lua_state, "html_url_arg"); - lua_pushcfunction(filter->lua_state, html_include_lua_filter); - lua_setglobal(filter->lua_state, "html_include"); - - if (luaL_dofile(filter->lua_state, filter->script_file)) { - error_lua_filter(filter); - lua_close(filter->lua_state); - filter->lua_state = NULL; - return 1; - } - return 0; -} - -static int open_lua_filter(struct cgit_filter *base, va_list ap) -{ - struct lua_filter *filter = (struct lua_filter *)base; - int i; - - if (init_lua_filter(filter)) - return 1; - - hook_write(base, write_lua_filter); - - lua_getglobal(filter->lua_state, "filter_open"); - for (i = 0; i < filter->base.argument_count; ++i) - lua_pushstring(filter->lua_state, va_arg(ap, char *)); - if (lua_pcall(filter->lua_state, filter->base.argument_count, 0, 0)) { - error_lua_filter(filter); - return 1; - } - return 0; -} - -static int close_lua_filter(struct cgit_filter *base) -{ - struct lua_filter *filter = (struct lua_filter *)base; - int ret = 0; - - lua_getglobal(filter->lua_state, "filter_close"); - if (lua_pcall(filter->lua_state, 0, 1, 0)) { - error_lua_filter(filter); - ret = -1; - } else { - ret = lua_tonumber(filter->lua_state, -1); - lua_pop(filter->lua_state, 1); - } - - unhook_write(); - return ret; -} - -static void fprintf_lua_filter(struct cgit_filter *base, FILE *f, const char *prefix) -{ - struct lua_filter *filter = (struct lua_filter *)base; - fprintf(f, "%slua:%s\n", prefix, filter->script_file); -} - - -static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count) -{ - struct lua_filter *filter; - - filter = xmalloc(sizeof(*filter)); - memset(filter, 0, sizeof(*filter)); - filter->base.open = open_lua_filter; - filter->base.close = close_lua_filter; - filter->base.fprintf = fprintf_lua_filter; - filter->base.cleanup = cleanup_lua_filter; - filter->base.argument_count = argument_count; - filter->script_file = xstrdup(cmd); - - return &filter->base; -} - -#endif - - int cgit_open_filter(struct cgit_filter *filter, ...) { int result; @@ -395,9 +163,6 @@ static const struct { struct cgit_filter *(*ctor)(const char *cmd, int argument_count); } filter_specs[] = { { "exec", new_exec_filter }, -#ifndef NO_LUA - { "lua", new_lua_filter }, -#endif }; struct cgit_filter *cgit_new_filter(const char *cmd, filter_type filtertype) diff --git a/third_party/cgit/filters/email-gravatar.lua b/third_party/cgit/filters/email-gravatar.lua deleted file mode 100644 index c39b490d6bb6..000000000000 --- a/third_party/cgit/filters/email-gravatar.lua +++ /dev/null @@ -1,35 +0,0 @@ --- This script may be used with the email-filter or repo.email-filter settings in cgitrc. --- It adds gravatar icons to author names. It is designed to be used with the lua: --- prefix in filters. It is much faster than the corresponding python script. --- --- Requirements: --- luaossl --- <http://25thandclement.com/~william/projects/luaossl.html> --- - -local digest = require("openssl.digest") - -function md5_hex(input) - local b = digest.new("md5"):final(input) - local x = "" - for i = 1, #b do - x = x .. string.format("%.2x", string.byte(b, i)) - end - return x -end - -function filter_open(email, page) - buffer = "" - md5 = md5_hex(email:sub(2, -2):lower()) -end - -function filter_close() - html("<img src='//www.gravatar.com/avatar/" .. md5 .. "?s=13&d=retro' width='13' height='13' alt='Gravatar' /> " .. buffer) - return 0 -end - -function filter_write(str) - buffer = buffer .. str -end - - diff --git a/third_party/cgit/filters/email-gravatar.py b/third_party/cgit/filters/email-gravatar.py index d70440ea543c..012113c59132 100755 --- a/third_party/cgit/filters/email-gravatar.py +++ b/third_party/cgit/filters/email-gravatar.py @@ -1,8 +1,5 @@ #!/usr/bin/env python3 -# Please prefer the email-gravatar.lua using lua: as a prefix over this script. This -# script is very slow, in comparison. -# # This script may be used with the email-filter or repo.email-filter settings in cgitrc. # # The following environment variables can be used to retrieve the configuration diff --git a/third_party/cgit/filters/email-libravatar.lua b/third_party/cgit/filters/email-libravatar.lua deleted file mode 100644 index 7336baf830a1..000000000000 --- a/third_party/cgit/filters/email-libravatar.lua +++ /dev/null @@ -1,36 +0,0 @@ --- This script may be used with the email-filter or repo.email-filter settings in cgitrc. --- It adds libravatar icons to author names. It is designed to be used with the lua: --- prefix in filters. --- --- Requirements: --- luaossl --- <http://25thandclement.com/~william/projects/luaossl.html> --- - -local digest = require("openssl.digest") - -function md5_hex(input) - local b = digest.new("md5"):final(input) - local x = "" - for i = 1, #b do - x = x .. string.format("%.2x", string.byte(b, i)) - end - return x -end - -function filter_open(email, page) - buffer = "" - md5 = md5_hex(email:sub(2, -2):lower()) -end - -function filter_close() - baseurl = os.getenv("HTTPS") and "https://seccdn.libravatar.org/" or "http://cdn.libravatar.org/" - html("<img src='" .. baseurl .. "avatar/" .. md5 .. "?s=13&d=retro' width='13' height='13' alt='Libravatar' /> " .. buffer) - return 0 -end - -function filter_write(str) - buffer = buffer .. str -end - - diff --git a/third_party/cgit/filters/file-authentication.lua b/third_party/cgit/filters/file-authentication.lua deleted file mode 100644 index 024880463c2a..000000000000 --- a/third_party/cgit/filters/file-authentication.lua +++ /dev/null @@ -1,359 +0,0 @@ --- This script may be used with the auth-filter. --- --- Requirements: --- luaossl --- <http://25thandclement.com/~william/projects/luaossl.html> --- luaposix --- <https://github.com/luaposix/luaposix> --- -local sysstat = require("posix.sys.stat") -local unistd = require("posix.unistd") -local rand = require("openssl.rand") -local hmac = require("openssl.hmac") - --- This file should contain a series of lines in the form of: --- username1:hash1 --- username2:hash2 --- username3:hash3 --- ... --- Hashes can be generated using something like `mkpasswd -m sha-512 -R 300000`. --- This file should not be world-readable. -local users_filename = "/etc/cgit-auth/users" - --- This file should contain a series of lines in the form of: --- groupname1:username1,username2,username3,... --- ... -local groups_filename = "/etc/cgit-auth/groups" - --- This file should contain a series of lines in the form of: --- reponame1:groupname1,groupname2,groupname3,... --- ... -local repos_filename = "/etc/cgit-auth/repos" - --- Set this to a path this script can write to for storing a persistent --- cookie secret, which should not be world-readable. -local secret_filename = "/var/cache/cgit/auth-secret" - --- --- --- Authentication functions follow below. Swap these out if you want different authentication semantics. --- --- - --- Looks up a hash for a given user. -function lookup_hash(user) - local line - for line in io.lines(users_filename) do - local u, h = string.match(line, "(.-):(.+)") - if u:lower() == user:lower() then - return h - end - end - return nil -end - --- Looks up users for a given repo. -function lookup_users(repo) - local users = nil - local groups = nil - local line, group, user - for line in io.lines(repos_filename) do - local r, g = string.match(line, "(.-):(.+)") - if r == repo then - groups = { } - for group in string.gmatch(g, "([^,]+)") do - groups[group:lower()] = true - end - break - end - end - if groups == nil then - return nil - end - for line in io.lines(groups_filename) do - local g, u = string.match(line, "(.-):(.+)") - if groups[g:lower()] then - if users == nil then - users = { } - end - for user in string.gmatch(u, "([^,]+)") do - users[user:lower()] = true - end - end - end - return users -end - - --- Sets HTTP cookie headers based on post and sets up redirection. -function authenticate_post() - local hash = lookup_hash(post["username"]) - local redirect = validate_value("redirect", post["redirect"]) - - if redirect == nil then - not_found() - return 0 - end - - redirect_to(redirect) - - if hash == nil or hash ~= unistd.crypt(post["password"], hash) then - set_cookie("cgitauth", "") - else - -- One week expiration time - local username = secure_value("username", post["username"], os.time() + 604800) - set_cookie("cgitauth", username) - end - - html("\n") - return 0 -end - - --- Returns 1 if the cookie is valid and 0 if it is not. -function authenticate_cookie() - accepted_users = lookup_users(cgit["repo"]) - if accepted_users == nil then - -- We return as valid if the repo is not protected. - return 1 - end - - local username = validate_value("username", get_cookie(http["cookie"], "cgitauth")) - if username == nil or not accepted_users[username:lower()] then - return 0 - else - return 1 - end -end - --- Prints the html for the login form. -function body() - html("<h2>Authentication Required</h2>") - html("<form method='post' action='") - html_attr(cgit["login"]) - html("'>") - html("<input type='hidden' name='redirect' value='") - html_attr(secure_value("redirect", cgit["url"], 0)) - html("' />") - html("<table>") - html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>") - html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>") - html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>") - html("</table></form>") - - return 0 -end - - - --- --- --- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions. --- --- - -local actions = {} -actions["authenticate-post"] = authenticate_post -actions["authenticate-cookie"] = authenticate_cookie -actions["body"] = body - -function filter_open(...) - action = actions[select(1, ...)] - - http = {} - http["cookie"] = select(2, ...) - http["method"] = select(3, ...) - http["query"] = select(4, ...) - http["referer"] = select(5, ...) - http["path"] = select(6, ...) - http["host"] = select(7, ...) - http["https"] = select(8, ...) - - cgit = {} - cgit["repo"] = select(9, ...) - cgit["page"] = select(10, ...) - cgit["url"] = select(11, ...) - cgit["login"] = select(12, ...) - -end - -function filter_close() - return action() -end - -function filter_write(str) - post = parse_qs(str) -end - - --- --- --- Utility functions based on keplerproject/wsapi. --- --- - -function url_decode(str) - if not str then - return "" - end - str = string.gsub(str, "+", " ") - str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end) - str = string.gsub(str, "\r\n", "\n") - return str -end - -function url_encode(str) - if not str then - return "" - end - str = string.gsub(str, "\n", "\r\n") - str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end) - str = string.gsub(str, " ", "+") - return str -end - -function parse_qs(qs) - local tab = {} - for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do - tab[url_decode(key)] = url_decode(val) - end - return tab -end - -function get_cookie(cookies, name) - cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";") - return url_decode(string.match(cookies, ";" .. name .. "=(.-);")) -end - -function tohex(b) - local x = "" - for i = 1, #b do - x = x .. string.format("%.2x", string.byte(b, i)) - end - return x -end - --- --- --- Cookie construction and validation helpers. --- --- - -local secret = nil - --- Loads a secret from a file, creates a secret, or returns one from memory. -function get_secret() - if secret ~= nil then - return secret - end - local secret_file = io.open(secret_filename, "r") - if secret_file == nil then - local old_umask = sysstat.umask(63) - local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16)) - local temporary_file = io.open(temporary_filename, "w") - if temporary_file == nil then - os.exit(177) - end - temporary_file:write(tohex(rand.bytes(32))) - temporary_file:close() - unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same. - unistd.unlink(temporary_filename) - sysstat.umask(old_umask) - secret_file = io.open(secret_filename, "r") - end - if secret_file == nil then - os.exit(177) - end - secret = secret_file:read() - secret_file:close() - if secret:len() ~= 64 then - os.exit(177) - end - return secret -end - --- Returns value of cookie if cookie is valid. Otherwise returns nil. -function validate_value(expected_field, cookie) - local i = 0 - local value = "" - local field = "" - local expiration = 0 - local salt = "" - local chmac = "" - - if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then - return nil - end - - for component in string.gmatch(cookie, "[^|]+") do - if i == 0 then - field = component - elseif i == 1 then - value = component - elseif i == 2 then - expiration = tonumber(component) - if expiration == nil then - expiration = -1 - end - elseif i == 3 then - salt = component - elseif i == 4 then - chmac = component - else - break - end - i = i + 1 - end - - if chmac == nil or chmac:len() == 0 then - return nil - end - - -- Lua hashes strings, so these comparisons are time invariant. - if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then - return nil - end - - if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then - return nil - end - - if url_decode(field) ~= expected_field then - return nil - end - - return url_decode(value) -end - -function secure_value(field, value, expiration) - if value == nil or value:len() <= 0 then - return "" - end - - local authstr = "" - local salt = tohex(rand.bytes(16)) - value = url_encode(value) - field = url_encode(field) - authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt - authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr)) - return authstr -end - -function set_cookie(cookie, value) - html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly") - if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then - html("; secure") - end - html("\n") -end - -function redirect_to(url) - html("Status: 302 Redirect\n") - html("Cache-Control: no-cache, no-store\n") - html("Location: " .. url .. "\n") -end - -function not_found() - html("Status: 404 Not Found\n") - html("Cache-Control: no-cache, no-store\n\n") -end diff --git a/third_party/cgit/filters/gentoo-ldap-authentication.lua b/third_party/cgit/filters/gentoo-ldap-authentication.lua deleted file mode 100644 index 673c88d1021c..000000000000 --- a/third_party/cgit/filters/gentoo-ldap-authentication.lua +++ /dev/null @@ -1,360 +0,0 @@ --- This script may be used with the auth-filter. Be sure to configure it as you wish. --- --- Requirements: --- luaossl --- <http://25thandclement.com/~william/projects/luaossl.html> --- lualdap >= 1.2 --- <https://git.zx2c4.com/lualdap/about/> --- luaposix --- <https://github.com/luaposix/luaposix> --- -local sysstat = require("posix.sys.stat") -local unistd = require("posix.unistd") -local lualdap = require("lualdap") -local rand = require("openssl.rand") -local hmac = require("openssl.hmac") - --- --- --- Configure these variables for your settings. --- --- - --- A list of password protected repositories, with which gentooAccess --- group is allowed to access each one. -local protected_repos = { - glouglou = "infra", - portage = "dev" -} - --- Set this to a path this script can write to for storing a persistent --- cookie secret, which should be guarded. -local secret_filename = "/var/cache/cgit/auth-secret" - - --- --- --- Authentication functions follow below. Swap these out if you want different authentication semantics. --- --- - --- Sets HTTP cookie headers based on post and sets up redirection. -function authenticate_post() - local redirect = validate_value("redirect", post["redirect"]) - - if redirect == nil then - not_found() - return 0 - end - - redirect_to(redirect) - - local groups = gentoo_ldap_user_groups(post["username"], post["password"]) - if groups == nil then - set_cookie("cgitauth", "") - else - -- One week expiration time - set_cookie("cgitauth", secure_value("gentoogroups", table.concat(groups, ","), os.time() + 604800)) - end - - html("\n") - return 0 -end - - --- Returns 1 if the cookie is valid and 0 if it is not. -function authenticate_cookie() - local required_group = protected_repos[cgit["repo"]] - if required_group == nil then - -- We return as valid if the repo is not protected. - return 1 - end - - local user_groups = validate_value("gentoogroups", get_cookie(http["cookie"], "cgitauth")) - if user_groups == nil or user_groups == "" then - return 0 - end - for group in string.gmatch(user_groups, "[^,]+") do - if group == required_group then - return 1 - end - end - return 0 -end - --- Prints the html for the login form. -function body() - html("<h2>Gentoo LDAP Authentication Required</h2>") - html("<form method='post' action='") - html_attr(cgit["login"]) - html("'>") - html("<input type='hidden' name='redirect' value='") - html_attr(secure_value("redirect", cgit["url"], 0)) - html("' />") - html("<table>") - html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>") - html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>") - html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>") - html("</table></form>") - - return 0 -end - --- --- --- Gentoo LDAP support. --- --- - -function gentoo_ldap_user_groups(username, password) - -- Ensure the user is alphanumeric - if username == nil or username:match("%W") then - return nil - end - - local who = "uid=" .. username .. ",ou=devs,dc=gentoo,dc=org" - - local ldap, err = lualdap.open_simple { - uri = "ldap://ldap1.gentoo.org", - who = who, - password = password, - starttls = true, - certfile = "/var/www/uwsgi/cgit/gentoo-ldap/star.gentoo.org.crt", - keyfile = "/var/www/uwsgi/cgit/gentoo-ldap/star.gentoo.org.key", - cacertfile = "/var/www/uwsgi/cgit/gentoo-ldap/ca.pem" - } - if ldap == nil then - return nil - end - - local group_suffix = ".group" - local group_suffix_len = group_suffix:len() - local groups = {} - for dn, attribs in ldap:search { base = who, scope = "subtree" } do - local access = attribs["gentooAccess"] - if dn == who and access ~= nil then - for i, v in ipairs(access) do - local vlen = v:len() - if vlen > group_suffix_len and v:sub(-group_suffix_len) == group_suffix then - table.insert(groups, v:sub(1, vlen - group_suffix_len)) - end - end - end - end - - ldap:close() - - return groups -end - --- --- --- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions. --- --- - -local actions = {} -actions["authenticate-post"] = authenticate_post -actions["authenticate-cookie"] = authenticate_cookie -actions["body"] = body - -function filter_open(...) - action = actions[select(1, ...)] - - http = {} - http["cookie"] = select(2, ...) - http["method"] = select(3, ...) - http["query"] = select(4, ...) - http["referer"] = select(5, ...) - http["path"] = select(6, ...) - http["host"] = select(7, ...) - http["https"] = select(8, ...) - - cgit = {} - cgit["repo"] = select(9, ...) - cgit["page"] = select(10, ...) - cgit["url"] = select(11, ...) - cgit["login"] = select(12, ...) - -end - -function filter_close() - return action() -end - -function filter_write(str) - post = parse_qs(str) -end - - --- --- --- Utility functions based on keplerproject/wsapi. --- --- - -function url_decode(str) - if not str then - return "" - end - str = string.gsub(str, "+", " ") - str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end) - str = string.gsub(str, "\r\n", "\n") - return str -end - -function url_encode(str) - if not str then - return "" - end - str = string.gsub(str, "\n", "\r\n") - str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end) - str = string.gsub(str, " ", "+") - return str -end - -function parse_qs(qs) - local tab = {} - for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do - tab[url_decode(key)] = url_decode(val) - end - return tab -end - -function get_cookie(cookies, name) - cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";") - return string.match(cookies, ";" .. name .. "=(.-);") -end - -function tohex(b) - local x = "" - for i = 1, #b do - x = x .. string.format("%.2x", string.byte(b, i)) - end - return x -end - --- --- --- Cookie construction and validation helpers. --- --- - -local secret = nil - --- Loads a secret from a file, creates a secret, or returns one from memory. -function get_secret() - if secret ~= nil then - return secret - end - local secret_file = io.open(secret_filename, "r") - if secret_file == nil then - local old_umask = sysstat.umask(63) - local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16)) - local temporary_file = io.open(temporary_filename, "w") - if temporary_file == nil then - os.exit(177) - end - temporary_file:write(tohex(rand.bytes(32))) - temporary_file:close() - unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same. - unistd.unlink(temporary_filename) - sysstat.umask(old_umask) - secret_file = io.open(secret_filename, "r") - end - if secret_file == nil then - os.exit(177) - end - secret = secret_file:read() - secret_file:close() - if secret:len() ~= 64 then - os.exit(177) - end - return secret -end - --- Returns value of cookie if cookie is valid. Otherwise returns nil. -function validate_value(expected_field, cookie) - local i = 0 - local value = "" - local field = "" - local expiration = 0 - local salt = "" - local chmac = "" - - if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then - return nil - end - - for component in string.gmatch(cookie, "[^|]+") do - if i == 0 then - field = component - elseif i == 1 then - value = component - elseif i == 2 then - expiration = tonumber(component) - if expiration == nil then - expiration = -1 - end - elseif i == 3 then - salt = component - elseif i == 4 then - chmac = component - else - break - end - i = i + 1 - end - - if chmac == nil or chmac:len() == 0 then - return nil - end - - -- Lua hashes strings, so these comparisons are time invariant. - if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then - return nil - end - - if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then - return nil - end - - if url_decode(field) ~= expected_field then - return nil - end - - return url_decode(value) -end - -function secure_value(field, value, expiration) - if value == nil or value:len() <= 0 then - return "" - end - - local authstr = "" - local salt = tohex(rand.bytes(16)) - value = url_encode(value) - field = url_encode(field) - authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt - authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr)) - return authstr -end - -function set_cookie(cookie, value) - html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly") - if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then - html("; secure") - end - html("\n") -end - -function redirect_to(url) - html("Status: 302 Redirect\n") - html("Cache-Control: no-cache, no-store\n") - html("Location: " .. url .. "\n") -end - -function not_found() - html("Status: 404 Not Found\n") - html("Cache-Control: no-cache, no-store\n\n") -end diff --git a/third_party/cgit/filters/owner-example.lua b/third_party/cgit/filters/owner-example.lua deleted file mode 100644 index 50fc25a8e53c..000000000000 --- a/third_party/cgit/filters/owner-example.lua +++ /dev/null @@ -1,17 +0,0 @@ --- This script is an example of an owner-filter. It replaces the --- usual query link with one to a fictional homepage. This script may --- be used with the owner-filter or repo.owner-filter settings in --- cgitrc with the `lua:` prefix. - -function filter_open() - buffer = "" -end - -function filter_close() - html(string.format("<a href=\"%s\">%s</a>", "http://wiki.example.com/about/" .. buffer, buffer)) - return 0 -end - -function filter_write(str) - buffer = buffer .. str -end diff --git a/third_party/cgit/filters/simple-authentication.lua b/third_party/cgit/filters/simple-authentication.lua deleted file mode 100644 index 23d345763bf6..000000000000 --- a/third_party/cgit/filters/simple-authentication.lua +++ /dev/null @@ -1,314 +0,0 @@ --- This script may be used with the auth-filter. Be sure to configure it as you wish. --- --- Requirements: --- luaossl --- <http://25thandclement.com/~william/projects/luaossl.html> --- luaposix --- <https://github.com/luaposix/luaposix> --- -local sysstat = require("posix.sys.stat") -local unistd = require("posix.unistd") -local rand = require("openssl.rand") -local hmac = require("openssl.hmac") - --- --- --- Configure these variables for your settings. --- --- - --- A list of password protected repositories along with the users who can access them. -local protected_repos = { - glouglou = { laurent = true, jason = true }, - qt = { jason = true, bob = true } -} - --- A list of users and hashes, generated with `mkpasswd -m sha-512 -R 300000`. -local users = { - jason = "$6$rounds=300000$YYJct3n/o.ruYK$HhpSeuCuW1fJkpvMZOZzVizeLsBKcGA/aF2UPuV5v60JyH2MVSG6P511UMTj2F3H75.IT2HIlnvXzNb60FcZH1", - laurent = "$6$rounds=300000$dP0KNHwYb3JKigT$pN/LG7rWxQ4HniFtx5wKyJXBJUKP7R01zTNZ0qSK/aivw8ywGAOdfYiIQFqFhZFtVGvr11/7an.nesvm8iJUi.", - bob = "$6$rounds=300000$jCLCCt6LUpTz$PI1vvd1yaVYcCzqH8QAJFcJ60b6W/6sjcOsU7mAkNo7IE8FRGW1vkjF8I/T5jt/auv5ODLb1L4S2s.CAyZyUC" -} - --- Set this to a path this script can write to for storing a persistent --- cookie secret, which should be guarded. -local secret_filename = "/var/cache/cgit/auth-secret" - --- --- --- Authentication functions follow below. Swap these out if you want different authentication semantics. --- --- - --- Sets HTTP cookie headers based on post and sets up redirection. -function authenticate_post() - local hash = users[post["username"]] - local redirect = validate_value("redirect", post["redirect"]) - - if redirect == nil then - not_found() - return 0 - end - - redirect_to(redirect) - - if hash == nil or hash ~= unistd.crypt(post["password"], hash) then - set_cookie("cgitauth", "") - else - -- One week expiration time - local username = secure_value("username", post["username"], os.time() + 604800) - set_cookie("cgitauth", username) - end - - html("\n") - return 0 -end - - --- Returns 1 if the cookie is valid and 0 if it is not. -function authenticate_cookie() - accepted_users = protected_repos[cgit["repo"]] - if accepted_users == nil then - -- We return as valid if the repo is not protected. - return 1 - end - - local username = validate_value("username", get_cookie(http["cookie"], "cgitauth")) - if username == nil or not accepted_users[username:lower()] then - return 0 - else - return 1 - end -end - --- Prints the html for the login form. -function body() - html("<h2>Authentication Required</h2>") - html("<form method='post' action='") - html_attr(cgit["login"]) - html("'>") - html("<input type='hidden' name='redirect' value='") - html_attr(secure_value("redirect", cgit["url"], 0)) - html("' />") - html("<table>") - html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>") - html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>") - html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>") - html("</table></form>") - - return 0 -end - - - --- --- --- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions. --- --- - -local actions = {} -actions["authenticate-post"] = authenticate_post -actions["authenticate-cookie"] = authenticate_cookie -actions["body"] = body - -function filter_open(...) - action = actions[select(1, ...)] - - http = {} - http["cookie"] = select(2, ...) - http["method"] = select(3, ...) - http["query"] = select(4, ...) - http["referer"] = select(5, ...) - http["path"] = select(6, ...) - http["host"] = select(7, ...) - http["https"] = select(8, ...) - - cgit = {} - cgit["repo"] = select(9, ...) - cgit["page"] = select(10, ...) - cgit["url"] = select(11, ...) - cgit["login"] = select(12, ...) - -end - -function filter_close() - return action() -end - -function filter_write(str) - post = parse_qs(str) -end - - --- --- --- Utility functions based on keplerproject/wsapi. --- --- - -function url_decode(str) - if not str then - return "" - end - str = string.gsub(str, "+", " ") - str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end) - str = string.gsub(str, "\r\n", "\n") - return str -end - -function url_encode(str) - if not str then - return "" - end - str = string.gsub(str, "\n", "\r\n") - str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end) - str = string.gsub(str, " ", "+") - return str -end - -function parse_qs(qs) - local tab = {} - for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do - tab[url_decode(key)] = url_decode(val) - end - return tab -end - -function get_cookie(cookies, name) - cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";") - return url_decode(string.match(cookies, ";" .. name .. "=(.-);")) -end - -function tohex(b) - local x = "" - for i = 1, #b do - x = x .. string.format("%.2x", string.byte(b, i)) - end - return x -end - --- --- --- Cookie construction and validation helpers. --- --- - -local secret = nil - --- Loads a secret from a file, creates a secret, or returns one from memory. -function get_secret() - if secret ~= nil then - return secret - end - local secret_file = io.open(secret_filename, "r") - if secret_file == nil then - local old_umask = sysstat.umask(63) - local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16)) - local temporary_file = io.open(temporary_filename, "w") - if temporary_file == nil then - os.exit(177) - end - temporary_file:write(tohex(rand.bytes(32))) - temporary_file:close() - unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same. - unistd.unlink(temporary_filename) - sysstat.umask(old_umask) - secret_file = io.open(secret_filename, "r") - end - if secret_file == nil then - os.exit(177) - end - secret = secret_file:read() - secret_file:close() - if secret:len() ~= 64 then - os.exit(177) - end - return secret -end - --- Returns value of cookie if cookie is valid. Otherwise returns nil. -function validate_value(expected_field, cookie) - local i = 0 - local value = "" - local field = "" - local expiration = 0 - local salt = "" - local chmac = "" - - if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then - return nil - end - - for component in string.gmatch(cookie, "[^|]+") do - if i == 0 then - field = component - elseif i == 1 then - value = component - elseif i == 2 then - expiration = tonumber(component) - if expiration == nil then - expiration = -1 - end - elseif i == 3 then - salt = component - elseif i == 4 then - chmac = component - else - break - end - i = i + 1 - end - - if chmac == nil or chmac:len() == 0 then - return nil - end - - -- Lua hashes strings, so these comparisons are time invariant. - if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then - return nil - end - - if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then - return nil - end - - if url_decode(field) ~= expected_field then - return nil - end - - return url_decode(value) -end - -function secure_value(field, value, expiration) - if value == nil or value:len() <= 0 then - return "" - end - - local authstr = "" - local salt = tohex(rand.bytes(16)) - value = url_encode(value) - field = url_encode(field) - authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt - authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr)) - return authstr -end - -function set_cookie(cookie, value) - html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly") - if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then - html("; secure") - end - html("\n") -end - -function redirect_to(url) - html("Status: 302 Redirect\n") - html("Cache-Control: no-cache, no-store\n") - html("Location: " .. url .. "\n") -end - -function not_found() - html("Status: 404 Not Found\n") - html("Cache-Control: no-cache, no-store\n\n") -end diff --git a/third_party/cgit/html.c b/third_party/cgit/html.c index 7f81965fddcd..ced781adc6b5 100644 --- a/third_party/cgit/html.c +++ b/third_party/cgit/html.c @@ -59,7 +59,7 @@ char *fmt(const char *format, ...) va_start(args, format); len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args); va_end(args); - if (len > sizeof(buf[bufidx])) { + if (len >= sizeof(buf[bufidx])) { fprintf(stderr, "[html.c] string truncated: %s\n", format); exit(1); } @@ -80,7 +80,7 @@ char *fmtalloc(const char *format, ...) void html_raw(const char *data, size_t size) { - if (write(STDOUT_FILENO, data, size) != size) + if (fwrite(data, 1, size, stdout) != size) die_errno("write error on html output"); } diff --git a/third_party/cgit/robots.txt b/third_party/cgit/robots.txt index 4ce948fec2c9..1b33266d534a 100644 --- a/third_party/cgit/robots.txt +++ b/third_party/cgit/robots.txt @@ -1,3 +1,4 @@ User-agent: * Disallow: /*/snapshot/* +Disallow: /*/blame/* Allow: / diff --git a/third_party/cgit/scan-tree.c b/third_party/cgit/scan-tree.c index 6a2f65a86b2c..1e3b43debf5b 100644 --- a/third_party/cgit/scan-tree.c +++ b/third_party/cgit/scan-tree.c @@ -137,8 +137,6 @@ static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn) repo->path = xstrdup(path->buf); while (!repo->owner) { if ((pwd = getpwuid(st.st_uid)) == NULL) { - fprintf(stderr, "Error reading owner-info for %s: %s (%d)\n", - path->buf, strerror(errno), errno); break; } if (pwd->pw_gecos) diff --git a/third_party/cgit/tests/filters/dump.lua b/third_party/cgit/tests/filters/dump.lua deleted file mode 100644 index 1f15c931051c..000000000000 --- a/third_party/cgit/tests/filters/dump.lua +++ /dev/null @@ -1,17 +0,0 @@ -function filter_open(...) - buffer = "" - for i = 1, select("#", ...) do - buffer = buffer .. select(i, ...) .. " " - end -end - -function filter_close() - html(buffer) - return 0 -end - -function filter_write(str) - buffer = buffer .. string.upper(str) -end - - diff --git a/third_party/cgit/tests/setup.sh b/third_party/cgit/tests/setup.sh index 8db810ff114f..31e7d5bb27b7 100755 --- a/third_party/cgit/tests/setup.sh +++ b/third_party/cgit/tests/setup.sh @@ -60,12 +60,6 @@ fi FILTER_DIRECTORY=$(cd ../filters && pwd) -if cgit --version | grep -F -q "[+] Lua scripting"; then - export CGIT_HAS_LUA=1 -else - export CGIT_HAS_LUA=0 -fi - mkrepo() { name=$1 count=$2 @@ -144,19 +138,6 @@ repo.email-filter=exec:$FILTER_DIRECTORY/dump.sh repo.source-filter=exec:$FILTER_DIRECTORY/dump.sh repo.readme=master:a+b EOF - - if [ $CGIT_HAS_LUA -eq 1 ]; then - cat >>cgitrc <<EOF -repo.url=filter-lua -repo.path=$PWD/repos/filter/.git -repo.desc=filtered repo -repo.about-filter=lua:$FILTER_DIRECTORY/dump.lua -repo.commit-filter=lua:$FILTER_DIRECTORY/dump.lua -repo.email-filter=lua:$FILTER_DIRECTORY/dump.lua -repo.source-filter=lua:$FILTER_DIRECTORY/dump.lua -repo.readme=master:a+b -EOF - fi } cgit_query() diff --git a/third_party/cgit/tests/t0105-commit.sh b/third_party/cgit/tests/t0105-commit.sh index 1a12ee39a9e7..cfed1e7d6933 100755 --- a/third_party/cgit/tests/t0105-commit.sh +++ b/third_party/cgit/tests/t0105-commit.sh @@ -11,7 +11,7 @@ test_expect_success 'find commit subject' ' grep "<div class=.commit-subject.>commit 5<" tmp ' -test_expect_success 'find commit msg' 'grep "<div class=.commit-msg.></div>" tmp' +test_expect_success 'find commit msg' 'grep "<pre class=.commit-msg.></pre>" tmp' test_expect_success 'find diffstat' 'grep "<table summary=.diffstat. class=.diffstat.>" tmp' test_expect_success 'find diff summary' ' @@ -29,8 +29,8 @@ test_expect_success 'root commit contains diffstat' ' ' test_expect_success 'root commit contains diff' ' - grep ">diff --git a/file-1 b/file-1<" tmp && - grep "<div class=.add.>+1</div>" tmp + grep ">diff --git a/file-1 b/file-1" tmp && + grep "<span class=.add.>+1</span>" tmp ' test_done diff --git a/third_party/cgit/tests/t0106-diff.sh b/third_party/cgit/tests/t0106-diff.sh index 82b645ec72ab..62a0a74a6473 100755 --- a/third_party/cgit/tests/t0106-diff.sh +++ b/third_party/cgit/tests/t0106-diff.sh @@ -9,11 +9,11 @@ test_expect_success 'find blob link' 'grep "<a href=./foo/tree/file-5?id=" tmp' test_expect_success 'find added file' 'grep "new file mode 100644" tmp' test_expect_success 'find hunk header' ' - grep "<div class=.hunk.>@@ -0,0 +1 @@</div>" tmp + grep "<span class=.hunk.>@@ -0,0 +1 @@</span>" tmp ' test_expect_success 'find added line' ' - grep "<div class=.add.>+5</div>" tmp + grep "<span class=.add.>+5</span>" tmp ' test_done diff --git a/third_party/cgit/tests/t0111-filter.sh b/third_party/cgit/tests/t0111-filter.sh index 2fdc3669f492..e5d357507cc3 100755 --- a/third_party/cgit/tests/t0111-filter.sh +++ b/third_party/cgit/tests/t0111-filter.sh @@ -4,9 +4,6 @@ test_description='Check filtered content' . ./setup.sh prefixes="exec" -if [ $CGIT_HAS_LUA -eq 1 ]; then - prefixes="$prefixes lua" -fi for prefix in $prefixes do diff --git a/third_party/cgit/ui-atom.c b/third_party/cgit/ui-atom.c index 1056f3639752..0cf8441d4691 100644 --- a/third_party/cgit/ui-atom.c +++ b/third_party/cgit/ui-atom.c @@ -67,17 +67,12 @@ static void add_entry(struct commit *commit, const char *host) html("'/>\n"); free(pageurl); } - htmlf("<id>%s</id>\n", hex); + html("<id>"); + html_txtf("urn:%s:%s", the_hash_algo->name, hex); + html("</id>\n"); html("<content type='text'>\n"); html_txt(info->msg); html("</content>\n"); - html("<content type='xhtml'>\n"); - html("<div xmlns='http://www.w3.org/1999/xhtml'>\n"); - html("<pre>\n"); - html_txt(info->msg); - html("</pre>\n"); - html("</div>\n"); - html("</content>\n"); html("</entry>\n"); cgit_free_commitinfo(info); } @@ -90,6 +85,7 @@ void cgit_print_atom(char *tip, const char *path, int max_count) struct commit *commit; struct rev_info rev; int argc = 2; + int first = 1; if (ctx.qry.show_all) argv[1] = "--all"; @@ -130,18 +126,30 @@ void cgit_print_atom(char *tip, const char *path, int max_count) html_txt(ctx.repo->desc); html("</subtitle>\n"); if (host) { + char *fullurl = cgit_currentfullurl(); char *repourl = cgit_repourl(ctx.repo->url); + html("<id>"); + html_txtf("%s%s%s", cgit_httpscheme(), host, fullurl); + html("</id>\n"); + html("<link rel='self' href='"); + html_attrf("%s%s%s", cgit_httpscheme(), host, fullurl); + html("'/>\n"); html("<link rel='alternate' type='text/html' href='"); - html(cgit_httpscheme()); - html_attr(host); - html_attr(repourl); + html_attrf("%s%s%s", cgit_httpscheme(), host, repourl); html("'/>\n"); + free(fullurl); free(repourl); } while ((commit = get_revision(&rev)) != NULL) { + if (first) { + html("<updated>"); + html_txt(show_date(commit->date, 0, + date_mode_from_type(DATE_ISO8601_STRICT))); + html("</updated>\n"); + first = 0; + } add_entry(commit, host); - free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + release_commit_memory(the_repository->parsed_objects, commit); commit->parents = NULL; } html("</feed>\n"); diff --git a/third_party/cgit/ui-blame.c b/third_party/cgit/ui-blame.c index 786a7105d5ac..ca770994a6a6 100644 --- a/third_party/cgit/ui-blame.c +++ b/third_party/cgit/ui-blame.c @@ -54,6 +54,15 @@ static void emit_blame_entry_hash(struct blame_entry *ent) html("</span>"); free(detail); + if (!parse_commit(suspect->commit) && suspect->commit->parents) { + struct commit *parent = suspect->commit->parents->item; + + html(" "); + cgit_blame_link("^", "Blame the previous revision", NULL, + ctx.qry.head, oid_to_hex(&parent->object.oid), + suspect->path); + } + while (line++ < ent->num_lines) html("\n"); } diff --git a/third_party/cgit/ui-commit.c b/third_party/cgit/ui-commit.c index 96eecebe1931..72cb98b72a3f 100644 --- a/third_party/cgit/ui-commit.c +++ b/third_party/cgit/ui-commit.c @@ -39,7 +39,7 @@ void cgit_print_commit(char *hex, const char *prefix) } info = cgit_parse_commit(commit); - format_display_notes(&oid, ¬es, PAGE_ENCODING, 0); + format_display_notes(&oid, ¬es, PAGE_ENCODING, 1); load_ref_decorations(NULL, DECORATE_FULL_REFS); @@ -121,11 +121,11 @@ void cgit_print_commit(char *hex, const char *prefix) cgit_close_filter(ctx.repo->commit_filter); show_commit_decorations(commit); html("</div>"); - html("<div class='commit-msg'>"); + html("<pre class='commit-msg'>"); cgit_open_filter(ctx.repo->commit_filter); html_txt(info->msg); cgit_close_filter(ctx.repo->commit_filter); - html("</div>"); + html("</pre>"); if (notes.len != 0) { html("<div class='notes-header'>Notes</div>"); html("<div class='notes'>"); diff --git a/third_party/cgit/ui-diff.c b/third_party/cgit/ui-diff.c index 5ed5990c2915..2a64ae8f14af 100644 --- a/third_party/cgit/ui-diff.c +++ b/third_party/cgit/ui-diff.c @@ -231,11 +231,11 @@ static void print_line(char *line, int len) else if (line[0] == '@') class = "hunk"; - htmlf("<div class='%s'>", class); + htmlf("<span class='%s'>", class); line[len-1] = '\0'; html_txt(line); - html("</div>"); line[len-1] = c; + html("</span>\n"); } static void header(const struct object_id *oid1, char *path1, int mode1, @@ -245,22 +245,23 @@ static void header(const struct object_id *oid1, char *path1, int mode1, int subproject; subproject = (S_ISGITLINK(mode1) || S_ISGITLINK(mode2)); - html("<div class='head'>"); + html("<span class='head'>"); html("diff --git a/"); html_txt(path1); html(" b/"); html_txt(path2); + html("\n"); if (mode1 == 0) - htmlf("<br/>new file mode %.6o", mode2); + htmlf("new file mode %.6o\n", mode2); if (mode2 == 0) - htmlf("<br/>deleted file mode %.6o", mode1); + htmlf("deleted file mode %.6o\n", mode1); if (!subproject) { abbrev1 = xstrdup(find_unique_abbrev(oid1, DEFAULT_ABBREV)); abbrev2 = xstrdup(find_unique_abbrev(oid2, DEFAULT_ABBREV)); - htmlf("<br/>index %s..%s", abbrev1, abbrev2); + htmlf("index %s..%s", abbrev1, abbrev2); free(abbrev1); free(abbrev2); if (mode1 != 0 && mode2 != 0) { @@ -268,28 +269,31 @@ static void header(const struct object_id *oid1, char *path1, int mode1, if (mode2 != mode1) htmlf("..%.6o", mode2); } + html("\n"); if (is_null_oid(oid1)) { path1 = "dev/null"; - html("<br/>--- /"); + html("--- /"); } else - html("<br/>--- a/"); + html("--- a/"); if (mode1 != 0) cgit_tree_link(path1, NULL, NULL, ctx.qry.head, oid_to_hex(old_rev_oid), path1); else html_txt(path1); + html("\n"); if (is_null_oid(oid2)) { path2 = "dev/null"; - html("<br/>+++ /"); + html("+++ /"); } else - html("<br/>+++ b/"); + html("+++ b/"); if (mode2 != 0) cgit_tree_link(path2, NULL, NULL, ctx.qry.head, oid_to_hex(new_rev_oid), path2); else html_txt(path2); + html("\n"); } - html("</div>"); + html("</span>"); } static void filepair_cb(struct diff_filepair *pair) @@ -488,12 +492,12 @@ void cgit_print_diff(const char *new_rev, const char *old_rev, html("<table summary='ssdiff' class='ssdiff'>"); } else { html("<table summary='diff' class='diff'>"); - html("<tr><td>"); + html("<tr><td><pre>"); } cgit_diff_tree(old_rev_oid, new_rev_oid, filepair_cb, prefix, ctx.qry.ignorews); if (!use_ssdiff) - html("</td></tr>"); + html("</pre></td></tr>"); html("</table>"); if (show_ctrls) diff --git a/third_party/cgit/ui-log.c b/third_party/cgit/ui-log.c index 565929f3f046..cfa9192b9365 100644 --- a/third_party/cgit/ui-log.c +++ b/third_party/cgit/ui-log.c @@ -75,11 +75,13 @@ void show_commit_decorations(struct commit *commit) * don't display anything. */ break; case DECORATION_REF_LOCAL: + html(" "); cgit_log_link(buf, NULL, "branch-deco", buf, NULL, ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg, 0); break; case DECORATION_REF_TAG: + html(" "); if (!read_ref(deco->name, &oid_tag) && !peel_iterated_oid(&oid_tag, &peeled)) is_annotated = !oideq(&oid_tag, &peeled); cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf); @@ -87,12 +89,14 @@ void show_commit_decorations(struct commit *commit) case DECORATION_REF_REMOTE: if (!ctx.repo->enable_remote_branches) break; + html(" "); cgit_log_link(buf, NULL, "remote-deco", NULL, oid_to_hex(&commit->object.oid), ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg, 0); break; default: + html(" "); cgit_commit_link(buf, NULL, "deco", ctx.qry.head, oid_to_hex(&commit->object.oid), ctx.qry.vpath); @@ -489,8 +493,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; /* nop */) { if (show_commit(commit, &rev)) i++; - free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + release_commit_memory(the_repository->parsed_objects, commit); commit->parents = NULL; } @@ -511,8 +514,7 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern i++; print_commit(commit, &rev); } - free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + release_commit_memory(the_repository->parsed_objects, commit); commit->parents = NULL; } if (pager) { diff --git a/third_party/cgit/ui-repolist.c b/third_party/cgit/ui-repolist.c index 529a2038baa2..97b11c5f1678 100644 --- a/third_party/cgit/ui-repolist.c +++ b/third_party/cgit/ui-repolist.c @@ -321,7 +321,7 @@ void cgit_print_repolist(void) } htmlf("<tr><td class='%s'>", !sorted && section ? "sublevel-repo" : "toplevel-repo"); - cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); + cgit_summary_link(ctx.repo->name, NULL, NULL, NULL); html("</td><td>"); repourl = cgit_repourl(ctx.repo->url); html_link_open(repourl, NULL, NULL); @@ -353,8 +353,10 @@ void cgit_print_repolist(void) if (ctx.cfg.enable_index_links) { html("<td>"); cgit_summary_link("summary", NULL, "button", NULL); + html(" "); cgit_log_link("log", NULL, "button", NULL, NULL, NULL, 0, NULL, NULL, ctx.qry.showmsg, 0); + html(" "); cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL); html("</td>"); } diff --git a/third_party/cgit/ui-shared.c b/third_party/cgit/ui-shared.c index cf7907ffe393..2e8896c982dd 100644 --- a/third_party/cgit/ui-shared.c +++ b/third_party/cgit/ui-shared.c @@ -836,7 +836,7 @@ void cgit_print_docend(void) if (ctx.cfg.footer) html_include(ctx.cfg.footer); else { - htmlf("<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit %s</a> " + htmlf("<div class='footer'>generated by <a href='https://git.causal.agency/cgit-pink/about/'>cgit-pink %s</a> " "(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string); html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_DOTTIME))); html("</div>\n"); @@ -898,7 +898,7 @@ void cgit_add_clone_urls(void (*fn)(const char *)) static int print_this_commit_option(void) { struct object_id oid; - if (get_oid(ctx.qry.head, &oid)) + if (!ctx.qry.head || get_oid(ctx.qry.head, &oid)) return 1; html_option(oid_to_hex(&oid), "this commit", ctx.qry.head); return 0; @@ -1003,7 +1003,7 @@ static void print_header(void) html("<td class='main'>"); if (ctx.repo) { - cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL); + cgit_summary_link(ctx.repo->name, NULL, NULL, NULL); if (ctx.env.authenticated) { html("</td><td class='form'>"); html("<form method='get'>\n"); @@ -1027,7 +1027,13 @@ static void print_header(void) if (ctx.repo) { html_txt(ctx.repo->desc); html("</td><td class='sub right'>"); - html_txt(ctx.repo->owner); + if (ctx.repo->owner_filter) { + cgit_open_filter(ctx.repo->owner_filter); + html_txt(ctx.repo->owner); + cgit_close_filter(ctx.repo->owner_filter); + } else { + html_txt(ctx.repo->owner); + } } else { if (ctx.cfg.root_desc) html_txt(ctx.cfg.root_desc); @@ -1046,29 +1052,37 @@ void cgit_print_pageheader(void) if (ctx.repo->readme.nr) { cgit_about_link("about", NULL, hc("about"), ctx.qry.head, ctx.qry.oid, ctx.qry.vpath); + html(" "); } cgit_summary_link("summary", NULL, hc("summary"), ctx.qry.head); + html(" "); cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head, ctx.qry.oid, NULL); + html(" "); cgit_log_link("log", NULL, hc("log"), ctx.qry.head, NULL, ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg, ctx.qry.follow); + html(" "); if (ctx.qry.page && !strcmp(ctx.qry.page, "blame")) cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head, ctx.qry.oid, ctx.qry.vpath); else cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head, - ctx.qry.oid, ctx.qry.vpath); + ctx.qry.oid, ctx.qry.vpath); + html(" "); cgit_commit_link("commit", NULL, hc("commit"), ctx.qry.head, ctx.qry.oid, ctx.qry.vpath); + html(" "); cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head, ctx.qry.oid, ctx.qry.oid2, ctx.qry.vpath); - if (ctx.repo->max_stats) + if (ctx.repo->max_stats) { + html(" "); cgit_stats_link("stats", NULL, hc("stats"), ctx.qry.head, ctx.qry.vpath); + } if (ctx.repo->homepage) { - html("<a href='"); + html(" <a href='"); html_attr(ctx.repo->homepage); html("'>homepage</a>"); } @@ -1212,9 +1226,12 @@ void cgit_set_title_from_path(const char *path) if (!path) return; - for (last_slash = path + strlen(path); (slash = memrchr(path, '/', last_slash - path)) != NULL; last_slash = slash) { + last_slash = path + strlen(path); + for (slash = last_slash; slash > path; --slash) { + if (*slash != '/') continue; strbuf_add(&sb, slash + 1, last_slash - slash - 1); strbuf_addstr(&sb, " \xc2\xab "); + last_slash = slash; } strbuf_add(&sb, path, last_slash - path); strbuf_addf(&sb, " - %s", ctx.page.title); diff --git a/third_party/cgit/ui-snapshot.c b/third_party/cgit/ui-snapshot.c index 18361a655384..280139355ab0 100644 --- a/third_party/cgit/ui-snapshot.c +++ b/third_party/cgit/ui-snapshot.c @@ -37,6 +37,9 @@ static int write_archive_type(const char *format, const char *hex, const char *p /* strvec guarantees a trailing NULL entry. */ memcpy(nargv, argv.v, sizeof(char *) * (argv.nr + 1)); + if (fflush(stdout)) + return errno; + result = write_archive(argv.nr, nargv, NULL, the_repository, NULL, 0); strvec_clear(&argv); free(nargv); diff --git a/third_party/cgit/ui-stats.c b/third_party/cgit/ui-stats.c index 09b3625e7e0c..40ed6c21df62 100644 --- a/third_party/cgit/ui-stats.c +++ b/third_party/cgit/ui-stats.c @@ -241,8 +241,7 @@ static struct string_list collect_stats(const struct cgit_period *period) memset(&authors, 0, sizeof(authors)); while ((commit = get_revision(&rev)) != NULL) { add_commit(&authors, commit, period); - free_commit_buffer(the_repository->parsed_objects, commit); - free_commit_list(commit->parents); + release_commit_memory(the_repository->parsed_objects, commit); commit->parents = NULL; } return authors; diff --git a/third_party/cgit/ui-summary.c b/third_party/cgit/ui-summary.c index d1bb6a59faea..ec7b76fabf24 100644 --- a/third_party/cgit/ui-summary.c +++ b/third_party/cgit/ui-summary.c @@ -114,6 +114,9 @@ void cgit_print_repo_readme(const char *path) } free(mimetype); + if (path) + ctx.page.title = fmtalloc("%s - %s", path, ctx.page.title); + cgit_print_layout_start(); if (ctx.repo->readme.nr == 0) goto done; diff --git a/third_party/cgit/ui-tag.c b/third_party/cgit/ui-tag.c index 08789102f678..7be28081493f 100644 --- a/third_party/cgit/ui-tag.c +++ b/third_party/cgit/ui-tag.c @@ -25,9 +25,9 @@ static void print_tag_content(char *buf) html_txt(buf); html("</div>"); if (p) { - html("<div class='commit-msg'>"); + html("<pre class='commit-msg'>"); html_txt(++p); - html("</div>"); + html("</pre>"); } } diff --git a/third_party/cgit/ui-tree.c b/third_party/cgit/ui-tree.c index 19074507635a..21e0b88448a4 100644 --- a/third_party/cgit/ui-tree.c +++ b/third_party/cgit/ui-tree.c @@ -261,15 +261,21 @@ static int ls_item(const struct object_id *oid, struct strbuf *base, cgit_log_link("log", NULL, "button", ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL, ctx.qry.showmsg, 0); - if (ctx.repo->max_stats) + if (ctx.repo->max_stats) { + html(" "); cgit_stats_link("stats", NULL, "button", ctx.qry.head, fullpath.buf); - if (!S_ISGITLINK(mode)) + } + if (!S_ISGITLINK(mode)) { + html(" "); cgit_plain_link("plain", NULL, "button", ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf); - if (!S_ISDIR(mode) && ctx.repo->enable_blame) + } + if (!S_ISDIR(mode) && ctx.repo->enable_blame) { + html(" "); cgit_blame_link("blame", NULL, "button", ctx.qry.head, walk_tree_ctx->curr_rev, fullpath.buf); + } html("</td></tr>\n"); cleanup: |