Skip to content

Enable nullable on NetConf/Scp/SshClient #1392

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
</PropertyGroup>

<!--
Disable nullable warnings on old frameworks because of missing annotations.
-->
<PropertyGroup Condition=" !$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0')) ">
<NoWarn>$(NoWarn);CS8602</NoWarn>
</PropertyGroup>

<!--
Add the stylecop config to each project.
-->
Expand Down
41 changes: 35 additions & 6 deletions src/Renci.SshNet/NetConfClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Threading;
Expand All @@ -19,7 +20,7 @@ public class NetConfClient : BaseClient
/// <summary>
/// Holds <see cref="INetConfSession"/> instance that used to communicate to the server.
/// </summary>
private INetConfSession _netConfSession;
private INetConfSession? _netConfSession;

/// <summary>
/// Gets or sets the operation timeout.
Expand Down Expand Up @@ -47,7 +48,7 @@ public TimeSpan OperationTimeout
/// <value>
/// The current NetConf session.
/// </value>
internal INetConfSession NetConfSession
internal INetConfSession? NetConfSession
{
get { return _netConfSession; }
}
Expand Down Expand Up @@ -160,9 +161,18 @@ internal NetConfClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, I
/// <value>
/// The NetConf server capabilities.
/// </value>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public XmlDocument ServerCapabilities
{
get { return _netConfSession.ServerCapabilities; }
get
{
if (_netConfSession is null)
{
throw new SshConnectionException("Client not connected.");
}

return _netConfSession.ServerCapabilities;
}
}

/// <summary>
Expand All @@ -171,9 +181,18 @@ public XmlDocument ServerCapabilities
/// <value>
/// The NetConf client capabilities.
/// </value>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public XmlDocument ClientCapabilities
{
get { return _netConfSession.ClientCapabilities; }
get
{
if (_netConfSession is null)
{
throw new SshConnectionException("Client not connected.");
}

return _netConfSession.ClientCapabilities;
}
}

/// <summary>
Expand All @@ -196,6 +215,11 @@ public XmlDocument ClientCapabilities
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public XmlDocument SendReceiveRpc(XmlDocument rpc)
{
if (_netConfSession is null)
{
throw new SshConnectionException("Client not connected.");
}

return _netConfSession.SendReceiveRpc(rpc, AutomaticMessageIdHandling);
}

Expand All @@ -222,6 +246,11 @@ public XmlDocument SendReceiveRpc(string xml)
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public XmlDocument SendCloseRpc()
{
if (_netConfSession is null)
{
throw new SshConnectionException("Client not connected.");
}

var rpc = new XmlDocument();
rpc.LoadXml("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rpc message-id=\"6666\" xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\"><close-session/></rpc>");
return _netConfSession.SendReceiveRpc(rpc, AutomaticMessageIdHandling);
Expand All @@ -244,7 +273,7 @@ protected override void OnDisconnecting()
{
base.OnDisconnecting();

_netConfSession.Disconnect();
_netConfSession?.Disconnect();
}

/// <summary>
Expand Down
54 changes: 48 additions & 6 deletions src/Renci.SshNet/ScpClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
Expand Down Expand Up @@ -127,12 +129,12 @@ public IRemotePathTransformation RemotePathTransformation
/// <summary>
/// Occurs when downloading file.
/// </summary>
public event EventHandler<ScpDownloadEventArgs> Downloading;
public event EventHandler<ScpDownloadEventArgs>? Downloading;

/// <summary>
/// Occurs when uploading file.
/// </summary>
public event EventHandler<ScpUploadEventArgs> Uploading;
public event EventHandler<ScpUploadEventArgs>? Uploading;

/// <summary>
/// Initializes a new instance of the <see cref="ScpClient"/> class.
Expand Down Expand Up @@ -246,8 +248,14 @@ internal ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServ
/// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length <see cref="string"/>.</exception>
/// <exception cref="ScpException">A directory with the specified path exists on the remote host.</exception>
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public void Upload(Stream source, string path)
{
if (Session is null)
{
throw new SshConnectionException("Client not connected.");
}

var posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(path);

using (var input = ServiceFactory.CreatePipeStream())
Expand Down Expand Up @@ -280,13 +288,19 @@ public void Upload(Stream source, string path)
/// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length <see cref="string"/>.</exception>
/// <exception cref="ScpException">A directory with the specified path exists on the remote host.</exception>
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public void Upload(FileInfo fileInfo, string path)
{
if (fileInfo is null)
{
throw new ArgumentNullException(nameof(fileInfo));
}

if (Session is null)
{
throw new SshConnectionException("Client not connected.");
}

var posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(path);

using (var input = ServiceFactory.CreatePipeStream())
Expand Down Expand Up @@ -323,6 +337,7 @@ public void Upload(FileInfo fileInfo, string path)
/// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length string.</exception>
/// <exception cref="ScpException"><paramref name="path"/> does not exist on the remote host, is not a directory or the user does not have the required permission.</exception>
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public void Upload(DirectoryInfo directoryInfo, string path)
{
if (directoryInfo is null)
Expand All @@ -340,6 +355,11 @@ public void Upload(DirectoryInfo directoryInfo, string path)
throw new ArgumentException("The path cannot be a zero-length string.", nameof(path));
}

if (Session is null)
{
throw new SshConnectionException("Client not connected.");
}

using (var input = ServiceFactory.CreatePipeStream())
using (var channel = Session.CreateChannelSession())
{
Expand Down Expand Up @@ -371,6 +391,7 @@ public void Upload(DirectoryInfo directoryInfo, string path)
/// <exception cref="ArgumentException"><paramref name="filename"/> is <see langword="null"/> or empty.</exception>
/// <exception cref="ScpException"><paramref name="filename"/> exists on the remote host, and is not a regular file.</exception>
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public void Download(string filename, FileInfo fileInfo)
{
if (string.IsNullOrEmpty(filename))
Expand All @@ -383,6 +404,11 @@ public void Download(string filename, FileInfo fileInfo)
throw new ArgumentNullException(nameof(fileInfo));
}

if (Session is null)
{
throw new SshConnectionException("Client not connected.");
}

using (var input = ServiceFactory.CreatePipeStream())
using (var channel = Session.CreateChannelSession())
{
Expand Down Expand Up @@ -411,6 +437,7 @@ public void Download(string filename, FileInfo fileInfo)
/// <exception cref="ArgumentNullException"><paramref name="directoryInfo"/> is <see langword="null"/>.</exception>
/// <exception cref="ScpException">File or directory with the specified path does not exist on the remote host.</exception>
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public void Download(string directoryName, DirectoryInfo directoryInfo)
{
if (string.IsNullOrEmpty(directoryName))
Expand All @@ -423,6 +450,11 @@ public void Download(string directoryName, DirectoryInfo directoryInfo)
throw new ArgumentNullException(nameof(directoryInfo));
}

if (Session is null)
{
throw new SshConnectionException("Client not connected.");
}

using (var input = ServiceFactory.CreatePipeStream())
using (var channel = Session.CreateChannelSession())
{
Expand Down Expand Up @@ -451,6 +483,7 @@ public void Download(string directoryName, DirectoryInfo directoryInfo)
/// <exception cref="ArgumentNullException"><paramref name="destination"/> is <see langword="null"/>.</exception>
/// <exception cref="ScpException"><paramref name="filename"/> exists on the remote host, and is not a regular file.</exception>
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public void Download(string filename, Stream destination)
{
if (string.IsNullOrWhiteSpace(filename))
Expand All @@ -463,6 +496,11 @@ public void Download(string filename, Stream destination)
throw new ArgumentNullException(nameof(destination));
}

if (Session is null)
{
throw new SshConnectionException("Client not connected.");
}

using (var input = ServiceFactory.CreatePipeStream())
using (var channel = Session.CreateChannelSession())
{
Expand Down Expand Up @@ -767,13 +805,17 @@ private void InternalDownload(IChannelSession channel, Stream input, FileSystemI

directoryCounter--;

currentDirectoryFullName = new DirectoryInfo(currentDirectoryFullName).Parent.FullName;

if (directoryCounter == 0)
{
break;
}

var currentDirectoryParent = new DirectoryInfo(currentDirectoryFullName).Parent;

Debug.Assert(currentDirectoryParent is not null, $"Should be {directoryCounter.ToString(CultureInfo.InvariantCulture)} levels deeper than {startDirectoryFullName}.");

currentDirectoryFullName = currentDirectoryParent.FullName;

continue;
}

Expand All @@ -795,7 +837,7 @@ private void InternalDownload(IChannelSession channel, Stream input, FileSystemI
else
{
// Don't create directory for first level
newDirectoryInfo = fileSystemInfo as DirectoryInfo;
newDirectoryInfo = (DirectoryInfo)fileSystemInfo;
}

directoryCounter++;
Expand Down
11 changes: 6 additions & 5 deletions src/Renci.SshNet/SshClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
Expand Down Expand Up @@ -27,7 +28,7 @@ public class SshClient : BaseClient
/// </value>
private bool _isDisposed;

private MemoryStream _inputStream;
private MemoryStream? _inputStream;

/// <summary>
/// Gets the list of forwarded ports.
Expand Down Expand Up @@ -272,7 +273,7 @@ public SshCommand RunCommand(string commandText)
/// Returns a representation of a <see cref="Shell" /> object.
/// </returns>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModes, int bufferSize)
public Shell CreateShell(Stream input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint>? terminalModes, int bufferSize)
{
EnsureSessionIsOpen();

Expand Down Expand Up @@ -333,7 +334,7 @@ public Shell CreateShell(Stream input, Stream output, Stream extendedOutput)
/// Returns a representation of a <see cref="Shell" /> object.
/// </returns>
/// <exception cref="SshConnectionException">Client is not connected.</exception>
public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint> terminalModes, int bufferSize)
public Shell CreateShell(Encoding encoding, string input, Stream output, Stream extendedOutput, string terminalName, uint columns, uint rows, uint width, uint height, IDictionary<TerminalModes, uint>? terminalModes, int bufferSize)
{
/*
* TODO Issue #1224: let shell dispose of input stream when we own the stream!
Expand Down Expand Up @@ -442,7 +443,7 @@ public ShellStream CreateShellStream(string terminalName, uint columns, uint row
/// to the drawable area of the window.
/// </para>
/// </remarks>
public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary<TerminalModes, uint> terminalModeValues)
public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary<TerminalModes, uint>? terminalModeValues)
{
EnsureSessionIsOpen();

Expand Down