1
1
using System ;
2
- using System . Text ;
3
2
using Renci . SshNet . Channels ;
4
3
using System . IO ;
5
4
using Renci . SshNet . Common ;
6
5
using System . Text . RegularExpressions ;
7
6
using System . Diagnostics . CodeAnalysis ;
8
7
using System . Net ;
8
+ using System . Collections . Generic ;
9
9
10
10
namespace Renci . SshNet
11
11
{
12
12
/// <summary>
13
13
/// Provides SCP client functionality.
14
14
/// </summary>
15
15
/// <remarks>
16
+ /// <para>
16
17
/// More information on the SCP protocol is available here:
17
18
/// https://github.com/net-ssh/net-scp/blob/master/lib/net/scp.rb
19
+ /// </para>
20
+ /// <para>
21
+ /// Known issues in OpenSSH:
22
+ /// <list type="bullet">
23
+ /// <item>
24
+ /// <description>Recursive download (-prf) does not deal well with specific UTF-8 and newline characters.</description>
25
+ /// <description>Recursive update does not support empty path for uploading to home directorydeal well with specific UTF-8 and newline characters.</description>
26
+ /// </item>
27
+ /// </list>
28
+ /// </para>
18
29
/// </remarks>
19
30
public partial class ScpClient : BaseClient
20
31
{
21
32
private static readonly Regex FileInfoRe = new Regex ( @"C(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)" ) ;
22
- private static char [ ] _byteToChar ;
23
33
24
34
/// <summary>
25
35
/// Gets or sets the operation timeout.
@@ -150,16 +160,6 @@ internal ScpClient(ConnectionInfo connectionInfo, bool ownsConnectionInfo, IServ
150
160
{
151
161
OperationTimeout = SshNet . Session . InfiniteTimeSpan ;
152
162
BufferSize = 1024 * 16 ;
153
-
154
- if ( _byteToChar == null )
155
- {
156
- _byteToChar = new char [ 128 ] ;
157
- var ch = '\0 ' ;
158
- for ( var i = 0 ; i < 128 ; i ++ )
159
- {
160
- _byteToChar [ i ] = ch ++ ;
161
- }
162
- }
163
163
}
164
164
165
165
#endregion
@@ -243,7 +243,7 @@ public void Download(string filename, Stream destination)
243
243
}
244
244
}
245
245
246
- private static void InternalSetTimestamp ( IChannelSession channel , Stream input , DateTime lastWriteTime , DateTime lastAccessime )
246
+ private void InternalSetTimestamp ( IChannelSession channel , Stream input , DateTime lastWriteTime , DateTime lastAccessime )
247
247
{
248
248
var zeroTime = new DateTime ( 1970 , 1 , 1 , 0 , 0 , 0 , 0 , DateTimeKind . Utc ) ;
249
249
var modificationSeconds = ( long ) ( lastWriteTime - zeroTime ) . TotalSeconds ;
@@ -329,7 +329,7 @@ private static void SendConfirmation(IChannel channel)
329
329
SendData ( channel , new byte [ ] { 0 } ) ;
330
330
}
331
331
332
- private static void SendConfirmation ( IChannel channel , byte errorCode , string message )
332
+ private void SendConfirmation ( IChannel channel , byte errorCode , string message )
333
333
{
334
334
SendData ( channel , new [ ] { errorCode } ) ;
335
335
SendData ( channel , string . Format ( "{0}\n " , message ) ) ;
@@ -339,7 +339,7 @@ private static void SendConfirmation(IChannel channel, byte errorCode, string me
339
339
/// Checks the return code.
340
340
/// </summary>
341
341
/// <param name="input">The output stream.</param>
342
- private static void CheckReturnCode ( Stream input )
342
+ private void CheckReturnCode ( Stream input )
343
343
{
344
344
var b = ReadByte ( input ) ;
345
345
@@ -351,9 +351,9 @@ private static void CheckReturnCode(Stream input)
351
351
}
352
352
}
353
353
354
- private static void SendData ( IChannel channel , string command )
354
+ private void SendData ( IChannel channel , string command )
355
355
{
356
- channel . SendData ( SshData . Utf8 . GetBytes ( command ) ) ;
356
+ channel . SendData ( ConnectionInfo . Encoding . GetBytes ( command ) ) ;
357
357
}
358
358
359
359
private static void SendData ( IChannel channel , byte [ ] buffer , int length )
@@ -374,35 +374,36 @@ private static int ReadByte(Stream stream)
374
374
return b ;
375
375
}
376
376
377
- private static string ReadString ( Stream stream )
377
+ /// <summary>
378
+ /// Read a LF-terminated string from the <see cref="Stream"/>.
379
+ /// </summary>
380
+ /// <param name="stream">The <see cref="Stream"/> to read from.</param>
381
+ /// <returns>
382
+ /// The string without trailing LF.
383
+ /// </returns>
384
+ private string ReadString ( Stream stream )
378
385
{
379
386
var hasError = false ;
380
387
381
- var sb = new StringBuilder ( ) ;
388
+ var buffer = new List < byte > ( ) ;
382
389
383
390
var b = ReadByte ( stream ) ;
384
-
385
391
if ( b == 1 || b == 2 )
386
392
{
387
393
hasError = true ;
388
394
b = ReadByte ( stream ) ;
389
395
}
390
396
391
- var ch = _byteToChar [ b ] ;
392
-
393
- while ( ch != '\n ' )
397
+ while ( b != SshNet . Session . LineFeed )
394
398
{
395
- sb . Append ( ch ) ;
396
-
399
+ buffer . Add ( ( byte ) b ) ;
397
400
b = ReadByte ( stream ) ;
398
-
399
- ch = _byteToChar [ b ] ;
400
401
}
401
402
402
403
if ( hasError )
403
- throw new ScpException ( sb . ToString ( ) ) ;
404
+ throw new ScpException ( ConnectionInfo . Encoding . GetString ( buffer . ToArray ( ) ) ) ;
404
405
405
- return sb . ToString ( ) ;
406
+ return ConnectionInfo . Encoding . GetString ( buffer . ToArray ( ) ) ;
406
407
}
407
408
}
408
409
}
0 commit comments