subtree(3p/cgit): merge cgit-pink into depot cgit
cgit-pink is a maintained fork of cgit that follows upstream git more closely and already contains a lot of patches we already had applied. Consequently, it seems sensible it becomes our future upstream, we may even be able to upstream some of our custom, less invasive patches. Change-Id: Ia081e4508866f32298986c7160f4890c8a7c8922
This commit is contained in:
commit
40803d9c6d
39 changed files with 185 additions and 1616 deletions
2
third_party/cgit/AUTHORS
vendored
2
third_party/cgit/AUTHORS
vendored
|
@ -1,5 +1,5 @@
|
|||
Maintainer:
|
||||
Jason A. Donenfeld <Jason@zx2c4.com>
|
||||
June McEnroe <june@causal.agency>
|
||||
|
||||
Contributors:
|
||||
Jason A. Donenfeld <Jason@zx2c4.com>
|
||||
|
|
6
third_party/cgit/Makefile
vendored
6
third_party/cgit/Makefile
vendored
|
@ -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
|
||||
|
||||
|
|
31
third_party/cgit/README
vendored
31
third_party/cgit/README
vendored
|
@ -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>
|
||||
|
|
52
third_party/cgit/cache.c
vendored
52
third_party/cgit/cache.c
vendored
|
@ -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
|
||||
|
||||
start_off = slot->keylen + 1;
|
||||
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)) {
|
||||
|
|
21
third_party/cgit/cgit.c
vendored
21
third_party/cgit/cgit.c
vendored
|
@ -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();
|
||||
|
|
32
third_party/cgit/cgit.css
vendored
32
third_party/cgit/cgit.css
vendored
|
@ -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%;
|
||||
}
|
||||
|
||||
|
|
1
third_party/cgit/cgit.h
vendored
1
third_party/cgit/cgit.h
vendored
|
@ -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);
|
||||
|
||||
|
|
27
third_party/cgit/cgit.mk
vendored
27
third_party/cgit/cgit.mk
vendored
|
@ -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))
|
||||
|
|
46
third_party/cgit/cgitrc.5.txt
vendored
46
third_party/cgit/cgitrc.5.txt
vendored
|
@ -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
|
||||
|
|
11
third_party/cgit/default.nix
vendored
11
third_party/cgit/default.nix
vendored
|
@ -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 ];
|
||||
};
|
||||
}
|
||||
|
|
BIN
third_party/cgit/favicon.ico
vendored
BIN
third_party/cgit/favicon.ico
vendored
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
239
third_party/cgit/filter.c
vendored
239
third_party/cgit/filter.c
vendored
|
@ -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)
|
||||
|
|
35
third_party/cgit/filters/email-gravatar.lua
vendored
35
third_party/cgit/filters/email-gravatar.lua
vendored
|
@ -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
|
||||
|
||||
|
3
third_party/cgit/filters/email-gravatar.py
vendored
3
third_party/cgit/filters/email-gravatar.py
vendored
|
@ -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
|
||||
|
|
36
third_party/cgit/filters/email-libravatar.lua
vendored
36
third_party/cgit/filters/email-libravatar.lua
vendored
|
@ -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
|
||||
|
||||
|
359
third_party/cgit/filters/file-authentication.lua
vendored
359
third_party/cgit/filters/file-authentication.lua
vendored
|
@ -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
|
|
@ -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
|
17
third_party/cgit/filters/owner-example.lua
vendored
17
third_party/cgit/filters/owner-example.lua
vendored
|
@ -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
|
314
third_party/cgit/filters/simple-authentication.lua
vendored
314
third_party/cgit/filters/simple-authentication.lua
vendored
|
@ -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
|
4
third_party/cgit/html.c
vendored
4
third_party/cgit/html.c
vendored
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
1
third_party/cgit/robots.txt
vendored
1
third_party/cgit/robots.txt
vendored
|
@ -1,3 +1,4 @@
|
|||
User-agent: *
|
||||
Disallow: /*/snapshot/*
|
||||
Disallow: /*/blame/*
|
||||
Allow: /
|
||||
|
|
2
third_party/cgit/scan-tree.c
vendored
2
third_party/cgit/scan-tree.c
vendored
|
@ -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)
|
||||
|
|
17
third_party/cgit/tests/filters/dump.lua
vendored
17
third_party/cgit/tests/filters/dump.lua
vendored
|
@ -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
|
||||
|
||||
|
19
third_party/cgit/tests/setup.sh
vendored
19
third_party/cgit/tests/setup.sh
vendored
|
@ -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()
|
||||
|
|
6
third_party/cgit/tests/t0105-commit.sh
vendored
6
third_party/cgit/tests/t0105-commit.sh
vendored
|
@ -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
|
||||
|
|
4
third_party/cgit/tests/t0106-diff.sh
vendored
4
third_party/cgit/tests/t0106-diff.sh
vendored
|
@ -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
|
||||
|
|
3
third_party/cgit/tests/t0111-filter.sh
vendored
3
third_party/cgit/tests/t0111-filter.sh
vendored
|
@ -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
|
||||
|
|
36
third_party/cgit/ui-atom.c
vendored
36
third_party/cgit/ui-atom.c
vendored
|
@ -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("<link rel='alternate' type='text/html' href='");
|
||||
html(cgit_httpscheme());
|
||||
html_attr(host);
|
||||
html_attr(repourl);
|
||||
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_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");
|
||||
|
|
9
third_party/cgit/ui-blame.c
vendored
9
third_party/cgit/ui-blame.c
vendored
|
@ -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");
|
||||
}
|
||||
|
|
6
third_party/cgit/ui-commit.c
vendored
6
third_party/cgit/ui-commit.c
vendored
|
@ -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'>");
|
||||
|
|
30
third_party/cgit/ui-diff.c
vendored
30
third_party/cgit/ui-diff.c
vendored
|
@ -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)
|
||||
|
|
10
third_party/cgit/ui-log.c
vendored
10
third_party/cgit/ui-log.c
vendored
|
@ -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) {
|
||||
|
|
4
third_party/cgit/ui-repolist.c
vendored
4
third_party/cgit/ui-repolist.c
vendored
|
@ -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>");
|
||||
}
|
||||
|
|
33
third_party/cgit/ui-shared.c
vendored
33
third_party/cgit/ui-shared.c
vendored
|
@ -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);
|
||||
|
|
3
third_party/cgit/ui-snapshot.c
vendored
3
third_party/cgit/ui-snapshot.c
vendored
|
@ -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);
|
||||
|
|
3
third_party/cgit/ui-stats.c
vendored
3
third_party/cgit/ui-stats.c
vendored
|
@ -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;
|
||||
|
|
3
third_party/cgit/ui-summary.c
vendored
3
third_party/cgit/ui-summary.c
vendored
|
@ -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;
|
||||
|
|
4
third_party/cgit/ui-tag.c
vendored
4
third_party/cgit/ui-tag.c
vendored
|
@ -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>");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
12
third_party/cgit/ui-tree.c
vendored
12
third_party/cgit/ui-tree.c
vendored
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue