package org.onexus.resource.manager.internal.ws.git;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.RepositoryCache;
import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.transport.resolver.RepositoryResolver;
import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException;
import org.eclipse.jgit.util.FS;
import org.onexus.resource.api.IResourceManager;
import org.onexus.resource.api.ORI;
import org.onexus.resource.api.Project;
import org.onexus.resource.manager.internal.ResourceManager;
import org.onexus.resource.manager.internal.providers.AbstractProjectProvider;
import org.onexus.resource.manager.internal.providers.GitProjectProvider;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Default resolver serving from the local filesystem.
*
* @param <C>
* type of connection
*/
public class FileResolver<C> implements RepositoryResolver<C> {
private ResourceManager resourceManager;
private final Map<String, Repository> exports;
/** Initialize an empty file based resolver. */
public FileResolver(ResourceManager resourceManager) {
this.resourceManager = resourceManager;
this.exports = new ConcurrentHashMap<String, Repository>();
}
public Repository open(final C req, final String name)
throws RepositoryNotFoundException, ServiceNotEnabledException {
if (isUnreasonableName(name))
throw new RepositoryNotFoundException(name);
Repository db = exports.get(nameWithDotGit(name));
if (db != null) {
db.incrementOpen();
return db;
}
ORI projectORI = null;
for (Project project : resourceManager.getProjects()) {
if (project.getName().equals(name)) {
projectORI = project.getORI();
break;
}
}
if (projectORI == null) {
throw new RepositoryNotFoundException(name);
}
AbstractProjectProvider projectProvider = resourceManager.getProjectsContainer().getProjectProvider(projectORI.toString());
if (!(projectProvider instanceof GitProjectProvider)) {
throw new RepositoryNotFoundException(name);
}
File projectFolder = projectProvider.getProjectFolder();
File dir = FileKey.resolve(projectFolder, FS.DETECTED);
if (dir == null) {
throw new RepositoryNotFoundException(name);
}
try {
FileKey key = FileKey.exact(dir, FS.DETECTED);
db = RepositoryCache.open(key, true);
} catch (IOException e) {
throw new RepositoryNotFoundException(name, e);
}
return db;
}
/**
* Add a single repository to the set that is exported by this daemon.
* <p>
* The existence (or lack-thereof) of <code>git-daemon-export-ok</code> is
* ignored by this method. The repository is always published.
*
* @param name
* name the repository will be published under.
* @param db
* the repository instance.
*/
public void exportRepository(String name, Repository db) {
exports.put(nameWithDotGit(name), db);
}
private static String nameWithDotGit(String name) {
if (name.endsWith(Constants.DOT_GIT_EXT))
return name;
return name + Constants.DOT_GIT_EXT;
}
private static boolean isUnreasonableName(final String name) {
if (name.length() == 0)
return true; // no empty paths
if (name.indexOf('\\') >= 0)
return true; // no windows/dos style paths
if (new File(name).isAbsolute())
return true; // no absolute paths
if (name.startsWith("../"))
return true; // no "l../etc/passwd"
if (name.contains("/../"))
return true; // no "foo/../etc/passwd"
if (name.contains("/./"))
return true; // "foo/./foo" is insane to ask
if (name.contains("//"))
return true; // double slashes is sloppy, don't use it
return false; // is a reasonable name
}
}