All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jwall.apache.httpd.service.SshFileSystem Maven / Gradle / Ivy

The newest version!
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *   Copyright (C) 2010-2014 Christian Bockermann 
 *    
 *   This file is part of the jwall.org apache-config library. The apache-config library is
 *   a parsing library to handle Apache HTTPD configuration files.
 *
 *   More information and documentation for the jwall-tools can be found at
 *   
 *                      http://www.jwall.org/apache-config
 *   
 *   This program is free software; you can redistribute it and/or modify it under
 *   the terms of the GNU General Public License as published by the Free Software
 *   Foundation; either version 3 of the License, or (at your option) any later version.
 *   
 *   This program is distributed in the hope that it will be useful, but WITHOUT ANY
 *   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 *   FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 * 
 *   You should have received a copy of the GNU General Public License along with this 
 *   program; if not, see .
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package org.jwall.apache.httpd.service;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.Vector;

import org.jwall.apache.httpd.MD5;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.SFTPException;
import ch.ethz.ssh2.SFTPv3Client;
import ch.ethz.ssh2.SFTPv3DirectoryEntry;
import ch.ethz.ssh2.SFTPv3FileAttributes;
import ch.ethz.ssh2.SFTPv3FileHandle;
import ch.ethz.ssh2.Session;


/**
 * 

* This class implements a simple file system wrapper which accesses files on * a remote machine via the SSH protocol. All transfers are thus being encrypted. *

*
 * TODO:  - Implement versioning file system?
 *        - Add support for Pubkey authentication
 * 
* * @author Christian Bockermann <[email protected]> * */ public class SshFileSystem extends AbstractFileSystem { static Logger log = LoggerFactory.getLogger( SshFileSystem.class ); String host; String user; String pass; Connection c; SFTPv3Client sftp; String md5sum = null; /** * Creates a new instance of this class which will establish a connection to the * given host using the specified username and password. * * @param host * @param username * @param password * @throws Exception If the connection failed (no connection, wrong authentication, ...) */ public SshFileSystem( String host, String username, String password ) throws Exception { this.host = host; this.user = username; this.pass = password; c = new Connection( host ); log.info( " connecting to {}", host ); c.connect(); log.info( " authenticating with user '{}', password '{}'", user, "*****" ); c.authenticateWithPassword( user, pass ); init( c ); } public SshFileSystem( Connection c ) throws Exception { if( !c.isAuthenticationComplete() ) throw new Exception( "Connection not fully authenticated!" ); host = c.getHostname(); init( c ); } private void init( Connection c ) throws Exception { sftp = new SFTPv3Client( c ); try { Session s = c.openSession(); s.execCommand( "which md5sum" ); BufferedReader r = new BufferedReader( new InputStreamReader( s.getStdout() ) ); md5sum = r.readLine(); r.close(); s.close(); log.info( "md5sum: {}", md5sum ); } catch (Exception e) { e.printStackTrace(); md5sum = null; } } private SFTPv3FileHandle getHandle( File file ){ try { if( !this.exists( file ) ) return null; SFTPv3FileAttributes stats = sftp.stat( file.getAbsolutePath() ); if( stats == null ) return null; return sftp.openFileRO( file.getAbsolutePath() ); } catch (Exception e) { log.error( "Cannot open file-handle: " + e.getMessage(), e ); //if( log.isDebugEnabled() ) // e.printStackTrace(); return null; } } /** * @see org.jwall.apache.httpd.service.FileSystem#canRead(java.io.File) */ public boolean canRead(File file) { // TODO: This is currently a fake implementation and needs to // be re-implemented to match the file permissions against // this connection's user-id SFTPv3FileHandle handle = getHandle( file ); if( handle == null ) return false; try { SFTPv3FileAttributes att = sftp.fstat( handle ); return att.isRegularFile() || att.isDirectory(); } catch (Exception e) { } return false; } /** * @see org.jwall.apache.httpd.service.FileSystem#exists(java.io.File) */ public boolean exists(File file) { try { for( Object o : sftp.ls( file.getParent() ) ){ SFTPv3DirectoryEntry de = (SFTPv3DirectoryEntry) o; if( de.filename.equals( file.getName() ) ) return true; } return false; } catch (Exception e) { return false; } } /** * @see org.jwall.apache.httpd.service.FileSystem#isDirectory(java.io.File) */ public boolean isDirectory(File file) { try { SFTPv3FileHandle handle = getHandle( file ); if( handle == null ) return false; SFTPv3FileAttributes att = sftp.fstat( handle ); return att.isDirectory(); } catch (Exception e) { e.printStackTrace(); } return false; } /** * @see org.jwall.apache.httpd.service.FileSystem#listFiles(java.io.File) */ public File[] listFiles(File directory) { if( ! isDirectory( directory ) ) return new File[0]; try { Vector vec = sftp.ls( directory.getAbsolutePath() ); File[] results = new File[ vec.size() ]; int i = 0; for( Object o : vec ){ SFTPv3DirectoryEntry de = (SFTPv3DirectoryEntry) o; results[i] = new File( directory + "/" + de.filename ); log.debug( "file[{}] = {}", i, results[i] ); i++; } return results; } catch (Exception e) { e.printStackTrace(); } return new File[0]; } public boolean isSymlink( File file ){ try { for( Object o : sftp.ls( file.getParent() ) ){ SFTPv3DirectoryEntry de = (SFTPv3DirectoryEntry) o; if( de.filename.equals( file.getName() ) ){ log.debug( "Checking {}", de.filename ); return de.longEntry.toLowerCase().startsWith( "l" ); } } } catch (Exception e) { e.printStackTrace(); } return false; } /** * @see org.jwall.apache.httpd.service.FileSystem#openInputStream(java.io.File) */ public InputStream openInputStream( File f ) throws IOException { return new ByteArrayInputStream( this.get( f ) ); } public String getAbsolutePath(File file) { String fp = file.getAbsolutePath(); try { fp = sftp.canonicalPath( file.getAbsolutePath() ); } catch (IOException e) { fp = file.getAbsolutePath(); } if( ! fp.startsWith( "/" ) ) fp = "/" + fp; return "sftp://" + user + ":*****@" + host + ":" + fp; } public long copy(File file, OutputStream out) throws IOException { if( isSymlink( file ) ){ throw new IOException( "Cannot copy symlink to output-stream!" ); } InputStream in = this.openInputStream( file ); byte[] buf = new byte[ 16 * 1024 ]; long total = 0L; int read = in.read( buf ); while( read > 0 ){ out.write( buf, 0, read ); total += read; read = in.read( buf ); } return total; } public URL getURL(File file) { try { return new URL( getAbsolutePath( file ) ); } catch (Exception e) { e.getMessage(); return null; } } /** * @see org.jwall.apache.httpd.service.FileSystem#openOutputStream(java.io.File) */ public OutputStream openOutputStream(File file) throws IOException { return new SshOutputStream( file, sftp, 1024 ); } public byte[] get(File file) throws IOException { log.info( "Retrieving file {}", file ); SCPClient scp = c.createSCPClient(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); scp.get( file.getPath(), baos ); baos.close(); return baos.toByteArray(); } public void put(File file, byte[] content) throws IOException { log.debug( "Sending file {}", file ); SCPClient scp = c.createSCPClient(); String parentDir = file.getParentFile().getAbsolutePath(); log.debug( "invoking 'mkdir -p {}'", parentDir ); Session s = c.openSession(); s.execCommand( "mkdir -p " + parentDir ); s.close(); scp.put( content, file.getName(), file.getParentFile().getAbsolutePath() ); } public class SshInputStream extends InputStream { Logger log = LoggerFactory.getLogger( SshInputStream.class ); File file; SFTPv3FileHandle fh; SFTPv3Client sftp; long offset = 0L; Long read = 0L; Long size = 0L; ByteBuffer buffer; int bufferSize = 1024; int bufferFilled = -1; public SshInputStream( File file, SFTPv3Client sftp, int bufferSize ) throws IOException { this.file = file; this.sftp = sftp; fh = sftp.openFileRO( file.getAbsolutePath() ); SFTPv3FileAttributes fattr = sftp.fstat( fh ); size = fattr.size; buffer = ByteBuffer.allocate( bufferSize ); this.bufferSize = bufferSize; } public void fillBuffer() throws IOException { buffer.clear(); int bytesRead = sftp.read( fh, offset, buffer.array(), buffer.position(), buffer.remaining() ); if( bufferFilled < 0 ) bufferFilled = bytesRead; else bufferFilled += bytesRead; //buffer.flip(); buffer.limit( bytesRead ); log.debug( "Filled buffer with {} bytes, buffer has {} bytes remaining", bytesRead, buffer.remaining() ); } @Override public int read() throws IOException { if( fh == null ) throw new IOException( "No filehandle available - stream already closed?" ); if( offset >= size ) return -1; if( offset >= bufferFilled ) fillBuffer(); if( offset >= bufferFilled || offset >= size ) return -1; int data = buffer.get(); offset++; return data; } /** * @see java.io.InputStream#close() */ @Override public void close() throws IOException { super.close(); this.fh = null; } } public class SshOutputStream extends OutputStream { Logger log = LoggerFactory.getLogger( SshOutputStream.class ); File file; SFTPv3FileHandle fh; SFTPv3Client sftp; long offset = 0L; Long read = 0L; Long size = 0L; ByteBuffer buffer; int bufferSize = 1024; int bufferFilled = -1; public SshOutputStream( File file, SFTPv3Client sftpClient, int bufferSize ) throws IOException { this.file = file; this.sftp = sftpClient; SFTPv3FileAttributes fattr = null; try { fattr = sftp.lstat( file.getAbsolutePath() ); if( fattr.isDirectory() ) throw new IOException( "Cannot open directory '" + file.getAbsolutePath() + "' for writing!" ); } catch (SFTPException se ){ if( se.getMessage().indexOf( "No such file") >= 0 ){ fh = sftp.createFile( file.getAbsolutePath() ); } else { throw se; } } if( fattr != null && fattr.isDirectory() ) throw new IOException( "Cannot open directory '" + file.getAbsolutePath() + "' for writing!" ); if( fattr == null || !fattr.isRegularFile() ){ log.debug( "File does not exist, creating empty file..." ); /* if( file.getParentFile() != null ){ log.info( "Creating parent directory..." ); sftp.mkdir( file.getParent(), 0x755 ); } */ fh = sftp.createFile( file.getAbsolutePath() ); //fh = sftp.openFileRW( file.getAbsolutePath() ); } else { log.debug( "Opening existing file {}", file ); fh = sftp.openFileRW( file.getAbsolutePath() ); } size = fattr.size; this.bufferSize = bufferSize; this.buffer = ByteBuffer.allocate( bufferSize ); } /** * @see java.io.OutputStream#write(int) */ @Override public void write(int arg0) throws IOException { if( fh == null ) throw new IOException( "No filehandle available - stream already closed?" ); if( buffer.remaining() == 0 ){ log.debug( "Buffer full, auto-flushing stream..." ); flush(); } log.debug( "adding byte to buffer: {}", (char) arg0 ); buffer.put( (byte) arg0 ); } /** * @see java.io.OutputStream#close() */ @Override public void close() throws IOException { if( fh == null ) throw new IOException( "No filehandle available - stream already closed?" ); flush(); log.debug( "Closing file-handle" ); sftp.closeFile( fh ); fh = null; } /** * @see java.io.OutputStream#flush() */ @Override public void flush() throws IOException { if( fh == null ) throw new IOException( "No filehandle available - stream already closed?" ); if( buffer.position() == 0 ){ log.debug( "buffer empty, not flushing" ); return; } int out = buffer.position(); StringBuffer s = new StringBuffer(); for( int i = 0; i < out; i++ ){ s.append( (char) buffer.array()[i] ); } log.debug( "Buffer content:\n{}", s ); log.debug( "Writing {} bytes to ssh-conncection", out ); sftp.write( fh, offset, buffer.array(), 0, out ); log.debug( "Clearing buffer..." ); buffer.clear(); offset += out; } } public void createSymlink( String destination, File name) throws IOException { log.debug( "Creating symlink {} -> {}", name.getAbsolutePath(), destination ); sftp.createSymlink( name.getAbsolutePath(), destination ); } public boolean createDirectory(File file) throws IOException { sftp.mkdir( file.getAbsolutePath(), 0x755 ); return isDirectory( file ); } public boolean delete(File file) throws IOException { sftp.rm( file.getAbsolutePath() ); return !this.exists( file ); } public String getRealPath( File f ) throws IOException { File file = f; if( isSymlink( f ) ){ for( Object o : sftp.ls( f.getParent() ) ){ SFTPv3DirectoryEntry de = (SFTPv3DirectoryEntry) o; log.debug( "Directory entry: {}", de.longEntry ); } log.debug( "File {} denotes a symlink!", file ); String realFile = sftp.readLink( file.getAbsolutePath() ); if( realFile.startsWith( "/" ) ) file = new File( realFile ); else { file = new File( f.getParent() + "/" + realFile ); } } else { file = f; } String path = sftp.canonicalPath( file.getAbsolutePath() ); log.debug( " real path is {}", path ); return path; } public String readSymlink(File symlink) { try { log.debug( "File {} denotes a symlink!", symlink ); return sftp.readLink( symlink.getPath() ); } catch (Exception e) { e.printStackTrace(); } return null; } public File resolveSymlink( File symlink ){ if( !isSymlink( symlink ) ) return symlink; try { log.debug( "File {} denotes a symlink!", symlink ); String realFile = sftp.readLink( symlink.getPath() ); if( realFile.startsWith( "/" ) ) symlink = new File( realFile ); else { symlink= new File( symlink.getParent() + "/" + realFile ); } return new File( sftp.canonicalPath( symlink.getPath() ) ); } catch (Exception e) { e.printStackTrace(); } return null; } /** * @see org.jwall.apache.httpd.service.FileSystem#md5(java.io.File) */ public String md5(File file) throws IOException { if( md5sum != null ){ Session s = c.openSession(); s.execCommand( md5sum + " " + file.getPath() ); BufferedReader r = new BufferedReader( new InputStreamReader( s.getStdout() ) ); String cksm = r.readLine(); r.close(); s.close(); if( cksm == null ) throw new IOException( "Failed to compute md5sum!" ); log.debug( "md5sum:\n{}", cksm ); int idx = cksm.indexOf( " " ); if( idx > 0 ) return cksm.substring( 0, idx ); else return cksm; } else { ByteArrayOutputStream out = new ByteArrayOutputStream(); InputStream fis = openInputStream( file ); byte[] buf = new byte[ 16 * 1024 ]; int read = fis.read( buf ); while( read > 0 ){ out.write( buf, 0, read ); read = fis.read( buf ); } out.close(); return MD5.md5( out.toByteArray() ); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy