mygit

[UNMAINTAINED] A cgit/webgit alternative, written in Rust
Log | Files | Refs | README | LICENSE

commit 4afe79413a568361385721927a64f928ed41118d
parent 928ba5e762b65ae1304b9c11c6be71b6d3163a37
Author: Johann150 <johann@qwertqwefsday.eu>
Date:   Mon, 22 Mar 2021 23:33:43 +0100

implement subtrees

This shouldn't introduce path traversal bugs because we are only putting the
path into git and never into the file system. In bare repositories the files
would not even exist on disk (at least not under those names), so putting
the paths into the file system wouldn't work anyway.

Diffstat:
Msrc/main.rs | 70+++++++++++++++++++++++++++++++++++++++++-----------------------------
Mtemplates/tree.html | 2+-
2 files changed, 42 insertions(+), 30 deletions(-)

diff --git a/src/main.rs b/src/main.rs @@ -290,8 +290,10 @@ async fn repo_refs(req: Request<()>) -> tide::Result { struct RepoTreeTemplate<'a> { repo: &'a Repository, tree: Tree<'a>, + path: &'a Path, spec: &'a str, } + async fn repo_tree(req: Request<()>) -> tide::Result { // TODO handle subtrees let repo = repo_from_request(&req.param("repo_name")?)?; @@ -310,6 +312,7 @@ async fn repo_tree(req: Request<()>) -> tide::Result { let tmpl = RepoTreeTemplate { repo: &repo, tree, + path: Path::new(""), spec, }; Ok(tmpl.into()) @@ -367,12 +370,13 @@ async fn repo_file(req: Request<()>) -> tide::Result { let spec = req.param("ref").ok().or_else(|| head.shorthand()).unwrap(); let commit = repo.revparse_single(spec)?.peel_to_commit()?; let tree = commit.tree()?; - let tree_entry = tree.get_name(req.param("object_name")?).unwrap(); + let path = Path::new(req.param("object_name")?); + let tree_entry = tree.get_path(path).unwrap(); // TODO make sure I am escaping html properly here // TODO allow disabling of syntax highlighting // TODO -- dont pull in memory, use iterators if possible let syntax_set = SyntaxSet::load_defaults_nonewlines(); - let extension = std::path::Path::new(tree_entry.name().unwrap()) + let extension = path .extension() .and_then(std::ffi::OsStr::to_str) .unwrap_or_default(); @@ -381,34 +385,42 @@ async fn repo_file(req: Request<()>) -> tide::Result { .unwrap_or_else(|| syntax_set.find_syntax_plain_text()); let ts = ThemeSet::load_defaults(); let theme = &ts.themes["InspiredGitHub"]; // TODO make customizable - let tree_obj = tree_entry.to_object(&repo)?; - if tree_obj.as_tree().is_some() { - // TODO render tree - } - let file_string = str::from_utf8(tree_obj.as_blob().unwrap().content())?; - let mut highlighter = syntect::easy::HighlightLines::new(&syntax_reference, &theme); - let (mut output, bg) = syntect::html::start_highlighted_html_snippet(&theme); - for (n, line) in syntect::util::LinesWithEndings::from(file_string).enumerate() { - let regions = highlighter.highlight(line, &syntax_set); - output.push_str(&format!( - "<a href='#L{0}' id='L{0}' class='line'>{0}</a>", - n + 1 - )); - syntect::html::append_highlighted_html_for_styled_line( - &regions[..], - syntect::html::IncludeBackground::IfDifferent(bg), - &mut output, - ); - } - output.push_str("</pre>\n"); + let tmpl = match tree_entry.to_object(&repo)?.into_tree() { + Ok(tree) => RepoTreeTemplate { + repo: &repo, + tree, + path, + spec: &spec, + } + .into(), + Err(tree_obj) => { + let file_string = str::from_utf8(tree_obj.as_blob().unwrap().content())?; + let mut highlighter = syntect::easy::HighlightLines::new(&syntax_reference, &theme); + let (mut output, bg) = syntect::html::start_highlighted_html_snippet(&theme); + for (n, line) in syntect::util::LinesWithEndings::from(file_string).enumerate() { + let regions = highlighter.highlight(line, &syntax_set); + output.push_str(&format!( + "<a href='#L{0}' id='L{0}' class='line'>{0}</a>", + n + 1 + )); + syntect::html::append_highlighted_html_for_styled_line( + &regions[..], + syntect::html::IncludeBackground::IfDifferent(bg), + &mut output, + ); + } + output.push_str("</pre>\n"); - let tmpl = RepoFileTemplate { - repo: &repo, - tree_entry: &tree_entry, - file_text: &output, - spec: &spec, + RepoFileTemplate { + repo: &repo, + tree_entry: &tree_entry, + file_text: &output, + spec: &spec, + } + .into() + } }; - Ok(tmpl.into()) + Ok(tmpl) } mod filters { @@ -490,7 +502,7 @@ async fn main() -> Result<(), std::io::Error> { app.at("/:repo_name/log/:ref").get(repo_log); // ref optional app.at("/:repo_name/tree").get(repo_tree); app.at("/:repo_name/tree/:ref").get(repo_tree); - app.at("/:repo_name/tree/:ref/item/:object_name") + app.at("/:repo_name/tree/:ref/item/*object_name") .get(repo_file); // Raw files, patch files app.listen(format!("[::]:{}", CONFIG.port)).await?; diff --git a/templates/tree.html b/templates/tree.html @@ -11,7 +11,7 @@ {{ entry.filemode()|unix_perms }} </td> <td class="filename"> - <a href="/{{repo|repo_name|urlencode_strict}}/tree/{{ spec }}/item/{{entry.name().unwrap()}}"> + <a href="/{{repo|repo_name|urlencode_strict}}/tree/{{ spec }}/item/{{path.join(entry.name().unwrap()).to_string_lossy()}}"> {{ entry.name().unwrap() }}{% if entry.to_object(repo).unwrap().as_tree().is_some() %}/{% endif %}</a> </td> <td class="filesize">