1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493 |
- using System;
- using System.IO;
- using System.Net.Sockets;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Reflection;
- using System.Threading;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Web;
- using System.Security.Cryptography.X509Certificates;
- using System.Globalization;
- using System.Net.FtpClient.Extensions;
- using System.Security.Authentication;
- namespace System.Net.FtpClient {
- /// <summary>
- /// Event is fired when a ssl certificate needs to be validated
- /// </summary>
- /// <param name="control">The contol connection that triggered the event</param>
- /// <param name="e">Event args</param>
- public delegate void FtpSslValidation(FtpClient control, FtpSslValidationEventArgs e);
- /// <summary>
- /// FTP Control Connection. Speaks the FTP protocol with the server and
- /// provides facilities for performing basic transactions.
- ///
- /// Debugging problems with FTP transactions is much easier to do when
- /// you can see exactly what is sent to the server and the reply
- /// System.Net.FtpClient gets in return. Please review the Debug example
- /// below for information on how to add TraceListeners for capturing
- /// the convorsation between System.Net.FtpClient and the server.
- /// </summary>
- /// <example>The following example illustrates how to assist in debugging
- /// System.Net.FtpClient by getting a transaction log from the server.
- /// <code source="..\Examples\Debug.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonstrates adding a custom file
- /// listing parser in the event that you encounter a list format
- /// not already supported.
- /// <code source="..\Examples\CustomParser.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonstrates how to validate
- /// a SSL certificate when using SSL/TLS.
- /// <code source="..\Examples\ValidateCertificate.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonsrates how to download a file.
- /// <code source="..\Examples\OpenRead.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonstrates how to download a file
- /// using a URI object.
- /// <code source="..\Examples\OpenReadURI.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonstrates how to upload a file.
- /// <code source="..\Examples\OpenWrite.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonstrates how to upload a file
- /// using a URI object.
- /// <code source="..\Examples\OpenWriteURI.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonstrates how to append to a file.
- /// <code source="..\Examples\OpenAppend.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonstrates how to append to a file
- /// using a URI object.
- /// <code source="..\Examples\OpenAppendURI.cs" lang="cs" />
- /// </example>
- /// <example>The following example demonstrates how to get a file
- /// listing from the server.
- /// <code source="..\Examples\GetListing.cs" lang="cs" />
- /// </example>
- public class FtpClient : IFtpClient {
- /// <summary>
- /// Used for internally syncrhonizing access to this
- /// object from multiple threads
- /// </summary>
- readonly Object m_lock = new Object();
- /// <summary>
- /// A list of asynchronoous methods that are in progress
- /// </summary>
- readonly Dictionary<IAsyncResult, object> m_asyncmethods = new Dictionary<IAsyncResult, object>();
- /// <summary>
- /// Control connection socket stream
- /// </summary>
- FtpSocketStream m_stream = null;
- bool m_isDisposed = false;
- /// <summary>
- /// Gets a value indicating if this object has already been disposed.
- /// </summary>
- public bool IsDisposed {
- get {
- return m_isDisposed;
- }
- private set {
- m_isDisposed = value;
- }
- }
- /// <summary>
- /// Gets the base stream for talking to the server via
- /// the control connection.
- /// </summary>
- protected Stream BaseStream {
- get {
- return m_stream;
- }
- }
- FtpIpVersion m_ipVersions = FtpIpVersion.ANY;
- /// <summary>
- /// Flags specifying which versions of the internet protocol to
- /// support when making a connection. All addresses returned during
- /// name resolution are tried until a successful connection is made.
- /// You can fine tune which versions of the internet protocol to use
- /// by adding or removing flags here. I.e., setting this property
- /// to FtpIpVersion.IPv4 will cause the connection process to
- /// ignore IPv6 addresses. The default value is ANY version.
- /// </summary>
- [FtpControlConnectionClone]
- public FtpIpVersion InternetProtocolVersions {
- get {
- return m_ipVersions;
- }
- set {
- m_ipVersions = value;
- }
- }
- int m_socketPollInterval = 15000;
- /// <summary>
- /// Gets or sets the length of time in miliseconds
- /// that must pass since the last socket activity
- /// before calling Poll() on the socket to test for
- /// connectivity. Setting this interval too low will
- /// have a negative impact on perfomance. Setting this
- /// interval to 0 disables Poll()'ing all together.
- /// The default value is 15 seconds.
- /// </summary>
- [FtpControlConnectionClone]
- public int SocketPollInterval {
- get { return m_socketPollInterval; }
- set {
- m_socketPollInterval = value;
- if (m_stream != null)
- m_stream.SocketPollInterval = value;
- }
- }
- bool m_staleDataTest = true;
- /// <summary>
- /// Gets or sets a value indicating whether a test should be performed to
- /// see if there is stale (unrequested data) sitting on the socket. In some
- /// cases the control connection may time out but before the server closes
- /// the connection it might send a 4xx response that was unexpected and
- /// can cause synchronization errors with transactions. To avoid this
- /// problem the Execute() method checks to see if there is any data
- /// available on the socket before executing a command. On Azure hosting
- /// platforms this check can cause an exception to be thrown. In order
- /// to work around the exception you can set this property to false
- /// which will skip the test entirely however doing so eliminates the
- /// best effort attempt of detecting such scenarios. See this thread
- /// for more details about the Azure problem:
- /// https://netftp.codeplex.com/discussions/535879
- /// </summary>
- [FtpControlConnectionClone]
- public bool StaleDataCheck {
- get { return m_staleDataTest; }
- set { m_staleDataTest = value; }
- }
- /// <summary>
- /// Gets a value indicating if the connection is alive
- /// </summary>
- public bool IsConnected {
- get {
- if (m_stream != null)
- return m_stream.IsConnected;
- return false;
- }
- }
- bool m_threadSafeDataChannels = true;
- /// <summary>
- /// When this value is set to true (default) the control connection
- /// is cloned and a new connection the server is established for the
- /// data channel operation. This is a thread safe approach to make
- /// asynchronous operations on a single control connection transparent
- /// to the developer.
- /// </summary>
- [FtpControlConnectionClone]
- public bool EnableThreadSafeDataConnections {
- get {
- return m_threadSafeDataChannels;
- }
- set {
- m_threadSafeDataChannels = value;
- }
- }
- bool m_isClone = false;
- /// <summary>
- /// Gets a value indicating if this control connection is a clone. This property
- /// is used with data streams to determine if the connection should be closed
- /// when the stream is closed. Servers typically only allow 1 data connection
- /// per control connection. If you try to open multiple data connections this
- /// object will be cloned for 2 or more resulting in N new connections to the
- /// server.
- /// </summary>
- internal bool IsClone {
- get {
- return m_isClone;
- }
- private set {
- m_isClone = value;
- }
- }
- Encoding m_textEncoding = Encoding.ASCII;
- /// <summary>
- /// Gets or sets the text encoding being used when talking with the server. The default
- /// value is Encoding.ASCII however upon connection, the client checks
- /// for UTF8 support and if it's there this property is switched over to
- /// Encoding.UTF8. Manually setting this value overrides automatic detection
- /// based on the FEAT list; if you change this value it's always used
- /// regardless of what the server advertises, if anything.
- /// </summary>
- [FtpControlConnectionClone]
- public Encoding Encoding {
- get {
- return m_textEncoding;
- }
- set {
- lock (m_lock)
- {
- m_textEncoding = value;
- }
- }
- }
- string m_host = null;
- /// <summary>
- /// The server to connect to
- /// </summary>
- [FtpControlConnectionClone]
- public string Host {
- get {
- return m_host;
- }
- set {
- m_host = value;
- }
- }
- int m_port = 0;
- /// <summary>
- /// The port to connect to. If this value is set to 0 (Default) the port used
- /// will be determined by the type of SSL used or if no SSL is to be used it
- /// will automatically connect to port 21.
- /// </summary>
- [FtpControlConnectionClone]
- public int Port {
- get {
- // automatically determine port
- // when m_port is 0.
- if (m_port == 0) {
- switch (EncryptionMode) {
- case FtpEncryptionMode.None:
- case FtpEncryptionMode.Explicit:
- return 21;
- case FtpEncryptionMode.Implicit:
- return 990;
- }
- }
- return m_port;
- }
- set {
- m_port = value;
- }
- }
- NetworkCredential m_credentials = null;
- /// <summary>
- /// Credentials used for authentication
- /// </summary>
- [FtpControlConnectionClone]
- public NetworkCredential Credentials {
- get {
- return m_credentials;
- }
- set {
- m_credentials = value;
- }
- }
- int m_maxDerefCount = 20;
- /// <summary>
- /// Gets or sets a value that controls the maximum depth
- /// of recursion that DereferenceLink() will follow symbolic
- /// links before giving up. You can also specify the value
- /// to be used as one of the overloaded parameters to the
- /// DereferenceLink() method. The default value is 20. Specifying
- /// -1 here means inifinitly try to resolve a link. This is
- /// not recommended for obvious reasons (stack overflow).
- /// </summary>
- [FtpControlConnectionClone]
- public int MaximumDereferenceCount {
- get {
- return m_maxDerefCount;
- }
- set {
- m_maxDerefCount = value;
- }
- }
- X509CertificateCollection m_clientCerts = new X509CertificateCollection();
- /// <summary>
- /// Client certificates to be used in SSL authentication process
- /// </summary>
- [FtpControlConnectionClone]
- public X509CertificateCollection ClientCertificates {
- get {
- return m_clientCerts;
- }
- protected set {
- m_clientCerts = value;
- }
- }
- FtpDataConnectionType m_dataConnectionType = FtpDataConnectionType.AutoPassive;
- /// <summary>
- /// 数据连接类型,默认为AutoPassive,它首先尝试与EPSV的连接,
- /// 如果失败,然后在放弃之前尝试PASV。 如果您确切知道需要哪种连接,
- /// 您可以通过在此处定义特定类型的被动或活动数据连接来略微提高性能。
- /// Data connection type, default is AutoPassive which tries
- /// a connection with EPSV first and if it fails then tries
- /// PASV before giving up. If you know exactly which kind of
- /// connection you need you can slightly increase performance
- /// by defining a speicific type of passive or active data
- /// connection here.
- /// </summary>
- [FtpControlConnectionClone]
- public FtpDataConnectionType DataConnectionType {
- get {
- return m_dataConnectionType;
- }
- set {
- m_dataConnectionType = value;
- }
- }
- bool m_ungracefullDisconnect = false;
- /// <summary>
- /// Disconnect from the server without sending QUIT. This helps
- /// work around IOExceptions caused by buggy connection resets
- /// when closing the control connection.
- /// </summary>
- [FtpControlConnectionClone]
- public bool UngracefullDisconnection {
- get {
- return m_ungracefullDisconnect;
- }
- set {
- m_ungracefullDisconnect = value;
- }
- }
- int m_connectTimeout = 15000;
- /// <summary>
- /// Gets or sets the length of time in miliseconds to wait for a connection
- /// attempt to succeed before giving up. Default is 15000 (15 seconds).
- /// </summary>
- [FtpControlConnectionClone]
- public int ConnectTimeout {
- get {
- return m_connectTimeout;
- }
- set {
- m_connectTimeout = value;
- }
- }
- int m_readTimeout = 15000;
- /// <summary>
- /// Gets or sets the length of time wait in miliseconds for data to be
- /// read from the underlying stream. The default value is 15000 (15 seconds).
- /// </summary>
- [FtpControlConnectionClone]
- public int ReadTimeout {
- get {
- return m_readTimeout;
- }
- set {
- m_readTimeout = value;
- }
- }
- int m_dataConnectionConnectTimeout = 15000;
- /// <summary>
- /// Gets or sets the length of time in miliseconds for a data connection
- /// to be established before giving up. Default is 15000 (15 seconds).
- /// </summary>
- [FtpControlConnectionClone]
- public int DataConnectionConnectTimeout {
- get {
- return m_dataConnectionConnectTimeout;
- }
- set {
- m_dataConnectionConnectTimeout = value;
- }
- }
- int m_dataConnectionReadTimeout = 15000;
- /// <summary>
- /// Gets or sets the length of time in miliseconds the data channel
- /// should wait for the server to send data. Default value is
- /// 15000 (15 seconds).
- /// </summary>
- [FtpControlConnectionClone]
- public int DataConnectionReadTimeout {
- get {
- return m_dataConnectionReadTimeout;
- }
- set {
- m_dataConnectionReadTimeout = value;
- }
- }
- bool m_keepAlive = false;
- /// <summary>
- /// Gets or sets a value indicating if SocketOption.KeepAlive should be set on
- /// the underlying stream's socket. If the connection is alive, the option is
- /// adjusted in real-time. The value is stored and the KeepAlive option is set
- /// accordingly upon any new connections. The value set here is also applied to
- /// all future data streams. It has no affect on cloned control connections or
- /// data connections already in progress. The default value is false.
- /// </summary>
- [FtpControlConnectionClone]
- public bool SocketKeepAlive {
- get {
- return m_keepAlive;
- }
- set {
- m_keepAlive = value;
- if (m_stream != null)
- m_stream.SetSocketOption(Sockets.SocketOptionLevel.Socket, Sockets.SocketOptionName.KeepAlive, value);
- }
- }
- FtpCapability m_caps = FtpCapability.NONE;
- /// <summary>
- /// Gets the server capabilties represented by flags
- /// </summary>
- [FtpControlConnectionClone]
- public FtpCapability Capabilities {
- get {
- if (m_stream == null || !m_stream.IsConnected) {
- Connect();
- }
- return m_caps;
- }
- protected set {
- m_caps = value;
- }
- }
- FtpHashAlgorithm m_hashAlgorithms = FtpHashAlgorithm.NONE;
- /// <summary>
- /// Get the hash types supported by the server, if any. This
- /// is a recent extension to the protocol that is not fully
- /// standardized and is not guarateed to work. See here for
- /// more details:
- /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
- /// </summary>
- public FtpHashAlgorithm HashAlgorithms {
- get {
- if (m_stream == null || !m_stream.IsConnected) {
- Connect();
- }
- return m_hashAlgorithms;
- }
- private set {
- m_hashAlgorithms = value;
- }
- }
- FtpEncryptionMode m_encryptionmode = FtpEncryptionMode.None;
- /// <summary>
- /// 要使用的SSL类型,或无。 默认值为none。 显式是TLS,隐式是SSL。
- /// Type of SSL to use, or none. Default is none. Explicit is TLS, Implicit is SSL.
- /// </summary>
- [FtpControlConnectionClone]
- public FtpEncryptionMode EncryptionMode {
- get {
- return m_encryptionmode;
- }
- set {
- m_encryptionmode = value;
- }
- }
- bool m_dataConnectionEncryption = true;
- /// <summary>
- /// Indicates if data channel transfers should be encrypted. Only valid if EncryptionMode
- /// property is not equal to FtpSslMode.None.
- /// </summary>
- [FtpControlConnectionClone]
- public bool DataConnectionEncryption {
- get {
- return m_dataConnectionEncryption;
- }
- set {
- m_dataConnectionEncryption = value;
- }
- }
- private SslProtocols m_SslProtocols = SslProtocols.Default;
- /// <summary>
- /// Encryption protocols to use. Only valid if EncryptionMode property is not equal to FtpSslMode.None.
- /// Default value is .NET Framework defaults from SslStream class.
- /// </summary>
- [FtpControlConnectionClone]
- public SslProtocols SslProtocols
- {
- get {
- return m_SslProtocols;
- }
- set {
- m_SslProtocols = value;
- }
- }
-
- FtpSslValidation m_sslvalidate = null;
- /// <summary>
- /// 触发事件以验证SSL证书。 如果未处理此事件,并且验证证书时出现错误,则连接将中止。
- /// Event is fired to validate SSL certificates. If this event is
- /// not handled and there are errors validating the certificate
- /// the connection will be aborted.
- /// </summary>
- /// <example><code source="..\Examples\ValidateCertificate.cs" lang="cs" /></example>
- public event FtpSslValidation ValidateCertificate {
- add {
- m_sslvalidate += value;
- }
- remove {
- m_sslvalidate -= value;
- }
- }
- /// <summary>
- /// Gets the type of system/server that we're
- /// connected to.
- /// </summary>
- public string SystemType {
- get {
- FtpReply reply = Execute("SYST");
- if (reply.Success)
- return reply.Message;
- return null;
- }
- }
- /// <summary>
- /// Performs a bitwise and to check if the specified
- /// flag is set on the Capabilities enum property.
- /// </summary>
- /// <param name="cap">The capability to check for</param>
- /// <returns>True if the feature was found</returns>
- public bool HasFeature(FtpCapability cap) {
- return ((this.Capabilities & cap) == cap);
- }
- /// <summary>
- /// Fires the SSL validation event
- /// </summary>
- /// <param name="e">Event Args</param>
- void OnValidateCertficate(FtpSslValidationEventArgs e) {
- FtpSslValidation evt;
- evt = m_sslvalidate;
- if (evt != null)
- evt(this, e);
- }
- /// <summary>
- /// Retretieves the delegate for the specified IAsyncResult and removes
- /// it from the m_asyncmethods collection if the operation is successfull
- /// </summary>
- /// <typeparam name="T">Type of delegate to retrieve</typeparam>
- /// <param name="ar">The IAsyncResult to retrieve the delegate for</param>
- /// <returns>The delegate that generated the specified IAsyncResult</returns>
- protected T GetAsyncDelegate<T>(IAsyncResult ar) {
- T func;
- lock (m_asyncmethods) {
- if (m_isDisposed) {
- throw new ObjectDisposedException("This connection object has already been disposed.");
- }
- if (!m_asyncmethods.ContainsKey(ar))
- throw new InvalidOperationException("The specified IAsyncResult could not be located.");
- if (!(m_asyncmethods[ar] is T)) {
- StackTrace st = new StackTrace(1);
- throw new InvalidCastException("The AsyncResult cannot be matched to the specified delegate. " +
- string.Format("Are you sure you meant to call {0} and not another method?",
- st.GetFrame(0).GetMethod().Name)
- );
- }
- func = (T)m_asyncmethods[ar];
- m_asyncmethods.Remove(ar);
- }
- return func;
- }
- /// <summary>
- /// Clones the control connection for opening multipe data streams
- /// </summary>
- /// <returns>A new control connection with the same property settings as this one</returns>
- /// <example><code source="..\Examples\CloneConnection.cs" lang="cs" /></example>
- protected FtpClient CloneConnection() {
- FtpClient conn = new FtpClient();
- conn.m_isClone = true;
- foreach (PropertyInfo prop in GetType().GetProperties()) {
- object[] attributes = prop.GetCustomAttributes(typeof(FtpControlConnectionClone), true);
- if (attributes != null && attributes.Length > 0) {
- prop.SetValue(conn, prop.GetValue(this, null), null);
- }
- }
- // always accept certficate no matter what because if code execution ever
- // gets here it means the certificate on the control connection object being
- // cloned was already accepted.
- conn.ValidateCertificate += new FtpSslValidation(
- delegate(FtpClient obj, FtpSslValidationEventArgs e) {
- e.Accept = true;
- });
- return conn;
- }
- /// <summary>
- /// Retreives a reply from the server. Do not execute this method
- /// unless you are sure that a reply has been sent, i.e., you
- /// executed a command. Doing so will cause the code to hang
- /// indefinitely waiting for a server reply that is never comming.
- /// </summary>
- /// <returns>FtpReply representing the response from the server</returns>
- /// <example><code source="..\Examples\BeginGetReply.cs" lang="cs" /></example>
- internal FtpReply GetReply() {
- FtpReply reply = new FtpReply();
- string buf;
- lock (m_lock)
- {
- if (!IsConnected)
- throw new InvalidOperationException("No connection to the server has been established.");
- m_stream.ReadTimeout = m_readTimeout;
- while ((buf = m_stream.ReadLine(Encoding)) != null) {
- Match m;
- FtpTrace.WriteLine(buf);
- if ((m = Regex.Match(buf, "^(?<code>[0-9]{3}) (?<message>.*)$")).Success) {
- reply.Code = m.Groups["code"].Value;
- reply.Message = m.Groups["message"].Value;
- break;
- }
- reply.InfoMessages += string.Format("{0}\n", buf);
- }
- }
- return reply;
- }
- /// <summary>
- /// Executes a command
- /// </summary>
- /// <param name="command">The command to execute with optional format place holders</param>
- /// <param name="args">Format parameters to the command</param>
- /// <returns>The servers reply to the command</returns>
- /// <example><code source="..\Examples\Execute.cs" lang="cs" /></example>
- public FtpReply Execute(string command, params object[] args) {
- return Execute(string.Format(command, args));
- }
- /// <summary>
- /// Executes a command
- /// </summary>
- /// <param name="command">The command to execute</param>
- /// <returns>The servers reply to the command</returns>
- /// <example><code source="..\Examples\Execute.cs" lang="cs" /></example>
- public FtpReply Execute(string command) {
- FtpReply reply;
- lock (m_lock)
- {
- if(StaleDataCheck) {
- if (m_stream != null && m_stream.SocketDataAvailable > 0) {
- // Data shouldn't be on the socket, if it is it probably
- // means we've been disconnected. Read and discard
- // whatever is there and close the connection.
- FtpTrace.WriteLine("There is stale data on the socket, maybe our connection timed out. Re-connecting.");
- if (m_stream.IsConnected && !m_stream.IsEncrypted) {
- byte[] buf = new byte[m_stream.SocketDataAvailable];
- m_stream.RawSocketRead(buf);
- FtpTrace.Write("The data was: ");
- FtpTrace.WriteLine(Encoding.GetString(buf).TrimEnd('\r', '\n'));
- }
- m_stream.Close();
- }
- }
- if (!IsConnected) {
- if (command == "QUIT") {
- FtpTrace.WriteLine("Not sending QUIT because the connection has already been closed.");
- return new FtpReply() {
- Code = "200",
- Message = "Connection already closed."
- };
- }
- Connect();
- }
- FtpTrace.WriteLine(command.StartsWith("PASS") ? "PASS <omitted>" : command);
- m_stream.WriteLine(m_textEncoding, command);
- reply = GetReply();
- }
- return reply;
- }
- delegate FtpReply AsyncExecute(string command);
- /// <summary>
- /// Performs an asynchronouse execution of the specified command
- /// </summary>
- /// <param name="command">The command to execute</param>
- /// <param name="callback">The AsyncCallback method</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginExecute.cs" lang="cs" /></example>
- public IAsyncResult BeginExecute(string command, AsyncCallback callback, object state) {
- AsyncExecute func;
- IAsyncResult ar;
- ar = (func = new AsyncExecute(Execute)).BeginInvoke(command, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends an asynchronous command
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginExecute</param>
- /// <returns>FtpReply object (never null).</returns>
- /// <example><code source="..\Examples\BeginExecute.cs" lang="cs" /></example>
- public FtpReply EndExecute(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncExecute>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Connect to the server. Throws ObjectDisposedException if this object has been disposed.
- /// </summary>
- /// <example><code source="..\Examples\Connect.cs" lang="cs" /></example>
- public virtual void Connect() {
- FtpReply reply;
- lock (m_lock)
- {
- if (IsDisposed)
- throw new ObjectDisposedException("This FtpClient object has been disposed. It is no longer accessible.");
- if (m_stream == null) {
- m_stream = new FtpSocketStream();
- m_stream.ValidateCertificate += new FtpSocketStreamSslValidation(FireValidateCertficate);
- }
- else
- if (IsConnected)
- Disconnect();
- if (Host == null)
- throw new FtpException("No host has been specified");
- if (!IsClone)
- m_caps = FtpCapability.NONE;
- m_hashAlgorithms = FtpHashAlgorithm.NONE;
- m_stream.ConnectTimeout = m_connectTimeout;
- m_stream.SocketPollInterval = m_socketPollInterval;
- m_stream.Connect(Host, Port, InternetProtocolVersions);
- m_stream.SetSocketOption(Sockets.SocketOptionLevel.Socket,
- Sockets.SocketOptionName.KeepAlive, m_keepAlive);
- if (EncryptionMode == FtpEncryptionMode.Implicit)
- m_stream.ActivateEncryption(Host,
- m_clientCerts.Count > 0 ? m_clientCerts : null,
- m_SslProtocols);
- if (!(reply = GetReply()).Success) {
- if (reply.Code == null) {
- throw new IOException("The connection was terminated before a greeting could be read.");
- }
- else {
- throw new FtpCommandException(reply);
- }
- }
- if (EncryptionMode == FtpEncryptionMode.Explicit) {
- if (!(reply = Execute("AUTH TLS")).Success)
- throw new FtpSecurityNotAvailableException("AUTH TLS command failed.");
- m_stream.ActivateEncryption(Host,
- m_clientCerts.Count > 0 ? m_clientCerts : null,
- m_SslProtocols);
- }
- if (m_credentials != null) {
- Authenticate();
- }
- if (m_stream.IsEncrypted && DataConnectionEncryption) {
- if (!(reply = Execute("PBSZ 0")).Success)
- throw new FtpCommandException(reply);
- if (!(reply = Execute("PROT P")).Success)
- throw new FtpCommandException(reply);
- }
- // if this is a clone these values
- // should have already been loaded
- // so save some bandwidth and CPU
- // time and skip executing this again.
- if (!IsClone) {
- if ((reply = Execute("FEAT")).Success && reply.InfoMessages != null) {
- GetFeatures(reply);
- }
- }
- // Enable UTF8 if the encoding is ASCII and UTF8 is supported
- if (m_textEncoding == Encoding.ASCII && HasFeature(FtpCapability.UTF8)) {
- m_textEncoding = Encoding.UTF8;
- }
- FtpTrace.WriteLine("Text encoding: " + m_textEncoding.ToString());
- if (m_textEncoding == Encoding.UTF8) {
- // If the server supports UTF8 it should already be enabled and this
- // command should not matter however there are conflicting drafts
- // about this so we'll just execute it to be safe.
- Execute("OPTS UTF8 ON");
- }
- }
- }
- /// <summary>
- /// Performs a login on the server. This method is overridable so
- /// that the login procedure can be changed to support, for example,
- /// a FTP proxy.
- /// </summary>
- protected virtual void Authenticate() {
- FtpReply reply;
- if (!(reply = Execute("USER {0}", Credentials.UserName)).Success)
- throw new FtpCommandException(reply);
- if (reply.Type == FtpResponseType.PositiveIntermediate
- && !(reply = Execute("PASS {0}", Credentials.Password)).Success)
- throw new FtpCommandException(reply);
- }
- /// <summary>
- /// Populates the capabilities flags based on capabilities
- /// supported by this server. This method is overridable
- /// so that new features can be supported
- /// </summary>
- /// <param name="reply">The reply object from the FEAT command. The InfoMessages property will
- /// contain a list of the features the server supported delimited by a new line '\n' character.</param>
- protected virtual void GetFeatures(FtpReply reply) {
- foreach (string feat in reply.InfoMessages.Split('\n')) {
- if (feat.ToUpper().Trim().StartsWith("MLST") || feat.ToUpper().Trim().StartsWith("MLSD"))
- m_caps |= FtpCapability.MLSD;
- else if (feat.ToUpper().Trim().StartsWith("MDTM"))
- m_caps |= FtpCapability.MDTM;
- else if (feat.ToUpper().Trim().StartsWith("REST STREAM"))
- m_caps |= FtpCapability.REST;
- else if (feat.ToUpper().Trim().StartsWith("SIZE"))
- m_caps |= FtpCapability.SIZE;
- else if (feat.ToUpper().Trim().StartsWith("UTF8"))
- m_caps |= FtpCapability.UTF8;
- else if (feat.ToUpper().Trim().StartsWith("PRET"))
- m_caps |= FtpCapability.PRET;
- else if (feat.ToUpper().Trim().StartsWith("MFMT"))
- m_caps |= FtpCapability.MFMT;
- else if (feat.ToUpper().Trim().StartsWith("MFCT"))
- m_caps |= FtpCapability.MFCT;
- else if (feat.ToUpper().Trim().StartsWith("MFF"))
- m_caps |= FtpCapability.MFF;
- else if (feat.ToUpper().Trim().StartsWith("MD5"))
- m_caps |= FtpCapability.MD5;
- else if (feat.ToUpper().Trim().StartsWith("XMD5"))
- m_caps |= FtpCapability.XMD5;
- else if (feat.ToUpper().Trim().StartsWith("XCRC"))
- m_caps |= FtpCapability.XCRC;
- else if (feat.ToUpper().Trim().StartsWith("XSHA1"))
- m_caps |= FtpCapability.XSHA1;
- else if (feat.ToUpper().Trim().StartsWith("XSHA256"))
- m_caps |= FtpCapability.XSHA256;
- else if (feat.ToUpper().Trim().StartsWith("XSHA512"))
- m_caps |= FtpCapability.XSHA512;
- else if (feat.ToUpper().Trim().StartsWith("HASH")) {
- Match m;
- m_caps |= FtpCapability.HASH;
- if ((m = Regex.Match(feat.ToUpper().Trim(), @"^HASH\s+(?<types>.*)$")).Success) {
- foreach (string type in m.Groups["types"].Value.Split(';')) {
- switch (type.ToUpper().Trim()) {
- case "SHA-1":
- case "SHA-1*":
- m_hashAlgorithms |= FtpHashAlgorithm.SHA1;
- break;
- case "SHA-256":
- case "SHA-256*":
- m_hashAlgorithms |= FtpHashAlgorithm.SHA256;
- break;
- case "SHA-512":
- case "SHA-512*":
- m_hashAlgorithms |= FtpHashAlgorithm.SHA512;
- break;
- case "MD5":
- case "MD5*":
- m_hashAlgorithms |= FtpHashAlgorithm.MD5;
- break;
- case "CRC":
- case "CRC*":
- m_hashAlgorithms |= FtpHashAlgorithm.CRC;
- break;
- }
- }
- }
- }
- }
- }
- delegate void AsyncConnect();
- /// <summary>
- /// Initiates a connection to the server
- /// </summary>
- /// <param name="callback">AsyncCallback method</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginConnect.cs" lang="cs" /></example>
- public IAsyncResult BeginConnect(AsyncCallback callback, object state) {
- AsyncConnect func;
- IAsyncResult ar;
- ar = (func = new AsyncConnect(Connect)).BeginInvoke(callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends an asynchronous connection attempt to the server
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginConnect()</param>
- /// <example><code source="..\Examples\BeginConnect.cs" lang="cs" /></example>
- public void EndConnect(IAsyncResult ar) {
- GetAsyncDelegate<AsyncConnect>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Catches the socket stream ssl validation event and fires the event handlers
- /// attached to this object for validating SSL certificates
- /// </summary>
- /// <param name="stream">The stream that fired the event</param>
- /// <param name="e">The event args used to validate the certficate</param>
- void FireValidateCertficate(FtpSocketStream stream, FtpSslValidationEventArgs e) {
- OnValidateCertficate(e);
- }
- /// <summary>
- /// Disconnect from the server
- /// </summary>
- public virtual void Disconnect() {
- lock (m_lock)
- {
- if (m_stream != null && m_stream.IsConnected) {
- try {
- if (!UngracefullDisconnection) {
- Execute("QUIT");
- }
- }
- catch (SocketException sockex) {
- FtpTrace.WriteLine("FtpClient.Disconnect(): SocketException caught and discarded while closing control connection: {0}", sockex.ToString());
- }
- catch (IOException ioex) {
- FtpTrace.WriteLine("FtpClient.Disconnect(): IOException caught and discarded while closing control connection: {0}", ioex.ToString());
- }
- catch (FtpCommandException cmdex) {
- FtpTrace.WriteLine("FtpClient.Disconnect(): FtpCommandException caught and discarded while closing control connection: {0}", cmdex.ToString());
- }
- catch (FtpException ftpex) {
- FtpTrace.WriteLine("FtpClient.Disconnect(): FtpException caught and discarded while closing control connection: {0}", ftpex.ToString());
- }
- finally {
- m_stream.Close();
- }
- }
- }
- }
- delegate void AsyncDisconnect();
- /// <summary>
- /// Initiates a disconnection on the server
- /// </summary>
- /// <param name="callback">AsyncCallback method</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginDisconnect.cs" lang="cs" /></example>
- public IAsyncResult BeginDisconnect(AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncDisconnect func;
- ar = (func = new AsyncDisconnect(Disconnect)).BeginInvoke(callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginDisconnect
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginDisconnect</param>
- /// <example><code source="..\Examples\BeginConnect.cs" lang="cs" /></example>
- public void EndDisconnect(IAsyncResult ar) {
- GetAsyncDelegate<AsyncDisconnect>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Opens the specified type of passive data stream
- /// </summary>
- /// <param name="type">Type of passive data stream to open</param>
- /// <param name="command">The command to execute that requires a data stream</param>
- /// <param name="restart">Restart location in bytes for file transfer</param>
- /// <returns>A data stream ready to be used</returns>
- FtpDataStream OpenPassiveDataStream(FtpDataConnectionType type, string command, long restart) {
- FtpDataStream stream = null;
- FtpReply reply;
- Match m;
- string host = null;
- int port = 0;
- if (m_stream == null)
- 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.");
- if (type == FtpDataConnectionType.EPSV || type == FtpDataConnectionType.AutoPassive) {
- if (!(reply = Execute("EPSV")).Success) {
- // if we're connected with IPv4 and data channel type is AutoPassive then fallback to IPv4
- if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoPassive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == Sockets.AddressFamily.InterNetwork)
- return OpenPassiveDataStream(FtpDataConnectionType.PASV, command, restart);
- throw new FtpCommandException(reply);
- }
- m = Regex.Match(reply.Message, @"\(\|\|\|(?<port>\d+)\|\)");
- if (!m.Success) {
- throw new FtpException("Failed to get the EPSV port from: " + reply.Message);
- }
- host = m_host;
- port = int.Parse(m.Groups["port"].Value);
- }
- else {
- if (m_stream.LocalEndPoint.AddressFamily != Sockets.AddressFamily.InterNetwork)
- throw new FtpException("Only IPv4 is supported by the PASV command. Use EPSV instead.");
- if (!(reply = Execute("PASV")).Success)
- throw new FtpCommandException(reply);
- m = Regex.Match(reply.Message,
- @"(?<quad1>\d+)," +
- @"(?<quad2>\d+)," +
- @"(?<quad3>\d+)," +
- @"(?<quad4>\d+)," +
- @"(?<port1>\d+)," +
- @"(?<port2>\d+)"
- );
- if (!m.Success || m.Groups.Count != 7)
- throw new FtpException(string.Format("Malformed PASV response: {0}", reply.Message));
- // PASVEX mode ignores the host supplied in the PASV response
- if (type == FtpDataConnectionType.PASVEX)
- host = m_host;
- else
- host = string.Format("{0}.{1}.{2}.{3}",
- m.Groups["quad1"].Value,
- m.Groups["quad2"].Value,
- m.Groups["quad3"].Value,
- m.Groups["quad4"].Value);
- port = (int.Parse(m.Groups["port1"].Value) << 8) + int.Parse(m.Groups["port2"].Value);
- }
- stream = new FtpDataStream(this);
- stream.ConnectTimeout = DataConnectionConnectTimeout;
- stream.ReadTimeout = DataConnectionReadTimeout;
- stream.Connect(host, port, InternetProtocolVersions);
- stream.SetSocketOption(Sockets.SocketOptionLevel.Socket, Sockets.SocketOptionName.KeepAlive, m_keepAlive);
- if (restart > 0) {
- if (!(reply = Execute("REST {0}", restart)).Success)
- throw new FtpCommandException(reply);
- }
- if (!(reply = Execute(command)).Success) {
- stream.Close();
- throw new FtpCommandException(reply);
- }
- // the command status is used to determine
- // if a reply needs to be read from the server
- // when the stream is closed so always set it
- // otherwise things can get out of sync.
- stream.CommandStatus = reply;
- // this needs to take place after the command is executed
- if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None)
- stream.ActivateEncryption(m_host,
- this.ClientCertificates.Count > 0 ? this.ClientCertificates : null,
- m_SslProtocols);
- return stream;
- }
- /// <summary>
- /// Opens the specified type of active data stream
- /// </summary>
- /// <param name="type">Type of passive data stream to open</param>
- /// <param name="command">The command to execute that requires a data stream</param>
- /// <param name="restart">Restart location in bytes for file transfer</param>
- /// <returns>A data stream ready to be used</returns>
- FtpDataStream OpenActiveDataStream(FtpDataConnectionType type, string command, long restart) {
- FtpDataStream stream = new FtpDataStream(this);
- FtpReply reply;
- IAsyncResult ar;
- if (m_stream == null)
- 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.");
- stream.Listen(m_stream.LocalEndPoint.Address, 0);
- ar = stream.BeginAccept(null, null);
- if (type == FtpDataConnectionType.EPRT || type == FtpDataConnectionType.AutoActive) {
- int ipver = 0;
- switch (stream.LocalEndPoint.AddressFamily) {
- case Sockets.AddressFamily.InterNetwork:
- ipver = 1; // IPv4
- break;
- case Sockets.AddressFamily.InterNetworkV6:
- ipver = 2; // IPv6
- break;
- default:
- throw new InvalidOperationException("The IP protocol being used is not supported.");
- }
- if (!(reply = Execute("EPRT |{0}|{1}|{2}|", ipver,
- stream.LocalEndPoint.Address.ToString(), stream.LocalEndPoint.Port)).Success) {
- // if we're connected with IPv4 and the data channel type is AutoActive then try to fall back to the PORT command
- if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoActive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == Sockets.AddressFamily.InterNetwork) {
- 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.
- stream.Close();
- return OpenActiveDataStream(FtpDataConnectionType.PORT, command, restart);
- }
- else {
- stream.Close();
- throw new FtpCommandException(reply);
- }
- }
- }
- else {
- if (m_stream.LocalEndPoint.AddressFamily != Sockets.AddressFamily.InterNetwork)
- throw new FtpException("Only IPv4 is supported by the PORT command. Use EPRT instead.");
- if (!(reply = Execute("PORT {0},{1},{2}",
- stream.LocalEndPoint.Address.ToString().Replace('.', ','),
- stream.LocalEndPoint.Port / 256,
- stream.LocalEndPoint.Port % 256)).Success) {
- stream.Close();
- throw new FtpCommandException(reply);
- }
- }
- if (restart > 0) {
- if (!(reply = Execute("REST {0}", restart)).Success)
- throw new FtpCommandException(reply);
- }
- if (!(reply = Execute(command)).Success) {
- stream.Close();
- throw new FtpCommandException(reply);
- }
- // the command status is used to determine
- // if a reply needs to be read from the server
- // when the stream is closed so always set it
- // otherwise things can get out of sync.
- stream.CommandStatus = reply;
- ar.AsyncWaitHandle.WaitOne(m_dataConnectionConnectTimeout);
- if (!ar.IsCompleted) {
- stream.Close();
- throw new TimeoutException("Timed out waiting for the server to connect to the active data socket.");
- }
- stream.EndAccept(ar);
- if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None)
- stream.ActivateEncryption(m_host,
- this.ClientCertificates.Count > 0 ? this.ClientCertificates : null,
- m_SslProtocols);
- stream.SetSocketOption(Sockets.SocketOptionLevel.Socket, Sockets.SocketOptionName.KeepAlive, m_keepAlive);
- stream.ReadTimeout = m_dataConnectionReadTimeout;
- return stream;
- }
- /// <summary>
- /// Opens a data stream.
- /// </summary>
- /// <param name='command'>The command to execute that requires a data stream</param>
- /// <param name="restart">Restart location in bytes for file transfer</param>
- /// <returns>The data stream.</returns>
- FtpDataStream OpenDataStream(string command, long restart) {
- FtpDataConnectionType type = m_dataConnectionType;
- FtpDataStream stream = null;
- lock (m_lock)
- {
- if (!IsConnected)
- Connect();
- // The PORT and PASV commands do not work with IPv6 so
- // if either one of those types are set change them
- // to EPSV or EPRT appropriately.
- if (m_stream.LocalEndPoint.AddressFamily == Sockets.AddressFamily.InterNetworkV6) {
- switch (type) {
- case FtpDataConnectionType.PORT:
- type = FtpDataConnectionType.EPRT;
- FtpTrace.WriteLine("Changed data connection type to EPRT because we are connected with IPv6.");
- break;
- case FtpDataConnectionType.PASV:
- case FtpDataConnectionType.PASVEX:
- type = FtpDataConnectionType.EPSV;
- FtpTrace.WriteLine("Changed data connection type to EPSV because we are connected with IPv6.");
- break;
- }
- }
- switch (type) {
- case FtpDataConnectionType.AutoPassive:
- case FtpDataConnectionType.EPSV:
- case FtpDataConnectionType.PASV:
- case FtpDataConnectionType.PASVEX:
- stream = OpenPassiveDataStream(type, command, restart);
- break;
- case FtpDataConnectionType.AutoActive:
- case FtpDataConnectionType.EPRT:
- case FtpDataConnectionType.PORT:
- stream = OpenActiveDataStream(type, command, restart);
- break;
- }
- if (stream == null)
- throw new InvalidOperationException("The specified data channel type is not implemented.");
- }
- return stream;
- }
- /// <summary>
- /// Disconnects a data stream
- /// </summary>
- /// <param name="stream">The data stream to close</param>
- internal FtpReply CloseDataStream(FtpDataStream stream) {
- FtpReply reply = new FtpReply();
- if (stream == null)
- throw new ArgumentException("The data stream parameter was null");
- lock (m_lock)
- {
- try
- {
- if (IsConnected) {
- // if the command that required the data connection was
- // not successful then there will be no reply from
- // the server, however if the command was successful
- // the server will send a reply when the data connection
- // is closed.
- if (stream.CommandStatus.Type == FtpResponseType.PositivePreliminary) {
- if (!(reply = GetReply()).Success) {
- throw new FtpCommandException(reply);
- }
- }
- }
- }
- finally {
- // if this is a clone of the original control
- // connection we should Dispose()
- if (IsClone) {
- Disconnect();
- Dispose();
- }
- }
- }
- return reply;
- }
- /// <summary>
- /// Opens the specified file for reading
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <returns>A stream for reading the file on the server</returns>
- /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
- public Stream OpenRead(string path) {
- return OpenRead(path, FtpDataType.Binary, 0);
- }
- /// <summary>
- /// Opens the specified file for reading
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <param name="type">ASCII/Binary</param>
- /// <returns>A stream for reading the file on the server</returns>
- /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
- public Stream OpenRead(string path, FtpDataType type) {
- return OpenRead(path, type, 0);
- }
- /// <summary>
- /// Opens the specified file for reading
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <param name="restart">Resume location</param>
- /// <returns>A stream for reading the file on the server</returns>
- /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
- public Stream OpenRead(string path, long restart) {
- return OpenRead(path, FtpDataType.Binary, restart);
- }
- /// <summary>
- /// Opens the specified file for reading
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <param name="type">ASCII/Binary</param>
- /// <param name="restart">Resume location</param>
- /// <returns>A stream for reading the file on the server</returns>
- /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
- public virtual Stream OpenRead(string path, FtpDataType type, long restart) {
- FtpClient client = null;
- FtpDataStream stream = null;
- long length = 0;
- lock (m_lock)
- {
- if (m_threadSafeDataChannels) {
- client = CloneConnection();
- client.Connect();
- client.SetWorkingDirectory(GetWorkingDirectory());
- }
- else {
- client = this;
- }
- client.SetDataType(type);
- length = client.GetFileSize(path);
- stream = client.OpenDataStream(string.Format("RETR {0}", path.GetFtpPath()), restart);
- }
- if (stream != null) {
- if (length > 0)
- stream.SetLength(length);
- if (restart > 0)
- stream.SetPosition(restart);
- }
- return stream;
- }
- /// <summary>
- /// Opens the specified file for reading
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
- public IAsyncResult BeginOpenRead(string path, AsyncCallback callback, object state) {
- return BeginOpenRead(path, FtpDataType.Binary, 0, callback, state);
- }
- /// <summary>
- /// Opens the specified file for reading
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <param name="type">ASCII/Binary</param>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
- public IAsyncResult BeginOpenRead(string path, FtpDataType type, AsyncCallback callback, object state) {
- return BeginOpenRead(path, type, 0, callback, state);
- }
- /// <summary>
- /// Opens the specified file for reading
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <param name="restart">Resume location</param>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
- public IAsyncResult BeginOpenRead(string path, long restart, AsyncCallback callback, object state) {
- return BeginOpenRead(path, FtpDataType.Binary, restart, callback, state);
- }
- delegate Stream AsyncOpenRead(string path, FtpDataType type, long restart);
- /// <summary>
- /// Opens the specified file for reading
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <param name="type">ASCII/Binary</param>
- /// <param name="restart">Resume location</param>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
- public IAsyncResult BeginOpenRead(string path, FtpDataType type, long restart, AsyncCallback callback, object state) {
- AsyncOpenRead func;
- IAsyncResult ar;
- ar = (func = new AsyncOpenRead(OpenRead)).BeginInvoke(path, type, restart, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginOpenRead()
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginOpenRead()</param>
- /// <returns>A readable stream</returns>
- /// <example><code source="..\Examples\BeginOpenRead.cs" lang="cs" /></example>
- public Stream EndOpenRead(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncOpenRead>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Opens the specified file for writing
- /// </summary>
- /// <param name="path">Full or relative path of the file</param>
- /// <returns>A stream for writing to the file on the server</returns>
- /// <example><code source="..\Examples\OpenWrite.cs" lang="cs" /></example>
- public Stream OpenWrite(string path) {
- return OpenWrite(path, FtpDataType.Binary);
- }
- /// <summary>
- /// Opens the specified file for writing
- /// </summary>
- /// <param name="path">Full or relative path of the file</param>
- /// <param name="type">ASCII/Binary</param>
- /// <returns>A stream for writing to the file on the server</returns>
- /// <example><code source="..\Examples\OpenWrite.cs" lang="cs" /></example>
- public virtual Stream OpenWrite(string path, FtpDataType type) {
- FtpClient client = null;
- FtpDataStream stream = null;
- long length = 0;
- lock (m_lock)
- {
- if (m_threadSafeDataChannels) {
- client = CloneConnection();
- client.Connect();
- client.SetWorkingDirectory(GetWorkingDirectory());
- }
- else {
- client = this;
- }
- client.SetDataType(type);
- length = client.GetFileSize(path);
- stream = client.OpenDataStream(string.Format("STOR {0}", path.GetFtpPath()), 0);
- if (length > 0 && stream != null)
- stream.SetLength(length);
- }
- return stream;
- }
- /// <summary>
- /// Opens the specified file for writing
- /// </summary>
- /// <param name="path">Full or relative path of the file</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginOpenWrite.cs" lang="cs" /></example>
- public IAsyncResult BeginOpenWrite(string path, AsyncCallback callback, object state) {
- return BeginOpenWrite(path, FtpDataType.Binary, callback, state);
- }
- delegate Stream AsyncOpenWrite(string path, FtpDataType type);
- /// <summary>
- /// Opens the specified file for writing
- /// </summary>
- /// <param name="path">Full or relative path of the file</param>
- /// <param name="type">ASCII/Binary</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginOpenWrite.cs" lang="cs" /></example>
- public IAsyncResult BeginOpenWrite(string path, FtpDataType type, AsyncCallback callback, object state) {
- AsyncOpenWrite func;
- IAsyncResult ar;
- ar = (func = new AsyncOpenWrite(OpenWrite)).BeginInvoke(path, type, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginOpenWrite()
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginOpenWrite()</param>
- /// <returns>A writable stream</returns>
- /// <example><code source="..\Examples\BeginOpenWrite.cs" lang="cs" /></example>
- public Stream EndOpenWrite(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncOpenWrite>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Opens the specified file to be appended to
- /// </summary>
- /// <param name="path">The full or relative path to the file to be opened</param>
- /// <returns>A stream for writing to the file on the server</returns>
- /// <example><code source="..\Examples\OpenAppend.cs" lang="cs" /></example>
- public Stream OpenAppend(string path) {
- return OpenAppend(path, FtpDataType.Binary);
- }
- /// <summary>
- /// Opens the specified file to be appended to
- /// </summary>
- /// <param name="path">The full or relative path to the file to be opened</param>
- /// <param name="type">ASCII/Binary</param>
- /// <returns>A stream for writing to the file on the server</returns>
- /// <example><code source="..\Examples\OpenAppend.cs" lang="cs" /></example>
- public virtual Stream OpenAppend(string path, FtpDataType type) {
- FtpClient client = null;
- FtpDataStream stream = null;
- long length = 0;
- lock (m_lock)
- {
- if (m_threadSafeDataChannels) {
- client = CloneConnection();
- client.Connect();
- client.SetWorkingDirectory(GetWorkingDirectory());
- }
- else {
- client = this;
- }
- client.SetDataType(type);
- length = client.GetFileSize(path);
- stream = client.OpenDataStream(string.Format("APPE {0}", path.GetFtpPath()), 0);
- if (length > 0 && stream != null) {
- stream.SetLength(length);
- stream.SetPosition(length);
- }
- }
- return stream;
- }
- /// <summary>
- /// Opens the specified file for writing
- /// </summary>
- /// <param name="path">Full or relative path of the file</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginOpenAppend.cs" lang="cs" /></example>
- public IAsyncResult BeginOpenAppend(string path, AsyncCallback callback, object state) {
- return BeginOpenAppend(path, FtpDataType.Binary, callback, state);
- }
- delegate Stream AsyncOpenAppend(string path, FtpDataType type);
- /// <summary>
- /// Opens the specified file for writing
- /// </summary>
- /// <param name="path">Full or relative path of the file</param>
- /// <param name="type">ASCII/Binary</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginOpenAppend.cs" lang="cs" /></example>
- public IAsyncResult BeginOpenAppend(string path, FtpDataType type, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncOpenAppend func;
- ar = (func = new AsyncOpenAppend(OpenAppend)).BeginInvoke(path, type, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginOpenAppend()
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginOpenWrite()</param>
- /// <returns>A writable stream</returns>
- /// <example><code source="..\Examples\BeginOpenAppend.cs" lang="cs" /></example>
- public Stream EndOpenAppend(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncOpenAppend>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Recursively dereferences a symbolic link. See the
- /// MaximumDereferenceCount property for controlling
- /// how deep this method will recurse before giving up.
- /// </summary>
- /// <param name="item">The symbolic link</param>
- /// <returns>FtpListItem, null if the link can't be dereferenced</returns>
- /// <example><code source="..\Examples\DereferenceLink.cs" lang="cs" /></example>
- public FtpListItem DereferenceLink(FtpListItem item) {
- return DereferenceLink(item, MaximumDereferenceCount);
- }
- /// <summary>
- /// Recursively dereferences a symbolic link
- /// </summary>
- /// <param name="item">The symbolic link</param>
- /// <param name="recMax">The maximum depth of recursion that can be performed before giving up.</param>
- /// <returns>FtpListItem, null if the link can't be dereferenced</returns>
- /// <example><code source="..\Examples\DereferenceLink.cs" lang="cs" /></example>
- public FtpListItem DereferenceLink(FtpListItem item, int recMax) {
- int count = 0;
- return DereferenceLink(item, recMax, ref count);
- }
- /// <summary>
- /// Derefence a FtpListItem object
- /// </summary>
- /// <param name="item">The item to derefence</param>
- /// <param name="recMax">Maximum recursive calls</param>
- /// <param name="count">Counter</param>
- /// <returns>FtpListItem, null if the link can't be dereferenced</returns>
- /// <example><code source="..\Examples\DereferenceLink.cs" lang="cs" /></example>
- FtpListItem DereferenceLink(FtpListItem item, int recMax, ref int count) {
- if (item.Type != FtpFileSystemObjectType.Link)
- throw new FtpException("You can only derefernce a symbolic link. Please verify the item type is Link.");
- if (item.LinkTarget == null)
- throw new FtpException("The link target was null. Please check this before trying to dereference the link.");
- foreach (FtpListItem obj in GetListing(item.LinkTarget.GetFtpDirectoryName(), FtpListOption.ForceList)) {
- if (item.LinkTarget == obj.FullName) {
- if (obj.Type == FtpFileSystemObjectType.Link) {
- if (++count == recMax)
- return null;
- return DereferenceLink(obj, recMax, ref count);
- }
- if (HasFeature(FtpCapability.MDTM)) {
- DateTime modify = GetModifiedTime(obj.FullName);
- if (modify != DateTime.MinValue)
- obj.Modified = modify;
- }
- if (obj.Type == FtpFileSystemObjectType.File && obj.Size < 0 && HasFeature(FtpCapability.SIZE))
- obj.Size = GetFileSize(obj.FullName);
- return obj;
- }
- }
- return null;
- }
- delegate FtpListItem AsyncDereferenceLink(FtpListItem item, int recMax);
- /// <summary>
- /// Derefence a FtpListItem object asynchronously
- /// </summary>
- /// <param name="item">The item to derefence</param>
- /// <param name="recMax">Maximum recursive calls</param>
- /// <param name="callback">AsyncCallback</param>
- /// <param name="state">State Object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginDereferenceLink.cs" lang="cs" /></example>
- public IAsyncResult BeginDereferenceLink(FtpListItem item, int recMax, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncDereferenceLink func;
- ar = (func = new AsyncDereferenceLink(DereferenceLink)).BeginInvoke(item, recMax, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Derefence a FtpListItem object asynchronously. See the
- /// MaximumDereferenceCount property for controlling
- /// how deep this method will recurse before giving up.
- /// </summary>
- /// <param name="item">The item to derefence</param>
- /// <param name="callback">AsyncCallback</param>
- /// <param name="state">State Object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginDereferenceLink.cs" lang="cs" /></example>
- public IAsyncResult BeginDereferenceLink(FtpListItem item, AsyncCallback callback, object state) {
- return BeginDereferenceLink(item, MaximumDereferenceCount, callback, state);
- }
- /// <summary>
- /// Ends a call to BeginDereferenceLink
- /// </summary>
- /// <param name="ar">IAsyncResult</param>
- /// <returns>FtpListItem, null if the link can't be dereferenced</returns>
- /// <example><code source="..\Examples\BeginDereferenceLink.cs" lang="cs" /></example>
- public FtpListItem EndDereferenceLink(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncDereferenceLink>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Returns information about a file system object. You should check the Capabilities
- /// flags for the FtpCapability.MLSD flag before calling this method. Failing to do
- /// so will result in an InvalidOperationException being thrown when the server
- /// does not support machine listings. Returns null if the server response can't
- /// be parsed or the server returns a failure completion code. The error for a failure
- /// is logged with FtpTrace. No exception is thrown on error because that would negate
- /// the usefullness of this method for checking for the existence of an object.
- /// </summary>
- /// <param name="path">The path of the object to retrieve information about</param>
- /// <returns>A FtpListItem object</returns>
- public FtpListItem GetObjectInfo(string path) {
- FtpReply reply;
- string[] res;
- if ((Capabilities & FtpCapability.MLSD) != FtpCapability.MLSD) {
- throw new InvalidOperationException("The GetObjectInfo method only works on servers that support machine listings. " +
- "Please check the Capabilities flags for FtpCapability.MLSD before calling this method.");
- }
- if ((reply = Execute("MLST {0}", path)).Success) {
- res = reply.InfoMessages.Split('\n');
- if (res.Length > 1) {
- string info = "";
- for (int i = 1; i < res.Length; i++) {
- info += res[i];
- }
- return FtpListItem.Parse(null, info, m_caps);
- }
- }
- else {
- FtpTrace.WriteLine("Failed to get object info for path {0} with error {1}", path, reply.ErrorMessage);
- }
- return null;
- }
- delegate FtpListItem AsyncGetObjectInfo(string path);
- /// <summary>
- /// Returns information about a file system object. You should check the Capabilities
- /// flags for the FtpCapability.MLSD flag before calling this method. Failing to do
- /// so will result in an InvalidOperationException being thrown when the server
- /// does not support machine listings. Returns null if the server response can't
- /// be parsed or the server returns a failure completion code. The error for a failure
- /// is logged with FtpTrace. No exception is thrown on error because that would negate
- /// the usefullness of this method for checking for the existence of an object.
- /// </summary>
- /// <param name="path">Path of the item to retrieve information about</param>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- public IAsyncResult BeginGetObjectInfo(string path, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncGetObjectInfo func;
- ar = (func = new AsyncGetObjectInfo(GetObjectInfo)).BeginInvoke(path, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginGetObjectInfo
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginGetObjectInfo</param>
- /// <returns>FtpListItem if the command succeeded, null if there was a problem.</returns>
- public FtpListItem EndGetObjectInfo(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncGetObjectInfo>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Gets a file listing from the server. Each FtpListItem object returned
- /// contains information about the file that was able to be retrieved. If
- /// a DateTime property is equal to DateTime.MinValue then it means the
- /// date in question was not able to be retrieved. If the Size property
- /// is equal to 0 then it means the size of the object could also not
- /// be retrieved.
- /// </summary>
- /// <returns>An array of FtpListItem objects</returns>
- /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
- public FtpListItem[] GetListing() {
- return GetListing(null);
- }
- /// <summary>
- /// Gets a file listing from the server. Each FtpListItem object returned
- /// contains information about the file that was able to be retrieved. If
- /// a DateTime property is equal to DateTime.MinValue then it means the
- /// date in question was not able to be retrieved. If the Size property
- /// is equal to 0 then it means the size of the object could also not
- /// be retrieved.
- /// </summary>
- /// <param name="path">The path of the directory to list</param>
- /// <returns>An array of FtpListItem objects</returns>
- /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
- public FtpListItem[] GetListing(string path) {
- return GetListing(path, 0);
- }
- /// <summary>
- /// Gets a file listing from the server. Each FtpListItem object returned
- /// contains information about the file that was able to be retrieved. If
- /// a DateTime property is equal to DateTime.MinValue then it means the
- /// date in question was not able to be retrieved. If the Size property
- /// is equal to 0 then it means the size of the object could also not
- /// be retrieved.
- /// </summary>
- /// <param name="path">The path of the directory to list</param>
- /// <param name="options">Options that dictacte how a list is performed and what information is gathered.</param>
- /// <returns>An array of FtpListItem objects</returns>
- /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
- public FtpListItem[] GetListing(string path, FtpListOption options) {
- FtpListItem item = null;
- List<FtpListItem> lst = new List<FtpListItem>();
- List<string> rawlisting = new List<string>();
- string listcmd = null;
- string pwd = GetWorkingDirectory();
- string buf = null;
- if (path == null || path.Trim().Length == 0) {
- pwd = GetWorkingDirectory();
- if (pwd != null && pwd.Trim().Length > 0)
- path = pwd;
- else
- path = "./";
- }
- else if (!path.StartsWith("/") && pwd != null && pwd.Trim().Length > 0) {
- if (path.StartsWith("./"))
- path = path.Remove(0, 2);
- path = string.Format("{0}/{1}", pwd, path).GetFtpPath();
- }
- // MLSD provides a machine parsable format with more
- // accurate information than most of the UNIX long list
- // formats which translates to more effcient file listings
- // so always prefer MLSD over LIST unless the caller of this
- // method overrides it with the ForceList option
- if ((options & FtpListOption.ForceList) != FtpListOption.ForceList && HasFeature(FtpCapability.MLSD)) {
- listcmd = "MLSD";
- }
- else {
- if ((options & FtpListOption.UseLS) == FtpListOption.UseLS) {
- listcmd = "LS";
- }
- else if ((options & FtpListOption.NameList) == FtpListOption.NameList) {
- listcmd = "NLST";
- }
- else {
- string listopts = "";
- listcmd = "LIST";
- if ((options & FtpListOption.AllFiles) == FtpListOption.AllFiles)
- listopts += "a";
- if ((options & FtpListOption.Recursive) == FtpListOption.Recursive)
- listopts += "R";
- if (listopts.Length > 0)
- listcmd += " -" + listopts;
- }
- }
- if((options & FtpListOption.NoPath) != FtpListOption.NoPath) {
- listcmd = string.Format("{0} {1}", listcmd, path.GetFtpPath());
- }
- lock (m_lock)
- {
- Execute("TYPE I");
- // read in raw file listing
- using (FtpDataStream stream = OpenDataStream(listcmd, 0)) {
- try {
- while ((buf = stream.ReadLine(Encoding)) != null) {
- if (buf.Length > 0) {
- rawlisting.Add(buf);
- //列出文件明细信息
- //FtpTrace.WriteLine(buf);
- }
- }
- }
- finally {
- stream.Close();
- }
- }
- }
- for (int i = 0; i < rawlisting.Count; i++) {
- buf = rawlisting[i];
- if ((options & FtpListOption.NameList) == FtpListOption.NameList) {
- // if NLST was used we only have a file name so
- // there is nothing to parse.
- item = new FtpListItem() {
- FullName = buf
- };
- if (DirectoryExists(item.FullName))
- item.Type = FtpFileSystemObjectType.Directory;
- else
- item.Type = FtpFileSystemObjectType.File;
- lst.Add(item);
- }
- else {
- // if this is a result of LIST -R then the path will be spit out
- // before each block of objects
- if (listcmd.StartsWith("LIST") && (options & FtpListOption.Recursive) == FtpListOption.Recursive) {
- if (buf.StartsWith("/") && buf.EndsWith(":")) {
- path = buf.TrimEnd(':');
- continue;
- }
- }
- // if the next line in the listing starts with spaces
- // it is assumed to be a continuation of the current line
- if (i + 1 < rawlisting.Count && (rawlisting[i + 1].StartsWith("\t") || rawlisting[i + 1].StartsWith(" ")))
- buf += rawlisting[++i];
- item = FtpListItem.Parse(path, buf, m_caps);
- // FtpListItem.Parse() returns null if the line
- // could not be parsed
- if (item != null && (item.Name != "." && item.Name != ".."))
- lst.Add(item);
- else
- FtpTrace.WriteLine("Failed to parse file listing: " + buf);
- }
- // load extended information that wasn't available if the list options flags say to do so.
- if (item != null) {
- // try to dereference symbolic links if the appropriate list
- // option was passed
- if (item.Type == FtpFileSystemObjectType.Link && (options & FtpListOption.DerefLinks) == FtpListOption.DerefLinks) {
- item.LinkObject = DereferenceLink(item);
- }
- if ((options & FtpListOption.Modify) == FtpListOption.Modify && HasFeature(FtpCapability.MDTM)) {
- // if the modified date was not loaded or the modified date is more than a day in the future
- // and the server supports the MDTM command, load the modified date.
- // most servers do not support retrieving the modified date
- // of a directory but we try any way.
- if (item.Modified == DateTime.MinValue || listcmd.StartsWith("LIST")) {
- DateTime modify;
- if (item.Type == FtpFileSystemObjectType.Directory)
- FtpTrace.WriteLine("Trying to retrieve modification time of a directory, some servers don't like this...");
- if ((modify = GetModifiedTime(item.FullName)) != DateTime.MinValue)
- item.Modified = modify;
- }
- }
- if ((options & FtpListOption.Size) == FtpListOption.Size && HasFeature(FtpCapability.SIZE)) {
- // if no size was parsed, the object is a file and the server
- // supports the SIZE command, then load the file size
- if (item.Size == -1) {
- if (item.Type != FtpFileSystemObjectType.Directory) {
- item.Size = GetFileSize(item.FullName);
- }
- else {
- item.Size = 0;
- }
- }
- }
- }
- }
- return lst.ToArray();
- }
- /// <summary>
- /// Gets a file listing from the server asynchronously
- /// </summary>
- /// <param name="callback">AsyncCallback method</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example>
- public IAsyncResult BeginGetListing(AsyncCallback callback, Object state) {
- return BeginGetListing(null, callback, state);
- }
- /// <summary>
- /// Gets a file listing from the server asynchronously
- /// </summary>
- /// <param name="path">The path to list</param>
- /// <param name="callback">AsyncCallback method</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example>
- public IAsyncResult BeginGetListing(string path, AsyncCallback callback, Object state) {
- return BeginGetListing(path, FtpListOption.Modify | FtpListOption.Size, callback, state);
- }
- delegate FtpListItem[] AsyncGetListing(string path, FtpListOption options);
- /// <summary>
- /// Gets a file listing from the server asynchronously
- /// </summary>
- /// <param name="path">The path to list</param>
- /// <param name="options">Options that dictate how the list operation is performed</param>
- /// <param name="callback">AsyncCallback method</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example>
- public IAsyncResult BeginGetListing(string path, FtpListOption options, AsyncCallback callback, Object state) {
- IAsyncResult ar;
- AsyncGetListing func;
- ar = (func = new AsyncGetListing(GetListing)).BeginInvoke(path, options, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends an asynchronous file listing
- /// </summary>
- /// <param name="ar">IAsyncResult return from BeginGetListing()</param>
- /// <returns>An array of items retrieved in the listing</returns>
- /// <example><code source="..\Examples\BeginGetListing.cs" lang="cs" /></example>
- public FtpListItem[] EndGetListing(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncGetListing>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Returns a file/directory listing using the NLST command.
- /// </summary>
- /// <returns>A string array of file and directory names if any were returned.</returns>
- public string[] GetNameListing() {
- return GetNameListing(null);
- }
- /// <summary>
- /// Returns a file/directory listing using the NLST command.
- /// </summary>
- /// <param name="path">The path of the directory to list</param>
- /// <returns>A string array of file and directory names if any were returned.</returns>
- /// <example><code source="..\Examples\GetNameListing.cs" lang="cs" /></example>
- public string[] GetNameListing(string path) {
- List<string> lst = new List<string>();
- string pwd = GetWorkingDirectory();
- /*if (path == null || path.GetFtpPath().Trim().Length == 0 || path.StartsWith(".")) {
- if (pwd == null || pwd.Length == 0) // couldn't get the working directory
- path = "./";
- else if (path.StartsWith("./"))
- path = string.Format("{0}/{1}", pwd, path.Remove(0, 2));
- else
- path = pwd;
- }*/
- path = path.GetFtpPath();
- if (path == null || path.Trim().Length == 0) {
- if (pwd != null && pwd.Trim().Length > 0)
- path = pwd;
- else
- path = "./";
- }
- else if (!path.StartsWith("/") && pwd != null && pwd.Trim().Length > 0) {
- if (path.StartsWith("./"))
- path = path.Remove(0, 2);
- path = string.Format("{0}/{1}", pwd, path).GetFtpPath();
- }
- lock (m_lock)
- {
- // always get the file listing in binary
- // to avoid any potential character translation
- // problems that would happen if in ASCII.
- Execute("TYPE I");
- using (FtpDataStream stream = OpenDataStream(string.Format("NLST {0}", path.GetFtpPath()), 0)) {
- string buf;
- try {
- while ((buf = stream.ReadLine(Encoding)) != null)
- lst.Add(buf);
- }
- finally {
- stream.Close();
- }
- }
- }
- return lst.ToArray();
- }
- delegate string[] AsyncGetNameListing(string path);
- /// <summary>
- /// Asynchronously gets a list of file and directory names for the specified path.
- /// </summary>
- /// <param name="path">The path of the directory to list</param>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginGetNameListing.cs" lang="cs" /></example>
- public IAsyncResult BeginGetNameListing(string path, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncGetNameListing func;
- ar = (func = new AsyncGetNameListing(GetNameListing)).BeginInvoke(path, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Asynchronously gets a list of file and directory names for the specified path.
- /// </summary>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginGetNameListing.cs" lang="cs" /></example>
- public IAsyncResult BeginGetNameListing(AsyncCallback callback, object state) {
- return BeginGetNameListing(null, callback, state);
- }
- /// <summary>
- /// Ends a call to BeginGetNameListing()
- /// </summary>
- /// <param name="ar">IAsyncResult object returned from BeginGetNameListing</param>
- /// <returns>An array of file and directory names if any were returned.</returns>
- /// <example><code source="..\Examples\BeginGetNameListing.cs" lang="cs" /></example>
- public string[] EndGetNameListing(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncGetNameListing>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Sets the data type of information sent over the data stream
- /// </summary>
- /// <param name="type">ASCII/Binary</param>
- protected void SetDataType(FtpDataType type) {
- FtpReply reply;
- lock (m_lock)
- {
- switch (type) {
- case FtpDataType.ASCII:
- if (!(reply = Execute("TYPE A")).Success)
- throw new FtpCommandException(reply);
- /*if (!(reply = Execute("STRU R")).Success)
- FtpTrace.WriteLine(reply.Message);*/
- break;
- case FtpDataType.Binary:
- if (!(reply = Execute("TYPE I")).Success)
- throw new FtpCommandException(reply);
- /*if (!(reply = Execute("STRU F")).Success)
- FtpTrace.WriteLine(reply.Message);*/
- break;
- default:
- throw new FtpException("Unsupported data type: " + type.ToString());
- }
- }
- }
- delegate void AsyncSetDataType(FtpDataType type);
- /// <summary>
- /// Asynchronously sets the data type on the server
- /// </summary>
- /// <param name="type">ASCII/Binary</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- protected IAsyncResult BeginSetDataType(FtpDataType type, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncSetDataType func;
- ar = (func = new AsyncSetDataType(SetDataType)).BeginInvoke(type, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginSetDataType()
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginSetDataType()</param>
- protected void EndSetDataType(IAsyncResult ar) {
- GetAsyncDelegate<AsyncSetDataType>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Sets the work directory on the server
- /// </summary>
- /// <param name="path">The path of the directory to change to</param>
- /// <example><code source="..\Examples\SetWorkingDirectory.cs" lang="cs" /></example>
- public void SetWorkingDirectory(string path) {
- FtpReply reply;
- string ftppath = path.GetFtpPath();
- if (ftppath == "." || ftppath == "./")
- return;
- lock (m_lock)
- {
- if (!(reply = Execute("CWD {0}", ftppath)).Success)
- throw new FtpCommandException(reply);
- }
- }
- delegate void AsyncSetWorkingDirectory(string path);
- /// <summary>
- /// Asynchronously changes the working directory on the server
- /// </summary>
- /// <param name="path">The directory to change to</param>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginSetWorkingDirectory.cs" lang="cs" /></example>
- public IAsyncResult BeginSetWorkingDirectory(string path, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncSetWorkingDirectory func;
- ar = (func = new AsyncSetWorkingDirectory(SetWorkingDirectory)).BeginInvoke(path, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends asynchronous directory change
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginSetWorkingDirectory</param>
- /// <example><code source="..\Examples\BeginSetWorkingDirectory.cs" lang="cs" /></example>
- public void EndSetWorkingDirectory(IAsyncResult ar) {
- GetAsyncDelegate<AsyncSetWorkingDirectory>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// 获取当前工作目录
- /// </summary>
- /// <returns>The current working directory, ./ if the response couldn't be parsed.</returns>
- /// <example><code source="..\Examples\GetWorkingDirectory.cs" lang="cs" /></example>
- public string GetWorkingDirectory() {
- FtpReply reply;
- Match m;
- lock (m_lock)
- {
- if (!(reply = Execute("PWD")).Success)
- throw new FtpCommandException(reply);
- }
- if ((m = Regex.Match(reply.Message, "\"(?<pwd>.*)\"")).Success) {
- return m.Groups["pwd"].Value;
- }
- // check for MODCOMP ftp path mentioned in forums: https://netftp.codeplex.com/discussions/444461
- if ((m = Regex.Match(reply.Message, "PWD = (?<pwd>.*)")).Success) {
- return m.Groups["pwd"].Value;
- }
- FtpTrace.WriteLine("无法解析工作目录: " + reply.Message);
- return "./";
- }
- delegate string AsyncGetWorkingDirectory();
- /// <summary>
- /// Asynchronously retrieves the working directory
- /// </summary>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginGetWorkingDirectory.cs" lang="cs" /></example>
- public IAsyncResult BeginGetWorkingDirectory(AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncGetWorkingDirectory func;
- ar = (func = new AsyncGetWorkingDirectory(GetWorkingDirectory)).BeginInvoke(callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends an asynchronous call to retrieve the working directory
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginGetWorkingDirectory</param>
- /// <returns>The current working directory</returns>
- /// <example><code source="..\Examples\BeginGetWorkingDirectory.cs" lang="cs" /></example>
- public string EndGetWorkingDirectory(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncGetWorkingDirectory>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Gets the size of the file
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <returns>-1 if the command fails, otherwise the file size</returns>
- /// <example><code source="..\Examples\GetFileSize.cs" lang="cs" /></example>
- public virtual long GetFileSize(string path) {
- FtpReply reply;
- long length = 0;
- lock (m_lock)
- {
- if (!(reply = Execute("SIZE {0}", path.GetFtpPath())).Success)
- return -1;
- if (!long.TryParse(reply.Message, out length))
- return -1;
- }
- return length;
- }
- delegate long AsyncGetFileSize(string path);
- /// <summary>
- /// Asynchronously retrieve the size of the specified file
- /// </summary>
- /// <param name="path">The full or relative path of the file</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginGetFileSize.cs" lang="cs" /></example>
- public IAsyncResult BeginGetFileSize(string path, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncGetFileSize func;
- ar = (func = new AsyncGetFileSize(GetFileSize)).BeginInvoke(path, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginGetFileSize()
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginGetFileSize</param>
- /// <returns>The size of the file, -1 if there was a problem.</returns>
- /// <example><code source="..\Examples\BeginGetFileSize.cs" lang="cs" /></example>
- public long EndGetFileSize(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncGetFileSize>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Gets the modified time of the file
- /// </summary>
- /// <param name="path">The full path to the file</param>
- /// <returns>The modified time, DateTime.MinValue if there was a problem</returns>
- /// <example><code source="..\Examples\GetModifiedTime.cs" lang="cs" /></example>
- public virtual DateTime GetModifiedTime(string path) {
- DateTime modify = DateTime.MinValue;
- FtpReply reply;
- lock (m_lock)
- {
- if ((reply = Execute("MDTM {0}", path.GetFtpPath())).Success)
- modify = reply.Message.GetFtpDate(DateTimeStyles.AssumeUniversal);
- }
- return modify;
- }
- delegate DateTime AsyncGetModifiedTime(string path);
- /// <summary>
- /// Gets the modified time of the file
- /// </summary>
- /// <param name="path">The full path to the file</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginGetModifiedTime.cs" lang="cs" /></example>
- public IAsyncResult BeginGetModifiedTime(string path, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncGetModifiedTime func;
- ar = (func = new AsyncGetModifiedTime(GetModifiedTime)).BeginInvoke(path, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginGetModifiedTime()
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginGetModifiedTime()</param>
- /// <returns>The modified time, DateTime.MinValue if there was a problem</returns>
- /// <example><code source="..\Examples\BeginGetModifiedTime.cs" lang="cs" /></example>
- public DateTime EndGetModifiedTime(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncGetModifiedTime>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Deletes a file on the server
- /// </summary>
- /// <param name="path">The full or relative path to the file</param>
- /// <example><code source="..\Examples\DeleteFile.cs" lang="cs" /></example>
- public void DeleteFile(string path) {
- FtpReply reply;
- lock (m_lock)
- {
- if (!(reply = Execute("DELE {0}", path.GetFtpPath())).Success)
- throw new FtpCommandException(reply);
- }
- }
- delegate void AsyncDeleteFile(string path);
- /// <summary>
- /// Asynchronously deletes a file from the server
- /// </summary>
- /// <param name="path">The full or relative path to the file</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginDeleteFile.cs" lang="cs" /></example>
- public IAsyncResult BeginDeleteFile(string path, AsyncCallback callback, object state) {
- IAsyncResult ar;
- AsyncDeleteFile func;
- ar = (func = new AsyncDeleteFile(DeleteFile)).BeginInvoke(path, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginDeleteFile
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginDeleteFile</param>
- /// <example><code source="..\Examples\BeginDeleteFile.cs" lang="cs" /></example>
- public void EndDeleteFile(IAsyncResult ar) {
- GetAsyncDelegate<AsyncDeleteFile>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Deletes the specified directory on the server.
- /// </summary>
- /// <param name="path">The full or relative path of the directory to delete</param>
- /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example>
- public void DeleteDirectory(string path) {
- DeleteDirectory(path, false);
- }
- /// <summary>
- /// Delets the specified directory on the server
- /// </summary>
- /// <param name="path">The full or relative path of the directory to delete</param>
- /// <param name="force">If the directory is not empty, remove its contents</param>
- /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example>
- public void DeleteDirectory(string path, bool force) {
- DeleteDirectory(path, force, 0);
- }
- /// <summary>
- /// Deletes the specified directory on the server
- /// </summary>
- /// <param name="path">The full or relative path of the directory to delete</param>
- /// <param name="force">If the directory is not empty, remove its contents</param>
- /// <param name="options">FtpListOptions for controlling how the directory
- /// contents are retrieved with the force option is true. If you experience problems
- /// the file listing can be fine tuned through this parameter.</param>
- /// <example><code source="..\Examples\DeleteDirectory.cs" lang="cs" /></example>
- public void DeleteDirectory(string path, bool force, FtpListOption options) {
- FtpReply reply;
- string ftppath = path.GetFtpPath();
- lock (m_lock)
- {
- if (force) {
- foreach (FtpListItem item in GetListing(path, options)) {
- switch (item.Type) {
- case FtpFileSystemObjectType.File:
- DeleteFile(item.FullName);
- break;
- case FtpFileSystemObjectType.Directory:
- DeleteDirectory(item.FullName, true, options);
- break;
- default:
- throw new FtpException("Don't know how to delete object type: " + item.Type);
- }
- }
- }
- // can't delete the working directory and
- // can't delete the server root.
- if (ftppath == "." || ftppath == "./" || ftppath == "/")
- return;
- if (!(reply = Execute("RMD {0}", ftppath)).Success)
- throw new FtpCommandException(reply);
- }
- }
- delegate void AsyncDeleteDirectory(string path, bool force, FtpListOption options);
- /// <summary>
- /// Asynchronously removes a directory from the server
- /// </summary>
- /// <param name="path">The full or relative path of the directory to delete</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
- public IAsyncResult BeginDeleteDirectory(string path, AsyncCallback callback, object state) {
- return BeginDeleteDirectory(path, true, 0, callback, state);
- }
- /// <summary>
- /// Asynchronously removes a directory from the server
- /// </summary>
- /// <param name="path">The full or relative path of the directory to delete</param>
- /// <param name="force">If the directory is not empty, remove its contents</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
- public IAsyncResult BeginDeleteDirectory(string path, bool force, AsyncCallback callback, object state) {
- return BeginDeleteDirectory(path, force, 0, callback, state);
- }
- /// <summary>
- /// Asynchronously removes a directory from the server
- /// </summary>
- /// <param name="path">The full or relative path of the directory to delete</param>
- /// <param name="force">If the directory is not empty, remove its contents</param>
- /// <param name="options">FtpListOptions for controlling how the directory
- /// contents are retrieved with the force option is true. If you experience problems
- /// the file listing can be fine tuned through this parameter.</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
- public IAsyncResult BeginDeleteDirectory(string path, bool force, FtpListOption options, AsyncCallback callback, object state) {
- AsyncDeleteDirectory func;
- IAsyncResult ar;
- ar = (func = new AsyncDeleteDirectory(DeleteDirectory)).BeginInvoke(path, force, options, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginDeleteDirectory()
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginDeleteDirectory</param>
- /// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
- public void EndDeleteDirectory(IAsyncResult ar) {
- GetAsyncDelegate<AsyncDeleteDirectory>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Tests if the specified directory exists on the server. This
- /// method works by trying to change the working directory to
- /// the path specified. If it succeeds, the directory is changed
- /// back to the old working directory and true is returned. False
- /// is returned otherwise and since the CWD failed it is assumed
- /// the working directory is still the same.
- /// </summary>
- /// <param name="path">The path of the directory</param>
- /// <returns>True if it exists, false otherwise.</returns>
- /// <example><code source="..\Examples\DirectoryExists.cs" lang="cs" /></example>
- public bool DirectoryExists(string path) {
- string pwd;
- string ftppath = path.GetFtpPath();
- if (ftppath == "." || ftppath == "./" || ftppath == "/")
- return true;
- lock (m_lock)
- {
- pwd = GetWorkingDirectory();
- if (Execute("CWD {0}", ftppath).Success) {
- FtpReply reply = Execute("CWD {0}", pwd.GetFtpPath());
- if (!reply.Success)
- throw new FtpException("DirectoryExists(): Failed to restore the working directory.");
- return true;
- }
- }
- return false;
- }
- delegate bool AsyncDirectoryExists(string path);
- /// <summary>
- /// Checks if a directory exists on the server asynchronously.
- /// </summary>
- /// <returns>IAsyncResult</returns>
- /// <param name='path'>The full or relative path of the directory to check for</param>
- /// <param name='callback'>Async callback</param>
- /// <param name='state'>State object</param>
- /// <example><code source="..\Examples\BeginDirectoryExists.cs" lang="cs" /></example>
- public IAsyncResult BeginDirectoryExists(string path, AsyncCallback callback, object state) {
- AsyncDirectoryExists func;
- IAsyncResult ar;
- ar = (func = new AsyncDirectoryExists(DirectoryExists)).BeginInvoke(path, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginDirectoryExists
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginDirectoryExists</param>
- /// <returns>True if the directory exists. False otherwise.</returns>
- /// <example><code source="..\Examples\BeginDirectoryExists.cs" lang="cs" /></example>
- public bool EndDirectoryExists(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncDirectoryExists>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Checks if a file exsts on the server by taking a
- /// file listing of the parent directory in the path
- /// and comparing the results the path supplied.
- /// </summary>
- /// <param name="path">The full or relative path to the file</param>
- /// <returns>True if the file exists</returns>
- /// <example><code source="..\Examples\FileExists.cs" lang="cs" /></example>
- public bool FileExists(string path) {
- return FileExists(path, 0);
- }
- /// <summary>
- /// Checks if a file exsts on the server by taking a
- /// file listing of the parent directory in the path
- /// and comparing the results the path supplied.
- /// </summary>
- /// <param name="path">The full or relative path to the file</param>
- /// <param name="options">Options for controling the file listing used to
- /// determine if the file exists.</param>
- /// <returns>True if the file exists</returns>
- /// <example><code source="..\Examples\FileExists.cs" lang="cs" /></example>
- public bool FileExists(string path, FtpListOption options) {
- string dirname = path.GetFtpDirectoryName();
- lock (m_lock)
- {
- if (!DirectoryExists(dirname))
- return false;
- foreach (FtpListItem item in GetListing(dirname, options))
- if (item.Type == FtpFileSystemObjectType.File && item.Name == path.GetFtpFileName())
- return true;
- }
- return false;
- }
- delegate bool AsyncFileExists(string path, FtpListOption options);
- /// <summary>
- /// Checks if a file exsts on the server by taking a
- /// file listing of the parent directory in the path
- /// and comparing the results the path supplied.
- /// </summary>
- /// <param name="path">The full or relative path to the file</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginFileExists.cs" lang="cs" /></example>
- public IAsyncResult BeginFileExists(string path, AsyncCallback callback, object state) {
- return BeginFileExists(path, 0, callback, state);
- }
- /// <summary>
- /// Checks if a file exsts on the server by taking a
- /// file listing of the parent directory in the path
- /// and comparing the results the path supplied.
- /// </summary>
- /// <param name="path">The full or relative path to the file</param>
- /// <param name="options">Options for controling the file listing used to
- /// determine if the file exists.</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginFileExists.cs" lang="cs" /></example>
- public IAsyncResult BeginFileExists(string path, FtpListOption options, AsyncCallback callback, object state) {
- AsyncFileExists func;
- IAsyncResult ar;
- ar = (func = new AsyncFileExists(FileExists)).BeginInvoke(path, options, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginFileExists
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginFileExists</param>
- /// <returns>True if the file exists</returns>
- /// <example><code source="..\Examples\BeginFileExists.cs" lang="cs" /></example>
- public bool EndFileExists(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncFileExists>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Creates a directory on the server. If the preceding
- /// directories do not exist they are created.
- /// </summary>
- /// <param name="path">The full or relative path to the new directory</param>
- /// <example><code source="..\Examples\CreateDirectory.cs" lang="cs" /></example>
- public void CreateDirectory(string path) {
- CreateDirectory(path, true);
- }
- /// <summary>
- /// Creates a directory on the server
- /// </summary>
- /// <param name="path">The full or relative path to the directory to create</param>
- /// <param name="force">Try to force all non-existant pieces of the path to be created</param>
- /// <example><code source="..\Examples\CreateDirectory.cs" lang="cs" /></example>
- public void CreateDirectory(string path, bool force) {
- FtpReply reply;
- string ftppath = path.GetFtpPath();
- if (ftppath == "." || ftppath == "./" || ftppath == "/")
- return;
- lock (m_lock)
- {
- path = path.GetFtpPath().TrimEnd('/');
- if (force && !DirectoryExists(path.GetFtpDirectoryName())) {
- FtpTrace.WriteLine(string.Format(
- "CreateDirectory(\"{0}\", {1}): Create non-existent parent: {2}",
- path, force, path.GetFtpDirectoryName()));
- CreateDirectory(path.GetFtpDirectoryName(), true);
- }
- else if (DirectoryExists(path))
- return;
- FtpTrace.WriteLine(string.Format("CreateDirectory(\"{0}\", {1})",
- ftppath, force));
- if (!(reply = Execute("MKD {0}", ftppath)).Success)
- throw new FtpCommandException(reply);
- }
- }
- delegate void AsyncCreateDirectory(string path, bool force);
- /// <summary>
- /// Creates a directory asynchronously
- /// </summary>
- /// <param name="path">The full or relative path to the directory to create</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginCreateDirectory.cs" lang="cs" /></example>
- public IAsyncResult BeginCreateDirectory(string path, AsyncCallback callback, object state) {
- return BeginCreateDirectory(path, true, callback, state);
- }
- /// <summary>
- /// Creates a directory asynchronously
- /// </summary>
- /// <param name="path">The full or relative path to the directory to create</param>
- /// <param name="force">Try to create the whole path if the preceding directories do not exist</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginCreateDirectory.cs" lang="cs" /></example>
- public IAsyncResult BeginCreateDirectory(string path, bool force, AsyncCallback callback, object state) {
- AsyncCreateDirectory func;
- IAsyncResult ar;
- ar = (func = new AsyncCreateDirectory(CreateDirectory)).BeginInvoke(path, force, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginCreateDirectory
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginCreateDirectory</param>
- /// <example><code source="..\Examples\BeginCreateDirectory.cs" lang="cs" /></example>
- public void EndCreateDirectory(IAsyncResult ar) {
- GetAsyncDelegate<AsyncCreateDirectory>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Renames an object on the remote file system.
- /// </summary>
- /// <param name="path">The full or relative path to the object</param>
- /// <param name="dest">The old or new full or relative path including the new name of the object</param>
- /// <example><code source="..\Examples\Rename.cs" lang="cs" /></example>
- public void Rename(string path, string dest) {
- FtpReply reply;
- lock (m_lock)
- {
- if (!(reply = Execute("RNFR {0}", path.GetFtpPath())).Success)
- throw new FtpCommandException(reply);
- if (!(reply = Execute("RNTO {0}", dest.GetFtpPath())).Success)
- throw new FtpCommandException(reply);
- }
- }
- delegate void AsyncRename(string path, string dest);
- /// <summary>
- /// Asynchronously renames an object on the server
- /// </summary>
- /// <param name="path">The full or relative path to the object</param>
- /// <param name="dest">The old or new full or relative path including the new name of the object</param>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- /// <example><code source="..\Examples\BeginRename.cs" lang="cs" /></example>
- public IAsyncResult BeginRename(string path, string dest, AsyncCallback callback, object state) {
- AsyncRename func;
- IAsyncResult ar;
- ar = (func = new AsyncRename(Rename)).BeginInvoke(path, dest, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginRename
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginRename</param>
- /// <example><code source="..\Examples\BeginRename.cs" lang="cs" /></example>
- public void EndRename(IAsyncResult ar) {
- GetAsyncDelegate<AsyncRename>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Gets the currently selected hash algorith for the HASH
- /// command. This feature is experimental. See this link
- /// for details:
- /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
- /// </summary>
- /// <returns>The FtpHashType flag or FtpHashType.NONE if there was a problem.</returns>
- /// <example><code source="..\Examples\GetHashAlgorithm.cs" lang="cs" /></example>
- public FtpHashAlgorithm GetHashAlgorithm() {
- FtpReply reply;
- FtpHashAlgorithm type = FtpHashAlgorithm.NONE;
- lock (m_lock)
- {
- if ((reply = Execute("OPTS HASH")).Success) {
- switch (reply.Message) {
- case "SHA-1":
- type = FtpHashAlgorithm.SHA1;
- break;
- case "SHA-256":
- type = FtpHashAlgorithm.SHA256;
- break;
- case "SHA-512":
- type = FtpHashAlgorithm.SHA512;
- break;
- case "MD5":
- type = FtpHashAlgorithm.MD5;
- break;
- }
- }
- }
- return type;
- }
- delegate FtpHashAlgorithm AsyncGetHashAlgorithm();
- /// <summary>
- /// Asynchronously get the hash algorithm being used by the HASH command.
- /// </summary>
- /// <param name="callback">Async callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- public IAsyncResult BeginGetHashAlgorithm(AsyncCallback callback, object state) {
- AsyncGetHashAlgorithm func;
- IAsyncResult ar;
- ar = (func = new AsyncGetHashAlgorithm(GetHashAlgorithm)).BeginInvoke(callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends a call to BeginGetHashAlgorithm
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginGetHashAlgorithm</param>
- public FtpHashAlgorithm EndGetHashAlgorithm(IAsyncResult ar) {
- return GetAsyncDelegate<AsyncGetHashAlgorithm>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Tells the server which hash algorith to use
- /// for the HASH command. If you specifiy an
- /// algorithm not listed in FtpClient.HashTypes
- /// a NotImplemented() exectpion will be thrown
- /// so be sure to query that list of Flags before
- /// selecting a hash algorithm. Support for the
- /// HASH command is experimental. Please see
- /// the following link for more details:
- /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
- /// </summary>
- /// <param name="type">Hash Algorithm</param>
- /// <example><code source="..\Examples\SetHashAlgorithm.cs" lang="cs" /></example>
- public void SetHashAlgorithm(FtpHashAlgorithm type) {
- FtpReply reply;
- string algorithm;
- lock (m_lock)
- {
- if ((HashAlgorithms & type) != type)
- throw new NotImplementedException(string.Format("The hash algorithm {0} was not advertised by the server.", type.ToString()));
- switch (type) {
- case FtpHashAlgorithm.SHA1:
- algorithm = "SHA-1";
- break;
- case FtpHashAlgorithm.SHA256:
- algorithm = "SHA-256";
- break;
- case FtpHashAlgorithm.SHA512:
- algorithm = "SHA-512";
- break;
- case FtpHashAlgorithm.MD5:
- algorithm = "MD5";
- break;
- default:
- algorithm = type.ToString();
- break;
- }
- if (!(reply = Execute("OPTS HASH {0}", algorithm)).Success)
- throw new FtpCommandException(reply);
- }
- }
- delegate void AsyncSetHashAlgorithm(FtpHashAlgorithm type);
- /// <summary>
- /// Asynchronously sets the hash algorithm type to be used with the HASH command.
- /// </summary>
- /// <param name="type">Hash algorithm to use</param>
- /// <param name="callback">Async Callback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- public IAsyncResult BeginSetHashAlgorithm(FtpHashAlgorithm type, AsyncCallback callback, object state) {
- AsyncSetHashAlgorithm func;
- IAsyncResult ar;
- ar = (func = new AsyncSetHashAlgorithm(SetHashAlgorithm)).BeginInvoke(type, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends an asynchronous call to BeginSetHashAlgorithm
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginSetHashAlgorithm</param>
- public void EndSetHashAlgorithm(IAsyncResult ar) {
- GetAsyncDelegate<AsyncSetHashAlgorithm>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Gets the hash of an object on the server using the
- /// currently selected hash algorithm. Supported
- /// algorithms, if any, are available in the HashAlgorithms
- /// property. You should confirm that it's not equal
- /// to FtpHashAlgorithm.NONE before calling this method
- /// otherwise the server trigger a FtpCommandException()
- /// due to a lack of support for the HASH command. You can
- /// set the algorithm using the SetHashAlgorithm() method and
- /// you can query the server for the current hash algorithm
- /// using the GetHashAlgorithm() method.
- ///
- /// This feature is experimental and based on the following draft:
- /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
- /// </summary>
- /// <param name="path">Full or relative path of the object to compute the hash for.</param>
- /// <returns>The hash of the file.</returns>
- /// <example><code source="..\Examples\GetHash.cs" lang="cs" /></example>
- public FtpHash GetHash(string path) {
- FtpReply reply;
- FtpHash hash = new FtpHash();
- Match m;
- if (path == null)
- throw new ArgumentException("GetHash(path) argument can't be null");
- lock (m_lock)
- {
- if (!(reply = Execute("HASH {0}", path.GetFtpPath())).Success)
- throw new FtpCommandException(reply);
- }
- // Current draft says the server should return this:
- // SHA-256 0-49 169cd22282da7f147cb491e559e9dd filename.ext
- if (!(m = Regex.Match(reply.Message,
- @"(?<algorithm>.+)\s" +
- @"(?<bytestart>\d+)-(?<byteend>\d+)\s" +
- @"(?<hash>.+)\s" +
- @"(?<filename>.+)")).Success) {
- // Current version of FileZilla returns this:
- // SHA-1 21c2ca15cf570582949eb59fb78038b9c27ffcaf
- m = Regex.Match(reply.Message, @"(?<algorithm>.+)\s(?<hash>.+)\s");
- }
- if (m != null && m.Success) {
- switch (m.Groups["algorithm"].Value) {
- case "SHA-1":
- hash.Algorithm = FtpHashAlgorithm.SHA1;
- break;
- case "SHA-256":
- hash.Algorithm = FtpHashAlgorithm.SHA256;
- break;
- case "SHA-512":
- hash.Algorithm = FtpHashAlgorithm.SHA512;
- break;
- case "MD5":
- hash.Algorithm = FtpHashAlgorithm.MD5;
- break;
- default:
- throw new NotImplementedException("Unknown hash algorithm: " + m.Groups["algorithm"].Value);
- }
- hash.Value = m.Groups["hash"].Value;
- }
- else {
- FtpTrace.WriteLine("Failed to parse hash from: {0}", reply.Message);
- }
- return hash;
- }
- delegate FtpHash AsyncGetHash(string path);
- /// <summary>
- /// Asynchronously retrieves the hash for the specified file
- /// </summary>
- /// <param name="path">The file you want the server to compute the hash for</param>
- /// <param name="callback">AsyncCallback</param>
- /// <param name="state">State object</param>
- /// <returns>IAsyncResult</returns>
- public IAsyncResult BeginGetHash(string path, AsyncCallback callback, object state) {
- AsyncGetHash func;
- IAsyncResult ar;
- ar = (func = new AsyncGetHash(GetHash)).BeginInvoke(path, callback, state);
- lock (m_asyncmethods) {
- m_asyncmethods.Add(ar, func);
- }
- return ar;
- }
- /// <summary>
- /// Ends an asynchronous call to BeginGetHash
- /// </summary>
- /// <param name="ar">IAsyncResult returned from BeginGetHash</param>
- public void EndGetHash(IAsyncResult ar) {
- GetAsyncDelegate<AsyncGetHash>(ar).EndInvoke(ar);
- }
- /// <summary>
- /// Disables UTF8 support and changes the Encoding property
- /// back to ASCII. If the server returns an error when trying
- /// to turn UTF8 off a FtpCommandException will be thrown.
- /// </summary>
- public void DisableUTF8() {
- FtpReply reply;
- lock (m_lock)
- {
- if (!(reply = Execute("OPTS UTF8 OFF")).Success)
- throw new FtpCommandException(reply);
- m_textEncoding = Encoding.ASCII;
- }
- }
- /// <summary>
- /// Disconnects from the server, releases resources held by this
- /// object.
- /// </summary>
- public void Dispose() {
- lock (m_lock)
- {
- if (IsDisposed)
- return;
- FtpTrace.WriteLine("Disposing FtpClient object...");
- try
- {
- if (IsConnected)
- {
- Disconnect();
- }
- }
- catch (Exception ex)
- {
- FtpTrace.WriteLine("FtpClient.Dispose(): Caught and discarded an exception while disconnecting from host: {0}", ex.ToString());
- }
- if (m_stream != null)
- {
- try
- {
- m_stream.Dispose();
- }
- catch (Exception ex)
- {
- FtpTrace.WriteLine("FtpClient.Dispose(): Caught and discarded an exception while disposing FtpStream object: {0}", ex.ToString());
- }
- finally
- {
- m_stream = null;
- }
- }
- m_credentials = null;
- m_textEncoding = null;
- m_host = null;
- m_asyncmethods.Clear();
- IsDisposed = true;
- GC.SuppressFinalize(this);
- }
- }
- /// <summary>
- /// Finalizer
- /// </summary>
- ~FtpClient() {
- Dispose();
- }
- /// <summary>
- /// Creates a new isntance of FtpClient
- /// </summary>
- public FtpClient() { }
- /// <summary>
- /// Connects to the specified URI. If the path specified by the URI ends with a
- /// / then the working directory is changed to the path specified.
- /// </summary>
- /// <param name="uri">The URI to parse</param>
- /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
- /// <returns>FtpClient object</returns>
- public static FtpClient Connect(Uri uri, bool checkcertificate) {
- FtpClient cl = new FtpClient();
- if (uri == null)
- throw new ArgumentException("Invalid URI object");
- switch (uri.Scheme.ToLower()) {
- case "ftp":
- case "ftps":
- break;
- default:
- throw new UriFormatException("The specified URI scheme is not supported. Please use ftp:// or ftps://");
- }
- cl.Host = uri.Host;
- cl.Port = uri.Port;
- if (uri.UserInfo != null && uri.UserInfo.Length > 0) {
- if (uri.UserInfo.Contains(":")) {
- string[] parts = uri.UserInfo.Split(':');
- if (parts.Length != 2)
- 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.");
- cl.Credentials = new NetworkCredential(HttpUtility.UrlDecode(parts[0]), HttpUtility.UrlDecode(parts[1]));
- }
- else
- cl.Credentials = new NetworkCredential(HttpUtility.UrlDecode(uri.UserInfo), "");
- }
- else {
- // if no credentials were supplied just make up
- // some for anonymous authentication.
- cl.Credentials = new NetworkCredential("ftp", "ftp");
- }
- cl.ValidateCertificate += new FtpSslValidation(delegate(FtpClient control, FtpSslValidationEventArgs e) {
- if (e.PolicyErrors != Security.SslPolicyErrors.None && checkcertificate)
- e.Accept = false;
- else
- e.Accept = true;
- });
- cl.Connect();
- if (uri.PathAndQuery != null && uri.PathAndQuery.EndsWith("/"))
- cl.SetWorkingDirectory(uri.PathAndQuery);
- return cl;
- }
- /// <summary>
- /// Connects to the specified URI. If the path specified by the URI ends with a
- /// / then the working directory is changed to the path specified.
- /// </summary>
- /// <param name="uri">The URI to parse</param>
- /// <returns>FtpClient object</returns>
- public static FtpClient Connect(Uri uri) {
- return Connect(uri, true);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
- /// <param name="datatype">ASCII/Binary mode</param>
- /// <param name="restart">Restart location</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenReadURI.cs" lang="cs" /></example>
- public static Stream OpenRead(Uri uri, bool checkcertificate, FtpDataType datatype, long restart) {
- FtpClient cl = null;
- if (uri.PathAndQuery == null || uri.PathAndQuery.Length == 0)
- throw new UriFormatException("The supplied URI does not contain a valid path.");
- if (uri.PathAndQuery.EndsWith("/"))
- throw new UriFormatException("The supplied URI points at a directory.");
- cl = Connect(uri, checkcertificate);
- cl.EnableThreadSafeDataConnections = false;
- return cl.OpenRead(uri.PathAndQuery, datatype, restart);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
- /// <param name="datatype">ASCII/Binary mode</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenReadURI.cs" lang="cs" /></example>
- public static Stream OpenRead(Uri uri, bool checkcertificate, FtpDataType datatype) {
- return OpenRead(uri, checkcertificate, datatype, 0);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenReadURI.cs" lang="cs" /></example>
- public static Stream OpenRead(Uri uri, bool checkcertificate) {
- return OpenRead(uri, checkcertificate, FtpDataType.Binary, 0);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenReadURI.cs" lang="cs" /></example>
- public static Stream OpenRead(Uri uri) {
- return OpenRead(uri, true, FtpDataType.Binary, 0);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
- /// <param name="datatype">ASCII/Binary mode</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenWriteURI.cs" lang="cs" /></example>
- public static Stream OpenWrite(Uri uri, bool checkcertificate, FtpDataType datatype) {
- FtpClient cl = null;
- if (uri.PathAndQuery == null || uri.PathAndQuery.Length == 0)
- throw new UriFormatException("The supplied URI does not contain a valid path.");
- if (uri.PathAndQuery.EndsWith("/"))
- throw new UriFormatException("The supplied URI points at a directory.");
- cl = Connect(uri, checkcertificate);
- cl.EnableThreadSafeDataConnections = false;
- return cl.OpenWrite(uri.PathAndQuery, datatype);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenWriteURI.cs" lang="cs" /></example>
- public static Stream OpenWrite(Uri uri, bool checkcertificate) {
- return OpenWrite(uri, checkcertificate, FtpDataType.Binary);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenWriteURI.cs" lang="cs" /></example>
- public static Stream OpenWrite(Uri uri) {
- return OpenWrite(uri, true, FtpDataType.Binary);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
- /// <param name="datatype">ASCII/Binary mode</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenAppendURI.cs" lang="cs" /></example>
- public static Stream OpenAppend(Uri uri, bool checkcertificate, FtpDataType datatype) {
- FtpClient cl = null;
- if (uri.PathAndQuery == null || uri.PathAndQuery.Length == 0)
- throw new UriFormatException("The supplied URI does not contain a valid path.");
- if (uri.PathAndQuery.EndsWith("/"))
- throw new UriFormatException("The supplied URI points at a directory.");
- cl = Connect(uri, checkcertificate);
- cl.EnableThreadSafeDataConnections = false;
- return cl.OpenAppend(uri.PathAndQuery, datatype);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <param name="checkcertificate">Indicates if a ssl certificate should be validated when using FTPS schemes</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenAppendURI.cs" lang="cs" /></example>
- public static Stream OpenAppend(Uri uri, bool checkcertificate) {
- return OpenAppend(uri, checkcertificate, FtpDataType.Binary);
- }
- /// <summary>
- /// Opens a stream to the file specified by the URI
- /// </summary>
- /// <param name="uri">FTP/FTPS URI pointing at a file</param>
- /// <returns>Stream object</returns>
- /// <example><code source="..\Examples\OpenAppendURI.cs" lang="cs" /></example>
- public static Stream OpenAppend(Uri uri) {
- return OpenAppend(uri, true, FtpDataType.Binary);
- }
- /// <summary>
- /// Used internally to mark properties in the control connection that
- /// should be cloned when opening a data connection.
- /// </summary>
- sealed class FtpControlConnectionClone : Attribute {
- }
- }
- }
|