FtpClient.cs 145 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493
  1. using System;
  2. using System.IO;
  3. using System.Net.Sockets;
  4. using System.Text;
  5. using System.Text.RegularExpressions;
  6. using System.Reflection;
  7. using System.Threading;
  8. using System.Collections.Generic;
  9. using System.Diagnostics;
  10. using System.Web;
  11. using System.Security.Cryptography.X509Certificates;
  12. using System.Globalization;
  13. using System.Net.FtpClient.Extensions;
  14. using System.Security.Authentication;
  15. namespace System.Net.FtpClient {
  16. /// <summary>
  17. /// Event is fired when a ssl certificate needs to be validated
  18. /// </summary>
  19. /// <param name="control">The contol connection that triggered the event</param>
  20. /// <param name="e">Event args</param>
  21. public delegate void FtpSslValidation(FtpClient control, FtpSslValidationEventArgs e);
  22. /// <summary>
  23. /// FTP Control Connection. Speaks the FTP protocol with the server and
  24. /// provides facilities for performing basic transactions.
  25. ///
  26. /// Debugging problems with FTP transactions is much easier to do when
  27. /// you can see exactly what is sent to the server and the reply
  28. /// System.Net.FtpClient gets in return. Please review the Debug example
  29. /// below for information on how to add TraceListeners for capturing
  30. /// the convorsation between System.Net.FtpClient and the server.
  31. /// </summary>
  32. /// <example>The following example illustrates how to assist in debugging
  33. /// System.Net.FtpClient by getting a transaction log from the server.
  34. /// <code source="..\Examples\Debug.cs" lang="cs" />
  35. /// </example>
  36. /// <example>The following example demonstrates adding a custom file
  37. /// listing parser in the event that you encounter a list format
  38. /// not already supported.
  39. /// <code source="..\Examples\CustomParser.cs" lang="cs" />
  40. /// </example>
  41. /// <example>The following example demonstrates how to validate
  42. /// a SSL certificate when using SSL/TLS.
  43. /// <code source="..\Examples\ValidateCertificate.cs" lang="cs" />
  44. /// </example>
  45. /// <example>The following example demonsrates how to download a file.
  46. /// <code source="..\Examples\OpenRead.cs" lang="cs" />
  47. /// </example>
  48. /// <example>The following example demonstrates how to download a file
  49. /// using a URI object.
  50. /// <code source="..\Examples\OpenReadURI.cs" lang="cs" />
  51. /// </example>
  52. /// <example>The following example demonstrates how to upload a file.
  53. /// <code source="..\Examples\OpenWrite.cs" lang="cs" />
  54. /// </example>
  55. /// <example>The following example demonstrates how to upload a file
  56. /// using a URI object.
  57. /// <code source="..\Examples\OpenWriteURI.cs" lang="cs" />
  58. /// </example>
  59. /// <example>The following example demonstrates how to append to a file.
  60. /// <code source="..\Examples\OpenAppend.cs" lang="cs" />
  61. /// </example>
  62. /// <example>The following example demonstrates how to append to a file
  63. /// using a URI object.
  64. /// <code source="..\Examples\OpenAppendURI.cs" lang="cs" />
  65. /// </example>
  66. /// <example>The following example demonstrates how to get a file
  67. /// listing from the server.
  68. /// <code source="..\Examples\GetListing.cs" lang="cs" />
  69. /// </example>
  70. public class FtpClient : IFtpClient {
  71. /// <summary>
  72. /// Used for internally syncrhonizing access to this
  73. /// object from multiple threads
  74. /// </summary>
  75. readonly Object m_lock = new Object();
  76. /// <summary>
  77. /// A list of asynchronoous methods that are in progress
  78. /// </summary>
  79. readonly Dictionary<IAsyncResult, object> m_asyncmethods = new Dictionary<IAsyncResult, object>();
  80. /// <summary>
  81. /// Control connection socket stream
  82. /// </summary>
  83. FtpSocketStream m_stream = null;
  84. bool m_isDisposed = false;
  85. /// <summary>
  86. /// Gets a value indicating if this object has already been disposed.
  87. /// </summary>
  88. public bool IsDisposed {
  89. get {
  90. return m_isDisposed;
  91. }
  92. private set {
  93. m_isDisposed = value;
  94. }
  95. }
  96. /// <summary>
  97. /// Gets the base stream for talking to the server via
  98. /// the control connection.
  99. /// </summary>
  100. protected Stream BaseStream {
  101. get {
  102. return m_stream;
  103. }
  104. }
  105. FtpIpVersion m_ipVersions = FtpIpVersion.ANY;
  106. /// <summary>
  107. /// Flags specifying which versions of the internet protocol to
  108. /// support when making a connection. All addresses returned during
  109. /// name resolution are tried until a successful connection is made.
  110. /// You can fine tune which versions of the internet protocol to use
  111. /// by adding or removing flags here. I.e., setting this property
  112. /// to FtpIpVersion.IPv4 will cause the connection process to
  113. /// ignore IPv6 addresses. The default value is ANY version.
  114. /// </summary>
  115. [FtpControlConnectionClone]
  116. public FtpIpVersion InternetProtocolVersions {
  117. get {
  118. return m_ipVersions;
  119. }
  120. set {
  121. m_ipVersions = value;
  122. }
  123. }
  124. int m_socketPollInterval = 15000;
  125. /// <summary>
  126. /// Gets or sets the length of time in miliseconds
  127. /// that must pass since the last socket activity
  128. /// before calling Poll() on the socket to test for
  129. /// connectivity. Setting this interval too low will
  130. /// have a negative impact on perfomance. Setting this
  131. /// interval to 0 disables Poll()'ing all together.
  132. /// The default value is 15 seconds.
  133. /// </summary>
  134. [FtpControlConnectionClone]
  135. public int SocketPollInterval {
  136. get { return m_socketPollInterval; }
  137. set {
  138. m_socketPollInterval = value;
  139. if (m_stream != null)
  140. m_stream.SocketPollInterval = value;
  141. }
  142. }
  143. bool m_staleDataTest = true;
  144. /// <summary>
  145. /// Gets or sets a value indicating whether a test should be performed to
  146. /// see if there is stale (unrequested data) sitting on the socket. In some
  147. /// cases the control connection may time out but before the server closes
  148. /// the connection it might send a 4xx response that was unexpected and
  149. /// can cause synchronization errors with transactions. To avoid this
  150. /// problem the Execute() method checks to see if there is any data
  151. /// available on the socket before executing a command. On Azure hosting
  152. /// platforms this check can cause an exception to be thrown. In order
  153. /// to work around the exception you can set this property to false
  154. /// which will skip the test entirely however doing so eliminates the
  155. /// best effort attempt of detecting such scenarios. See this thread
  156. /// for more details about the Azure problem:
  157. /// https://netftp.codeplex.com/discussions/535879
  158. /// </summary>
  159. [FtpControlConnectionClone]
  160. public bool StaleDataCheck {
  161. get { return m_staleDataTest; }
  162. set { m_staleDataTest = value; }
  163. }
  164. /// <summary>
  165. /// Gets a value indicating if the connection is alive
  166. /// </summary>
  167. public bool IsConnected {
  168. get {
  169. if (m_stream != null)
  170. return m_stream.IsConnected;
  171. return false;
  172. }
  173. }
  174. bool m_threadSafeDataChannels = true;
  175. /// <summary>
  176. /// When this value is set to true (default) the control connection
  177. /// is cloned and a new connection the server is established for the
  178. /// data channel operation. This is a thread safe approach to make
  179. /// asynchronous operations on a single control connection transparent
  180. /// to the developer.
  181. /// </summary>
  182. [FtpControlConnectionClone]
  183. public bool EnableThreadSafeDataConnections {
  184. get {
  185. return m_threadSafeDataChannels;
  186. }
  187. set {
  188. m_threadSafeDataChannels = value;
  189. }
  190. }
  191. bool m_isClone = false;
  192. /// <summary>
  193. /// Gets a value indicating if this control connection is a clone. This property
  194. /// is used with data streams to determine if the connection should be closed
  195. /// when the stream is closed. Servers typically only allow 1 data connection
  196. /// per control connection. If you try to open multiple data connections this
  197. /// object will be cloned for 2 or more resulting in N new connections to the
  198. /// server.
  199. /// </summary>
  200. internal bool IsClone {
  201. get {
  202. return m_isClone;
  203. }
  204. private set {
  205. m_isClone = value;
  206. }
  207. }
  208. Encoding m_textEncoding = Encoding.ASCII;
  209. /// <summary>
  210. /// Gets or sets the text encoding being used when talking with the server. The default
  211. /// value is Encoding.ASCII however upon connection, the client checks
  212. /// for UTF8 support and if it's there this property is switched over to
  213. /// Encoding.UTF8. Manually setting this value overrides automatic detection
  214. /// based on the FEAT list; if you change this value it's always used
  215. /// regardless of what the server advertises, if anything.
  216. /// </summary>
  217. [FtpControlConnectionClone]
  218. public Encoding Encoding {
  219. get {
  220. return m_textEncoding;
  221. }
  222. set {
  223. lock (m_lock)
  224. {
  225. m_textEncoding = value;
  226. }
  227. }
  228. }
  229. string m_host = null;
  230. /// <summary>
  231. /// The server to connect to
  232. /// </summary>
  233. [FtpControlConnectionClone]
  234. public string Host {
  235. get {
  236. return m_host;
  237. }
  238. set {
  239. m_host = value;
  240. }
  241. }
  242. int m_port = 0;
  243. /// <summary>
  244. /// The port to connect to. If this value is set to 0 (Default) the port used
  245. /// will be determined by the type of SSL used or if no SSL is to be used it
  246. /// will automatically connect to port 21.
  247. /// </summary>
  248. [FtpControlConnectionClone]
  249. public int Port {
  250. get {
  251. // automatically determine port
  252. // when m_port is 0.
  253. if (m_port == 0) {
  254. switch (EncryptionMode) {
  255. case FtpEncryptionMode.None:
  256. case FtpEncryptionMode.Explicit:
  257. return 21;
  258. case FtpEncryptionMode.Implicit:
  259. return 990;
  260. }
  261. }
  262. return m_port;
  263. }
  264. set {
  265. m_port = value;
  266. }
  267. }
  268. NetworkCredential m_credentials = null;
  269. /// <summary>
  270. /// Credentials used for authentication
  271. /// </summary>
  272. [FtpControlConnectionClone]
  273. public NetworkCredential Credentials {
  274. get {
  275. return m_credentials;
  276. }
  277. set {
  278. m_credentials = value;
  279. }
  280. }
  281. int m_maxDerefCount = 20;
  282. /// <summary>
  283. /// Gets or sets a value that controls the maximum depth
  284. /// of recursion that DereferenceLink() will follow symbolic
  285. /// links before giving up. You can also specify the value
  286. /// to be used as one of the overloaded parameters to the
  287. /// DereferenceLink() method. The default value is 20. Specifying
  288. /// -1 here means inifinitly try to resolve a link. This is
  289. /// not recommended for obvious reasons (stack overflow).
  290. /// </summary>
  291. [FtpControlConnectionClone]
  292. public int MaximumDereferenceCount {
  293. get {
  294. return m_maxDerefCount;
  295. }
  296. set {
  297. m_maxDerefCount = value;
  298. }
  299. }
  300. X509CertificateCollection m_clientCerts = new X509CertificateCollection();
  301. /// <summary>
  302. /// Client certificates to be used in SSL authentication process
  303. /// </summary>
  304. [FtpControlConnectionClone]
  305. public X509CertificateCollection ClientCertificates {
  306. get {
  307. return m_clientCerts;
  308. }
  309. protected set {
  310. m_clientCerts = value;
  311. }
  312. }
  313. FtpDataConnectionType m_dataConnectionType = FtpDataConnectionType.AutoPassive;
  314. /// <summary>
  315. /// 数据连接类型,默认为AutoPassive,它首先尝试与EPSV的连接,
  316. /// 如果失败,然后在放弃之前尝试PASV。 如果您确切知道需要哪种连接,
  317. /// 您可以通过在此处定义特定类型的被动或活动数据连接来略微提高性能。
  318. /// Data connection type, default is AutoPassive which tries
  319. /// a connection with EPSV first and if it fails then tries
  320. /// PASV before giving up. If you know exactly which kind of
  321. /// connection you need you can slightly increase performance
  322. /// by defining a speicific type of passive or active data
  323. /// connection here.
  324. /// </summary>
  325. [FtpControlConnectionClone]
  326. public FtpDataConnectionType DataConnectionType {
  327. get {
  328. return m_dataConnectionType;
  329. }
  330. set {
  331. m_dataConnectionType = value;
  332. }
  333. }
  334. bool m_ungracefullDisconnect = false;
  335. /// <summary>
  336. /// Disconnect from the server without sending QUIT. This helps
  337. /// work around IOExceptions caused by buggy connection resets
  338. /// when closing the control connection.
  339. /// </summary>
  340. [FtpControlConnectionClone]
  341. public bool UngracefullDisconnection {
  342. get {
  343. return m_ungracefullDisconnect;
  344. }
  345. set {
  346. m_ungracefullDisconnect = value;
  347. }
  348. }
  349. int m_connectTimeout = 15000;
  350. /// <summary>
  351. /// Gets or sets the length of time in miliseconds to wait for a connection
  352. /// attempt to succeed before giving up. Default is 15000 (15 seconds).
  353. /// </summary>
  354. [FtpControlConnectionClone]
  355. public int ConnectTimeout {
  356. get {
  357. return m_connectTimeout;
  358. }
  359. set {
  360. m_connectTimeout = value;
  361. }
  362. }
  363. int m_readTimeout = 15000;
  364. /// <summary>
  365. /// Gets or sets the length of time wait in miliseconds for data to be
  366. /// read from the underlying stream. The default value is 15000 (15 seconds).
  367. /// </summary>
  368. [FtpControlConnectionClone]
  369. public int ReadTimeout {
  370. get {
  371. return m_readTimeout;
  372. }
  373. set {
  374. m_readTimeout = value;
  375. }
  376. }
  377. int m_dataConnectionConnectTimeout = 15000;
  378. /// <summary>
  379. /// Gets or sets the length of time in miliseconds for a data connection
  380. /// to be established before giving up. Default is 15000 (15 seconds).
  381. /// </summary>
  382. [FtpControlConnectionClone]
  383. public int DataConnectionConnectTimeout {
  384. get {
  385. return m_dataConnectionConnectTimeout;
  386. }
  387. set {
  388. m_dataConnectionConnectTimeout = value;
  389. }
  390. }
  391. int m_dataConnectionReadTimeout = 15000;
  392. /// <summary>
  393. /// Gets or sets the length of time in miliseconds the data channel
  394. /// should wait for the server to send data. Default value is
  395. /// 15000 (15 seconds).
  396. /// </summary>
  397. [FtpControlConnectionClone]
  398. public int DataConnectionReadTimeout {
  399. get {
  400. return m_dataConnectionReadTimeout;
  401. }
  402. set {
  403. m_dataConnectionReadTimeout = value;
  404. }
  405. }
  406. bool m_keepAlive = false;
  407. /// <summary>
  408. /// Gets or sets a value indicating if SocketOption.KeepAlive should be set on
  409. /// the underlying stream's socket. If the connection is alive, the option is
  410. /// adjusted in real-time. The value is stored and the KeepAlive option is set
  411. /// accordingly upon any new connections. The value set here is also applied to
  412. /// all future data streams. It has no affect on cloned control connections or
  413. /// data connections already in progress. The default value is false.
  414. /// </summary>
  415. [FtpControlConnectionClone]
  416. public bool SocketKeepAlive {
  417. get {
  418. return m_keepAlive;
  419. }
  420. set {
  421. m_keepAlive = value;
  422. if (m_stream != null)
  423. m_stream.SetSocketOption(Sockets.SocketOptionLevel.Socket, Sockets.SocketOptionName.KeepAlive, value);
  424. }
  425. }
  426. FtpCapability m_caps = FtpCapability.NONE;
  427. /// <summary>
  428. /// Gets the server capabilties represented by flags
  429. /// </summary>
  430. [FtpControlConnectionClone]
  431. public FtpCapability Capabilities {
  432. get {
  433. if (m_stream == null || !m_stream.IsConnected) {
  434. Connect();
  435. }
  436. return m_caps;
  437. }
  438. protected set {
  439. m_caps = value;
  440. }
  441. }
  442. FtpHashAlgorithm m_hashAlgorithms = FtpHashAlgorithm.NONE;
  443. /// <summary>
  444. /// Get the hash types supported by the server, if any. This
  445. /// is a recent extension to the protocol that is not fully
  446. /// standardized and is not guarateed to work. See here for
  447. /// more details:
  448. /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
  449. /// </summary>
  450. public FtpHashAlgorithm HashAlgorithms {
  451. get {
  452. if (m_stream == null || !m_stream.IsConnected) {
  453. Connect();
  454. }
  455. return m_hashAlgorithms;
  456. }
  457. private set {
  458. m_hashAlgorithms = value;
  459. }
  460. }
  461. FtpEncryptionMode m_encryptionmode = FtpEncryptionMode.None;
  462. /// <summary>
  463. /// 要使用的SSL类型,或无。 默认值为none。 显式是TLS,隐式是SSL。
  464. /// Type of SSL to use, or none. Default is none. Explicit is TLS, Implicit is SSL.
  465. /// </summary>
  466. [FtpControlConnectionClone]
  467. public FtpEncryptionMode EncryptionMode {
  468. get {
  469. return m_encryptionmode;
  470. }
  471. set {
  472. m_encryptionmode = value;
  473. }
  474. }
  475. bool m_dataConnectionEncryption = true;
  476. /// <summary>
  477. /// Indicates if data channel transfers should be encrypted. Only valid if EncryptionMode
  478. /// property is not equal to FtpSslMode.None.
  479. /// </summary>
  480. [FtpControlConnectionClone]
  481. public bool DataConnectionEncryption {
  482. get {
  483. return m_dataConnectionEncryption;
  484. }
  485. set {
  486. m_dataConnectionEncryption = value;
  487. }
  488. }
  489. private SslProtocols m_SslProtocols = SslProtocols.Default;
  490. /// <summary>
  491. /// Encryption protocols to use. Only valid if EncryptionMode property is not equal to FtpSslMode.None.
  492. /// Default value is .NET Framework defaults from SslStream class.
  493. /// </summary>
  494. [FtpControlConnectionClone]
  495. public SslProtocols SslProtocols
  496. {
  497. get {
  498. return m_SslProtocols;
  499. }
  500. set {
  501. m_SslProtocols = value;
  502. }
  503. }
  504. FtpSslValidation m_sslvalidate = null;
  505. /// <summary>
  506. /// 触发事件以验证SSL证书。 如果未处理此事件,并且验证证书时出现错误,则连接将中止。
  507. /// Event is fired to validate SSL certificates. If this event is
  508. /// not handled and there are errors validating the certificate
  509. /// the connection will be aborted.
  510. /// </summary>
  511. /// <example><code source="..\Examples\ValidateCertificate.cs" lang="cs" /></example>
  512. public event FtpSslValidation ValidateCertificate {
  513. add {
  514. m_sslvalidate += value;
  515. }
  516. remove {
  517. m_sslvalidate -= value;
  518. }
  519. }
  520. /// <summary>
  521. /// Gets the type of system/server that we're
  522. /// connected to.
  523. /// </summary>
  524. public string SystemType {
  525. get {
  526. FtpReply reply = Execute("SYST");
  527. if (reply.Success)
  528. return reply.Message;
  529. return null;
  530. }
  531. }
  532. /// <summary>
  533. /// Performs a bitwise and to check if the specified
  534. /// flag is set on the Capabilities enum property.
  535. /// </summary>
  536. /// <param name="cap">The capability to check for</param>
  537. /// <returns>True if the feature was found</returns>
  538. public bool HasFeature(FtpCapability cap) {
  539. return ((this.Capabilities & cap) == cap);
  540. }
  541. /// <summary>
  542. /// Fires the SSL validation event
  543. /// </summary>
  544. /// <param name="e">Event Args</param>
  545. void OnValidateCertficate(FtpSslValidationEventArgs e) {
  546. FtpSslValidation evt;
  547. evt = m_sslvalidate;
  548. if (evt != null)
  549. evt(this, e);
  550. }
  551. /// <summary>
  552. /// Retretieves the delegate for the specified IAsyncResult and removes
  553. /// it from the m_asyncmethods collection if the operation is successfull
  554. /// </summary>
  555. /// <typeparam name="T">Type of delegate to retrieve</typeparam>
  556. /// <param name="ar">The IAsyncResult to retrieve the delegate for</param>
  557. /// <returns>The delegate that generated the specified IAsyncResult</returns>
  558. protected T GetAsyncDelegate<T>(IAsyncResult ar) {
  559. T func;
  560. lock (m_asyncmethods) {
  561. if (m_isDisposed) {
  562. throw new ObjectDisposedException("This connection object has already been disposed.");
  563. }
  564. if (!m_asyncmethods.ContainsKey(ar))
  565. throw new InvalidOperationException("The specified IAsyncResult could not be located.");
  566. if (!(m_asyncmethods[ar] is T)) {
  567. StackTrace st = new StackTrace(1);
  568. throw new InvalidCastException("The AsyncResult cannot be matched to the specified delegate. " +
  569. string.Format("Are you sure you meant to call {0} and not another method?",
  570. st.GetFrame(0).GetMethod().Name)
  571. );
  572. }
  573. func = (T)m_asyncmethods[ar];
  574. m_asyncmethods.Remove(ar);
  575. }
  576. return func;
  577. }
  578. /// <summary>
  579. /// Clones the control connection for opening multipe data streams
  580. /// </summary>
  581. /// <returns>A new control connection with the same property settings as this one</returns>
  582. /// <example><code source="..\Examples\CloneConnection.cs" lang="cs" /></example>
  583. protected FtpClient CloneConnection() {
  584. FtpClient conn = new FtpClient();
  585. conn.m_isClone = true;
  586. foreach (PropertyInfo prop in GetType().GetProperties()) {
  587. object[] attributes = prop.GetCustomAttributes(typeof(FtpControlConnectionClone), true);
  588. if (attributes != null && attributes.Length > 0) {
  589. prop.SetValue(conn, prop.GetValue(this, null), null);
  590. }
  591. }
  592. // always accept certficate no matter what because if code execution ever
  593. // gets here it means the certificate on the control connection object being
  594. // cloned was already accepted.
  595. conn.ValidateCertificate += new FtpSslValidation(
  596. delegate(FtpClient obj, FtpSslValidationEventArgs e) {
  597. e.Accept = true;
  598. });
  599. return conn;
  600. }
  601. /// <summary>
  602. /// Retreives a reply from the server. Do not execute this method
  603. /// unless you are sure that a reply has been sent, i.e., you
  604. /// executed a command. Doing so will cause the code to hang
  605. /// indefinitely waiting for a server reply that is never comming.
  606. /// </summary>
  607. /// <returns>FtpReply representing the response from the server</returns>
  608. /// <example><code source="..\Examples\BeginGetReply.cs" lang="cs" /></example>
  609. internal FtpReply GetReply() {
  610. FtpReply reply = new FtpReply();
  611. string buf;
  612. lock (m_lock)
  613. {
  614. if (!IsConnected)
  615. throw new InvalidOperationException("No connection to the server has been established.");
  616. m_stream.ReadTimeout = m_readTimeout;
  617. while ((buf = m_stream.ReadLine(Encoding)) != null) {
  618. Match m;
  619. FtpTrace.WriteLine(buf);
  620. if ((m = Regex.Match(buf, "^(?<code>[0-9]{3}) (?<message>.*)$")).Success) {
  621. reply.Code = m.Groups["code"].Value;
  622. reply.Message = m.Groups["message"].Value;
  623. break;
  624. }
  625. reply.InfoMessages += string.Format("{0}\n", buf);
  626. }
  627. }
  628. return reply;
  629. }
  630. /// <summary>
  631. /// Executes a command
  632. /// </summary>
  633. /// <param name="command">The command to execute with optional format place holders</param>
  634. /// <param name="args">Format parameters to the command</param>
  635. /// <returns>The servers reply to the command</returns>
  636. /// <example><code source="..\Examples\Execute.cs" lang="cs" /></example>
  637. public FtpReply Execute(string command, params object[] args) {
  638. return Execute(string.Format(command, args));
  639. }
  640. /// <summary>
  641. /// Executes a command
  642. /// </summary>
  643. /// <param name="command">The command to execute</param>
  644. /// <returns>The servers reply to the command</returns>
  645. /// <example><code source="..\Examples\Execute.cs" lang="cs" /></example>
  646. public FtpReply Execute(string command) {
  647. FtpReply reply;
  648. lock (m_lock)
  649. {
  650. if(StaleDataCheck) {
  651. if (m_stream != null && m_stream.SocketDataAvailable > 0) {
  652. // Data shouldn't be on the socket, if it is it probably
  653. // means we've been disconnected. Read and discard
  654. // whatever is there and close the connection.
  655. FtpTrace.WriteLine("There is stale data on the socket, maybe our connection timed out. Re-connecting.");
  656. if (m_stream.IsConnected && !m_stream.IsEncrypted) {
  657. byte[] buf = new byte[m_stream.SocketDataAvailable];
  658. m_stream.RawSocketRead(buf);
  659. FtpTrace.Write("The data was: ");
  660. FtpTrace.WriteLine(Encoding.GetString(buf).TrimEnd('\r', '\n'));
  661. }
  662. m_stream.Close();
  663. }
  664. }
  665. if (!IsConnected) {
  666. if (command == "QUIT") {
  667. FtpTrace.WriteLine("Not sending QUIT because the connection has already been closed.");
  668. return new FtpReply() {
  669. Code = "200",
  670. Message = "Connection already closed."
  671. };
  672. }
  673. Connect();
  674. }
  675. FtpTrace.WriteLine(command.StartsWith("PASS") ? "PASS <omitted>" : command);
  676. m_stream.WriteLine(m_textEncoding, command);
  677. reply = GetReply();
  678. }
  679. return reply;
  680. }
  681. delegate FtpReply AsyncExecute(string command);
  682. /// <summary>
  683. /// Performs an asynchronouse execution of the specified command
  684. /// </summary>
  685. /// <param name="command">The command to execute</param>
  686. /// <param name="callback">The AsyncCallback method</param>
  687. /// <param name="state">State object</param>
  688. /// <returns>IAsyncResult</returns>
  689. /// <example><code source="..\Examples\BeginExecute.cs" lang="cs" /></example>
  690. public IAsyncResult BeginExecute(string command, AsyncCallback callback, object state) {
  691. AsyncExecute func;
  692. IAsyncResult ar;
  693. ar = (func = new AsyncExecute(Execute)).BeginInvoke(command, callback, state);
  694. lock (m_asyncmethods) {
  695. m_asyncmethods.Add(ar, func);
  696. }
  697. return ar;
  698. }
  699. /// <summary>
  700. /// Ends an asynchronous command
  701. /// </summary>
  702. /// <param name="ar">IAsyncResult returned from BeginExecute</param>
  703. /// <returns>FtpReply object (never null).</returns>
  704. /// <example><code source="..\Examples\BeginExecute.cs" lang="cs" /></example>
  705. public FtpReply EndExecute(IAsyncResult ar) {
  706. return GetAsyncDelegate<AsyncExecute>(ar).EndInvoke(ar);
  707. }
  708. /// <summary>
  709. /// Connect to the server. Throws ObjectDisposedException if this object has been disposed.
  710. /// </summary>
  711. /// <example><code source="..\Examples\Connect.cs" lang="cs" /></example>
  712. public virtual void Connect() {
  713. FtpReply reply;
  714. lock (m_lock)
  715. {
  716. if (IsDisposed)
  717. throw new ObjectDisposedException("This FtpClient object has been disposed. It is no longer accessible.");
  718. if (m_stream == null) {
  719. m_stream = new FtpSocketStream();
  720. m_stream.ValidateCertificate += new FtpSocketStreamSslValidation(FireValidateCertficate);
  721. }
  722. else
  723. if (IsConnected)
  724. Disconnect();
  725. if (Host == null)
  726. throw new FtpException("No host has been specified");
  727. if (!IsClone)
  728. m_caps = FtpCapability.NONE;
  729. m_hashAlgorithms = FtpHashAlgorithm.NONE;
  730. m_stream.ConnectTimeout = m_connectTimeout;
  731. m_stream.SocketPollInterval = m_socketPollInterval;
  732. m_stream.Connect(Host, Port, InternetProtocolVersions);
  733. m_stream.SetSocketOption(Sockets.SocketOptionLevel.Socket,
  734. Sockets.SocketOptionName.KeepAlive, m_keepAlive);
  735. if (EncryptionMode == FtpEncryptionMode.Implicit)
  736. m_stream.ActivateEncryption(Host,
  737. m_clientCerts.Count > 0 ? m_clientCerts : null,
  738. m_SslProtocols);
  739. if (!(reply = GetReply()).Success) {
  740. if (reply.Code == null) {
  741. throw new IOException("The connection was terminated before a greeting could be read.");
  742. }
  743. else {
  744. throw new FtpCommandException(reply);
  745. }
  746. }
  747. if (EncryptionMode == FtpEncryptionMode.Explicit) {
  748. if (!(reply = Execute("AUTH TLS")).Success)
  749. throw new FtpSecurityNotAvailableException("AUTH TLS command failed.");
  750. m_stream.ActivateEncryption(Host,
  751. m_clientCerts.Count > 0 ? m_clientCerts : null,
  752. m_SslProtocols);
  753. }
  754. if (m_credentials != null) {
  755. Authenticate();
  756. }
  757. if (m_stream.IsEncrypted && DataConnectionEncryption) {
  758. if (!(reply = Execute("PBSZ 0")).Success)
  759. throw new FtpCommandException(reply);
  760. if (!(reply = Execute("PROT P")).Success)
  761. throw new FtpCommandException(reply);
  762. }
  763. // if this is a clone these values
  764. // should have already been loaded
  765. // so save some bandwidth and CPU
  766. // time and skip executing this again.
  767. if (!IsClone) {
  768. if ((reply = Execute("FEAT")).Success && reply.InfoMessages != null) {
  769. GetFeatures(reply);
  770. }
  771. }
  772. // Enable UTF8 if the encoding is ASCII and UTF8 is supported
  773. if (m_textEncoding == Encoding.ASCII && HasFeature(FtpCapability.UTF8)) {
  774. m_textEncoding = Encoding.UTF8;
  775. }
  776. FtpTrace.WriteLine("Text encoding: " + m_textEncoding.ToString());
  777. if (m_textEncoding == Encoding.UTF8) {
  778. // If the server supports UTF8 it should already be enabled and this
  779. // command should not matter however there are conflicting drafts
  780. // about this so we'll just execute it to be safe.
  781. Execute("OPTS UTF8 ON");
  782. }
  783. }
  784. }
  785. /// <summary>
  786. /// Performs a login on the server. This method is overridable so
  787. /// that the login procedure can be changed to support, for example,
  788. /// a FTP proxy.
  789. /// </summary>
  790. protected virtual void Authenticate() {
  791. FtpReply reply;
  792. if (!(reply = Execute("USER {0}", Credentials.UserName)).Success)
  793. throw new FtpCommandException(reply);
  794. if (reply.Type == FtpResponseType.PositiveIntermediate
  795. && !(reply = Execute("PASS {0}", Credentials.Password)).Success)
  796. throw new FtpCommandException(reply);
  797. }
  798. /// <summary>
  799. /// Populates the capabilities flags based on capabilities
  800. /// supported by this server. This method is overridable
  801. /// so that new features can be supported
  802. /// </summary>
  803. /// <param name="reply">The reply object from the FEAT command. The InfoMessages property will
  804. /// contain a list of the features the server supported delimited by a new line '\n' character.</param>
  805. protected virtual void GetFeatures(FtpReply reply) {
  806. foreach (string feat in reply.InfoMessages.Split('\n')) {
  807. if (feat.ToUpper().Trim().StartsWith("MLST") || feat.ToUpper().Trim().StartsWith("MLSD"))
  808. m_caps |= FtpCapability.MLSD;
  809. else if (feat.ToUpper().Trim().StartsWith("MDTM"))
  810. m_caps |= FtpCapability.MDTM;
  811. else if (feat.ToUpper().Trim().StartsWith("REST STREAM"))
  812. m_caps |= FtpCapability.REST;
  813. else if (feat.ToUpper().Trim().StartsWith("SIZE"))
  814. m_caps |= FtpCapability.SIZE;
  815. else if (feat.ToUpper().Trim().StartsWith("UTF8"))
  816. m_caps |= FtpCapability.UTF8;
  817. else if (feat.ToUpper().Trim().StartsWith("PRET"))
  818. m_caps |= FtpCapability.PRET;
  819. else if (feat.ToUpper().Trim().StartsWith("MFMT"))
  820. m_caps |= FtpCapability.MFMT;
  821. else if (feat.ToUpper().Trim().StartsWith("MFCT"))
  822. m_caps |= FtpCapability.MFCT;
  823. else if (feat.ToUpper().Trim().StartsWith("MFF"))
  824. m_caps |= FtpCapability.MFF;
  825. else if (feat.ToUpper().Trim().StartsWith("MD5"))
  826. m_caps |= FtpCapability.MD5;
  827. else if (feat.ToUpper().Trim().StartsWith("XMD5"))
  828. m_caps |= FtpCapability.XMD5;
  829. else if (feat.ToUpper().Trim().StartsWith("XCRC"))
  830. m_caps |= FtpCapability.XCRC;
  831. else if (feat.ToUpper().Trim().StartsWith("XSHA1"))
  832. m_caps |= FtpCapability.XSHA1;
  833. else if (feat.ToUpper().Trim().StartsWith("XSHA256"))
  834. m_caps |= FtpCapability.XSHA256;
  835. else if (feat.ToUpper().Trim().StartsWith("XSHA512"))
  836. m_caps |= FtpCapability.XSHA512;
  837. else if (feat.ToUpper().Trim().StartsWith("HASH")) {
  838. Match m;
  839. m_caps |= FtpCapability.HASH;
  840. if ((m = Regex.Match(feat.ToUpper().Trim(), @"^HASH\s+(?<types>.*)$")).Success) {
  841. foreach (string type in m.Groups["types"].Value.Split(';')) {
  842. switch (type.ToUpper().Trim()) {
  843. case "SHA-1":
  844. case "SHA-1*":
  845. m_hashAlgorithms |= FtpHashAlgorithm.SHA1;
  846. break;
  847. case "SHA-256":
  848. case "SHA-256*":
  849. m_hashAlgorithms |= FtpHashAlgorithm.SHA256;
  850. break;
  851. case "SHA-512":
  852. case "SHA-512*":
  853. m_hashAlgorithms |= FtpHashAlgorithm.SHA512;
  854. break;
  855. case "MD5":
  856. case "MD5*":
  857. m_hashAlgorithms |= FtpHashAlgorithm.MD5;
  858. break;
  859. case "CRC":
  860. case "CRC*":
  861. m_hashAlgorithms |= FtpHashAlgorithm.CRC;
  862. break;
  863. }
  864. }
  865. }
  866. }
  867. }
  868. }
  869. delegate void AsyncConnect();
  870. /// <summary>
  871. /// Initiates a connection to the server
  872. /// </summary>
  873. /// <param name="callback">AsyncCallback method</param>
  874. /// <param name="state">State object</param>
  875. /// <returns>IAsyncResult</returns>
  876. /// <example><code source="..\Examples\BeginConnect.cs" lang="cs" /></example>
  877. public IAsyncResult BeginConnect(AsyncCallback callback, object state) {
  878. AsyncConnect func;
  879. IAsyncResult ar;
  880. ar = (func = new AsyncConnect(Connect)).BeginInvoke(callback, state);
  881. lock (m_asyncmethods) {
  882. m_asyncmethods.Add(ar, func);
  883. }
  884. return ar;
  885. }
  886. /// <summary>
  887. /// Ends an asynchronous connection attempt to the server
  888. /// </summary>
  889. /// <param name="ar">IAsyncResult returned from BeginConnect()</param>
  890. /// <example><code source="..\Examples\BeginConnect.cs" lang="cs" /></example>
  891. public void EndConnect(IAsyncResult ar) {
  892. GetAsyncDelegate<AsyncConnect>(ar).EndInvoke(ar);
  893. }
  894. /// <summary>
  895. /// Catches the socket stream ssl validation event and fires the event handlers
  896. /// attached to this object for validating SSL certificates
  897. /// </summary>
  898. /// <param name="stream">The stream that fired the event</param>
  899. /// <param name="e">The event args used to validate the certficate</param>
  900. void FireValidateCertficate(FtpSocketStream stream, FtpSslValidationEventArgs e) {
  901. OnValidateCertficate(e);
  902. }
  903. /// <summary>
  904. /// Disconnect from the server
  905. /// </summary>
  906. public virtual void Disconnect() {
  907. lock (m_lock)
  908. {
  909. if (m_stream != null && m_stream.IsConnected) {
  910. try {
  911. if (!UngracefullDisconnection) {
  912. Execute("QUIT");
  913. }
  914. }
  915. catch (SocketException sockex) {
  916. FtpTrace.WriteLine("FtpClient.Disconnect(): SocketException caught and discarded while closing control connection: {0}", sockex.ToString());
  917. }
  918. catch (IOException ioex) {
  919. FtpTrace.WriteLine("FtpClient.Disconnect(): IOException caught and discarded while closing control connection: {0}", ioex.ToString());
  920. }
  921. catch (FtpCommandException cmdex) {
  922. FtpTrace.WriteLine("FtpClient.Disconnect(): FtpCommandException caught and discarded while closing control connection: {0}", cmdex.ToString());
  923. }
  924. catch (FtpException ftpex) {
  925. FtpTrace.WriteLine("FtpClient.Disconnect(): FtpException caught and discarded while closing control connection: {0}", ftpex.ToString());
  926. }
  927. finally {
  928. m_stream.Close();
  929. }
  930. }
  931. }
  932. }
  933. delegate void AsyncDisconnect();
  934. /// <summary>
  935. /// Initiates a disconnection on the server
  936. /// </summary>
  937. /// <param name="callback">AsyncCallback method</param>
  938. /// <param name="state">State object</param>
  939. /// <returns>IAsyncResult</returns>
  940. /// <example><code source="..\Examples\BeginDisconnect.cs" lang="cs" /></example>
  941. public IAsyncResult BeginDisconnect(AsyncCallback callback, object state) {
  942. IAsyncResult ar;
  943. AsyncDisconnect func;
  944. ar = (func = new AsyncDisconnect(Disconnect)).BeginInvoke(callback, state);
  945. lock (m_asyncmethods) {
  946. m_asyncmethods.Add(ar, func);
  947. }
  948. return ar;
  949. }
  950. /// <summary>
  951. /// Ends a call to BeginDisconnect
  952. /// </summary>
  953. /// <param name="ar">IAsyncResult returned from BeginDisconnect</param>
  954. /// <example><code source="..\Examples\BeginConnect.cs" lang="cs" /></example>
  955. public void EndDisconnect(IAsyncResult ar) {
  956. GetAsyncDelegate<AsyncDisconnect>(ar).EndInvoke(ar);
  957. }
  958. /// <summary>
  959. /// Opens the specified type of passive data stream
  960. /// </summary>
  961. /// <param name="type">Type of passive data stream to open</param>
  962. /// <param name="command">The command to execute that requires a data stream</param>
  963. /// <param name="restart">Restart location in bytes for file transfer</param>
  964. /// <returns>A data stream ready to be used</returns>
  965. FtpDataStream OpenPassiveDataStream(FtpDataConnectionType type, string command, long restart) {
  966. FtpDataStream stream = null;
  967. FtpReply reply;
  968. Match m;
  969. string host = null;
  970. int port = 0;
  971. if (m_stream == null)
  972. throw new InvalidOperationException("The control connection stream is null! Generally this means there is no connection to the server. Cannot open a passive data stream.");
  973. if (type == FtpDataConnectionType.EPSV || type == FtpDataConnectionType.AutoPassive) {
  974. if (!(reply = Execute("EPSV")).Success) {
  975. // if we're connected with IPv4 and data channel type is AutoPassive then fallback to IPv4
  976. if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoPassive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == Sockets.AddressFamily.InterNetwork)
  977. return OpenPassiveDataStream(FtpDataConnectionType.PASV, command, restart);
  978. throw new FtpCommandException(reply);
  979. }
  980. m = Regex.Match(reply.Message, @"\(\|\|\|(?<port>\d+)\|\)");
  981. if (!m.Success) {
  982. throw new FtpException("Failed to get the EPSV port from: " + reply.Message);
  983. }
  984. host = m_host;
  985. port = int.Parse(m.Groups["port"].Value);
  986. }
  987. else {
  988. if (m_stream.LocalEndPoint.AddressFamily != Sockets.AddressFamily.InterNetwork)
  989. throw new FtpException("Only IPv4 is supported by the PASV command. Use EPSV instead.");
  990. if (!(reply = Execute("PASV")).Success)
  991. throw new FtpCommandException(reply);
  992. m = Regex.Match(reply.Message,
  993. @"(?<quad1>\d+)," +
  994. @"(?<quad2>\d+)," +
  995. @"(?<quad3>\d+)," +
  996. @"(?<quad4>\d+)," +
  997. @"(?<port1>\d+)," +
  998. @"(?<port2>\d+)"
  999. );
  1000. if (!m.Success || m.Groups.Count != 7)
  1001. throw new FtpException(string.Format("Malformed PASV response: {0}", reply.Message));
  1002. // PASVEX mode ignores the host supplied in the PASV response
  1003. if (type == FtpDataConnectionType.PASVEX)
  1004. host = m_host;
  1005. else
  1006. host = string.Format("{0}.{1}.{2}.{3}",
  1007. m.Groups["quad1"].Value,
  1008. m.Groups["quad2"].Value,
  1009. m.Groups["quad3"].Value,
  1010. m.Groups["quad4"].Value);
  1011. port = (int.Parse(m.Groups["port1"].Value) << 8) + int.Parse(m.Groups["port2"].Value);
  1012. }
  1013. stream = new FtpDataStream(this);
  1014. stream.ConnectTimeout = DataConnectionConnectTimeout;
  1015. stream.ReadTimeout = DataConnectionReadTimeout;
  1016. stream.Connect(host, port, InternetProtocolVersions);
  1017. stream.SetSocketOption(Sockets.SocketOptionLevel.Socket, Sockets.SocketOptionName.KeepAlive, m_keepAlive);
  1018. if (restart > 0) {
  1019. if (!(reply = Execute("REST {0}", restart)).Success)
  1020. throw new FtpCommandException(reply);
  1021. }
  1022. if (!(reply = Execute(command)).Success) {
  1023. stream.Close();
  1024. throw new FtpCommandException(reply);
  1025. }
  1026. // the command status is used to determine
  1027. // if a reply needs to be read from the server
  1028. // when the stream is closed so always set it
  1029. // otherwise things can get out of sync.
  1030. stream.CommandStatus = reply;
  1031. // this needs to take place after the command is executed
  1032. if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None)
  1033. stream.ActivateEncryption(m_host,
  1034. this.ClientCertificates.Count > 0 ? this.ClientCertificates : null,
  1035. m_SslProtocols);
  1036. return stream;
  1037. }
  1038. /// <summary>
  1039. /// Opens the specified type of active data stream
  1040. /// </summary>
  1041. /// <param name="type">Type of passive data stream to open</param>
  1042. /// <param name="command">The command to execute that requires a data stream</param>
  1043. /// <param name="restart">Restart location in bytes for file transfer</param>
  1044. /// <returns>A data stream ready to be used</returns>
  1045. FtpDataStream OpenActiveDataStream(FtpDataConnectionType type, string command, long restart) {
  1046. FtpDataStream stream = new FtpDataStream(this);
  1047. FtpReply reply;
  1048. IAsyncResult ar;
  1049. if (m_stream == null)
  1050. throw new InvalidOperationException("The control connection stream is null! Generally this means there is no connection to the server. Cannot open an active data stream.");
  1051. stream.Listen(m_stream.LocalEndPoint.Address, 0);
  1052. ar = stream.BeginAccept(null, null);
  1053. if (type == FtpDataConnectionType.EPRT || type == FtpDataConnectionType.AutoActive) {
  1054. int ipver = 0;
  1055. switch (stream.LocalEndPoint.AddressFamily) {
  1056. case Sockets.AddressFamily.InterNetwork:
  1057. ipver = 1; // IPv4
  1058. break;
  1059. case Sockets.AddressFamily.InterNetworkV6:
  1060. ipver = 2; // IPv6
  1061. break;
  1062. default:
  1063. throw new InvalidOperationException("The IP protocol being used is not supported.");
  1064. }
  1065. if (!(reply = Execute("EPRT |{0}|{1}|{2}|", ipver,
  1066. stream.LocalEndPoint.Address.ToString(), stream.LocalEndPoint.Port)).Success) {
  1067. // if we're connected with IPv4 and the data channel type is AutoActive then try to fall back to the PORT command
  1068. if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoActive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == Sockets.AddressFamily.InterNetwork) {
  1069. stream.ControlConnection = null; // we don't want this failed EPRT attempt to close our control connection when the stream is closed so clear out the reference.
  1070. stream.Close();
  1071. return OpenActiveDataStream(FtpDataConnectionType.PORT, command, restart);
  1072. }
  1073. else {
  1074. stream.Close();
  1075. throw new FtpCommandException(reply);
  1076. }
  1077. }
  1078. }
  1079. else {
  1080. if (m_stream.LocalEndPoint.AddressFamily != Sockets.AddressFamily.InterNetwork)
  1081. throw new FtpException("Only IPv4 is supported by the PORT command. Use EPRT instead.");
  1082. if (!(reply = Execute("PORT {0},{1},{2}",
  1083. stream.LocalEndPoint.Address.ToString().Replace('.', ','),
  1084. stream.LocalEndPoint.Port / 256,
  1085. stream.LocalEndPoint.Port % 256)).Success) {
  1086. stream.Close();
  1087. throw new FtpCommandException(reply);
  1088. }
  1089. }
  1090. if (restart > 0) {
  1091. if (!(reply = Execute("REST {0}", restart)).Success)
  1092. throw new FtpCommandException(reply);
  1093. }
  1094. if (!(reply = Execute(command)).Success) {
  1095. stream.Close();
  1096. throw new FtpCommandException(reply);
  1097. }
  1098. // the command status is used to determine
  1099. // if a reply needs to be read from the server
  1100. // when the stream is closed so always set it
  1101. // otherwise things can get out of sync.
  1102. stream.CommandStatus = reply;
  1103. ar.AsyncWaitHandle.WaitOne(m_dataConnectionConnectTimeout);
  1104. if (!ar.IsCompleted) {
  1105. stream.Close();
  1106. throw new TimeoutException("Timed out waiting for the server to connect to the active data socket.");
  1107. }
  1108. stream.EndAccept(ar);
  1109. if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None)
  1110. stream.ActivateEncryption(m_host,
  1111. this.ClientCertificates.Count > 0 ? this.ClientCertificates : null,
  1112. m_SslProtocols);
  1113. stream.SetSocketOption(Sockets.SocketOptionLevel.Socket, Sockets.SocketOptionName.KeepAlive, m_keepAlive);
  1114. stream.ReadTimeout = m_dataConnectionReadTimeout;
  1115. return stream;
  1116. }
  1117. /// <summary>
  1118. /// Opens a data stream.
  1119. /// </summary>
  1120. /// <param name='command'>The command to execute that requires a data stream</param>
  1121. /// <param name="restart">Restart location in bytes for file transfer</param>
  1122. /// <returns>The data stream.</returns>
  1123. FtpDataStream OpenDataStream(string command, long restart) {
  1124. FtpDataConnectionType type = m_dataConnectionType;
  1125. FtpDataStream stream = null;
  1126. lock (m_lock)
  1127. {
  1128. if (!IsConnected)
  1129. Connect();
  1130. // The PORT and PASV commands do not work with IPv6 so
  1131. // if either one of those types are set change them
  1132. // to EPSV or EPRT appropriately.
  1133. if (m_stream.LocalEndPoint.AddressFamily == Sockets.AddressFamily.InterNetworkV6) {
  1134. switch (type) {
  1135. case FtpDataConnectionType.PORT:
  1136. type = FtpDataConnectionType.EPRT;
  1137. FtpTrace.WriteLine("Changed data connection type to EPRT because we are connected with IPv6.");
  1138. break;
  1139. case FtpDataConnectionType.PASV:
  1140. case FtpDataConnectionType.PASVEX:
  1141. type = FtpDataConnectionType.EPSV;
  1142. FtpTrace.WriteLine("Changed data connection type to EPSV because we are connected with IPv6.");
  1143. break;
  1144. }
  1145. }
  1146. switch (type) {
  1147. case FtpDataConnectionType.AutoPassive:
  1148. case FtpDataConnectionType.EPSV:
  1149. case FtpDataConnectionType.PASV:
  1150. case FtpDataConnectionType.PASVEX:
  1151. stream = OpenPassiveDataStream(type, command, restart);
  1152. break;
  1153. case FtpDataConnectionType.AutoActive:
  1154. case FtpDataConnectionType.EPRT:
  1155. case FtpDataConnectionType.PORT:
  1156. stream = OpenActiveDataStream(type, command, restart);
  1157. break;
  1158. }
  1159. if (stream == null)
  1160. throw new InvalidOperationException("The specified data channel type is not implemented.");
  1161. }
  1162. return stream;
  1163. }
  1164. /// <summary>
  1165. /// Disconnects a data stream
  1166. /// </summary>
  1167. /// <param name="stream">The data stream to close</param>
  1168. internal FtpReply CloseDataStream(FtpDataStream stream) {
  1169. FtpReply reply = new FtpReply();
  1170. if (stream == null)
  1171. throw new ArgumentException("The data stream parameter was null");
  1172. lock (m_lock)
  1173. {
  1174. try
  1175. {
  1176. if (IsConnected) {
  1177. // if the command that required the data connection was
  1178. // not successful then there will be no reply from
  1179. // the server, however if the command was successful
  1180. // the server will send a reply when the data connection
  1181. // is closed.
  1182. if (stream.CommandStatus.Type == FtpResponseType.PositivePreliminary) {
  1183. if (!(reply = GetReply()).Success) {
  1184. throw new FtpCommandException(reply);
  1185. }
  1186. }
  1187. }
  1188. }
  1189. finally {
  1190. // if this is a clone of the original control
  1191. // connection we should Dispose()
  1192. if (IsClone) {
  1193. Disconnect();
  1194. Dispose();
  1195. }
  1196. }
  1197. }
  1198. return reply;
  1199. }
  1200. /// <summary>
  1201. /// Opens the specified file for reading
  1202. /// </summary>
  1203. /// <param name="path">The full or relative path of the file</param>
  1204. /// <returns>A stream for reading the file on the server</returns>
  1205. /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
  1206. public Stream OpenRead(string path) {
  1207. return OpenRead(path, FtpDataType.Binary, 0);
  1208. }
  1209. /// <summary>
  1210. /// Opens the specified file for reading
  1211. /// </summary>
  1212. /// <param name="path">The full or relative path of the file</param>
  1213. /// <param name="type">ASCII/Binary</param>
  1214. /// <returns>A stream for reading the file on the server</returns>
  1215. /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
  1216. public Stream OpenRead(string path, FtpDataType type) {
  1217. return OpenRead(path, type, 0);
  1218. }
  1219. /// <summary>
  1220. /// Opens the specified file for reading
  1221. /// </summary>
  1222. /// <param name="path">The full or relative path of the file</param>
  1223. /// <param name="restart">Resume location</param>
  1224. /// <returns>A stream for reading the file on the server</returns>
  1225. /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
  1226. public Stream OpenRead(string path, long restart) {
  1227. return OpenRead(path, FtpDataType.Binary, restart);
  1228. }
  1229. /// <summary>
  1230. /// Opens the specified file for reading
  1231. /// </summary>
  1232. /// <param name="path">The full or relative path of the file</param>
  1233. /// <param name="type">ASCII/Binary</param>
  1234. /// <param name="restart">Resume location</param>
  1235. /// <returns>A stream for reading the file on the server</returns>
  1236. /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
  1237. public virtual Stream OpenRead(string path, FtpDataType type, long restart) {
  1238. FtpClient client = null;
  1239. FtpDataStream stream = null;
  1240. long length = 0;
  1241. lock (m_lock)
  1242. {
  1243. if (m_threadSafeDataChannels) {
  1244. client = CloneConnection();
  1245. client.Connect();
  1246. client.SetWorkingDirectory(GetWorkingDirectory());
  1247. }
  1248. else {
  1249. client = this;
  1250. }
  1251. client.SetDataType(type);
  1252. length = client.GetFileSize(path);
  1253. stream = client.OpenDataStream(string.Format("RETR {0}", path.GetFtpPath()), restart);
  1254. }
  1255. if (stream != null) {
  1256. if (length > 0)
  1257. stream.SetLength(length);
  1258. if (restart > 0)
  1259. stream.SetPosition(restart);
  1260. }
  1261. return stream;
  1262. }
  1263. /// <summary>
  1264. /// Opens the specified file for reading
  1265. /// </summary>
  1266. /// <param name="path">The full or relative path of the file</param>
  1267. /// <param name="callback">Async Callback</param>
  1268. /// <param name="state">State object</param>
  1269. /// <returns>IAsyncResult</returns>
  1270. /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
  1271. public IAsyncResult BeginOpenRead(string path, AsyncCallback callback, object state) {
  1272. return BeginOpenRead(path, FtpDataType.Binary, 0, callback, state);
  1273. }
  1274. /// <summary>
  1275. /// Opens the specified file for reading
  1276. /// </summary>
  1277. /// <param name="path">The full or relative path of the file</param>
  1278. /// <param name="type">ASCII/Binary</param>
  1279. /// <param name="callback">Async Callback</param>
  1280. /// <param name="state">State object</param>
  1281. /// <returns>IAsyncResult</returns>
  1282. /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
  1283. public IAsyncResult BeginOpenRead(string path, FtpDataType type, AsyncCallback callback, object state) {
  1284. return BeginOpenRead(path, type, 0, callback, state);
  1285. }
  1286. /// <summary>
  1287. /// Opens the specified file for reading
  1288. /// </summary>
  1289. /// <param name="path">The full or relative path of the file</param>
  1290. /// <param name="restart">Resume location</param>
  1291. /// <param name="callback">Async Callback</param>
  1292. /// <param name="state">State object</param>
  1293. /// <returns>IAsyncResult</returns>
  1294. /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
  1295. public IAsyncResult BeginOpenRead(string path, long restart, AsyncCallback callback, object state) {
  1296. return BeginOpenRead(path, FtpDataType.Binary, restart, callback, state);
  1297. }
  1298. delegate Stream AsyncOpenRead(string path, FtpDataType type, long restart);
  1299. /// <summary>
  1300. /// Opens the specified file for reading
  1301. /// </summary>
  1302. /// <param name="path">The full or relative path of the file</param>
  1303. /// <param name="type">ASCII/Binary</param>
  1304. /// <param name="restart">Resume location</param>
  1305. /// <param name="callback">Async Callback</param>
  1306. /// <param name="state">State object</param>
  1307. /// <returns>IAsyncResult</returns>
  1308. /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
  1309. public IAsyncResult BeginOpenRead(string path, FtpDataType type, long restart, AsyncCallback callback, object state) {
  1310. AsyncOpenRead func;
  1311. IAsyncResult ar;
  1312. ar = (func = new AsyncOpenRead(OpenRead)).BeginInvoke(path, type, restart, callback, state);
  1313. lock (m_asyncmethods) {
  1314. m_asyncmethods.Add(ar, func);
  1315. }
  1316. return ar;
  1317. }
  1318. /// <summary>
  1319. /// Ends a call to BeginOpenRead()
  1320. /// </summary>
  1321. /// <param name="ar">IAsyncResult returned from BeginOpenRead()</param>
  1322. /// <returns>A readable stream</returns>
  1323. /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
  1324. public Stream EndOpenRead(IAsyncResult ar) {
  1325. return GetAsyncDelegate<AsyncOpenRead>(ar).EndInvoke(ar);
  1326. }
  1327. /// <summary>
  1328. /// Opens the specified file for writing
  1329. /// </summary>
  1330. /// <param name="path">Full or relative path of the file</param>
  1331. /// <returns>A stream for writing to the file on the server</returns>
  1332. /// <example><code source="..\Examples\OpenWrite.cs" lang="cs" /></example>
  1333. public Stream OpenWrite(string path) {
  1334. return OpenWrite(path, FtpDataType.Binary);
  1335. }
  1336. /// <summary>
  1337. /// Opens the specified file for writing
  1338. /// </summary>
  1339. /// <param name="path">Full or relative path of the file</param>
  1340. /// <param name="type">ASCII/Binary</param>
  1341. /// <returns>A stream for writing to the file on the server</returns>
  1342. /// <example><code source="..\Examples\OpenWrite.cs" lang="cs" /></example>
  1343. public virtual Stream OpenWrite(string path, FtpDataType type) {
  1344. FtpClient client = null;
  1345. FtpDataStream stream = null;
  1346. long length = 0;
  1347. lock (m_lock)
  1348. {
  1349. if (m_threadSafeDataChannels) {
  1350. client = CloneConnection();
  1351. client.Connect();
  1352. client.SetWorkingDirectory(GetWorkingDirectory());
  1353. }
  1354. else {
  1355. client = this;
  1356. }
  1357. client.SetDataType(type);
  1358. length = client.GetFileSize(path);
  1359. stream = client.OpenDataStream(string.Format("STOR {0}", path.GetFtpPath()), 0);
  1360. if (length > 0 && stream != null)
  1361. stream.SetLength(length);
  1362. }
  1363. return stream;
  1364. }
  1365. /// <summary>
  1366. /// Opens the specified file for writing
  1367. /// </summary>
  1368. /// <param name="path">Full or relative path of the file</param>
  1369. /// <param name="callback">Async callback</param>
  1370. /// <param name="state">State object</param>
  1371. /// <returns>IAsyncResult</returns>
  1372. /// <example><code source="..\Examples\BeginOpenWrite.cs" lang="cs" /></example>
  1373. public IAsyncResult BeginOpenWrite(string path, AsyncCallback callback, object state) {
  1374. return BeginOpenWrite(path, FtpDataType.Binary, callback, state);
  1375. }
  1376. delegate Stream AsyncOpenWrite(string path, FtpDataType type);
  1377. /// <summary>
  1378. /// Opens the specified file for writing
  1379. /// </summary>
  1380. /// <param name="path">Full or relative path of the file</param>
  1381. /// <param name="type">ASCII/Binary</param>
  1382. /// <param name="callback">Async callback</param>
  1383. /// <param name="state">State object</param>
  1384. /// <returns>IAsyncResult</returns>
  1385. /// <example><code source="..\Examples\BeginOpenWrite.cs" lang="cs" /></example>
  1386. public IAsyncResult BeginOpenWrite(string path, FtpDataType type, AsyncCallback callback, object state) {
  1387. AsyncOpenWrite func;
  1388. IAsyncResult ar;
  1389. ar = (func = new AsyncOpenWrite(OpenWrite)).BeginInvoke(path, type, callback, state);
  1390. lock (m_asyncmethods) {
  1391. m_asyncmethods.Add(ar, func);
  1392. }
  1393. return ar;
  1394. }
  1395. /// <summary>
  1396. /// Ends a call to BeginOpenWrite()
  1397. /// </summary>
  1398. /// <param name="ar">IAsyncResult returned from BeginOpenWrite()</param>
  1399. /// <returns>A writable stream</returns>
  1400. /// <example><code source="..\Examples\BeginOpenWrite.cs" lang="cs" /></example>
  1401. public Stream EndOpenWrite(IAsyncResult ar) {
  1402. return GetAsyncDelegate<AsyncOpenWrite>(ar).EndInvoke(ar);
  1403. }
  1404. /// <summary>
  1405. /// Opens the specified file to be appended to
  1406. /// </summary>
  1407. /// <param name="path">The full or relative path to the file to be opened</param>
  1408. /// <returns>A stream for writing to the file on the server</returns>
  1409. /// <example><code source="..\Examples\OpenAppend.cs" lang="cs" /></example>
  1410. public Stream OpenAppend(string path) {
  1411. return OpenAppend(path, FtpDataType.Binary);
  1412. }
  1413. /// <summary>
  1414. /// Opens the specified file to be appended to
  1415. /// </summary>
  1416. /// <param name="path">The full or relative path to the file to be opened</param>
  1417. /// <param name="type">ASCII/Binary</param>
  1418. /// <returns>A stream for writing to the file on the server</returns>
  1419. /// <example><code source="..\Examples\OpenAppend.cs" lang="cs" /></example>
  1420. public virtual Stream OpenAppend(string path, FtpDataType type) {
  1421. FtpClient client = null;
  1422. FtpDataStream stream = null;
  1423. long length = 0;
  1424. lock (m_lock)
  1425. {
  1426. if (m_threadSafeDataChannels) {
  1427. client = CloneConnection();
  1428. client.Connect();
  1429. client.SetWorkingDirectory(GetWorkingDirectory());
  1430. }
  1431. else {
  1432. client = this;
  1433. }
  1434. client.SetDataType(type);
  1435. length = client.GetFileSize(path);
  1436. stream = client.OpenDataStream(string.Format("APPE {0}", path.GetFtpPath()), 0);
  1437. if (length > 0 && stream != null) {
  1438. stream.SetLength(length);
  1439. stream.SetPosition(length);
  1440. }
  1441. }
  1442. return stream;
  1443. }
  1444. /// <summary>
  1445. /// Opens the specified file for writing
  1446. /// </summary>
  1447. /// <param name="path">Full or relative path of the file</param>
  1448. /// <param name="callback">Async callback</param>
  1449. /// <param name="state">State object</param>
  1450. /// <returns>IAsyncResult</returns>
  1451. /// <example><code source="..\Examples\BeginOpenAppend.cs" lang="cs" /></example>
  1452. public IAsyncResult BeginOpenAppend(string path, AsyncCallback callback, object state) {
  1453. return BeginOpenAppend(path, FtpDataType.Binary, callback, state);
  1454. }
  1455. delegate Stream AsyncOpenAppend(string path, FtpDataType type);
  1456. /// <summary>
  1457. /// Opens the specified file for writing
  1458. /// </summary>
  1459. /// <param name="path">Full or relative path of the file</param>
  1460. /// <param name="type">ASCII/Binary</param>
  1461. /// <param name="callback">Async callback</param>
  1462. /// <param name="state">State object</param>
  1463. /// <returns>IAsyncResult</returns>
  1464. /// <example><code source="..\Examples\BeginOpenAppend.cs" lang="cs" /></example>
  1465. public IAsyncResult BeginOpenAppend(string path, FtpDataType type, AsyncCallback callback, object state) {
  1466. IAsyncResult ar;
  1467. AsyncOpenAppend func;
  1468. ar = (func = new AsyncOpenAppend(OpenAppend)).BeginInvoke(path, type, callback, state);
  1469. lock (m_asyncmethods) {
  1470. m_asyncmethods.Add(ar, func);
  1471. }
  1472. return ar;
  1473. }
  1474. /// <summary>
  1475. /// Ends a call to BeginOpenAppend()
  1476. /// </summary>
  1477. /// <param name="ar">IAsyncResult returned from BeginOpenWrite()</param>
  1478. /// <returns>A writable stream</returns>
  1479. /// <example><code source="..\Examples\BeginOpenAppend.cs" lang="cs" /></example>
  1480. public Stream EndOpenAppend(IAsyncResult ar) {
  1481. return GetAsyncDelegate<AsyncOpenAppend>(ar).EndInvoke(ar);
  1482. }
  1483. /// <summary>
  1484. /// Recursively dereferences a symbolic link. See the
  1485. /// MaximumDereferenceCount property for controlling
  1486. /// how deep this method will recurse before giving up.
  1487. /// </summary>
  1488. /// <param name="item">The symbolic link</param>
  1489. /// <returns>FtpListItem, null if the link can't be dereferenced</returns>
  1490. /// <example><code source="..\Examples\DereferenceLink.cs" lang="cs" /></example>
  1491. public FtpListItem DereferenceLink(FtpListItem item) {
  1492. return DereferenceLink(item, MaximumDereferenceCount);
  1493. }
  1494. /// <summary>
  1495. /// Recursively dereferences a symbolic link
  1496. /// </summary>
  1497. /// <param name="item">The symbolic link</param>
  1498. /// <param name="recMax">The maximum depth of recursion that can be performed before giving up.</param>
  1499. /// <returns>FtpListItem, null if the link can't be dereferenced</returns>
  1500. /// <example><code source="..\Examples\DereferenceLink.cs" lang="cs" /></example>
  1501. public FtpListItem DereferenceLink(FtpListItem item, int recMax) {
  1502. int count = 0;
  1503. return DereferenceLink(item, recMax, ref count);
  1504. }
  1505. /// <summary>
  1506. /// Derefence a FtpListItem object
  1507. /// </summary>
  1508. /// <param name="item">The item to derefence</param>
  1509. /// <param name="recMax">Maximum recursive calls</param>
  1510. /// <param name="count">Counter</param>
  1511. /// <returns>FtpListItem, null if the link can't be dereferenced</returns>
  1512. /// <example><code source="..\Examples\DereferenceLink.cs" lang="cs" /></example>
  1513. FtpListItem DereferenceLink(FtpListItem item, int recMax, ref int count) {
  1514. if (item.Type != FtpFileSystemObjectType.Link)
  1515. throw new FtpException("You can only derefernce a symbolic link. Please verify the item type is Link.");
  1516. if (item.LinkTarget == null)
  1517. throw new FtpException("The link target was null. Please check this before trying to dereference the link.");
  1518. foreach (FtpListItem obj in GetListing(item.LinkTarget.GetFtpDirectoryName(), FtpListOption.ForceList)) {
  1519. if (item.LinkTarget == obj.FullName) {
  1520. if (obj.Type == FtpFileSystemObjectType.Link) {
  1521. if (++count == recMax)
  1522. return null;
  1523. return DereferenceLink(obj, recMax, ref count);
  1524. }
  1525. if (HasFeature(FtpCapability.MDTM)) {
  1526. DateTime modify = GetModifiedTime(obj.FullName);
  1527. if (modify != DateTime.MinValue)
  1528. obj.Modified = modify;
  1529. }
  1530. if (obj.Type == FtpFileSystemObjectType.File && obj.Size < 0 && HasFeature(FtpCapability.SIZE))
  1531. obj.Size = GetFileSize(obj.FullName);
  1532. return obj;
  1533. }
  1534. }
  1535. return null;
  1536. }
  1537. delegate FtpListItem AsyncDereferenceLink(FtpListItem item, int recMax);
  1538. /// <summary>
  1539. /// Derefence a FtpListItem object asynchronously
  1540. /// </summary>
  1541. /// <param name="item">The item to derefence</param>
  1542. /// <param name="recMax">Maximum recursive calls</param>
  1543. /// <param name="callback">AsyncCallback</param>
  1544. /// <param name="state">State Object</param>
  1545. /// <returns>IAsyncResult</returns>
  1546. /// <example><code source="..\Examples\BeginDereferenceLink.cs" lang="cs" /></example>
  1547. public IAsyncResult BeginDereferenceLink(FtpListItem item, int recMax, AsyncCallback callback, object state) {
  1548. IAsyncResult ar;
  1549. AsyncDereferenceLink func;
  1550. ar = (func = new AsyncDereferenceLink(DereferenceLink)).BeginInvoke(item, recMax, callback, state);
  1551. lock (m_asyncmethods) {
  1552. m_asyncmethods.Add(ar, func);
  1553. }
  1554. return ar;
  1555. }
  1556. /// <summary>
  1557. /// Derefence a FtpListItem object asynchronously. See the
  1558. /// MaximumDereferenceCount property for controlling
  1559. /// how deep this method will recurse before giving up.
  1560. /// </summary>
  1561. /// <param name="item">The item to derefence</param>
  1562. /// <param name="callback">AsyncCallback</param>
  1563. /// <param name="state">State Object</param>
  1564. /// <returns>IAsyncResult</returns>
  1565. /// <example><code source="..\Examples\BeginDereferenceLink.cs" lang="cs" /></example>
  1566. public IAsyncResult BeginDereferenceLink(FtpListItem item, AsyncCallback callback, object state) {
  1567. return BeginDereferenceLink(item, MaximumDereferenceCount, callback, state);
  1568. }
  1569. /// <summary>
  1570. /// Ends a call to BeginDereferenceLink
  1571. /// </summary>
  1572. /// <param name="ar">IAsyncResult</param>
  1573. /// <returns>FtpListItem, null if the link can't be dereferenced</returns>
  1574. /// <example><code source="..\Examples\BeginDereferenceLink.cs" lang="cs" /></example>
  1575. public FtpListItem EndDereferenceLink(IAsyncResult ar) {
  1576. return GetAsyncDelegate<AsyncDereferenceLink>(ar).EndInvoke(ar);
  1577. }
  1578. /// <summary>
  1579. /// Returns information about a file system object. You should check the Capabilities
  1580. /// flags for the FtpCapability.MLSD flag before calling this method. Failing to do
  1581. /// so will result in an InvalidOperationException being thrown when the server
  1582. /// does not support machine listings. Returns null if the server response can't
  1583. /// be parsed or the server returns a failure completion code. The error for a failure
  1584. /// is logged with FtpTrace. No exception is thrown on error because that would negate
  1585. /// the usefullness of this method for checking for the existence of an object.
  1586. /// </summary>
  1587. /// <param name="path">The path of the object to retrieve information about</param>
  1588. /// <returns>A FtpListItem object</returns>
  1589. public FtpListItem GetObjectInfo(string path) {
  1590. FtpReply reply;
  1591. string[] res;
  1592. if ((Capabilities & FtpCapability.MLSD) != FtpCapability.MLSD) {
  1593. throw new InvalidOperationException("The GetObjectInfo method only works on servers that support machine listings. " +
  1594. "Please check the Capabilities flags for FtpCapability.MLSD before calling this method.");
  1595. }
  1596. if ((reply = Execute("MLST {0}", path)).Success) {
  1597. res = reply.InfoMessages.Split('\n');
  1598. if (res.Length > 1) {
  1599. string info = "";
  1600. for (int i = 1; i < res.Length; i++) {
  1601. info += res[i];
  1602. }
  1603. return FtpListItem.Parse(null, info, m_caps);
  1604. }
  1605. }
  1606. else {
  1607. FtpTrace.WriteLine("Failed to get object info for path {0} with error {1}", path, reply.ErrorMessage);
  1608. }
  1609. return null;
  1610. }
  1611. delegate FtpListItem AsyncGetObjectInfo(string path);
  1612. /// <summary>
  1613. /// Returns information about a file system object. You should check the Capabilities
  1614. /// flags for the FtpCapability.MLSD flag before calling this method. Failing to do
  1615. /// so will result in an InvalidOperationException being thrown when the server
  1616. /// does not support machine listings. Returns null if the server response can't
  1617. /// be parsed or the server returns a failure completion code. The error for a failure
  1618. /// is logged with FtpTrace. No exception is thrown on error because that would negate
  1619. /// the usefullness of this method for checking for the existence of an object.
  1620. /// </summary>
  1621. /// <param name="path">Path of the item to retrieve information about</param>
  1622. /// <param name="callback">Async Callback</param>
  1623. /// <param name="state">State object</param>
  1624. /// <returns>IAsyncResult</returns>
  1625. public IAsyncResult BeginGetObjectInfo(string path, AsyncCallback callback, object state) {
  1626. IAsyncResult ar;
  1627. AsyncGetObjectInfo func;
  1628. ar = (func = new AsyncGetObjectInfo(GetObjectInfo)).BeginInvoke(path, callback, state);
  1629. lock (m_asyncmethods) {
  1630. m_asyncmethods.Add(ar, func);
  1631. }
  1632. return ar;
  1633. }
  1634. /// <summary>
  1635. /// Ends a call to BeginGetObjectInfo
  1636. /// </summary>
  1637. /// <param name="ar">IAsyncResult returned from BeginGetObjectInfo</param>
  1638. /// <returns>FtpListItem if the command succeeded, null if there was a problem.</returns>
  1639. public FtpListItem EndGetObjectInfo(IAsyncResult ar) {
  1640. return GetAsyncDelegate<AsyncGetObjectInfo>(ar).EndInvoke(ar);
  1641. }
  1642. /// <summary>
  1643. /// Gets a file listing from the server. Each FtpListItem object returned
  1644. /// contains information about the file that was able to be retrieved. If
  1645. /// a DateTime property is equal to DateTime.MinValue then it means the
  1646. /// date in question was not able to be retrieved. If the Size property
  1647. /// is equal to 0 then it means the size of the object could also not
  1648. /// be retrieved.
  1649. /// </summary>
  1650. /// <returns>An array of FtpListItem objects</returns>
  1651. /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
  1652. public FtpListItem[] GetListing() {
  1653. return GetListing(null);
  1654. }
  1655. /// <summary>
  1656. /// Gets a file listing from the server. Each FtpListItem object returned
  1657. /// contains information about the file that was able to be retrieved. If
  1658. /// a DateTime property is equal to DateTime.MinValue then it means the
  1659. /// date in question was not able to be retrieved. If the Size property
  1660. /// is equal to 0 then it means the size of the object could also not
  1661. /// be retrieved.
  1662. /// </summary>
  1663. /// <param name="path">The path of the directory to list</param>
  1664. /// <returns>An array of FtpListItem objects</returns>
  1665. /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
  1666. public FtpListItem[] GetListing(string path) {
  1667. return GetListing(path, 0);
  1668. }
  1669. /// <summary>
  1670. /// Gets a file listing from the server. Each FtpListItem object returned
  1671. /// contains information about the file that was able to be retrieved. If
  1672. /// a DateTime property is equal to DateTime.MinValue then it means the
  1673. /// date in question was not able to be retrieved. If the Size property
  1674. /// is equal to 0 then it means the size of the object could also not
  1675. /// be retrieved.
  1676. /// </summary>
  1677. /// <param name="path">The path of the directory to list</param>
  1678. /// <param name="options">Options that dictacte how a list is performed and what information is gathered.</param>
  1679. /// <returns>An array of FtpListItem objects</returns>
  1680. /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
  1681. public FtpListItem[] GetListing(string path, FtpListOption options) {
  1682. FtpListItem item = null;
  1683. List<FtpListItem> lst = new List<FtpListItem>();
  1684. List<string> rawlisting = new List<string>();
  1685. string listcmd = null;
  1686. string pwd = GetWorkingDirectory();
  1687. string buf = null;
  1688. if (path == null || path.Trim().Length == 0) {
  1689. pwd = GetWorkingDirectory();
  1690. if (pwd != null && pwd.Trim().Length > 0)
  1691. path = pwd;
  1692. else
  1693. path = "./";
  1694. }
  1695. else if (!path.StartsWith("/") && pwd != null && pwd.Trim().Length > 0) {
  1696. if (path.StartsWith("./"))
  1697. path = path.Remove(0, 2);
  1698. path = string.Format("{0}/{1}", pwd, path).GetFtpPath();
  1699. }
  1700. // MLSD provides a machine parsable format with more
  1701. // accurate information than most of the UNIX long list
  1702. // formats which translates to more effcient file listings
  1703. // so always prefer MLSD over LIST unless the caller of this
  1704. // method overrides it with the ForceList option
  1705. if ((options & FtpListOption.ForceList) != FtpListOption.ForceList && HasFeature(FtpCapability.MLSD)) {
  1706. listcmd = "MLSD";
  1707. }
  1708. else {
  1709. if ((options & FtpListOption.UseLS) == FtpListOption.UseLS) {
  1710. listcmd = "LS";
  1711. }
  1712. else if ((options & FtpListOption.NameList) == FtpListOption.NameList) {
  1713. listcmd = "NLST";
  1714. }
  1715. else {
  1716. string listopts = "";
  1717. listcmd = "LIST";
  1718. if ((options & FtpListOption.AllFiles) == FtpListOption.AllFiles)
  1719. listopts += "a";
  1720. if ((options & FtpListOption.Recursive) == FtpListOption.Recursive)
  1721. listopts += "R";
  1722. if (listopts.Length > 0)
  1723. listcmd += " -" + listopts;
  1724. }
  1725. }
  1726. if((options & FtpListOption.NoPath) != FtpListOption.NoPath) {
  1727. listcmd = string.Format("{0} {1}", listcmd, path.GetFtpPath());
  1728. }
  1729. lock (m_lock)
  1730. {
  1731. Execute("TYPE I");
  1732. // read in raw file listing
  1733. using (FtpDataStream stream = OpenDataStream(listcmd, 0)) {
  1734. try {
  1735. while ((buf = stream.ReadLine(Encoding)) != null) {
  1736. if (buf.Length > 0) {
  1737. rawlisting.Add(buf);
  1738. //列出文件明细信息
  1739. //FtpTrace.WriteLine(buf);
  1740. }
  1741. }
  1742. }
  1743. finally {
  1744. stream.Close();
  1745. }
  1746. }
  1747. }
  1748. for (int i = 0; i < rawlisting.Count; i++) {
  1749. buf = rawlisting[i];
  1750. if ((options & FtpListOption.NameList) == FtpListOption.NameList) {
  1751. // if NLST was used we only have a file name so
  1752. // there is nothing to parse.
  1753. item = new FtpListItem() {
  1754. FullName = buf
  1755. };
  1756. if (DirectoryExists(item.FullName))
  1757. item.Type = FtpFileSystemObjectType.Directory;
  1758. else
  1759. item.Type = FtpFileSystemObjectType.File;
  1760. lst.Add(item);
  1761. }
  1762. else {
  1763. // if this is a result of LIST -R then the path will be spit out
  1764. // before each block of objects
  1765. if (listcmd.StartsWith("LIST") && (options & FtpListOption.Recursive) == FtpListOption.Recursive) {
  1766. if (buf.StartsWith("/") && buf.EndsWith(":")) {
  1767. path = buf.TrimEnd(':');
  1768. continue;
  1769. }
  1770. }
  1771. // if the next line in the listing starts with spaces
  1772. // it is assumed to be a continuation of the current line
  1773. if (i + 1 < rawlisting.Count && (rawlisting[i + 1].StartsWith("\t") || rawlisting[i + 1].StartsWith(" ")))
  1774. buf += rawlisting[++i];
  1775. item = FtpListItem.Parse(path, buf, m_caps);
  1776. // FtpListItem.Parse() returns null if the line
  1777. // could not be parsed
  1778. if (item != null && (item.Name != "." && item.Name != ".."))
  1779. lst.Add(item);
  1780. else
  1781. FtpTrace.WriteLine("Failed to parse file listing: " + buf);
  1782. }
  1783. // load extended information that wasn't available if the list options flags say to do so.
  1784. if (item != null) {
  1785. // try to dereference symbolic links if the appropriate list
  1786. // option was passed
  1787. if (item.Type == FtpFileSystemObjectType.Link && (options & FtpListOption.DerefLinks) == FtpListOption.DerefLinks) {
  1788. item.LinkObject = DereferenceLink(item);
  1789. }
  1790. if ((options & FtpListOption.Modify) == FtpListOption.Modify && HasFeature(FtpCapability.MDTM)) {
  1791. // if the modified date was not loaded or the modified date is more than a day in the future
  1792. // and the server supports the MDTM command, load the modified date.
  1793. // most servers do not support retrieving the modified date
  1794. // of a directory but we try any way.
  1795. if (item.Modified == DateTime.MinValue || listcmd.StartsWith("LIST")) {
  1796. DateTime modify;
  1797. if (item.Type == FtpFileSystemObjectType.Directory)
  1798. FtpTrace.WriteLine("Trying to retrieve modification time of a directory, some servers don't like this...");
  1799. if ((modify = GetModifiedTime(item.FullName)) != DateTime.MinValue)
  1800. item.Modified = modify;
  1801. }
  1802. }
  1803. if ((options & FtpListOption.Size) == FtpListOption.Size && HasFeature(FtpCapability.SIZE)) {
  1804. // if no size was parsed, the object is a file and the server
  1805. // supports the SIZE command, then load the file size
  1806. if (item.Size == -1) {
  1807. if (item.Type != FtpFileSystemObjectType.Directory) {
  1808. item.Size = GetFileSize(item.FullName);
  1809. }
  1810. else {
  1811. item.Size = 0;
  1812. }
  1813. }
  1814. }
  1815. }
  1816. }
  1817. return lst.ToArray();
  1818. }
  1819. /// <summary>
  1820. /// Gets a file listing from the server asynchronously
  1821. /// </summary>
  1822. /// <param name="callback">AsyncCallback method</param>
  1823. /// <param name="state">State object</param>
  1824. /// <returns>IAsyncResult</returns>
  1825. /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example>
  1826. public IAsyncResult BeginGetListing(AsyncCallback callback, Object state) {
  1827. return BeginGetListing(null, callback, state);
  1828. }
  1829. /// <summary>
  1830. /// Gets a file listing from the server asynchronously
  1831. /// </summary>
  1832. /// <param name="path">The path to list</param>
  1833. /// <param name="callback">AsyncCallback method</param>
  1834. /// <param name="state">State object</param>
  1835. /// <returns>IAsyncResult</returns>
  1836. /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example>
  1837. public IAsyncResult BeginGetListing(string path, AsyncCallback callback, Object state) {
  1838. return BeginGetListing(path, FtpListOption.Modify | FtpListOption.Size, callback, state);
  1839. }
  1840. delegate FtpListItem[] AsyncGetListing(string path, FtpListOption options);
  1841. /// <summary>
  1842. /// Gets a file listing from the server asynchronously
  1843. /// </summary>
  1844. /// <param name="path">The path to list</param>
  1845. /// <param name="options">Options that dictate how the list operation is performed</param>
  1846. /// <param name="callback">AsyncCallback method</param>
  1847. /// <param name="state">State object</param>
  1848. /// <returns>IAsyncResult</returns>
  1849. /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example>
  1850. public IAsyncResult BeginGetListing(string path, FtpListOption options, AsyncCallback callback, Object state) {
  1851. IAsyncResult ar;
  1852. AsyncGetListing func;
  1853. ar = (func = new AsyncGetListing(GetListing)).BeginInvoke(path, options, callback, state);
  1854. lock (m_asyncmethods) {
  1855. m_asyncmethods.Add(ar, func);
  1856. }
  1857. return ar;
  1858. }
  1859. /// <summary>
  1860. /// Ends an asynchronous file listing
  1861. /// </summary>
  1862. /// <param name="ar">IAsyncResult return from BeginGetListing()</param>
  1863. /// <returns>An array of items retrieved in the listing</returns>
  1864. /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example>
  1865. public FtpListItem[] EndGetListing(IAsyncResult ar) {
  1866. return GetAsyncDelegate<AsyncGetListing>(ar).EndInvoke(ar);
  1867. }
  1868. /// <summary>
  1869. /// Returns a file/directory listing using the NLST command.
  1870. /// </summary>
  1871. /// <returns>A string array of file and directory names if any were returned.</returns>
  1872. public string[] GetNameListing() {
  1873. return GetNameListing(null);
  1874. }
  1875. /// <summary>
  1876. /// Returns a file/directory listing using the NLST command.
  1877. /// </summary>
  1878. /// <param name="path">The path of the directory to list</param>
  1879. /// <returns>A string array of file and directory names if any were returned.</returns>
  1880. /// <example><code source="..\Examples\GetNameListing.cs" lang="cs" /></example>
  1881. public string[] GetNameListing(string path) {
  1882. List<string> lst = new List<string>();
  1883. string pwd = GetWorkingDirectory();
  1884. /*if (path == null || path.GetFtpPath().Trim().Length == 0 || path.StartsWith(".")) {
  1885. if (pwd == null || pwd.Length == 0) // couldn't get the working directory
  1886. path = "./";
  1887. else if (path.StartsWith("./"))
  1888. path = string.Format("{0}/{1}", pwd, path.Remove(0, 2));
  1889. else
  1890. path = pwd;
  1891. }*/
  1892. path = path.GetFtpPath();
  1893. if (path == null || path.Trim().Length == 0) {
  1894. if (pwd != null && pwd.Trim().Length > 0)
  1895. path = pwd;
  1896. else
  1897. path = "./";
  1898. }
  1899. else if (!path.StartsWith("/") && pwd != null && pwd.Trim().Length > 0) {
  1900. if (path.StartsWith("./"))
  1901. path = path.Remove(0, 2);
  1902. path = string.Format("{0}/{1}", pwd, path).GetFtpPath();
  1903. }
  1904. lock (m_lock)
  1905. {
  1906. // always get the file listing in binary
  1907. // to avoid any potential character translation
  1908. // problems that would happen if in ASCII.
  1909. Execute("TYPE I");
  1910. using (FtpDataStream stream = OpenDataStream(string.Format("NLST {0}", path.GetFtpPath()), 0)) {
  1911. string buf;
  1912. try {
  1913. while ((buf = stream.ReadLine(Encoding)) != null)
  1914. lst.Add(buf);
  1915. }
  1916. finally {
  1917. stream.Close();
  1918. }
  1919. }
  1920. }
  1921. return lst.ToArray();
  1922. }
  1923. delegate string[] AsyncGetNameListing(string path);
  1924. /// <summary>
  1925. /// Asynchronously gets a list of file and directory names for the specified path.
  1926. /// </summary>
  1927. /// <param name="path">The path of the directory to list</param>
  1928. /// <param name="callback">Async Callback</param>
  1929. /// <param name="state">State object</param>
  1930. /// <returns>IAsyncResult</returns>
  1931. /// <example><code source="..\Examples\BeginGetNameListing.cs" lang="cs" /></example>
  1932. public IAsyncResult BeginGetNameListing(string path, AsyncCallback callback, object state) {
  1933. IAsyncResult ar;
  1934. AsyncGetNameListing func;
  1935. ar = (func = new AsyncGetNameListing(GetNameListing)).BeginInvoke(path, callback, state);
  1936. lock (m_asyncmethods) {
  1937. m_asyncmethods.Add(ar, func);
  1938. }
  1939. return ar;
  1940. }
  1941. /// <summary>
  1942. /// Asynchronously gets a list of file and directory names for the specified path.
  1943. /// </summary>
  1944. /// <param name="callback">Async Callback</param>
  1945. /// <param name="state">State object</param>
  1946. /// <returns>IAsyncResult</returns>
  1947. /// <example><code source="..\Examples\BeginGetNameListing.cs" lang="cs" /></example>
  1948. public IAsyncResult BeginGetNameListing(AsyncCallback callback, object state) {
  1949. return BeginGetNameListing(null, callback, state);
  1950. }
  1951. /// <summary>
  1952. /// Ends a call to BeginGetNameListing()
  1953. /// </summary>
  1954. /// <param name="ar">IAsyncResult object returned from BeginGetNameListing</param>
  1955. /// <returns>An array of file and directory names if any were returned.</returns>
  1956. /// <example><code source="..\Examples\BeginGetNameListing.cs" lang="cs" /></example>
  1957. public string[] EndGetNameListing(IAsyncResult ar) {
  1958. return GetAsyncDelegate<AsyncGetNameListing>(ar).EndInvoke(ar);
  1959. }
  1960. /// <summary>
  1961. /// Sets the data type of information sent over the data stream
  1962. /// </summary>
  1963. /// <param name="type">ASCII/Binary</param>
  1964. protected void SetDataType(FtpDataType type) {
  1965. FtpReply reply;
  1966. lock (m_lock)
  1967. {
  1968. switch (type) {
  1969. case FtpDataType.ASCII:
  1970. if (!(reply = Execute("TYPE A")).Success)
  1971. throw new FtpCommandException(reply);
  1972. /*if (!(reply = Execute("STRU R")).Success)
  1973. FtpTrace.WriteLine(reply.Message);*/
  1974. break;
  1975. case FtpDataType.Binary:
  1976. if (!(reply = Execute("TYPE I")).Success)
  1977. throw new FtpCommandException(reply);
  1978. /*if (!(reply = Execute("STRU F")).Success)
  1979. FtpTrace.WriteLine(reply.Message);*/
  1980. break;
  1981. default:
  1982. throw new FtpException("Unsupported data type: " + type.ToString());
  1983. }
  1984. }
  1985. }
  1986. delegate void AsyncSetDataType(FtpDataType type);
  1987. /// <summary>
  1988. /// Asynchronously sets the data type on the server
  1989. /// </summary>
  1990. /// <param name="type">ASCII/Binary</param>
  1991. /// <param name="callback">Async callback</param>
  1992. /// <param name="state">State object</param>
  1993. /// <returns>IAsyncResult</returns>
  1994. protected IAsyncResult BeginSetDataType(FtpDataType type, AsyncCallback callback, object state) {
  1995. IAsyncResult ar;
  1996. AsyncSetDataType func;
  1997. ar = (func = new AsyncSetDataType(SetDataType)).BeginInvoke(type, callback, state);
  1998. lock (m_asyncmethods) {
  1999. m_asyncmethods.Add(ar, func);
  2000. }
  2001. return ar;
  2002. }
  2003. /// <summary>
  2004. /// Ends a call to BeginSetDataType()
  2005. /// </summary>
  2006. /// <param name="ar">IAsyncResult returned from BeginSetDataType()</param>
  2007. protected void EndSetDataType(IAsyncResult ar) {
  2008. GetAsyncDelegate<AsyncSetDataType>(ar).EndInvoke(ar);
  2009. }
  2010. /// <summary>
  2011. /// Sets the work directory on the server
  2012. /// </summary>
  2013. /// <param name="path">The path of the directory to change to</param>
  2014. /// <example><code source="..\Examples\SetWorkingDirectory.cs" lang="cs" /></example>
  2015. public void SetWorkingDirectory(string path) {
  2016. FtpReply reply;
  2017. string ftppath = path.GetFtpPath();
  2018. if (ftppath == "." || ftppath == "./")
  2019. return;
  2020. lock (m_lock)
  2021. {
  2022. if (!(reply = Execute("CWD {0}", ftppath)).Success)
  2023. throw new FtpCommandException(reply);
  2024. }
  2025. }
  2026. delegate void AsyncSetWorkingDirectory(string path);
  2027. /// <summary>
  2028. /// Asynchronously changes the working directory on the server
  2029. /// </summary>
  2030. /// <param name="path">The directory to change to</param>
  2031. /// <param name="callback">Async Callback</param>
  2032. /// <param name="state">State object</param>
  2033. /// <returns>IAsyncResult</returns>
  2034. /// <example><code source="..\Examples\BeginSetWorkingDirectory.cs" lang="cs" /></example>
  2035. public IAsyncResult BeginSetWorkingDirectory(string path, AsyncCallback callback, object state) {
  2036. IAsyncResult ar;
  2037. AsyncSetWorkingDirectory func;
  2038. ar = (func = new AsyncSetWorkingDirectory(SetWorkingDirectory)).BeginInvoke(path, callback, state);
  2039. lock (m_asyncmethods) {
  2040. m_asyncmethods.Add(ar, func);
  2041. }
  2042. return ar;
  2043. }
  2044. /// <summary>
  2045. /// Ends asynchronous directory change
  2046. /// </summary>
  2047. /// <param name="ar">IAsyncResult returned from BeginSetWorkingDirectory</param>
  2048. /// <example><code source="..\Examples\BeginSetWorkingDirectory.cs" lang="cs" /></example>
  2049. public void EndSetWorkingDirectory(IAsyncResult ar) {
  2050. GetAsyncDelegate<AsyncSetWorkingDirectory>(ar).EndInvoke(ar);
  2051. }
  2052. /// <summary>
  2053. /// 获取当前工作目录
  2054. /// </summary>
  2055. /// <returns>The current working directory, ./ if the response couldn't be parsed.</returns>
  2056. /// <example><code source="..\Examples\GetWorkingDirectory.cs" lang="cs" /></example>
  2057. public string GetWorkingDirectory() {
  2058. FtpReply reply;
  2059. Match m;
  2060. lock (m_lock)
  2061. {
  2062. if (!(reply = Execute("PWD")).Success)
  2063. throw new FtpCommandException(reply);
  2064. }
  2065. if ((m = Regex.Match(reply.Message, "\"(?<pwd>.*)\"")).Success) {
  2066. return m.Groups["pwd"].Value;
  2067. }
  2068. // check for MODCOMP ftp path mentioned in forums: https://netftp.codeplex.com/discussions/444461
  2069. if ((m = Regex.Match(reply.Message, "PWD = (?<pwd>.*)")).Success) {
  2070. return m.Groups["pwd"].Value;
  2071. }
  2072. FtpTrace.WriteLine("无法解析工作目录: " + reply.Message);
  2073. return "./";
  2074. }
  2075. delegate string AsyncGetWorkingDirectory();
  2076. /// <summary>
  2077. /// Asynchronously retrieves the working directory
  2078. /// </summary>
  2079. /// <param name="callback">Async callback</param>
  2080. /// <param name="state">State object</param>
  2081. /// <returns>IAsyncResult</returns>
  2082. /// <example><code source="..\Examples\BeginGetWorkingDirectory.cs" lang="cs" /></example>
  2083. public IAsyncResult BeginGetWorkingDirectory(AsyncCallback callback, object state) {
  2084. IAsyncResult ar;
  2085. AsyncGetWorkingDirectory func;
  2086. ar = (func = new AsyncGetWorkingDirectory(GetWorkingDirectory)).BeginInvoke(callback, state);
  2087. lock (m_asyncmethods) {
  2088. m_asyncmethods.Add(ar, func);
  2089. }
  2090. return ar;
  2091. }
  2092. /// <summary>
  2093. /// Ends an asynchronous call to retrieve the working directory
  2094. /// </summary>
  2095. /// <param name="ar">IAsyncResult returned from BeginGetWorkingDirectory</param>
  2096. /// <returns>The current working directory</returns>
  2097. /// <example><code source="..\Examples\BeginGetWorkingDirectory.cs" lang="cs" /></example>
  2098. public string EndGetWorkingDirectory(IAsyncResult ar) {
  2099. return GetAsyncDelegate<AsyncGetWorkingDirectory>(ar).EndInvoke(ar);
  2100. }
  2101. /// <summary>
  2102. /// Gets the size of the file
  2103. /// </summary>
  2104. /// <param name="path">The full or relative path of the file</param>
  2105. /// <returns>-1 if the command fails, otherwise the file size</returns>
  2106. /// <example><code source="..\Examples\GetFileSize.cs" lang="cs" /></example>
  2107. public virtual long GetFileSize(string path) {
  2108. FtpReply reply;
  2109. long length = 0;
  2110. lock (m_lock)
  2111. {
  2112. if (!(reply = Execute("SIZE {0}", path.GetFtpPath())).Success)
  2113. return -1;
  2114. if (!long.TryParse(reply.Message, out length))
  2115. return -1;
  2116. }
  2117. return length;
  2118. }
  2119. delegate long AsyncGetFileSize(string path);
  2120. /// <summary>
  2121. /// Asynchronously retrieve the size of the specified file
  2122. /// </summary>
  2123. /// <param name="path">The full or relative path of the file</param>
  2124. /// <param name="callback">Async callback</param>
  2125. /// <param name="state">State object</param>
  2126. /// <returns>IAsyncResult</returns>
  2127. /// <example><code source="..\Examples\BeginGetFileSize.cs" lang="cs" /></example>
  2128. public IAsyncResult BeginGetFileSize(string path, AsyncCallback callback, object state) {
  2129. IAsyncResult ar;
  2130. AsyncGetFileSize func;
  2131. ar = (func = new AsyncGetFileSize(GetFileSize)).BeginInvoke(path, callback, state);
  2132. lock (m_asyncmethods) {
  2133. m_asyncmethods.Add(ar, func);
  2134. }
  2135. return ar;
  2136. }
  2137. /// <summary>
  2138. /// Ends a call to BeginGetFileSize()
  2139. /// </summary>
  2140. /// <param name="ar">IAsyncResult returned from BeginGetFileSize</param>
  2141. /// <returns>The size of the file, -1 if there was a problem.</returns>
  2142. /// <example><code source="..\Examples\BeginGetFileSize.cs" lang="cs" /></example>
  2143. public long EndGetFileSize(IAsyncResult ar) {
  2144. return GetAsyncDelegate<AsyncGetFileSize>(ar).EndInvoke(ar);
  2145. }
  2146. /// <summary>
  2147. /// Gets the modified time of the file
  2148. /// </summary>
  2149. /// <param name="path">The full path to the file</param>
  2150. /// <returns>The modified time, DateTime.MinValue if there was a problem</returns>
  2151. /// <example><code source="..\Examples\GetModifiedTime.cs" lang="cs" /></example>
  2152. public virtual DateTime GetModifiedTime(string path) {
  2153. DateTime modify = DateTime.MinValue;
  2154. FtpReply reply;
  2155. lock (m_lock)
  2156. {
  2157. if ((reply = Execute("MDTM {0}", path.GetFtpPath())).Success)
  2158. modify = reply.Message.GetFtpDate(DateTimeStyles.AssumeUniversal);
  2159. }
  2160. return modify;
  2161. }
  2162. delegate DateTime AsyncGetModifiedTime(string path);
  2163. /// <summary>
  2164. /// Gets the modified time of the file
  2165. /// </summary>
  2166. /// <param name="path">The full path to the file</param>
  2167. /// <param name="callback">Async callback</param>
  2168. /// <param name="state">State object</param>
  2169. /// <returns>IAsyncResult</returns>
  2170. /// <example><code source="..\Examples\BeginGetModifiedTime.cs" lang="cs" /></example>
  2171. public IAsyncResult BeginGetModifiedTime(string path, AsyncCallback callback, object state) {
  2172. IAsyncResult ar;
  2173. AsyncGetModifiedTime func;
  2174. ar = (func = new AsyncGetModifiedTime(GetModifiedTime)).BeginInvoke(path, callback, state);
  2175. lock (m_asyncmethods) {
  2176. m_asyncmethods.Add(ar, func);
  2177. }
  2178. return ar;
  2179. }
  2180. /// <summary>
  2181. /// Ends a call to BeginGetModifiedTime()
  2182. /// </summary>
  2183. /// <param name="ar">IAsyncResult returned from BeginGetModifiedTime()</param>
  2184. /// <returns>The modified time, DateTime.MinValue if there was a problem</returns>
  2185. /// <example><code source="..\Examples\BeginGetModifiedTime.cs" lang="cs" /></example>
  2186. public DateTime EndGetModifiedTime(IAsyncResult ar) {
  2187. return GetAsyncDelegate<AsyncGetModifiedTime>(ar).EndInvoke(ar);
  2188. }
  2189. /// <summary>
  2190. /// Deletes a file on the server
  2191. /// </summary>
  2192. /// <param name="path">The full or relative path to the file</param>
  2193. /// <example><code source="..\Examples\DeleteFile.cs" lang="cs" /></example>
  2194. public void DeleteFile(string path) {
  2195. FtpReply reply;
  2196. lock (m_lock)
  2197. {
  2198. if (!(reply = Execute("DELE {0}", path.GetFtpPath())).Success)
  2199. throw new FtpCommandException(reply);
  2200. }
  2201. }
  2202. delegate void AsyncDeleteFile(string path);
  2203. /// <summary>
  2204. /// Asynchronously deletes a file from the server
  2205. /// </summary>
  2206. /// <param name="path">The full or relative path to the file</param>
  2207. /// <param name="callback">Async callback</param>
  2208. /// <param name="state">State object</param>
  2209. /// <returns>IAsyncResult</returns>
  2210. /// <example><code source="..\Examples\BeginDeleteFile.cs" lang="cs" /></example>
  2211. public IAsyncResult BeginDeleteFile(string path, AsyncCallback callback, object state) {
  2212. IAsyncResult ar;
  2213. AsyncDeleteFile func;
  2214. ar = (func = new AsyncDeleteFile(DeleteFile)).BeginInvoke(path, callback, state);
  2215. lock (m_asyncmethods) {
  2216. m_asyncmethods.Add(ar, func);
  2217. }
  2218. return ar;
  2219. }
  2220. /// <summary>
  2221. /// Ends a call to BeginDeleteFile
  2222. /// </summary>
  2223. /// <param name="ar">IAsyncResult returned from BeginDeleteFile</param>
  2224. /// <example><code source="..\Examples\BeginDeleteFile.cs" lang="cs" /></example>
  2225. public void EndDeleteFile(IAsyncResult ar) {
  2226. GetAsyncDelegate<AsyncDeleteFile>(ar).EndInvoke(ar);
  2227. }
  2228. /// <summary>
  2229. /// Deletes the specified directory on the server.
  2230. /// </summary>
  2231. /// <param name="path">The full or relative path of the directory to delete</param>
  2232. /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example>
  2233. public void DeleteDirectory(string path) {
  2234. DeleteDirectory(path, false);
  2235. }
  2236. /// <summary>
  2237. /// Delets the specified directory on the server
  2238. /// </summary>
  2239. /// <param name="path">The full or relative path of the directory to delete</param>
  2240. /// <param name="force">If the directory is not empty, remove its contents</param>
  2241. /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example>
  2242. public void DeleteDirectory(string path, bool force) {
  2243. DeleteDirectory(path, force, 0);
  2244. }
  2245. /// <summary>
  2246. /// Deletes the specified directory on the server
  2247. /// </summary>
  2248. /// <param name="path">The full or relative path of the directory to delete</param>
  2249. /// <param name="force">If the directory is not empty, remove its contents</param>
  2250. /// <param name="options">FtpListOptions for controlling how the directory
  2251. /// contents are retrieved with the force option is true. If you experience problems
  2252. /// the file listing can be fine tuned through this parameter.</param>
  2253. /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example>
  2254. public void DeleteDirectory(string path, bool force, FtpListOption options) {
  2255. FtpReply reply;
  2256. string ftppath = path.GetFtpPath();
  2257. lock (m_lock)
  2258. {
  2259. if (force) {
  2260. foreach (FtpListItem item in GetListing(path, options)) {
  2261. switch (item.Type) {
  2262. case FtpFileSystemObjectType.File:
  2263. DeleteFile(item.FullName);
  2264. break;
  2265. case FtpFileSystemObjectType.Directory:
  2266. DeleteDirectory(item.FullName, true, options);
  2267. break;
  2268. default:
  2269. throw new FtpException("Don't know how to delete object type: " + item.Type);
  2270. }
  2271. }
  2272. }
  2273. // can't delete the working directory and
  2274. // can't delete the server root.
  2275. if (ftppath == "." || ftppath == "./" || ftppath == "/")
  2276. return;
  2277. if (!(reply = Execute("RMD {0}", ftppath)).Success)
  2278. throw new FtpCommandException(reply);
  2279. }
  2280. }
  2281. delegate void AsyncDeleteDirectory(string path, bool force, FtpListOption options);
  2282. /// <summary>
  2283. /// Asynchronously removes a directory from the server
  2284. /// </summary>
  2285. /// <param name="path">The full or relative path of the directory to delete</param>
  2286. /// <param name="callback">Async callback</param>
  2287. /// <param name="state">State object</param>
  2288. /// <returns>IAsyncResult</returns>
  2289. /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
  2290. public IAsyncResult BeginDeleteDirectory(string path, AsyncCallback callback, object state) {
  2291. return BeginDeleteDirectory(path, true, 0, callback, state);
  2292. }
  2293. /// <summary>
  2294. /// Asynchronously removes a directory from the server
  2295. /// </summary>
  2296. /// <param name="path">The full or relative path of the directory to delete</param>
  2297. /// <param name="force">If the directory is not empty, remove its contents</param>
  2298. /// <param name="callback">Async callback</param>
  2299. /// <param name="state">State object</param>
  2300. /// <returns>IAsyncResult</returns>
  2301. /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
  2302. public IAsyncResult BeginDeleteDirectory(string path, bool force, AsyncCallback callback, object state) {
  2303. return BeginDeleteDirectory(path, force, 0, callback, state);
  2304. }
  2305. /// <summary>
  2306. /// Asynchronously removes a directory from the server
  2307. /// </summary>
  2308. /// <param name="path">The full or relative path of the directory to delete</param>
  2309. /// <param name="force">If the directory is not empty, remove its contents</param>
  2310. /// <param name="options">FtpListOptions for controlling how the directory
  2311. /// contents are retrieved with the force option is true. If you experience problems
  2312. /// the file listing can be fine tuned through this parameter.</param>
  2313. /// <param name="callback">Async callback</param>
  2314. /// <param name="state">State object</param>
  2315. /// <returns>IAsyncResult</returns>
  2316. /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
  2317. public IAsyncResult BeginDeleteDirectory(string path, bool force, FtpListOption options, AsyncCallback callback, object state) {
  2318. AsyncDeleteDirectory func;
  2319. IAsyncResult ar;
  2320. ar = (func = new AsyncDeleteDirectory(DeleteDirectory)).BeginInvoke(path, force, options, callback, state);
  2321. lock (m_asyncmethods) {
  2322. m_asyncmethods.Add(ar, func);
  2323. }
  2324. return ar;
  2325. }
  2326. /// <summary>
  2327. /// Ends a call to BeginDeleteDirectory()
  2328. /// </summary>
  2329. /// <param name="ar">IAsyncResult returned from BeginDeleteDirectory</param>
  2330. /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
  2331. public void EndDeleteDirectory(IAsyncResult ar) {
  2332. GetAsyncDelegate<AsyncDeleteDirectory>(ar).EndInvoke(ar);
  2333. }
  2334. /// <summary>
  2335. /// Tests if the specified directory exists on the server. This
  2336. /// method works by trying to change the working directory to
  2337. /// the path specified. If it succeeds, the directory is changed
  2338. /// back to the old working directory and true is returned. False
  2339. /// is returned otherwise and since the CWD failed it is assumed
  2340. /// the working directory is still the same.
  2341. /// </summary>
  2342. /// <param name="path">The path of the directory</param>
  2343. /// <returns>True if it exists, false otherwise.</returns>
  2344. /// <example><code source="..\Examples\DirectoryExists.cs" lang="cs" /></example>
  2345. public bool DirectoryExists(string path) {
  2346. string pwd;
  2347. string ftppath = path.GetFtpPath();
  2348. if (ftppath == "." || ftppath == "./" || ftppath == "/")
  2349. return true;
  2350. lock (m_lock)
  2351. {
  2352. pwd = GetWorkingDirectory();
  2353. if (Execute("CWD {0}", ftppath).Success) {
  2354. FtpReply reply = Execute("CWD {0}", pwd.GetFtpPath());
  2355. if (!reply.Success)
  2356. throw new FtpException("DirectoryExists(): Failed to restore the working directory.");
  2357. return true;
  2358. }
  2359. }
  2360. return false;
  2361. }
  2362. delegate bool AsyncDirectoryExists(string path);
  2363. /// <summary>
  2364. /// Checks if a directory exists on the server asynchronously.
  2365. /// </summary>
  2366. /// <returns>IAsyncResult</returns>
  2367. /// <param name='path'>The full or relative path of the directory to check for</param>
  2368. /// <param name='callback'>Async callback</param>
  2369. /// <param name='state'>State object</param>
  2370. /// <example><code source="..\Examples\BeginDirectoryExists.cs" lang="cs" /></example>
  2371. public IAsyncResult BeginDirectoryExists(string path, AsyncCallback callback, object state) {
  2372. AsyncDirectoryExists func;
  2373. IAsyncResult ar;
  2374. ar = (func = new AsyncDirectoryExists(DirectoryExists)).BeginInvoke(path, callback, state);
  2375. lock (m_asyncmethods) {
  2376. m_asyncmethods.Add(ar, func);
  2377. }
  2378. return ar;
  2379. }
  2380. /// <summary>
  2381. /// Ends a call to BeginDirectoryExists
  2382. /// </summary>
  2383. /// <param name="ar">IAsyncResult returned from BeginDirectoryExists</param>
  2384. /// <returns>True if the directory exists. False otherwise.</returns>
  2385. /// <example><code source="..\Examples\BeginDirectoryExists.cs" lang="cs" /></example>
  2386. public bool EndDirectoryExists(IAsyncResult ar) {
  2387. return GetAsyncDelegate<AsyncDirectoryExists>(ar).EndInvoke(ar);
  2388. }
  2389. /// <summary>
  2390. /// Checks if a file exsts on the server by taking a
  2391. /// file listing of the parent directory in the path
  2392. /// and comparing the results the path supplied.
  2393. /// </summary>
  2394. /// <param name="path">The full or relative path to the file</param>
  2395. /// <returns>True if the file exists</returns>
  2396. /// <example><code source="..\Examples\FileExists.cs" lang="cs" /></example>
  2397. public bool FileExists(string path) {
  2398. return FileExists(path, 0);
  2399. }
  2400. /// <summary>
  2401. /// Checks if a file exsts on the server by taking a
  2402. /// file listing of the parent directory in the path
  2403. /// and comparing the results the path supplied.
  2404. /// </summary>
  2405. /// <param name="path">The full or relative path to the file</param>
  2406. /// <param name="options">Options for controling the file listing used to
  2407. /// determine if the file exists.</param>
  2408. /// <returns>True if the file exists</returns>
  2409. /// <example><code source="..\Examples\FileExists.cs" lang="cs" /></example>
  2410. public bool FileExists(string path, FtpListOption options) {
  2411. string dirname = path.GetFtpDirectoryName();
  2412. lock (m_lock)
  2413. {
  2414. if (!DirectoryExists(dirname))
  2415. return false;
  2416. foreach (FtpListItem item in GetListing(dirname, options))
  2417. if (item.Type == FtpFileSystemObjectType.File && item.Name == path.GetFtpFileName())
  2418. return true;
  2419. }
  2420. return false;
  2421. }
  2422. delegate bool AsyncFileExists(string path, FtpListOption options);
  2423. /// <summary>
  2424. /// Checks if a file exsts on the server by taking a
  2425. /// file listing of the parent directory in the path
  2426. /// and comparing the results the path supplied.
  2427. /// </summary>
  2428. /// <param name="path">The full or relative path to the file</param>
  2429. /// <param name="callback">Async callback</param>
  2430. /// <param name="state">State object</param>
  2431. /// <returns>IAsyncResult</returns>
  2432. /// <example><code source="..\Examples\BeginFileExists.cs" lang="cs" /></example>
  2433. public IAsyncResult BeginFileExists(string path, AsyncCallback callback, object state) {
  2434. return BeginFileExists(path, 0, callback, state);
  2435. }
  2436. /// <summary>
  2437. /// Checks if a file exsts on the server by taking a
  2438. /// file listing of the parent directory in the path
  2439. /// and comparing the results the path supplied.
  2440. /// </summary>
  2441. /// <param name="path">The full or relative path to the file</param>
  2442. /// <param name="options">Options for controling the file listing used to
  2443. /// determine if the file exists.</param>
  2444. /// <param name="callback">Async callback</param>
  2445. /// <param name="state">State object</param>
  2446. /// <returns>IAsyncResult</returns>
  2447. /// <example><code source="..\Examples\BeginFileExists.cs" lang="cs" /></example>
  2448. public IAsyncResult BeginFileExists(string path, FtpListOption options, AsyncCallback callback, object state) {
  2449. AsyncFileExists func;
  2450. IAsyncResult ar;
  2451. ar = (func = new AsyncFileExists(FileExists)).BeginInvoke(path, options, callback, state);
  2452. lock (m_asyncmethods) {
  2453. m_asyncmethods.Add(ar, func);
  2454. }
  2455. return ar;
  2456. }
  2457. /// <summary>
  2458. /// Ends a call to BeginFileExists
  2459. /// </summary>
  2460. /// <param name="ar">IAsyncResult returned from BeginFileExists</param>
  2461. /// <returns>True if the file exists</returns>
  2462. /// <example><code source="..\Examples\BeginFileExists.cs" lang="cs" /></example>
  2463. public bool EndFileExists(IAsyncResult ar) {
  2464. return GetAsyncDelegate<AsyncFileExists>(ar).EndInvoke(ar);
  2465. }
  2466. /// <summary>
  2467. /// Creates a directory on the server. If the preceding
  2468. /// directories do not exist they are created.
  2469. /// </summary>
  2470. /// <param name="path">The full or relative path to the new directory</param>
  2471. /// <example><code source="..\Examples\CreateDirectory.cs" lang="cs" /></example>
  2472. public void CreateDirectory(string path) {
  2473. CreateDirectory(path, true);
  2474. }
  2475. /// <summary>
  2476. /// Creates a directory on the server
  2477. /// </summary>
  2478. /// <param name="path">The full or relative path to the directory to create</param>
  2479. /// <param name="force">Try to force all non-existant pieces of the path to be created</param>
  2480. /// <example><code source="..\Examples\CreateDirectory.cs" lang="cs" /></example>
  2481. public void CreateDirectory(string path, bool force) {
  2482. FtpReply reply;
  2483. string ftppath = path.GetFtpPath();
  2484. if (ftppath == "." || ftppath == "./" || ftppath == "/")
  2485. return;
  2486. lock (m_lock)
  2487. {
  2488. path = path.GetFtpPath().TrimEnd('/');
  2489. if (force && !DirectoryExists(path.GetFtpDirectoryName())) {
  2490. FtpTrace.WriteLine(string.Format(
  2491. "CreateDirectory(\"{0}\", {1}): Create non-existent parent: {2}",
  2492. path, force, path.GetFtpDirectoryName()));
  2493. CreateDirectory(path.GetFtpDirectoryName(), true);
  2494. }
  2495. else if (DirectoryExists(path))
  2496. return;
  2497. FtpTrace.WriteLine(string.Format("CreateDirectory(\"{0}\", {1})",
  2498. ftppath, force));
  2499. if (!(reply = Execute("MKD {0}", ftppath)).Success)
  2500. throw new FtpCommandException(reply);
  2501. }
  2502. }
  2503. delegate void AsyncCreateDirectory(string path, bool force);
  2504. /// <summary>
  2505. /// Creates a directory asynchronously
  2506. /// </summary>
  2507. /// <param name="path">The full or relative path to the directory to create</param>
  2508. /// <param name="callback">Async callback</param>
  2509. /// <param name="state">State object</param>
  2510. /// <returns>IAsyncResult</returns>
  2511. /// <example><code source="..\Examples\BeginCreateDirectory.cs" lang="cs" /></example>
  2512. public IAsyncResult BeginCreateDirectory(string path, AsyncCallback callback, object state) {
  2513. return BeginCreateDirectory(path, true, callback, state);
  2514. }
  2515. /// <summary>
  2516. /// Creates a directory asynchronously
  2517. /// </summary>
  2518. /// <param name="path">The full or relative path to the directory to create</param>
  2519. /// <param name="force">Try to create the whole path if the preceding directories do not exist</param>
  2520. /// <param name="callback">Async callback</param>
  2521. /// <param name="state">State object</param>
  2522. /// <returns>IAsyncResult</returns>
  2523. /// <example><code source="..\Examples\BeginCreateDirectory.cs" lang="cs" /></example>
  2524. public IAsyncResult BeginCreateDirectory(string path, bool force, AsyncCallback callback, object state) {
  2525. AsyncCreateDirectory func;
  2526. IAsyncResult ar;
  2527. ar = (func = new AsyncCreateDirectory(CreateDirectory)).BeginInvoke(path, force, callback, state);
  2528. lock (m_asyncmethods) {
  2529. m_asyncmethods.Add(ar, func);
  2530. }
  2531. return ar;
  2532. }
  2533. /// <summary>
  2534. /// Ends a call to BeginCreateDirectory
  2535. /// </summary>
  2536. /// <param name="ar">IAsyncResult returned from BeginCreateDirectory</param>
  2537. /// <example><code source="..\Examples\BeginCreateDirectory.cs" lang="cs" /></example>
  2538. public void EndCreateDirectory(IAsyncResult ar) {
  2539. GetAsyncDelegate<AsyncCreateDirectory>(ar).EndInvoke(ar);
  2540. }
  2541. /// <summary>
  2542. /// Renames an object on the remote file system.
  2543. /// </summary>
  2544. /// <param name="path">The full or relative path to the object</param>
  2545. /// <param name="dest">The old or new full or relative path including the new name of the object</param>
  2546. /// <example><code source="..\Examples\Rename.cs" lang="cs" /></example>
  2547. public void Rename(string path, string dest) {
  2548. FtpReply reply;
  2549. lock (m_lock)
  2550. {
  2551. if (!(reply = Execute("RNFR {0}", path.GetFtpPath())).Success)
  2552. throw new FtpCommandException(reply);
  2553. if (!(reply = Execute("RNTO {0}", dest.GetFtpPath())).Success)
  2554. throw new FtpCommandException(reply);
  2555. }
  2556. }
  2557. delegate void AsyncRename(string path, string dest);
  2558. /// <summary>
  2559. /// Asynchronously renames an object on the server
  2560. /// </summary>
  2561. /// <param name="path">The full or relative path to the object</param>
  2562. /// <param name="dest">The old or new full or relative path including the new name of the object</param>
  2563. /// <param name="callback">Async callback</param>
  2564. /// <param name="state">State object</param>
  2565. /// <returns>IAsyncResult</returns>
  2566. /// <example><code source="..\Examples\BeginRename.cs" lang="cs" /></example>
  2567. public IAsyncResult BeginRename(string path, string dest, AsyncCallback callback, object state) {
  2568. AsyncRename func;
  2569. IAsyncResult ar;
  2570. ar = (func = new AsyncRename(Rename)).BeginInvoke(path, dest, callback, state);
  2571. lock (m_asyncmethods) {
  2572. m_asyncmethods.Add(ar, func);
  2573. }
  2574. return ar;
  2575. }
  2576. /// <summary>
  2577. /// Ends a call to BeginRename
  2578. /// </summary>
  2579. /// <param name="ar">IAsyncResult returned from BeginRename</param>
  2580. /// <example><code source="..\Examples\BeginRename.cs" lang="cs" /></example>
  2581. public void EndRename(IAsyncResult ar) {
  2582. GetAsyncDelegate<AsyncRename>(ar).EndInvoke(ar);
  2583. }
  2584. /// <summary>
  2585. /// Gets the currently selected hash algorith for the HASH
  2586. /// command. This feature is experimental. See this link
  2587. /// for details:
  2588. /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
  2589. /// </summary>
  2590. /// <returns>The FtpHashType flag or FtpHashType.NONE if there was a problem.</returns>
  2591. /// <example><code source="..\Examples\GetHashAlgorithm.cs" lang="cs" /></example>
  2592. public FtpHashAlgorithm GetHashAlgorithm() {
  2593. FtpReply reply;
  2594. FtpHashAlgorithm type = FtpHashAlgorithm.NONE;
  2595. lock (m_lock)
  2596. {
  2597. if ((reply = Execute("OPTS HASH")).Success) {
  2598. switch (reply.Message) {
  2599. case "SHA-1":
  2600. type = FtpHashAlgorithm.SHA1;
  2601. break;
  2602. case "SHA-256":
  2603. type = FtpHashAlgorithm.SHA256;
  2604. break;
  2605. case "SHA-512":
  2606. type = FtpHashAlgorithm.SHA512;
  2607. break;
  2608. case "MD5":
  2609. type = FtpHashAlgorithm.MD5;
  2610. break;
  2611. }
  2612. }
  2613. }
  2614. return type;
  2615. }
  2616. delegate FtpHashAlgorithm AsyncGetHashAlgorithm();
  2617. /// <summary>
  2618. /// Asynchronously get the hash algorithm being used by the HASH command.
  2619. /// </summary>
  2620. /// <param name="callback">Async callback</param>
  2621. /// <param name="state">State object</param>
  2622. /// <returns>IAsyncResult</returns>
  2623. public IAsyncResult BeginGetHashAlgorithm(AsyncCallback callback, object state) {
  2624. AsyncGetHashAlgorithm func;
  2625. IAsyncResult ar;
  2626. ar = (func = new AsyncGetHashAlgorithm(GetHashAlgorithm)).BeginInvoke(callback, state);
  2627. lock (m_asyncmethods) {
  2628. m_asyncmethods.Add(ar, func);
  2629. }
  2630. return ar;
  2631. }
  2632. /// <summary>
  2633. /// Ends a call to BeginGetHashAlgorithm
  2634. /// </summary>
  2635. /// <param name="ar">IAsyncResult returned from BeginGetHashAlgorithm</param>
  2636. public FtpHashAlgorithm EndGetHashAlgorithm(IAsyncResult ar) {
  2637. return GetAsyncDelegate<AsyncGetHashAlgorithm>(ar).EndInvoke(ar);
  2638. }
  2639. /// <summary>
  2640. /// Tells the server which hash algorith to use
  2641. /// for the HASH command. If you specifiy an
  2642. /// algorithm not listed in FtpClient.HashTypes
  2643. /// a NotImplemented() exectpion will be thrown
  2644. /// so be sure to query that list of Flags before
  2645. /// selecting a hash algorithm. Support for the
  2646. /// HASH command is experimental. Please see
  2647. /// the following link for more details:
  2648. /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
  2649. /// </summary>
  2650. /// <param name="type">Hash Algorithm</param>
  2651. /// <example><code source="..\Examples\SetHashAlgorithm.cs" lang="cs" /></example>
  2652. public void SetHashAlgorithm(FtpHashAlgorithm type) {
  2653. FtpReply reply;
  2654. string algorithm;
  2655. lock (m_lock)
  2656. {
  2657. if ((HashAlgorithms & type) != type)
  2658. throw new NotImplementedException(string.Format("The hash algorithm {0} was not advertised by the server.", type.ToString()));
  2659. switch (type) {
  2660. case FtpHashAlgorithm.SHA1:
  2661. algorithm = "SHA-1";
  2662. break;
  2663. case FtpHashAlgorithm.SHA256:
  2664. algorithm = "SHA-256";
  2665. break;
  2666. case FtpHashAlgorithm.SHA512:
  2667. algorithm = "SHA-512";
  2668. break;
  2669. case FtpHashAlgorithm.MD5:
  2670. algorithm = "MD5";
  2671. break;
  2672. default:
  2673. algorithm = type.ToString();
  2674. break;
  2675. }
  2676. if (!(reply = Execute("OPTS HASH {0}", algorithm)).Success)
  2677. throw new FtpCommandException(reply);
  2678. }
  2679. }
  2680. delegate void AsyncSetHashAlgorithm(FtpHashAlgorithm type);
  2681. /// <summary>
  2682. /// Asynchronously sets the hash algorithm type to be used with the HASH command.
  2683. /// </summary>
  2684. /// <param name="type">Hash algorithm to use</param>
  2685. /// <param name="callback">Async Callback</param>
  2686. /// <param name="state">State object</param>
  2687. /// <returns>IAsyncResult</returns>
  2688. public IAsyncResult BeginSetHashAlgorithm(FtpHashAlgorithm type, AsyncCallback callback, object state) {
  2689. AsyncSetHashAlgorithm func;
  2690. IAsyncResult ar;
  2691. ar = (func = new AsyncSetHashAlgorithm(SetHashAlgorithm)).BeginInvoke(type, callback, state);
  2692. lock (m_asyncmethods) {
  2693. m_asyncmethods.Add(ar, func);
  2694. }
  2695. return ar;
  2696. }
  2697. /// <summary>
  2698. /// Ends an asynchronous call to BeginSetHashAlgorithm
  2699. /// </summary>
  2700. /// <param name="ar">IAsyncResult returned from BeginSetHashAlgorithm</param>
  2701. public void EndSetHashAlgorithm(IAsyncResult ar) {
  2702. GetAsyncDelegate<AsyncSetHashAlgorithm>(ar).EndInvoke(ar);
  2703. }
  2704. /// <summary>
  2705. /// Gets the hash of an object on the server using the
  2706. /// currently selected hash algorithm. Supported
  2707. /// algorithms, if any, are available in the HashAlgorithms
  2708. /// property. You should confirm that it's not equal
  2709. /// to FtpHashAlgorithm.NONE before calling this method
  2710. /// otherwise the server trigger a FtpCommandException()
  2711. /// due to a lack of support for the HASH command. You can
  2712. /// set the algorithm using the SetHashAlgorithm() method and
  2713. /// you can query the server for the current hash algorithm
  2714. /// using the GetHashAlgorithm() method.
  2715. ///
  2716. /// This feature is experimental and based on the following draft:
  2717. /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
  2718. /// </summary>
  2719. /// <param name="path">Full or relative path of the object to compute the hash for.</param>
  2720. /// <returns>The hash of the file.</returns>
  2721. /// <example><code source="..\Examples\GetHash.cs" lang="cs" /></example>
  2722. public FtpHash GetHash(string path) {
  2723. FtpReply reply;
  2724. FtpHash hash = new FtpHash();
  2725. Match m;
  2726. if (path == null)
  2727. throw new ArgumentException("GetHash(path) argument can't be null");
  2728. lock (m_lock)
  2729. {
  2730. if (!(reply = Execute("HASH {0}", path.GetFtpPath())).Success)
  2731. throw new FtpCommandException(reply);
  2732. }
  2733. // Current draft says the server should return this:
  2734. // SHA-256 0-49 169cd22282da7f147cb491e559e9dd filename.ext
  2735. if (!(m = Regex.Match(reply.Message,
  2736. @"(?<algorithm>.+)\s" +
  2737. @"(?<bytestart>\d+)-(?<byteend>\d+)\s" +
  2738. @"(?<hash>.+)\s" +
  2739. @"(?<filename>.+)")).Success) {
  2740. // Current version of FileZilla returns this:
  2741. // SHA-1 21c2ca15cf570582949eb59fb78038b9c27ffcaf
  2742. m = Regex.Match(reply.Message, @"(?<algorithm>.+)\s(?<hash>.+)\s");
  2743. }
  2744. if (m != null && m.Success) {
  2745. switch (m.Groups["algorithm"].Value) {
  2746. case "SHA-1":
  2747. hash.Algorithm = FtpHashAlgorithm.SHA1;
  2748. break;
  2749. case "SHA-256":
  2750. hash.Algorithm = FtpHashAlgorithm.SHA256;
  2751. break;
  2752. case "SHA-512":
  2753. hash.Algorithm = FtpHashAlgorithm.SHA512;
  2754. break;
  2755. case "MD5":
  2756. hash.Algorithm = FtpHashAlgorithm.MD5;
  2757. break;
  2758. default:
  2759. throw new NotImplementedException("Unknown hash algorithm: " + m.Groups["algorithm"].Value);
  2760. }
  2761. hash.Value = m.Groups["hash"].Value;
  2762. }
  2763. else {
  2764. FtpTrace.WriteLine("Failed to parse hash from: {0}", reply.Message);
  2765. }
  2766. return hash;
  2767. }
  2768. delegate FtpHash AsyncGetHash(string path);
  2769. /// <summary>
  2770. /// Asynchronously retrieves the hash for the specified file
  2771. /// </summary>
  2772. /// <param name="path">The file you want the server to compute the hash for</param>
  2773. /// <param name="callback">AsyncCallback</param>
  2774. /// <param name="state">State object</param>
  2775. /// <returns>IAsyncResult</returns>
  2776. public IAsyncResult BeginGetHash(string path, AsyncCallback callback, object state) {
  2777. AsyncGetHash func;
  2778. IAsyncResult ar;
  2779. ar = (func = new AsyncGetHash(GetHash)).BeginInvoke(path, callback, state);
  2780. lock (m_asyncmethods) {
  2781. m_asyncmethods.Add(ar, func);
  2782. }
  2783. return ar;
  2784. }
  2785. /// <summary>
  2786. /// Ends an asynchronous call to BeginGetHash
  2787. /// </summary>
  2788. /// <param name="ar">IAsyncResult returned from BeginGetHash</param>
  2789. public void EndGetHash(IAsyncResult ar) {
  2790. GetAsyncDelegate<AsyncGetHash>(ar).EndInvoke(ar);
  2791. }
  2792. /// <summary>
  2793. /// Disables UTF8 support and changes the Encoding property
  2794. /// back to ASCII. If the server returns an error when trying
  2795. /// to turn UTF8 off a FtpCommandException will be thrown.
  2796. /// </summary>
  2797. public void DisableUTF8() {
  2798. FtpReply reply;
  2799. lock (m_lock)
  2800. {
  2801. if (!(reply = Execute("OPTS UTF8 OFF")).Success)
  2802. throw new FtpCommandException(reply);
  2803. m_textEncoding = Encoding.ASCII;
  2804. }
  2805. }
  2806. /// <summary>
  2807. /// Disconnects from the server, releases resources held by this
  2808. /// object.
  2809. /// </summary>
  2810. public void Dispose() {
  2811. lock (m_lock)
  2812. {
  2813. if (IsDisposed)
  2814. return;
  2815. FtpTrace.WriteLine("Disposing FtpClient object...");
  2816. try
  2817. {
  2818. if (IsConnected)
  2819. {
  2820. Disconnect();
  2821. }
  2822. }
  2823. catch (Exception ex)
  2824. {
  2825. FtpTrace.WriteLine("FtpClient.Dispose(): Caught and discarded an exception while disconnecting from host: {0}", ex.ToString());
  2826. }
  2827. if (m_stream != null)
  2828. {
  2829. try
  2830. {
  2831. m_stream.Dispose();
  2832. }
  2833. catch (Exception ex)
  2834. {
  2835. FtpTrace.WriteLine("FtpClient.Dispose(): Caught and discarded an exception while disposing FtpStream object: {0}", ex.ToString());
  2836. }
  2837. finally
  2838. {
  2839. m_stream = null;
  2840. }
  2841. }
  2842. m_credentials = null;
  2843. m_textEncoding = null;
  2844. m_host = null;
  2845. m_asyncmethods.Clear();
  2846. IsDisposed = true;
  2847. GC.SuppressFinalize(this);
  2848. }
  2849. }
  2850. /// <summary>
  2851. /// Finalizer
  2852. /// </summary>
  2853. ~FtpClient() {
  2854. Dispose();
  2855. }
  2856. /// <summary>
  2857. /// Creates a new isntance of FtpClient
  2858. /// </summary>
  2859. public FtpClient() { }
  2860. /// <summary>
  2861. /// Connects to the specified URI. If the path specified by the URI ends with a
  2862. /// / then the working directory is changed to the path specified.
  2863. /// </summary>
  2864. /// <param name="uri">The URI to parse</param>
  2865. /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
  2866. /// <returns>FtpClient object</returns>
  2867. public static FtpClient Connect(Uri uri, bool checkcertificate) {
  2868. FtpClient cl = new FtpClient();
  2869. if (uri == null)
  2870. throw new ArgumentException("Invalid URI object");
  2871. switch (uri.Scheme.ToLower()) {
  2872. case "ftp":
  2873. case "ftps":
  2874. break;
  2875. default:
  2876. throw new UriFormatException("The specified URI scheme is not supported. Please use ftp:// or ftps://");
  2877. }
  2878. cl.Host = uri.Host;
  2879. cl.Port = uri.Port;
  2880. if (uri.UserInfo != null && uri.UserInfo.Length > 0) {
  2881. if (uri.UserInfo.Contains(":")) {
  2882. string[] parts = uri.UserInfo.Split(':');
  2883. if (parts.Length != 2)
  2884. throw new UriFormatException("The user info portion of the URI contains more than 1 colon. The username and password portion of the URI should be URL encoded.");
  2885. cl.Credentials = new NetworkCredential(HttpUtility.UrlDecode(parts[0]), HttpUtility.UrlDecode(parts[1]));
  2886. }
  2887. else
  2888. cl.Credentials = new NetworkCredential(HttpUtility.UrlDecode(uri.UserInfo), "");
  2889. }
  2890. else {
  2891. // if no credentials were supplied just make up
  2892. // some for anonymous authentication.
  2893. cl.Credentials = new NetworkCredential("ftp", "ftp");
  2894. }
  2895. cl.ValidateCertificate += new FtpSslValidation(delegate(FtpClient control, FtpSslValidationEventArgs e) {
  2896. if (e.PolicyErrors != Security.SslPolicyErrors.None && checkcertificate)
  2897. e.Accept = false;
  2898. else
  2899. e.Accept = true;
  2900. });
  2901. cl.Connect();
  2902. if (uri.PathAndQuery != null && uri.PathAndQuery.EndsWith("/"))
  2903. cl.SetWorkingDirectory(uri.PathAndQuery);
  2904. return cl;
  2905. }
  2906. /// <summary>
  2907. /// Connects to the specified URI. If the path specified by the URI ends with a
  2908. /// / then the working directory is changed to the path specified.
  2909. /// </summary>
  2910. /// <param name="uri">The URI to parse</param>
  2911. /// <returns>FtpClient object</returns>
  2912. public static FtpClient Connect(Uri uri) {
  2913. return Connect(uri, true);
  2914. }
  2915. /// <summary>
  2916. /// Opens a stream to the file specified by the URI
  2917. /// </summary>
  2918. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  2919. /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
  2920. /// <param name="datatype">ASCII/Binary mode</param>
  2921. /// <param name="restart">Restart location</param>
  2922. /// <returns>Stream object</returns>
  2923. /// <example><code source="..\Examples\OpenReadURI.cs" lang="cs" /></example>
  2924. public static Stream OpenRead(Uri uri, bool checkcertificate, FtpDataType datatype, long restart) {
  2925. FtpClient cl = null;
  2926. if (uri.PathAndQuery == null || uri.PathAndQuery.Length == 0)
  2927. throw new UriFormatException("The supplied URI does not contain a valid path.");
  2928. if (uri.PathAndQuery.EndsWith("/"))
  2929. throw new UriFormatException("The supplied URI points at a directory.");
  2930. cl = Connect(uri, checkcertificate);
  2931. cl.EnableThreadSafeDataConnections = false;
  2932. return cl.OpenRead(uri.PathAndQuery, datatype, restart);
  2933. }
  2934. /// <summary>
  2935. /// Opens a stream to the file specified by the URI
  2936. /// </summary>
  2937. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  2938. /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
  2939. /// <param name="datatype">ASCII/Binary mode</param>
  2940. /// <returns>Stream object</returns>
  2941. /// <example><code source="..\Examples\OpenReadURI.cs" lang="cs" /></example>
  2942. public static Stream OpenRead(Uri uri, bool checkcertificate, FtpDataType datatype) {
  2943. return OpenRead(uri, checkcertificate, datatype, 0);
  2944. }
  2945. /// <summary>
  2946. /// Opens a stream to the file specified by the URI
  2947. /// </summary>
  2948. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  2949. /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
  2950. /// <returns>Stream object</returns>
  2951. /// <example><code source="..\Examples\OpenReadURI.cs" lang="cs" /></example>
  2952. public static Stream OpenRead(Uri uri, bool checkcertificate) {
  2953. return OpenRead(uri, checkcertificate, FtpDataType.Binary, 0);
  2954. }
  2955. /// <summary>
  2956. /// Opens a stream to the file specified by the URI
  2957. /// </summary>
  2958. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  2959. /// <returns>Stream object</returns>
  2960. /// <example><code source="..\Examples\OpenReadURI.cs" lang="cs" /></example>
  2961. public static Stream OpenRead(Uri uri) {
  2962. return OpenRead(uri, true, FtpDataType.Binary, 0);
  2963. }
  2964. /// <summary>
  2965. /// Opens a stream to the file specified by the URI
  2966. /// </summary>
  2967. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  2968. /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
  2969. /// <param name="datatype">ASCII/Binary mode</param>
  2970. /// <returns>Stream object</returns>
  2971. /// <example><code source="..\Examples\OpenWriteURI.cs" lang="cs" /></example>
  2972. public static Stream OpenWrite(Uri uri, bool checkcertificate, FtpDataType datatype) {
  2973. FtpClient cl = null;
  2974. if (uri.PathAndQuery == null || uri.PathAndQuery.Length == 0)
  2975. throw new UriFormatException("The supplied URI does not contain a valid path.");
  2976. if (uri.PathAndQuery.EndsWith("/"))
  2977. throw new UriFormatException("The supplied URI points at a directory.");
  2978. cl = Connect(uri, checkcertificate);
  2979. cl.EnableThreadSafeDataConnections = false;
  2980. return cl.OpenWrite(uri.PathAndQuery, datatype);
  2981. }
  2982. /// <summary>
  2983. /// Opens a stream to the file specified by the URI
  2984. /// </summary>
  2985. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  2986. /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
  2987. /// <returns>Stream object</returns>
  2988. /// <example><code source="..\Examples\OpenWriteURI.cs" lang="cs" /></example>
  2989. public static Stream OpenWrite(Uri uri, bool checkcertificate) {
  2990. return OpenWrite(uri, checkcertificate, FtpDataType.Binary);
  2991. }
  2992. /// <summary>
  2993. /// Opens a stream to the file specified by the URI
  2994. /// </summary>
  2995. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  2996. /// <returns>Stream object</returns>
  2997. /// <example><code source="..\Examples\OpenWriteURI.cs" lang="cs" /></example>
  2998. public static Stream OpenWrite(Uri uri) {
  2999. return OpenWrite(uri, true, FtpDataType.Binary);
  3000. }
  3001. /// <summary>
  3002. /// Opens a stream to the file specified by the URI
  3003. /// </summary>
  3004. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  3005. /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
  3006. /// <param name="datatype">ASCII/Binary mode</param>
  3007. /// <returns>Stream object</returns>
  3008. /// <example><code source="..\Examples\OpenAppendURI.cs" lang="cs" /></example>
  3009. public static Stream OpenAppend(Uri uri, bool checkcertificate, FtpDataType datatype) {
  3010. FtpClient cl = null;
  3011. if (uri.PathAndQuery == null || uri.PathAndQuery.Length == 0)
  3012. throw new UriFormatException("The supplied URI does not contain a valid path.");
  3013. if (uri.PathAndQuery.EndsWith("/"))
  3014. throw new UriFormatException("The supplied URI points at a directory.");
  3015. cl = Connect(uri, checkcertificate);
  3016. cl.EnableThreadSafeDataConnections = false;
  3017. return cl.OpenAppend(uri.PathAndQuery, datatype);
  3018. }
  3019. /// <summary>
  3020. /// Opens a stream to the file specified by the URI
  3021. /// </summary>
  3022. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  3023. /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
  3024. /// <returns>Stream object</returns>
  3025. /// <example><code source="..\Examples\OpenAppendURI.cs" lang="cs" /></example>
  3026. public static Stream OpenAppend(Uri uri, bool checkcertificate) {
  3027. return OpenAppend(uri, checkcertificate, FtpDataType.Binary);
  3028. }
  3029. /// <summary>
  3030. /// Opens a stream to the file specified by the URI
  3031. /// </summary>
  3032. /// <param name="uri">FTP/FTPS URI pointing at a file</param>
  3033. /// <returns>Stream object</returns>
  3034. /// <example><code source="..\Examples\OpenAppendURI.cs" lang="cs" /></example>
  3035. public static Stream OpenAppend(Uri uri) {
  3036. return OpenAppend(uri, true, FtpDataType.Binary);
  3037. }
  3038. /// <summary>
  3039. /// Used internally to mark properties in the control connection that
  3040. /// should be cloned when opening a data connection.
  3041. /// </summary>
  3042. sealed class FtpControlConnectionClone : Attribute {
  3043. }
  3044. }
  3045. }