diff options
author | kx <kx@radix.pro> | 2023-03-24 03:51:10 +0300 |
---|---|---|
committer | kx <kx@radix.pro> | 2023-03-24 03:51:10 +0300 |
commit | 05d292b208dfe01324826b4c87bbc4da3389a0d5 (patch) | |
tree | b10a2269e9320785f3b61189e75f6778fa167986 /cgitcgi/ctx.c | |
parent | 40ab18a661ff6ada40e73969be293918d346a2f5 (diff) | |
download | cgit-ui-05d292b208dfe01324826b4c87bbc4da3389a0d5.tar.xz |
Version 0.1.7
Diffstat (limited to 'cgitcgi/ctx.c')
-rw-r--r-- | cgitcgi/ctx.c | 1803 |
1 files changed, 1803 insertions, 0 deletions
diff --git a/cgitcgi/ctx.c b/cgitcgi/ctx.c new file mode 100644 index 0000000..f5f00aa --- /dev/null +++ b/cgitcgi/ctx.c @@ -0,0 +1,1803 @@ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <stdio.h> +#include <sys/sysinfo.h> +#include <sys/types.h> +#ifdef HAVE_INTTYPES_H +#include <inttypes.h> +#else +#include <stdint.h> +#endif +#include <stddef.h> /* offsetof(3) */ +#include <dirent.h> +#include <sys/stat.h> /* chmod(2) */ +#include <sys/file.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <limits.h> +#include <string.h> /* strdup(3) */ +#include <strings.h> /* strcasecmp(3) */ +#include <libgen.h> /* basename(3) */ +#include <ctype.h> /* tolower(3) */ +#include <errno.h> +#include <time.h> +#include <sys/time.h> +#include <pwd.h> +#include <grp.h> +#include <stdarg.h> +#include <locale.h> +#include <unistd.h> + +#include <git2.h> + +#include <nls.h> + +#include <defs.h> + +#include <fatal.h> +#include <http.h> +#include <html.h> + +#include <dlist.h> +#include <strbuf.h> +#include <repolist.h> +#include <wrapper.h> +#include <system.h> +#include <date.h> + +#include <ctx.h> + +#include <git-shared.h> +#include <ui-shared.h> + + +/* Allocate memory in .bss segment: */ +static struct __context mctx; + +/* Share address of __context: */ +struct __context *pmctx = &mctx; + +void __mctx_init( void ) +{ + pmctx->_cur_brk = (void *)&((pmctx->_mem)[0]); +} + +static int +__brk( void *end_d ) +{ + void *ptr = __mem; + + if( (unsigned char *)end_d < (unsigned char *)ptr || + (unsigned char *)end_d > (unsigned char *)ptr + CONTEXT_MEM_SIZE ) + { + errno = ENOMEM; + return( -1 ); + } + + /* + __cur_brk = (unsigned char *)end_d; + + Функция __brk() лишь проверяет границы + области памяти, значение __cur_brk выставляется + в __sbrk() . + *************************************************/ + + return( 0 ); + +} /* End of __brk() */ + +void * +__sbrk( int incr ) +{ + void *ptr = __cur_brk; + register int rc; + + if( incr == 0 ) return( ptr ); + + rc = __brk( ptr + incr ); + if( rc == -1 ) + { + /* errno is set into __brk() */ + return( (void *)0 ); + } + + __cur_brk = ((unsigned char *)ptr) + (int)incr; + + return( ptr ); + +} /* End of __sbrk() */ + + +struct cgit_context ctx; + +const char *ptype_repolist = "repolist"; +const char *ptype_repo = "repo"; + +void cgit_prepare_context( void ) +{ + memset( &ctx, 0, sizeof(ctx) ); + + ctx.env.http_host = getenv("HTTP_HOST"); + ctx.env.https = getenv("HTTPS"); + ctx.env.no_http = getenv("NO_HTTP"); + ctx.env.path_info = getenv("PATH_INFO"); + ctx.env.query_string = getenv("QUERY_STRING"); + ctx.env.request_uri = getenv("REQUEST_URI"); + + if( ctx.env.request_uri == NULL ) + ctx.env.request_uri = "/"; + + ctx.env.request_scheme = getenv("REQUEST_SCHEME"); + ctx.env.request_method = getenv("REQUEST_METHOD"); + ctx.env.script_name = getenv("SCRIPT_NAME"); + ctx.env.server_name = getenv("SERVER_NAME"); + ctx.env.server_port = getenv("SERVER_PORT"); + ctx.env.http_cookie = getenv("HTTP_COOKIE"); + ctx.env.content_lenght = getenv("CONTENT_LENGTH") ? strtoul(getenv("CONTENT_LENGTH"), NULL, 10) : 0; + ctx.env.http_root = NULL; + ctx.env.authenticated = 0; + + ctx.query.ofs = 0; + ctx.query.rev = "0"; + ctx.query.revision = NULL; + ctx.query.operation = NULL; + ctx.query.search = NULL; + + ctx.page.mimetype = "text/html"; + ctx.page.charset = "UTF-8"; + ctx.page.size = 0; + ctx.page.modified = time(NULL); + ctx.page.expires = ctx.page.modified + 5 * 60; + ctx.page.status = 200; + ctx.page.status_message = "OK"; + ctx.page.header = "/.cgit/html/header.html"; + ctx.page.footer = "/.cgit/html/footer.html"; + + ctx.vars.css = "/.cgit/css/cgit.css"; + ctx.vars.owner = "Andrey V.Kosteltsev"; + ctx.vars.author = "Andrey V.Kosteltsev"; + ctx.vars.description = "Git repositories hosted at Solar System, Earth"; + ctx.vars.keywords = "cGit repositories"; + ctx.vars.title = "Git Repositories"; + ctx.vars.favicon_path = "/.cgit/pixmaps/favicon"; + ctx.vars.syntax_highlight_css = "_cgit.css"; + ctx.vars.logo = "/.cgit/pixmaps/cgit-banner-280x280.png"; + ctx.vars.logo_alt = "Example.org"; + ctx.vars.logo_link = "https://example.org"; + ctx.vars.home_page = "https://example.org"; + ctx.vars.snapshots = "tar.xz"; + ctx.vars.status_line = "Git Repositories"; + ctx.vars.main_menu_logo = "/.cgit/pixmaps/logo/git-logo-white-256x256.svg"; + ctx.vars.main_menu_item = "<a href='/'>Index</a>"; + ctx.vars.left_menu_items = ""; + ctx.vars.popup_menu_items = "<div class='item'><span class='icon las la-home'></span><a href='https://example.org/' target='_blank'>Home page</a></div>"; + ctx.vars.right_menu_items = "<div class='item'><a href='https://example.org/' target='_blank'>Home page</a></div>"; + ctx.vars.copyright_notice = "By using any website materials you agree to indicate source."; + ctx.vars.copyright = "© 2022 Andrey V.Kosteltsev. All Rights Reserved."; + ctx.vars.page_type = ptype_repolist; + ctx.vars.page_size = "200"; + ctx.vars.num_of_repos = "0"; + + ctx.repo.name = NULL; + ctx.repo.info = CGIT_INFO_INIT; + ctx.repo.git_root = NULL; + ctx.repo.repo_root = NULL; + ctx.repo.relative_path = NULL; + ctx.repo.relative_info = CGIT_INFO_INIT; + ctx.repo.relative_html = NULL; + ctx.repo.relative_href = "/"; + ctx.repo.search_placeholder = "repository..."; + ctx.repo.trunk = "master"; + ctx.repo.clone_prefix = NULL; + ctx.repo.clone_ro_prefix = NULL; + ctx.repo.nbranches = 0; + ctx.repo.ncommits = 0; + ctx.repo.ntags = 0; + + ctx.promo.analytic_links = NULL; + ctx.promo.analytic_scripts = NULL; + ctx.promo.donate = 0; + ctx.promo.donate_css = NULL; + ctx.promo.donate_html = NULL; + ctx.promo.donate_js = NULL; + ctx.promo.donate_header = NULL; + ctx.promo.donate_purpose = NULL; + + ctx.vers.git = NULL; + ctx.vers.nginx = NULL; + ctx.vers.cgit = PROGRAM_VERSION; +} + +void cgit_prepare_template_variables( void ) +{ + envtab_install( &strbuf_envtab, "css", ctx.vars.css, fatal_html ); + envtab_install( &strbuf_envtab, "owner", ctx.vars.owner, fatal_html ); + envtab_install( &strbuf_envtab, "author", ctx.vars.author, fatal_html ); + envtab_install( &strbuf_envtab, "description", ctx.vars.description, fatal_html ); + envtab_install( &strbuf_envtab, "keywords", ctx.vars.keywords, fatal_html ); + envtab_install( &strbuf_envtab, "title", ctx.vars.title, fatal_html ); + envtab_install( &strbuf_envtab, "favicon-path", ctx.vars.favicon_path, fatal_html ); + envtab_install( &strbuf_envtab, "syntax-highlight-css", ctx.vars.syntax_highlight_css, fatal_html ); + envtab_install( &strbuf_envtab, "logo", ctx.vars.logo, fatal_html ); + envtab_install( &strbuf_envtab, "logo-alt", ctx.vars.logo_alt, fatal_html ); + envtab_install( &strbuf_envtab, "logo-link", ctx.vars.logo_link, fatal_html ); + envtab_install( &strbuf_envtab, "home-page", ctx.vars.home_page, fatal_html ); + envtab_install( &strbuf_envtab, "snapshots", ctx.vars.snapshots, fatal_html ); + envtab_install( &strbuf_envtab, "status-line", ctx.vars.status_line, fatal_html ); + envtab_install( &strbuf_envtab, "main-menu-logo", ctx.vars.main_menu_logo, fatal_html ); + envtab_install( &strbuf_envtab, "main-menu-item", ctx.vars.main_menu_item, fatal_html ); + envtab_install( &strbuf_envtab, "left-menu-items", ctx.vars.left_menu_items, fatal_html ); + envtab_install( &strbuf_envtab, "popup-menu-items", ctx.vars.popup_menu_items, fatal_html ); + envtab_install( &strbuf_envtab, "right-menu-items", ctx.vars.right_menu_items, fatal_html ); + + envtab_install( &strbuf_envtab, "relative-html", ctx.repo.relative_html, fatal_html ); + envtab_install( &strbuf_envtab, "search-placeholder", ctx.repo.search_placeholder, fatal_html ); + + envtab_install( &strbuf_envtab, "analytic-links", ctx.promo.analytic_links, fatal_html ); + envtab_install( &strbuf_envtab, "analytic-scripts", ctx.promo.analytic_scripts, fatal_html ); + envtab_install( &strbuf_envtab, "donate-css", ctx.promo.donate_css, fatal_html ); + envtab_install( &strbuf_envtab, "donate-html", ctx.promo.donate_html, fatal_html ); + envtab_install( &strbuf_envtab, "donate-js", ctx.promo.donate_js, fatal_html ); + envtab_install( &strbuf_envtab, "donate-header", ctx.promo.donate_header, fatal_html ); + envtab_install( &strbuf_envtab, "donate-purpose", ctx.promo.donate_purpose, fatal_html ); + + envtab_install( &strbuf_envtab, "git-version", ctx.vers.git, fatal_html ); + envtab_install( &strbuf_envtab, "nginx-version", ctx.vers.nginx, fatal_html ); + envtab_install( &strbuf_envtab, "cgit-version", ctx.vers.cgit, fatal_html ); + + envtab_install( &strbuf_envtab, "copyright-notice", ctx.vars.copyright_notice, fatal_html ); + envtab_install( &strbuf_envtab, "copyright", ctx.vars.copyright, fatal_html ); + + envtab_install( &strbuf_envtab, "page-type", ctx.vars.page_type, fatal_html ); + envtab_install( &strbuf_envtab, "page-size", ctx.vars.page_size, fatal_html ); + envtab_install( &strbuf_envtab, "num-of-repos", ctx.vars.num_of_repos, fatal_html ); +} + +void cgit_release_template_variables( void ) +{ + envtab_release( &strbuf_envtab ); +} + +static void get_selfdir( void ) +{ + char path[PATH_MAX]; + ssize_t len; + + bzero( (void *)path, PATH_MAX ); + + len = readlink( "/proc/self/exe", &path[0], (size_t)PATH_MAX ); + if( len > 0 && len < PATH_MAX ) + { + char *selfdir = NULL; + + selfdir = (char *)__sbrk( (int)len + 1 ); + strcpy( selfdir, (const char *)dirname( (char *)&path[0] ) ); + ctx.env.http_root = selfdir; + } + else + fatal_html( "cannot get selfdir" ); +} + +static size_t read_http_root_raw_file( struct strbuf *sb, const char *fname ) +{ + char path[PATH_MAX]; + int fd = -1; + size_t len = 0; + + if( !sb || !fname || !*fname ) return len; + + bzero( (void *)path, PATH_MAX ); + strcpy( (char *)&path[0], ctx.env.http_root ); + if( *fname != '/' ) + strcat( (char *)&path[0], "/" ); + + strcat( (char *)&path[0], fname ); + + if( (fd = open( (const char *)&path[0], O_RDONLY )) == -1 ) + fatal_html( "Cannot open %s file: %s", fname, strerror( errno ) ); + + len = strbuf_read( sb, fd, 0 ); + close( fd ); + + return len; +} + +static size_t read_http_root_env_file( struct strbuf *sb, const char *fname ) +{ + char path[PATH_MAX]; + FILE *fp = NULL; + size_t len = 0; + + if( !sb || !fname || !*fname ) return len; + + bzero( (void *)path, PATH_MAX ); + strcpy( (char *)&path[0], ctx.env.http_root ); + if( *fname != '/' ) + strcat( (char *)&path[0], "/" ); + + strcat( (char *)&path[0], fname ); + + if( (fp = fopen( (const char *)&path[0], "r" )) == NULL ) + fatal_html( "Cannot open %s file: %s", fname, strerror( errno ) ); + + len = strbuf_env_fread( sb, fp ); + fclose( fp ); + + if( len > STRBUF_MAXLINE ) + fatal_html( "The '%s' file is too large. No more than 8192 bytes is allowed for includes", fname ); + + return len; +} + +static void read_donate_html( void ) +{ + struct strbuf buf = STRBUF_INIT; + char *html = NULL; + + if( !ctx.promo.donate ) return; + if( !ctx.promo.donate_html || !*ctx.promo.donate_html ) return; + + (void)read_http_root_env_file( &buf, ctx.promo.donate_html ); + + html = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)html, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.donate_html = (const char *)html; + strbuf_release( &buf ); +} + + +static char psize[4] = { 0, 0, 0, 0 }; + +static void ctx_page_size_from_global( void ) +{ + struct variable f = { (unsigned char *)"page-size", { 0 }, DT_NUMERICAL }, *var = NULL; + var = lookup_global( lookup_global_section( config ), &f ); + if( var ) + { + if( var->type == DT_NUMERICAL ) + { + if( var->_v.val > 9 && var->_v.val < 201 ) + snprintf( &psize[0], 4, "%d", var->_v.val ); + else + snprintf( &psize[0], 4, "%d", 200 ); + ctx.vars.page_size = &psize[0]; + } + else + { + int size = 0; + sscanf( (const char *)var->_v.vptr, "%d", &size ); + if( size > 9 && size < 201 ) + ctx.vars.page_size = (const char *)var->_v.vptr; + else + { + snprintf( &psize[0], 4, "%d", 200 ); + ctx.vars.page_size = &psize[0]; + } + } + } +} + +static void ctx_page_size_from_repo( struct repo *repo ) +{ + struct variable f = { (unsigned char *)"page-size", { 0 }, DT_NUMERICAL }, *var = NULL; + var = lookup( repo, &f ); + if( var ) + { + if( var->type == DT_NUMERICAL ) + { + if( var->_v.val > 9 && var->_v.val < 201 ) + snprintf( &psize[0], 4, "%d", var->_v.val ); + else + snprintf( &psize[0], 4, "%d", 200 ); + ctx.vars.page_size = &psize[0]; + } + else + { + int size = 0; + sscanf( (const char *)var->_v.vptr, "%d", &size ); + if( size > 9 && size < 201 ) + ctx.vars.page_size = (const char *)var->_v.vptr; + else + { + snprintf( &psize[0], 4, "%d", 200 ); + ctx.vars.page_size = &psize[0]; + } + } + } +} + +static void ctx_repo_dirs_from_global( void ) +{ + struct variable git_root = { (unsigned char *)"git-root", { 0 }, DT_PATH }; + struct variable repo_root = { (unsigned char *)"repo-root", { 0 }, DT_PATH }; + struct variable trunk = { (unsigned char *)"trunk", { 0 }, DT_PATH }; + struct variable ro_prefix = { (unsigned char *)"clone-prefix-readonly", { 0 }, DT_PATH }; + struct variable rw_prefix = { (unsigned char *)"clone-prefix", { 0 }, DT_PATH }; + struct variable *var = NULL; + + var = lookup_global( lookup_global_section( config ), &git_root ); + if( var ) + { + ctx.repo.git_root = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &repo_root ); + if( var ) + { + ctx.repo.repo_root = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &trunk ); + if( var ) + { + ctx.repo.trunk = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &rw_prefix ); + if( var ) + { + ctx.repo.clone_prefix = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &ro_prefix ); + if( var ) + { + ctx.repo.clone_ro_prefix = (const char *)var->_v.vptr; + } +} + +static void ctx_site_vars_from_global( void ) +{ + struct variable css = { (unsigned char *)"css", { 0 }, DT_PATH }; + struct variable owner = { (unsigned char *)"owner", { 0 }, DT_STRING }; + struct variable author = { (unsigned char *)"author", { 0 }, DT_STRING }; + struct variable title = { (unsigned char *)"title", { 0 }, DT_STRING }; + struct variable description = { (unsigned char *)"description", { 0 }, DT_STRING }; + struct variable keywords = { (unsigned char *)"keywords", { 0 }, DT_STRING }; + struct variable copyright_notice = { (unsigned char *)"copyright-notice", { 0 }, DT_STRING }; + struct variable copyright = { (unsigned char *)"copyright", { 0 }, DT_STRING }; + struct variable favicon_path = { (unsigned char *)"favicon-path", { 0 }, DT_PATH }; + struct variable logo = { (unsigned char *)"logo", { 0 }, DT_PATH }; + struct variable logo_alt = { (unsigned char *)"logo-alt", { 0 }, DT_STRING }; + struct variable logo_link = { (unsigned char *)"logo-link", { 0 }, DT_STRING }; + struct variable home_page = { (unsigned char *)"home-page", { 0 }, DT_STRING }; + struct variable snapshots = { (unsigned char *)"snapshots", { 0 }, DT_PATH }; + struct variable main_menu_logo = { (unsigned char *)"main-menu-logo", { 0 }, DT_PATH }; + struct variable syntax_highlight_css = { (unsigned char *)"syntax-highlight-css", { 0 }, DT_PATH }; + struct variable *var = NULL; + + var = lookup_global( lookup_global_section( config ), &css ); + if( var ) + { + ctx.vars.css = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &owner ); + if( var ) + { + ctx.vars.owner = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &author ); + if( var ) + { + ctx.vars.author = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &title ); + if( var ) + { + ctx.vars.title = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &description ); + if( var ) + { + ctx.vars.description = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &keywords ); + if( var ) + { + ctx.vars.keywords = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), ©right_notice ); + if( var ) + { + ctx.vars.copyright_notice = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), ©right ); + if( var ) + { + ctx.vars.copyright = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &favicon_path ); + if( var ) + { + ctx.vars.favicon_path = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &logo ); + if( var ) + { + ctx.vars.logo = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &logo_alt ); + if( var ) + { + ctx.vars.logo_alt = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &logo_link ); + if( var ) + { + ctx.vars.logo_link = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &home_page ); + if( var ) + { + ctx.vars.home_page = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &snapshots ); + if( var ) + { + ctx.vars.snapshots = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &main_menu_logo ); + if( var ) + { + ctx.vars.main_menu_logo = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &syntax_highlight_css ); + if( var ) + { + ctx.vars.syntax_highlight_css = (const char *)var->_v.vptr; + } +} + +static void ctx_promo_vars_from_global( void ) +{ + struct variable analytic_links = { (unsigned char *)"analytic-links", { 0 }, DT_PATH }; + struct variable analytic_scripts = { (unsigned char *)"analytic-scripts", { 0 }, DT_PATH }; + struct variable donate = { (unsigned char *)"donate", { 0 }, DT_NUMERICAL }; + struct variable donate_css = { (unsigned char *)"donate-css", { 0 }, DT_PATH }; + struct variable donate_html = { (unsigned char *)"donate-html", { 0 }, DT_PATH }; + struct variable donate_js = { (unsigned char *)"donate-js", { 0 }, DT_PATH }; + struct variable donate_header = { (unsigned char *)"donate-header", { 0 }, DT_STRING }; + struct variable donate_purpose = { (unsigned char *)"donate-purpose", { 0 }, DT_STRING }; + struct variable *var = NULL; + + var = lookup_global( lookup_global_section( config ), &analytic_links ); + if( var ) + { + ctx.promo.analytic_links = (const char *)var->_v.vptr; + + if( ctx.promo.analytic_links && *ctx.promo.analytic_links ) + { + struct strbuf buf = STRBUF_INIT; + char *links = NULL; + + (void)read_http_root_raw_file( &buf, ctx.promo.analytic_links ); + + links = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)links, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.analytic_links = (const char *)links; + strbuf_release( &buf ); + } + } + var = lookup_global( lookup_global_section( config ), &analytic_scripts ); + if( var ) + { + ctx.promo.analytic_scripts = (const char *)var->_v.vptr; + + if( ctx.promo.analytic_scripts && *ctx.promo.analytic_scripts ) + { + struct strbuf buf = STRBUF_INIT; + char *scripts = NULL; + + (void)read_http_root_raw_file( &buf, ctx.promo.analytic_scripts ); + + scripts = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)scripts, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.analytic_scripts = (const char *)scripts; + strbuf_release( &buf ); + } + } + + var = lookup_global( lookup_global_section( config ), &donate ); + if( var ) + { + if( var->type == DT_NUMERICAL ) + { + ctx.promo.donate = var->_v.val; + if( ctx.promo.donate ) + ctx.promo.donate = 1; + } + } + + if( ctx.promo.donate ) + { + var = lookup_global( lookup_global_section( config ), &donate_css ); + if( var ) + { + ctx.promo.donate_css = (const char *)var->_v.vptr; + + if( ctx.promo.donate_css && *ctx.promo.donate_css ) + { + struct strbuf buf = STRBUF_INIT; + char *css = NULL; + + strbuf_addf( &buf, "<link rel=\"stylesheet\" href=\"%s\">", ctx.promo.donate_css ); + strbuf_trim( &buf ); + css = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)css, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.donate_css = (const char *)css; + strbuf_release( &buf ); + } + } + var = lookup_global( lookup_global_section( config ), &donate_js ); + if( var ) + { + ctx.promo.donate_js = (const char *)var->_v.vptr; + + if( ctx.promo.donate_js && *ctx.promo.donate_js ) + { + struct strbuf buf = STRBUF_INIT; + char *js = NULL; + + strbuf_addf( &buf, "<script src=\"%s\"></script>", ctx.promo.donate_js ); + strbuf_trim( &buf ); + js = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)js, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.donate_js = (const char *)js; + strbuf_release( &buf ); + } + } + var = lookup_global( lookup_global_section( config ), &donate_header ); + if( var ) + { + ctx.promo.donate_header = (const char *)var->_v.vptr; + } + var = lookup_global( lookup_global_section( config ), &donate_purpose ); + if( var ) + { + ctx.promo.donate_purpose = (const char *)var->_v.vptr; + } + + var = lookup_global( lookup_global_section( config ), &donate_html ); + if( var ) + { + ctx.promo.donate_html = (const char *)var->_v.vptr; + + if( ctx.promo.donate_html && *ctx.promo.donate_html ) + { + envtab_install( &strbuf_envtab, "donate-header", ctx.promo.donate_header, fatal_html ); + envtab_install( &strbuf_envtab, "donate-purpose", ctx.promo.donate_purpose, fatal_html ); + read_donate_html(); + envtab_release( &strbuf_envtab ); + } + } + } +} + +static void ctx_repo_dirs_from_repo( struct repo *repo ) +{ + struct variable git_root = { (unsigned char *)"git-root", { 0 }, DT_PATH }; + struct variable repo_root = { (unsigned char *)"repo-root", { 0 }, DT_PATH }; + struct variable trunk = { (unsigned char *)"trunk", { 0 }, DT_PATH }; + struct variable ro_prefix = { (unsigned char *)"clone-prefix-readonly", { 0 }, DT_PATH }; + struct variable rw_prefix = { (unsigned char *)"clone-prefix", { 0 }, DT_PATH }; + struct variable *var = NULL; + + if( !repo ) return; + + var = lookup( repo, &git_root ); + if( var ) + { + ctx.repo.git_root = (const char *)var->_v.vptr; + } + var = lookup( repo, &repo_root ); + if( var ) + { + ctx.repo.repo_root = (const char *)var->_v.vptr; + } + var = lookup( repo, &trunk ); + if( var ) + { + ctx.repo.trunk = (const char *)var->_v.vptr; + } + var = lookup( repo, &rw_prefix ); + if( var ) + { + ctx.repo.clone_prefix = (const char *)var->_v.vptr; + } + var = lookup( repo, &ro_prefix ); + if( var ) + { + ctx.repo.clone_ro_prefix = (const char *)var->_v.vptr; + } +} + +static void ctx_site_vars_from_repo( struct repo *repo ) +{ + struct variable css = { (unsigned char *)"css", { 0 }, DT_PATH }; + struct variable owner = { (unsigned char *)"owner", { 0 }, DT_STRING }; + struct variable author = { (unsigned char *)"author", { 0 }, DT_STRING }; + struct variable title = { (unsigned char *)"title", { 0 }, DT_STRING }; + struct variable description = { (unsigned char *)"description", { 0 }, DT_STRING }; + struct variable keywords = { (unsigned char *)"keywords", { 0 }, DT_STRING }; + struct variable copyright_notice = { (unsigned char *)"copyright-notice", { 0 }, DT_STRING }; + struct variable copyright = { (unsigned char *)"copyright", { 0 }, DT_STRING }; + struct variable favicon_path = { (unsigned char *)"favicon-path", { 0 }, DT_PATH }; + struct variable logo = { (unsigned char *)"logo", { 0 }, DT_PATH }; + struct variable logo_alt = { (unsigned char *)"logo-alt", { 0 }, DT_STRING }; + struct variable logo_link = { (unsigned char *)"logo-link", { 0 }, DT_STRING }; + struct variable home_page = { (unsigned char *)"home-page", { 0 }, DT_STRING }; + struct variable snapshots = { (unsigned char *)"snapshots", { 0 }, DT_PATH }; + struct variable main_menu_logo = { (unsigned char *)"main-menu-logo", { 0 }, DT_PATH }; + struct variable syntax_highlight_css = { (unsigned char *)"syntax-highlight-css", { 0 }, DT_PATH }; + struct variable *var = NULL; + + if( !repo ) return; + + var = lookup( repo, &css ); + if( var ) + { + ctx.vars.css = (const char *)var->_v.vptr; + } + var = lookup( repo, &owner ); + if( var ) + { + ctx.vars.owner = (const char *)var->_v.vptr; + } + var = lookup( repo, &author ); + if( var ) + { + ctx.vars.author = (const char *)var->_v.vptr; + } + var = lookup( repo, &title ); + if( var ) + { + ctx.vars.title = (const char *)var->_v.vptr; + } + var = lookup( repo, &description ); + if( var ) + { + ctx.vars.description = (const char *)var->_v.vptr; + } + var = lookup( repo, &keywords ); + if( var ) + { + ctx.vars.keywords = (const char *)var->_v.vptr; + } + var = lookup( repo, ©right_notice ); + if( var ) + { + ctx.vars.copyright_notice = (const char *)var->_v.vptr; + } + var = lookup( repo, ©right ); + if( var ) + { + ctx.vars.copyright = (const char *)var->_v.vptr; + } + var = lookup( repo, &favicon_path ); + if( var ) + { + ctx.vars.favicon_path = (const char *)var->_v.vptr; + } + var = lookup( repo, &logo ); + if( var ) + { + ctx.vars.logo = (const char *)var->_v.vptr; + } + var = lookup( repo, &logo_alt ); + if( var ) + { + ctx.vars.logo_alt = (const char *)var->_v.vptr; + } + var = lookup( repo, &logo_link ); + if( var ) + { + ctx.vars.logo_link = (const char *)var->_v.vptr; + } + var = lookup( repo, &home_page ); + if( var ) + { + ctx.vars.home_page = (const char *)var->_v.vptr; + } + var = lookup( repo, &snapshots ); + if( var ) + { + ctx.vars.snapshots = (const char *)var->_v.vptr; + } + var = lookup( repo, &main_menu_logo ); + if( var ) + { + ctx.vars.main_menu_logo = (const char *)var->_v.vptr; + } + var = lookup( repo, &syntax_highlight_css ); + if( var ) + { + ctx.vars.syntax_highlight_css = (const char *)var->_v.vptr; + } +} + +static void ctx_promo_vars_from_repo( struct repo *repo ) +{ + struct variable analytic_links = { (unsigned char *)"analytic-links", { 0 }, DT_PATH }; + struct variable analytic_scripts = { (unsigned char *)"analytic-scripts", { 0 }, DT_PATH }; + struct variable donate = { (unsigned char *)"donate", { 0 }, DT_NUMERICAL }; + struct variable donate_css = { (unsigned char *)"donate-css", { 0 }, DT_PATH }; + struct variable donate_html = { (unsigned char *)"donate-html", { 0 }, DT_PATH }; + struct variable donate_js = { (unsigned char *)"donate-js", { 0 }, DT_PATH }; + struct variable donate_header = { (unsigned char *)"donate-header", { 0 }, DT_STRING }; + struct variable donate_purpose = { (unsigned char *)"donate-purpose", { 0 }, DT_STRING }; + struct variable *var = NULL; + + /* + Analytics reads from global section only: + */ + var = lookup_global( lookup_global_section( config ), &analytic_links ); + if( var ) + { + ctx.promo.analytic_links = (const char *)var->_v.vptr; + + if( ctx.promo.analytic_links && *ctx.promo.analytic_links ) + { + struct strbuf buf = STRBUF_INIT; + char *links = NULL; + + (void)read_http_root_raw_file( &buf, ctx.promo.analytic_links ); + + links = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)links, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.analytic_links = (const char *)links; + strbuf_release( &buf ); + } + } + var = lookup_global( lookup_global_section( config ), &analytic_scripts ); + if( var ) + { + ctx.promo.analytic_scripts = (const char *)var->_v.vptr; + + if( ctx.promo.analytic_scripts && *ctx.promo.analytic_scripts ) + { + struct strbuf buf = STRBUF_INIT; + char *scripts = NULL; + + (void)read_http_root_raw_file( &buf, ctx.promo.analytic_scripts ); + + scripts = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)scripts, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.analytic_scripts = (const char *)scripts; + strbuf_release( &buf ); + } + } + + var = lookup( repo, &donate ); + if( var ) + { + if( var->type == DT_NUMERICAL ) + { + ctx.promo.donate = var->_v.val; + if( ctx.promo.donate ) + ctx.promo.donate = 1; + } + } + + if( ctx.promo.donate ) + { + var = lookup( repo, &donate_css ); + if( var ) + { + ctx.promo.donate_css = (const char *)var->_v.vptr; + + if( ctx.promo.donate_css && *ctx.promo.donate_css ) + { + struct strbuf buf = STRBUF_INIT; + char *css = NULL; + + strbuf_addf( &buf, "<link rel=\"stylesheet\" href=\"%s\">", ctx.promo.donate_css ); + strbuf_trim( &buf ); + css = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)css, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.donate_css = (const char *)css; + strbuf_release( &buf ); + } + } + var = lookup( repo, &donate_js ); + if( var ) + { + ctx.promo.donate_js = (const char *)var->_v.vptr; + + if( ctx.promo.donate_js && *ctx.promo.donate_js ) + { + struct strbuf buf = STRBUF_INIT; + char *js = NULL; + + strbuf_addf( &buf, "<script src=\"%s\"></script>", ctx.promo.donate_js ); + strbuf_trim( &buf ); + js = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)js, (const void *)buf.buf, buf.len + 1 ); + ctx.promo.donate_js = (const char *)js; + strbuf_release( &buf ); + } + } + var = lookup( repo, &donate_header ); + if( var ) + { + ctx.promo.donate_header = (const char *)var->_v.vptr; + } + var = lookup( repo, &donate_purpose ); + if( var ) + { + ctx.promo.donate_purpose = (const char *)var->_v.vptr; + } + var = lookup( repo, &donate_html ); + if( var ) + { + ctx.promo.donate_html = (const char *)var->_v.vptr; + + if( ctx.promo.donate_html && *ctx.promo.donate_html ) + { + envtab_install( &strbuf_envtab, "donate-header", ctx.promo.donate_header, fatal_html ); + envtab_install( &strbuf_envtab, "donate-purpose", ctx.promo.donate_purpose, fatal_html ); + read_donate_html(); + envtab_release( &strbuf_envtab ); + } + } + } +} + + + +static char nrepos[11] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +static void ctx_num_of_repos( void ) +{ + int32_t num = repolist_length( config ); + num &= 0x7fffffff; + if( num > 0 ) + { + snprintf( &nrepos[0], 11, "%d", num ); + ctx.vars.num_of_repos = &nrepos[0]; + } +} + +static void ctx_repolist_status_line( void ) +{ + struct strbuf buf = STRBUF_INIT; + char *nrepos_string = NULL; + char *status_line = NULL; + int32_t num = 0; + + sscanf( ctx.vars.num_of_repos, "%d", &num ); + + strbuf_addf( &buf, Q_("%d Git Repository", "%d Git Repositories", num), num ); + nrepos_string = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)nrepos_string, (const void *)buf.buf, buf.len + 1 ); + strbuf_release( &buf ); + + buf.alloc = 0, buf.len = 0, buf.fatal = strbuf_fatal, buf.buf = strbuf_slopbuf; + + strbuf_addf( &buf, "<span class=\"las la-cloud-upload\"></span> %s\n", nrepos_string ); + if( ctx.promo.donate ) + strbuf_addf( &buf, " | <a class=\"donate\"><span class=\"icon las la-credit-card\"></span> Donate</a>\n" ); + + status_line = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)status_line, (const void *)buf.buf, buf.len + 1 ); + strbuf_release( &buf ); + + ctx.vars.status_line = (const char *)status_line; +} + +static void ctx_repo_status_line( struct cgit_repository *rctx ) +{ + struct strbuf buf = STRBUF_INIT; + struct strbuf branches = STRBUF_INIT; + struct strbuf commits = STRBUF_INIT; + struct strbuf tags = STRBUF_INIT; + char *status_line = NULL; + + if( !rctx ) return; + + strbuf_addf( &commits, Q_("%d Commit", "%d Commits", rctx->ncommits), rctx->ncommits ); + strbuf_addf( &branches, Q_("%d Branch", "%d Branches", rctx->nbranches), rctx->nbranches ); + strbuf_addf( &tags, Q_("%d Tag", "%d Tags", rctx->ntags), rctx->ntags ); + + if( rctx->ncommits == 9999 ) + strbuf_addf( &buf, "<span class=\"las la-code-commit\"></span> More than %s \n", commits.buf ); + else + strbuf_addf( &buf, "<span class=\"las la-code-commit\"></span> %s \n", commits.buf ); + strbuf_addf( &buf, "<span class=\"las la-code-branch\"></span> %s \n", branches.buf ); + strbuf_addf( &buf, "<span class=\"las la-tags\"></span> %s\n", tags.buf ); + if( ctx.promo.donate ) + strbuf_addf( &buf, " | <a class=\"donate\"><span class=\"icon las la-credit-card\"></span> Donate</a>\n" ); + + strbuf_release( &branches ); + strbuf_release( &commits ); + strbuf_release( &tags ); + + status_line = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)status_line, (const void *)buf.buf, buf.len + 1 ); + strbuf_release( &buf ); + + ctx.vars.status_line = (const char *)status_line; +} + +static void ctx_repolist_menu( void ) +{ + struct strbuf mmenu = STRBUF_INIT; + struct strbuf popup = STRBUF_INIT; + struct strbuf right = STRBUF_INIT; + + char *main_menu = NULL; + char *popup_menu = NULL; + char *right_menu = NULL; + + strbuf_addf( &mmenu, "<a href=\"/\">Index</a>\n" ); + strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-home\"></span><a href=\"%s\" target=\"_blank\">Home page</a></div>\n", ctx.vars.home_page ); + strbuf_addf( &right, "<div class=\"item\"><a href=\"%s\" target=\"_blank\">Home page</a></div>\n", ctx.vars.home_page ); + + main_menu = (char *)__sbrk( (int)mmenu.len + 1 ); + memcpy( (void *)main_menu, (const void *)mmenu.buf, mmenu.len + 1 ); + strbuf_release( &mmenu ); + ctx.vars.main_menu_item = (const char *)main_menu; + + popup_menu = (char *)__sbrk( (int)popup.len + 1 ); + memcpy( (void *)popup_menu, (const void *)popup.buf, popup.len + 1 ); + strbuf_release( &popup ); + ctx.vars.popup_menu_items = (const char *)popup_menu; + + right_menu = (char *)__sbrk( (int)right.len + 1 ); + memcpy( (void *)right_menu, (const void *)right.buf, right.len + 1 ); + strbuf_release( &right ); + ctx.vars.right_menu_items = (const char *)right_menu; +} + +static void ctx_repo_menu( struct cgit_repository *rctx ) +{ + struct strbuf mmenu = STRBUF_INIT; + struct strbuf left = STRBUF_INIT; + struct strbuf popup = STRBUF_INIT; + struct strbuf right = STRBUF_INIT; + + char *main_menu = NULL; + char *left_menu = NULL; + char *popup_menu = NULL; + char *right_menu = NULL; + + if( !rctx ) return; + + strbuf_addf( &mmenu, "<a href=\"/\">Index</a>\n" ); + + strbuf_addf( &left, "<div class=\"item\"><a href=\"/%s/%s/\">Trunk</a></div>\n", rctx->name, "trunk" ); + strbuf_addf( &left, "<div class=\"item\"><a href=\"/%s/%s/\">Branches</a></div>\n", rctx->name, "branches" ); + strbuf_addf( &left, "<div class=\"item\"><a href=\"/%s/%s/\">Tags</a></div>\n", rctx->name, "tags" ); + + strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-road\"></span><a href=\"/%s/%s/\">Trunk</a></div>\n", rctx->name, "trunk" ); + strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-code-branch\"></span><a href=\"/%s/%s/\">Branches</a></div>\n", rctx->name, "branches" ); + strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-tags\"></span><a href=\"/%s/%s/\">Tags</a></div>\n", rctx->name, "tags" ); + strbuf_addf( &popup, "<div class=\"divider\"></div>\n" ); + strbuf_addf( &popup, "<div class=\"item\"><span class=\"icon las la-home\"></span><a href=\"%s\" target=\"_blank\">Home page</a></div>\n", ctx.vars.home_page ); + + strbuf_addf( &right, "<div class=\"item\"><a href=\"%s\" target=\"_blank\">Home page</a></div>\n", ctx.vars.home_page ); + + main_menu = (char *)__sbrk( (int)mmenu.len + 1 ); + memcpy( (void *)main_menu, (const void *)mmenu.buf, mmenu.len + 1 ); + strbuf_release( &mmenu ); + ctx.vars.main_menu_item = (const char *)main_menu; + + left_menu = (char *)__sbrk( (int)left.len + 1 ); + memcpy( (void *)left_menu, (const void *)left.buf, left.len + 1 ); + strbuf_release( &left ); + ctx.vars.left_menu_items = (const char *)left_menu; + + popup_menu = (char *)__sbrk( (int)popup.len + 1 ); + memcpy( (void *)popup_menu, (const void *)popup.buf, popup.len + 1 ); + strbuf_release( &popup ); + ctx.vars.popup_menu_items = (const char *)popup_menu; + + right_menu = (char *)__sbrk( (int)right.len + 1 ); + memcpy( (void *)right_menu, (const void *)right.buf, right.len + 1 ); + strbuf_release( &right ); + ctx.vars.right_menu_items = (const char *)right_menu; +} + + +static void ctx_header_from_global( void ) +{ + struct strbuf buf = STRBUF_INIT; + struct variable head = { (unsigned char *)"header", { 0 }, DT_PATH }, *var = NULL; + char *pheader = NULL; + + var = lookup_global( lookup_global_section( config ), &head ); + strbuf_selfdir( &buf ); + + if( var ) { strbuf_addf( &buf, "%s", (const char *)var->_v.vptr ); } + else { strbuf_addf( &buf, "%s", ctx.page.header ); } + + pheader = (char *)__sbrk( (int)buf.len + 1 ); + sprintf( pheader, "%s", buf.buf ); + strbuf_release( &buf ); + ctx.page.header = (const char *)pheader; +} + +static void ctx_header_from_repo( struct repo *repo ) +{ + struct strbuf buf = STRBUF_INIT; + struct variable head = { (unsigned char *)"header", { 0 }, DT_PATH }, *var = NULL; + char *pheader = NULL; + + var = lookup( repo, &head ); + strbuf_selfdir( &buf ); + + if( var ) { strbuf_addf( &buf, "%s", (const char *)var->_v.vptr ); } + else { strbuf_addf( &buf, "%s", ctx.page.header ); } + + pheader = (char *)__sbrk( (int)buf.len + 1 ); + sprintf( pheader, "%s", buf.buf ); + strbuf_release( &buf ); + ctx.page.header = (const char *)pheader; +} + + +static void ctx_footer_from_global( void ) +{ + struct strbuf buf = STRBUF_INIT; + struct variable foot = { (unsigned char *)"footer", { 0 }, DT_PATH }, *var = NULL; + char *pfooter = NULL; + + var = lookup_global( lookup_global_section( config ), &foot ); + strbuf_selfdir( &buf ); + + if( var ) { strbuf_addf( &buf, "%s", (const char *)var->_v.vptr ); } + else { strbuf_addf( &buf, "%s", ctx.page.footer ); } + + pfooter = (char *)__sbrk( (int)buf.len + 1 ); + sprintf( pfooter, "%s", buf.buf ); + strbuf_release( &buf ); + ctx.page.footer = (const char *)pfooter; +} + +static void ctx_footer_from_repo( struct repo *repo ) +{ + struct strbuf buf = STRBUF_INIT; + struct variable foot = { (unsigned char *)"footer", { 0 }, DT_PATH }, *var = NULL; + char *pfooter = NULL; + + var = lookup( repo, &foot ); + strbuf_selfdir( &buf ); + + if( var ) { strbuf_addf( &buf, "%s", (const char *)var->_v.vptr ); } + else { strbuf_addf( &buf, "%s", ctx.page.footer ); } + + pfooter = (char *)__sbrk( (int)buf.len + 1 ); + sprintf( pfooter, "%s", buf.buf ); + strbuf_release( &buf ); + ctx.page.footer = (const char *)pfooter; +} + + +static char name[PATH_MAX] = { 0 }; +static char repo_root[PATH_MAX] = { 0 }; +static char relative_path[PATH_MAX] = { 0 }; + +static char try[PATH_MAX] = { 0 }; + +static struct repo *ctx_repo_name( void ) +{ + struct repo *repo = NULL; + char *s, *p = NULL, *n, *path, *path_info; + int len = 0; + + if( strcmp( "/", ctx.env.path_info ) ) + { + path_info = xstrdup( ctx.env.path_info ); + s = path_info; + + while( *s ) + { + while( *s && is_dir_sep( *s ) ) + ++s; + n = p = s; + + while( *p && !is_dir_sep( *p ) ) + ++p; + if( *p ) + { + *p = '\0'; s = ++p; + + /**************************************************************** + The repo name can be given as a relative path in the git-root: + */ + if( repo_root[0] ) + { + sprintf( try, "%s", repo_root ); + strcat( try, "/" ); + strcat( try, n ); + } + else + { + sprintf( try, "%s", n ); + } + + if( (repo = lookup_repo( config, try )) ) + { + sprintf( name, "%s", try ); + { + char *rr = strstr( repo_root, try ); + if( rr ) + *rr = '\0'; + else + repo_root[0] = '\0'; + } + break; + } + else + { + if( repo_root[0] ) + strcat( repo_root, "/" ); + strcat( repo_root, n ); + } + } + else + s = p; + } + + ctx.repo.name = (const char *)&name[0]; + ctx.repo.repo_root = (const char *)&repo_root[0]; + + path = p; + + if( *path ) + { + len = (int)strlen( path ); + + if( path[len-1] =='/' ) { path[len-1] = '\0'; --len; } + len += 1; + + sprintf( relative_path, "%s", path ); + ctx.repo.relative_path = (const char *)&relative_path[0]; + } + + free( path_info ); + } + + return repo; +} + + +/************************************************************* + Get integer value of query parameter 'name' and remove this + parameter 'name=..' from ctx.env.query_string. + */ +int ctx_grab_int_query_param( const char *name ) +{ + char *pofs = NULL, *query_string, *s = (char *)ctx.env.query_string; + int ret = 0, len = 0; + + if( !name || !*name ) return ret; + + if( s && *s && (pofs = strstr( s, name )) ) + { + char *rem, *p, *val = NULL; + struct strbuf buf = STRBUF_INIT; + + p = pofs; + + /* move to end of 'ofs=val' declaration: */ + while( *p && *p != '&' && *p != '=' ) ++p; + if( *p == '=' ) { val = ++p; while( *p && *p != '&' ) ++p; } + if( *p ) { *p = '\0'; rem = ++p; } + else + { + rem = p; + if( s < pofs && (pofs[-1] == '&' || pofs[-1] == '=') ) --pofs; + } + + /* fill buffer: */ + while( s < pofs ) { strbuf_addch( &buf, *s ); ++s; } + s = rem; + while( *s ) { strbuf_addch( &buf, *s ); ++s; } + strbuf_addch( &buf, '\0' ); + + /* get value: */ + if( val && *val ) { ret = atoi( val ); } + else { ret = 0; } + + len = (int)buf.len; + query_string = (char *)__sbrk( len ); + memcpy( (void *)query_string, (const void *)buf.buf, (size_t)len ); + strbuf_release( &buf ); + ctx.env.query_string = (const char *)query_string; + } + + return ret; +} + +/************************************************************* + Get string value of query parameter 'name' and remove this + parameter 'name=..' from ctx.env.query_string. + */ +const char *ctx_grab_str_query_param( const char *name ) +{ + const char *ret = NULL; + char *pofs = NULL, *query_string, *s = (char *)ctx.env.query_string; + int len = 0; + + if( !name || !*name ) return ret; + + if( s && *s && (pofs = strstr( s, name )) ) + { + char *rem, *p, *val = NULL; + struct strbuf buf = STRBUF_INIT; + + p = pofs; + + /* move to end of 'ofs=val' declaration: */ + while( *p && *p != '&' && *p != '=' ) ++p; + if( *p == '=' ) { val = ++p; while( *p && *p != '&' ) ++p; } + if( *p ) { *p = '\0'; rem = ++p; } + else + { + rem = p; + if( s < pofs && (pofs[-1] == '&' || pofs[-1] == '=') ) --pofs; + } + + /* fill buffer: */ + while( s < pofs ) { strbuf_addch( &buf, *s ); ++s; } + s = rem; + while( *s ) { strbuf_addch( &buf, *s ); ++s; } + strbuf_addch( &buf, '\0' ); + + /* get value: */ + if( val && *val ) + { + char *value = NULL; + + len = (int)strlen( val ) + 1; + value = (char *)__sbrk( len ); + memcpy( (void *)value, (const void *)val, (size_t)len ); + + ret = (const char *)value; + } + + len = (int)buf.len; + query_string = (char *)__sbrk( len ); + memcpy( (void *)query_string, (const void *)buf.buf, (size_t)len ); + strbuf_release( &buf ); + ctx.env.query_string = (const char *)query_string; + } + + return ret; +} + +/*************************************************************** + Rmove parametr with specified 'name=...' fom query string and + return modified query string allocated in context _mem[]. + */ +const char *ctx_remove_query_param( const char *query_string, const char *name ) +{ + char *pofs = NULL, *query = NULL, *s = (char *)query_string; + int len = 0; + + if( !name || !*name ) return (const char *)query; + + if( s && *s && (pofs = strstr( s, name )) ) + { + char *rem, *p; + struct strbuf buf = STRBUF_INIT; + + p = pofs; + + /* move to end of 'ofs=val' declaration: */ + while( *p && *p != '&' && *p != '=' ) ++p; + if( *p == '=' ) { while( *p && *p != '&' ) ++p; } + if( *p ) { *p = '\0'; rem = ++p; } + else + { + rem = p; + if( s < pofs && (pofs[-1] == '&' || pofs[-1] == '=') ) --pofs; + } + + /* fill buffer: */ + while( s < pofs ) { strbuf_addch( &buf, *s ); ++s; } + s = rem; + while( *s ) { strbuf_addch( &buf, *s ); ++s; } + strbuf_addch( &buf, '\0' ); + + len = (int)buf.len; + query = (char *)__sbrk( len ); + memcpy( (void *)query, (const void *)buf.buf, (size_t)len ); + strbuf_release( &buf ); + return (const char *)query; + } + + return (const char *)query; +} + + +static void ctx_search_placeholder( void ) +{ + struct strbuf buf = STRBUF_INIT; + char *revision; + int len; + + if( !strcmp( ctx.vars.page_type, ptype_repo ) ) + { + if( ctx.query.rev && strcmp( ctx.query.rev, "0" ) ) + strbuf_addf( &buf, "%0.12s...", ctx.query.rev ); + else + strbuf_addf( &buf, "%0.12s...", ctx.repo.info.revision ); + + len = (int)strlen( buf.buf ) + 1; + revision = (char *)__sbrk( len ); + memcpy( (void *)revision, (const void *)buf.buf, (size_t)len ); + strbuf_release( &buf ); + + ctx.repo.search_placeholder = (const char *)revision; + /* ctx.repo.search_placeholder = "HEAD"; */ + } +} + +static void ctx_relative_html( void ) +{ + struct strbuf buf = STRBUF_INIT; + char *relative_html; + int len; + + if( !strcmp( ctx.vars.page_type, ptype_repolist ) ) + { + if( ctx.env.http_host && *ctx.env.http_host ) + { + strbuf_addf( &buf, "<a class='base' href='/'>%s</a>/", ctx.env.http_host ); + } + else if( ctx.env.server_name && *ctx.env.server_name ) + { + strbuf_addf( &buf, "<a class='base' href='/'>%s</a>/", ctx.env.server_name ); + } + } + else if( !strcmp( ctx.vars.page_type, ptype_repo ) ) + { + if( ctx.repo.name && *ctx.repo.name ) + { + if( ctx.repo.repo_root && *ctx.repo.repo_root ) + strbuf_addf( &buf, "<a class='base' href='/%s/%s/'>%s/%s</a>/", ctx.repo.repo_root, ctx.repo.name, ctx.repo.repo_root, ctx.repo.name ); + else + strbuf_addf( &buf, "<a class='base' href='/%s/'>%s</a>/", ctx.repo.name, ctx.repo.name ); + } + if( ctx.repo.relative_path && *ctx.repo.relative_path ) + { + char *p, *s, *rem; + int msize; + char *path, *href; + size_t length; + + length = strlen( ctx.repo.relative_path ) + 1; + msize = (int)strlen(ctx.repo.name) + (int)strlen(ctx.repo.relative_path) + 4; /* '/' repo.name '/' relative_path '/' + '\0' */ + + href = (char *)__sbrk( msize ); + sprintf( href, "/%s/", ctx.repo.name ); + + path = (char *)__sbrk( msize ); + memcpy( (void *)path, (const void *)ctx.repo.relative_path, length ); + + p = s = path; + while( *p && !is_dir_sep( *p ) ) ++p; + if( *p ) { *p = '\0'; rem = ++p; } + else { rem = p; } + strcat( href, s ); strcat( href, "/" ); + + if( !strcmp( s, "trunk" ) || + !strcmp( s, "branches" ) || + !strcmp( s, "tags" ) ) + { + strbuf_addf( &buf, "<a class='base' href='%s'>%s</a>/", href, s ); + + /********************************************** + Searching the real name of branch or tag: + */ + if( !strcmp( s, "branches" ) || !strcmp( s, "tags" ) ) + { + char reminder[PATH_MAX] = { 0 }; + char *n, *r = (char *)&reminder[0]; + + sprintf( reminder, "%s", rem ); + n = r; + + while( *r && !is_dir_sep( *r ) ) + ++r; + if( *r ) + { + *r = '\0'; ++r; + } + + if( n && *n ) + { + int found = 0; + char ref[PATH_MAX] = { 0 }; + char *b; + + struct cgit_ref_names *names = NULL; + + sprintf( ref, "%s", n ); + + if( !strcmp( s, "branches" ) ) + lookup_branches_by_prefix( &names, n ); + else + lookup_tags_by_prefix( &names, n ); + + while( *r ) + { + b = r; + while( *r && *r != '/' ) + ++r; + if( *r ) + { + *r = '\0'; ++r; + } + + if( b && *b ) + { + char probe[PATH_MAX] = { 0 }; + sprintf( probe, "%s/%s", ref, b ); + + found = 0; + { + size_t i, len = strlen( probe ); + for( i = 0; i < names->len; ++i ) + { + if( !strncmp( probe, names->name[i], len ) ) + { + ++found; + } + } + } + if( found == 1 ) + { + strcat( ref, "/" ); + strcat( ref, b ); + break; + } + if( found ) + { + strcat( ref, "/" ); + strcat( ref, b ); + } + } + } + cgit_ref_names_free( names ); + + if( found ) + { + strcat( href, ref ); strcat( href, "/" ); + strbuf_addf( &buf, "<a class='relative' href='%s'>%s</a>/", href, ref ); + rem += r - &reminder[0]; + } + } + } + /* + End of searching real name of branch or tag. + **********************************************/ + } + else + { + strbuf_addf( &buf, "<a class='relative' href='%s'>%s</a>/", href, s ); + } + + s = rem; + while( *s ) + { + p = s; + while( *p && !is_dir_sep( *p ) ) ++p; + if( *p ) { *p = '\0'; rem = ++p; } + else { rem = p; } + strcat( href, s ); strcat( href, "/" ); + strbuf_addf( &buf, "<a class='relative' href='%s'>%s</a>/", href, s ); + s = rem; + } + + if( ctx.repo.relative_info.kind == GIT_OBJECT_BLOB ) + strbuf_trim_trailing_dir_sep( &buf ); + + ctx.repo.relative_href = (const char *)href; + + (void)__sbrk( - msize ); /* free ctx memory alocated for path only */ + } + } + + len = (int)strlen( buf.buf ) + 1; + relative_html = (char *)__sbrk( len ); + memcpy( (void *)relative_html, (const void *)buf.buf, (size_t)len ); + strbuf_release( &buf ); + ctx.repo.relative_html = (const char *)relative_html; +} + +static void ctx_check_query_uri( int replace_query_string ) +{ + /* + Nginx passed QUERY_STRING for all cgi engines by: {fastcgi, scgi, uwsgi}_params. + But PATH_INFO passed onlyfor UWSGI by uwsgi_params. + */ + if( !ctx.env.path_info && ctx.env.request_uri && *ctx.env.request_uri ) + { + char *path_info = NULL, *query_string = NULL; + char *p, *path, *query = NULL; + int len; + + p = path = (char *)ctx.env.request_uri; + + while( *p && *p != '?' ) ++p; + if( *p == '?' ) { *p = '\0'; query = ++p; } + + len = strlen( path ) + 1; + path_info = (char *)__sbrk( len ); + memcpy( (void *)path_info, (const void *)path, (size_t)len ); + ctx.env.path_info = (const char *)path_info; + + if( query && *query && replace_query_string ) + { + len = strlen( query ) + 1; + query_string = (char *)__sbrk( len ); + memcpy( (void *)query_string, (const void *)query, (size_t)len ); + ctx.env.query_string = (const char *)query_string; + } + } +} + + +static void querystring_cb( const char *name, const char *value ) +{ + char *val; + int len; + + if( !value ) + value = ""; + + if( !strcmp( name, "rev" ) ) + { + len = (int)strlen( value ) + 1; + val = (char *)__sbrk( len ); + memcpy( (void *)val, (const void *)value, (size_t)len ); + ctx.query.revision = (const char *)val; + if( !strcasecmp( val, "HEAD" ) ) + ctx.query.rev = "0"; + else + ctx.query.rev = ctx.query.revision; + /***************************************************************************** + NOTE: + ==== + We have to call http_parse_querystring() strongly after calling + cgit_rpath_info() or ctx_relative_html() because the structs + ctx.repo.info and ctx.repo.relative_info should be completed before + parsing special Git revision names. + + Revision at start of the date '{' DATE '}' we will not support. + *****************************************************************************/ + } + else if( !strcmp( name, "op" ) ) + { + len = (int)strlen( value ) + 1; + val = (char *)__sbrk( len ); + memcpy( (void *)val, (const void *)value, (size_t)len ); + ctx.query.operation = (const char *)val; + } + else if( !strcmp( name, "search" ) ) + { + len = (int)strlen( value ) + 1; + val = (char *)__sbrk( len ); + memcpy( (void *)val, (const void *)value, (size_t)len ); + ctx.query.search = (const char *)val; + } +/* + else if( another parameter ) + { + } + ... + */ + /* NOTE: Do not parse 'ofs=' parameter! */ +} + + +void cgit_parse_query( void ) +{ + char *path_info = NULL; + + get_selfdir(); + + ctx_check_query_uri( 1 ); + + path_info = xstrdup( ctx.env.path_info ); + + ctx.query.ofs = ctx_grab_int_query_param( "ofs" ); + http_parse_querystring( ctx.env.query_string, querystring_cb ); + if( !strcmp( ctx.query.rev, "0" ) ) + { + (void)ctx_grab_str_query_param( "rev" ); + ctx.query.rev = "0"; + } + + if( path_info ) + { + if( !strcmp( "/", path_info ) ) + { + ctx_page_size_from_global(); + ctx_repo_dirs_from_global(); + ctx_site_vars_from_global(); + ctx_promo_vars_from_global(); + ctx_header_from_global(); + ctx_footer_from_global(); + ctx_num_of_repos(); + ctx_repolist_status_line(); + ctx_repolist_menu(); + } + else + { + struct repo *repo = NULL; + + repo = ctx_repo_name(); + if( repo ) + { + ctx_page_size_from_repo( repo ); + ctx_repo_dirs_from_repo( repo ); + ctx_site_vars_from_repo( repo ); + ctx_promo_vars_from_repo( repo ); + ctx_header_from_repo( repo ); + ctx_footer_from_repo( repo ); + ctx.vars.page_type = ptype_repo; + + cgit_repo_info( &ctx.repo.info, NULL ); + if( ctx.repo.relative_path ) + { + cgit_rpath_info( &ctx.repo.relative_info, ctx.repo.relative_path, (!strcmp( ctx.query.rev, "0" )) ? NULL : ctx.query.rev ); + if( ctx.repo.relative_info.kind != GIT_OBJECT_TREE && ctx.repo.relative_info.kind != GIT_OBJECT_BLOB ) + { + /* Query string requested invalid revision: */ + memcpy( (void *)&ctx.repo.relative_info, (const void *)&ctx.repo.info, sizeof( struct cgit_info ) ); + ctx.query.revision = NULL; + (void)ctx_grab_str_query_param( "rev" ); + ctx.query.rev = "0"; + } + } + else + memcpy( (void *)&ctx.repo.relative_info, (const void *)&ctx.repo.info, sizeof( struct cgit_info ) ); + + cgit_repo_branches_number( &ctx.repo ); + cgit_repo_commits_number( &ctx.repo ); + cgit_repo_tags_number( &ctx.repo ); + ctx_repo_status_line( &ctx.repo ); + ctx_repo_menu( &ctx.repo ); + } + else + { + ctx_page_size_from_global(); + ctx_repo_dirs_from_global(); + ctx_header_from_global(); + ctx_footer_from_global(); + + ctx.page.status = 404; + ctx.page.status_message = "Page not found"; + + ctx.vars.title = "404"; + { + struct strbuf buf = STRBUF_INIT; + char *stmsg = NULL; + + /************************************************************************** + We slipped to the end of PATH_INFO when we were looking for a repository + and now we printout the repo-root: + */ + strbuf_addf( &buf, "Git Repository '%s' not found.", ctx.repo.repo_root ); + stmsg = (char *)__sbrk( (int)buf.len + 1 ); + memcpy( (void *)stmsg, (const void *)buf.buf, buf.len + 1 ); + strbuf_release( &buf ); + ctx.vars.description = (const char *)stmsg; + } + } + + ctx_num_of_repos(); + } + + ctx_relative_html(); + ctx_search_placeholder(); + + cgit_git_version( &ctx.vers ); + cgit_nginx_version( &ctx.vers ); + + free( path_info ); + } +} |