ScpClient.NET.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. using System;
  2. using Renci.SshNet.Channels;
  3. using System.IO;
  4. using Renci.SshNet.Common;
  5. using System.Text.RegularExpressions;
  6. namespace Renci.SshNet
  7. {
  8. /// <summary>
  9. /// Provides SCP client functionality.
  10. /// </summary>
  11. public partial class ScpClient
  12. {
  13. private static readonly Regex _directoryInfoRe = new Regex(@"D(?<mode>\d{4}) (?<length>\d+) (?<filename>.+)");
  14. private static readonly Regex _timestampRe = new Regex(@"T(?<mtime>\d+) 0 (?<atime>\d+) 0");
  15. /// <summary>
  16. /// Uploads the specified file to the remote host.
  17. /// </summary>
  18. /// <param name="fileInfo">The file system info.</param>
  19. /// <param name="path">The path.</param>
  20. /// <exception cref="ArgumentNullException"><paramref name="fileInfo" /> is null.</exception>
  21. /// <exception cref="ArgumentException"><paramref name="path"/> is null or empty.</exception>
  22. public void Upload(FileInfo fileInfo, string path)
  23. {
  24. if (fileInfo == null)
  25. throw new ArgumentNullException("fileInfo");
  26. if (string.IsNullOrEmpty(path))
  27. throw new ArgumentException("path");
  28. using (var input = new PipeStream())
  29. using (var channel = this.Session.CreateClientChannel<ChannelSession>())
  30. {
  31. channel.DataReceived += delegate(object sender, ChannelDataEventArgs e)
  32. {
  33. input.Write(e.Data, 0, e.Data.Length);
  34. input.Flush();
  35. };
  36. channel.Open();
  37. // Send channel command request
  38. channel.SendExecRequest(string.Format("scp -t \"{0}\"", path));
  39. this.CheckReturnCode(input);
  40. this.InternalUpload(channel, input, fileInfo, fileInfo.Name);
  41. channel.Close();
  42. }
  43. }
  44. /// <summary>
  45. /// Uploads the specified directory to the remote host.
  46. /// </summary>
  47. /// <param name="directoryInfo">The directory info.</param>
  48. /// <param name="path">The path.</param>
  49. /// <exception cref="ArgumentNullException">fileSystemInfo</exception>
  50. /// <exception cref="ArgumentException"><paramref name="path"/> is null or empty.</exception>
  51. public void Upload(DirectoryInfo directoryInfo, string path)
  52. {
  53. if (directoryInfo == null)
  54. throw new ArgumentNullException("directoryInfo");
  55. if (string.IsNullOrEmpty(path))
  56. throw new ArgumentException("path");
  57. using (var input = new PipeStream())
  58. using (var channel = this.Session.CreateClientChannel<ChannelSession>())
  59. {
  60. channel.DataReceived += delegate(object sender, ChannelDataEventArgs e)
  61. {
  62. input.Write(e.Data, 0, e.Data.Length);
  63. input.Flush();
  64. };
  65. channel.Open();
  66. // Send channel command request
  67. channel.SendExecRequest(string.Format("scp -rt \"{0}\"", path));
  68. this.CheckReturnCode(input);
  69. this.InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc);
  70. this.SendData(channel, string.Format("D0755 0 {0}\n", Path.GetFileName(path)));
  71. this.CheckReturnCode(input);
  72. this.InternalUpload(channel, input, directoryInfo);
  73. this.SendData(channel, "E\n");
  74. this.CheckReturnCode(input);
  75. channel.Close();
  76. }
  77. }
  78. /// <summary>
  79. /// Downloads the specified file from the remote host to local file.
  80. /// </summary>
  81. /// <param name="filename">Remote host file name.</param>
  82. /// <param name="fileInfo">Local file information.</param>
  83. /// <exception cref="ArgumentNullException"><paramref name="fileInfo"/> is null.</exception>
  84. /// <exception cref="ArgumentException"><paramref name="filename"/> is null or empty.</exception>
  85. public void Download(string filename, FileInfo fileInfo)
  86. {
  87. if (string.IsNullOrEmpty(filename))
  88. throw new ArgumentException("filename");
  89. if (fileInfo == null)
  90. throw new ArgumentNullException("fileInfo");
  91. using (var input = new PipeStream())
  92. using (var channel = this.Session.CreateClientChannel<ChannelSession>())
  93. {
  94. channel.DataReceived += delegate(object sender, ChannelDataEventArgs e)
  95. {
  96. input.Write(e.Data, 0, e.Data.Length);
  97. input.Flush();
  98. };
  99. channel.Open();
  100. // Send channel command request
  101. channel.SendExecRequest(string.Format("scp -pf \"{0}\"", filename));
  102. this.SendConfirmation(channel); // Send reply
  103. this.InternalDownload(channel, input, fileInfo);
  104. channel.Close();
  105. }
  106. }
  107. /// <summary>
  108. /// Downloads the specified directory from the remote host to local directory.
  109. /// </summary>
  110. /// <param name="directoryName">Remote host directory name.</param>
  111. /// <param name="directoryInfo">Local directory information.</param>
  112. /// <exception cref="ArgumentException"><paramref name="directoryName"/> is null or empty.</exception>
  113. /// <exception cref="ArgumentNullException"><paramref name="directoryInfo"/> is null.</exception>
  114. public void Download(string directoryName, DirectoryInfo directoryInfo)
  115. {
  116. if (string.IsNullOrEmpty(directoryName))
  117. throw new ArgumentException("directoryName");
  118. if (directoryInfo == null)
  119. throw new ArgumentNullException("directoryInfo");
  120. using (var input = new PipeStream())
  121. using (var channel = this.Session.CreateClientChannel<ChannelSession>())
  122. {
  123. channel.DataReceived += delegate(object sender, ChannelDataEventArgs e)
  124. {
  125. input.Write(e.Data, 0, e.Data.Length);
  126. input.Flush();
  127. };
  128. channel.Open();
  129. // Send channel command request
  130. channel.SendExecRequest(string.Format("scp -prf \"{0}\"", directoryName));
  131. this.SendConfirmation(channel); // Send reply
  132. this.InternalDownload(channel, input, directoryInfo);
  133. channel.Close();
  134. }
  135. }
  136. private void InternalUpload(ChannelSession channel, Stream input, FileInfo fileInfo, string filename)
  137. {
  138. this.InternalSetTimestamp(channel, input, fileInfo.LastWriteTimeUtc, fileInfo.LastAccessTimeUtc);
  139. using (var source = fileInfo.OpenRead())
  140. {
  141. this.InternalUpload(channel, input, source, filename);
  142. }
  143. }
  144. private void InternalUpload(ChannelSession channel, Stream input, DirectoryInfo directoryInfo)
  145. {
  146. // Upload files
  147. var files = directoryInfo.GetFiles();
  148. foreach (var file in files)
  149. {
  150. this.InternalUpload(channel, input, file, file.Name);
  151. }
  152. // Upload directories
  153. var directories = directoryInfo.GetDirectories();
  154. foreach (var directory in directories)
  155. {
  156. this.InternalSetTimestamp(channel, input, directoryInfo.LastWriteTimeUtc, directoryInfo.LastAccessTimeUtc);
  157. this.SendData(channel, string.Format("D0755 0 {0}\n", directory.Name));
  158. this.CheckReturnCode(input);
  159. this.InternalUpload(channel, input, directory);
  160. this.SendData(channel, "E\n");
  161. this.CheckReturnCode(input);
  162. }
  163. }
  164. private void InternalDownload(ChannelSession channel, Stream input, FileSystemInfo fileSystemInfo)
  165. {
  166. DateTime modifiedTime = DateTime.Now;
  167. DateTime accessedTime = DateTime.Now;
  168. var startDirectoryFullName = fileSystemInfo.FullName;
  169. var currentDirectoryFullName = startDirectoryFullName;
  170. var directoryCounter = 0;
  171. while (true)
  172. {
  173. var message = ReadString(input);
  174. if (message == "E")
  175. {
  176. this.SendConfirmation(channel); // Send reply
  177. directoryCounter--;
  178. currentDirectoryFullName = new DirectoryInfo(currentDirectoryFullName).Parent.FullName;
  179. if (directoryCounter == 0)
  180. break;
  181. continue;
  182. }
  183. var match = _directoryInfoRe.Match(message);
  184. if (match.Success)
  185. {
  186. this.SendConfirmation(channel); // Send reply
  187. // Read directory
  188. var mode = long.Parse(match.Result("${mode}"));
  189. var filename = match.Result("${filename}");
  190. DirectoryInfo newDirectoryInfo;
  191. if (directoryCounter > 0)
  192. {
  193. newDirectoryInfo = Directory.CreateDirectory(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, filename));
  194. newDirectoryInfo.LastAccessTime = accessedTime;
  195. newDirectoryInfo.LastWriteTime = modifiedTime;
  196. }
  197. else
  198. {
  199. // Dont create directory for first level
  200. newDirectoryInfo = fileSystemInfo as DirectoryInfo;
  201. }
  202. directoryCounter++;
  203. currentDirectoryFullName = newDirectoryInfo.FullName;
  204. continue;
  205. }
  206. match = _fileInfoRe.Match(message);
  207. if (match.Success)
  208. {
  209. // Read file
  210. this.SendConfirmation(channel); // Send reply
  211. var mode = match.Result("${mode}");
  212. var length = long.Parse(match.Result("${length}"));
  213. var fileName = match.Result("${filename}");
  214. var fileInfo = fileSystemInfo as FileInfo;
  215. if (fileInfo == null)
  216. fileInfo = new FileInfo(string.Format("{0}{1}{2}", currentDirectoryFullName, Path.DirectorySeparatorChar, fileName));
  217. using (var output = fileInfo.OpenWrite())
  218. {
  219. this.InternalDownload(channel, input, output, fileName, length);
  220. }
  221. fileInfo.LastAccessTime = accessedTime;
  222. fileInfo.LastWriteTime = modifiedTime;
  223. if (directoryCounter == 0)
  224. break;
  225. continue;
  226. }
  227. match = _timestampRe.Match(message);
  228. if (match.Success)
  229. {
  230. // Read timestamp
  231. this.SendConfirmation(channel); // Send reply
  232. var mtime = long.Parse(match.Result("${mtime}"));
  233. var atime = long.Parse(match.Result("${atime}"));
  234. var zeroTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
  235. modifiedTime = zeroTime.AddSeconds(mtime);
  236. accessedTime = zeroTime.AddSeconds(atime);
  237. continue;
  238. }
  239. this.SendConfirmation(channel, 1, string.Format("\"{0}\" is not valid protocol message.", message));
  240. }
  241. }
  242. partial void SendData(ChannelSession channel, string command)
  243. {
  244. channel.SendData(System.Text.Encoding.Default.GetBytes(command));
  245. }
  246. }
  247. }