all repos — cgit @ 40fbefba0514b33988d453aea05aa2b956e98f84

a hyperfast web frontend for git written in c

ui-plain.c (view raw)

  1/* ui-plain.c: functions for output of plain blobs by path
  2 *
  3 * Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
  4 *
  5 * Licensed under GNU General Public License v2
  6 *   (see COPYING for full license text)
  7 */
  8
  9#include "cgit.h"
 10#include "ui-plain.h"
 11#include "html.h"
 12#include "ui-shared.h"
 13
 14struct walk_tree_context {
 15	int match_baselen;
 16	int match;
 17};
 18
 19static int print_object(const unsigned char *sha1, const char *path)
 20{
 21	enum object_type type;
 22	char *buf, *mimetype;
 23	unsigned long size;
 24
 25	type = sha1_object_info(sha1, &size);
 26	if (type == OBJ_BAD) {
 27		cgit_print_error_page(404, "Not found", "Not found");
 28		return 0;
 29	}
 30
 31	buf = read_sha1_file(sha1, &type, &size);
 32	if (!buf) {
 33		cgit_print_error_page(404, "Not found", "Not found");
 34		return 0;
 35	}
 36
 37	mimetype = get_mimetype_for_filename(path);
 38	ctx.page.mimetype = mimetype;
 39
 40	if (!ctx.repo->enable_html_serving) {
 41		html("X-Content-Type-Options: nosniff\n");
 42		html("Content-Security-Policy: default-src 'none'\n");
 43		if (mimetype) {
 44			/* Built-in white list allows PDF and everything that isn't text/ and application/ */
 45			if ((!strncmp(mimetype, "text/", 5) || !strncmp(mimetype, "application/", 12)) && strcmp(mimetype, "application/pdf"))
 46				ctx.page.mimetype = NULL;
 47		}
 48	}
 49
 50	if (!ctx.page.mimetype) {
 51		if (buffer_is_binary(buf, size)) {
 52			ctx.page.mimetype = "application/octet-stream";
 53			ctx.page.charset = NULL;
 54		} else {
 55			ctx.page.mimetype = "text/plain";
 56		}
 57	}
 58	ctx.page.filename = path;
 59	ctx.page.size = size;
 60	ctx.page.etag = sha1_to_hex(sha1);
 61	cgit_print_http_headers();
 62	html_raw(buf, size);
 63	free(mimetype);
 64	free(buf);
 65	return 1;
 66}
 67
 68static char *buildpath(const char *base, int baselen, const char *path)
 69{
 70	if (path[0])
 71		return fmtalloc("%.*s%s/", baselen, base, path);
 72	else
 73		return fmtalloc("%.*s/", baselen, base);
 74}
 75
 76static void print_dir(const unsigned char *sha1, const char *base,
 77		      int baselen, const char *path)
 78{
 79	char *fullpath, *slash;
 80	size_t len;
 81
 82	fullpath = buildpath(base, baselen, path);
 83	slash = (fullpath[0] == '/' ? "" : "/");
 84	ctx.page.etag = sha1_to_hex(sha1);
 85	cgit_print_http_headers();
 86	htmlf("<html><head><title>%s", slash);
 87	html_txt(fullpath);
 88	htmlf("</title></head>\n<body>\n<h2>%s", slash);
 89	html_txt(fullpath);
 90	html("</h2>\n<ul>\n");
 91	len = strlen(fullpath);
 92	if (len > 1) {
 93		fullpath[len - 1] = 0;
 94		slash = strrchr(fullpath, '/');
 95		if (slash)
 96			*(slash + 1) = 0;
 97		else {
 98			free(fullpath);
 99			fullpath = NULL;
100		}
101		html("<li>");
102		cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
103				fullpath);
104		html("</li>\n");
105	}
106	free(fullpath);
107}
108
109static void print_dir_entry(const unsigned char *sha1, const char *base,
110			    int baselen, const char *path, unsigned mode)
111{
112	char *fullpath;
113
114	fullpath = buildpath(base, baselen, path);
115	if (!S_ISDIR(mode) && !S_ISGITLINK(mode))
116		fullpath[strlen(fullpath) - 1] = 0;
117	html("  <li>");
118	if (S_ISGITLINK(mode)) {
119		cgit_submodule_link(NULL, fullpath, sha1_to_hex(sha1));
120	} else
121		cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
122				fullpath);
123	html("</li>\n");
124	free(fullpath);
125}
126
127static void print_dir_tail(void)
128{
129	html(" </ul>\n</body></html>\n");
130}
131
132static int walk_tree(const unsigned char *sha1, struct strbuf *base,
133		const char *pathname, unsigned mode, int stage, void *cbdata)
134{
135	struct walk_tree_context *walk_tree_ctx = cbdata;
136
137	if (base->len == walk_tree_ctx->match_baselen) {
138		if (S_ISREG(mode)) {
139			if (print_object(sha1, pathname))
140				walk_tree_ctx->match = 1;
141		} else if (S_ISDIR(mode)) {
142			print_dir(sha1, base->buf, base->len, pathname);
143			walk_tree_ctx->match = 2;
144			return READ_TREE_RECURSIVE;
145		}
146	} else if (base->len < INT_MAX && (int)base->len > walk_tree_ctx->match_baselen) {
147		print_dir_entry(sha1, base->buf, base->len, pathname, mode);
148		walk_tree_ctx->match = 2;
149	} else if (S_ISDIR(mode)) {
150		return READ_TREE_RECURSIVE;
151	}
152
153	return 0;
154}
155
156static int basedir_len(const char *path)
157{
158	char *p = strrchr(path, '/');
159	if (p)
160		return p - path + 1;
161	return 0;
162}
163
164void cgit_print_plain(void)
165{
166	const char *rev = ctx.qry.sha1;
167	unsigned char sha1[20];
168	struct commit *commit;
169	struct pathspec_item path_items = {
170		.match = ctx.qry.path,
171		.len = ctx.qry.path ? strlen(ctx.qry.path) : 0
172	};
173	struct pathspec paths = {
174		.nr = 1,
175		.items = &path_items
176	};
177	struct walk_tree_context walk_tree_ctx = {
178		.match = 0
179	};
180
181	if (!rev)
182		rev = ctx.qry.head;
183
184	if (get_sha1(rev, sha1)) {
185		cgit_print_error_page(404, "Not found", "Not found");
186		return;
187	}
188	commit = lookup_commit_reference(sha1);
189	if (!commit || parse_commit(commit)) {
190		cgit_print_error_page(404, "Not found", "Not found");
191		return;
192	}
193	if (!path_items.match) {
194		path_items.match = "";
195		walk_tree_ctx.match_baselen = -1;
196		print_dir(commit->tree->object.oid.hash, "", 0, "");
197		walk_tree_ctx.match = 2;
198	}
199	else
200		walk_tree_ctx.match_baselen = basedir_len(path_items.match);
201	read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
202	if (!walk_tree_ctx.match)
203		cgit_print_error_page(404, "Not found", "Not found");
204	else if (walk_tree_ctx.match == 2)
205		print_dir_tail();
206}