package com.pastdev.jsch.nio.file; import static com.pastdev.jsch.nio.file.UnixSshFileSystemProvider.PATH_SEPARATOR; import static com.pastdev.jsch.nio.file.UnixSshFileSystemProvider.PATH_SEPARATOR_STRING; import java.io.IOException; import java.net.URI; import java.nio.file.FileStore; import java.nio.file.Path; import java.nio.file.PathMatcher; import java.nio.file.WatchService; import java.nio.file.attribute.GroupPrincipal; import java.nio.file.attribute.UserPrincipal; import java.nio.file.attribute.UserPrincipalLookupService; import java.nio.file.attribute.UserPrincipalNotFoundException; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import com.jcraft.jsch.JSchException; public class UnixSshFileSystem extends AbstractSshFileSystem { private UnixSshPath defaultDirectory; private UnixSshPath rootDirectory; public UnixSshFileSystem( UnixSshFileSystemProvider provider, URI uri, Map<String, ?> environment ) throws IOException { super( provider, uri, environment ); this.defaultDirectory = new UnixSshPath( this, uri.getPath() ); if ( !defaultDirectory.isAbsolute() ) { throw new RuntimeException( "default directory must be absolute" ); } rootDirectory = new UnixSshPath( this, PATH_SEPARATOR_STRING ); } @Override public void close() throws IOException { getCommandRunner().close(); provider().removeFileSystem( this ); } UnixSshPath getDefaultDirectory() { return defaultDirectory; } @Override public Iterable<FileStore> getFileStores() { // TODO Auto-generated method stub throw new UnsupportedOperationException(); } @Override public UnixSshPath getPath( String first, String... more ) { if ( more == null || more.length == 0 ) return new UnixSshPath( this, first ); StringBuilder builder = new StringBuilder( first ); for ( String part : more ) { builder.append( PATH_SEPARATOR ) .append( part ); } return new UnixSshPath( this, builder.toString() ); } @Override public PathMatcher getPathMatcher( String syntaxAndPattern ) { int firstColon = syntaxAndPattern.indexOf( ':' ); if ( firstColon == -1 ) { throw new IllegalArgumentException( "must be of the form 'syntax:pattern'" ); } String syntax = syntaxAndPattern.substring( 0, firstColon ); String patternString = syntaxAndPattern.substring( firstColon + 1 ); if ( syntax.equalsIgnoreCase( "glob" ) ) { StringBuilder builder = new StringBuilder().append( '^' ); char[] chars = patternString.toCharArray(); for ( int i = 0; i < chars.length; i++ ) { char c = chars[i]; if ( c == '*' ) { if ( chars[i + 1] == '*' ) { builder.append( ".*" ); i++; } else { builder.append( "[^/]*" ); } } else if ( c == '?' ) { builder.append( '.' ); } else if ( c == '{' ) { builder.append( "(?:" ); while ( true ) { if ( ++i < chars.length ) { c = chars[i]; } else { throw new IllegalArgumentException( "invalid glob '" + patternString + "'" ); } if ( c == ',' ) { builder.append( '|' ); } else if ( c == '}' ) { break; } else { builder.append( c ); } } builder.append( ")" ); } else if ( c == '[' ) { builder.append( '[' ); boolean first = true; boolean caratEncountered = false; while ( true ) { if ( ++i < chars.length ) { c = chars[i]; } else { throw new IllegalArgumentException( "invalid glob '" + patternString + "', unclosed range" ); } if ( first ) { if ( c == '!' ) { builder.append( "^" ); } else if ( c == '^' ) { caratEncountered = true; } else { builder.append( c ); } first = false; continue; } if ( c == ']' ) { break; } else if ( c == '\\' ) { builder.append( "\\\\" ); } else { builder.append( c ); } } if ( caratEncountered ) builder.append( '^' ); builder.append( ']' ); } else if ( c == '\\' ) { if ( ++i < chars.length ) { builder.append( chars[i] ); } else { throw new IllegalArgumentException( "invalid glob '" + patternString + "'" ); } } else if ( c == '.' ) { builder.append( "\\." ); } else if ( c == '+' ) { builder.append( "\\+" ); } else if ( c == '(' ) { builder.append( "\\(" ); } else { builder.append( c ); } } patternString = builder.append( '$' ).toString(); } else if ( syntax.equalsIgnoreCase( "regex" ) ) { // do nothing, pattern string is already regex } else { throw new UnsupportedOperationException( "i dont have any clue what '" + syntax + "' is supposed to mean" ); } final Pattern pattern = Pattern.compile( patternString ); return new PathMatcher() { @Override public boolean matches( Path path ) { return pattern.matcher( path.toString() ).matches(); } }; } @Override public Iterable<Path> getRootDirectories() { return Collections.unmodifiableList( Arrays.asList( new Path[] { rootDirectory } ) ); } @Override public String getSeparator() { return PATH_SEPARATOR_STRING; } @Override public UserPrincipalLookupService getUserPrincipalLookupService() { return new UserPrincipalLookupService() { @Override public UserPrincipal lookupPrincipalByName( String user ) throws IOException { try { if ( getCommandRunner().execute( "id " + user ).getExitCode() == 0 ) { return new StandardUserPrincipal( user ); } else { throw new UserPrincipalNotFoundException( user + " does not exist" ); } } catch ( JSchException e ) { throw new IOException( e ); } } @Override public GroupPrincipal lookupPrincipalByGroupName( String group ) throws IOException { try { // I don't like this, but don't have a better way right // now... // Should be pretty safe in most instances if ( getCommandRunner().execute( "egrep -i \"^" + group + "\" /etc/group" ).getExitCode() == 0 ) { return new StandardGroupPrincipal( group ); } else { throw new UserPrincipalNotFoundException( group + " does not exist" ); } } catch ( JSchException e ) { throw new IOException( e ); } } }; } @Override public boolean isOpen() { // command runner will just open a new session if the current session is // closed, so it is effectively always open return true; } @Override public boolean isReadOnly() { // TODO Auto-generated method stub return false; } @Override public WatchService newWatchService() throws IOException { if ( getBooleanFromEnvironment( "watchservice.inotify" ) ) { return UnixSshFileSystemWatchService.inotifyWatchService(); } else { // TODO make sure these values are set in environment, or get good // defaults return UnixSshFileSystemWatchService.pollingWatchService( getLongFromEnvironment( "watchservice.polling.interval" ), getTimeUnitFromEnvironment( "watchservice.polling.timeunit" ) ); } } @Override public UnixSshFileSystemProvider provider() { return (UnixSshFileSystemProvider)super.provider(); } @Override public Set<String> supportedFileAttributeViews() { Set<String> supportedFileAttributeViews = new HashSet<String>(); supportedFileAttributeViews.addAll( Arrays.asList( new String[] { "basic", "posix" } ) ); return Collections.unmodifiableSet( supportedFileAttributeViews ); } }