/* * 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"); } static char *get_mimetype_for_filename(const char *filename) { const char *ext; char *type, line[1024]; struct string_list fields = STRING_LIST_INIT_NODUP; size_t i; FILE *file; struct string_list_item *entry; if (!filename) return NULL; ext = strrchr(filename, '.'); if (!ext) return NULL; ++ext; if (!ext[0]) return NULL; entry = string_list_lookup(&ctx.cfg.mimetypes, ext); if (entry) return xstrdup(entry->util); if (!ctx.cfg.mimetype_file) return NULL; file = fopen(ctx.cfg.mimetype_file, "r"); if (!file) return NULL; while (fgets(line, sizeof(line), file)) { // A line of a mime.types file is one type followed by every // extension that maps to it. string_list_split_in_place(&fields, line, " \t\r\n", -1); string_list_remove_empty_items(&fields, 0); if (!fields.nr || fields.items[0].string[0] == '#') { string_list_clear(&fields, 0); continue; } type = fields.items[0].string; for (i = 1; i < fields.nr; i++) { if (!strcasecmp(ext, fields.items[i].string)) { fclose(file); return xstrdup(type); } } string_list_clear(&fields, 0); } fclose(file); return NULL; } /* * Writes the response for the object, error pages included, so the walk * does not go on to report the path as missing. */ static void print_object(const struct object_id *oid, const char *path) { enum object_type type; char *buf, *mimetype; size_t 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; } if (cgit_blob_too_large(size)) { cgit_print_blob_too_large(size); return; } buf = odb_read_object(the_repository->objects, oid, &type, &size); if (!buf) { cgit_print_error_page(404, "Not Found", "Not found"); return; } mimetype = get_mimetype_for_filename(path); ctx.page.mimetype = mimetype; if (!ctx.repo->enable_html_serving) { ctx.page.untrusted = 1; if (mimetype && is_unsafe_type(mimetype)) ctx.page.mimetype = NULL; } if (!ctx.page.mimetype) ctx.page.mimetype = buffer_is_binary(buf, size) ? "application/octet-stream" : "text/plain"; ctx.page.filename = path; ctx.page.size = size; cgit_print_http_headers(); html_raw(buf, size); // ctx.page.mimetype may still point into mimetype, and the headers // are read again by whatever renders after this, so it stays alive // for the rest of the request. free(buf); } static char *build_path(const char *base, int baselen, const char *path) { if (path[0]) return xstrfmt("%.*s%s/", baselen, base, path); else return xstrfmt("%.*s/", baselen, base); } static void print_dir(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] == '/' ? "" : "/"); cgit_print_http_headers(); // A full document of its own, and without the doctype and charset // the browser would parse it in quirks mode. html("\n\n
\n"); html("\n"); htmlf("