@ -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 .
*
@ -257,6 +230,44 @@ public class Daemon {
this . packConfig = pc ;
}
/ * *
* 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 .
*
@ -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 ( ) ;
}
}