aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui-summary.c
diff options
context:
.c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/* ui-snapshot.c: generate snapshot of a commit
 *
 * Copyright (C) 2006 Lars Hjemli
 * Copyright (C) 2012 Jason A. Donenfeld <Jason@zx2c4.com>
 *
 * Licensed under GNU General Public License v2
 *   (see COPYING for full license text)
 */

#include "cgit.h"
#include "html.h"
#include "ui-shared.h"

static int write_archive_type(const char *format, const char *hex, const char *prefix)
{
	struct argv_array argv = ARGV_ARRAY_INIT;
	argv_array_push(&argv, "snapshot");
	argv_array_push(&argv, format);
	if (prefix) {
		argv_array_push(&argv, "--prefix");
		argv_array_push(&argv, fmt("%s/", prefix));
	}
	argv_array_push(&argv, hex);
	return write_archive(argv.argc, argv.argv, NULL, 1, NULL, 0);
}

static int write_tar_archive(const char *hex, const char *prefix)
{
	return write_archive_type("--format=tar", hex, prefix);
}

static int write_zip_archive(const char *hex, const char *prefix)
{
	return write_archive_type("--format=zip", hex, prefix);
}

static int write_compressed_tar_archive(const char *hex,
					const char *prefix,
					char *filter_argv[])
{
	int rv;
	struct cgit_filter f;

	f.cmd = filter_argv[0];
	f.argv = filter_argv;
	cgit_open_filter(&f);
	rv = write_tar_archive(hex, prefix);
	cgit_close_filter(&f);
	return rv;
}

static int write_tar_gzip_archive(const char *hex, const char *prefix)
{
	char *argv[] = { "gzip", "-n", NULL };
	return write_compressed_tar_archive(hex, prefix, argv);
}

static int write_tar_bzip2_archive(const char *hex, const char *prefix)
{
	char *argv[] = { "bzip2", NULL };
	return write_compressed_tar_archive(hex, prefix, argv);
}

static int write_tar_xz_archive(const char *hex, const char *prefix)
{
	char *argv[] = { "xz", NULL };
	return write_compressed_tar_archive(hex, prefix, argv);
}

const struct cgit_snapshot_format cgit_snapshot_formats[] = {
	{ ".zip", "application/x-zip", write_zip_archive, 0x01 },
	{ ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x02 },
	{ ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x04 },
	{ ".tar", "application/x-tar", write_tar_archive, 0x08 },
	{ ".tar.xz", "application/x-xz", write_tar_xz_archive, 0x10 },
	{ NULL }
};

static const struct cgit_snapshot_format *get_format(const char *filename)
{
	const struct cgit_snapshot_format *fmt;
	int fl, sl;

	fl = strlen(filename);
	for (fmt = cgit_snapshot_formats; fmt->suffix; fmt++) {
		sl = strlen(fmt->suffix);
		if (sl >= fl)
			continue;
		if (!strcmp(fmt->suffix, filename + fl - sl))
			return fmt;
	}
	return NULL;
}

static int make_snapshot(const struct cgit_snapshot_format *format,
			 const char *hex, const char *prefix,
			 const char *filename)
{
	unsigned char sha1[20];

	if (get_sha1(hex, sha1)) {
		cgit_print_error(fmt("Bad object id: %s", hex));
		return 1;
	}
	if (!lookup_commit_reference(sha1)) {
		cgit_print_error(fmt("Not a commit reference: %s", hex));
		return 1;
	}
	ctx.page.mimetype = xstrdup(format->mimetype);
	ctx.page.filename = xstrdup(filename);
	cgit_print_http_headers(&ctx);
	format->write_func(hex, prefix);
	return 0;
}

/* Try to guess the requested revision from the requested snapshot name.
 * First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become
 * "cgit-0.7.2". If this is a valid commit object name we've got a winner.
 * Otherwise, if the snapshot name has a prefix matching the result from
 * repo_basename(), we strip the basename and any following '-' and '_'
 * characters ("cgit-0.7.2" -> "0.7.2") and check the resulting name once
 * more. If this still isn't a valid commit object name, we check if pre-
 * pending a 'v' to the remaining snapshot name ("0.7.2" -> "v0.7.2") gives
 * us something valid.
 */
static const char *get_ref_from_filename(const char *url, const char *filename,
					 const struct cgit_snapshot_format *format)
{
	const char *reponame;
	unsigned char sha1[20];
	char *snapshot;

	snapshot = xstrdup(filename);
	snapshot[strlen(snapshot) - strlen(format->suffix)] = '\0';

	if (get_sha1(snapshot, sha1) == 0)
		return snapshot;

	reponame = cgit_repobasename(url);
	if (prefixcmp(snapshot, reponame) == 0) {
		snapshot += strlen(reponame);
		while (snapshot && (*snapshot == '-' || *snapshot == '_'))
			snapshot++;
	}

	if (get_sha1(snapshot, sha1) == 0)
		return snapshot;

	snapshot = fmt("v%s", snapshot);
	if (get_sha1(snapshot, sha1) == 0)
		return snapshot;

	return NULL;
}

void show_error(char *msg)
{
	ctx.page.mimetype = "text/html";
	cgit_print_http_headers(&ctx);
	cgit_print_docstart(&ctx);
	cgit_print_pageheader(&ctx);
	cgit_print_error(msg);
	cgit_print_docend();
}

void cgit_print_snapshot(const char *head, const char *hex,
			 const char *filename, int snapshots, int dwim)
{
	const struct cgit_snapshot_format* f;
	char *prefix = NULL;

	if (!filename) {
		show_error("No snapshot name specified");
		return;
	}

	f = get_format(filename);
	if (!f) {
		show_error(xstrdup(fmt("Unsupported snapshot format: %s",
				       filename)));
		return;
	}

	if (!hex && dwim) {
		hex = get_ref_from_filename(ctx.repo->url, filename, f);
		if (hex == NULL) {
			html_status(404, "Not found", 0);
			return;
		}
		prefix = xstrdup(filename);
		prefix[strlen(filename) - strlen(f->suffix)] = '\0';
	}

	if (!hex)
		hex = head;

	if (!prefix)
		prefix = xstrdup(cgit_repobasename(ctx.repo->url));

	make_snapshot(f, hex, prefix, filename);
	free(prefix);
}