diff options
author | Shawn O. Pearce <spearce@spearce.org> | 2011-02-06 16:38:02 -0800 |
---|---|---|
committer | Shawn O. Pearce <spearce@spearce.org> | 2011-02-14 18:28:21 -0800 |
commit | 1b7a5a29600e9711548bed267b61e198a058f50b (patch) | |
tree | c9fa99c9707e2a0737f1794f86078b554bf55702 /org.eclipse.jgit | |
parent | 8235b88a4bb453b6bf5dbfbb6f12f25d3c23793e (diff) | |
download | jgit-1b7a5a29600e9711548bed267b61e198a058f50b.tar.gz jgit-1b7a5a29600e9711548bed267b61e198a058f50b.zip |
daemon: Use HTTP's resolver and factory pattern
Using a resolver and factory pattern for the anonymous git:// Daemon
class makes transport.Daemon more useful on non-file storage systems,
or in embedded applications where the caller wants more precise
control over the work tasks constructed within the daemon.
Rather than defining new interfaces, move the existing HTTP ones
into transport.resolver and make them generic on the connection
handle type. For HTTP, continue to use HttpServletRequest, and
for transport.Daemon use DaemonClient.
To remain compatible with transport.Daemon, FileResolver needs to
learn how to use multiple base directories, and how to export any
Repository instance at a fixed name.
Change-Id: I1efa6b2bd7c6567e983fbbf346947238ea2e847e
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
Diffstat (limited to 'org.eclipse.jgit')
12 files changed, 754 insertions, 134 deletions
diff --git a/org.eclipse.jgit/META-INF/MANIFEST.MF b/org.eclipse.jgit/META-INF/MANIFEST.MF index e3152606ca..9d1f957c53 100644 --- a/org.eclipse.jgit/META-INF/MANIFEST.MF +++ b/org.eclipse.jgit/META-INF/MANIFEST.MF @@ -25,6 +25,7 @@ Export-Package: org.eclipse.jgit;version="0.12.0", org.eclipse.jgit.storage.file;version="0.12.0", org.eclipse.jgit.storage.pack;version="0.12.0", org.eclipse.jgit.transport;version="0.12.0", + org.eclipse.jgit.transport.resolver;version="0.12.0", org.eclipse.jgit.treewalk;version="0.12.0", org.eclipse.jgit.treewalk.filter;version="0.12.0", org.eclipse.jgit.util;version="0.12.0", diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties index 4681daa662..e48638397d 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties @@ -384,7 +384,9 @@ rewinding=Rewinding to commit {0} searchForReuse=Finding sources searchForSizes=Getting sizes sequenceTooLargeForDiffAlgorithm=Sequence too large for difference algorithm. +serviceNotEnabledNoName=Service not enabled serviceNotPermitted={0} not permitted +serviceNotPermittedNoName=Service not permitted shortCompressedStreamAt=Short compressed stream at {0} shortReadOfBlock=Short read of block. shortReadOfOptionalDIRCExtensionExpectedAnotherBytes=Short read of optional DIRC extension {0}; expected another {1} bytes within the section. diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java index e29c9bc24e..7276dd26fb 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java @@ -444,7 +444,9 @@ public class JGitText extends TranslationBundle { /***/ public String searchForReuse; /***/ public String searchForSizes; /***/ public String sequenceTooLargeForDiffAlgorithm; + /***/ public String serviceNotEnabledNoName; /***/ public String serviceNotPermitted; + /***/ public String serviceNotPermittedNoName; /***/ public String shortCompressedStreamAt; /***/ public String shortReadOfBlock; /***/ public String shortReadOfOptionalDIRCExtensionExpectedAnotherBytes; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java index 0bc5fb3a2b..feeabb2e61 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/Daemon.java @@ -43,28 +43,26 @@ package org.eclipse.jgit.transport; -import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; +import java.io.OutputStream; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jgit.JGitText; -import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.PersonIdent; import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.lib.RepositoryCache; -import org.eclipse.jgit.lib.RepositoryCache.FileKey; import org.eclipse.jgit.storage.pack.PackConfig; -import org.eclipse.jgit.util.FS; +import org.eclipse.jgit.transport.resolver.ReceivePackFactory; +import org.eclipse.jgit.transport.resolver.RepositoryResolver; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; +import org.eclipse.jgit.transport.resolver.UploadPackFactory; /** Basic daemon for the anonymous <code>git://</code> transport protocol. */ public class Daemon { @@ -79,12 +77,6 @@ public class Daemon { private final ThreadGroup processors; - private volatile boolean exportAll; - - private Map<String, Repository> exports; - - private Collection<File> exportBase; - private boolean run; private Thread acceptThread; @@ -93,6 +85,12 @@ public class Daemon { private PackConfig packConfig; + private volatile RepositoryResolver<DaemonClient> repositoryResolver; + + private volatile UploadPackFactory<DaemonClient> uploadPackFactory; + + private volatile ReceivePackFactory<DaemonClient> receivePackFactory; + /** Configure a daemon to listen on any available network port. */ public Daemon() { this(null); @@ -107,10 +105,40 @@ public class Daemon { */ public Daemon(final InetSocketAddress addr) { myAddress = addr; - exports = new ConcurrentHashMap<String, Repository>(); - exportBase = new CopyOnWriteArrayList<File>(); processors = new ThreadGroup("Git-Daemon"); + repositoryResolver = (RepositoryResolver<DaemonClient>) RepositoryResolver.NONE; + + uploadPackFactory = new UploadPackFactory<DaemonClient>() { + public UploadPack create(DaemonClient req, Repository db) + throws ServiceNotEnabledException, + ServiceNotAuthorizedException { + UploadPack up = new UploadPack(db); + up.setTimeout(getTimeout()); + up.setPackConfig(getPackConfig()); + return up; + } + }; + + receivePackFactory = new ReceivePackFactory<DaemonClient>() { + public ReceivePack create(DaemonClient req, Repository db) + throws ServiceNotEnabledException, + ServiceNotAuthorizedException { + ReceivePack rp = new ReceivePack(db); + + InetAddress peer = req.getRemoteAddress(); + String host = peer.getCanonicalHostName(); + if (host == null) + host = peer.getHostAddress(); + String name = "anonymous"; + String email = name + "@" + host; + rp.setRefLogIdent(new PersonIdent(name, email)); + rp.setTimeout(getTimeout()); + + return rp; + } + }; + services = new DaemonService[] { new DaemonService("upload-pack", "uploadpack") { { @@ -119,12 +147,13 @@ public class Daemon { @Override protected void execute(final DaemonClient dc, - final Repository db) throws IOException { - final UploadPack rp = new UploadPack(db); - final InputStream in = dc.getInputStream(); - rp.setTimeout(Daemon.this.getTimeout()); - rp.setPackConfig(Daemon.this.packConfig); - rp.upload(in, dc.getOutputStream(), null); + final Repository db) throws IOException, + ServiceNotEnabledException, + ServiceNotAuthorizedException { + UploadPack up = uploadPackFactory.create(dc, db); + InputStream in = dc.getInputStream(); + OutputStream out = dc.getOutputStream(); + up.upload(in, out, null); } }, new DaemonService("receive-pack", "receivepack") { { @@ -133,18 +162,13 @@ public class Daemon { @Override protected void execute(final DaemonClient dc, - final Repository db) throws IOException { - final InetAddress peer = dc.getRemoteAddress(); - String host = peer.getCanonicalHostName(); - if (host == null) - host = peer.getHostAddress(); - final ReceivePack rp = new ReceivePack(db); - final InputStream in = dc.getInputStream(); - final String name = "anonymous"; - final String email = name + "@" + host; - rp.setRefLogIdent(new PersonIdent(name, email)); - rp.setTimeout(Daemon.this.getTimeout()); - rp.receive(in, dc.getOutputStream(), null); + final Repository db) throws IOException, + ServiceNotEnabledException, + ServiceNotAuthorizedException { + ReceivePack rp = receivePackFactory.create(dc, db); + InputStream in = dc.getInputStream(); + OutputStream out = dc.getOutputStream(); + rp.receive(in, out, null); } } }; } @@ -173,62 +197,6 @@ public class Daemon { return null; } - /** - * @return false if <code>git-daemon-export-ok</code> is required to export - * a repository; true if <code>git-daemon-export-ok</code> is - * ignored. - * @see #setExportAll(boolean) - */ - public boolean isExportAll() { - return exportAll; - } - - /** - * Set whether or not to export all repositories. - * <p> - * If false (the default), repositories must have a - * <code>git-daemon-export-ok</code> file to be accessed through this - * daemon. - * <p> - * If true, all repositories are available through the daemon, whether or - * not <code>git-daemon-export-ok</code> exists. - * - * @param export - */ - public void setExportAll(final boolean export) { - exportAll = export; - } - - /** - * 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, final Repository db) { - if (!name.endsWith(Constants.DOT_GIT_EXT)) - name = name + Constants.DOT_GIT_EXT; - exports.put(name, db); - RepositoryCache.register(db); - } - - /** - * Recursively export all Git repositories within a directory. - * - * @param dir - * the directory to export. This directory must not itself be a - * git repository, but any directory below it which has a file - * named <code>git-daemon-export-ok</code> will be published. - */ - public void exportDirectory(final File dir) { - exportBase.add(dir); - } - /** @return timeout (in seconds) before aborting an IO operation. */ public int getTimeout() { return timeout; @@ -246,6 +214,11 @@ public class Daemon { timeout = seconds; } + /** @return configuration controlling packing, may be null. */ + public PackConfig getPackConfig() { + return packConfig; + } + /** * Set the configuration used by the pack generator. * @@ -258,6 +231,44 @@ public class Daemon { } /** + * Set the resolver used to locate a repository by name. + * + * @param resolver + * the resolver instance. + */ + public void setRepositoryResolver(RepositoryResolver<DaemonClient> resolver) { + repositoryResolver = resolver; + } + + /** + * Set the factory to construct and configure per-request UploadPack. + * + * @param factory + * the factory. If null upload-pack is disabled. + */ + @SuppressWarnings("unchecked") + public void setUploadPackFactory(UploadPackFactory<DaemonClient> factory) { + if (factory != null) + uploadPackFactory = factory; + else + uploadPackFactory = (UploadPackFactory<DaemonClient>) UploadPackFactory.DISABLED; + } + + /** + * Set the factory to construct and configure per-request ReceivePack. + * + * @param factory + * the factory. If null receive-pack is disabled. + */ + @SuppressWarnings("unchecked") + public void setReceivePackFactory(ReceivePackFactory<DaemonClient> factory) { + if (factory != null) + receivePackFactory = factory; + else + receivePackFactory = (ReceivePackFactory<DaemonClient>) ReceivePackFactory.DISABLED; + } + + /** * Start this daemon on a background thread. * * @throws IOException @@ -325,6 +336,12 @@ public class Daemon { public void run() { try { dc.execute(s); + } catch (RepositoryNotFoundException e) { + // Ignored. Client cannot use this repository. + } catch (ServiceNotEnabledException e) { + // Ignored. Client cannot use this repository. + } catch (ServiceNotAuthorizedException e) { + // Ignored. Client cannot use this repository. } catch (IOException e) { // Ignore unexpected IO exceptions from clients e.printStackTrace(); @@ -352,7 +369,7 @@ public class Daemon { return null; } - Repository openRepository(String name) { + Repository openRepository(DaemonClient client, String name) { // Assume any attempt to use \ was by a Windows client // and correct to the more typical / used in Git URIs. // @@ -363,48 +380,20 @@ public class Daemon { if (!name.startsWith("/")) return null; - // Forbid Windows UNC paths as they might escape the base - // - if (name.startsWith("//")) + try { + return repositoryResolver.open(client, name.substring(1)); + } catch (RepositoryNotFoundException e) { + // null signals it "wasn't found", which is all that is suitable + // for the remote client to know. return null; - - // Forbid funny paths which contain an up-reference, they - // might be trying to escape and read /../etc/password. - // - if (name.contains("/../")) + } catch (ServiceNotAuthorizedException e) { + // null signals it "wasn't found", which is all that is suitable + // for the remote client to know. return null; - name = name.substring(1); - - Repository db; - db = exports.get(name.endsWith(Constants.DOT_GIT_EXT) ? name : name - + Constants.DOT_GIT_EXT); - if (db != null) { - db.incrementOpen(); - return db; - } - - for (final File baseDir : exportBase) { - final File gitdir = FileKey.resolve(new File(baseDir, name), FS.DETECTED); - if (gitdir != null && canExport(gitdir)) - return openRepository(gitdir); - } - return null; - } - - private static Repository openRepository(final File gitdir) { - try { - return RepositoryCache.open(FileKey.exact(gitdir, FS.DETECTED)); - } catch (IOException err) { + } catch (ServiceNotEnabledException e) { // null signals it "wasn't found", which is all that is suitable // for the remote client to know. return null; } } - - private boolean canExport(final File d) { - if (isExportAll()) { - return true; - } - return new File(d, "git-daemon-export-ok").exists(); - } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonClient.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonClient.java index 0b8de03439..23e3379f91 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonClient.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonClient.java @@ -51,6 +51,9 @@ import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; + /** Active network client of {@link Daemon}. */ public class DaemonClient { private final Daemon daemon; @@ -89,8 +92,8 @@ public class DaemonClient { return rawOut; } - void execute(final Socket sock) - throws IOException { + void execute(final Socket sock) throws IOException, + ServiceNotEnabledException, ServiceNotAuthorizedException { rawIn = new BufferedInputStream(sock.getInputStream()); rawOut = new BufferedOutputStream(sock.getOutputStream()); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java index 2b94957983..e88b4abb7a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/DaemonService.java @@ -49,6 +49,8 @@ import java.io.IOException; import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Config.SectionParser; +import org.eclipse.jgit.transport.resolver.ServiceNotAuthorizedException; +import org.eclipse.jgit.transport.resolver.ServiceNotEnabledException; /** A service exposed by {@link Daemon} over anonymous <code>git://</code>. */ public abstract class DaemonService { @@ -125,9 +127,10 @@ public abstract class DaemonService { } void execute(final DaemonClient client, final String commandLine) - throws IOException { + throws IOException, ServiceNotEnabledException, + ServiceNotAuthorizedException { final String name = commandLine.substring(command.length() + 1); - final Repository db = client.getDaemon().openRepository(name); + Repository db = client.getDaemon().openRepository(client, name); if (db == null) return; try { @@ -145,5 +148,6 @@ public abstract class DaemonService { } abstract void execute(DaemonClient client, Repository db) - throws IOException; + throws IOException, ServiceNotEnabledException, + ServiceNotAuthorizedException; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java new file mode 100644 index 0000000000..1d4f2beb18 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/FileResolver.java @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.transport.resolver; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +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.util.FS; + +/** + * Default resolver serving from the local filesystem. + * + * @param <C> + * type of connection + */ +public class FileResolver<C> implements RepositoryResolver<C> { + private volatile boolean exportAll; + + private final Map<String, Repository> exports; + + private final Collection<File> exportBase; + + /** Initialize an empty file based resolver. */ + public FileResolver() { + exports = new ConcurrentHashMap<String, Repository>(); + exportBase = new CopyOnWriteArrayList<File>(); + } + + /** + * Create a new resolver for the given path. + * + * @param basePath + * the base path all repositories are rooted under. + * @param exportAll + * if true, exports all repositories, ignoring the check for the + * {@code git-daemon-export-ok} files. + */ + public FileResolver(final File basePath, final boolean exportAll) { + this(); + exportDirectory(basePath); + setExportAll(exportAll); + } + + 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; + } + + for (File base : exportBase) { + File dir = FileKey.resolve(new File(base, name), FS.DETECTED); + if (dir == null) + continue; + + try { + FileKey key = FileKey.exact(dir, FS.DETECTED); + db = RepositoryCache.open(key, true); + } catch (IOException e) { + throw new RepositoryNotFoundException(name, e); + } + + try { + if (isExportOk(req, name, db)) { + // We have to leak the open count to the caller, they + // are responsible for closing the repository if we + // complete successfully. + return db; + } else + throw new ServiceNotEnabledException(); + + } catch (RuntimeException e) { + db.close(); + throw new RepositoryNotFoundException(name, e); + + } catch (IOException e) { + db.close(); + throw new RepositoryNotFoundException(name, e); + + } catch (ServiceNotEnabledException e) { + db.close(); + throw e; + } + } + + if (exportBase.size() == 1) { + File dir = new File(exportBase.iterator().next(), name); + throw new RepositoryNotFoundException(name, + new RepositoryNotFoundException(dir)); + } + + throw new RepositoryNotFoundException(name); + } + + /** + * @return false if <code>git-daemon-export-ok</code> is required to export + * a repository; true if <code>git-daemon-export-ok</code> is + * ignored. + * @see #setExportAll(boolean) + */ + public boolean isExportAll() { + return exportAll; + } + + /** + * Set whether or not to export all repositories. + * <p> + * If false (the default), repositories must have a + * <code>git-daemon-export-ok</code> file to be accessed through this + * daemon. + * <p> + * If true, all repositories are available through the daemon, whether or + * not <code>git-daemon-export-ok</code> exists. + * + * @param export + */ + public void setExportAll(final boolean export) { + exportAll = export; + } + + /** + * 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); + } + + /** + * Recursively export all Git repositories within a directory. + * + * @param dir + * the directory to export. This directory must not itself be a + * git repository, but any directory below it which has a file + * named <code>git-daemon-export-ok</code> will be published. + */ + public void exportDirectory(final File dir) { + exportBase.add(dir); + } + + /** + * Check if this repository can be served. + * <p> + * The default implementation of this method returns true only if either + * {@link #isExportAll()} is true, or the {@code git-daemon-export-ok} file + * is present in the repository's directory. + * + * @param req + * the current HTTP request. + * @param repositoryName + * name of the repository, as present in the URL. + * @param db + * the opened repository instance. + * @return true if the repository is accessible; false if not. + * @throws IOException + * the repository could not be accessed, the caller will claim + * the repository does not exist. + */ + protected boolean isExportOk(C req, String repositoryName, Repository db) + throws IOException { + if (isExportAll()) + return true; + else if (db.getDirectory() != null) + return new File(db.getDirectory(), "git-daemon-export-ok").exists(); + else + return false; + } + + 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 + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java new file mode 100644 index 0000000000..4cf49f5531 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ReceivePackFactory.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.transport.resolver; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.ReceivePack; + +/** + * Create and configure {@link ReceivePack} service instance. + * + * @param <C> + * type of connection + */ +public interface ReceivePackFactory<C> { + /** A factory disabling the ReceivePack service for all repositories */ + public static final ReceivePackFactory<?> DISABLED = new ReceivePackFactory<Object>() { + public ReceivePack create(Object req, Repository db) + throws ServiceNotEnabledException { + throw new ServiceNotEnabledException(); + } + }; + + /** + * Create and configure a new ReceivePack instance for a repository. + * + * @param req + * current request, in case information from the request may help + * configure the ReceivePack instance. + * @param db + * the repository the receive would write into. + * @return the newly configured ReceivePack instance, must not be null. + * @throws ServiceNotEnabledException + * this factory refuses to create the instance because it is not + * allowed on the target repository, by any user. + * @throws ServiceNotAuthorizedException + * this factory refuses to create the instance for this HTTP + * request and repository, such as due to a permission error. + */ + ReceivePack create(C req, Repository db) throws ServiceNotEnabledException, + ServiceNotAuthorizedException; +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java new file mode 100644 index 0000000000..b2354193e1 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/RepositoryResolver.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.transport.resolver; + +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.Repository; + +/** + * Locate a Git {@link Repository} by name from the URL. + * + * @param <C> + * type of connection. + */ +public interface RepositoryResolver<C> { + /** Resolver configured to open nothing. */ + public static final RepositoryResolver<?> NONE = new RepositoryResolver<Object>() { + public Repository open(Object req, String name) + throws RepositoryNotFoundException { + throw new RepositoryNotFoundException(name); + } + }; + + /** + * Locate and open a reference to a {@link Repository}. + * <p> + * The caller is responsible for closing the returned Repository. + * + * @param req + * the current request, may be used to inspect session state + * including cookies or user authentication. + * @param name + * name of the repository, as parsed out of the URL. + * @return the opened repository instance, never null. + * @throws RepositoryNotFoundException + * the repository does not exist or the name is incorrectly + * formatted as a repository name. + * @throws ServiceNotAuthorizedException + * the repository exists, but HTTP access is not allowed for the + * current user. + * @throws ServiceNotEnabledException + * the repository exists, but HTTP access is not allowed on the + * target repository, by any user. + */ + Repository open(C req, String name) throws RepositoryNotFoundException, + ServiceNotAuthorizedException, ServiceNotEnabledException; +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotAuthorizedException.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotAuthorizedException.java new file mode 100644 index 0000000000..3e2047b085 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotAuthorizedException.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.transport.resolver; + +import org.eclipse.jgit.JGitText; + +/** Indicates the request service is not authorized for current user. */ +public class ServiceNotAuthorizedException extends Exception { + private static final long serialVersionUID = 1L; + + /** Indicates the request service is not available. */ + public ServiceNotAuthorizedException() { + super(JGitText.get().serviceNotPermittedNoName); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotEnabledException.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotEnabledException.java new file mode 100644 index 0000000000..ac607f9b0e --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/ServiceNotEnabledException.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.transport.resolver; + +import org.eclipse.jgit.JGitText; + +/** Indicates the request service is not enabled on a repository. */ +public class ServiceNotEnabledException extends Exception { + private static final long serialVersionUID = 1L; + + /** Indicates the request service is not available. */ + public ServiceNotEnabledException() { + super(JGitText.get().serviceNotEnabledNoName); + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java new file mode 100644 index 0000000000..f0d2ba865b --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/resolver/UploadPackFactory.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2009-2010, Google Inc. + * and other copyright owners as documented in the project's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package org.eclipse.jgit.transport.resolver; + +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.transport.UploadPack; + +/** + * Create and configure {@link UploadPack} service instance. + * + * @param <C> + * the connection type + */ +public interface UploadPackFactory<C> { + /** A factory disabling the UploadPack service for all repositories. */ + public static final UploadPackFactory<?> DISABLED = new UploadPackFactory<Object>() { + public UploadPack create(Object req, Repository db) + throws ServiceNotEnabledException { + throw new ServiceNotEnabledException(); + } + }; + + /** + * Create and configure a new UploadPack instance for a repository. + * + * @param req + * current request, in case information from the request may help + * configure the UploadPack instance. + * @param db + * the repository the upload would read from. + * @return the newly configured UploadPack instance, must not be null. + * @throws ServiceNotEnabledException + * this factory refuses to create the instance because it is not + * allowed on the target repository, by any user. + * @throws ServiceNotAuthorizedException + * this factory refuses to create the instance for this HTTP + * request and repository, such as due to a permission error. + */ + UploadPack create(C req, Repository db) throws ServiceNotEnabledException, + ServiceNotAuthorizedException; +} |