Package com.slim.utils

Source Code of com.slim.utils.Scp$FileInfo

package com.slim.utils;
/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You 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.
*
*/

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

/**
* This class is using the scp client to transfer data and information for the repository.
* <P>
* * It is based on the SCPClient from the ganymed ssh library from Christian Plattner, released under a BSD style
* license.
* <P>
* * To minimize the dependency to the ssh library and because we needed some additional functionality, we decided to
* copy'n'paste the single class rather than to inherit or delegate it somehow.
* <P>
* * Nevertheless credit should go to the original author.
*/
public class Scp {
  private static final int MODE_LENGTH = 4;

  private static final int SEND_FILE_BUFFER_LENGTH = 40000;

  private static final int SEND_BYTES_BUFFER_LENGTH = 512;

  private static final int MIN_TLINE_LENGTH = 8;

  private static final int CLINE_SPACE_INDEX2 = 5;

  private static final int CLINE_SPACE_INDEX1 = 4;

  private static final int MIN_C_LINE_LENGTH = 8;

  private static final int DEFAULT_LINE_BUFFER_LENGTH = 30;

  private static final int BUFFER_SIZE = 64 * 1024;

  /*
   * Maximum length authorized for scp lines. This is a random limit - if your path names are longer, then adjust it.
   */
  private static final int MAX_SCP_LINE_LENGTH = 8192;

  private final Session session;

  public class FileInfo {
    private String filename;

    private long length;

    private long lastModified;

    /**
     * @param filename
     *            The filename to set.
     */
    public void setFilename(String filename) {
      this.filename = filename;
    }

    /**
     * @return Returns the filename.
     */
    public String getFilename() {
      return filename;
    }

    /**
     * @param length
     *            The length to set.
     */
    public void setLength(long length) {
      this.length = length;
    }

    /**
     * @return Returns the length.
     */
    public long getLength() {
      return length;
    }

    /**
     * @param lastModified
     *            The lastModified to set.
     */
    public void setLastModified(long lastModified) {
      this.lastModified = lastModified;
    }

    /**
     * @return Returns the lastModified.
     */
    public long getLastModified() {
      return lastModified;
    }
  }

  public Scp(Session session) {
    if (session == null) {
      throw new IllegalArgumentException("Cannot accept null argument!");
    }
    this.session = session;
  }

  private void readResponse(InputStream is) throws IOException, Exception {
    if (is.available() > 0) {
      int c = is.read();

      if (c == 0) {
        return;
      }

      if (c == -1) {
        throw new Exception("Remote scp terminated unexpectedly.");
      }

      if ((c != 1) && (c != 2)) {
        throw new Exception("Remote scp sent illegal error code.");
      }

      if (c == 2) {
        throw new Exception("Remote scp terminated with error.");
      }

      String err = receiveLine(is);
      throw new Exception("Remote scp terminated with error (" + err + ").");
    }
  }

  private String receiveLine(InputStream is) throws IOException, Exception {
    StringBuffer sb = new StringBuffer(DEFAULT_LINE_BUFFER_LENGTH);

    while (true) {

      if (sb.length() > MAX_SCP_LINE_LENGTH) {
        throw new Exception("Remote scp sent a too long line");
      }

      int c = is.read();

      if (c < 0) {
        throw new Exception("Remote scp terminated unexpectedly.");
      }

      if (c == '\n') {
        break;
      }

      sb.append((char) c);

    }
    return sb.toString();
  }

  private void parseCLine(String line, FileInfo fileInfo) throws Exception {
    /* Minimum line: "xxxx y z" ---> 8 chars */

    long len;

    if (line.length() < MIN_C_LINE_LENGTH) {
      throw new Exception("Malformed C line sent by remote SCP binary, line too short.");
    }

    if ((line.charAt(CLINE_SPACE_INDEX1) != ' ') || (line.charAt(CLINE_SPACE_INDEX2) == ' ')) {
      throw new Exception("Malformed C line sent by remote SCP binary.");
    }

    int lengthNameSep = line.indexOf(' ', CLINE_SPACE_INDEX2);

    if (lengthNameSep == -1) {
      throw new Exception("Malformed C line sent by remote SCP binary.");
    }

    String lengthSubstring = line.substring(CLINE_SPACE_INDEX2, lengthNameSep);
    String nameSubstring = line.substring(lengthNameSep + 1);

    if ((lengthSubstring.length() <= 0) || (nameSubstring.length() <= 0)) {
      throw new Exception("Malformed C line sent by remote SCP binary.");
    }

    if ((CLINE_SPACE_INDEX2 + 1 + lengthSubstring.length() + nameSubstring.length()) != line.length()) {
      throw new Exception("Malformed C line sent by remote SCP binary.");
    }

    try {
      len = Long.parseLong(lengthSubstring);
    } catch (NumberFormatException e) {
      throw new Exception("Malformed C line sent by remote SCP binary, cannot parse file length.");
    }

    if (len < 0) {
      throw new Exception("Malformed C line sent by remote SCP binary, illegal file length.");
    }

    fileInfo.setLength(len);
    fileInfo.setFilename(nameSubstring);
  }

  private void parseTLine(String line, FileInfo fileInfo) throws Exception {
    /* Minimum line: "0 0 0 0" ---> 8 chars */

    long modtime;
    long firstMsec;
    long atime;
    long secondMsec;

    if (line.length() < MIN_TLINE_LENGTH) {
      throw new Exception("Malformed T line sent by remote SCP binary, line too short.");
    }

    int firstMsecBegin = line.indexOf(" ") + 1;
    if (firstMsecBegin == 0 || firstMsecBegin >= line.length()) {
      throw new Exception("Malformed T line sent by remote SCP binary, line not enough data.");
    }

    int atimeBegin = line.indexOf(" ", firstMsecBegin + 1) + 1;
    if (atimeBegin == 0 || atimeBegin >= line.length()) {
      throw new Exception("Malformed T line sent by remote SCP binary, line not enough data.");
    }

    int secondMsecBegin = line.indexOf(" ", atimeBegin + 1) + 1;
    if (secondMsecBegin == 0 || secondMsecBegin >= line.length()) {
      throw new Exception("Malformed T line sent by remote SCP binary, line not enough data.");
    }

    try {
      modtime = Long.parseLong(line.substring(0, firstMsecBegin - 1));
      firstMsec = Long.parseLong(line.substring(firstMsecBegin, atimeBegin - 1));
      atime = Long.parseLong(line.substring(atimeBegin, secondMsecBegin - 1));
      secondMsec = Long.parseLong(line.substring(secondMsecBegin));
    } catch (NumberFormatException e) {
      // LOGGER.error(e);
      throw new Exception("Malformed C line sent by remote SCP binary, cannot parse file length.");
    }

    if (modtime < 0 || firstMsec < 0 || atime < 0 || secondMsec < 0) {
      throw new Exception("Malformed C line sent by remote SCP binary, illegal file length.");
    }

    fileInfo.setLastModified(modtime);
  }

  private void sendFile(Channel channel, String localFile, String remoteName, String mode)
    throws IOException,
    Exception {
    byte[] buffer = new byte[BUFFER_SIZE];

    OutputStream os = new BufferedOutputStream(channel.getOutputStream(), SEND_FILE_BUFFER_LENGTH);
    InputStream is = new BufferedInputStream(channel.getInputStream(), SEND_BYTES_BUFFER_LENGTH);

    try {
      if (channel.isConnected()) {
        channel.start();
      } else {
        channel.connect();
      }
    } catch (JSchException e1) {
      throw (IOException) new IOException("Channel connection problems").initCause(e1);
    }

    readResponse(is);

    File f = new File(localFile);
    long remain = f.length();

    String cMode = mode;
    if (cMode == null) {
      cMode = "0600";
    }
    String cline = "C" + cMode + " " + remain + " " + remoteName + "\n";

    os.write(cline.getBytes());
    os.flush();

    readResponse(is);

    FileInputStream fis = null;

    try {
      fis = new FileInputStream(f);

      while (remain > 0) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        int trans;
        if (remain > buffer.length) {
          trans = buffer.length;
        } else {
          trans = (int) remain;
        }
        if (fis.read(buffer, 0, trans) != trans) {
          throw new IOException("Cannot read enough from local file " + localFile);
        }

        os.write(buffer, 0, trans);

        remain -= trans;
      }

      fis.close();
    } catch (Exception e) {
      if (fis != null) {
        fis.close();
      }
      // LOGGER.error(e);
      throw new Exception(e);
    }

    os.write(0);
    os.flush();

    readResponse(is);

    os.write("E\n".getBytes());
    os.flush();
  }

  /**
   * Receive a file via scp and store it in a stream
   *
   * @param channel
   *            ssh channel to use
   * @param file
   *            to receive from remote
   * @param target
   *            to store file into (if null, get only file info)
   * @return file information of the file we received
   * @throws IOException
   *             in case of network or protocol trouble
   * @throws Exception
   *             in case of problems on the target system (connection is fine)
   */
  private FileInfo receiveStream(Channel channel, String file, OutputStream targetStream)
    throws IOException,
    Exception {
    byte[] buffer = new byte[BUFFER_SIZE];

    OutputStream os = channel.getOutputStream();
    InputStream is = channel.getInputStream();
    try {
      if (channel.isConnected()) {
        channel.start();
      } else {
        channel.connect();
      }
    } catch (JSchException e1) {
      throw (IOException) new IOException("Channel connection problems").initCause(e1);
    }
    os.write(0x0);
    os.flush();

    FileInfo fileInfo = new FileInfo();

    while (true) {
      int c = is.read();
      if (c < 0) {
        throw new Exception("Remote scp terminated unexpectedly.");
      }

      String line = receiveLine(is);

      if (c == 'T') {
        parseTLine(line, fileInfo);
        os.write(0x0);
        os.flush();
        continue;
      }
      if ((c == 1) || (c == 2)) {
        throw new Exception("Remote SCP error: " + line);
      }

      if (c == 'C') {
        parseCLine(line, fileInfo);
        break;
      }
      throw new Exception("Remote SCP error: " + ((char) c) + line);
    }
    if (targetStream != null) {

      os.write(0x0);
      os.flush();

      try {
        long remain = fileInfo.getLength();

        while (remain > 0) {
          int trans;
          if (remain > buffer.length) {
            trans = buffer.length;
          } else {
            trans = (int) remain;
          }

          int thisTimeReceived = is.read(buffer, 0, trans);

          if (thisTimeReceived < 0) {
            throw new IOException("Remote scp terminated connection unexpectedly");
          }

          targetStream.write(buffer, 0, thisTimeReceived);

          remain -= thisTimeReceived;
        }

        targetStream.close();
      } catch (IOException e) {
        if (targetStream != null) {
          targetStream.close();
        }
        // LOGGER.error(e);
        throw (e);
      }

      readResponse(is);

      os.write(0x0);
      os.flush();
    }
    return fileInfo;
  }

  /**
   * @return
   * @throws JSchException
   */
  private ChannelExec getExecChannel() throws JSchException {
    ChannelExec channel;
    channel = (ChannelExec) session.openChannel("exec");
    return channel;
  }

  /**
   * Copy a local file to a remote site, uses the specified mode when creating the file on the remote side.
   *
   * @param localFile
   *            Path and name of local file. Must be absolute.
   * @param remoteTargetDir
   *            Remote target directory where the file has to end up (optional)
   * @param remoteTargetName
   *            file name to use on the target system
   * @param mode
   *            a four digit string (e.g., 0644, see "man chmod", "man open")
   * @throws IOException
   *             in case of network problems
   * @throws Exception
   *             in case of problems on the target system (connection ok)
   */
  public void put(String localFile, String remoteTargetDir, String remoteTargetName, String mode) throws Exception {
    ChannelExec channel = null;

    if ((localFile == null) || (remoteTargetName == null)) {
      throw new IllegalArgumentException("Null argument.");
    }

    if (mode != null) {
      if (mode.length() != MODE_LENGTH) {
        throw new IllegalArgumentException("Invalid mode.");
      }

      for (int i = 0; i < mode.length(); i++) {
        if (!Character.isDigit(mode.charAt(i))) {
          throw new IllegalArgumentException("Invalid mode.");
        }
      }
    }

    String cmd = "scp -t ";
    if (mode != null) {
      cmd = cmd + "-p ";
    }
    if (remoteTargetDir != null && remoteTargetDir.length() > 0) {
      cmd = cmd + "-d " + remoteTargetDir;
    }

    try {
      channel = getExecChannel();
      channel.setCommand(cmd);
      sendFile(channel, localFile, remoteTargetName, mode);
      channel.disconnect();
    } catch (JSchException e) {
      if (channel != null) {
        channel.disconnect();
      }
      e.printStackTrace();
      // LOGGER.error(e);
      throw new Exception("Error during SCP transfer." + e.getMessage());
    } catch (Exception e) {
      e.printStackTrace();
      // LOGGER.error(e);
      throw new Exception(e.getLocalizedMessage());
    }
  }

  /**
   * Download a file from the remote server to a local file.
   *
   * @param remoteFile
   *            Path and name of the remote file.
   * @param localTarget
   *            Local file where to store the data. Must be absolute.
   * @throws IOException
   *             in case of network problems
   * @throws Exception
   *             in case of problems on the target system (connection ok)
   */
  public void get(String remoteFile, String localTarget) throws Exception {
    try {
      File f = new File(localTarget);
      FileOutputStream fop = new FileOutputStream(f);
      get(remoteFile, fop);
    } catch (IOException e) {
      e.printStackTrace();
      // LOGGER.error(e);
      throw new Exception(e.getLocalizedMessage());
    }
  }

  /**
   * Download a file from the remote server into an OutputStream
   *
   * @param remoteFile
   *            Path and name of the remote file.
   * @param localTarget
   *            OutputStream to store the data.
   * @throws IOException
   *             in case of network problems
   * @throws Exception
   *             in case of problems on the target system (connection ok)
   */
  private void get(String remoteFile, OutputStream localTarget) throws IOException, Exception {
    ChannelExec channel = null;

    if ((remoteFile == null) || (localTarget == null)) {
      throw new IllegalArgumentException("Null argument.");
    }

    String cmd = "scp -p -f " + remoteFile;

    try {
      channel = getExecChannel();
      channel.setCommand(cmd);
      receiveStream(channel, remoteFile, localTarget);
      channel.disconnect();
    } catch (JSchException e) {
      if (channel != null) {
        channel.disconnect();
      }
      throw (IOException) new IOException("Error during SCP transfer." + e.getMessage()).initCause(e);
    }
  }

  /**
   * Initiates an SCP sequence but stops after getting fileinformation header
   *
   * @param remoteFile
   *            to get information for
   * @return the file information got
   * @throws IOException
   *             in case of network problems
   * @throws Exception
   *             in case of problems on the target system (connection ok)
   */
  public FileInfo getFileinfo(String remoteFile) throws IOException, Exception {
    ChannelExec channel = null;
    FileInfo fileInfo = null;

    if (remoteFile == null) {
      throw new IllegalArgumentException("Null argument.");
    }

    String cmd = "scp -p -f \"" + remoteFile + "\"";

    try {
      channel = getExecChannel();
      channel.setCommand(cmd);
      fileInfo = receiveStream(channel, remoteFile, null);
      channel.disconnect();
    } catch (JSchException e) {
      throw (IOException) new IOException("Error during SCP transfer." + e.getMessage()).initCause(e);
    } finally {
      if (channel != null) {
        channel.disconnect();
      }
    }
    return fileInfo;
  }
}
TOP

Related Classes of com.slim.utils.Scp$FileInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.