~aw/patches

mygit: implement subtrees v1 PROPOSED

Johann Galle
Johann Galle: 2
 implement subtrees
 run cargo fmt

 4 files changed, 47 insertions(+), 34 deletions(-)
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~aw/patches/patches/21387/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH mygit v1 1/2] implement subtrees Export this patch

Johann Galle
From: Johann150 <johann@qwertqwefsday.eu>

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.
---
 src/main.rs         | 70 ++++++++++++++++++++++++++-------------------
 templates/tree.html |  2 +-
 2 files changed, 42 insertions(+), 30 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 146e764..4490fb7 100644
--- 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
index 8e187f9..836e847 100644
--- 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">
-- 
2.20.1
Thank you! 

[PATCH mygit v1 2/2] run cargo fmt Export this patch

Johann Galle
From: Johann150 <johann@qwertqwefsday.eu>

---
 src/errorpage.rs | 7 ++++---
 src/main.rs      | 2 +-
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/errorpage.rs b/src/errorpage.rs
index dbfcba7..3df280f 100644
--- a/src/errorpage.rs
+++ b/src/errorpage.rs
@@ -12,17 +12,18 @@ struct ErrorTemplate {
pub struct ErrorToErrorpage;

#[async_trait::async_trait]
impl<State: Clone + Send + Sync + 'static> Middleware<State> for ErrorToErrorpage{
impl<State: Clone + Send + Sync + 'static> Middleware<State> for ErrorToErrorpage {
    async fn handle(&self, req: Request<State>, next: Next<'_, State>) -> tide::Result {
        let resource = req.url().path().to_string();
        let mut response = next.run(req).await;
        if let Some(err) = response.take_error() {
            let status = err.status();
            response = ErrorTemplate{
            response = ErrorTemplate {
                resource,
                status,
                message: err.into_inner().to_string(),
            }.into();
            }
            .into();
            response.set_status(status);
        }

diff --git a/src/main.rs b/src/main.rs
index 4490fb7..c78f8f1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -135,7 +135,7 @@ fn repo_from_request(repo_name: &str) -> Result<Repository, tide::Error> {
        .into_owned();
    if repo_name.contains("..") {
        // Prevent path traversal
        return Err(tide::Error::from_str(400, "Invalid name"))
        return Err(tide::Error::from_str(400, "Invalid name"));
    }
    let repo_path = Path::new(&CONFIG.projectroot).join(repo_name);
    Repository::open(repo_path).or_else(|_| {
-- 
2.20.1