<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Recent changes to feature-requests</title><link>https://sourceforge.net/p/sshtools/feature-requests/</link><description>Recent changes to feature-requests</description><atom:link href="https://sourceforge.net/p/sshtools/feature-requests/feed.rss" rel="self"/><language>en</language><lastBuildDate>Fri, 11 Apr 2008 13:07:15 -0000</lastBuildDate><atom:link href="https://sourceforge.net/p/sshtools/feature-requests/feed.rss" rel="self" type="application/rss+xml"/><item><title>ability to send control signals to remote process</title><link>https://sourceforge.net/p/sshtools/feature-requests/20/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;Signals cannot be passed at present through OutputBuffer as they are not recognized by the console. We need a way to pass these signals.&lt;/p&gt;
&lt;p&gt;ABRT&lt;br /&gt;
ALRM&lt;br /&gt;
FPE&lt;br /&gt;
HUP&lt;br /&gt;
ILL&lt;br /&gt;
INT&lt;br /&gt;
KILL&lt;br /&gt;
PIPE&lt;br /&gt;
QUIT&lt;br /&gt;
SEGV&lt;br /&gt;
TERM&lt;br /&gt;
USR1&lt;br /&gt;
USR2&lt;/p&gt;
&lt;p&gt;similar to this function implemented in a proprietary package - &lt;a href="http://www.sshtools.com/content/developer/maverick/docs/com/maverick/ssh2/Ssh2Session.html#signal\"&gt;http://www.sshtools.com/content/developer/maverick/docs/com/maverick/ssh2/Ssh2Session.html#signal\&lt;/a&gt;(java.lang.String)&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">sid</dc:creator><pubDate>Fri, 11 Apr 2008 13:07:15 -0000</pubDate><guid>https://sourceforge.net3ce2328be22cc2fa8a79230066a285ea4d17c588</guid></item><item><title>Need changes for better performance of large file upload</title><link>https://sourceforge.net/p/sshtools/feature-requests/19/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;While using SSHTools in our project, we found that the performance for uploading large files ( &amp;gt; 1 MB) is very poor compared to what is possible by a manual transfer (via scp say) and even using another open source tool. As we have decided to use SSHTools, we did some changes in the code (for v 0.2.7) , and found that performance for this case is significantly faster now (possibly upto 10 times). I am detailing the defects we found that can hamper the performance for uploading large files, and attaching the changed files for v0.2.7 along with a README that explains the exact changes in each file that were done:&lt;/p&gt;
&lt;p&gt;I found two inefficiencies –&lt;br /&gt;
1) The library is inefficient in sending the appropriate size of chunk of data to the server. So it ends up spending app. twice the time (in some cases) in encrypting and padding as opposed to what is actually required. &lt;/p&gt;
&lt;p&gt;2) It blocks for the response from the server; this could be inefficient in some cases, such as upload of large files. Here you get ‘Window Adjust’ messages in between, and there may be a delay in the response for the data received from the server. But in such cases, the library will block; thus slowing it down considerably.&lt;/p&gt;
&lt;p&gt;The attached zip contains the changed files. Can we have these changes incorporated into v0.2.7 (after your review of course), so that instead of we making any changes , we get these changes from the original writers, and have more faith in the product. I also believe these changes, if properly incorporated can help SSHTools library latest versions also (if these kind of changes are already not there).&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">nchawla</dc:creator><pubDate>Mon, 11 Jun 2007 12:36:57 -0000</pubDate><guid>https://sourceforge.net56bd15d03962c1422151fa427bf1d4415b7b6275</guid></item><item><title>Need a way to set/get packet size in SftpClient</title><link>https://sourceforge.net/p/sshtools/feature-requests/18/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;I was unable to put via SFTP as I was getting a packet &lt;br /&gt;
too large error.  In order to accomodate, I have &lt;br /&gt;
modified my local copy of SftpClient such that the &lt;br /&gt;
following methods exist (these would be very useful in &lt;br /&gt;
full version):&lt;/p&gt;
&lt;p&gt;/**&lt;br /&gt;
* &amp;lt;p&amp;gt;&lt;br /&gt;
* Allows the packet size of 65535 or less to be &lt;br /&gt;
specified from the client&lt;br /&gt;
* (Default packet size is 65535)&lt;br /&gt;
* &amp;lt;/p&amp;gt;&lt;br /&gt;
*&lt;br /&gt;
* @param packetSize Size of desired packet&lt;br /&gt;
* @since 0.2.0&lt;br /&gt;
*/&lt;br /&gt;
public void setPacketSize (int packetSize)&lt;br /&gt;
{   BLOCKSIZE = ( packetSize &amp;lt;= 65535 ? &lt;br /&gt;
packetSize : BLOCKSIZE);  }&lt;/p&gt;
&lt;p&gt;/**&lt;br /&gt;
* &amp;lt;p&amp;gt;&lt;br /&gt;
* Returns the currently defined packed size&lt;br /&gt;
* (Default packet size is 65535)&lt;br /&gt;
* &amp;lt;/p&amp;gt;&lt;br /&gt;
*&lt;br /&gt;
* @return The current packet size&lt;br /&gt;
* @since 0.2.0&lt;br /&gt;
*/&lt;br /&gt;
public int getPacketSize()&lt;br /&gt;
{  return BLOCKSIZE; }&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Howard Ungar</dc:creator><pubDate>Thu, 21 Sep 2006 19:44:55 -0000</pubDate><guid>https://sourceforge.net1413548a4aacddb7e428a17be9f2192d840289ed</guid></item><item><title>Support Certificate Authenticate</title><link>https://sourceforge.net/p/sshtools/feature-requests/17/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;Do you intend to support this type of Authentication &lt;br /&gt;
(Certificate Authentication) in J2SSH?? (if yes When?)&lt;br /&gt;
thanks&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Iglezias,LM</dc:creator><pubDate>Wed, 31 May 2006 12:29:01 -0000</pubDate><guid>https://sourceforge.net2e6c18f5d4e2893eaa0b540465bd669a87173f48</guid></item><item><title>try chmod configurable for SftpClient</title><link>https://sourceforge.net/p/sshtools/feature-requests/16/</link><description>&lt;pre&gt;Adapt SshClient.java and SftpClient.java make it 
configurable to allow NOT to try a chmod for a new 
file/directory on remote server. This can give errors 
if chmod is not allowed \(dropbox on remote server with 
minimal rights\).

Adapted code SshClient.java :

/\*
\*  SSHTools - Java SSH2 API
\*
\*  Copyright \(C\) 2002-2003 Lee David Painter and 
Contributors.
\*
\*  Contributions made by:
\*
\*  Brett Smith
\*  Richard Pernavas
\*  Erwin Bolwidt
\*
\*  This program is free software; you can 
redistribute it and/or
\*  modify it under the terms of the GNU Library 
General Public License
\*  as published by the Free Software Foundation; 
either version 2 of
\*  the License, or \(at your option\) any later version.
\*
\*  You may also distribute it and/or modify it under 
the terms of the
\*  Apache style J2SSH Software License. A copy of 
which should have
\*  been provided with the distribution.
\*
\*  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
\*  License document supplied with your distribution 
for more details.
\*
\*/
package com.sshtools.j2ssh;

import 
com.sshtools.j2ssh.authentication.AuthenticationProtoco
lClient;
import 
com.sshtools.j2ssh.authentication.AuthenticationProtoco
lState;
import 
com.sshtools.j2ssh.authentication.PublicKeyAuthenticati
onClient;
import 
com.sshtools.j2ssh.authentication.SshAuthenticationClie
nt;
import 
com.sshtools.j2ssh.configuration.SshConnectionPropertie
s;
import com.sshtools.j2ssh.connection.Channel;
import 
com.sshtools.j2ssh.connection.ChannelEventAdapter;
import 
com.sshtools.j2ssh.connection.ChannelEventListener;
import com.sshtools.j2ssh.connection.ChannelFactory;
import 
com.sshtools.j2ssh.connection.ConnectionProtocol;
import com.sshtools.j2ssh.forwarding.ForwardingClient;
import com.sshtools.j2ssh.net.TransportProvider;
import com.sshtools.j2ssh.net.TransportProviderFactory;
import com.sshtools.j2ssh.session.SessionChannelClient;
import com.sshtools.j2ssh.sftp.SftpSubsystemClient;
import 
com.sshtools.j2ssh.transport.ConsoleKnownHostsKeyVerifi
cation;
import 
com.sshtools.j2ssh.transport.HostKeyVerification;
import 
com.sshtools.j2ssh.transport.TransportProtocolClient;
import 
com.sshtools.j2ssh.transport.TransportProtocolState;
import 
com.sshtools.j2ssh.transport.publickey.SshPublicKey;
import com.sshtools.j2ssh.util.State;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.io.IOException;

import java.net.UnknownHostException;

import java.util.Iterator;
import java.util.List;
import java.util.Vector;


/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Implements an SSH client with methods to connect to 
a remote server and
\* perform all necersary SSH functions such as SCP, 
SFTP, executing commands,
\* starting the users shell and perform port 
forwarding.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* There are several steps to perform prior to 
performing the desired task.
\* This involves the making the initial connection, 
authenticating the user
\* and creating a session to execute a command, shell 
or subsystem and/or
\* configuring the port forwarding manager.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* To create a connection use the following code:&amp;amp;lt;br&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* // Create a instance and connect SshClient
\* ssh = new SshClient\(\);
\* ssh.connect\("hostname"\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\* Once this code has executed and returned
\* the connection is ready for authentication:&amp;amp;lt;br&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* PasswordAuthenticationClient pwd = new 
PasswordAuthenticationClient\(\);
\* pwd.setUsername\("foo"\);
\* pwd.setPassword\("xxxxx"\);
\* // Authenticate the user
\* int result = ssh.authenticate\(pwd\);
\* if\(result==AuthenticationProtocolState.COMPLETED\) \{
\*    // Authentication complete
\* \}
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\* Once authenticated the user's shell can be 
started:&amp;amp;lt;br&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* // Open a session channel
\* SessionChannelClient session =
\*                      ssh.openSessionChannel\(\);
\*
\* // Request a pseudo terminal, if you do not you may 
not see the prompt
\* if\(session.requestPseudoTerminal\("ansi", 80, 24, 0, 
0, ""\) \{
\*      // Start the users shell
\*      if\(session.startShell\(\)\) \{
\*         // Do something with the session output
\*         session.getOutputStream\(\).write\("echo 
message\n"\);
\*         ....
\*       \}
\* \}
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @author Lee David Painter
\* @version $Revision: 1.75 $
\*
\* @since 0.2.0
\*/
public class SshClient \{
private static Log log = LogFactory.getLog
\(SshClient.class\);

/\*\*
\* The SSH Authentication protocol implementation 
for this SSH client. The
\* SSH Authentication protocol runs over the SSH 
Transport protocol as a
\* transport protocol service.
\*/
protected AuthenticationProtocolClient 
authentication;

/\*\*
\* The SSH Connection protocol implementation for 
this SSH client. The
\* connection protocol runs over the SSH Transport 
protocol as a transport
\* protocol service and is started by the 
authentication protocol after a
\* successful authentication.
\*/
protected ConnectionProtocol connection;

/\*\* Provides a high level management interface for 
SSH port forwarding. \*/
protected ForwardingClient forwarding;

/\*\* The SSH Transport protocol implementation for 
this SSH Client. \*/
protected TransportProtocolClient transport;

/\*\* The current state of the authentication for 
the current connection. \*/
protected int authenticationState = 
AuthenticationProtocolState.READY;

/\*\*
\* The timeout in milliseconds for the underlying 
transport provider
\* \(typically a Socket\).
\*/
protected int socketTimeout = 0;

/\*\*
\* A Transport protocol event handler instance 
that receives notifications
\* of transport layer events such as Socket 
timeouts and disconnection.
\*/
protected SshEventAdapter eventHandler = null;

/\*\* The currently active channels for this SSH 
Client connection. \*/
protected Vector activeChannels = new Vector\(\);

/\*\*
\* An channel event listener implemention to 
maintain the active channel
\* list.
\*/
protected ActiveChannelEventListener 
activeChannelListener = new ActiveChannelEventListener
\(\);

/\*\*
\* Flag indicating whether the forwarding instance 
is created when the
\* connection is made.
\*/
protected boolean useDefaultForwarding = true;

/\*\* The currently active Sftp clients \*/
private Vector activeSftpClients = new Vector\(\);

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Contructs an unitilialized SshClient ready for 
connecting.
\* &amp;amp;lt;/p&amp;amp;gt;
\*/
public SshClient\(\) \{
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the server's authentication banner.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* In some jurisdictions, sending a warning 
message before authentication
\* may be relevant for getting legal protection.  
Many UNIX machines, for
\* example, normally display text from 
\`/etc/issue', or use "tcp wrappers"
\* or similar software to display a banner before 
issuing a login prompt.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* The server may or may not send this message. 
Call this method to
\* retrieve the message, specifying a timeout 
limit to wait for the
\* message.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param timeout The number of milliseconds to 
wait for the banner message
\*        before returning
\*
\* @return The server's banner message
\*
\* @exception IOException If an IO error occurs 
reading the message
\*
\* @since 0.2.0
\*/
public String getAuthenticationBanner\(int timeout\)
throws IOException \{
if \(authentication == null\) \{
return "";
\} else \{
return authentication.getBannerMessage
\(timeout\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the list of available authentication 
methods for a given user.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* A client may request a list of authentication 
methods that may continue
\* by using the "none" authentication method.This 
method calls the "none"
\* method and returns the available authentication 
methods.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param username The name of the account for 
which you require the
\*        available authentication methods
\*
\* @return A list of Strings, for 
example "password", "publickey" &amp;amp;
\*         "keyboard-interactive"
\*
\* @exception IOException If an IO error occurs 
during the operation
\*
\* @since 0.2.0
\*/
public List getAvailableAuthMethods\(String 
username\)
throws IOException \{
if \(authentication \!= null\) \{
return authentication.getAvailableAuths
\(username,
connection.getServiceName\(\)\);
\} else \{
return null;
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the connection state of the client.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return true if the client is connected, false 
otherwise
\*
\* @since 0.2.0
\*/
public boolean isConnected\(\) \{
State state = \(transport == null\) ? null : 
transport.getState\(\);
int value = \(state == null\) ? 
TransportProtocolState.DISCONNECTED
: state.getValue\(\);

return \(\(value == 
TransportProtocolState.CONNECTED\) ||
\(value == 
TransportProtocolState.PERFORMING\_KEYEXCHANGE\)\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Evaluate whether the client has successfully 
authenticated.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return true if the client is authenticated, 
otherwise false
\*/
public boolean isAuthenticated\(\) \{
return authenticationState == 
AuthenticationProtocolState.COMPLETE;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the identification string sent by the 
server during protocol
\* negotiation. For example "SSH-2.0-OpenSSH\_p3.4".
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return The server's identification string.
\*
\* @since 0.2.0
\*/
public String getServerId\(\) \{
return transport.getRemoteId\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the server's public key supplied during 
key exchange.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return the server's public key
\*
\* @since 0.2.0
\*/
public SshPublicKey getServerHostKey\(\) \{
return transport.getServerHostKey\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the transport protocol's connection 
state.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return The transport protocol's state
\*
\* @since 0.2.0
\*/
public TransportProtocolState getConnectionState\(\) 
\{
return transport.getState\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the default port forwarding manager.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return This connection's forwarding client
\*
\* @since 0.2.0
\*/
public ForwardingClient getForwardingClient\(\) \{
return forwarding;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Return's a rough guess at the server's EOL 
setting. This is simply
\* derived from the identification string and 
should not be used as a cast
\* iron proof on the EOL setting.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return The transport protocol's EOL constant
\*
\* @since 0.2.0
\*/
public int getRemoteEOL\(\) \{
return transport.getRemoteEOL\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Set the event handler for the underlying 
transport protocol.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;
\* &amp;amp;lt;pre&amp;amp;gt;
\* ssh.setEventHandler\(new 
TransportProtocolEventHandler\(\) \{
\*
\*   public void onSocketTimeout\(TransportProtocol 
transport\) \{&amp;amp;lt;br&amp;amp;gt;
\*     // Do something to handle the socket 
timeout&amp;amp;lt;br&amp;amp;gt;
\*   \}
\*
\*   public void onDisconnect\(TransportProtocol 
transport\) \{
\*     // Perhaps some clean up?
\*   \}
\* \}\);
\* &amp;amp;lt;/pre&amp;amp;gt;
\* &amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @param eventHandler The event handler instance 
to receive transport
\*        protocol events
\*
\* @see 
com.sshtools.j2ssh.transport.TransportProtocolEventHand
ler
\* @since 0.2.0
\*/
public void addEventHandler\(SshEventAdapter 
eventHandler\) \{
// If were connected then add, otherwise store 
for later connection
if \(transport \!= null\) \{
transport.addEventHandler\(eventHandler\);
authentication.addEventListener
\(eventHandler\);
\} else \{
this.eventHandler = eventHandler;
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Set's the socket timeout \(in milliseconds\) for 
the underlying transport
\* provider. This MUST be called prior to connect.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;
\* SshClient ssh = new SshClient\(\);
\* ssh.setSocketTimeout\(30000\);
\* ssh.connect\("hostname"\);
\* &amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @param milliseconds The number of milliseconds 
without activity before
\*        the timeout event occurs
\*
\* @since 0.2.0
\*/
public void setSocketTimeout\(int milliseconds\) \{
this.socketTimeout = milliseconds;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Return's a rough guess at the server's EOL 
setting. This is simply
\* derived from the identification string and 
should not be used as a cast
\* iron proof on the EOL setting.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return The EOL string
\*
\* @since 0.2.0
\*/
public String getRemoteEOLString\(\) \{
return \(\(transport.getRemoteEOL\(\) == 
TransportProtocolClient.EOL\_CRLF\)
? "\r\n" : "\n"\);
\}

/\*\*
\* Get the connection properties for this 
connection.
\*
\* @return
\*/
public SshConnectionProperties 
getConnectionProperties\(\) \{
return transport.getProperties\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Authenticate the user on the remote host.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* To authenticate the user, create an 
&amp;amp;lt;code&amp;amp;gt;SshAuthenticationClient&amp;amp;lt;/code&amp;amp;gt;
\* instance and configure it with the 
authentication details.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;code&amp;amp;gt; PasswordAuthenticationClient pwd = new
\* PasswordAuthenticationClient\(\); pwd.setUsername
\("root"\);
\* pwd.setPassword\("xxxxxxxxx"\); int result = 
ssh.authenticate\(pwd\);
\* &amp;amp;lt;/code&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* The method returns a result value will one of 
the public static values
\* defined in 
&amp;amp;lt;code&amp;amp;gt;AuthenticationProtocolState&amp;amp;lt;/code&amp;amp;gt;. These are&amp;amp;lt;br&amp;amp;gt;
\* &amp;amp;lt;br&amp;amp;gt;
\* COMPLETED - The authentication succeeded.&amp;amp;lt;br&amp;amp;gt;
\* PARTIAL   - The authentication succeeded but a 
further authentication
\* method is required.&amp;amp;lt;br&amp;amp;gt;
\* FAILED    - The authentication failed.&amp;amp;lt;br&amp;amp;gt;
\* CANCELLED - The user cancelled authentication 
\(can only be returned
\* when the user is prompted for information.&amp;amp;lt;br&amp;amp;gt;
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param auth A configured 
SshAuthenticationClient instance ready for
\*        authentication
\*
\* @return The authentication result
\*
\* @exception IOException If an IO error occurs 
during authentication
\*
\* @since 0.2.0
\*/
public int authenticate\(SshAuthenticationClient 
auth\)
throws IOException \{
// Do the authentication
authenticationState = 
authentication.authenticate\(auth, connection\);

if \(\(authenticationState == 
AuthenticationProtocolState.COMPLETE\) &amp;amp;&amp;amp;
useDefaultForwarding\) \{
// Use some method to synchronize 
forwardings on the ForwardingClient
forwarding.synchronizeConfiguration
\(transport.getProperties\(\)\);
\}

return authenticationState;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Determine whether a private/public key pair 
will be accepted for public
\* key authentication.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* When using public key authentication, the 
signing of data could take
\* some time depending upon the available machine 
resources. By calling
\* this method, you can determine whether the 
server will accept a key for
\* authentication by providing the public key. The 
server will verify the
\* key against the user's authorized keys and 
return true should the
\* public key be authorized. The caller can then 
proceed with the private
\* key operation.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param username The username for authentication
\* @param key The public key for which 
authentication will be attempted
\*
\* @return true if the server will accept the key, 
otherwise false
\*
\* @exception IOException If an IO error occurs 
during the operation
\* @throws SshException
\*
\* @since 0.2.0
\*/
public boolean acceptsKey\(String username, 
SshPublicKey key\)
throws IOException \{
if \(authenticationState \!= 
AuthenticationProtocolState.COMPLETE\) \{
PublicKeyAuthenticationClient pk = new 
PublicKeyAuthenticationClient\(\);

return pk.acceptsKey\(authentication, 
username,
connection.getServiceName\(\), key\);
\} else \{
throw new SshException\("Authentication has 
been completed\!"\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Connect the client to the server using default 
connection properties.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* This call attempts to connect to the hostname 
specified on the standard
\* SSH port of 22 and uses all the default 
connection properties. This
\* call is the equivilent of calling:
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* SshConnectionProperties properties = new
\*                           
SshConnectionProperties\(\);
\* properties.setHostname\("hostname"\);
\* ssh.connect\(properties\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @param hostname The hostname of the server to 
connect
\*
\* @exception IOException If an IO error occurs 
during the connect
\*            operation
\*
\* @see \#connect
\(com.sshtools.j2ssh.configuration.SshConnectionProperti
es\)
\* @since 0.2.0
\*/
public void connect\(String hostname\) throws 
IOException \{
connect\(hostname, 22, new 
ConsoleKnownHostsKeyVerification\(\)\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Connect the client to the server using the 
default connection
\* properties.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* This call attempts to connect to the hostname 
specified on the standard
\* SSH port of 22 and uses all the default 
connection properties. When
\* this method returns the connection has been 
established, the server's
\* identity been verified and the connection is 
ready for user
\* authentication. Host key verification will be 
performed using the host
\* key verification instance provided:
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* // Connect and consult $HOME/.ssh/known\_hosts
\* ssh.connect\("hostname", new 
ConsoleKnownHostsKeyVerification\(\)\);
\* // Connect and allow any host
\* ssh.connect\("hostname", new
\*                 IgnoreHostKeyVerification\(\)\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* You can provide your own host key verification 
process by implementing
\* the &amp;amp;lt;code&amp;amp;gt;HostKeyVerification&amp;amp;lt;/code&amp;amp;gt; interface.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param hostname The hostname of the server to 
connect
\* @param hosts The host key verification instance 
to consult for host key
\*        validation
\*
\* @exception IOException If an IO error occurs 
during the connect
\*            operation
\*
\* @see \#connect
\(com.sshtools.j2ssh.configuration.SshConnectionProperti
es,
\*      
com.sshtools.j2ssh.transport.HostKeyVerification\)
\* @since 0.2.0
\*/
public void connect\(String hostname, 
HostKeyVerification hosts\)
throws IOException \{
connect\(hostname, 22, hosts\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Connect the client to the server on a specified 
port with default
\* connection properties.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* This call attempts to connect to the hostname 
and port specified. This
\* call is the equivilent of calling:
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;/pre&amp;amp;gt;
\* SshConnectionProperties properties = new
\*                               
SshConnectionProperties\(\);
\* properties.setHostname\("hostname"\);
\* properties.setPort\(10022\);
\* ssh.connect\(properties\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @param hostname The hostname of the server to 
connect
\* @param port The port to connect
\*
\* @exception IOException If an IO error occurs 
during the connect
\*            operation
\*
\* @see \#connect
\(com.sshtools.j2ssh.configuration.SshConnectionProperti
es\)
\* @since 0.2.0
\*/
public void connect\(String hostname, int port\) 
throws IOException \{
connect\(hostname, port, new 
ConsoleKnownHostsKeyVerification\(\)\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Connect the client to the server on a specified 
port with default
\* connection properties.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* This call attempts to connect to the hostname 
and port specified. When
\* this method returns the connection has been 
established, the server's
\* identity been verified and the connection is 
ready for user
\* authentication. Host key verification will be 
performed using the host
\* key verification instance provided:
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* // Connect and consult $HOME/.ssh/known\_hosts
\* ssh.connect\("hostname", new 
ConsoleKnownHostsKeyVerification\(\)\);
\* // Connect and allow any host
\* ssh.connect\("hostname", new
\*                 IgnoreHostKeyVerification\(\)\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* You can provide your own host key verification 
process by implementing
\* the &amp;amp;lt;code&amp;amp;gt;HostKeyVerification&amp;amp;lt;/code&amp;amp;gt; interface.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param hostname The hostname of the server to 
connect
\* @param port The port to connect
\* @param hosts The host key verification instance 
to consult for host key
\*        validation
\*
\* @exception IOException If an IO error occurs 
during the connect
\*            operation
\*
\* @see \#connect
\(com.sshtools.j2ssh.configuration.SshConnectionProperti
es,
\*      
com.sshtools.j2ssh.transport.HostKeyVerification\)
\* @since 0.2.0
\*/
public void connect\(String hostname, int port, 
HostKeyVerification hosts\)
throws IOException \{
SshConnectionProperties properties = new 
SshConnectionProperties\(\);
properties.setHost\(hostname\);
properties.setPort\(port\);
connect\(properties, hosts\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Connect the client to the server with the 
specified properties.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* This call attempts to connect to using the 
connection properties
\* specified. When this method returns the 
connection has been
\* established, the server's identity been 
verified and the connection is
\* ready for user authentication. To use this 
method first create a
\* properties instance and set the required fields.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* SshConnectionProperties properties = new
\*                         SshConnectionProperties
\(\);
\* properties.setHostname\("hostname"\);
\* properties.setPort\(10022\);
\* properties.setPrefCSEncryption\("blowfish-cbc"\);
\* ssh.connect\(properties\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Host key verification will be performed using
\* &amp;amp;lt;code&amp;amp;gt;ConsoleKnownHostsKeyVerification&amp;amp;lt;/code&amp;amp;gt; 
and so this call is the
\* equivilent of calling:
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* ssh.connect\("hostname", new 
ConsoleKnownHostsKeyVerification\(\)\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* If the key is not matched to any keys already 
in the
\* $HOME/.ssh/known\_hosts file, the user will be 
prompted via the console
\* to confirm the identity of the remote server. 
The user will receive the
\* following prompt.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;code&amp;amp;gt; The host shell.sourceforge.net is 
currently unknown to the system
\* The host key fingerprint is: 1024: 4c 68 3 d4 
5c 58 a6 1d 9d 17 13 24
\* 14 48 ba 99 Do you want to allow this host key? 
\[Yes|No|Always\]:
\* &amp;amp;lt;/code&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Selecting the "always" option will write the 
key to the known\_hosts
\* file.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param properties The connection properties
\*
\* @exception IOException If an IO error occurs 
during the connect
\*            operation
\*
\* @since 0.2.0
\*/
public void connect\(SshConnectionProperties 
properties\)
throws IOException \{
connect\(properties, new 
ConsoleKnownHostsKeyVerification\(\)\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Connect the client to the server with the 
specified properties.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* This call attempts to connect to using the 
connection properties
\* specified. When this method returns the 
connection has been
\* established, the server's identity been 
verified and the connection is
\* ready for user authentication. To use this 
method first create a
\* properties instance and set the required fields.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* SshConnectionProperties properties = new
\*                             
SshConnectionProperties\(\);
\* properties.setHostname\("hostname"\);
\* properties.setPort\(22\);             // Defaults 
to 22
\* // Set the prefered client-&amp;amp;gt;server encryption
\* ssh.setPrefCSEncryption\("blowfish-cbc"\);
\* // Set the prefered server-&amp;amp;gt;client encrpytion
\* ssh.setPrefSCEncrpyion\("3des-cbc"\);
\* ssh.connect\(properties\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Host key verification will be performed using 
the host key verification
\* instance provided:&amp;amp;lt;br&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* // Connect and consult $HOME/.ssh/known\_hosts
\* ssh.connect\("hostname", new 
ConsoleKnownHostsKeyVerification\(\)\);
\* // Connect and allow any host
\* ssh.connect\("hostname", new
\*                 IgnoreHostKeyVerification\(\)\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\* You can provide your own host key verification 
process by implementing the
\* &amp;amp;lt;code&amp;amp;gt;HostKeyVerification&amp;amp;lt;/code&amp;amp;gt; interface.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param properties The connection properties
\* @param hostVerification The host key 
verification instance to consult
\*        for host  key validation
\*
\* @exception UnknownHostException If the host is 
unknown
\* @exception IOException If an IO error occurs 
during the connect
\*            operation
\*
\* @since 0.2.0
\*/
public void connect\(SshConnectionProperties 
properties,
HostKeyVerification hostVerification\)
throws UnknownHostException, IOException \{
TransportProvider provider = 
TransportProviderFactory.connectTransportProvider
\(properties /\*, connectTimeout\*/,
socketTimeout\);

// Start the transport protocol
transport = new TransportProtocolClient
\(hostVerification\);
transport.addEventHandler\(eventHandler\);
transport.startTransportProtocol\(provider, 
properties\);

// Start the authentication protocol
authentication = new 
AuthenticationProtocolClient\(\);
authentication.addEventListener\(eventHandler\);
transport.requestService\(authentication\);
connection = new ConnectionProtocol\(\);

if \(useDefaultForwarding\) \{
forwarding = new ForwardingClient
\(connection\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Sets the timeout value for the key exchange.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* When this time limit is reached the transport 
protocol will initiate a
\* key re-exchange. The default value is one hour 
with the minumin timeout
\* being 60 seconds.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param seconds The number of seconds beofre key 
re-exchange
\*
\* @exception IOException If the timeout value is 
invalid
\*
\* @since 0.2.0
\*/
public void setKexTimeout\(long seconds\) throws 
IOException \{
transport.setKexTimeout\(seconds\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Sets the key exchance transfer limit in 
kilobytes.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Once this amount of data has been transfered 
the transport protocol will
\* initiate a key re-exchange. The default value 
is one gigabyte of data
\* with the mimimun value of 10 kilobytes.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param kilobytes The data transfer limit in 
kilobytes
\*
\* @exception IOException If the data transfer 
limit is invalid
\*/
public void setKexTransferLimit\(long kilobytes\) 
throws IOException \{
transport.setKexTransferLimit\(kilobytes\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Set's the send ignore flag to send random data 
packets.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* If this flag is set to true, then the transport 
protocol will send
\* additional SSH\_MSG\_IGNORE packets with random 
data.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param sendIgnore true if you want to turn on 
random packet data,
\*        otherwise false
\*
\* @since 0.2.0
\*/
public void setSendIgnore\(boolean sendIgnore\) \{
transport.setSendIgnore\(sendIgnore\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Turn the default forwarding manager on/off.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* If this flag is set to false before connection, 
the client will not
\* create a port forwarding manager. Use this to 
provide you own
\* forwarding implementation.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param useDefaultForwarding Set to false if you 
not wish to use the
\*        default forwarding manager.
\*
\* @since 0.2.0
\*/
public void setUseDefaultForwarding\(boolean 
useDefaultForwarding\) \{
this.useDefaultForwarding = 
useDefaultForwarding;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Disconnect the client.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @since 0.2.0
\*/
public void disconnect\(\) \{
if \(connection \!= null\) \{
connection.stop\(\);
\}

if \(transport \!= null\) \{
transport.disconnect\("Terminating 
connection"\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the number of bytes transmitted to the 
remote server.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return The number of bytes transmitted
\*
\* @since 0.2.0
\*/
public long getOutgoingByteCount\(\) \{
return transport.getOutgoingByteCount\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the number of bytes received from the 
remote server.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return The number of bytes received
\*
\* @since 0.2.0
\*/
public long getIncomingByteCount\(\) \{
return transport.getIncomingByteCount\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the number of active channels for this 
client.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* This is the total count of sessions, port 
forwarding, sftp, scp and
\* custom channels currently open.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return The number of active channels
\*
\* @since 0.2.0
\*/
public int getActiveChannelCount\(\) \{
synchronized \(activeChannels\) \{
return activeChannels.size\(\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the list of active channels.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return The list of active channels
\*
\* @since 0.2.0
\*/
public List getActiveChannels\(\) \{
synchronized \(activeChannels\) \{
return \(List\) activeChannels.clone\(\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns true if there is an active session 
channel of the specified
\* type.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* When a session is created, it is assigned a 
default type. For instance,
\* when a session is created it as a type 
of "uninitialized"; however when
\* a shell is started on the session, the type is 
set to "shell". This
\* also occurs for commands where the type is set 
to the command which is
\* executed and subsystems where the type is set 
to the subsystem name.
\* This allows each session to be saved in the 
active session channel's
\* list and recalled later. It is also possible to 
set the session
\* channel's type using the setSessionType method 
of the
\* &amp;amp;lt;code&amp;amp;gt;SessionChannelClient&amp;amp;lt;/code&amp;amp;gt; class.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* if\(ssh.hasActiveSession\("shell"\)\) \{
\*      SessionChannelClient session =
\*           ssh.getActiveSession\("shell"\);
\* \}
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @param type The string specifying the channel 
type
\*
\* @return true if an active session channel 
exists, otherwise false
\*
\* @since 0.2.0
\*/
public boolean hasActiveSession\(String type\) \{
Iterator it = activeChannels.iterator\(\);
Object obj;

while \(it.hasNext\(\)\) \{
obj = it.next\(\);

if \(obj instanceof SessionChannelClient\) \{
if \(\(\(SessionChannelClient\) 
obj\).getSessionType\(\).equals\(type\)\) \{
return true;
\}
\}
\}

return false;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the active session channel of the given 
type.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param type The type fo session channel
\*
\* @return The session channel instance
\*
\* @exception IOException If the session type does 
not exist
\*
\* @since 0.2.0
\*/
public SessionChannelClient getActiveSession
\(String type\)
throws IOException \{
Iterator it = activeChannels.iterator\(\);
Object obj;

while \(it.hasNext\(\)\) \{
obj = it.next\(\);

if \(obj instanceof SessionChannelClient\) \{
if \(\(\(SessionChannelClient\) 
obj\).getSessionType\(\).equals\(type\)\) \{
return \(SessionChannelClient\) obj;
\}
\}
\}

throw new IOException\("There are no active " + 
type + " sessions"\);
\}

/\*\*
\* Determine whether the channel supplied is an 
active channel
\*
\* @param channel
\*
\* @return
\*/
public boolean isActiveChannel\(Channel channel\) \{
return activeChannels.contains\(channel\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open's a session channel on the remote server.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* A session channel may be used to start the 
user's shell, execute a
\* command or start a subsystem such as SFTP.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return An new session channel
\*
\* @exception IOException If authentication has 
not been completed, the
\*            server refuses to open the channel 
or a general IO error
\*            occurs
\*
\* @see 
com.sshtools.j2ssh.session.SessionChannelClient
\* @since 0.2.0
\*/
public SessionChannelClient openSessionChannel\(\) 
throws IOException \{
return openSessionChannel\(null\);
\}

/\*\*
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open's a session channel on the remote server.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* A session channel may be used to start the 
user's shell, execute a
\* command or start a subsystem such as SFTP.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param eventListener an event listner interface 
to add to the channel
\*
\* @return
\*
\* @throws IOException
\* @throws SshException
\*/
public SessionChannelClient openSessionChannel\(
ChannelEventListener eventListener\) throws 
IOException \{
if \(authenticationState \!= 
AuthenticationProtocolState.COMPLETE\) \{
throw new SshException\("Authentication has 
not been completed\!"\);
\}

SessionChannelClient session = new 
SessionChannelClient\(\);
session.addEventListener
\(activeChannelListener\);

if \(\!connection.openChannel\(session, 
eventListener\)\) \{
throw new SshException\("The server refused 
to open a session"\);
\}

return session;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open an SFTP client for file transfer 
operations.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* SftpClient sftp = ssh.openSftpClient\(\);
\* sftp.cd\("foo"\);
\* sftp.put\("somefile.txt"\);
\* sftp.quit\(\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @return Returns an initialized SFTP client
\*
\* @exception IOException If an IO error occurs 
during the operation
\*
\* @see SftpClient
\* @since 0.2.0
\*/
public SftpClient openSftpClient\(\) throws 
IOException \{
return openSftpClient\(null\);
\}


/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open an SFTP client for file transfer 
operations.
\* &amp;amp;lt;/p&amp;amp;gt;
\* with configurable try chmod
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* SftpClient sftp = ssh.openSftpClient\(\);
\* sftp.cd\("foo"\);
\* sftp.put\("somefile.txt"\);
\* sftp.quit\(\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @return Returns an initialized SFTP client
\*
\* @exception IOException If an IO error occurs 
during the operation
\*
\* @see SftpClient
\* @since 0.2.0
\*/
public SftpClient openSftpClient\(boolean tryChmod\) 
throws IOException \{
return openSftpClient\(null, tryChmod\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open an SFTP client for file transfer 
operations. Adds the supplied
\* event listener to the underlying channel.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param eventListener
\*
\* @return
\*
\* @throws IOException
\*/
public SftpClient openSftpClient
\(ChannelEventListener eventListener\)
throws IOException \{
SftpClient sftp = new SftpClient\(this, 
eventListener\);
activeSftpClients.add\(sftp\);

return sftp;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open an SFTP client for file transfer 
operations. Adds the supplied
\* event listener to the underlying channel.
\* with configurable try chmod
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param eventListener
\*
\* @return
\*
\* @throws IOException
\*/
public SftpClient openSftpClient
\(ChannelEventListener eventListener, boolean tryChmod\)
throws IOException \{
SftpClient sftp = new SftpClient\(this, 
eventListener, tryChmod\);
activeSftpClients.add\(sftp\);

return sftp;
\}

/\*\*
\* Determine if there are existing sftp clients 
open
\*
\* @return
\*/
public boolean hasActiveSftpClient\(\) \{
synchronized \(activeSftpClients\) \{
return activeSftpClients.size\(\) &amp;amp;gt; 0;
\}
\}

/\*\*
\* Get an active sftp client
\*
\* @return
\*
\* @throws IOException
\* @throws SshException
\*/
public SftpClient getActiveSftpClient\(\) throws 
IOException \{
synchronized \(activeSftpClients\) \{
if \(activeSftpClients.size\(\) &amp;amp;gt; 0\) \{
return \(SftpClient\) 
activeSftpClients.get\(0\);
\} else \{
throw new SshException\("There are no 
active SFTP clients"\);
\}
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open an SCP client for file transfer operations 
where SFTP is not
\* supported.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Sets the local working directory to the user's 
home directory
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* ScpClient scp = ssh.openScpClient\(\);
\* scp.put\("somefile.txt"\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @return An initialized SCP client
\*
\* @exception IOException If an IO error occurs 
during the operation
\*
\* @see ScpClient
\* @since 0.2.0
\*/
public ScpClient openScpClient\(\) throws 
IOException \{
return new ScpClient\(new File
\(System.getProperty\("user.home"\)\), this,
false, activeChannelListener\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open an SCP client for file transfer operations 
where SFTP is not
\* supported.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* This method sets a local current working 
directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* ScpClient scp = ssh.openScpClient\("foo"\);
\* scp.put\("somefile.txt"\);
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @param cwd The local directory as the base for 
all local files
\*
\* @return An intialized SCP client
\*
\* @exception IOException If an IO error occurs 
during the operation
\*
\* @see SftpClient
\* @since 0.2.0
\*/
public ScpClient openScpClient\(File cwd\) throws 
IOException \{
return new ScpClient\(cwd, this, false, 
activeChannelListener\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open's an Sftp channel.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Use this sftp channel if you require a lower 
level api into the SFTP
\* protocol.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return an initialized sftp subsystem instance
\*
\* @throws IOException if an IO error occurs or 
the channel cannot be
\*         opened
\*
\* @since 0.2.0
\*/
public SftpSubsystemClient openSftpChannel\(\) 
throws IOException \{
return openSftpChannel\(null\);
\}

/\*\*
\* Open an SftpSubsystemChannel. For advanced use 
only
\*
\* @param eventListener
\*
\* @return
\*
\* @throws IOException
\* @throws SshException
\*/
public SftpSubsystemClient openSftpChannel\(
ChannelEventListener eventListener\) throws 
IOException \{
SessionChannelClient session = 
openSessionChannel\(eventListener\);
SftpSubsystemClient sftp = new 
SftpSubsystemClient\(\);

if \(\!openChannel\(sftp\)\) \{
throw new SshException\("The SFTP subsystem 
failed to start"\);
\}

// Initialize SFTP
if \(\!sftp.initialize\(\)\) \{
throw new SshException\(
"The SFTP Subsystem could not be 
initialized"\);
\}

return sftp;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Open's a channel.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Call this method to open a custom channel. This 
method is used by all
\* other channel opening methods. For example the 
openSessionChannel
\* method could be implemented as:&amp;amp;lt;br&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;
\* SessionChannelClient session =
\*                 new SessionChannelClient\(\);
\* if\(ssh.openChannel\(session\)\) \{
\*    // Channel is now open
\* \}
\* &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param channel
\*
\* @return true if the channel was opened, 
otherwise false
\*
\* @exception IOException if an IO error occurs
\* @throws SshException
\*
\* @since 0.2.0
\*/
public boolean openChannel\(Channel channel\) throws 
IOException \{
if \(authenticationState \!= 
AuthenticationProtocolState.COMPLETE\) \{
throw new SshException\("Authentication has 
not been completed\!"\);
\}

// Open the channel providing our channel 
listener so we can track
return connection.openChannel\(channel, 
activeChannelListener\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Instructs the underlying connection protocol to 
allow channels of the
\* given type to be opened by the server.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* The client does not allow channels to be opened 
by default. Call this
\* method to allow the server to open channels by 
providing a
\* &amp;amp;lt;code&amp;amp;gt;ChannelFactory&amp;amp;lt;/code&amp;amp;gt; implementation to 
create instances upon
\* request.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param channelName The channel type name
\* @param cf The factory implementation that will 
create instances of the
\*        channel when a channel open request is 
recieved.
\*
\* @exception IOException if an IO error occurs
\*
\* @since 0.2.0
\*/
public void allowChannelOpen\(String channelName, 
ChannelFactory cf\)
throws IOException \{
connection.addChannelFactory\(channelName, cf\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Stops the specified channel type from being 
opended.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param channelName The channel type name
\*
\* @throws IOException if an IO error occurs
\*
\* @since 0.2.1
\*/
public void denyChannelOpen\(String channelName\) 
throws IOException \{
connection.removeChannelFactory\(channelName\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Send a global request to the server.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* The SSH specification provides a global request 
mechanism which is used
\* for starting/stopping remote forwarding. This 
is a general mechanism
\* which can be used for other purposes if the 
server supports the global
\* requests.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param requestName The name of the global 
request
\* @param wantReply true if the server should send 
an explict reply
\* @param requestData the global request data
\*
\* @return true if the global request succeeded or 
wantReply==false,
\*         otherwise false
\*
\* @throws IOException if an IO error occurs
\*
\* @since 0.2.0
\*/
public byte\[\] sendGlobalRequest\(String 
requestName, boolean wantReply,
byte\[\] requestData\) throws IOException \{
return connection.sendGlobalRequest
\(requestName, wantReply, requestData\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Implements the 
&amp;amp;lt;code&amp;amp;gt;ChannelEventListener&amp;amp;lt;/code&amp;amp;gt; interface to provide
\* real time tracking of active channels.
\* &amp;amp;lt;/p&amp;amp;gt;
\*/
class ActiveChannelEventListener extends 
ChannelEventAdapter \{
/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Adds the channel to the active channel list.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param channel The channel being opened
\*/
public void onChannelOpen\(Channel channel\) \{
synchronized \(activeChannels\) \{
activeChannels.add\(channel\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Removes the closed channel from the clients 
active channels list.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param channel The channle being closed
\*/
public void onChannelClose\(Channel channel\) \{
synchronized \(activeChannels\) \{
activeChannels.remove\(channel\);
\}
\}
\}
\}


Adapted code SftpClient.java :


/\*
\*  SSHTools - Java SSH2 API
\*
\*  Copyright \(C\) 2002-2003 Lee David Painter and 
Contributors.
\*
\*  Contributions made by:
\*
\*  Brett Smith
\*  Richard Pernavas
\*  Erwin Bolwidt
\*
\*  This program is free software; you can 
redistribute it and/or
\*  modify it under the terms of the GNU Library 
General Public License
\*  as published by the Free Software Foundation; 
either version 2 of
\*  the License, or \(at your option\) any later version.
\*
\*  You may also distribute it and/or modify it under 
the terms of the
\*  Apache style J2SSH Software License. A copy of 
which should have
\*  been provided with the distribution.
\*
\*  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
\*  License document supplied with your distribution 
for more details.
\*
\*/
package com.sshtools.j2ssh;

import com.sshtools.j2ssh.connection.\*;
import com.sshtools.j2ssh.io.\*;
import com.sshtools.j2ssh.sftp.\*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.Vector;


/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Implements a Secure File Transfer \(SFTP\) client.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @author Lee David Painter
\* @version $Revision: 1.44 $
\*
\* @since 0.2.0
\*/
public class SftpClient \{
SftpSubsystemClient sftp;
String cwd;
String lcwd;
private int BLOCKSIZE = 65535;
boolean tryChmod = true ;

// Default permissions is determined by 
default\_permissions ^ umask
int umask = 0022;
int default\_permissions = 0777;

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Constructs the SFTP client.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param ssh the &amp;amp;lt;code&amp;amp;gt;SshClient&amp;amp;lt;/code&amp;amp;gt; instance
\*
\* @throws IOException if an IO error occurs
\*/
SftpClient\(SshClient ssh\) throws IOException \{
this\(ssh, null\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Constructs the SFTP client.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param ssh the &amp;amp;lt;code&amp;amp;gt;SshClient&amp;amp;lt;/code&amp;amp;gt; instance
\* with configurable try chmod
\*
\* @throws IOException if an IO error occurs
\*/
SftpClient\(SshClient ssh, boolean tryChmod\) throws 
IOException \{
this\(ssh, null, tryChmod\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Constructs the SFTP client with a given channel 
event listener.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param ssh the &amp;amp;lt;code&amp;amp;gt;SshClient&amp;amp;lt;/code&amp;amp;gt; instance
\* @param eventListener an event listener 
implementation
\*
\* @throws IOException if an IO error occurs
\*/
SftpClient\(SshClient ssh, ChannelEventListener 
eventListener\)
throws IOException \{
if \(\!ssh.isConnected\(\)\) \{
throw new IOException\("SshClient is not 
connected"\);
\}

this.sftp = ssh.openSftpChannel\(eventListener\);

// Get the users default directory
cwd = sftp.getDefaultDirectory\(\);
lcwd = System.getProperty\("user.home"\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Constructs the SFTP client with a given channel 
event listener.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param ssh the &amp;amp;lt;code&amp;amp;gt;SshClient&amp;amp;lt;/code&amp;amp;gt; instance
\* @param eventListener an event listener 
implementation
\* with configurable try chmod
\*
\* @throws IOException if an IO error occurs
\*/
SftpClient\(SshClient ssh, ChannelEventListener 
eventListener, boolean tryChmod\)
throws IOException \{
if \(\!ssh.isConnected\(\)\) \{
throw new IOException\("SshClient is not 
connected"\);
\}

this.sftp = ssh.openSftpChannel\(eventListener\);
this.tryChmod = tryChmod ;

// Get the users default directory
cwd = sftp.getDefaultDirectory\(\);
lcwd = System.getProperty\("user.home"\);
\}

/\*\*
\* Sets the umask used by this client.
\* @param umask
\* @return the previous umask value
\*/
public int umask\(int umask\) \{
int old = umask;
this.umask = umask;

return old;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Changes the working directory on the remote 
server.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param dir the new working directory
\*
\* @throws IOException if an IO error occurs or 
the file does not exist
\* @throws FileNotFoundException
\*
\* @since 0.2.0
\*/
public void cd\(String dir\) throws IOException \{
try \{
String actual;

if \(dir.equals\(""\)\) \{
actual = sftp.getDefaultDirectory\(\);
\} else \{
actual = resolveRemotePath\(dir\);
actual = sftp.getAbsolutePath\(actual\);
\}

FileAttributes attr = sftp.getAttributes
\(actual\);

if \(\!attr.isDirectory\(\)\) \{
throw new IOException\(dir + " is not a 
directory"\);
\}

cwd = actual;
\} catch \(IOException ex\) \{
throw new FileNotFoundException\(dir + " 
could not be found"\);
\}
\}

private File resolveLocalPath\(String path\) throws 
IOException \{
File f = new File\(path\);

if \(\!f.isAbsolute\(\)\) \{
f = new File\(lcwd, path\);
\}

return f;
\}

private String resolveRemotePath\(String path\) 
throws IOException \{
verifyConnection\(\);

String actual;

if \(\!path.startsWith\("/"\)\) \{
actual = cwd + \(cwd.endsWith
\("/"\) ? "" : "/"\) + path;
\} else \{
actual = path;
\}

return actual;
\}

private void verifyConnection\(\) throws 
SshException \{
if \(sftp.isClosed\(\)\) \{
throw new SshException\("The SFTP 
connection has been closed"\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Creates a new directory on the remote server. 
This method will throw an
\* exception if the directory already exists. To 
create directories and
\* disregard any errors use the 
&amp;amp;lt;code&amp;amp;gt;mkdirs&amp;amp;lt;/code&amp;amp;gt; method.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param dir the name of the new directory
\*
\* @throws IOException if an IO error occurs or if 
the directory already
\*         exists
\*
\* @since 0.2.0
\*/
public void mkdir\(String dir\) throws IOException \{
String actual = resolveRemotePath\(dir\);

try \{
FileAttributes attrs = stat\(actual\);

if \(\!attrs.isDirectory\(\)\) \{
throw new IOException\("File already 
exists named " + dir\);
\}
\} catch \(IOException ex\) \{
sftp.makeDirectory\(actual\);
if \(tryChmod\) chmod\(default\_permissions ^ 
umask, actual\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Create a directory or set of directories. This 
method will not fail even
\* if the directories exist. It is advisable to 
test whether the directory
\* exists before attempting an operation by using 
the &amp;amp;lt;code&amp;amp;gt;stat&amp;amp;lt;/code&amp;amp;gt;
\* method to return the directories attributes.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param dir the path of directories to create.
\*/
public void mkdirs\(String dir\) \{
StringTokenizer tokens = new StringTokenizer
\(dir, "/"\);
String path = dir.startsWith\("/"\) ? "/" : "";

while \(tokens.hasMoreElements\(\)\) \{
path += \(String\) tokens.nextElement\(\);

try \{
stat\(path\);
\} catch \(IOException ex\) \{
try \{
mkdir\(path\);
\} catch \(IOException ex2\) \{
\}
\}

path += "/";
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the absolute path name of the current 
remote working directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return the absolute path of the remote working 
directory.
\*
\* @since 0.2.0
\*/
public String pwd\(\) \{
return cwd;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* List the contents of the current remote working 
directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns a list of &amp;amp;lt;code&amp;amp;gt;SftpFile&amp;amp;lt;/code&amp;amp;gt; 
instances for the current
\* working directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return a list of SftpFile for the current 
working directory
\*
\* @throws IOException if an IO error occurs
\*
\* @see com.sshtools.j2ssh.sftp.SftpFile
\* @since 0.2.0
\*/
public List ls\(\) throws IOException \{
return ls\(cwd\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* List the contents remote directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns a list of &amp;amp;lt;code&amp;amp;gt;SftpFile&amp;amp;lt;/code&amp;amp;gt; 
instances for the remote
\* directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param path the path on the remote server to 
list
\*
\* @return a list of SftpFile for the remote 
directory
\*
\* @throws IOException if an IO error occurs
\*
\* @see com.sshtools.j2ssh.sftp.SftpFile
\* @since 0.2.0
\*/
public List ls\(String path\) throws IOException \{
String actual = resolveRemotePath\(path\);
FileAttributes attrs = sftp.getAttributes
\(actual\);

if \(\!attrs.isDirectory\(\)\) \{
throw new IOException\(path + " is not a 
directory"\);
\}

SftpFile file = sftp.openDirectory\(actual\);
Vector children = new Vector\(\);

while \(sftp.listChildren\(file, children\) &amp;amp;gt; -1\) 
\{
;
\}

file.close\(\);

return children;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Changes the local working directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param path the path to the new working 
directory
\*
\* @throws IOException if an IO error occurs
\*
\* @since 0.2.0
\*/
public void lcd\(String path\) throws IOException \{
File actual;

if \(\!isLocalAbsolutePath\(path\)\) \{
actual = new File\(lcwd, path\);
\} else \{
actual = new File\(path\);
\}

if \(\!actual.isDirectory\(\)\) \{
throw new IOException\(path + " is not a 
directory"\);
\}

lcwd = actual.getCanonicalPath\(\);
\}

private static boolean isLocalAbsolutePath\(String 
path\) \{
return \(new File\(path\)\).isAbsolute\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the absolute path to the local working 
directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return the absolute path of the local working 
directory.
\*
\* @since 0.2.0
\*/
public String lpwd\(\) \{
return lcwd;
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Download the remote file to the local computer.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param path the path to the remote file
\* @param progress
\*
\* @return
\*
\* @throws IOException if an IO error occurs of 
the file does not exist
\* @throws TransferCancelledException
\*
\* @since 0.2.0
\*/
public FileAttributes get\(String path, 
FileTransferProgress progress\)
throws IOException, TransferCancelledException 
\{
String localfile;

if \(path.lastIndexOf\("/"\) &amp;amp;gt; -1\) \{
localfile = path.substring\(path.lastIndexOf
\("/"\) + 1\);
\} else \{
localfile = path;
\}

return get\(path, localfile, progress\);
\}

/\*\*
\*
\*
\* @param path
\*
\* @return
\*
\* @throws IOException
\*/
public FileAttributes get\(String path\) throws 
IOException \{
return get\(path, \(FileTransferProgress\) null\);
\}

private void transferFile\(InputStream in, 
OutputStream out\)
throws IOException, TransferCancelledException 
\{
transferFile\(in, out, null\);
\}

private void transferFile\(InputStream in, 
OutputStream out,
FileTransferProgress progress\)
throws IOException, TransferCancelledException 
\{
try \{
long bytesSoFar = 0;
byte\[\] buffer = new byte\[BLOCKSIZE\];
int read;

while \(\(read = in.read\(buffer\)\) &amp;amp;gt; -1\) \{
if \(\(progress \!= null\) &amp;amp;&amp;amp; 
progress.isCancelled\(\)\) \{
throw new 
TransferCancelledException\(\);
\}

if \(read &amp;amp;gt; 0\) \{
out.write\(buffer, 0, read\);

//out.flush\(\);
bytesSoFar += read;

if \(progress \!= null\) \{
progress.progressed
\(bytesSoFar\);
\}
\}
\}
\} finally \{
try \{
in.close\(\);
out.close\(\);
\} catch \(IOException ex\) \{
\}
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Download the remote file to the local computer. 
If the paths provided
\* are not absolute the current working directory 
is used.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param remote the path/name of the remote file
\* @param local the path/name to place the file on 
the local computer
\* @param progress
\*
\* @return
\*
\* @throws IOException if an IO error occurs or 
the file does not exist
\* @throws TransferCancelledException
\*
\* @since 0.2.0
\*/
public FileAttributes get\(String remote, String 
local,
FileTransferProgress progress\)
throws IOException, TransferCancelledException 
\{
File localPath = resolveLocalPath\(local\);

if \(\!localPath.exists\(\)\) \{
localPath.getParentFile\(\).mkdirs\(\);
localPath.createNewFile\(\);
\}

FileOutputStream out = new FileOutputStream
\(localPath\);

return get\(remote, out, progress\);
\}

/\*\*
\*
\*
\* @param remote
\* @param local
\*
\* @return
\*
\* @throws IOException
\*/
public FileAttributes get\(String remote, String 
local\)
throws IOException \{
return get\(remote, local, null\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Download the remote file writing it to the 
specified
\* &amp;amp;lt;code&amp;amp;gt;OutputStream&amp;amp;lt;/code&amp;amp;gt;. The OutputStream is 
closed by this mehtod
\* even if the operation fails.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param remote the path/name of the remote file
\* @param local the OutputStream to write
\* @param progress
\*
\* @return
\*
\* @throws IOException if an IO error occurs or 
the file does not exist
\* @throws TransferCancelledException
\*
\* @since 0.2.0
\*/
public FileAttributes get\(String remote, 
OutputStream local,
FileTransferProgress progress\)
throws IOException, TransferCancelledException 
\{
String remotePath = resolveRemotePath\(remote\);
FileAttributes attrs = stat\(remotePath\);

if \(progress \!= null\) \{
progress.started\(attrs.getSize\(\).longValue
\(\), remotePath\);
\}

SftpFileInputStream in = new 
SftpFileInputStream\(sftp.openFile\(
remotePath, 
SftpSubsystemClient.OPEN\_READ\)\);
transferFile\(in, local, progress\);

if \(progress \!= null\) \{
progress.completed\(\);
\}

return attrs;
\}

/\*\*
\*
\*
\* @param remote
\* @param local
\*
\* @return
\*
\* @throws IOException
\*/
public FileAttributes get\(String remote, 
OutputStream local\)
throws IOException \{
return get\(remote, local, null\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the state of the SFTP client. The 
client is closed if the
\* underlying session channel is closed. Invoking 
the &amp;amp;lt;code&amp;amp;gt;quit&amp;amp;lt;/code&amp;amp;gt;
\* method of this object will close the underlying 
session channel.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @return true if the client is still connected, 
otherwise false
\*
\* @since 0.2.0
\*/
public boolean isClosed\(\) \{
return sftp.isClosed\(\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Upload a file to the remote computer.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param local the path/name of the local file
\* @param progress
\*
\* @return
\*
\* @throws IOException if an IO error occurs or 
the file does not exist
\* @throws TransferCancelledException
\*
\* @since 0.2.0
\*/
public void put\(String local, FileTransferProgress 
progress\)
throws IOException, TransferCancelledException 
\{
File f = new File\(local\);
put\(local, f.getName\(\), progress\);
\}

/\*\*
\*
\*
\* @param local
\*
\* @return
\*
\* @throws IOException
\*/
public void put\(String local\) throws IOException \{
put\(local, \(FileTransferProgress\) null\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Upload a file to the remote computer. If the 
paths provided are not
\* absolute the current working directory is used.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param local the path/name of the local file
\* @param remote the path/name of the destination 
file
\* @param progress
\*
\* @return
\*
\* @throws IOException if an IO error occurs or 
the file does not exist
\* @throws TransferCancelledException
\*
\* @since 0.2.0
\*/
public void put\(String local, String remote, 
FileTransferProgress progress\)
throws IOException, TransferCancelledException 
\{
File localPath = resolveLocalPath\(local\);
FileInputStream in = new FileInputStream
\(localPath\);

try \{
FileAttributes attrs = stat\(remote\);

if \(attrs.isDirectory\(\)\) \{
File f = new File\(local\);
remote += \(\(remote.endsWith
\("/"\) ? "" : "/"\) + f.getName\(\)\);
\}
\} catch \(IOException ex\) \{
\}

put\(in, remote, progress\);
\}

/\*\*
\*
\*
\* @param local
\* @param remote
\*
\* @return
\*
\* @throws IOException
\*/
public void put\(String local, String remote\) 
throws IOException \{
put\(local, remote, null\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Upload a file to the remote computer reading 
from the specified &amp;amp;lt;code&amp;amp;gt;
\* InputStream&amp;amp;lt;/code&amp;amp;gt;. The InputStream is closed, 
even if the operation
\* fails.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param in the InputStream being read
\* @param remote the path/name of the destination 
file
\* @param progress
\*
\* @return
\*
\* @throws IOException if an IO error occurs
\* @throws TransferCancelledException
\*
\* @since 0.2.0
\*/
public void put\(InputStream in, String remote, 
FileTransferProgress progress\)
throws IOException, TransferCancelledException 
\{
String remotePath = resolveRemotePath\(remote\);
SftpFileOutputStream out;
FileAttributes attrs;
boolean newfile = false;

try \{
attrs = stat\(remotePath\);
out = new SftpFileOutputStream
\(sftp.openFile\(remotePath,

SftpSubsystemClient.OPEN\_CREATE |

SftpSubsystemClient.OPEN\_TRUNCATE |

SftpSubsystemClient.OPEN\_WRITE\)\);
\} catch \(IOException ex\) \{
attrs = new FileAttributes\(\);
newfile = true;
attrs.setPermissions\(new UnsignedInteger32
\(default\_permissions ^
umask\)\);
out = new SftpFileOutputStream
\(sftp.openFile\(remotePath,

SftpSubsystemClient.OPEN\_CREATE |

SftpSubsystemClient.OPEN\_WRITE, attrs\)\);
\}

if \(progress \!= null\) \{
progress.started\(in.available\(\), 
remotePath\);
\}

transferFile\(in, out, progress\);

if \(progress \!= null\) \{
progress.completed\(\);
\}

// Set the permissions here since at creation 
they dont always work
if \(newfile\) \{
if \(tryChmod\) chmod\(default\_permissions ^ 
umask, remotePath\);
\}
\}

/\*\*
\*
\*
\* @param in
\* @param remote
\*
\* @return
\*
\* @throws IOException
\*/
public void put\(InputStream in, String remote\) 
throws IOException \{
put\(in, remote, null\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Sets the user ID to owner for the file or 
directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param uid numeric user id of the new owner
\* @param path the path to the remote 
file/directory
\*
\* @throws IOException if an IO error occurs or 
the file does not exist
\*
\* @since 0.2.0
\*/
public void chown\(int uid, String path\) throws 
IOException \{
String actual = resolveRemotePath\(path\);
FileAttributes attrs = sftp.getAttributes
\(actual\);
attrs.setUID\(new UnsignedInteger32\(uid\)\);
sftp.setAttributes\(actual, attrs\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Sets the group ID for the file or directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param gid the numeric group id for the new 
group
\* @param path the path to the remote 
file/directory
\*
\* @throws IOException if an IO error occurs or 
the file does not exist
\*
\* @since 0.2.0
\*/
public void chgrp\(int gid, String path\) throws 
IOException \{
String actual = resolveRemotePath\(path\);
FileAttributes attrs = sftp.getAttributes
\(actual\);
attrs.setGID\(new UnsignedInteger32\(gid\)\);
sftp.setAttributes\(actual, attrs\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Changes the access permissions or modes of the 
specified file or
\* directory.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Modes determine who can read, change or execute 
a file.
\* &amp;amp;lt;/p&amp;amp;gt;
\* &amp;amp;lt;blockquote&amp;amp;gt;&amp;amp;lt;pre&amp;amp;gt;Absolute modes are octal 
numbers specifying the complete list of
\* attributes for the files; you specify 
attributes by OR'ing together
\* these bits.
\*
\* 0400       Individual read
\* 0200       Individual write
\* 0100       Individual execute \(or list 
directory\)
\* 0040       Group read
\* 0020       Group write
\* 0010       Group execute
\* 0004       Other read
\* 0002       Other write
\* 0001       Other execute &amp;amp;lt;/pre&amp;amp;gt;&amp;amp;lt;/blockquote&amp;amp;gt;
\*
\* @param permissions the absolute mode of the 
file/directory
\* @param path the path to the file/directory on 
the remote server
\*
\* @throws IOException if an IO error occurs or 
the file if not found
\*
\* @since 0.2.0
\*/
public void chmod\(int permissions, String path\) 
throws IOException \{
String actual = resolveRemotePath\(path\);
sftp.changePermissions\(actual, permissions\);
\}

public void umask\(String umask\) throws IOException 
\{
try \{
this.umask = Integer.parseInt\(umask, 8\);
\} catch \(NumberFormatException ex\) \{
throw new IOException\(
"umask must be 4 digit octal number 
e.g. 0022"\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Rename a file on the remote computer.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param oldpath the old path
\* @param newpath the new path
\*
\* @throws IOException if an IO error occurs
\*
\* @since 0.2.0
\*/
public void rename\(String oldpath, String newpath\)
throws IOException \{
String from = resolveRemotePath\(oldpath\);
String to = resolveRemotePath\(newpath\);
sftp.renameFile\(from, to\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Remove a file or directory from the remote 
computer.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param path the path of the remote 
file/directory
\*
\* @throws IOException if an IO error occurs
\*
\* @since 0.2.0
\*/
public void rm\(String path\) throws IOException \{
String actual = resolveRemotePath\(path\);
FileAttributes attrs = sftp.getAttributes
\(actual\);

if \(attrs.isDirectory\(\)\) \{
sftp.removeDirectory\(actual\);
\} else \{
sftp.removeFile\(actual\);
\}
\}

/\*\*
\*
\*
\* @param path
\* @param force
\* @param recurse
\*
\* @throws IOException
\*/
public void rm\(String path, boolean force, boolean 
recurse\)
throws IOException \{
String actual = resolveRemotePath\(path\);
FileAttributes attrs = sftp.getAttributes
\(actual\);
SftpFile file;

if \(attrs.isDirectory\(\)\) \{
List list = ls\(path\);

if \(\!force &amp;amp;&amp;amp; \(list.size\(\) &amp;amp;gt; 0\)\) \{
throw new IOException\(
"You cannot delete non-empty 
directory, use force=true to overide"\);
\} else \{
for \(Iterator it = list.iterator\(\); 
it.hasNext\(\);\) \{
file = \(SftpFile\) it.next\(\);

if \(file.isDirectory\(\) &amp;amp;&amp;amp; \!
file.getFilename\(\).equals\("."\) &amp;amp;&amp;amp;
\!file.getFilename\(\).equals
\(".."\)\) \{
if \(recurse\) \{
rm\(file.getAbsolutePath\(\), 
force, recurse\);
\} else \{
throw new IOException\(
"Directory has 
contents, cannot delete without recurse=true"\);
\}
\} else if \(file.isFile\(\)\) \{
sftp.removeFile
\(file.getAbsolutePath\(\)\);
\}
\}
\}

sftp.removeDirectory\(actual\);
\} else \{
sftp.removeFile\(actual\);
\}
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Create a symbolic link on the remote computer.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param path the path to the existing file
\* @param link the new link
\*
\* @throws IOException if an IO error occurs or 
the operation is not
\*         supported on the remote platform
\*
\* @since 0.2.0
\*/
public void symlink\(String path, String link\) 
throws IOException \{
String actualPath = resolveRemotePath\(path\);
String actualLink = resolveRemotePath\(link\);
sftp.createSymbolicLink\(actualPath, 
actualLink\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Returns the attributes of the file from the 
remote computer.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @param path the path of the file on the remote 
computer
\*
\* @return the attributes
\*
\* @throws IOException if an IO error occurs or 
the file does not exist
\*
\* @see com.sshtools.j2ssh.sftp.FileAttributes
\* @since 0.2.0
\*/
public FileAttributes stat\(String path\) throws 
IOException \{
String actual = resolveRemotePath\(path\);

return sftp.getAttributes\(actual\);
\}

/\*\*
\*
\*
\* @param path
\*
\* @return
\*
\* @throws IOException
\*/
public String getAbsolutePath\(String path\) throws 
IOException \{
String actual = resolveRemotePath\(path\);

return sftp.getAbsolutePath\(path\);
\}

/\*\*
\* &amp;amp;lt;p&amp;amp;gt;
\* Close the SFTP client.
\* &amp;amp;lt;/p&amp;amp;gt;
\*
\* @throws IOException
\*
\* @since 0.2.0
\*/
public void quit\(\) throws IOException \{
sftp.close\(\);
\}

/\*\*
\*
\*
\* @param localdir
\* @param remotedir
\* @param recurse
\* @param sync
\* @param commit
\* @param progress
\*
\* @return
\*
\* @throws IOException
\*/
public DirectoryOperation copyLocalDirectory
\(String localdir,
String remotedir, boolean recurse, boolean 
sync, boolean commit,
FileTransferProgress progress\) throws 
IOException \{
DirectoryOperation op = new DirectoryOperation
\(\);

// Record the previous
String pwd = pwd\(\);
String lpwd = lpwd\(\);
File local = resolveLocalPath\(localdir\);
remotedir = resolveRemotePath\(remotedir\);
remotedir += \(remotedir.endsWith
\("/"\) ? "" : "/"\);
remotedir += local.getName\(\);
remotedir += \(remotedir.endsWith
\("/"\) ? "" : "/"\);

// Setup the remote directory if were 
committing
if \(commit\) \{
try \{
FileAttributes attrs = stat\(remotedir\);
\} catch \(IOException ex\) \{
mkdir\(remotedir\);
\}
\}

// List the local files and verify against the 
remote server
File\[\] ls = local.listFiles\(\);

if \(ls \!= null\) \{
for \(int i = 0; i &amp;amp;lt; ls.length; i++\) \{
if \(ls\[i\].isDirectory\(\) &amp;amp;&amp;amp; \!ls
\[i\].getName\(\).equals\("."\) &amp;amp;&amp;amp;
\!ls\[i\].getName\(\).equals\(".."\)\) 
\{
if \(recurse\) \{
File f = new File\(local, ls
\[i\].getName\(\)\);
op.addDirectoryOperation
\(copyLocalDirectory\(
f.getAbsolutePath\(\), 
remotedir, recurse, sync,
commit, progress\), f\);
\}
\} else if \(ls\[i\].isFile\(\)\) \{
try \{
FileAttributes attrs = stat
\(remotedir +
ls\[i\].getName\(\)\);

if \(\(ls\[i\].length\(\) == 
attrs.getSize\(\).longValue\(\)\) &amp;amp;&amp;amp;
\(\(ls\[i\].lastModified
\(\) / 1000\) == attrs.getModifiedTime\(\)

.longValue\(\)\)\) \{
op.addUnchangedFile\(ls\[i\]\);
\} else \{
op.addUpdatedFile\(ls\[i\]\);
\}
\} catch \(IOException ex1\) \{
op.addNewFile\(ls\[i\]\);
\}

if \(commit\) \{
put\(ls\[i\].getAbsolutePath\(\),
remotedir + ls\[i\].getName
\(\), progress\);

FileAttributes attrs = stat
\(remotedir +
ls\[i\].getName\(\)\);
attrs.setTimes\(new 
UnsignedInteger32\(
ls\[i\].lastModified\(\) / 
1000\),
new UnsignedInteger32\(ls
\[i\].lastModified\(\) / 1000\)\);
sftp.setAttributes\(remotedir + 
ls\[i\].getName\(\), attrs\);
\}
\}
\}
\}

if \(sync\) \{
// List the contents of the new local 
directory and remove any
// files/directories that were not updated
try \{
List files = ls\(remotedir\);
SftpFile file;
File f;

for \(Iterator it = files.iterator\(\); 
it.hasNext\(\);\) \{
file = \(SftpFile\) it.next\(\);

// Create a local file object to 
test for its existence
f = new File\(local, 
file.getFilename\(\)\);

if \(\!op.containsFile\(file\) &amp;amp;&amp;amp;
\!file.getFilename\(\).equals
\("."\) &amp;amp;&amp;amp;
\!file.getFilename\(\).equals
\(".."\)\) \{
op.addDeletedFile\(file\);

if \(commit\) \{
if \(file.isDirectory\(\)\) \{
// Recurse through the 
directory, deleting stuff
recurseMarkForDeletion
\(file, op\);

if \(commit\) \{
rm
\(file.getAbsolutePath\(\), true, true\);
\}
\} else if \(file.isFile\(\)\) \{
rm\(file.getAbsolutePath
\(\)\);
\}
\}
\}
\}
\} catch \(IOException ex2\) \{
// Ignorew since if it does not exist 
we cant delete it
\}
\}

// Return the operation details
return op;
\}

/\*\*
\*
\*
\* @param eventListener
\*/
public void addEventListener\(ChannelEventListener 
eventListener\) \{
sftp.addEventListener\(eventListener\);
\}

private void recurseMarkForDeletion\(SftpFile file, 
DirectoryOperation op\)
throws IOException \{
List list = ls\(file.getAbsolutePath\(\)\);
op.addDeletedFile\(file\);

for \(Iterator it = list.iterator\(\); it.hasNext
\(\);\) \{
file = \(SftpFile\) it.next\(\);

if \(file.isDirectory\(\) &amp;amp;&amp;amp; \!file.getFilename
\(\).equals\("."\) &amp;amp;&amp;amp;
\!file.getFilename\(\).equals\(".."\)\) \{
recurseMarkForDeletion\(file, op\);
\} else if \(file.isFile\(\)\) \{
op.addDeletedFile\(file\);
\}
\}
\}

private void recurseMarkForDeletion\(File file, 
DirectoryOperation op\)
throws IOException \{
File\[\] list = file.listFiles\(\);
op.addDeletedFile\(file\);

if \(list \!= null\) \{
for \(int i = 0; i &amp;amp;lt; list.length; i++\) \{
file = list\[i\];

if \(file.isDirectory\(\) &amp;amp;&amp;amp; \!file.getName
\(\).equals\("."\) &amp;amp;&amp;amp;
\!file.getName\(\).equals\(".."\)\) \{
recurseMarkForDeletion\(file, op\);
\} else if \(file.isFile\(\)\) \{
op.addDeletedFile\(file\);
\}
\}
\}
\}

/\*\*
\*
\*
\* @param remotedir
\* @param localdir
\* @param recurse
\* @param sync
\* @param commit
\* @param progress
\*
\* @return
\*
\* @throws IOException
\*/
public DirectoryOperation copyRemoteDirectory
\(String remotedir,
String localdir, boolean recurse, boolean 
sync, boolean commit,
FileTransferProgress progress\) throws 
IOException \{
// Create an operation object to hold the 
information
DirectoryOperation op = new DirectoryOperation
\(\);

// Record the previous working directoies
String pwd = pwd\(\);
String lpwd = lpwd\(\);
cd\(remotedir\);

// Setup the local cwd
String base = remotedir;
int idx = base.lastIndexOf\('/'\);

if \(idx \!= -1\) \{
base = base.substring\(idx + 1\);
\}

File local = new File\(localdir, base\);

//				File local = 
new File\(localdir, remotedir\);
if \(\!local.isAbsolute\(\)\) \{
local = new File\(lpwd\(\), localdir\);
\}

if \(\!local.exists\(\) &amp;amp;&amp;amp; commit\) \{
local.mkdir\(\);
\}

List files = ls\(\);
SftpFile file;
File f;

for \(Iterator it = files.iterator\(\); it.hasNext
\(\);\) \{
file = \(SftpFile\) it.next\(\);

if \(file.isDirectory\(\) &amp;amp;&amp;amp; \!file.getFilename
\(\).equals\("."\) &amp;amp;&amp;amp;
\!file.getFilename\(\).equals\(".."\)\) \{
if \(recurse\) \{
f = new File\(local, 
file.getFilename\(\)\);
op.addDirectoryOperation
\(copyRemoteDirectory\(
file.getFilename\(\), 
local.getAbsolutePath\(\),
recurse, sync, commit, 
progress\), f\);
\}
\} else if \(file.isFile\(\)\) \{
f = new File\(local, file.getFilename
\(\)\);

if \(f.exists\(\) &amp;amp;&amp;amp;
\(f.length\(\) == 
file.getAttributes\(\).getSize\(\).longValue\(\)\) &amp;amp;&amp;amp;
\(\(f.lastModified\(\) / 1000\) == 
file.getAttributes\(\)

.getModifiedTime\(\)

.longValue\(\)\)\) \{
if \(commit\) \{
op.addUnchangedFile\(f\);
\} else \{
op.addUnchangedFile\(file\);
\}

continue;
\}

if \(f.exists\(\)\) \{
if \(commit\) \{
op.addUpdatedFile\(f\);
\} else \{
op.addUpdatedFile\(file\);
\}
\} else \{
if \(commit\) \{
op.addNewFile\(f\);
\} else \{
op.addNewFile\(file\);
\}
\}

if \(commit\) \{
FileAttributes attrs = get
\(file.getFilename\(\),
f.getAbsolutePath\(\), 
progress\);
f.setLastModified
\(attrs.getModifiedTime\(\).longValue\(\) \* 1000\);
\}
\}
\}

if \(sync\) \{
// List the contents of the new local 
directory and remove any
// files/directories that were not updated
File\[\] contents = local.listFiles\(\);

if \(contents \!= null\) \{
for \(int i = 0; i &amp;amp;lt; contents.length; 
i++\) \{
if \(\!op.containsFile\(contents\[i\]\)\) 
\{
op.addDeletedFile\(contents\[i\]\);

if \(contents\[i\].isDirectory\(\) 
&amp;amp;&amp;amp;
\!contents\[i\].getName
\(\).equals\("."\) &amp;amp;&amp;amp;
\!contents\[i\].getName
\(\).equals\(".."\)\) \{
recurseMarkForDeletion
\(contents\[i\], op\);

if \(commit\) \{

IOUtil.recurseDeleteDirectory\(contents\[i\]\);
\}
\} else if \(commit\) \{
contents\[i\].delete\(\);
\}
\}
\}
\}
\}

cd\(pwd\);

return op;
\}
\}
&lt;/pre&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">M_S_Hater</dc:creator><pubDate>Fri, 03 Mar 2006 12:04:04 -0000</pubDate><guid>https://sourceforge.net71a08de518cb14c97aef215aebdf9b4a401f2e2a</guid></item><item><title>Set input/output streams</title><link>https://sourceforge.net/p/sshtools/feature-requests/15/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;It would be extremely useful if alternate input/output&lt;br /&gt;
streams could be given so that everything isn't dealing&lt;br /&gt;
with System.in and System.out .&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Brad Mace</dc:creator><pubDate>Tue, 16 Nov 2004 00:52:35 -0000</pubDate><guid>https://sourceforge.net2e18f7ffce04928cf6e14547fc6363cb1733744b</guid></item><item><title>jdk 1.5 compliant</title><link>https://sourceforge.net/p/sshtools/feature-requests/14/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;Just tiny changes to enable the package to be compiled &lt;br /&gt;
under jdk 1.5&lt;/p&gt;
&lt;p&gt;enum is a reserved keyword for jdk 1.5, need to be &lt;br /&gt;
renamed it from 4 files&lt;/p&gt;
&lt;p&gt;.getState() conflict with Thread.getState() under jdk &lt;br /&gt;
1.5, need to be renamed.&lt;/p&gt;
&lt;p&gt;thus the package can be compiled under jdk 1.5. for &lt;br /&gt;
complete jdk 1.5 compliant, program code may need &lt;br /&gt;
significant changes.&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Perry</dc:creator><pubDate>Thu, 14 Oct 2004 23:00:30 -0000</pubDate><guid>https://sourceforge.net061a78794021f8bc60beb4f4eef484edafdc7cd9</guid></item><item><title>Save dsa key file</title><link>https://sourceforge.net/p/sshtools/feature-requests/13/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;Hi,&lt;br /&gt;
Is it possible to save the dsa file linked to a&lt;br /&gt;
connection saved as a file to prevent to reselect it&lt;br /&gt;
everytime you connect to the ssh server.&lt;br /&gt;
Thanks&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">newbeewan</dc:creator><pubDate>Mon, 13 Sep 2004 15:07:31 -0000</pubDate><guid>https://sourceforge.net5c9ba8c2d56ac4c01b482ad1f671fce2d97f95cb</guid></item><item><title>Send input to multiple terminals</title><link>https://sourceforge.net/p/sshtools/feature-requests/12/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;I would like to be able to group terminals and send key&lt;br /&gt;
events  to them at once or individualy, for an example&lt;br /&gt;
of this check out multixterm or sun's ctelnet. &lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Bear</dc:creator><pubDate>Mon, 22 Mar 2004 09:30:15 -0000</pubDate><guid>https://sourceforge.net6ae4fe7cb76f92749f578f28ed942fc3dcfdc287</guid></item><item><title>MacOSX installer anyone?</title><link>https://sourceforge.net/p/sshtools/feature-requests/11/</link><description>&lt;div class="markdown_content"&gt;&lt;p&gt;If I'm correct, the linux installer does not work for&lt;br /&gt;
MacOSX.  Could anyone knowledgeable build one?&lt;/p&gt;
&lt;p&gt;Thanks&lt;/p&gt;
&lt;p&gt;Vincent Keunen&lt;/p&gt;&lt;/div&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Vincent Keunen</dc:creator><pubDate>Fri, 02 Jan 2004 17:20:13 -0000</pubDate><guid>https://sourceforge.net49277c374da597681ea0132a3103d1b2a00ef6e3</guid></item></channel></rss>