Skip to content

Commit b4722b8

Browse files
authored
Enable nullable on NetConf/Scp/SshClient (#1392)
* Enable nullable on NetConf/Scp/SshClient * fix formatting * improve directoryCounter check * disable nullable warnings on old frameworks since the libraries are missing a lot of nullable attributes in the old frameworks, this causes a lot of false positive nullable warnings. Simply disable these warnings for old frameworks.
1 parent 23323aa commit b4722b8

File tree

4 files changed

+96
-17
lines changed

4 files changed

+96
-17
lines changed

Directory.Build.props

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,13 @@
2323
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
2424
</PropertyGroup>
2525

26+
<!--
27+
Disable nullable warnings on old frameworks because of missing annotations.
28+
-->
29+
<PropertyGroup Condition=" !$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0')) ">
30+
<NoWarn>$(NoWarn);CS8602</NoWarn>
31+
</PropertyGroup>
32+
2633
<!--
2734
Add the stylecop config to each project.
2835
-->

src/Renci.SshNet/NetConfClient.cs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
#nullable enable
2+
using System;
23
using System.Diagnostics.CodeAnalysis;
34
using System.Net;
45
using System.Threading;
@@ -19,7 +20,7 @@ public class NetConfClient : BaseClient
1920
/// <summary>
2021
/// Holds <see cref="INetConfSession"/> instance that used to communicate to the server.
2122
/// </summary>
22-
private INetConfSession _netConfSession;
23+
private INetConfSession? _netConfSession;
2324

2425
/// <summary>
2526
/// Gets or sets the operation timeout.
@@ -47,7 +48,7 @@ public TimeSpan OperationTimeout
4748
/// <value>
4849
/// The current NetConf session.
4950
/// </value>
50-
internal INetConfSession NetConfSession
51+
internal INetConfSession? NetConfSession
5152
{
5253
get { return _netConfSession; }
5354
}
@@ -160,9 +161,18 @@ internal NetConfClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, I
160161
/// <value>
161162
/// The NetConf server capabilities.
162163
/// </value>
164+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
163165
public XmlDocument ServerCapabilities
164166
{
165-
get { return _netConfSession.ServerCapabilities; }
167+
get
168+
{
169+
if (_netConfSession is null)
170+
{
171+
throw new SshConnectionException("Client not connected.");
172+
}
173+
174+
return _netConfSession.ServerCapabilities;
175+
}
166176
}
167177

168178
/// <summary>
@@ -171,9 +181,18 @@ public XmlDocument ServerCapabilities
171181
/// <value>
172182
/// The NetConf client capabilities.
173183
/// </value>
184+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
174185
public XmlDocument ClientCapabilities
175186
{
176-
get { return _netConfSession.ClientCapabilities; }
187+
get
188+
{
189+
if (_netConfSession is null)
190+
{
191+
throw new SshConnectionException("Client not connected.");
192+
}
193+
194+
return _netConfSession.ClientCapabilities;
195+
}
177196
}
178197

179198
/// <summary>
@@ -196,6 +215,11 @@ public XmlDocument ClientCapabilities
196215
/// <exception cref="SshConnectionException">Client is not connected.</exception>
197216
public XmlDocument SendReceiveRpc(XmlDocument rpc)
198217
{
218+
if (_netConfSession is null)
219+
{
220+
throw new SshConnectionException("Client not connected.");
221+
}
222+
199223
return _netConfSession.SendReceiveRpc(rpc, AutomaticMessageIdHandling);
200224
}
201225

@@ -222,6 +246,11 @@ public XmlDocument SendReceiveRpc(string xml)
222246
/// <exception cref="SshConnectionException">Client is not connected.</exception>
223247
public XmlDocument SendCloseRpc()
224248
{
249+
if (_netConfSession is null)
250+
{
251+
throw new SshConnectionException("Client not connected.");
252+
}
253+
225254
var rpc = new XmlDocument();
226255
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>");
227256
return _netConfSession.SendReceiveRpc(rpc, AutomaticMessageIdHandling);
@@ -244,7 +273,7 @@ protected override void OnDisconnecting()
244273
{
245274
base.OnDisconnecting();
246275

247-
_netConfSession.Disconnect();
276+
_netConfSession?.Disconnect();
248277
}
249278

250279
/// <summary>

src/Renci.SshNet/ScpClient.cs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
using System;
1+
#nullable enable
2+
using System;
23
using System.Collections.Generic;
4+
using System.Diagnostics;
35
using System.Diagnostics.CodeAnalysis;
46
using System.Globalization;
57
using System.IO;
@@ -127,12 +129,12 @@ public IRemotePathTransformation RemotePathTransformation
127129
/// <summary>
128130
/// Occurs when downloading file.
129131
/// </summary>
130-
public event EventHandler<ScpDownloadEventArgs> Downloading;
132+
public event EventHandler<ScpDownloadEventArgs>? Downloading;
131133

132134
/// <summary>
133135
/// Occurs when uploading file.
134136
/// </summary>
135-
public event EventHandler<ScpUploadEventArgs> Uploading;
137+
public event EventHandler<ScpUploadEventArgs>? Uploading;
136138

137139
/// <summary>
138140
/// Initializes a new instance of the <see cref="ScpClient"/> class.
@@ -246,8 +248,14 @@ internal ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServ
246248
/// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length <see cref="string"/>.</exception>
247249
/// <exception cref="ScpException">A directory with the specified path exists on the remote host.</exception>
248250
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
251+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
249252
public void Upload(Stream source, string path)
250253
{
254+
if (Session is null)
255+
{
256+
throw new SshConnectionException("Client not connected.");
257+
}
258+
251259
var posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(path);
252260

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

299+
if (Session is null)
300+
{
301+
throw new SshConnectionException("Client not connected.");
302+
}
303+
290304
var posixPath = PosixPath.CreateAbsoluteOrRelativeFilePath(path);
291305

292306
using (var input = ServiceFactory.CreatePipeStream())
@@ -323,6 +337,7 @@ public void Upload(FileInfo fileInfo, string path)
323337
/// <exception cref="ArgumentException"><paramref name="path"/> is a zero-length string.</exception>
324338
/// <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>
325339
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
340+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
326341
public void Upload(DirectoryInfo directoryInfo, string path)
327342
{
328343
if (directoryInfo is null)
@@ -340,6 +355,11 @@ public void Upload(DirectoryInfo directoryInfo, string path)
340355
throw new ArgumentException("The path cannot be a zero-length string.", nameof(path));
341356
}
342357

358+
if (Session is null)
359+
{
360+
throw new SshConnectionException("Client not connected.");
361+
}
362+
343363
using (var input = ServiceFactory.CreatePipeStream())
344364
using (var channel = Session.CreateChannelSession())
345365
{
@@ -371,6 +391,7 @@ public void Upload(DirectoryInfo directoryInfo, string path)
371391
/// <exception cref="ArgumentException"><paramref name="filename"/> is <see langword="null"/> or empty.</exception>
372392
/// <exception cref="ScpException"><paramref name="filename"/> exists on the remote host, and is not a regular file.</exception>
373393
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
394+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
374395
public void Download(string filename, FileInfo fileInfo)
375396
{
376397
if (string.IsNullOrEmpty(filename))
@@ -383,6 +404,11 @@ public void Download(string filename, FileInfo fileInfo)
383404
throw new ArgumentNullException(nameof(fileInfo));
384405
}
385406

407+
if (Session is null)
408+
{
409+
throw new SshConnectionException("Client not connected.");
410+
}
411+
386412
using (var input = ServiceFactory.CreatePipeStream())
387413
using (var channel = Session.CreateChannelSession())
388414
{
@@ -411,6 +437,7 @@ public void Download(string filename, FileInfo fileInfo)
411437
/// <exception cref="ArgumentNullException"><paramref name="directoryInfo"/> is <see langword="null"/>.</exception>
412438
/// <exception cref="ScpException">File or directory with the specified path does not exist on the remote host.</exception>
413439
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
440+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
414441
public void Download(string directoryName, DirectoryInfo directoryInfo)
415442
{
416443
if (string.IsNullOrEmpty(directoryName))
@@ -423,6 +450,11 @@ public void Download(string directoryName, DirectoryInfo directoryInfo)
423450
throw new ArgumentNullException(nameof(directoryInfo));
424451
}
425452

453+
if (Session is null)
454+
{
455+
throw new SshConnectionException("Client not connected.");
456+
}
457+
426458
using (var input = ServiceFactory.CreatePipeStream())
427459
using (var channel = Session.CreateChannelSession())
428460
{
@@ -451,6 +483,7 @@ public void Download(string directoryName, DirectoryInfo directoryInfo)
451483
/// <exception cref="ArgumentNullException"><paramref name="destination"/> is <see langword="null"/>.</exception>
452484
/// <exception cref="ScpException"><paramref name="filename"/> exists on the remote host, and is not a regular file.</exception>
453485
/// <exception cref="SshException">The secure copy execution request was rejected by the server.</exception>
486+
/// <exception cref="SshConnectionException">Client is not connected.</exception>
454487
public void Download(string filename, Stream destination)
455488
{
456489
if (string.IsNullOrWhiteSpace(filename))
@@ -463,6 +496,11 @@ public void Download(string filename, Stream destination)
463496
throw new ArgumentNullException(nameof(destination));
464497
}
465498

499+
if (Session is null)
500+
{
501+
throw new SshConnectionException("Client not connected.");
502+
}
503+
466504
using (var input = ServiceFactory.CreatePipeStream())
467505
using (var channel = Session.CreateChannelSession())
468506
{
@@ -767,13 +805,17 @@ private void InternalDownload(IChannelSession channel, Stream input, FileSystemI
767805

768806
directoryCounter--;
769807

770-
currentDirectoryFullName = new DirectoryInfo(currentDirectoryFullName).Parent.FullName;
771-
772808
if (directoryCounter == 0)
773809
{
774810
break;
775811
}
776812

813+
var currentDirectoryParent = new DirectoryInfo(currentDirectoryFullName).Parent;
814+
815+
Debug.Assert(currentDirectoryParent is not null, $"Should be {directoryCounter.ToString(CultureInfo.InvariantCulture)} levels deeper than {startDirectoryFullName}.");
816+
817+
currentDirectoryFullName = currentDirectoryParent.FullName;
818+
777819
continue;
778820
}
779821

@@ -795,7 +837,7 @@ private void InternalDownload(IChannelSession channel, Stream input, FileSystemI
795837
else
796838
{
797839
// Don't create directory for first level
798-
newDirectoryInfo = fileSystemInfo as DirectoryInfo;
840+
newDirectoryInfo = (DirectoryInfo)fileSystemInfo;
799841
}
800842

801843
directoryCounter++;

src/Renci.SshNet/SshClient.cs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
#nullable enable
2+
using System;
23
using System.Collections.Generic;
34
using System.Diagnostics.CodeAnalysis;
45
using System.IO;
@@ -27,7 +28,7 @@ public class SshClient : BaseClient
2728
/// </value>
2829
private bool _isDisposed;
2930

30-
private MemoryStream _inputStream;
31+
private MemoryStream? _inputStream;
3132

3233
/// <summary>
3334
/// Gets the list of forwarded ports.
@@ -272,7 +273,7 @@ public SshCommand RunCommand(string commandText)
272273
/// Returns a representation of a <see cref="Shell" /> object.
273274
/// </returns>
274275
/// <exception cref="SshConnectionException">Client is not connected.</exception>
275-
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)
276+
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)
276277
{
277278
EnsureSessionIsOpen();
278279

@@ -333,7 +334,7 @@ public Shell CreateShell(Stream input, Stream output, Stream extendedOutput)
333334
/// Returns a representation of a <see cref="Shell" /> object.
334335
/// </returns>
335336
/// <exception cref="SshConnectionException">Client is not connected.</exception>
336-
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)
337+
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)
337338
{
338339
/*
339340
* TODO Issue #1224: let shell dispose of input stream when we own the stream!
@@ -442,7 +443,7 @@ public ShellStream CreateShellStream(string terminalName, uint columns, uint row
442443
/// to the drawable area of the window.
443444
/// </para>
444445
/// </remarks>
445-
public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary<TerminalModes, uint> terminalModeValues)
446+
public ShellStream CreateShellStream(string terminalName, uint columns, uint rows, uint width, uint height, int bufferSize, IDictionary<TerminalModes, uint>? terminalModeValues)
446447
{
447448
EnsureSessionIsOpen();
448449

0 commit comments

Comments
 (0)