SftpClient.cs 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302
  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using Renci.SshNet.Sftp;
  6. using System.Text;
  7. using Renci.SshNet.Common;
  8. using System.Globalization;
  9. using System.Threading;
  10. namespace Renci.SshNet
  11. {
  12. /// <summary>
  13. ///
  14. /// </summary>
  15. public partial class SftpClient : BaseClient
  16. {
  17. /// <summary>
  18. /// Holds SftpSession instance that used to communicate to the SFTP server
  19. /// </summary>
  20. private SftpSession _sftpSession;
  21. /// <summary>
  22. /// Gets or sets the operation timeout.
  23. /// </summary>
  24. /// <value>The operation timeout.</value>
  25. public TimeSpan OperationTimeout { get; set; }
  26. /// <summary>
  27. /// Gets or sets the size of the buffer.
  28. /// </summary>
  29. /// <value>The size of the buffer.</value>
  30. public uint BufferSize { get; set; }
  31. /// <summary>
  32. /// Gets remote working directory.
  33. /// </summary>
  34. public string WorkingDirectory
  35. {
  36. get
  37. {
  38. if (this._sftpSession == null)
  39. return null;
  40. return this._sftpSession.WorkingDirectory;
  41. }
  42. }
  43. /// <summary>
  44. /// Gets sftp protocol version.
  45. /// </summary>
  46. public int ProtocolVersion { get; private set; }
  47. #region Constructors
  48. /// <summary>
  49. /// Initializes a new instance of the <see cref="SftpClient"/> class.
  50. /// </summary>
  51. /// <param name="connectionInfo">The connection info.</param>
  52. /// <exception cref="ArgumentNullException"><paramref name="connectionInfo"/> is null.</exception>
  53. public SftpClient(ConnectionInfo connectionInfo)
  54. : base(connectionInfo)
  55. {
  56. this.OperationTimeout = new TimeSpan(0, 0, 0, 0, -1);
  57. this.BufferSize = 1024 * 32 - 38;
  58. }
  59. /// <summary>
  60. /// Initializes a new instance of the <see cref="SftpClient"/> class.
  61. /// </summary>
  62. /// <param name="host">Connection host.</param>
  63. /// <param name="port">Connection port.</param>
  64. /// <param name="username">Authentication username.</param>
  65. /// <param name="password">Authentication password.</param>
  66. /// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
  67. /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, or <paramref name="username"/> is null or contains whitespace characters.</exception>
  68. /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="System.Net.IPEndPoint.MinPort"/> and <see cref="System.Net.IPEndPoint.MaxPort"/>.</exception>
  69. public SftpClient(string host, int port, string username, string password)
  70. : this(new PasswordConnectionInfo(host, port, username, password))
  71. {
  72. }
  73. /// <summary>
  74. /// Initializes a new instance of the <see cref="SftpClient"/> class.
  75. /// </summary>
  76. /// <param name="host">Connection host.</param>
  77. /// <param name="username">Authentication username.</param>
  78. /// <param name="password">Authentication password.</param>
  79. /// <exception cref="ArgumentNullException"><paramref name="password"/> is null.</exception>
  80. /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, or <paramref name="username"/> is null or contains whitespace characters.</exception>
  81. public SftpClient(string host, string username, string password)
  82. : this(host, 22, username, password)
  83. {
  84. }
  85. /// <summary>
  86. /// Initializes a new instance of the <see cref="SftpClient"/> class.
  87. /// </summary>
  88. /// <param name="host">Connection host.</param>
  89. /// <param name="port">Connection port.</param>
  90. /// <param name="username">Authentication username.</param>
  91. /// <param name="keyFiles">Authentication private key file(s) .</param>
  92. /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is null.</exception>
  93. /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is null or contains whitespace characters.</exception>
  94. /// <exception cref="ArgumentOutOfRangeException"><paramref name="port"/> is not within <see cref="System.Net.IPEndPoint.MinPort"/> and <see cref="System.Net.IPEndPoint.MaxPort"/>.</exception>
  95. public SftpClient(string host, int port, string username, params PrivateKeyFile[] keyFiles)
  96. : this(new PrivateKeyConnectionInfo(host, port, username, keyFiles))
  97. {
  98. }
  99. /// <summary>
  100. /// Initializes a new instance of the <see cref="SftpClient"/> class.
  101. /// </summary>
  102. /// <param name="host">Connection host.</param>
  103. /// <param name="username">Authentication username.</param>
  104. /// <param name="keyFiles">Authentication private key file(s) .</param>
  105. /// <exception cref="ArgumentNullException"><paramref name="keyFiles"/> is null.</exception>
  106. /// <exception cref="ArgumentException"><paramref name="host"/> is invalid, -or- <paramref name="username"/> is null or contains whitespace characters.</exception>
  107. public SftpClient(string host, string username, params PrivateKeyFile[] keyFiles)
  108. : this(host, 22, username, keyFiles)
  109. {
  110. }
  111. #endregion
  112. /// <summary>
  113. /// Changes remote directory to path.
  114. /// </summary>
  115. /// <param name="path">New directory path.</param>
  116. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  117. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  118. /// <exception cref="SftpPermissionDeniedException">Permission to change directory denied by remote host -or- a SSH command was denied by the server.</exception>
  119. /// <exception cref="SftpPathNotFoundException">The path in <paramref name="path"/> was not found on the remote host.</exception>
  120. /// <exception cref="SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  121. public void ChangeDirectory(string path)
  122. {
  123. if (path == null)
  124. throw new ArgumentNullException("path");
  125. // Ensure that connection is established.
  126. this.EnsureConnection();
  127. this._sftpSession.ChangeDirectory(path);
  128. }
  129. /// <summary>
  130. /// Changes permissions of file(s) to specified mode.
  131. /// </summary>
  132. /// <param name="path">File(s) path, may match multiple files.</param>
  133. /// <param name="mode">The mode.</param>
  134. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  135. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  136. /// <exception cref="SftpPermissionDeniedException">Permission to change permission on the path(s) was denied by the remote host -or- a SSH command was denied by the server.</exception>
  137. /// <exception cref="SftpPathNotFoundException">The path in <paramref name="path"/> was not found on the remote host.</exception>
  138. /// <exception cref="SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  139. public void ChangePermissions(string path, short mode)
  140. {
  141. var file = this.Get(path);
  142. file.SetPermissions(mode);
  143. }
  144. /// <summary>
  145. /// Creates remote directory specified by path.
  146. /// </summary>
  147. /// <param name="path">Directory path to create.</param>
  148. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  149. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  150. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to create the directory was denied by the remote host -or- a SSH command was denied by the server.</exception>
  151. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  152. public void CreateDirectory(string path)
  153. {
  154. if (path.IsNullOrWhiteSpace())
  155. throw new ArgumentException(path);
  156. // Ensure that connection is established.
  157. this.EnsureConnection();
  158. var fullPath = this._sftpSession.GetCanonicalPath(path);
  159. this._sftpSession.RequestMkDir(fullPath);
  160. }
  161. /// <summary>
  162. /// Deletes remote directory specified by path.
  163. /// </summary>
  164. /// <param name="path">Directory to be deleted path.</param>
  165. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  166. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  167. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to delete the directory was denied by the remote host -or- a SSH command was denied by the server.</exception>
  168. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  169. public void DeleteDirectory(string path)
  170. {
  171. if (path.IsNullOrWhiteSpace())
  172. throw new ArgumentException("path");
  173. // Ensure that connection is established.
  174. this.EnsureConnection();
  175. var fullPath = this._sftpSession.GetCanonicalPath(path);
  176. this._sftpSession.RequestRmDir(fullPath);
  177. }
  178. /// <summary>
  179. /// Deletes remote file specified by path.
  180. /// </summary>
  181. /// <param name="path">File to be deleted path.</param>
  182. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  183. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  184. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to delete the file was denied by the remote host -or- a SSH command was denied by the server.</exception>
  185. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  186. public void DeleteFile(string path)
  187. {
  188. if (path.IsNullOrWhiteSpace())
  189. throw new ArgumentException("path");
  190. // Ensure that connection is established.
  191. this.EnsureConnection();
  192. var fullPath = this._sftpSession.GetCanonicalPath(path);
  193. this._sftpSession.RequestRemove(fullPath);
  194. }
  195. /// <summary>
  196. /// Renames remote file from old path to new path.
  197. /// </summary>
  198. /// <param name="oldPath">Path to the old file location.</param>
  199. /// <param name="newPath">Path to the new file location.</param>
  200. /// <exception cref="ArgumentNullException"><paramref name="oldPath"/> or <paramref name="newPath"/> is null.</exception>
  201. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  202. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to rename the file was denied by the remote host -or- a SSH command was denied by the server.</exception>
  203. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  204. public void RenameFile(string oldPath, string newPath)
  205. {
  206. if (oldPath == null)
  207. throw new ArgumentNullException("oldPath");
  208. if (newPath == null)
  209. throw new ArgumentNullException("newPath");
  210. // Ensure that connection is established.
  211. this.EnsureConnection();
  212. var oldFullPath = this._sftpSession.GetCanonicalPath(oldPath);
  213. var newFullPath = this._sftpSession.GetCanonicalPath(newPath);
  214. this._sftpSession.RequestRename(oldFullPath, newFullPath);
  215. }
  216. /// <summary>
  217. /// Creates a symbolic link from old path to new path.
  218. /// </summary>
  219. /// <param name="path">The old path.</param>
  220. /// <param name="linkPath">The new path.</param>
  221. /// <exception cref="ArgumentException"><paramref name="path"/> or <paramref name="linkPath"/> is null or contains whitespace characters.</exception>
  222. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  223. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to create the symbolic link was denied by the remote host -or- a SSH command was denied by the server.</exception>
  224. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  225. public void SymbolicLink(string path, string linkPath)
  226. {
  227. if (path.IsNullOrWhiteSpace())
  228. throw new ArgumentException("path");
  229. if (linkPath.IsNullOrWhiteSpace())
  230. throw new ArgumentException("linkPath");
  231. // Ensure that connection is established.
  232. this.EnsureConnection();
  233. var fullPath = this._sftpSession.GetCanonicalPath(path);
  234. var linkFullPath = this._sftpSession.GetCanonicalPath(linkPath);
  235. this._sftpSession.RequestSymLink(fullPath, linkFullPath);
  236. }
  237. /// <summary>
  238. /// Retrieves list of files in remote directory.
  239. /// </summary>
  240. /// <param name="path">The path.</param>
  241. /// <returns>List of directory entries</returns>
  242. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  243. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  244. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host -or- a SSH command was denied by the server.</exception>
  245. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  246. public IEnumerable<SftpFile> ListDirectory(string path)
  247. {
  248. return InternalListDirectory(path, null);
  249. }
  250. /// <summary>
  251. /// Begins an asynchronous operation of retrieving list of files in remote directory.
  252. /// </summary>
  253. /// <param name="path">The path.</param>
  254. /// <param name="asyncCallback">The method to be called when the asynchronous write operation is completed.</param>
  255. /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
  256. /// <returns>
  257. /// An <see cref="IAsyncResult"/> that references the asynchronous operation.
  258. /// </returns>
  259. public IAsyncResult BeginListDirectory(string path, AsyncCallback asyncCallback, object state)
  260. {
  261. var asyncResult = new SftpListDirectoryAsyncResult(asyncCallback, state);
  262. this.ExecuteThread(() =>
  263. {
  264. try
  265. {
  266. var result = this.InternalListDirectory(path, asyncResult);
  267. asyncResult.SetAsCompleted(result, false);
  268. }
  269. catch (Exception exp)
  270. {
  271. asyncResult.SetAsCompleted(exp, false);
  272. }
  273. });
  274. return asyncResult;
  275. }
  276. /// <summary>
  277. /// Ends an asynchronous operation of retrieving list of files in remote directory.
  278. /// </summary>
  279. /// <param name="asyncResult">The pending asynchronous SFTP request.</param>
  280. /// <returns>
  281. /// List of files
  282. /// </returns>
  283. /// <exception cref="ArgumentException">Either the IAsyncResult object (<paramref name="asyncResult"/>) did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.</exception>
  284. public IEnumerable<SftpFile> EndListDirectory(IAsyncResult asyncResult)
  285. {
  286. var ar = asyncResult as SftpListDirectoryAsyncResult;
  287. if (ar == null || ar.EndInvokeCalled)
  288. throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.");
  289. // Wait for operation to complete, then return result or throw exception
  290. return ar.EndInvoke();
  291. }
  292. /// <summary>
  293. /// Gets reference to remote file or directory.
  294. /// </summary>
  295. /// <param name="path">The path.</param>
  296. /// <returns></returns>
  297. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  298. public SftpFile Get(string path)
  299. {
  300. if (path == null)
  301. throw new ArgumentNullException("path");
  302. // Ensure that connection is established.
  303. this.EnsureConnection();
  304. var fullPath = this._sftpSession.GetCanonicalPath(path);
  305. var attributes = this._sftpSession.RequestLStat(fullPath);
  306. return new SftpFile(this._sftpSession, fullPath, attributes);
  307. }
  308. /// <summary>
  309. /// Checks whether file pr directory exists;
  310. /// </summary>
  311. /// <param name="path">The path.</param>
  312. /// <returns><c>true</c> if directory or file exists; otherwise <c>false</c>.</returns>
  313. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  314. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  315. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to perform the operation was denied by the remote host -or- a SSH command was denied by the server.</exception>
  316. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  317. public bool Exists(string path)
  318. {
  319. if (path.IsNullOrWhiteSpace())
  320. throw new ArgumentException("path");
  321. // Ensure that connection is established.
  322. this.EnsureConnection();
  323. var fullPath = this._sftpSession.GetCanonicalPath(path);
  324. // Try to open as a file
  325. var handle = this._sftpSession.RequestOpen(fullPath, Flags.Read, true);
  326. if (handle == null)
  327. {
  328. handle = this._sftpSession.RequestOpenDir(fullPath, true);
  329. }
  330. if (handle == null)
  331. {
  332. return false;
  333. }
  334. else
  335. {
  336. this._sftpSession.RequestClose(handle);
  337. return true;
  338. }
  339. }
  340. /// <summary>
  341. /// Downloads remote file specified by the path into the stream.
  342. /// </summary>
  343. /// <param name="path">File to download.</param>
  344. /// <param name="output">Stream to write the file into.</param>
  345. /// <exception cref="ArgumentNullException"><paramref name="output"/> is null.</exception>
  346. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  347. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  348. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to perform the operation was denied by the remote host -or- a SSH command was denied by the server.</exception>
  349. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  350. /// <remarks>Method calls made by this method to <paramref name="output"/>, may under certain conditions result in exceptions thrown by the stream.</remarks>
  351. public void DownloadFile(string path, Stream output)
  352. {
  353. this.InternalDownloadFile(path, output, null);
  354. }
  355. /// <summary>
  356. /// Begins an asynchronous file downloading into the stream.
  357. /// </summary>
  358. /// <param name="path">The path.</param>
  359. /// <param name="output">The output.</param>
  360. /// <param name="asyncCallback">The method to be called when the asynchronous write operation is completed.</param>
  361. /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
  362. /// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
  363. /// <exception cref="ArgumentNullException"><paramref name="output"/> is null.</exception>
  364. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  365. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  366. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to perform the operation was denied by the remote host -or- a SSH command was denied by the server.</exception>
  367. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  368. /// <remarks>Method calls made by this method to <paramref name="output"/>, may under certain conditions result in exceptions thrown by the stream.</remarks>
  369. public IAsyncResult BeginDownloadFile(string path, Stream output, AsyncCallback asyncCallback, object state)
  370. {
  371. if (path.IsNullOrWhiteSpace())
  372. throw new ArgumentException("path");
  373. if (output == null)
  374. throw new ArgumentNullException("output");
  375. // Ensure that connection is established.
  376. this.EnsureConnection();
  377. var asyncResult = new SftpDownloadAsyncResult(asyncCallback, state);
  378. this.ExecuteThread(() =>
  379. {
  380. try
  381. {
  382. this.InternalDownloadFile(path, output, asyncResult);
  383. asyncResult.SetAsCompleted(null, false);
  384. }
  385. catch (Exception exp)
  386. {
  387. asyncResult.SetAsCompleted(exp, false);
  388. }
  389. });
  390. return asyncResult;
  391. }
  392. /// <summary>
  393. /// Ends an asynchronous file downloading into the stream.
  394. /// </summary>
  395. /// <param name="asyncResult">The pending asynchronous SFTP request.</param>
  396. /// <exception cref="ArgumentException">Either the IAsyncResult object (<paramref name="asyncResult"/>) did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.</exception>
  397. public void EndDownloadFile(IAsyncResult asyncResult)
  398. {
  399. var ar = asyncResult as SftpDownloadAsyncResult;
  400. if (ar == null || ar.EndInvokeCalled)
  401. throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.");
  402. // Wait for operation to complete, then return result or throw exception
  403. ar.EndInvoke();
  404. }
  405. /// <summary>
  406. /// Uploads stream into remote file..
  407. /// </summary>
  408. /// <param name="input">Data input stream.</param>
  409. /// <param name="path">Remote file path.</param>
  410. /// <exception cref="ArgumentNullException"><paramref name="input"/> is null.</exception>
  411. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  412. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  413. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to upload the file was denied by the remote host -or- a SSH command was denied by the server.</exception>
  414. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  415. /// <remarks>Method calls made by this method to <paramref name="input"/>, may under certain conditions result in exceptions thrown by the stream.</remarks>
  416. public void UploadFile(Stream input, string path)
  417. {
  418. this.UploadFile(input, path, true);
  419. }
  420. /// <summary>
  421. /// Uploads stream into remote file..
  422. /// </summary>
  423. /// <param name="input">Data input stream.</param>
  424. /// <param name="path">Remote file path.</param>
  425. /// <param name="canOverride">if set to <c>true</c> then existing file will be overwritten.</param>
  426. /// <exception cref="ArgumentNullException"><paramref name="input"/> is null.</exception>
  427. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  428. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  429. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to upload the file was denied by the remote host -or- a SSH command was denied by the server.</exception>
  430. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  431. /// <remarks>
  432. /// Method calls made by this method to <paramref name="input"/>, may under certain conditions result in exceptions thrown by the stream.
  433. /// </remarks>
  434. public void UploadFile(Stream input, string path, bool canOverride)
  435. {
  436. var flags = Flags.Write | Flags.Truncate;
  437. if (canOverride)
  438. flags |= Flags.CreateNewOrOpen;
  439. else
  440. flags |= Flags.CreateNew;
  441. this.InternalUploadFile(input, path, null, flags);
  442. }
  443. /// <summary>
  444. /// Begins an asynchronous uploading the steam into remote file.
  445. /// </summary>
  446. /// <param name="input">Data input stream.</param>
  447. /// <param name="path">Remote file path.</param>
  448. /// <param name="asyncCallback">The method to be called when the asynchronous write operation is completed.</param>
  449. /// <param name="state">A user-provided object that distinguishes this particular asynchronous write request from other requests.</param>
  450. /// <returns>An <see cref="IAsyncResult"/> that references the asynchronous operation.</returns>
  451. /// <exception cref="ArgumentNullException"><paramref name="input"/> is null.</exception>
  452. /// <exception cref="ArgumentException"><paramref name="path"/> is null or contains whitespace characters.</exception>
  453. /// <exception cref="SshConnectionException">Client is not connected.</exception>
  454. /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to list the contents of the directory was denied by the remote host -or- a SSH command was denied by the server.</exception>
  455. /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
  456. /// <remarks>Method calls made by this method to <paramref name="input"/>, may under certain conditions result in exceptions thrown by the stream.</remarks>
  457. public IAsyncResult BeginUploadFile(Stream input, string path, AsyncCallback asyncCallback, object state)
  458. {
  459. if (input == null)
  460. throw new ArgumentNullException("input");
  461. if (path.IsNullOrWhiteSpace())
  462. throw new ArgumentException("path");
  463. // Ensure that connection is established.
  464. this.EnsureConnection();
  465. var asyncResult = new SftpUploadAsyncResult(asyncCallback, state);
  466. this.ExecuteThread(() =>
  467. {
  468. try
  469. {
  470. this.InternalUploadFile(input, path, asyncResult, Flags.Write | Flags.CreateNewOrOpen | Flags.Truncate);
  471. asyncResult.SetAsCompleted(null, false);
  472. }
  473. catch (Exception exp)
  474. {
  475. asyncResult.SetAsCompleted(exp, false);
  476. }
  477. });
  478. return asyncResult;
  479. }
  480. /// <summary>
  481. /// Ends an asynchronous uploading the steam into remote file.
  482. /// </summary>
  483. /// <param name="asyncResult">The pending asynchronous SFTP request.</param>
  484. /// <exception cref="ArgumentException">Either the IAsyncResult object (<paramref name="asyncResult"/>) did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.</exception>
  485. public void EndUploadFile(IAsyncResult asyncResult)
  486. {
  487. var ar = asyncResult as SftpUploadAsyncResult;
  488. if (ar == null || ar.EndInvokeCalled)
  489. throw new ArgumentException("Either the IAsyncResult object did not come from the corresponding async method on this type, or EndExecute was called multiple times with the same IAsyncResult.");
  490. // Wait for operation to complete, then return result or throw exception
  491. ar.EndInvoke();
  492. }
  493. #region File Methods
  494. /// <summary>
  495. /// Appends lines to a file, and then closes the file.
  496. /// </summary>
  497. /// <param name="path">The file to append the lines to. The file is created if it does not already exist.</param>
  498. /// <param name="contents">The lines to append to the file.</param>
  499. /// <exception cref="ArgumentNullException"><paramref name="path"/> or <paramref name="contents"/> is null.</exception>
  500. public void AppendAllLines(string path, IEnumerable<string> contents)
  501. {
  502. if (contents == null)
  503. throw new ArgumentNullException("contents");
  504. using (var stream = this.AppendText(path))
  505. {
  506. foreach (var line in contents)
  507. {
  508. stream.WriteLine(line);
  509. }
  510. }
  511. }
  512. /// <summary>
  513. /// Appends lines to a file by using a specified encoding, and then closes the file.
  514. /// </summary>
  515. /// <param name="path">The file to append the lines to. The file is created if it does not already exist.</param>
  516. /// <param name="contents">The lines to append to the file.</param>
  517. /// <param name="encoding">The character encoding to use.</param>
  518. /// <exception cref="ArgumentNullException"><paramref name="path"/>, <paramref name="contents"/> or <paramref name="encoding"/> is null.</exception>
  519. public void AppendAllLines(string path, IEnumerable<string> contents, Encoding encoding)
  520. {
  521. if (contents == null)
  522. throw new ArgumentNullException("contents");
  523. using (var stream = this.AppendText(path, encoding))
  524. {
  525. foreach (var line in contents)
  526. {
  527. stream.WriteLine(line);
  528. }
  529. }
  530. }
  531. /// <summary>
  532. /// Opens a file, appends the specified string to the file, and then closes the file.
  533. /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file.
  534. /// </summary>
  535. /// <param name="path">The file to append the specified string to.</param>
  536. /// <param name="contents">The string to append to the file.</param>
  537. /// <exception cref="ArgumentNullException"><paramref name="path"/> or <paramref name="contents"/> is null.</exception>
  538. public void AppendAllText(string path, string contents)
  539. {
  540. using (var stream = this.AppendText(path))
  541. {
  542. stream.Write(contents);
  543. }
  544. }
  545. /// <summary>
  546. /// Opens a file, appends the specified string to the file, and then closes the file.
  547. /// If the file does not exist, this method creates a file, writes the specified string to the file, then closes the file.
  548. /// </summary>
  549. /// <param name="path">The file to append the specified string to.</param>
  550. /// <param name="contents">The string to append to the file.</param>
  551. /// <param name="encoding">The character encoding to use.</param>
  552. /// <exception cref="ArgumentNullException"><paramref name="path"/>, <paramref name="contents"/> or <paramref name="encoding"/> is null.</exception>
  553. public void AppendAllText(string path, string contents, Encoding encoding)
  554. {
  555. using (var stream = this.AppendText(path, encoding))
  556. {
  557. stream.Write(contents);
  558. }
  559. }
  560. /// <summary>
  561. /// Creates a <see cref="System.IO.StreamWriter"/> that appends UTF-8 encoded text to an existing file.
  562. /// </summary>
  563. /// <param name="path">The path to the file to append to.</param>
  564. /// <returns>A StreamWriter that appends UTF-8 encoded text to an existing file.</returns>
  565. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  566. public StreamWriter AppendText(string path)
  567. {
  568. return this.AppendText(path, Encoding.UTF8);
  569. }
  570. /// <summary>
  571. /// Creates a <see cref="System.IO.StreamWriter"/> that appends UTF-8 encoded text to an existing file.
  572. /// </summary>
  573. /// <param name="path">The path to the file to append to.</param>
  574. /// <param name="encoding">The character encoding to use.</param>
  575. /// <returns>
  576. /// A StreamWriter that appends UTF-8 encoded text to an existing file.
  577. /// </returns>
  578. /// <exception cref="ArgumentNullException"><paramref name="path"/> or <paramref name="encoding"/> is null.</exception>
  579. public StreamWriter AppendText(string path, Encoding encoding)
  580. {
  581. if (encoding == null)
  582. throw new ArgumentNullException("encoding");
  583. return new StreamWriter(new SftpFileStream(this._sftpSession, path, FileMode.Append, FileAccess.Write), encoding);
  584. }
  585. /// <summary>
  586. /// Creates or overwrites a file in the specified path.
  587. /// </summary>
  588. /// <param name="path">The path and name of the file to create.</param>
  589. /// <returns>A <see cref="SftpFileStream"/> that provides read/write access to the file specified in path</returns>
  590. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  591. public SftpFileStream Create(string path)
  592. {
  593. return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite);
  594. }
  595. /// <summary>
  596. /// Creates or overwrites the specified file.
  597. /// </summary>
  598. /// <param name="path">The path and name of the file to create.</param>
  599. /// <param name="bufferSize">The number of bytes buffered for reads and writes to the file.</param>
  600. /// <returns>A <see cref="SftpFileStream"/> that provides read/write access to the file specified in path</returns>
  601. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  602. public SftpFileStream Create(string path, int bufferSize)
  603. {
  604. return new SftpFileStream(this._sftpSession, path, FileMode.Create, FileAccess.ReadWrite, bufferSize);
  605. }
  606. /// <summary>
  607. /// Creates or opens a file for writing UTF-8 encoded text.
  608. /// </summary>
  609. /// <param name="path">The file to be opened for writing.</param>
  610. /// <returns>A <see cref="System.IO.StreamWriter"/> that writes to the specified file using UTF-8 encoding.</returns>
  611. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  612. public StreamWriter CreateText(string path)
  613. {
  614. return new StreamWriter(this.OpenWrite(path), Encoding.UTF8);
  615. }
  616. /// <summary>
  617. /// Creates or opens a file for writing UTF-8 encoded text.
  618. /// </summary>
  619. /// <param name="path">The file to be opened for writing.</param>
  620. /// <param name="encoding">The character encoding to use.</param>
  621. /// <returns> A <see cref="System.IO.StreamWriter"/> that writes to the specified file using UTF-8 encoding. </returns>
  622. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  623. public StreamWriter CreateText(string path, Encoding encoding)
  624. {
  625. return new StreamWriter(this.OpenWrite(path), encoding);
  626. }
  627. /// <summary>
  628. /// Deletes the specified file or directory. An exception is not thrown if the specified file does not exist.
  629. /// </summary>
  630. /// <param name="path">The name of the file or directory to be deleted. Wildcard characters are not supported.</param>
  631. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  632. public void Delete(string path)
  633. {
  634. var file = this.Get(path);
  635. file.Delete();
  636. }
  637. /// <summary>
  638. /// Returns the date and time the specified file or directory was last accessed.
  639. /// </summary>
  640. /// <param name="path">The file or directory for which to obtain access date and time information.</param>
  641. /// <returns>A <see cref="System.DateTime"/> structure set to the date and time that the specified file or directory was last accessed. This value is expressed in local time.</returns>
  642. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  643. public DateTime GetLastAccessTime(string path)
  644. {
  645. var file = this.Get(path);
  646. return file.LastAccessTime;
  647. }
  648. /// <summary>
  649. /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last accessed.
  650. /// </summary>
  651. /// <param name="path">The file or directory for which to obtain access date and time information.</param>
  652. /// <returns>A <see cref="System.DateTime"/> structure set to the date and time that the specified file or directory was last accessed. This value is expressed in UTC time.</returns>
  653. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  654. public DateTime GetLastAccessTimeUtc(string path)
  655. {
  656. var file = this.Get(path);
  657. return file.LastAccessTime.ToUniversalTime();
  658. }
  659. /// <summary>
  660. /// Returns the date and time the specified file or directory was last written to.
  661. /// </summary>
  662. /// <param name="path">The file or directory for which to obtain write date and time information.</param>
  663. /// <returns>A <see cref="System.DateTime"/> structure set to the date and time that the specified file or directory was last written to. This value is expressed in local time.</returns>
  664. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  665. public DateTime GetLastWriteTime(string path)
  666. {
  667. var file = this.Get(path);
  668. return file.LastWriteTime;
  669. }
  670. /// <summary>
  671. /// Returns the date and time, in coordinated universal time (UTC), that the specified file or directory was last written to.
  672. /// </summary>
  673. /// <param name="path">The file or directory for which to obtain write date and time information.</param>
  674. /// <returns>A <see cref="System.DateTime"/> structure set to the date and time that the specified file or directory was last written to. This value is expressed in UTC time.</returns>
  675. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  676. public DateTime GetLastWriteTimeUtc(string path)
  677. {
  678. var file = this.Get(path);
  679. return file.LastWriteTime.ToUniversalTime();
  680. }
  681. /// <summary>
  682. /// Opens a <see cref="SftpFileStream"/> on the specified path with read/write access.
  683. /// </summary>
  684. /// <param name="path">The file to open.</param>
  685. /// <param name="mode">A <see cref="System.IO.FileMode"/> value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten.</param>
  686. /// <returns>An unshared <see cref="SftpFileStream"/> that provides access to the specified file, with the specified mode and access.</returns>
  687. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  688. public SftpFileStream Open(string path, FileMode mode)
  689. {
  690. return new SftpFileStream(this._sftpSession, path, mode, FileAccess.ReadWrite);
  691. }
  692. /// <summary>
  693. /// Opens a <see cref="SftpFileStream"/> on the specified path, with the specified mode and access.
  694. /// </summary>
  695. /// <param name="path">The file to open.</param>
  696. /// <param name="mode">A <see cref="System.IO.FileMode"/> value that specifies whether a file is created if one does not exist, and determines whether the contents of existing files are retained or overwritten.</param>
  697. /// <param name="access">A <see cref="System.IO.FileAccess"/> value that specifies the operations that can be performed on the file.</param>
  698. /// <returns>An unshared <see cref="SftpFileStream"/> that provides access to the specified file, with the specified mode and access.</returns>
  699. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  700. public SftpFileStream Open(string path, FileMode mode, FileAccess access)
  701. {
  702. return new SftpFileStream(this._sftpSession, path, mode, access);
  703. }
  704. /// <summary>
  705. /// Opens an existing file for reading.
  706. /// </summary>
  707. /// <param name="path">The file to be opened for reading.</param>
  708. /// <returns>A read-only System.IO.FileStream on the specified path.</returns>
  709. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  710. public SftpFileStream OpenRead(string path)
  711. {
  712. return new SftpFileStream(this._sftpSession, path, FileMode.Open, FileAccess.Read);
  713. }
  714. /// <summary>
  715. /// Opens an existing UTF-8 encoded text file for reading.
  716. /// </summary>
  717. /// <param name="path">The file to be opened for reading.</param>
  718. /// <returns>A <see cref="System.IO.StreamReader"/> on the specified path.</returns>
  719. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  720. public StreamReader OpenText(string path)
  721. {
  722. return new StreamReader(this.OpenRead(path), Encoding.UTF8);
  723. }
  724. /// <summary>
  725. /// Opens an existing file for writing.
  726. /// </summary>
  727. /// <param name="path">The file to be opened for writing.</param>
  728. /// <returns>An unshared <see cref="SftpFileStream"/> object on the specified path with <see cref="System.IO.FileAccess.Write"/> access.</returns>
  729. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  730. public SftpFileStream OpenWrite(string path)
  731. {
  732. return new SftpFileStream(this._sftpSession, path, FileMode.OpenOrCreate, FileAccess.Write);
  733. }
  734. /// <summary>
  735. /// Opens a binary file, reads the contents of the file into a byte array, and then closes the file.
  736. /// </summary>
  737. /// <param name="path">The file to open for reading.</param>
  738. /// <returns>A byte array containing the contents of the file.</returns>
  739. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  740. public byte[] ReadAllBytes(string path)
  741. {
  742. using (var stream = this.OpenRead(path))
  743. {
  744. var buffer = new byte[stream.Length];
  745. stream.Read(buffer, 0, buffer.Length);
  746. return buffer;
  747. }
  748. }
  749. /// <summary>
  750. /// Opens a text file, reads all lines of the file, and then closes the file.
  751. /// </summary>
  752. /// <param name="path">The file to open for reading.</param>
  753. /// <returns>A string array containing all lines of the file.</returns>
  754. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  755. public string[] ReadAllLines(string path)
  756. {
  757. return this.ReadAllLines(path, Encoding.UTF8);
  758. }
  759. /// <summary>
  760. /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file.
  761. /// </summary>
  762. /// <param name="path">The file to open for reading.</param>
  763. /// <param name="encoding">The encoding applied to the contents of the file.</param>
  764. /// <returns>A string array containing all lines of the file.</returns>
  765. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  766. public string[] ReadAllLines(string path, Encoding encoding)
  767. {
  768. var lines = new List<string>();
  769. using (var stream = new StreamReader(this.OpenRead(path), encoding))
  770. {
  771. while (!stream.EndOfStream)
  772. {
  773. lines.Add(stream.ReadLine());
  774. }
  775. }
  776. return lines.ToArray();
  777. }
  778. /// <summary>
  779. /// Opens a text file, reads all lines of the file, and then closes the file.
  780. /// </summary>
  781. /// <param name="path">The file to open for reading.</param>
  782. /// <returns>A string containing all lines of the file.</returns>
  783. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  784. public string ReadAllText(string path)
  785. {
  786. return this.ReadAllText(path, Encoding.UTF8);
  787. }
  788. /// <summary>
  789. /// Opens a file, reads all lines of the file with the specified encoding, and then closes the file.
  790. /// </summary>
  791. /// <param name="path">The file to open for reading.</param>
  792. /// <param name="encoding">The encoding applied to the contents of the file.</param>
  793. /// <returns>A string containing all lines of the file.</returns>
  794. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  795. public string ReadAllText(string path, Encoding encoding)
  796. {
  797. var lines = new List<string>();
  798. using (var stream = new StreamReader(this.OpenRead(path), encoding))
  799. {
  800. return stream.ReadToEnd();
  801. }
  802. }
  803. /// <summary>
  804. /// Reads the lines of a file.
  805. /// </summary>
  806. /// <param name="path">The file to read.</param>
  807. /// <returns>The lines of the file.</returns>
  808. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  809. public IEnumerable<string> ReadLines(string path)
  810. {
  811. return this.ReadAllLines(path);
  812. }
  813. /// <summary>
  814. /// Read the lines of a file that has a specified encoding.
  815. /// </summary>
  816. /// <param name="path">The file to read.</param>
  817. /// <param name="encoding">The encoding that is applied to the contents of the file.</param>
  818. /// <returns>The lines of the file.</returns>
  819. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  820. public IEnumerable<string> ReadLines(string path, Encoding encoding)
  821. {
  822. return this.ReadAllLines(path, encoding);
  823. }
  824. /// <summary>
  825. /// Sets the date and time the specified file was last accessed.
  826. /// </summary>
  827. /// <param name="path">The file for which to set the access date and time information.</param>
  828. /// <param name="lastAccessTime">A <see cref="System.DateTime"/> containing the value to set for the last access date and time of path. This value is expressed in local time.</param>
  829. [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")]
  830. public void SetLastAccessTime(string path, DateTime lastAccessTime)
  831. {
  832. throw new NotImplementedException();
  833. }
  834. /// <summary>
  835. /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last accessed.
  836. /// </summary>
  837. /// <param name="path">The file for which to set the access date and time information.</param>
  838. /// <param name="lastAccessTimeUtc">A <see cref="System.DateTime"/> containing the value to set for the last access date and time of path. This value is expressed in UTC time.</param>
  839. [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")]
  840. public void SetLastAccessTimeUtc(string path, DateTime lastAccessTimeUtc)
  841. {
  842. throw new NotImplementedException();
  843. }
  844. /// <summary>
  845. /// Sets the date and time that the specified file was last written to.
  846. /// </summary>
  847. /// <param name="path">The file for which to set the date and time information.</param>
  848. /// <param name="lastWriteTime">A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in local time.</param>
  849. [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")]
  850. public void SetLastWriteTime(string path, DateTime lastWriteTime)
  851. {
  852. throw new NotImplementedException();
  853. }
  854. /// <summary>
  855. /// Sets the date and time, in coordinated universal time (UTC), that the specified file was last written to.
  856. /// </summary>
  857. /// <param name="path">The file for which to set the date and time information.</param>
  858. /// <param name="lastWriteTimeUtc">A System.DateTime containing the value to set for the last write date and time of path. This value is expressed in UTC time.</param>
  859. [Obsolete("Note: This method currently throws NotImplementedException because it has not yet been implemented.")]
  860. public void SetLastWriteTimeUtc(string path, DateTime lastWriteTimeUtc)
  861. {
  862. throw new NotImplementedException();
  863. }
  864. /// <summary>
  865. /// Creates a new file, writes the specified byte array to the file, and then closes the file. If the target file already exists, it is overwritten.
  866. /// </summary>
  867. /// <param name="path">The file to write to.</param>
  868. /// <param name="bytes">The bytes to write to the file.</param>
  869. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  870. public void WriteAllBytes(string path, byte[] bytes)
  871. {
  872. using (var stream = this.OpenWrite(path))
  873. {
  874. stream.Write(bytes, 0, bytes.Length);
  875. }
  876. }
  877. /// <summary>
  878. /// Creates a new file, writes a collection of strings to the file, and then closes the file.
  879. /// </summary>
  880. /// <param name="path">The file to write to.</param>
  881. /// <param name="contents">The lines to write to the file.</param>
  882. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  883. public void WriteAllLines(string path, IEnumerable<string> contents)
  884. {
  885. this.WriteAllLines(path, contents, Encoding.UTF8);
  886. }
  887. /// <summary>
  888. /// Creates a new file, write the specified string array to the file, and then closes the file.
  889. /// </summary>
  890. /// <param name="path">The file to write to.</param>
  891. /// <param name="contents">The string array to write to the file.</param>
  892. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  893. public void WriteAllLines(string path, string[] contents)
  894. {
  895. this.WriteAllLines(path, contents, Encoding.UTF8);
  896. }
  897. /// <summary>
  898. /// Creates a new file by using the specified encoding, writes a collection of strings to the file, and then closes the file.
  899. /// </summary>
  900. /// <param name="path">The file to write to.</param>
  901. /// <param name="contents">The lines to write to the file.</param>
  902. /// <param name="encoding">The character encoding to use.</param>
  903. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  904. public void WriteAllLines(string path, IEnumerable<string> contents, Encoding encoding)
  905. {
  906. using (var stream = this.CreateText(path, encoding))
  907. {
  908. foreach (var line in contents)
  909. {
  910. stream.WriteLine(line);
  911. }
  912. }
  913. }
  914. /// <summary>
  915. /// Creates a new file, writes the specified string array to the file by using the specified encoding, and then closes the file.
  916. /// </summary>
  917. /// <param name="path">The file to write to.</param>
  918. /// <param name="contents">The string array to write to the file.</param>
  919. /// <param name="encoding">An <see cref="System.Text.Encoding"/> object that represents the character encoding applied to the string array.</param>
  920. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  921. public void WriteAllLines(string path, string[] contents, Encoding encoding)
  922. {
  923. using (var stream = this.CreateText(path, encoding))
  924. {
  925. foreach (var line in contents)
  926. {
  927. stream.WriteLine(line);
  928. }
  929. }
  930. }
  931. /// <summary>
  932. /// Creates a new file, writes the specified string to the file, and then closes the file. If the target file already exists, it is overwritten.
  933. /// </summary>
  934. /// <param name="path">The file to write to.</param>
  935. /// <param name="contents">The string to write to the file.</param>
  936. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  937. public void WriteAllText(string path, string contents)
  938. {
  939. using (var stream = this.CreateText(path))
  940. {
  941. stream.Write(contents);
  942. }
  943. }
  944. /// <summary>
  945. /// Creates a new file, writes the specified string to the file using the specified encoding, and then closes the file. If the target file already exists, it is overwritten.
  946. /// </summary>
  947. /// <param name="path">The file to write to.</param>
  948. /// <param name="contents">The string to write to the file.</param>
  949. /// <param name="encoding">The encoding to apply to the string.</param>
  950. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  951. public void WriteAllText(string path, string contents, Encoding encoding)
  952. {
  953. using (var stream = this.CreateText(path, encoding))
  954. {
  955. stream.Write(contents);
  956. }
  957. }
  958. /// <summary>
  959. /// Gets the <see cref="SftpFileAttributes"/> of the file on the path.
  960. /// </summary>
  961. /// <param name="path">The path to the file.</param>
  962. /// <returns>The <see cref="SftpFileAttributes"/> of the file on the path.</returns>
  963. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  964. public SftpFileAttributes GetAttributes(string path)
  965. {
  966. var fullPath = this._sftpSession.GetCanonicalPath(path);
  967. return this._sftpSession.RequestLStat(fullPath);
  968. }
  969. /// <summary>
  970. /// Sets the specified <see cref="SftpFileAttributes"/> of the file on the specified path.
  971. /// </summary>
  972. /// <param name="path">The path to the file.</param>
  973. /// <param name="fileAttributes">The desired <see cref="SftpFileAttributes"/>.</param>
  974. /// <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  975. public void SetAttributes(string path, SftpFileAttributes fileAttributes)
  976. {
  977. var fullPath = this._sftpSession.GetCanonicalPath(path);
  978. this._sftpSession.RequestSetStat(fullPath, fileAttributes);
  979. }
  980. // Please don't forget this when you implement these methods: <exception cref="ArgumentNullException"><paramref name="path"/> is null.</exception>
  981. //public FileSecurity GetAccessControl(string path);
  982. //public FileSecurity GetAccessControl(string path, AccessControlSections includeSections);
  983. //public DateTime GetCreationTime(string path);
  984. //public DateTime GetCreationTimeUtc(string path);
  985. //public void SetAccessControl(string path, FileSecurity fileSecurity);
  986. //public void SetCreationTime(string path, DateTime creationTime);
  987. //public void SetCreationTimeUtc(string path, DateTime creationTimeUtc);
  988. #endregion
  989. private IEnumerable<SftpFile> InternalListDirectory(string path, SftpListDirectoryAsyncResult asynchResult)
  990. {
  991. if (path == null)
  992. throw new ArgumentNullException("path");
  993. // Ensure that connection is established.
  994. this.EnsureConnection();
  995. var fullPath = this._sftpSession.GetCanonicalPath(path);
  996. var handle = this._sftpSession.RequestOpenDir(fullPath);
  997. var basePath = fullPath;
  998. if (!basePath.EndsWith("/"))
  999. basePath = string.Format("{0}/", fullPath);
  1000. var result = new List<SftpFile>();
  1001. var files = this._sftpSession.RequestReadDir(handle);
  1002. while (files != null)
  1003. {
  1004. result.AddRange(from f in files
  1005. select new SftpFile(this._sftpSession, string.Format(CultureInfo.InvariantCulture, "{0}{1}", basePath, f.Key), f.Value));
  1006. if (asynchResult != null)
  1007. {
  1008. asynchResult.Update(result.Count);
  1009. }
  1010. files = this._sftpSession.RequestReadDir(handle);
  1011. }
  1012. this._sftpSession.RequestClose(handle);
  1013. return result;
  1014. }
  1015. private void InternalDownloadFile(string path, Stream output, SftpDownloadAsyncResult asynchResult)
  1016. {
  1017. if (output == null)
  1018. throw new ArgumentNullException("output");
  1019. if (path.IsNullOrWhiteSpace())
  1020. throw new ArgumentException("path");
  1021. // Ensure that connection is established.
  1022. this.EnsureConnection();
  1023. var fullPath = this._sftpSession.GetCanonicalPath(path);
  1024. var handle = this._sftpSession.RequestOpen(fullPath, Flags.Read);
  1025. ulong offset = 0;
  1026. var data = this._sftpSession.RequestRead(handle, offset, this.BufferSize);
  1027. // Read data while available
  1028. while (data.Length > 0)
  1029. {
  1030. output.Write(data, 0, data.Length);
  1031. output.Flush();
  1032. offset += (ulong)data.Length;
  1033. // Call callback to report number of bytes read
  1034. if (asynchResult != null)
  1035. {
  1036. asynchResult.Update(offset);
  1037. }
  1038. data = this._sftpSession.RequestRead(handle, offset, this.BufferSize);
  1039. }
  1040. this._sftpSession.RequestClose(handle);
  1041. }
  1042. private void InternalUploadFile(Stream input, string path, SftpUploadAsyncResult asynchResult, Flags flags)
  1043. {
  1044. if (input == null)
  1045. throw new ArgumentNullException("input");
  1046. if (path.IsNullOrWhiteSpace())
  1047. throw new ArgumentException("path");
  1048. // Ensure that connection is established.
  1049. this.EnsureConnection();
  1050. var fullPath = this._sftpSession.GetCanonicalPath(path);
  1051. var handle = this._sftpSession.RequestOpen(fullPath, flags);
  1052. ulong offset = 0;
  1053. var buffer = new byte[this.BufferSize];
  1054. var uploadCompleted = false;
  1055. do
  1056. {
  1057. var bytesRead = input.Read(buffer, 0, buffer.Length);
  1058. if (bytesRead < this.BufferSize)
  1059. {
  1060. var data = new byte[bytesRead];
  1061. Buffer.BlockCopy(buffer, 0, data, 0, bytesRead);
  1062. using (var wait = new AutoResetEvent(false))
  1063. {
  1064. this._sftpSession.RequestWrite(handle, offset, data, wait);
  1065. }
  1066. uploadCompleted = true;
  1067. }
  1068. else
  1069. {
  1070. this._sftpSession.RequestWrite(handle, offset, buffer, null);
  1071. }
  1072. offset += (uint)bytesRead;
  1073. // Call callback to report number of bytes read
  1074. if (asynchResult != null)
  1075. {
  1076. asynchResult.Update(offset);
  1077. }
  1078. } while (!uploadCompleted);
  1079. this._sftpSession.RequestClose(handle);
  1080. }
  1081. partial void ExecuteThread(Action action);
  1082. /// <summary>
  1083. /// Called when client is connected to the server.
  1084. /// </summary>
  1085. protected override void OnConnected()
  1086. {
  1087. base.OnConnected();
  1088. this._sftpSession = new SftpSession(this.Session, this.OperationTimeout);
  1089. this._sftpSession.Connect();
  1090. // Resolve current running version
  1091. this.ProtocolVersion = this._sftpSession.ProtocolVersion;
  1092. }
  1093. /// <summary>
  1094. /// Called when client is disconnecting from the server.
  1095. /// </summary>
  1096. protected override void OnDisconnecting()
  1097. {
  1098. base.OnDisconnecting();
  1099. this._sftpSession.Disconnect();
  1100. }
  1101. /// <summary>
  1102. /// Releases unmanaged and - optionally - managed resources
  1103. /// </summary>
  1104. /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged ResourceMessages.</param>
  1105. protected override void Dispose(bool disposing)
  1106. {
  1107. if (this._sftpSession != null)
  1108. {
  1109. this._sftpSession.Dispose();
  1110. this._sftpSession = null;
  1111. }
  1112. base.Dispose(disposing);
  1113. }
  1114. }
  1115. }