SftpClient.cs 47 KB

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