ScpClient.NET.cs 12 KB

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