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

org.pentaho.di.job.entries.sftp.SFTPClient Maven / Gradle / Ivy

The newest version!
/*! ******************************************************************************
 *
 * Pentaho Data Integration
 *
 * Copyright (C) 2002-2018 by Hitachi Vantara : http://www.pentaho.com
 *
 *******************************************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ******************************************************************************/

package org.pentaho.di.job.entries.sftp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;

import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileType;
import org.apache.commons.vfs2.FileUtil;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.util.Utils;
import org.pentaho.di.core.exception.KettleFileException;
import org.pentaho.di.core.exception.KettleJobException;
import org.pentaho.di.core.vfs.KettleVFS;

import com.google.common.annotations.VisibleForTesting;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Proxy;
import com.jcraft.jsch.ProxyHTTP;
import com.jcraft.jsch.ProxySOCKS5;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;

public class SFTPClient {

  private static final String COMPRESSION_S2C = "compression.s2c";
  private static final String COMPRESSION_C2S = "compression.c2s";

  public static final String PROXY_TYPE_SOCKS5 = "SOCKS5";
  public static final String PROXY_TYPE_HTTP = "HTTP";
  public static final String HTTP_DEFAULT_PORT = "80";
  public static final String SOCKS5_DEFAULT_PORT = "1080";
  public static final int SSH_DEFAULT_PORT = 22;

  // -D parameter telling whether we should use GSSAPI authentication or not
  static final String ENV_PARAM_USERAUTH_GSSAPI = "userauth.gssapi.enabled";

  private static final String PREFERRED_AUTH_CONFIG_NAME = "PreferredAuthentications";
  private static final String PREFERRED_AUTH_DEFAULT = "publickey,keyboard-interactive,password";
  // adding GSSAPI to be the last one
  private static final String PREFERRED_AUTH_WITH_GSSAPI = PREFERRED_AUTH_DEFAULT + ",gssapi-with-mic";

  private InetAddress serverIP;
  private int serverPort;
  private String userName;
  private String password;
  private String prvkey = null; // Private key
  private String passphrase = null; // Empty passphrase for now
  private String compression = null;

  private Session s;
  private ChannelSftp c;

  /**
   * Init Helper Class with connection settings
   *
   * @param serverIP
   *          IP address of remote server
   * @param serverPort
   *          port of remote server
   * @param userName
   *          username of remote server
   * @throws KettleJobException
   */
  public SFTPClient( InetAddress serverIP, int serverPort, String userName ) throws KettleJobException {
    this( serverIP, serverPort, userName, null, null );
  }

  /**
   * Init Helper Class with connection settings
   *
   * @param serverIP
   *          IP address of remote server
   * @param serverPort
   *          port of remote server
   * @param userName
   *          username of remote server
   * @param privateKeyFilename
   *          filename of private key
   * @throws KettleJobException
   */
  public SFTPClient( InetAddress serverIP, int serverPort, String userName, String privateKeyFilename ) throws KettleJobException {
    this( serverIP, serverPort, userName, privateKeyFilename, null );
  }

  /**
   * Init Helper Class with connection settings
   *
   * @param serverIP
   *          IP address of remote server
   * @param serverPort
   *          port of remote server
   * @param userName
   *          username of remote server
   * @param privateKeyFilename
   *          filename of private key
   * @param passPhrase
   *          passphrase
   * @throws KettleJobException
   */
  public SFTPClient( InetAddress serverIP, int serverPort, String userName, String privateKeyFilename,
    String passPhrase ) throws KettleJobException {

    if ( serverIP == null || serverPort < 0 || userName == null || userName.equals( "" ) ) {
      throw new KettleJobException(
        "For a SFTP connection server name and username must be set and server port must be greater than zero." );
    }

    this.serverIP = serverIP;
    this.serverPort = serverPort;
    this.userName = userName;

    JSch jsch = createJSch();
    try {
      if ( !Utils.isEmpty( privateKeyFilename ) ) {
        // We need to use private key authentication
        this.prvkey = privateKeyFilename;
        byte[] passphrasebytes = new byte[0];
        if ( !Utils.isEmpty( passPhrase ) ) {
          // Set passphrase
          this.passphrase = passPhrase;
          passphrasebytes = GetPrivateKeyPassPhrase().getBytes();
        }
        jsch.addIdentity( getUserName(), FileUtil.getContent( KettleVFS.getFileObject( prvkey ) ), // byte[] privateKey
          null, // byte[] publicKey
          passphrasebytes ); // byte[] passPhrase
      }
      s = jsch.getSession( userName, serverIP.getHostAddress(), serverPort );
      s.setConfig( PREFERRED_AUTH_CONFIG_NAME, getPreferredAuthentications() );
    } catch ( IOException e ) {
      throw new KettleJobException( e );
    } catch ( KettleFileException e ) {
      throw new KettleJobException( e );
    } catch ( JSchException e ) {
      throw new KettleJobException( e );
    }
  }

  public void login( String password ) throws KettleJobException {
    this.password = password;

    s.setPassword( this.getPassword() );
    try {
      java.util.Properties config = new java.util.Properties();
      config.put( "StrictHostKeyChecking", "no" );
      // set compression property
      // zlib, none
      String compress = getCompression();
      if ( compress != null ) {
        config.put( COMPRESSION_S2C, compress );
        config.put( COMPRESSION_C2S, compress );
      }
      s.setConfig( config );
      s.connect();
      Channel channel = s.openChannel( "sftp" );
      channel.connect();
      c = (ChannelSftp) channel;
    } catch ( JSchException e ) {
      throw new KettleJobException( e );
    }
  }

  public void chdir( String dirToChangeTo ) throws KettleJobException {
    try {
      c.cd( dirToChangeTo.replace( "\\\\", "/" ).
        replace( "\\", "/" ) );
    } catch ( SftpException e ) {
      throw new KettleJobException( e );
    }
  }

  public String[] dir() throws KettleJobException {
    String[] fileList = null;

    try {
      java.util.Vector v = c.ls( "." );
      java.util.Vector o = new java.util.Vector();
      if ( v != null ) {
        for ( int i = 0; i < v.size(); i++ ) {
          Object obj = v.elementAt( i );
          if ( obj != null && obj instanceof com.jcraft.jsch.ChannelSftp.LsEntry ) {
            LsEntry lse = (com.jcraft.jsch.ChannelSftp.LsEntry) obj;
            if ( !lse.getAttrs().isDir() ) {
              o.add( lse.getFilename() );
            }
          }
        }
      }
      if ( o.size() > 0 ) {
        fileList = new String[o.size()];
        o.copyInto( fileList );
      }
    } catch ( SftpException e ) {
      throw new KettleJobException( e );
    }

    return fileList;
  }

  public void get( FileObject localFile, String remoteFile ) throws KettleJobException {
    OutputStream localStream = null;
    try {
      localStream = KettleVFS.getOutputStream( localFile, false );
      c.get( remoteFile, localStream );
    } catch ( SftpException e ) {
      throw new KettleJobException( e );
    } catch ( IOException e ) {
      throw new KettleJobException( e );
    } finally {
      if ( localStream != null ) {
        try {
          localStream.close();
        } catch ( IOException ignore ) {
          // Ignore any IOException, as we're trying to close the stream anyways
        }
      }
    }
  }

  /**
   * @deprecated use {@link #get(FileObject, String)}
   * @param localFilePath
   * @param remoteFile
   * @throws KettleJobException
   */
  @Deprecated
  public void get( String localFilePath, String remoteFile ) throws KettleJobException {
    int mode = ChannelSftp.OVERWRITE;
    try {
      c.get( remoteFile, localFilePath, null, mode );
    } catch ( SftpException e ) {
      throw new KettleJobException( e );
    }
  }

  public String pwd() throws KettleJobException {
    try {
      return c.pwd();
    } catch ( SftpException e ) {
      throw new KettleJobException( e );
    }
  }

  public void put( FileObject fileObject, String remoteFile ) throws KettleJobException {
    int mode = ChannelSftp.OVERWRITE;
    InputStream inputStream = null;
    try {
      inputStream = KettleVFS.getInputStream( fileObject );
      c.put( inputStream, remoteFile, null, mode );
    } catch ( Exception e ) {
      throw new KettleJobException( e );
    } finally {
      if ( inputStream != null ) {
        try {
          inputStream.close();
        } catch ( IOException e ) {
          throw new KettleJobException( e );
        }
      }
    }
  }

  public void put( InputStream inputStream, String remoteFile ) throws KettleJobException {
    int mode = ChannelSftp.OVERWRITE;

    try {
      c.put( inputStream, remoteFile, null, mode );
    } catch ( Exception e ) {
      throw new KettleJobException( e );
    } finally {
      if ( inputStream != null ) {
        try {
          inputStream.close();
        } catch ( IOException e ) {
          throw new KettleJobException( e );
        }
      }
    }
  }

  public void delete( String file ) throws KettleJobException {
    try {
      c.rm( file );
    } catch ( SftpException e ) {
      throw new KettleJobException( e );
    }
  }

  /**
   * Creates the given folder. The {@param path} can be either absolute or relative.
   * Allows creation of nested folders.
   */
  public void createFolder( String path ) throws KettleJobException {
    try {
      String[] folders = path.split( "/" );
      folders[ 0 ] = ( path.charAt( 0 ) != '/' ? pwd() + "/" : "" ) + folders[ 0 ];

      for ( int i = 1; i < folders.length; i++ ) {
        folders[ i ] = folders[ i - 1 ] + "/" + folders[ i ];
      }

      for ( String f : folders ) {
        if ( f.length() != 0 && !folderExists( f ) ) {
          c.mkdir( f );
        }
      }
    } catch ( ArrayIndexOutOfBoundsException e ) {
      throw new KettleJobException( e );
    } catch ( SftpException e ) {
      throw new KettleJobException( e );
    }
  }

  /**
   * Rename the file.
   */
  public void renameFile( String sourcefilename, String destinationfilename ) throws KettleJobException {
    try {
      c.rename( sourcefilename, destinationfilename );
    } catch ( SftpException e ) {
      throw new KettleJobException( e );
    }
  }

  public FileType getFileType( String filename ) throws KettleJobException {
    try {
      SftpATTRS attrs = c.stat( filename );
      if ( attrs == null ) {
        return FileType.IMAGINARY;
      }

      if ( ( attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS ) == 0 ) {
        throw new KettleJobException( "Unknown permissions error" );
      }

      if ( attrs.isDir() ) {
        return FileType.FOLDER;
      } else {
        return FileType.FILE;
      }
    } catch ( Exception e ) {
      throw new KettleJobException( e );
    }
  }

  public boolean folderExists( String foldername ) {
    boolean retval = false;
    try {
      SftpATTRS attrs = c.stat( foldername );
      if ( attrs == null ) {
        return false;
      }

      if ( ( attrs.getFlags() & SftpATTRS.SSH_FILEXFER_ATTR_PERMISSIONS ) == 0 ) {
        throw new KettleJobException( "Unknown permissions error" );
      }

      retval = attrs.isDir();
    } catch ( Exception e ) {
      // Folder can not be found!
    }
    return retval;
  }

  public void setProxy( String host, String port, String user, String pass, String proxyType ) throws KettleJobException {

    if ( Utils.isEmpty( host ) || Const.toInt( port, 0 ) == 0 ) {
      throw new KettleJobException( "Proxy server name must be set and server port must be greater than zero." );
    }
    Proxy proxy = null;
    String proxyhost = host + ":" + port;

    if ( proxyType.equals( PROXY_TYPE_HTTP ) ) {
      proxy = new ProxyHTTP( proxyhost );
      if ( !Utils.isEmpty( user ) ) {
        ( (ProxyHTTP) proxy ).setUserPasswd( user, pass );
      }
    } else if ( proxyType.equals( PROXY_TYPE_SOCKS5 ) ) {
      proxy = new ProxySOCKS5( proxyhost );
      if ( !Utils.isEmpty( user ) ) {
        ( (ProxySOCKS5) proxy ).setUserPasswd( user, pass );
      }
    }
    s.setProxy( proxy );
  }

  public void disconnect() {
    if ( c != null ) {
      c.disconnect();
    }
    if ( s != null ) {
      s.disconnect();
    }
  }

  public String GetPrivateKeyFileName() {
    return this.prvkey;
  }

  public String GetPrivateKeyPassPhrase() {
    return this.passphrase;
  }

  public String getPassword() {
    return password;
  }

  public int getServerPort() {
    return serverPort;
  }

  public String getUserName() {
    return userName;
  }

  public InetAddress getServerIP() {
    return serverIP;
  }

  public void setCompression( String compression ) {
    this.compression = compression;
  }

  public String getCompression() {
    if ( this.compression == null ) {
      return null;
    }
    if ( this.compression.equals( "zlib" ) ) {
      // compatibility with OpenSSH implementation of delayed compression
      // https://www.openssh.com/txt/draft-miller-secsh-compression-delayed-00.txt
      return "[email protected],zlib";
    }
    if ( this.compression.equals( "none" ) ) {
      return null;
    }
    return this.compression;
  }

  @VisibleForTesting
  JSch createJSch() {
    return new JSch();
  }

  /**
   * Whether we should use GSSAPI when authenticating or not.
   */
  private String getPreferredAuthentications() {
    String param = Const.getEnvironmentVariable( ENV_PARAM_USERAUTH_GSSAPI, null );
    return Boolean.valueOf( param ) ? PREFERRED_AUTH_WITH_GSSAPI : PREFERRED_AUTH_DEFAULT;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy