<commit_before>/*
 * Copyright 2014 gitblit.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitblit.servlet;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.tika.Tika;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.Constants;
import com.gitblit.Keys;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.PathModel;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * Serves the content of a branch.
 *
 * @author <NAME>
 *
 */
@Singleton
public class RawServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	private transient Logger logger = LoggerFactory.getLogger(RawServlet.class);

	private final IRuntimeManager runtimeManager;

	private final IRepositoryManager repositoryManager;

	@Inject
	public RawServlet(
			IRuntimeManager runtimeManager,
			IRepositoryManager repositoryManager) {

		this.runtimeManager = runtimeManager;
		this.repositoryManager = repositoryManager;
	}

	/**
	 * Returns an url to this servlet for the specified parameters.
	 *
	 * @param baseURL
	 * @param repository
	 * @param branch
	 * @param path
	 * @return an url
	 */
	public static String asLink(String baseURL, String repository, String branch, String path) {
		if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
			baseURL = baseURL.substring(0, baseURL.length() - 1);
		}
		if (repository.length() > 0 && repository.charAt(repository.length() - 1) == '/') {
			repository = repository.substring(0, repository.length() - 1);
		}
		if (repository.length() > 0 && repository.charAt(0) == '/') {
			repository = repository.substring(1);
		}

		char fsc = '!';
		char c = GitblitContext.getManager(IRuntimeManager.class).getSettings().getChar(Keys.web.forwardSlashCharacter, '/');
		if (c != '/') {
			fsc = c;
		}
		if (branch != null) {
			branch = Repository.shortenRefName(branch).replace('/', fsc);
		}

		if (path != null && path.length() > 0 && path.charAt(0) == '/') {
			path = path.substring(1);
		}
		String encodedPath = path == null ? "" : path.replace('/', fsc);
		return baseURL + Constants.RAW_PATH + repository + "/" + (branch == null ? "" : (branch + "/" + encodedPath));
	}


	/**
	 * Find and return the name of a branch from a given repository in a HTTP request path info.
	 * The branch name returned is transformed to the form in the repository, i.e. a transformation
	 * of the forward slash character in the URL is reversed.
	 *
	 * @param repository
	 * 				Path of repository, no leading slash, no trailing slash
	 * @param pathInfo
	 * 				The sanitised path info from a HTTP request, i.e. without the leading slash.
	 *
	 * @return	The name of the branch from the path info, unescaped.
	 */
	String getBranch(String repository, String pathInfo)
	{
		if (pathInfo == null || pathInfo.isEmpty() || pathInfo.equals("/")) return "";
		String branch = pathInfo.substring(pathInfo.indexOf(repository) + repository.length() + 1);
		int fs = branch.indexOf('/');
		if (fs > -1) {
			branch = branch.substring(0, fs);
		}
		char c = runtimeManager.getSettings().getChar(Keys.web.forwardSlashCharacter, '/');
		return branch.replace('!', '/').replace(c, '/');
	}

	/**
	 * Find and return the path from a given repository and given branch in a HTTP request path info.
	 * The path string returned is transformed to the form in the repository, i.e. a transformation
	 * of the forward slash character in the URL is reversed.
	 *
	 * @param repository
	 * 				Path of repository, no leading slash, no trailing slash
	 * @param branch
	 * 				Branch name from the repository, i.e. with forward slash character, no leading slash, no trailing slash.
	 * @param pathInfo
	 * 				The sanitised path info from a HTTP request, i.e. without the leading slash.
	 *
	 * @return	The file/folder path part from the path info, in unescaped form.
	 */
	String getPath(String repository, String branch, String pathInfo)
	{
		if (pathInfo == null || pathInfo.isEmpty() || pathInfo.equals("/")) return "";
		String base = repository + "/" + branch;
		if (pathInfo.equals(base)) {
			return "";
		}
		String path = pathInfo.substring(pathInfo.indexOf(base) + base.length() + 1);
		if (path.endsWith("/")) {
			path = path.substring(0, path.length() - 1);
		}
		char c = runtimeManager.getSettings().getChar(Keys.web.forwardSlashCharacter, '/');
		return path.replace('!', '/').replace(c, '/');
	}

	protected boolean renderIndex() {
		return false;
	}

	/**
	 * Retrieves the specified resource from the specified branch of the
	 * repository.
	 *
	 * @param request
	 * @param response
	 * @throws javax.servlet.ServletException
	 * @throws java.io.IOException
	 */
	private void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String path = request.getPathInfo();
		if (path.toLowerCase().endsWith(".git")) {
			// forward to url with trailing /
			// this is important for relative pages links
			response.sendRedirect(request.getServletPath() + path + "/");<commit_msg>raw: Fix getPath with lead-ins or missing trailing slashes after the branch.<commit_after>/*
 * Copyright 2014 gitblit.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitblit.servlet;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.tika.Tika;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.MutableObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.treewalk.filter.PathFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitblit.Constants;
import com.gitblit.Keys;
import com.gitblit.manager.IRepositoryManager;
import com.gitblit.manager.IRuntimeManager;
import com.gitblit.models.PathModel;
import com.gitblit.utils.ByteFormat;
import com.gitblit.utils.JGitUtils;
import com.gitblit.utils.MarkdownUtils;
import com.gitblit.utils.StringUtils;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * Serves the content of a branch.
 *
 * @author <NAME>
 *
 */
@Singleton
public class RawServlet extends HttpServlet {

	private static final long serialVersionUID = 1L;

	private transient Logger logger = LoggerFactory.getLogger(RawServlet.class);

	private final IRuntimeManager runtimeManager;

	private final IRepositoryManager repositoryManager;

	@Inject
	public RawServlet(
			IRuntimeManager runtimeManager,
			IRepositoryManager repositoryManager) {

		this.runtimeManager = runtimeManager;
		this.repositoryManager = repositoryManager;
	}

	/**
	 * Returns an url to this servlet for the specified parameters.
	 *
	 * @param baseURL
	 * @param repository
	 * @param branch
	 * @param path
	 * @return an url
	 */
	public static String asLink(String baseURL, String repository, String branch, String path) {
		if (baseURL.length() > 0 && baseURL.charAt(baseURL.length() - 1) == '/') {
			baseURL = baseURL.substring(0, baseURL.length() - 1);
		}
		if (repository.length() > 0 && repository.charAt(repository.length() - 1) == '/') {
			repository = repository.substring(0, repository.length() - 1);
		}
		if (repository.length() > 0 && repository.charAt(0) == '/') {
			repository = repository.substring(1);
		}

		char fsc = '!';
		char c = GitblitContext.getManager(IRuntimeManager.class).getSettings().getChar(Keys.web.forwardSlashCharacter, '/');
		if (c != '/') {
			fsc = c;
		}
		if (branch != null) {
			branch = Repository.shortenRefName(branch).replace('/', fsc);
		}

		if (path != null && path.length() > 0 && path.charAt(0) == '/') {
			path = path.substring(1);
		}
		String encodedPath = path == null ? "" : path.replace('/', fsc);
		return baseURL + Constants.RAW_PATH + repository + "/" + (branch == null ? "" : (branch + "/" + encodedPath));
	}


	/**
	 * Find and return the name of a branch from a given repository in a HTTP request path info.
	 * The branch name returned is transformed to the form in the repository, i.e. a transformation
	 * of the forward slash character in the URL is reversed.
	 *
	 * @param repository
	 * 				Path of repository, no leading slash, no trailing slash
	 * @param pathInfo
	 * 				The sanitised path info from a HTTP request, i.e. without the leading slash.
	 *
	 * @return	The name of the branch from the path info, unescaped.
	 */
	String getBranch(String repository, String pathInfo)
	{
		if (pathInfo == null || pathInfo.isEmpty() || pathInfo.equals("/")) return "";
		String branch = pathInfo.substring(pathInfo.indexOf(repository) + repository.length() + 1);
		int fs = branch.indexOf('/');
		if (fs > -1) {
			branch = branch.substring(0, fs);
		}
		char c = runtimeManager.getSettings().getChar(Keys.web.forwardSlashCharacter, '/');
		return branch.replace('!', '/').replace(c, '/');
	}

	/**
	 * Find and return the path from a given repository and given branch in a HTTP request path info.
	 * The path string returned is transformed to the form in the repository, i.e. a transformation
	 * of the forward slash character in the URL is reversed.
	 *
	 * @param repository
	 * 				Path of repository, no leading slash, no trailing slash
	 * @param branch
	 * 				Branch name from the repository, i.e. with forward slash character, no leading slash, no trailing slash.
	 * @param pathInfo
	 * 				The sanitised path info from a HTTP request, i.e. without the leading slash.
	 *
	 * @return	The file/folder path part from the path info, in unescaped form.
	 */
	String getPath(String repository, String branch, String pathInfo)
	{
		if (pathInfo == null || pathInfo.isEmpty() || pathInfo.equals("/")) return "";
		String base = repository + "/" + branch;

		// 'repository/' or 'repository/branch' or 'repository/branch/'
		if (pathInfo.equals(base)) {
			return "";
		}
		// I have no idea why 'indexOf(base)' is used, which assumes something could come before 'base' in
		// the pathInfo string. But since it is here, we handle it until we completly refactor the paths used
		// in Gitblit to something sensible.
		// 'leadin/repository/'
		// 'leadin/repository/branch'
		int pathStart = pathInfo.indexOf(base) + base.length();
		// 'leadin/repository/branch/'
		if (pathStart < pathInfo.length() && pathInfo.charAt(pathStart) == '/') pathStart++;
		if (pathInfo.length() == pathStart) return "";
		String path = pathInfo.substring(pathStart);
		// 'leadin/repository/branch/path/'
		if (path.endsWith("/")) {
			path = path.substring(0, path.length() - 1);
		}
		// 'leadin/repository/branch/path'
		char c = runtimeManager.getSettings().getChar(Keys.web.forwardSlashCharacter, '/');
		return path.replace('!', '/').replace(c, '/');
	}

	protected boolean renderIndex() {
		return false;
	}

	/**
	 * Retrieves the specified resource from the specified branch of the
	 * repository.
	 *
	 * @param request
	 * @param response
	 * @throws javax.servlet.ServletException
	 * @throws java.io.IOException
	 */
	private void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		String path = request.getPathInfo();
		if (path.toLowerCase().endsWith(".git")) {
			// forward to url with trailing /
			// this is important for relative pages links
			response.sendRedirect(request.getServletPath() + path + "/");
{'fraction_non_alphanumeric': 0.1205253124338064, 'fraction_numerical': 0.004306997105133093, 'mean_word_length': 4.001412429378531, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7385069', 'n_tokens_mistral': 4463, 'n_tokens_neox': 4133, 'n_words': 1275}