using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using Renci.SshNet.Common;
namespace Renci.SshNet
{
///
/// Provides client connection to SSH server.
///
public class SshClient : BaseClient
{
///
/// Holds the list of forwarded ports
///
private readonly List _forwardedPorts;
///
/// Holds a value indicating whether the current instance is disposed.
///
///
/// true if the current instance is disposed; otherwise, false.
///
private bool _isDisposed;
private Stream _inputStream;
///
/// Gets the list of forwarded ports.
///
public IEnumerable ForwardedPorts
{
get
{
return _forwardedPorts.AsReadOnly();
}
}
#region Constructors
///
/// Initializes a new instance of the class.
///
/// The connection info.
///
///
///
///
///
///
/// is null.
public SshClient(ConnectionInfo connectionInfo)
: this(connectionInfo, false)
{
}
///
/// Initializes a new instance of the class.
///
/// Connection host.
/// Connection port.
/// Authentication username.
/// Authentication password.
/// is null.
/// is invalid, or is null or contains only whitespace characters.
/// is not within and .
[SuppressMessage("Microsoft.Reliability", "C2A000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
public SshClient(string host, int port, string username, string password)
: this(new PasswordConnectionInfo(host, port, username, password), true)
{
}
///
/// Initializes a new instance of the class.
///
/// Connection host.
/// Authentication username.
/// Authentication password.
///
///
///
/// is null.
/// is invalid, or is null or contains only whitespace characters.
public SshClient(string host, string username, string password)
: this(host, ConnectionInfo.DefaultPort, username, password)
{
}
///
/// Initializes a new instance of the class.
///
/// Connection host.
/// Connection port.
/// Authentication username.
/// Authentication private key file(s) .
///
///
///
///
/// is null.
/// is invalid, -or- is null or contains only whitespace characters.
/// is not within and .
[SuppressMessage("Microsoft.Reliability", "CA2000:DisposeObjectsBeforeLosingScope", Justification = "Disposed in Dispose(bool) method.")]
public SshClient(string host, int port, string username, params PrivateKeyFile[] keyFiles)
: this(new PrivateKeyConnectionInfo(host, port, username, keyFiles), true)
{
}
///
/// Initializes a new instance of the class.
///
/// Connection host.
/// Authentication username.
/// Authentication private key file(s) .
///
///
///
///
/// is null.
/// is invalid, -or- is null or contains only whitespace characters.
public SshClient(string host, string username, params PrivateKeyFile[] keyFiles)
: this(host, ConnectionInfo.DefaultPort, username, keyFiles)
{
}
///
/// Initializes a new instance of the class.
///
/// The connection info.
/// Specified whether this instance owns the connection info.
/// is null.
///
/// If is true, then the
/// connection info will be disposed when this instance is disposed.
///
private SshClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo)
: this(connectionInfo, ownsConnectionInfo, new ServiceFactory())
{
}
///
/// Initializes a new instance of the class.
///
/// The connection info.
/// Specified whether this instance owns the connection info.
/// The factory to use for creating new services.
/// is null.
/// is null.
///
/// If is true, then the
/// connection info will be disposed when this instance is disposed.
///
internal SshClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServiceFactory serviceFactory)
: base(connectionInfo, ownsConnectionInfo, serviceFactory)
{
_forwardedPorts = new List();
}
#endregion
///
/// Called when client is disconnecting from the server.
///
protected override void OnDisconnecting()
{
base.OnDisconnecting();
foreach (var port in _forwardedPorts)
{
port.Stop();
}
}
///
/// Adds the forwarded port.
///
/// The port.
///
///
///
///
/// Forwarded port is already added to a different client.
/// is null.
/// Client is not connected.
public void AddForwardedPort(ForwardedPort port)
{
if (port == null)
throw new ArgumentNullException("port");
EnsureSessionIsOpen();
AttachForwardedPort(port);
_forwardedPorts.Add(port);
}
///
/// Stops and removes the forwarded port from the list.
///
/// Forwarded port.
/// is null.
public void RemoveForwardedPort(ForwardedPort port)
{
if (port == null)
throw new ArgumentNullException("port");
// Stop port forwarding before removing it
port.Stop();
DetachForwardedPort(port);
_forwardedPorts.Remove(port);
}
private void AttachForwardedPort(ForwardedPort port)
{
if (port.Session != null && port.Session != Session)
throw new InvalidOperationException("Forwarded port is already added to a different client.");
port.Session = Session;
}
private static void DetachForwardedPort(ForwardedPort port)
{
port.Session = null;
}
///
/// Creates the command to be executed.
///
/// The command text.
/// object.
/// Client is not connected.
public SshCommand CreateCommand(string commandText)
{
return CreateCommand(commandText, ConnectionInfo.Encoding);
}
///
/// Creates the command to be executed with specified encoding.
///
/// The command text.
/// The encoding to use for results.
/// object which uses specified encoding.
/// This method will change current default encoding.
/// Client is not connected.
/// or is null.
public SshCommand CreateCommand(string commandText, Encoding encoding)
{
EnsureSessionIsOpen();
ConnectionInfo.Encoding = encoding;
return new SshCommand(Session, commandText, encoding);
}
///
/// Creates and executes the command.
///
/// The command text.
/// Returns an instance of with execution results.
/// This method internally uses asynchronous calls.
///
///
///
///
/// CommandText property is empty.
/// Invalid Operation - An existing channel was used to execute this command.
/// Asynchronous operation is already in progress.
/// Client is not connected.
/// is null.
public SshCommand RunCommand(string commandText)
{
var cmd = CreateCommand(commandText);
cmd.Execute();
return cmd;
}
///
/// Creates the shell.
///
/// The input.
/// The output.
/// The extended output.
/// Name of the terminal.
/// The columns.
/// The rows.
/// The width.
/// The height.
/// The terminal mode.
/// Size of the internal read buffer.
///
/// Returns a representation of a object.
///
/// Client is not connected.
public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize)
{
EnsureSessionIsOpen();
return new Shell(Session, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize);
}
///
/// Creates the shell.
///
/// The input.
/// The output.
/// The extended output.
/// Name of the terminal.
/// The columns.
/// The rows.
/// The width.
/// The height.
/// The terminal mode.
///
/// Returns a representation of a object.
///
/// Client is not connected.
public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes)
{
return CreateShell(input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, 1024);
}
///
/// Creates the shell.
///
/// The input.
/// The output.
/// The extended output.
///
/// Returns a representation of a object.
///
/// Client is not connected.
public Shell CreateShell(Stream input, Stream output, Stream extendedOutput)
{
return CreateShell(input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024);
}
///
/// Creates the shell.
///
/// The encoding to use to send the input.
/// The input.
/// The output.
/// The extended output.
/// Name of the terminal.
/// The columns.
/// The rows.
/// The width.
/// The height.
/// The terminal mode.
/// Size of the internal read buffer.
///
/// Returns a representation of a object.
///
/// Client is not connected.
public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes, int bufferSize)
{
// TODO let shell dispose of input stream when we own the stream!
_inputStream = new MemoryStream();
var writer = new StreamWriter(_inputStream, encoding);
writer.Write(input);
writer.Flush();
_inputStream.Seek(0, SeekOrigin.Begin);
return CreateShell(_inputStream, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, bufferSize);
}
///
/// Creates the shell.
///
/// The encoding.
/// The input.
/// The output.
/// The extended output.
/// Name of the terminal.
/// The columns.
/// The rows.
/// The width.
/// The height.
/// The terminal modes.
///
/// Returns a representation of a object.
///
/// Client is not connected.
public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary terminalModes)
{
return CreateShell(encoding, input, output, extendedOutput, terminalName, columns, rows, width, height, terminalModes, 1024);
}
///
/// Creates the shell.
///
/// The encoding.
/// The input.
/// The output.
/// The extended output.
///
/// Returns a representation of a object.
///
/// Client is not connected.
public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput)
{
return CreateShell(encoding, input, output, extendedOutput, string.Empty, 0, 0, 0, 0, null, 1024);
}
///
/// Creates the shell stream.
///
/// The TERM environment variable.
/// The terminal width in columns.
/// The terminal width in rows.
/// The terminal height in pixels.
/// The terminal height in pixels.
/// Size of the buffer.
///
/// The created instance.
///
/// Client is not connected.
///
///
/// The TERM environment variable contains an identifier for the text window's capabilities.
/// You can get a detailed list of these cababilities by using the ‘infocmp’ command.
///
///
/// The column/row dimensions override the pixel dimensions(when nonzero). Pixel dimensions refer
/// to the drawable area of the window.
///
///
public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize)
{
return CreateShellStream(terminalName, columns, rows, width, height, bufferSize, null);
}
///
/// Creates the shell stream.
///
/// The TERM environment variable.
/// The terminal width in columns.
/// The terminal width in rows.
/// The terminal height in pixels.
/// The terminal height in pixels.
/// Size of the buffer.
/// The terminal mode values.
///
/// The created instance.
///
/// Client is not connected.
///
///
/// The TERM environment variable contains an identifier for the text window's capabilities.
/// You can get a detailed list of these cababilities by using the ‘infocmp’ command.
///
///
/// The column/row dimensions override the pixel dimensions(when nonzero). Pixel dimensions refer
/// to the drawable area of the window.
///
///
public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary terminalModeValues)
{
EnsureSessionIsOpen();
return new ShellStream(Session, terminalName, columns, rows, width, height, terminalModeValues);
}
///
/// Stops forwarded ports.
///
protected override void OnDisconnected()
{
base.OnDisconnected();
for (var i = _forwardedPorts.Count - 1; i >= 0; i--)
{
var port = _forwardedPorts[i];
DetachForwardedPort(port);
_forwardedPorts.RemoveAt(i);
}
}
///
/// Releases unmanaged and - optionally - managed resources
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (_isDisposed)
return;
if (disposing)
{
if (_inputStream != null)
{
_inputStream.Dispose();
_inputStream = null;
}
_isDisposed = true;
}
}
private void EnsureSessionIsOpen()
{
if (Session == null)
throw new SshConnectionException("Client not connected.");
}
}
}