/* * The plain page, which hands over a repository's own bytes rather than * rendering a view of them. A file is written out whole under a content type * guessed from its name, though a repository that has not enabled html serving * keeps only the types a browser will not act on. A directory, or a request * carrying no path, is answered with a bare document of links to the entries * below it rather than with one of cgit's themed pages. */ #define USE_THE_REPOSITORY_VARIABLE #include "cgit.h" #include "html.h" #include "shared.h" #include "ui-plain.h" #include "ui-shared.h" /* * A listing is opened by the entry that matched and closed only once the walk * is over, so the end of the page has to tell the three cases apart. */ enum response { RESPONSE_NONE, RESPONSE_BLOB, RESPONSE_LISTING }; struct walk_tree_context { // Length of the directory part of the requested path, slash included, // and -1 when no path was requested so that no base length can equal // it. int dir_len; enum response response; }; /* * Everything below text/ and application/ can carry markup or script that a * browser would run against the site, so only PDF is let back through. */ static int is_unsafe_type(const char *mimetype) { return (starts_with(mimetype, "text/") || starts_with(mimetype, "application/")) && strcmp(mimetype, "application/pdf"); } /* * A nonzero return says the response has been written, error pages included, * so the walk does not go on to report the path as missing. */ static int print_object(const struct object_id *oid, const char *path) { enum object_type type; char *buf, *mimetype; unsigned long size; type = odb_read_object_info(the_repository->objects, oid, &size); if (type == OBJ_BAD) { cgit_print_error_page(404, "Not found", "Not found"); return 1; } // The limit counts kilobytes and is checked before the read, so a huge // blob is kept out of memory rather than noticed once it is there. if (ctx.cfg.max_blob_size && size / 1024 > (unsigned long)ctx.cfg.max_blob_size) { cgit_print_error_page(413, "Too large", "Object size (%luKB) exceeds limit (%dKB)", size / 1024, ctx.cfg.max_blob_size); return 1; } buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) { cgit_print_error_page(404, "Not found", "Not found"); return 1; } mimetype = cgit_get_mimetype_for_filename(path); ctx.page.mimetype = mimetype; if (!ctx.repo->enable_html_serving) { // The bytes are whatever the repository holds, so the browser // is told not to guess a type of its own and not to load // anything they reference. Both lines must go out before // cgit_print_http_headers, which closes the header block. html("X-Content-Type-Options: nosniff\n"); html("Content-Security-Policy: default-src 'none'\n"); if (mimetype && is_unsafe_type(mimetype)) ctx.page.mimetype = NULL; } if (!ctx.page.mimetype) { if (buffer_is_binary(buf, size)) { ctx.page.mimetype = "application/octet-stream"; ctx.page.charset = NULL; } else { ctx.page.mimetype = "text/plain"; } } ctx.page.filename = path; ctx.page.size = size; ctx.page.etag = oid_to_hex(oid); cgit_print_http_headers(); html_raw(buf, size); free(mimetype); free(buf); return 1; } static char *build_path(const char *base, int baselen, const char *path) { if (path[0]) return cgit_fmtalloc("%.*s%s/", baselen, base, path); else return cgit_fmtalloc("%.*s/", baselen, base); } static void print_dir(const struct object_id *oid, const char *base, int baselen, const char *path) { char *fullpath; const char *leading_slash; size_t len; fullpath = build_path(base, baselen, path); leading_slash = (fullpath[0] == '/' ? "" : "/"); ctx.page.etag = oid_to_hex(oid); cgit_print_http_headers(); // The listing is a full document of its own, so it carries the same // doctype and charset as the layout pages or the browser would parse // it in quirks mode. html("\n\n
\n"); html("\n"); htmlf("