| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443 |
- {
- $Project$
- $Workfile$
- $Revision$
- $DateUTC$
- $Id$
- This file is part of the Indy (Internet Direct) project, and is offered
- under the dual-licensing agreement described on the Indy website.
- (http://www.indyproject.org/)
- Copyright:
- (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
- }
- {
- $Log$
- }
- {
- Rev 1.126 4/28/2005 BTaylor
- Changed .Size to use Int64
- Rev 1.125 4/15/2005 9:10:10 AM JPMugaas
- Changed the default timeout in TIdFTP to one minute and made a comment about
- this.
- Some firewalls don't handle control connections properly during long data
- transfers. They will timeout the control connection because it's idle and
- making it worse is that they will chop off a connection instead of closing it
- causing TIdFTP to wait forever for nothing.
- Rev 1.124 3/20/2005 10:42:44 PM JPMugaas
- Marked TIdFTP.Quit as deprecated. We need to keep it only for compatibility.
- Rev 1.123 3/20/2005 2:44:08 PM JPMugaas
- Should now send quit. Verified here.
- Rev 1.122 3/12/2005 6:57:12 PM JPMugaas
- Attempt to add ACCT support for firewalls. I also used some logic from some
- WS-FTP Pro about ACCT to be more consistant with those Firescripts.
- Rev 1.121 3/10/2005 2:41:12 PM JPMugaas
- Removed the UseTelnetAbort property. It turns out that sending the sequence
- is causing problems on a few servers. I have made a comment about this in
- the source-code so someone later on will know why I decided not to send
- those.
- Rev 1.120 3/9/2005 10:05:54 PM JPMugaas
- Minor changes for Indy conventions.
- Rev 1.119 3/9/2005 9:15:46 PM JPMugaas
- Changes submitted by Craig Peterson, Scooter Software He noted this:
- "We had a user who's FTP server prompted for account info after a
- regular login, so I had to add an explicit Account string property and
- an OnNeedAccount event that we could use for a prompt." This does break any
- code using TIdFTP.Account.
- TODO: See about integrating Account Info into the proxy login sequences.
- Rev 1.118 3/9/2005 10:40:16 AM JPMugaas
- Made comment explaining why I had made a workaround in a procedure.
- Rev 1.117 3/9/2005 10:28:32 AM JPMugaas
- Fix for Abort problem when uploading. A workaround I made for WS-FTP Pro
- Server was not done correctly.
- Rev 1.116 3/9/2005 1:21:38 AM JPMugaas
- Made refinement to Abort and the data transfers to follow what Kudzu had
- originally done in Indy 8. I also fixed a problem with ABOR at
- ftp.ipswitch.com and I fixed a regression at ftp.marist.edu that occured when
- getting a directory.
- Rev 1.115 3/8/2005 12:14:50 PM JPMugaas
- Renamed UseOOBAbort to UseTelnetAbort because that's more accurate. We still
- don't support Out of Band Data (hopefully, we'll never have to do that).
- Rev 1.114 3/7/2005 10:40:10 PM JPMugaas
- Improvements:
- 1) Removed some duplicate code.
- 2) ABOR should now be properly handled outside of a data operation.
- 3) I added a UseOOBAbort read-write public property for controlling how the
- ABOR command is sent. If true, the Telnet sequences are sent or if false,
- the ABOR without sequences is sent. This is set to false by default because
- one FTP client (SmartFTP recently removed the Telnet sequences from their
- program).
- This code is expiriemental.
- Rev 1.113 3/7/2005 5:46:34 PM JPMugaas
- Reworked FTP Abort code to make it more threadsafe and make abort work. This
- is PRELIMINARY.
- Rev 1.112 3/5/2005 3:33:56 PM JPMugaas
- Fix for some compiler warnings having to do with TStream.Read being platform
- specific. This was fixed by changing the Compressor API to use TIdStreamVCL
- instead of TStream. I also made appropriate adjustments to other units for
- this.
- Rev 1.111 2/24/2005 6:46:36 AM JPMugaas
- Clarrified remarks I made and added a few more comments about syntax in
- particular cases in the set modified file date procedures.
- That's really been a ball....NOT!!!!
- Rev 1.110 2/24/2005 6:25:08 AM JPMugaas
- Attempt to fix problem setting Date with Titan FTP Server. I had made an
- incorrect assumption about MDTM on that system. It uses Syntax 3 (see my
- earlier note above the File Date Set problem.
- Rev 1.109 2/23/2005 6:32:54 PM JPMugaas
- Made note about MDTM syntax inconsistancy. There's a discussion about it.
- Rev 1.108 2/12/2005 8:08:04 AM JPMugaas
- Attempt to fix MDTM bug where msec was being sent.
- Rev 1.107 1/12/2005 11:26:44 AM JPMugaas
- Memory Leak fix when processing MLSD output and some minor tweeks Remy had
- E-Mailed me last night.
- Rev 1.106 11/18/2004 2:39:32 PM JPMugaas
- Support for another FTP Proxy type.
- Rev 1.105 11/18/2004 12:18:50 AM JPMugaas
- Fixed compile error.
- Rev 1.104 11/17/2004 3:59:22 PM JPMugaas
- Fixed a TODO item about FTP Proxy support with a "Transparent" proxy. I
- think you connect to the regular host and the firewall will intercept its
- login information.
- Rev 1.103 11/16/2004 7:31:52 AM JPMugaas
- Made a comment noting that UserSite is the same as USER after login for later
- reference.
- Rev 1.102 11/5/2004 1:54:42 AM JPMugaas
- Minor adjustment - should not detect TitanFTPD better (tested at:
- ftp.southrivertech.com).
- If MLSD is being used, SITE ZONE will not be issued. It's not needed because
- the MLSD spec indicates the time is based on GMT.
- Rev 1.101 10/27/2004 12:58:08 AM JPMugaas
- Improvement from Tobias Giesen http://www.superflexible.com
- His notation is below:
- "here's a fix for TIdFTP.IndexOfFeatLine. It does not work the
- way it is used in TIdFTP.SetModTime, because it only
- compares the first word of the FeatLine."
- Rev 1.100 10/26/2004 9:19:10 PM JPMugaas
- Fixed references.
- Rev 1.99 9/16/2004 3:24:04 AM JPMugaas
- TIdFTP now compresses to the IOHandler and decompresses from the IOHandler.
- Noted some that the ZLib code is based was taken from ZLibEx.
- Rev 1.98 9/13/2004 12:15:42 AM JPMugaas
- Now should be able to handle some values better as suggested by Michael J.
- Leave.
- Rev 1.97 9/11/2004 10:58:06 AM JPMugaas
- FTP now decompresses output directly to the IOHandler.
- Rev 1.96 9/10/2004 7:37:42 PM JPMugaas
- Fixed a bug. We needed to set Passthrough instead of calling StartSSL. This
- was causing a SSL problem with upload.
- Rev 1.95 8/2/04 5:56:16 PM RLebeau
- Tweaks to TIdFTP.InitDataChannel()
- Rev 1.94 7/30/2004 1:55:04 AM DSiders
- Corrected DoOnRetrievedDir naming.
- Rev 1.93 7/30/2004 12:36:32 AM DSiders
- Corrected spelling in OnRetrievedDir, DoOnRetrievedDir declarations.
- Rev 1.92 7/29/2004 2:15:28 AM JPMugaas
- New property for controlling what AUTH command is sent. Fixed some minor
- issues with FTP properties. Some were not set to defaults causing
- unpredictable results -- OOPS!!!
- Rev 1.91 7/29/2004 12:04:40 AM JPMugaas
- New events for Get and Put as suggested by Don Sides and to complement an
- event done by APR.
- Rev 1.90 7/28/2004 10:16:14 AM JPMugaas
- New events for determining when a listing is finished and when the dir
- parsing begins and ends. Dir parsing is done sometimes when DirectoryListing
- is referenced.
- Rev 1.89 7/27/2004 2:03:54 AM JPMugaas
- New property:
- ExternalIP - used to specify an IP address for the PORT and EPRT commands.
- This should be blank unless you are behind a NAT and you need to use PORT
- transfers with SSL. You would set ExternalIP to the NAT's IP address on the
- Internet.
- The idea is this:
- 1) You set up your NAT to forward a range ports ports to your computer behind
- the NAT.
- 2) You specify that a port range with the DataPortMin and DataPortMin
- properties.
- 3) You set ExternalIP to the NAT's Internet IP address.
- I have verified this with Indy and WS FTP Pro behind a NAT router.
- Rev 1.88 7/23/04 7:09:50 PM RLebeau
- Bug fix for TFileStream access rights in Get()
- Rev 1.87 7/18/2004 3:00:12 PM DSiders
- Added localization comments.
- Rev 1.86 7/16/2004 4:28:40 AM JPMugaas
- CCC Support in TIdFTP to complement that capability in TIdFTPServer.
- Rev 1.85 7/13/04 6:48:14 PM RLebeau
- Added support for new DataPort and DataPortMin/Max properties
- Rev 1.84 7/6/2004 4:51:46 PM DSiders
- Corrected spelling of Challenge in properties, methods, types.
- Rev 1.83 7/3/2004 3:15:50 AM JPMugaas
- Checked in so everyone else can work on stuff while I'm away.
- Rev 1.82 6/27/2004 1:45:38 AM JPMugaas
- Can now optionally support LastAccessTime like Smartftp's FTP Server could.
- I also made the MLST listing object and parser support this as well.
- Rev 1.81 6/20/2004 8:31:58 PM JPMugaas
- New events for reporting greeting and after login banners during the login
- sequence.
- Rev 1.80 6/20/2004 6:56:42 PM JPMugaas
- Start oin attempt to support FXP with Deflate compression. More work will
- need to be done.
- Rev 1.79 6/17/2004 3:42:32 PM JPMugaas
- Adjusted code for removal of dmBlock and dmCompressed. Made TransferMode a
- property. Note that the Set method is odd because I am trying to keep
- compatibility with older Indy versions.
- Rev 1.78 6/14/2004 6:19:02 PM JPMugaas
- This now refers to TIdStreamVCL when downloading isntead of directly to a
- memory stream when compressing data.
- Rev 1.77 6/14/2004 8:34:52 AM JPMugaas
- Fix for AV on Put with Passive := True.
- Rev 1.76 6/11/2004 9:34:12 AM DSiders
- Added "Do not Localize" comments.
- Rev 1.75 2004.05.20 11:37:16 AM czhower
- IdStreamVCL
- Rev 1.74 5/6/2004 6:54:26 PM JPMugaas
- FTP Port transfers with TransparentProxies is enabled. This only works if
- the TransparentProxy supports a "bind" request.
- Rev 1.73 5/4/2004 11:16:28 AM JPMugaas
- TransferTimeout property added and enabled (Bug 96).
- Rev 1.72 5/4/2004 11:07:12 AM JPMugaas
- Timeouts should now be reenabled in TIdFTP.
- Rev 1.71 4/19/2004 5:05:02 PM JPMugaas
- Class rework Kudzu wanted.
- Rev 1.70 2004.04.16 9:31:42 PM czhower
- Remove unnecessary duplicate string parsing and replaced with .assign.
- Rev 1.69 2004.04.15 7:09:04 PM czhower
- .NET overloads
- Rev 1.68 4/15/2004 9:46:48 AM JPMugaas
- List no longer requires a TStrings. It turns out that it was an optional
- parameter.
- Rev 1.67 2004.04.15 2:03:28 PM czhower
- Removed login param from connect and made it a prop like POP3.
- Rev 1.66 3/3/2004 5:57:40 AM JPMugaas
- Some IFDEF excluses were removed because the functionality is now in DotNET.
- Rev 1.65 2004.03.03 11:54:26 AM czhower
- IdStream change
- Rev 1.64 2/20/2004 1:01:06 PM JPMugaas
- Preliminary FTP PRET command support for using PASV with a distributed FTP
- server (Distributed PASV -
- http://drftpd.org/wiki/wiki.phtml?title=Distributed_PASV).
- Rev 1.63 2/17/2004 12:25:52 PM JPMugaas
- The client now supports MODE Z (deflate) uploads and downloads as specified
- by http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt
- Rev 1.62 2004.02.03 5:45:10 PM czhower
- Name changes
- Rev 1.61 2004.02.03 2:12:06 PM czhower
- $I path change
- Rev 1.60 1/27/2004 10:17:10 PM JPMugaas
- Fix from Steve Loft for a server that sends something like this:
- "227 Passive mode OK (195,92,195,164,4,99 )"
- Rev 1.59 1/27/2004 3:59:28 PM SPerry
- StringStream ->IdStringStream
- Rev 1.58 24/01/2004 19:13:58 CCostelloe
- Cleaned up warnings
- Rev 1.57 1/21/2004 2:27:50 PM JPMugaas
- Bullete Proof FTPD and Titan FTP support SITE ZONE. Saw this in a command
- database in StaffFTP.
- InitComponent.
- Rev 1.56 1/19/2004 9:05:38 PM JPMugaas
- Fixes to FTP Set Date functionality.
- Introduced properties for Time Zone information from the server. The way it
- works is this, if TIdFTP detects you are using "Serv-U" or SITE ZONE is
- listed in the FEAT reply, Indy obtains the time zone information with the
- SITE ZONE command and makes the appropriate calculation. Indy then uses this
- information to calculate a timestamp to send to the server with the MDTM
- command. You can also use the Time Zone information yourself to convert the
- FTP directory listing item timestamps into GMT and than convert that to your
- local time.
- FTP Voyager uses SITE ZONE as I've described.
- Rev 1.55 1/19/2004 4:39:08 AM JPMugaas
- You can now set the time for a file on the server. Note that these methods
- try to treat the time as relative to GMT.
- Rev 1.54 1/17/2004 9:09:30 PM JPMugaas
- Should now compile.
- Rev 1.53 1/17/2004 7:48:02 PM JPMugaas
- FXP site to site transfer code was redone for improvements with FXP with TLS.
- It actually works and I verified with RaidenFTPD
- (http://www.raidenftpd.com/) and the Indy FTP server components. I also
- lowered the requirements for TLS FXP transfers. The requirements now are:
- 1) Only server (either the recipient or the sendor) has to support SSCN
- or
- 2) The server receiving a PASV must support CPSV and the transfer is done
- with IPv4.
- Rev 1.52 1/9/2004 2:51:26 PM JPMugaas
- Started IPv6 support.
- Rev 1.51 11/27/2003 4:55:28 AM JPMugaas
- Made STOU functionality separate from PUT functionality. Put now requires a
- destination filename except where a source-file name is given. In that case,
- the default is the filename from the source string.
- Rev 1.50 10/26/2003 04:28:50 PM JPMugaas
- Reworked Status.
- The old one was problematic because it assumed that STAT was a request to
- send a directory listing through the control channel. This assumption is not
- correct. It provides a way to get a freeform status report from a server.
- With a Path parameter, it should work like a LIST command except that the
- control connection is used. We don't support that feature and you should use
- our LIst method to get the directory listing anyway, IMAO.
- Rev 1.49 10/26/2003 9:17:46 PM BGooijen
- Compiles in DotNet, and partially works there
- Rev 1.48 10/24/2003 12:43:48 PM JPMugaas
- Should work again.
- Rev 1.47 2003.10.24 10:43:04 AM czhower
- TIdSTream to dos
- Rev 1.46 10/20/2003 03:06:10 PM JPMugaas
- SHould now work.
- Rev 1.45 10/20/2003 01:00:38 PM JPMugaas
- EIdException no longer raised. Some things were being gutted needlessly.
- Rev 1.44 10/19/2003 12:58:20 PM DSiders
- Added localization comments.
- Rev 1.43 2003.10.14 9:56:50 PM czhower
- Compile todos
- Rev 1.42 2003.10.12 3:50:40 PM czhower
- Compile todos
- Rev 1.41 10/10/2003 11:32:26 PM SPerry
- -
- Rev 1.40 10/9/2003 10:17:02 AM JPMugaas
- Added overload for GetLoginPassword for providing a challanage string which
- doesn't have to the last command reply.
- Added CLNT support.
- Rev 1.39 10/7/2003 05:46:20 AM JPMugaas
- SSCN Support added.
- Rev 1.38 10/6/2003 08:56:44 PM JPMugaas
- Reworked the FTP list parsing framework so that the user can obtain the list
- of capabilities from a parser class with TIdFTP. This should permit the user
- to present a directory listing differently for each parser (some FTP list
- parsers do have different capabilities).
- Rev 1.37 10/1/2003 12:51:18 AM JPMugaas
- SSL with active (PORT) transfers now should work again.
- Rev 1.36 9/30/2003 09:50:38 PM JPMugaas
- FTP with TLS should work better. It turned out that we were negotiating it
- several times causing a hang. I also made sure that we send PBSZ 0 and PROT
- P for both implicit and explicit TLS. Data ports should work in PASV again.
- Rev 1.35 9/28/2003 11:41:06 PM JPMugaas
- Reworked Eldos's proposed FTP fix as suggested by Henrick Hellström by moving
- all of the IOHandler creation code to InitDataChannel. This should reduce
- the likelihood of error.
- Rev 1.33 9/18/2003 11:22:40 AM JPMugaas
- Removed a temporary workaround for an OnWork bug that was in the Indy Core.
- That bug was fixed so there's no sense in keeping a workaround here.
- Rev 1.32 9/12/2003 08:05:30 PM JPMugaas
- A temporary fix for OnWork events not firing. The bug is that OnWork events
- aren't used in IOHandler where ReadStream really is located.
- Rev 1.31 9/8/2003 02:33:00 AM JPMugaas
- OnCustomFTPProxy added to allow Indy to support custom FTP proxies. When
- using this event, you are responsible for programming the FTP Proxy and FTP
- Server login sequence.
- GetLoginPassword method function for returning the password used when logging
- into a FTP server which handles OTP calculation. This way, custom firewall
- support can handle One-Time-Password system transparently. You do have to
- send the User ID before calling this function because the OTP challenge is
- part of the reply.
- Rev 1.30 6/10/2003 11:10:00 PM JPMugaas
- Made comments about our loop that tries several AUTH command variations.
- Some servers may only accept AUTH SSL while other servers only accept AUTH
- TLS.
- Rev 1.29 5/26/2003 12:21:54 PM JPMugaas
- Rev 1.28 5/25/2003 03:54:20 AM JPMugaas
- Rev 1.27 5/19/2003 08:11:32 PM JPMugaas
- Now should compile properly with new code in Core.
- Rev 1.26 5/8/2003 11:27:42 AM JPMugaas
- Moved feature negoation properties down to the ExplicitTLSClient level as
- feature negotiation goes hand in hand with explicit TLS support.
- Rev 1.25 4/5/2003 02:06:34 PM JPMugaas
- TLS handshake itself can now be handled.
- Rev 1.24 4/4/2003 8:01:32 PM BGooijen
- now creates iohandler for dataconnection
- Rev 1.23 3/31/2003 08:40:18 AM JPMugaas
- Fixed problem with QUIT command.
- Rev 1.22 3/27/2003 3:41:28 PM BGooijen
- Changed because some properties are moved to IOHandler
- Rev 1.21 3/27/2003 05:46:24 AM JPMugaas
- Updated framework with an event if the TLS negotiation command fails.
- Cleaned up some duplicate code in the clients.
- Rev 1.20 3/26/2003 04:19:20 PM JPMugaas
- Cleaned-up some code and illiminated some duplicate things.
- Rev 1.19 3/24/2003 04:56:10 AM JPMugaas
- A typecast was incorrect and could cause a potential source of instability if
- a TIdIOHandlerStack was not used.
- Rev 1.18 3/16/2003 06:09:58 PM JPMugaas
- Fixed port setting bug.
- Rev 1.17 3/16/2003 02:40:16 PM JPMugaas
- FTP client with new design.
- Rev 1.16 3/16/2003 1:02:44 AM BGooijen
- Added 2 events to give the user more control to the dataconnection, moved
- SendTransferType, enabled ssl
- Rev 1.15 3/13/2003 09:48:58 AM JPMugaas
- Now uses an abstract SSL base class instead of OpenSSL so 3rd-party vendors
- can plug-in their products.
- Rev 1.14 3/7/2003 11:51:52 AM JPMugaas
- Fixed a writeln bug and an IOError issue.
- Rev 1.13 3/3/2003 07:06:26 PM JPMugaas
- FFreeIOHandlerOnDisconnect to FreeIOHandlerOnDisconnect at Bas's instruction
- Rev 1.12 2/21/2003 06:54:36 PM JPMugaas
- The FTP list processing has been restructured so that Directory output is not
- done by IdFTPList. This now also uses the IdFTPListParserBase for parsing so
- that the code is more scalable.
- Rev 1.11 2/17/2003 04:45:36 PM JPMugaas
- Now temporarily change the transfer mode to ASCII when requesting a DIR.
- TOPS20 does not like transfering dirs in binary mode and it might be a good
- idea to do it anyway.
- Rev 1.10 2/16/2003 03:22:20 PM JPMugaas
- Removed the Data Connection assurance stuff. We figure things out from the
- draft specificaiton, the only servers we found would not send any data after
- the new commands were sent, and there were only 2 server types that supported
- it anyway.
- Rev 1.9 2/16/2003 10:51:08 AM JPMugaas
- Attempt to implement:
- http://www.ietf.org/internet-drafts/draft-ietf-ftpext-data-connection-assuranc
- e-00.txt
- Currently commented out because it does not work.
- Rev 1.8 2/14/2003 11:40:16 AM JPMugaas
- Fixed compile error.
- Rev 1.7 2/14/2003 10:38:32 AM JPMugaas
- Removed a problematic override for GetInternelResponse. It was messing up
- processing of the FEAT.
- Rev 1.6 12-16-2002 20:48:10 BGooijen
- now uses TIdIOHandler.ConstructIOHandler to construct iohandlers
- IPv6 works again
- Independant of TIdIOHandlerStack again
- Rev 1.5 12-15-2002 23:27:26 BGooijen
- now compiles on Indy 10, but some things like IPVersion still need to be
- changed
- Rev 1.4 12/15/2002 04:07:02 PM JPMugaas
- Started port to Indy 10. Still can not complete it though.
- Rev 1.3 12/6/2002 05:29:38 PM JPMugaas
- Now decend from TIdTCPClientCustom instead of TIdTCPClient.
- Rev 1.2 12/1/2002 04:18:02 PM JPMugaas
- Moved all dir parsing code to one place. Reworked to use more than one line
- for determining dir format type along with flfNextLine dir format type.
- Rev 1.1 11/14/2002 04:02:58 PM JPMugaas
- Removed cludgy code that was a workaround for the RFC Reply limitation. That
- is no longer limited.
- Rev 1.0 11/14/2002 02:20:00 PM JPMugaas
- 2002-10-25 - J. Peter Mugaas
- - added XCRC support - specified by "GlobalSCAPE Secure FTP Server User’s Guide"
- which is available at http://www.globalscape.com
- and also explained at http://www.southrivertech.com/support/titanftp/webhelp/titanftp.htm
- - added COMB support - specified by "GlobalSCAPE Secure FTP Server User’s Guide"
- which is available at http://www.globalscape.com
- and also explained at http://www.southrivertech.com/support/titanftp/webhelp/titanftp.htm
- 2002-10-24 - J. Peter Mugaas
- - now supports RFC 2640 - FTP Internalization
- 2002-09-18
- _ added AFromBeginning parameter to InternalPut to correctly honor the AAppend parameter of Put
- 2002-09-05 - J. Peter Mugaas
- - now complies with RFC 2389 - Feature negotiation mechanism for the File Transfer Protocol
- - now complies with RFC 2428 - FTP Extensions for IPv6 and NATs
- 2002-08-27 - Andrew P.Rybin
- - proxy support fix (non-standard ftp port's)
- 2002-01-xx - Andrew P.Rybin
- - Proxy support, OnAfterGet (ex:decrypt, set srv timestamp)
- - J.Peter Mugaas: not readonly ProxySettings
- A Neillans - 10/17/2001
- Merged changes submitted by Andrew P.Rybin
- Correct command case problems - some servers expect commands in Uppercase only.
- SP - 06/08/2001
- Added a few more functions
- Doychin - 02/18/2001
- OnAfterLogin event handler and Login method
- OnAfterLogin is executed after successfull login but before setting up the
- connection properties. This event can be used to provide FTP proxy support
- from the user application. Look at the FTP demo program for more information
- on how to provide such support.
- Doychin - 02/17/2001
- New onFTPStatus event
- New Quote method for executing commands not implemented by the compoent
- -CleanDir contributed by Amedeo Lanza
- }
- unit IdFTP;
- {
- TODO: Change the FTP demo to demonstrate the use of the new events and add proxy support
- }
- interface
- {$i IdCompilerDefines.inc}
- uses
- Classes,
- IdAssignedNumbers, IdGlobal, IdExceptionCore,
- IdExplicitTLSClientServerBase, IdFTPCommon, IdFTPList, IdFTPListParseBase,
- IdException, IdIOHandler, IdIOHandlerSocket, IdReply, IdReplyFTP, IdBaseComponent,
- IdSocketHandle, IdTCPConnection, IdTCPClient,
- IdThreadSafe, IdZLibCompressorBase;
- type
- //APR 011216:
- TIdFtpProxyType = (
- fpcmNone,//Connect method:
- fpcmUserSite, //Send command USER user@hostname - USER after login (see: http://isservices.tcd.ie/internet/command_config.php)
- fpcmSite, //Send command SITE (with logon)
- fpcmOpen, //Send command OPEN
- fpcmUserPass,//USER user@firewalluser@hostname / PASS pass@firewallpass
- fpcmTransparent, //First use the USER and PASS command with the firewall username and password, and then with the target host username and password.
- fpcmUserHostFireWallID, //USER hostuserId@hostname firewallUsername
- fpcmNovellBorder, //Novell BorderManager Proxy
- fpcmHttpProxyWithFtp, //HTTP Proxy with FTP support. Will be supported in Indy 10
- fpcmCustomProxy // use OnCustomFTPProxy to customize the proxy login
- ); //TIdFtpProxyType
- //This has to be in the same order as TLS_AUTH_NAMES
- TAuthCmd = (tAuto, tAuthTLS, tAuthSSL, tAuthTLSC, tAuthTLSP);
- const
- Id_TIdFTP_TransferType = {ftBinary} ftASCII; // RLebeau 1/22/08: per RFC 959
- Id_TIdFTP_Passive = False;
- Id_TIdFTP_UseNATFastTrack = False;
- Id_TIdFTP_HostPortDelimiter = ':';
- Id_TIdFTP_DataConAssurance = False;
- Id_TIdFTP_DataPortProtection = ftpdpsClear;
- //
- DEF_Id_TIdFTP_Implicit = False;
- DEF_Id_FTP_UseExtendedDataPort = False;
- DEF_Id_TIdFTP_UseExtendedData = False;
- DEF_Id_TIdFTP_UseMIS = True;
- DEF_Id_FTP_UseCCC = False;
- DEF_Id_FTP_AUTH_CMD = tAuto;
- DEF_Id_FTP_ListenTimeout = 10000; // ten seconds
- {
- Soem firewalls don't handle control connections properly during long data transfers.
- They will timeout the control connection because it's idle and making it worse is that they
- will chop off a connection instead of closing it causing TIdFTP to wait forever for nothing.
- }
- DEF_Id_FTP_READTIMEOUT = 60000; //one minute
- DEF_Id_FTP_UseHOST = True;
- DEF_Id_FTP_PassiveUseControlHost = False;
- DEF_Id_FTP_AutoIssueFEAT = True;
- DEF_Id_FTP_AutoLogin = True;
- type
- //Added by SP
- TIdCreateFTPList = procedure(ASender: TObject; var VFTPList: TIdFTPListItems) of object;
- //TIdCheckListFormat = procedure(ASender: TObject; const ALine: String; var VListFormat: TIdFTPListFormat) of object;
- TOnAfterClientLogin = TNotifyEvent;
- TIdFtpAfterGet = procedure(ASender: TObject; AStream: TStream) of object; //APR
- TIdOnDataChannelCreate = procedure(ASender: TObject; ADataChannel: TIdTCPConnection) of object;
- TIdOnDataChannelDestroy = procedure(ASender: TObject; ADataChannel: TIdTCPConnection) of object;
- TIdNeedAccountEvent = procedure(ASender: TObject; var VAcct: string) of object;
- TIdFTPBannerEvent = procedure (ASender: TObject; const AMsg : String) of object;
- TIdFtpProxySettings = class (TPersistent)
- protected
- FHost, FUserName, FPassword: String;
- FProxyType: TIdFtpProxyType;
- FPort: TIdPort;
- public
- procedure Assign(Source: TPersistent); override;
- published
- property ProxyType: TIdFtpProxyType read FProxyType write FProxyType;
- property Host: String read FHost write FHost;
- property UserName: String read FUserName write FUserName;
- property Password: String read FPassword write FPassword;
- property Port: TIdPort read FPort write FPort;
- end;
- TIdFTPTZInfo = class(TPersistent)
- protected
- FGMTOffset : TDateTime;
- FGMTOffsetAvailable : Boolean;
- public
- procedure Assign(Source: TPersistent); override;
- published
- property GMTOffset : TDateTime read FGMTOffset write FGMTOffset;
- property GMTOffsetAvailable : Boolean read FGMTOffsetAvailable write FGMTOffsetAvailable;
- end;
- TIdFTPKeepAlive = class(TPersistent)
- protected
- FUseKeepAlive: Boolean;
- FIdleTimeMS: Integer;
- FIntervalMS: Integer;
- public
- procedure Assign(Source: TPersistent); override;
- published
- // TODO: replace UseKeepAlive with an enum/set that allows keepalives to
- // be enabled on the command connection for its entire lifetime, not just
- // during transfers, and maybe also add an option to enable keepalives on
- // the data connections as well...
- property UseKeepAlive: Boolean read FUseKeepAlive write FUseKeepAlive;
- property IdleTimeMS: Integer read FIdleTimeMS write FIdleTimeMS;
- property IntervalMS: Integer read FIntervalMS write FIntervalMS;
- end;
- TIdFTP = class(TIdExplicitTLSClient)
- protected
- FAutoLogin: Boolean;
- FAutoIssueFEAT : Boolean;
- FCurrentTransferMode : TIdFTPTransferMode;
- FClientInfo : TIdFTPClientIdentifier;
- FServerInfo : TIdFTPServerIdentifier;
- FDataSettingsSent: Boolean; // only send SSL data settings once per connection
- FUsingSFTP : Boolean; //enable SFTP internel flag
- FUsingCCC : Boolean; //are we using FTP with SSL on a clear control channel?
- FUseHOST: Boolean;
- FServerHOST: String;
- FCanUseMLS : Boolean; //can we use MLISx instead of LIST
- FUsingExtDataPort : Boolean; //are NAT Extensions (RFC 2428 available) flag
- FUsingNATFastTrack : Boolean;//are we using NAT fastrack feature
- FCanResume: Boolean;
- FListResult: TStrings;
- FLoginMsg: TIdReplyFTP;
- FPassive: Boolean;
- FPassiveUseControlHost: Boolean;
- FDataPortProtection : TIdFTPDataPortSecurity;
- FAUTHCmd : TAuthCmd;
- FDataPort: TIdPort;
- FDataPortMin: TIdPort;
- FDataPortMax: TIdPort;
- FDefStringEncoding: IIdTextEncoding;
- FExternalIP : String;
- FResumeTested: Boolean;
- FServerDesc: string;
- FSystemDesc: string;
- FTransferType: TIdFTPTransferType;
- FTransferTimeout : Integer;
- FListenTimeout : Integer;
- FDataChannel: TIdTCPConnection;
- FDirectoryListing: TIdFTPListItems;
- FDirFormat : String;
- FListParserClass : TIdFTPListParseClass;
- FOnAfterClientLogin: TNotifyEvent;
- FOnCreateFTPList: TIdCreateFTPList;
- FOnBeforeGet: TNotifyEvent;
- FOnBeforePut: TIdFtpAfterGet;
- //in case someone needs to do something special with the data being uploaded
- FOnAfterGet: TIdFtpAfterGet; //APR
- FOnAfterPut: TNotifyEvent; //JPM at Don Sider's suggestion
- FOnNeedAccount: TIdNeedAccountEvent;
- FOnCustomFTPProxy : TNotifyEvent;
- FOnDataChannelCreate: TIdOnDataChannelCreate;
- FOnDataChannelDestroy: TIdOnDataChannelDestroy;
- FProxySettings: TIdFtpProxySettings;
- FUseExtensionDataPort : Boolean;
- FTryNATFastTrack : Boolean;
- FUseMLIS : Boolean;
- FLangsSupported : TStrings;
- FUseCCC: Boolean;
- //is the SSCN Client method on for this connection?
- FSSCNOn : Boolean;
- FIsCompressionSupported : Boolean;
- FOnBannerBeforeLogin : TIdFTPBannerEvent;
- FOnBannerAfterLogin : TIdFTPBannerEvent;
- FOnBannerWarning : TIdFTPBannerEvent;
- FTZInfo : TIdFTPTZInfo;
- {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FCompressor : TIdZLibCompressorBase;
- //ZLib settings
- FZLibCompressionLevel : Integer; //7
- FZLibWindowBits : Integer; //-15
- FZLibMemLevel : Integer; //8
- FZLibStratagy : Integer; //0 - default
- //dir events for some GUI programs.
- //The directory was Retrieved from the FTP server.
- FOnRetrievedDir : TNotifyEvent;
- //parsing is done only when DirectoryListing is referenced
- FOnDirParseStart : TNotifyEvent;
- FOnDirParseEnd : TNotifyEvent;
- //we probably need an Abort flag so we know when an abort is sent.
- //It turns out that one server will send a 550 or 451 error followed by an
- //ABOR successfull
- FAbortFlag : TIdThreadSafeBoolean;
- FAccount: string;
- FNATKeepAlive: TIdFTPKeepAlive;
- //
- procedure DoOnDataChannelCreate;
- procedure DoOnDataChannelDestroy;
- procedure DoOnRetrievedDir;
- procedure DoOnDirParseStart;
- procedure DoOnDirParseEnd;
- procedure FinalizeDataOperation;
- procedure SetTZInfo(const Value: TIdFTPTZInfo);
- function IsSiteZONESupported : Boolean;
- function IndexOfFeatLine(const AFeatLine : String):Integer;
- procedure ClearSSCN;
- function SetSSCNToOn : Boolean;
- procedure SendInternalPassive(const ACmd : String; var VIP: string; var VPort: TIdPort);
- procedure SendCPassive(var VIP: string; var VPort: TIdPort);
- function FindAuthCmd : String;
- //
- function GetReplyClass: TIdReplyClass; override;
- //
- procedure ParseFTPList;
- procedure SetPassive(const AValue : Boolean);
- procedure SetTryNATFastTrack(const AValue: Boolean);
- procedure DoTryNATFastTrack;
- procedure SetUseExtensionDataPort(const AValue: Boolean);
- procedure SetIPVersion(const AValue: TIdIPVersion); override;
- procedure SetIOHandler(AValue: TIdIOHandler); override;
- function GetSupportsTLS: Boolean; override;
- procedure ConstructDirListing;
- procedure DoAfterLogin;
- procedure DoFTPList;
- procedure DoCustomFTPProxy;
- procedure DoOnBannerAfterLogin(AText : TStrings);
- procedure DoOnBannerBeforeLogin(AText : TStrings);
- procedure DoOnBannerWarning(AText : TStrings);
- procedure SendPBSZ; //protection buffer size
- procedure SendPROT; //data port protection
- procedure SendDataSettings; //this is for the extensions only;
- // procedure DoCheckListFormat(const ALine: String);
- function GetDirectoryListing: TIdFTPListItems;
- // function GetOnParseCustomListFormat: TIdOnParseCustomListFormat;
- procedure InitDataChannel;
- //PRET is to help distributed FTP systems by letting them know what you will do
- //before issuing a PASV. See: http://drftpd.mog.se/wiki/wiki.phtml?title=Distributed_PASV#PRE_Transfer_Command_for_Distributed_PASV_Transfers
- //for a discussion.
- procedure SendPret(const ACommand : String);
- procedure InternalGet(const ACommand: string; ADest: TStream; AResume: Boolean = false);
- procedure InternalPut(const ACommand: string; ASource: TStream; AFromBeginning: Boolean = True; AResume: Boolean = False);
- // procedure SetOnParseCustomListFormat(const AValue: TIdOnParseCustomListFormat);
- procedure SendPassive(var VIP: string; var VPort: TIdPort);
- procedure SendPort(AHandle: TIdSocketHandle); overload;
- procedure SendPort(const AIP : String; const APort : TIdPort); overload;
- procedure ParseEPSV(const AReply : String; var VIP : String; var VPort : TIdPort);
- //These two are for RFC 2428.txt
- procedure SendEPort(AHandle: TIdSocketHandle); overload;
- procedure SendEPort(const AIP : String; const APort : TIdPort; const AIPVersion : TIdIPVersion); overload;
- procedure SendEPassive(var VIP: string; var VPort: TIdPort);
- function SendHost: Int16;
- procedure SetProxySettings(const Value: TIdFtpProxySettings);
- procedure SetClientInfo(const AValue: TIdFTPClientIdentifier);
- procedure SetCompressor(AValue: TIdZLibCompressorBase);
- procedure SendTransferType(AValue: TIdFTPTransferType);
- procedure SetTransferType(AValue: TIdFTPTransferType);
- procedure DoBeforeGet; virtual;
- procedure DoBeforePut(AStream: TStream); virtual;
- procedure DoAfterGet(AStream: TStream); virtual; //APR
- procedure DoAfterPut; virtual;
- class procedure FXPSetTransferPorts(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv : Boolean);
- class procedure FXPSendFile(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String);
- class function InternalEncryptedTLSFXP(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean) : Boolean;
- class function InternalUnencryptedFXP(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean;
- class function ValidateInternalIsTLSFXP(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv : Boolean): Boolean;
- procedure InitComponent; override;
- procedure SetUseTLS(AValue : TIdUseTLS); override;
- procedure Notification(AComponent: TComponent; Operation: TOperation); override;
- procedure SetDataPortProtection(AValue : TIdFTPDataPortSecurity);
- procedure SetAUTHCmd(const AValue : TAuthCmd);
- procedure SetDefStringEncoding(AValue: IIdTextEncoding);
- procedure SetUseCCC(const AValue: Boolean);
- procedure SetNATKeepAlive(AValue: TIdFTPKeepAlive);
- procedure IssueFEAT;
- //specific server detection
- function IsOldServU: Boolean;
- function IsBPFTP : Boolean;
- function IsTitan : Boolean;
- function IsWSFTP : Boolean;
- function IsIIS: Boolean;
- function CheckAccount: Boolean;
- function IsAccountNeeded : Boolean;
- function GetSupportsVerification : Boolean;
- public
- {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
- constructor Create(AOwner: TComponent); reintroduce; overload;
- {$ENDIF}
- procedure GetInternalResponse(AEncoding: IIdTextEncoding = nil); override;
- function {$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(
- const AResponse: Int16; const AAllowedResponses: array of Int16): Int16; override;
- function IsExtSupported(const ACmd : String):Boolean;
- procedure ExtractFeatFacts(const ACmd : String; AResults : TStrings);
- //this function transparantly handles OTP based on the Last command response
- //so it needs to be called only after the USER command or equivilent.
- function GetLoginPassword : String; overload;
- function GetLoginPassword(const APrompt : String) : String; overload;
- procedure Abort; virtual;
- procedure Allocate(AAllocateBytes: Integer);
- procedure ChangeDir(const ADirName: string);
- procedure ChangeDirUp;
- procedure Connect; override;
- destructor Destroy; override;
- procedure Delete(const AFilename: string);
- procedure FileStructure(AStructure: TIdFTPDataStructure);
- procedure Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = false); overload;
- procedure Get(const ASourceFile, ADestFile: string; const ACanOverwrite: boolean = false; AResume: Boolean = false); overload;
- procedure Help(AHelpContents: TStrings; ACommand: String = '');
- procedure KillDataChannel; virtual;
- //.NET Overload
- procedure List; overload;
- //.NET Overload
- procedure List(const ASpecifier: string; ADetails: Boolean = True); overload;
- procedure List(ADest: TStrings; const ASpecifier: string = ''; ADetails: Boolean = True); overload;
- procedure ExtListDir(ADest: TStrings = nil; const ADirectory: string = '');
- procedure ExtListItem(ADest: TStrings; AFList : TIdFTPListItems; const AItem: string=''); overload;
- procedure ExtListItem(ADest: TStrings; const AItem: string = ''); overload;
- procedure ExtListItem(AFList : TIdFTPListItems; const AItem : String= ''); overload;
- function FileDate(const AFileName : String; const AsGMT : Boolean = False): TDateTime;
- procedure Login;
- procedure MakeDir(const ADirName: string);
- procedure Noop;
- procedure SetCmdOpt(const ACMD, AOptions : String);
- procedure Put(const ASource: TStream; const ADestFile: string;
- const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1); overload;
- procedure Put(const ASourceFile: string; const ADestFile: string = '';
- const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1); overload;
- procedure StoreUnique(const ASource: TStream; const AStartPos: TIdStreamSize = -1); overload;
- procedure StoreUnique(const ASourceFile: string; const AStartPos: TIdStreamSize = -1); overload;
- procedure SiteToSiteUpload(const AToSite : TIdFTP; const ASourceFile : String; const ADestFile : String = '');
- procedure SiteToSiteDownload(const AFromSite: TIdFTP; const ASourceFile : String; const ADestFile : String = '');
- procedure DisconnectNotifyPeer; override;
- procedure Quit; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPECATED_MSG} 'Use Disconnect() instead'{$ENDIF};{$ENDIF}
- function Quote(const ACommand: String): Int16;
- procedure RemoveDir(const ADirName: string);
- procedure Rename(const ASourceFile, ADestFile: string);
- function ResumeSupported: Boolean;
- function RetrieveCurrentDir: string;
- procedure Site(const ACommand: string);
- function Size(const AFileName: String): Int64;
- procedure Status(AStatusList: TStrings);
- procedure StructureMount(APath: String);
- procedure TransferMode(ATransferMode: TIdFTPTransferMode);
- procedure ReInitialize(ADelay: UInt32 = 10);
- procedure SetLang(const ALangTag : String);
- function CRC(const AFIleName : String; const AStartPoint : Int64 = 0; const AEndPoint : Int64=0) : Int64;
- //verify file was uploaded, this is more comprehensive than the above
- function VerifyFile(ALocalFile : TStream; const ARemoteFile : String;
- const AStartPoint : TIdStreamSize = 0; const AByteCount : TIdStreamSize = 0) : Boolean; overload;
- function VerifyFile(const ALocalFile, ARemoteFile : String;
- const AStartPoint : TIdStreamSize = 0; const AByteCount : TIdStreamSize = 0) : Boolean; overload;
- //file parts must be in order in TStrings parameter
- //GlobalScape FTP Pro uses this for multipart simultanious file uploading
- procedure CombineFiles(const ATargetFile : String; AFileParts : TStrings);
- //Set modified file time.
- procedure SetModTime(const AFileName: String; const ALocalTime: TDateTime);
- procedure SetModTimeGMT(const AFileName : String; const AGMTTime: TDateTime);
- // servers that support MDTM yyyymmddhhmmss[+-xxx] and also support LIST -T
- //This is true for servers that are known to support these even if they aren't
- //listed in the FEAT reply.
- function IsServerMDTZAndListTForm : Boolean;
- property IsCompressionSupported : Boolean read FIsCompressionSupported;
- //
- property SupportsVerification : Boolean read GetSupportsVerification;
- property CanResume: Boolean read ResumeSupported;
- property CanUseMLS : Boolean read FCanUseMLS;
- property DirectoryListing: TIdFTPListItems read GetDirectoryListing;
- property DirFormat : String read FDirFormat;
- property LangsSupported : TStrings read FLangsSupported;
- property ListParserClass : TIdFTPListParseClass read FListParserClass write FListParserClass;
- property LoginMsg: TIdReplyFTP read FLoginMsg;
- property ListResult: TStrings read FListResult;
- property SystemDesc: string read FSystemDesc;
- property TZInfo : TIdFTPTZInfo read FTZInfo write SetTZInfo;
- property UsingExtDataPort : Boolean read FUsingExtDataPort;
- property UsingNATFastTrack : Boolean read FUsingNATFastTrack;
- property UsingSFTP : Boolean read FUsingSFTP;
- property CurrentTransferMode : TIdFTPTransferMode read FCurrentTransferMode write TransferMode;
- property DefStringEncoding : IIdTextEncoding read FDefStringEncoding write SetDefStringEncoding;
- property ServerInfo : TIdFTPServerIdentifier read FServerInfo;
- published
- {$IFDEF DOTNET}
- {$IFDEF DOTNET_2_OR_ABOVE}
- property IPVersion default ID_DEFAULT_IP_VERSION;
- {$ENDIF}
- {$ELSE}
- property IPVersion default ID_DEFAULT_IP_VERSION;
- {$ENDIF}
- property AutoIssueFEAT : Boolean read FAutoIssueFEAT write FAutoIssueFEAT default DEF_Id_FTP_AutoIssueFEAT;
- property AutoLogin: Boolean read FAutoLogin write FAutoLogin default DEF_Id_FTP_AutoLogin;
- // This is an object that can compress and decompress FTP Deflate encoding
- property Compressor : TIdZLibCompressorBase read FCompressor write SetCompressor;
- property Host;
- property UseCCC : Boolean read FUseCCC write SetUseCCC default DEF_Id_FTP_UseCCC;
- property Passive: boolean read FPassive write SetPassive default Id_TIdFTP_Passive;
- property PassiveUseControlHost: Boolean read FPassiveUseControlHost write FPassiveUseControlHost default DEF_Id_FTP_PassiveUseControlHost;
- property DataPortProtection : TIdFTPDataPortSecurity read FDataPortProtection write SetDataPortProtection default Id_TIdFTP_DataPortProtection;
- property AUTHCmd : TAuthCmd read FAUTHCmd write SetAUTHCmd default DEF_Id_FTP_AUTH_CMD;
- property ConnectTimeout;
- property DataPort: TIdPort read FDataPort write FDataPort default 0;
- property DataPortMin: TIdPort read FDataPortMin write FDataPortMin default 0;
- property DataPortMax: TIdPort read FDataPortMax write FDataPortMax default 0;
- property ExternalIP : String read FExternalIP write FExternalIP;
- property Password;
- property TransferType: TIdFTPTransferType read FTransferType write SetTransferType default Id_TIdFTP_TransferType;
- property TransferTimeout: Integer read FTransferTimeout write FTransferTimeout default IdDefTimeout;
- property ListenTimeout : Integer read FListenTimeout write FListenTimeout default DEF_Id_FTP_ListenTimeout;
- property Username;
- property Port default IDPORT_FTP;
- property UseExtensionDataPort : Boolean read FUseExtensionDataPort write SetUseExtensionDataPort default DEF_Id_TIdFTP_UseExtendedData;
- property UseMLIS : Boolean read FUseMLIS write FUseMLIS default DEF_Id_TIdFTP_UseMIS;
- property TryNATFastTrack : Boolean read FTryNATFastTrack write SetTryNATFastTrack default Id_TIdFTP_UseNATFastTrack;
- property NATKeepAlive: TIdFTPKeepAlive read FNATKeepAlive write SetNATKeepAlive;
- property ProxySettings: TIdFtpProxySettings read FProxySettings write SetProxySettings;
- property Account: string read FAccount write FAccount;
- property ClientInfo : TIdFTPClientIdentifier read FClientInfo write SetClientInfo;
- property UseHOST: Boolean read FUseHOST write FUseHOST default DEF_Id_FTP_UseHOST;
- property ServerHOST: String read FServerHOST write FServerHOST;
- property UseTLS;
- property OnTLSNotAvailable;
- property OnBannerBeforeLogin : TIdFTPBannerEvent read FOnBannerBeforeLogin write FOnBannerBeforeLogin;
- property OnBannerAfterLogin : TIdFTPBannerEvent read FOnBannerAfterLogin write FOnBannerAfterLogin;
- property OnBannerWarning : TIdFTPBannerEvent read FOnBannerWarning write FOnBannerWarning;
- property OnBeforeGet: TNotifyEvent read FOnBeforeGet write FOnBeforeGet;
- property OnBeforePut: TIdFtpAfterGet read FOnBeforePut write FOnBeforePut;
- property OnAfterClientLogin: TOnAfterClientLogin read FOnAfterClientLogin write FOnAfterClientLogin;
- property OnCreateFTPList: TIdCreateFTPList read FOnCreateFTPList write FOnCreateFTPList;
- property OnAfterGet: TIdFtpAfterGet read FOnAfterGet write FOnAfterGet; //APR
- property OnAfterPut: TNotifyEvent read FOnAfterPut write FOnAfterPut;
- property OnNeedAccount: TIdNeedAccountEvent read FOnNeedAccount write FOnNeedAccount;
- property OnCustomFTPProxy : TNotifyEvent read FOnCustomFTPProxy write FOnCustomFTPProxy;
- property OnDataChannelCreate: TIdOnDataChannelCreate read FOnDataChannelCreate write FOnDataChannelCreate;
- property OnDataChannelDestroy: TIdOnDataChannelDestroy read FOnDataChannelDestroy write FOnDataChannelDestroy;
- //The directory was Retrieved from the FTP server.
- property OnRetrievedDir : TNotifyEvent read FOnRetrievedDir write FOnRetrievedDir;
- //parsing is done only when DirectoryLiusting is referenced
- property OnDirParseStart : TNotifyEvent read FOnDirParseStart write FOnDirParseStart;
- property OnDirParseEnd : TNotifyEvent read FOnDirParseEnd write FOnDirParseEnd;
- property ReadTimeout default DEF_Id_FTP_READTIMEOUT;
- end;
- EIdFTPException = class(EIdException);
- EIdFTPFileAlreadyExists = class(EIdFTPException);
- EIdFTPMustUseExtWithIPv6 = class(EIdFTPException);
- EIdFTPMustUseExtWithNATFastTrack = class(EIdFTPException);
- EIdFTPPassiveMustBeTrueWithNATFT = class(EIdFTPException);
- EIdFTPServerSentInvalidPort = class(EIdFTPException);
- EIdFTPSiteToSiteTransfer = class(EIdFTPException);
- EIdFTPSToSNATFastTrack = class(EIdFTPSiteToSiteTransfer);
- EIdFTPSToSNoDataProtection = class(EIdFTPSiteToSiteTransfer);
- EIdFTPSToSIPProtoMustBeSame = class(EIdFTPSiteToSiteTransfer);
- EIdFTPSToSBothMostSupportSSCN = class(EIdFTPSiteToSiteTransfer);
- EIdFTPSToSTransModesMustBeSame = class(EIdFTPSiteToSiteTransfer);
- EIdFTPOnCustomFTPProxyRequired = class(EIdFTPException);
- EIdFTPConnAssuranceFailure = class(EIdFTPException);
- EIdFTPWrongIOHandler = class(EIdFTPException);
- EIdFTPUploadFileNameCanNotBeEmpty = class(EIdFTPException);
- EIdFTPDataPortProtection = class(EIdFTPException);
- EIdFTPNoDataPortProtectionAfterCCC = class(EIdFTPDataPortProtection);
- EIdFTPNoDataPortProtectionWOEncryption = class(EIdFTPDataPortProtection);
- EIdFTPNoCCCWOEncryption = class(EIdFTPException);
- EIdFTPAUTHException = class(EIdFTPException);
- EIdFTPNoAUTHWOSSL = class(EIdFTPAUTHException);
- EIdFTPCanNotSetAUTHCon = class(EIdFTPAUTHException);
- EIdFTPMissingCompressor = class(EIdFTPException);
- EIdFTPCompressorNotReady = class(EIdFTPException);
- EIdFTPUnsupportedTransferMode = class(EIdFTPException);
- EIdFTPUnsupportedTransferType = class(EIdFTPException);
- implementation
- uses
- //facilitate inlining only.
- {$IFDEF KYLIXCOMPAT}
- Libc,
- {$ENDIF}
- {$IFDEF USE_VCL_POSIX}
- Posix.SysSelect,
- Posix.SysTime,
- Posix.Unistd,
- {$ENDIF}
- {$IFDEF DOTNET}
- {$IFDEF USE_INLINE}
- System.IO,
- System.Threading,
- {$ENDIF}
- {$ENDIF}
- IdComponent,
- IdFIPS,
- IdResourceStringsCore, IdIOHandlerStack, IdResourceStringsProtocols,
- IdSSL, IdGlobalProtocols, IdHash, IdHashCRC, IdHashSHA, IdHashMessageDigest,
- IdStack, IdStackConsts, IdSimpleServer, IdOTPCalculator, SysUtils;
- const
- cIPVersions: array[TIdIPVersion] of String = ('1', '2'); {do not localize}
- type
- TIdFTPListResult = class(TStringList)
- private
- FDetails: Boolean; //Did the developer use the NLST command for the last list command
- FUsedMLS : Boolean; //Did the developer use MLSx commands for the last list command
- public
- property Details: Boolean read FDetails;
- property UsedMLS: Boolean read FUsedMLS;
- end;
- {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
- constructor TIdFTP.Create(AOwner: TComponent);
- begin
- inherited Create(AOwner);
- end;
- {$ENDIF}
- procedure TIdFTP.InitComponent;
- begin
- inherited InitComponent;
- //
- FIPVersion := ID_DEFAULT_IP_VERSION;
- //
- FAutoLogin := DEF_Id_FTP_AutoLogin;
- FRegularProtPort := IdPORT_FTP;
- FImplicitTLSProtPort := IdPORT_ftps;
- FExplicitTLSProtPort := IdPORT_FTP;
- //
- Port := IDPORT_FTP;
- Passive := Id_TIdFTP_Passive;
- FPassiveUseControlHost := DEF_Id_FTP_PassiveUseControlHost;
- FDataPortProtection := Id_TIdFTP_DataPortProtection;
- FUseCCC := DEF_Id_FTP_UseCCC;
- FAUTHCmd := DEF_Id_FTP_AUTH_CMD;
- FUseHOST := DEF_Id_FTP_UseHOST;
- FDataPort := 0;
- FDataPortMin := 0;
- FDataPortMax := 0;
- FDefStringEncoding := IndyTextEncoding_8Bit;
- FUseExtensionDataPort := DEF_Id_TIdFTP_UseExtendedData;
- FTryNATFastTrack := Id_TIdFTP_UseNATFastTrack;
- FTransferType := Id_TIdFTP_TransferType;
- FTransferTimeout := IdDefTimeout;
- FListenTimeout := DEF_Id_FTP_ListenTimeout;
- FLoginMsg := TIdReplyFTP.Create(nil);
- FListResult := TIdFTPListResult.Create;
- FLangsSupported := TStringList.Create;
- FCanResume := False;
- FResumeTested := False;
- FProxySettings:= TIdFtpProxySettings.Create; //APR
- FClientInfo := TIdFTPClientIdentifier.Create;
- FServerInfo := TIdFTPServerIdentifier.Create;
- FTZInfo := TIdFTPTZInfo.Create;
- FTZInfo.FGMTOffsetAvailable := False;
- FUseMLIS := DEF_Id_TIdFTP_UseMIS;
- FCanUseMLS := False; //initialize MLIS flags
- //Settings specified by
- // http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt
- FZLibCompressionLevel := DEF_ZLIB_COMP_LEVEL;
- FZLibWindowBits := DEF_ZLIB_WINDOW_BITS; //-15 - no extra headers
- FZLibMemLevel := DEF_ZLIB_MEM_LEVEL;
- FZLibStratagy := DEF_ZLIB_STRATAGY; // - default
- //
- FAbortFlag := TIdThreadSafeBoolean.Create;
- FAbortFlag.Value := False;
- {
- Some firewalls don't handle control connections properly during long
- data transfers. They will timeout the control connection because it
- is idle and making it worse is that they will chop off a connection
- instead of closing it, causing TIdFTP to wait forever for nothing.
- }
- FNATKeepAlive := TIdFTPKeepAlive.Create;
- ReadTimeout := DEF_Id_FTP_READTIMEOUT;
- FAutoIssueFEAT := DEF_Id_FTP_AutoIssueFEAT;
- end;
- {$IFNDEF HAS_TryEncodeTime}
- // TODO: move this to IdGlobal or IdGlobalProtocols...
- function TryEncodeTime(Hour, Min, Sec, MSec: Word; out VTime: TDateTime): Boolean;
- begin
- try
- VTime := EncodeTime(Hour, Min, Sec, MSec);
- Result := True;
- except
- Result := False;
- end;
- end;
- {$ENDIF}
- {$IFNDEF HAS_TryStrToInt}
- // TODO: use the implementation already in IdGlobalProtocols...
- function TryStrToInt(const S: string; out Value: Integer): Boolean;
- {$IFDEF USE_INLINE}inline;{$ENDIF}
- var
- E: Integer;
- begin
- Val(S, Value, E);
- Result := E = 0;
- end;
- {$ENDIF}
- procedure TIdFTP.Connect;
- var
- LHost: String;
- LPort: TIdPort;
- LBuf : String;
- LSendQuitOnError: Boolean;
- LOffs: Integer;
- LRetryWithoutHOST: Boolean;
- begin
- LSendQuitOnError := False;
- FCurrentTransferMode := dmStream;
- FTZInfo.FGMTOffsetAvailable := False;
- //FSSCNOn should be set to false to prevent problems.
- FSSCNOn := False;
- FUsingSFTP := False;
- FUsingCCC := False;
- FDataSettingsSent := False;
- if FUseExtensionDataPort then begin
- FUsingExtDataPort := True;
- end;
- FUsingNATFastTrack := False;
- FCapabilities.Clear;
- try
- //APR 011216: proxy support
- LHost := FHost;
- LPort := FPort;
- try
- //I think fpcmTransparent means to connect to the regular host and the firewalll
- //intercepts the login information.
- if (ProxySettings.ProxyType <> fpcmNone) and (ProxySettings.ProxyType <> fpcmTransparent) and
- (Length(ProxySettings.Host) > 0) then begin
- FHost := ProxySettings.Host;
- FPort := ProxySettings.Port;
- end;
- if FUseTLS = utUseImplicitTLS then begin
- //at this point, we treat implicit FTP as if it were explicit FTP with TLS
- FUsingSFTP := True;
- end;
- inherited Connect;
- finally
- FHost := LHost;
- FPort := LPort;
- end;
- // RLebeau: must not send/receive UTF-8 before negotiating for it...
- IOHandler.DefStringEncoding := FDefStringEncoding;
- {$IFDEF STRING_IS_ANSI}
- IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
- {$ENDIF}
- // RLebeau: RFC 959 says that the greeting can be preceeded by a 1xx
- // reply and that the client should wait for the 220 reply when this
- // happens. Also, the RFC says that 120 should be used, but some
- // servers use other 1xx codes, such as 130, so handle 1xx generically
- // calling GetInternalResponse() directly to avoid duplicate calls
- // to CheckResponse() for the initial response if it is not 1xx
- GetInternalResponse;
- if (LastCmdResult.NumericCode div 100) = 1 then begin
- DoOnBannerWarning(LastCmdResult.FormattedReply);
- GetResponse(220);
- end else begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(LastCmdResult.NumericCode, [220]);
- end;
- LSendQuitOnError := True;
- FGreeting.Assign(LastCmdResult);
- // Save initial greeting for server identification in case FGreeting changes
- // in response to the HOST command
- if FGreeting.Text.Count > 0 then begin
- FServerDesc := FGreeting.Text[0];
- end else begin
- FServerDesc := '';
- end;
- // Implement HOST command as specified by
- // http://tools.ietf.org/html/draft-hethmon-mcmurray-ftp-hosts-01
- // Do not check the response for failures. The draft suggests allowing
- // 220 (success) and 500/502 (unsupported), but vsftpd returns 530, and
- // whatever ftp.microsoft.com is running returns 504.
- if UseHOST then begin
- // RLebeau: WS_FTP Server 5.x disconnects if the command fails,
- // whereas WS_FTP Server 6+ does not. If the server disconnected
- // here, let's mimic FTP Voyager by reconnecting without using
- // the HOST command again...
- //
- // RLebeau 11/18/2013: some other servers also disconnect on a failed
- // HOST command, so no longer retrying connect for WSFTP exclusively...
- //
- // RLebeau 11/22/2014: encountered one case where the server disconnects
- // before the reply is received. So checking for that as well...
- //
- LRetryWithoutHOST := False;
- try
- if SendHost() <> 220 then begin
- IOHandler.CheckForDisconnect(True, True);
- end;
- except
- on E: EIdConnClosedGracefully do begin
- LRetryWithoutHOST := True;
- end;
- on E: EIdSocketError do begin
- if (E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET) then begin
- LRetryWithoutHOST := True;
- end else begin
- raise;
- end;
- end;
- end;
- if LRetryWithoutHOST then
- begin
- Disconnect(False);
- if Assigned(IOHandler) then begin
- IOHandler.InputBuffer.Clear;
- end;
- UseHOST := False;
- try
- Connect;
- finally
- UseHOST := True;
- end;
- Exit;
- end;
- end else begin
- FGreeting.Assign(LastCmdResult);
- end;
- DoOnBannerBeforeLogin (FGreeting.FormattedReply);
- // RLebeau: having an AutoIssueFeat property doesn't make sense to
- // me. There are commands below that require FEAT's response, but
- // if the user sets AutoIssueFeat to False, these commands will not
- // be allowed to execute!
- if AutoLogin then begin
- Login;
- DoAfterLogin;
- //Fast track is set only one time per connection and no more, even
- //with REINIT
- if TryNATFastTrack then begin
- DoTryNATFastTrack;
- end;
- if FUseTLS = utUseImplicitTLS then begin
- //at this point, we treat implicit FTP as if it were explicit FTP with TLS
- FUsingSFTP := True;
- end;
- // OpenVMS 7.1 replies with 200 instead of 215 - What does the RFC say about this?
- // if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('SYST', [200, 215, 500]) = 500 then begin {do not localize}
- //Do not fault if SYST was not understood by the server. Novel Netware FTP
- //may not understand SYST.
- if SendCmd('SYST') = 500 then begin {do not localize}
- FSystemDesc := RSFTPUnknownHost;
- end else begin
- FSystemDesc := LastCmdResult.Text[0];
- end;
- if IsSiteZONESupported then begin
- if SendCmd('SITE ZONE') = 210 then begin {do not localize}
- if LastCmdResult.Text.Count > 0 then begin
- LBuf := LastCmdResult.Text[0];
- // some servers (Serv-U, etc) use a 'UTC' offset string, ie
- // "UTC-300", specifying the number of minutes from UTC. Other
- // servers (Apache) use a GMT offset string instead, ie "-0300".
- if TextStartsWith(LBuf, 'UTC-') then begin {do not localize}
- // Titan FTP 6.26.634 incorrectly returns UTC-2147483647 when it's
- // first installed.
- FTZInfo.FGMTOffsetAvailable :=
- TryStrToInt(Copy(LBuf, 4, MaxInt), LOffs) and
- TryEncodeTime(Abs(LOffs) div 60, Abs(LOffs) mod 60, 0, 0, FTZInfo.FGMTOffset);
- if FTZInfo.FGMTOffsetAvailable and (LOffs < 0) then
- FTZInfo.FGMTOffset := -FTZInfo.FGMTOffset
- end else begin
- FTZInfo.FGMTOffsetAvailable := True;
- FTZInfo.GMTOffset := GmtOffsetStrToDateTime(LBuf);
- end;
- end;
- end;
- end;
- SendTransferType(FTransferType);
- {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpReady, [RSFTPStatusReady]);
- end else begin
- // OpenVMS 7.1 replies with 200 instead of 215 - What does the RFC say about this?
- // if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('SYST', [200, 215, 500]) = 500 then begin {do not localize}
- //Do not fault if SYST was not understood by the server. Novel Netware FTP
- //may not understand SYST.
- if SendCmd('SYST') = 500 then begin {do not localize}
- FSystemDesc := RSFTPUnknownHost;
- end else begin
- FSystemDesc := LastCmdResult.Text[0];
- end;
- if FAutoIssueFEAT then begin
- IssueFEAT;
- end;
- end;
- except
- Disconnect(LSendQuitOnError); // RLebeau: do not send the QUIT command if the greeting was not received
- raise;
- end;
- end;
- function TIdFTP.SendHost: Int16;
- var
- LHost: String;
- begin
- LHost := FServerHOST;
- if LHost = '' then begin
- LHost := FHost;
- end;
- if Socket <> nil then begin
- if (IPVersion = Id_IPv6) and (MakeCanonicalIPv6Address(LHost) <> '') then begin
- LHost := '[' + LHost + ']'; {do not localize}
- end;
- end;
- Result := SendCmd('HOST ' + LHost); {do not localize}
- end;
- procedure TIdFTP.SetTransferType(AValue: TIdFTPTransferType);
- begin
- if AValue <> FTransferType then begin
- if not Assigned(FDataChannel) then begin
- if Connected then begin
- SendTransferType(AValue);
- end;
- FTransferType := AValue;
- end;
- end;
- end;
- procedure TIdFTP.SendTransferType(AValue: TIdFTPTransferType);
- var
- s: string;
- begin
- s := '';
- case AValue of
- ftAscii: s := 'A'; {do not localize}
- ftBinary: s := 'I'; {do not localize}
- else
- raise EIdFTPUnsupportedTransferType.Create(RSFTPUnsupportedTransferType);
- end;
- SendCmd('TYPE ' + s, 200); {do not localize}
- end;
- function TIdFTP.ResumeSupported: Boolean;
- begin
- if not FResumeTested then begin
- FResumeTested := True;
- FCanResume := Quote('REST 1') = 350; {do not localize}
- Quote('REST 0'); {do not localize}
- end;
- Result := FCanResume;
- end;
- procedure TIdFTP.Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = False);
- begin
- //for SSL FXP, we have to do it here because InternalGet is used by the LIST command
- //where SSCN is ignored.
- ClearSSCN;
- AResume := AResume and CanResume;
- DoBeforeGet;
- // RLebeau 7/26/06: do not do this! It breaks the ability to resume files
- // ADest.Position := 0;
- InternalGet('RETR ' + ASourceFile, ADest, AResume);
- DoAfterGet(ADest);
- end;
- procedure TIdFTP.Get(const ASourceFile, ADestFile: string; const ACanOverwrite: Boolean = False;
- AResume: Boolean = False);
- var
- LDestStream: TStream;
- begin
- AResume := AResume and CanResume;
- if ACanOverwrite and (not AResume) then begin
- SysUtils.DeleteFile(ADestFile);
- LDestStream := TIdFileCreateStream.Create(ADestFile);
- end
- else if (not ACanOverwrite) and AResume then begin
- LDestStream := TIdAppendFileStream.Create(ADestFile);
- end
- else if not FileExists(ADestFile) then begin
- LDestStream := TIdFileCreateStream.Create(ADestFile);
- end
- else begin
- raise EIdFTPFileAlreadyExists.Create(RSDestinationFileAlreadyExists);
- end;
- try
- Get(ASourceFile, LDestStream, AResume);
- finally
- FreeAndNil(LDestStream);
- end;
- end;
- procedure TIdFTP.DoBeforeGet;
- begin
- if Assigned(FOnBeforeGet) then begin
- FOnBeforeGet(Self);
- end;
- end;
- procedure TIdFTP.DoBeforePut(AStream: TStream);
- begin
- if Assigned(FOnBeforePut) then begin
- FOnBeforePut(Self, AStream);
- end;
- end;
- procedure TIdFTP.DoAfterGet(AStream: TStream);//APR
- begin
- if Assigned(FOnAfterGet) then begin
- FOnAfterGet(Self, AStream);
- end;
- end;
- procedure TIdFTP.DoAfterPut;
- begin
- if Assigned(FOnAfterPut) then begin
- FOnAfterPut(Self);
- end;
- end;
- procedure TIdFTP.ConstructDirListing;
- begin
- if not Assigned(FDirectoryListing) then begin
- if not IsDesignTime then begin
- DoFTPList;
- end;
- if not Assigned(FDirectoryListing) then begin
- FDirectoryListing := TIdFTPListItems.Create;
- end;
- end else begin
- FDirectoryListing.Clear;
- end;
- end;
- procedure TIdFTP.List(ADest: TStrings; const ASpecifier: string = ''; ADetails: Boolean = True); {do not localize}
- var
- LDest: TMemoryStream;
- LTrans : TIdFTPTransferType;
- begin
- if ADetails and UseMLIS and FCanUseMLS then begin
- ExtListDir(ADest, ASpecifier);
- Exit;
- end;
- // Note that for LIST, it might be best to put the connection in ASCII mode
- // because some old servers such as TOPS20 might require this. We restore
- // it if the original mode was not ASCII. It's a good idea to do this
- // anyway because some clients still do this such as WS_FTP Pro and
- // Microsoft's FTP Client.
- LTrans := TransferType;
- if LTrans <> ftASCII then begin
- Self.TransferType := ftASCII;
- end;
- try
- LDest := TMemoryStream.Create;
- try
- InternalGet(TrimRight(iif(ADetails, 'LIST', 'NLST') + ' ' + ASpecifier), LDest); {do not localize}
- FreeAndNil(FDirectoryListing);
- FDirFormat := '';
- LDest.Position := 0;
- FListResult.Text := ReadStringFromStream(LDest, -1, IOHandler.DefStringEncoding{$IFDEF STRING_IS_ANSI}, IOHandler.DefAnsiEncoding{$ENDIF});
- TIdFTPListResult(FListResult).FDetails := ADetails;
- TIdFTPListResult(FListResult).FUsedMLS := False;
- // FDirFormat will be updated in ParseFTPList...
- finally
- FreeAndNil(LDest);
- end;
- if ADest <> nil then begin
- ADest.Assign(FListResult);
- end;
- DoOnRetrievedDir;
- finally
- if LTrans <> ftASCII then begin
- TransferType := LTrans;
- end;
- end;
- end;
- const
- AbortedReplies : array [0..5] of Int16 =
- (226,426, 450,451,425,550);
- //226 was added because one server will return that twice if you aborted
- //during an upload.
- AcceptableAbortReplies : array [0..8] of Int16 =
- (225, 226, 250, 426, 450,451,425,550,552);
- //GlobalScape Secure FTP Server returns a 552 for an aborted file
-
- procedure TIdFTP.FinalizeDataOperation;
- var
- LResponse : Int16;
- begin
- DoOnDataChannelDestroy;
- if FDataChannel <> nil then begin
- FDataChannel.IOHandler := nil;
- FreeAndNil(FDataChannel);
- end;
- {
- This is a bug fix for servers will do something like this:
- [2] Mon 06Jun05 13:33:28 - (000007) PASV
- [6] Mon 06Jun05 13:33:28 - (000007) 227 Entering Passive Mode (192,168,1,107,4,22)
- [2] Mon 06Jun05 13:33:28 - (000007) RETR test.txt.txt
- [6] Mon 06Jun05 13:33:28 - (000007) 550 /test.txt.txt: No such file or directory.
- [2] Mon 06Jun05 13:34:28 - (000007) QUIT
- [6] Mon 06Jun05 13:34:28 - (000007) 221 Goodbye!
- [5] Mon 06Jun05 13:34:28 - (000007) Closing connection for user TEST (00:01:08 connected)
- }
- if (LastCmdResult.NumericCode div 100) > 2 then
- begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpAborted, [RSFTPStatusAbortTransfer]);
- Exit;
- end;
- {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpReady, [RSFTPStatusDoneTransfer]);
- // 226 = download successful, 225 = Abort successful}
- if FAbortFlag.Value then begin
- LResponse := {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}(AcceptableAbortReplies);
- //Experimental -
- if PosInSmallIntArray(LResponse,AbortedReplies) > -1 then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([226, 225]);
- end;
- //IMPORTANT!!! KEEP THIS COMMENT!!!
- //
- //This is a workaround for a problem. When uploading a file on
- //one FTP server and aborting that upload, I got this:
- //
- //Sent 3/9/2005 10:34:58 AM: STOR --------
- //Recv 3/9/2005 10:34:58 AM: 150 Opening BINARY mode data connection for [3513]Red_Glas.zip
- //Sent 3/9/2005 10:34:59 AM: ABOR
- //Recv 3/9/2005 10:35:00 AM: 226 Transfer complete.
- //Recv 3/9/2005 10:35:00 AM: 226 Abort successful
- //
- //but at ftp.ipswitch.com (a WS_FTP Server 5.0.4 (2555009845) server ),
- //I was getting this when aborting a download
- //
- //Sent 3/9/2005 12:43:41 AM: RETR imail6.pdf
- //Recv 3/9/2005 12:43:41 AM: 150 Opening BINARY data connection for imail6.pdf (2150082 bytes)
- //Sent 3/9/2005 12:43:42 AM: ABOR
- //Recv 3/9/2005 12:43:42 AM: 226 abort successful
- //Recv 3/9/2005 12:43:43 AM: 425 transfer canceled
- //
- if LResponse = 226 then begin
- if IOHandler.Readable(10) then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}(AbortedReplies);
- end;
- end;
- {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpAborted, [RSFTPStatusAbortTransfer]);
- //end experimental section
- end else begin
- //ftp.marist.edu returns 250
- {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([226, 225, 250]);
- end;
- end;
- procedure TIdFTP.InternalPut(const ACommand: string; ASource: TStream;
- AFromBeginning: Boolean = True; AResume: Boolean = False);
- {$IFNDEF MSWINDOWS}
- procedure WriteStreamFromBeginning;
- var
- LBuffer: TIdBytes;
- LBufSize: Integer;
- begin
- // Copy entire stream without relying on ASource.Size so Unix-to-DOS
- // conversion can be done on the fly.
- BeginWork(wmWrite, ASource.Size);
- try
- SetLength(LBuffer, FDataChannel.IOHandler.SendBufferSize);
- while True do begin
- LBufSize := ASource.Read(LBuffer[0], Length(LBuffer));
- if LBufSize > 0 then
- FDataChannel.IOHandler.Write(LBuffer, LBufSize)
- else
- Break;
- end;
- finally
- EndWork(wmWrite);
- end;
- end;
- {$ENDIF}
- var
- LIP: string;
- LPort: TIdPort;
- LPasvCl : TIdTCPClient;
- LPortSv : TIdSimpleServer;
- LSocketList, LReadList: TIdSocketList;
- LDataSocket: TIdStackSocketHandle;
- // under ARC, convert a weak reference to a strong reference before working with it
- LCompressor : TIdZLibCompressorBase;
- begin
- FAbortFlag.Value := False;
- LCompressor := nil;
- if FCurrentTransferMode = dmDeflate then begin
- LCompressor := FCompressor;
- if not Assigned(LCompressor) then begin
- raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor);
- end;
- if not LCompressor.IsReady then begin
- raise EIdFTPCompressorNotReady.Create(RSFTPCompressorNotReady);
- end;
- end;
- //for SSL FXP, we have to do it here because there is no command were a client
- //submits data through a data port where the SSCN setting is ignored.
- ClearSSCN;
- {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpTransfer, [RSFTPStatusStartTransfer]);
- // try
- if FPassive then begin
- SendPret(ACommand);
- if FUsingExtDataPort then begin
- SendEPassive(LIP, LPort);
- end else begin
- SendPassive(LIP, LPort);
- end;
- // TODO: InternalGet() does not send these commands until after the data channel
- // is established, should we be doing the same here?
- if AResume then begin
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ASource.Position), [350]); {do not localize}
- end;
- IOHandler.WriteLn(ACommand);
- //
- if Socket <> nil then begin
- FDataChannel := TIdTCPClient.Create(nil);
- end else begin
- FDataChannel := nil;
- end;
- LPasvCl := TIdTCPClient(FDataChannel);
- try
- InitDataChannel;
- if (Self.Socket <> nil) and PassiveUseControlHost then begin
- //Do not use an assignment from Self.Host
- //because a DNS name may not resolve to the same
- //IP address every time. This is the case where
- //the workload is distributed around several servers.
- //Besides, we already know the Peer's IP address so
- //why waste time querying it.
- LIP := Self.Socket.Binding.PeerIP;
- end;
- if LPasvCl <> nil then begin
- LPasvCl.Host := LIP;
- LPasvCl.Port := LPort;
- DoOnDataChannelCreate;
- // TODO: if Connect() fails and PassiveUseControlHost is false, try connecting to the command host...
- LPasvCl.Connect;
- end;
- try
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([110, 125, 150]);
- try
- if FDataChannel <> nil then begin
- if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin
- TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False;
- end;
- if Assigned(LCompressor) then begin
- LCompressor.CompressFTPToIO(ASource, FDataChannel.IOHandler,
- FZLibCompressionLevel, FZLibWindowBits, FZLibMemLevel, FZLibStratagy);
- end else begin
- if AFromBeginning then begin
- {$IFNDEF MSWINDOWS}
- WriteStreamFromBeginning;
- {$ELSE}
- FDataChannel.IOHandler.Write(ASource, 0, False); // from beginning
- {$ENDIF}
- end else begin
- FDataChannel.IOHandler.Write(ASource, -1, False); // from current position
- end;
- end;
- end;
- except
- on E: EIdSocketError do
- begin
- // If 10038 - abort was called. Server will return 225
- if E.LastError <> 10038 then begin
- raise;
- end;
- end;
- end;
- finally
- if LPasvCl <> nil then begin
- LPasvCl.Disconnect(False);
- end;
- end;
- finally
- FinalizeDataOperation;
- end;
- end else begin
- if Socket <> nil then begin
- FDataChannel := TIdSimpleServer.Create(nil);
- end else begin
- FDataChannel := nil;
- end;
- LPortSv := TIdSimpleServer(FDataChannel);
- try
- InitDataChannel;
- if LPortSv <> nil then begin
- LPortSv.BoundIP := Self.Socket.Binding.IP;
- LPortSv.BoundPort := FDataPort;
- LPortSv.BoundPortMin := FDataPortMin;
- LPortSv.BoundPortMax := FDataPortMax;
- DoOnDataChannelCreate;
- LPortSv.BeginListen;
- if FUsingExtDataPort then begin
- SendEPort(LPortSv.Binding);
- end else begin
- SendPort(LPortSv.Binding);
- end;
- if AResume then begin
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ASource.Position), [350]); {do not localize}
- end;
- // RLebeau 5/15/2020: there are some FTP servers (vsFTPd, etc) that will try to
- // establish the transfer connection as soon as they receive the STOR/STOU/APPE
- // command and before sending a response, thus causing SendCmd() to hang and the
- // connection to fail. Per RFC 959 Section 3.2:
- //
- // "The passive data transfer process (this may be a user-DTP or a second server-DTP)
- // shall "listen" on the data port prior to sending a transfer request command. The
- // FTP request command determines the direction of the data transfer. The server,
- // upon receiving the transfer request, will initiate the data connection to the port.
- // When the connection is established, the data transfer begins between DTP's, and the
- // server-PI sends a confirming reply to the user-PI."
- //
- // So, since we have now seen cases where a server sends a reply first and then opens
- // the connection, and cases where a server opens the connection first and then sends
- // a reply, we need to monitor both ports simultaneously and act accordingly...
- //Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(ACommand, [125, 150]);
- LSocketList := TIdSocketList.CreateSocketList;
- try
- LDataSocket := LPortSv.Binding.Handle;
- LSocketList.Add(Socket.Binding.Handle);
- LSocketList.Add(LDataSocket);
- IOHandler.WriteLn(ACommand);
- LReadList := nil;
- if not LSocketList.SelectReadList(LReadList, ListenTimeout) then begin
- raise EIdAcceptTimeout.Create(RSAcceptTimeout);
- end;
- try
- if LReadList.ContainsSocket(LDataSocket) then
- begin
- LPortSv.Listen(0);
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([125, 150]);
- end else
- begin
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([125, 150]);
- LPortSv.Listen(ListenTimeout); // TODO: minus elapsed time already used by SelectReadList()
- end;
- finally
- LReadList.Free;
- end;
- finally
- LSocketList.Free;
- end;
- if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin
- TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False;
- end;
- if Assigned(LCompressor) then begin
- LCompressor.CompressFTPToIO(ASource, FDataChannel.IOHandler,
- FZLibCompressionLevel, FZLibWindowBits, FZLibMemLevel, FZLibStratagy);
- end
- else if AFromBeginning then begin
- {$IFNDEF MSWINDOWS}
- WriteStreamFromBeginning;
- {$ELSE}
- FDataChannel.IOHandler.Write(ASource, 0, False); // from beginning
- {$ENDIF}
- end else begin
- FDataChannel.IOHandler.Write(ASource, -1, False); // from current position
- end;
- end else
- begin
- // TODO:
- {
- if FUsingExtDataPort then begin
- SendEPort(?);
- end else begin
- SendPort(?);
- end;
- }
- if AResume then begin
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ASource.Position), [350]); {do not localize}
- end;
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(ACommand, [125, 150]);
- end;
- finally
- FinalizeDataOperation;
- end;
- end;
- { This will silently ignore the STOR request if the server has forcibly disconnected
- (kicked or timed out) before the request starts
- except
- //Note that you are likely to get an exception you abort a transfer
- //hopefully, this will make things work better.
- on E: EIdConnClosedGracefully do begin
- end;
- end;}
- { commented out because we might need to revert back to this
- if new code fails.
- if (LResponse = 426) or (LResponse = 450) then
- begin
- // some servers respond with 226 on ABOR
- ($IFDEF OVERLOADED_OPENARRAY_BUG)GetResponseArr($ELSE)GetResponse($ENDIF)([226, 225]);
- ($IFDEF OVERLOADED_OPENARRAY_BUG)DoStatusArr($ELSE)DoStatus($ENDIF)(ftpAborted, [RSFTPStatusAbortTransfer]);
- end;
- }
- end;
- procedure TIdFTP.InternalGet(const ACommand: string; ADest: TStream; AResume: Boolean = false);
- var
- LIP: string;
- LPort: TIdPort;
- LPasvCl : TIdTCPClient;
- LPortSv : TIdSimpleServer;
- LSocketList, LReadList: TIdSocketList;
- LDataSocket: TIdStackSocketHandle;
- // under ARC, convert a weak reference to a strong reference before working with it
- LCompressor: TIdZLibCompressorBase;
- begin
- FAbortFlag.Value := False;
- LCompressor := nil;
- if FCurrentTransferMode = dmDeflate then begin
- LCompressor := FCompressor;
- if not Assigned(LCompressor) then begin
- raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor);
- end;
- if not LCompressor.IsReady then begin
- raise EIdFTPCompressorNotReady.Create(RSFTPCompressorNotReady);
- end;
- end;
- {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpTransfer, [RSFTPStatusStartTransfer]);
- if FPassive then begin
- SendPret(ACommand);
- //PASV or EPSV
- if FUsingExtDataPort then begin
- SendEPassive(LIP, LPort);
- end else begin
- SendPassive(LIP, LPort);
- end;
- if Socket <> nil then begin
- FDataChannel := TIdTCPClient.Create(nil);
- end else begin
- FDataChannel := nil;
- end;
- LPasvCl := TIdTCPClient(FDataChannel);
- try
- InitDataChannel;
- if (Self.Socket <> nil) and PassiveUseControlHost then begin
- //Do not use an assignment from Self.Host
- //because a DNS name may not resolve to the same
- //IP address every time. This is the case where
- //the workload is distributed around several servers.
- //Besides, we already know the Peer's IP address so
- //why waste time querying it.
- LIP := Self.Socket.Binding.PeerIP;
- end;
- if LPasvCl <> nil then begin
- LPasvCl.Host := LIP;
- LPasvCl.Port := LPort;
- DoOnDataChannelCreate;
- // TODO: if Connect() fails and PassiveUseControlHost is false, try connecting to the command host...
- LPasvCl.Connect;
- end;
- try
- if AResume then begin
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ADest.Position), [350]); {do not localize}
- end;
- // APR: Ericsson Switch FTP
- //
- // RLebeau: some servers send 450 when no files are
- // present, so do not read the stream in that case
- if Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(ACommand, [125, 150, 154, 450]) <> 450 then
- begin
- if LPasvCl <> nil then begin
- if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin
- TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False;
- end;
- if Assigned(LCompressor) then begin
- LCompressor.DecompressFTPFromIO(LPasvCl.IOHandler, ADest, FZLibWindowBits);
- end else begin
- LPasvCl.IOHandler.ReadStream(ADest, -1, True);
- end;
- end;
- end;
- finally
- if LPasvCl <> nil then begin
- LPasvCl.Disconnect(False);
- end;
- end;
- finally
- FinalizeDataOperation;
- end;
- end else begin
- // PORT or EPRT
- if Socket <> nil then begin
- FDataChannel := TIdSimpleServer.Create(nil);
- end else begin
- FDataChannel := nil;
- end;
- LPortSv := TIdSimpleServer(FDataChannel);
- try
- InitDataChannel;
- if LPortSv <> nil then begin
- LPortSv.BoundIP := Self.Socket.Binding.IP;
- LPortSv.BoundPort := FDataPort;
- LPortSv.BoundPortMin := FDataPortMin;
- LPortSv.BoundPortMax := FDataPortMax;
- DoOnDataChannelCreate;
- LPortSv.BeginListen;
- if FUsingExtDataPort then begin
- SendEPort(LPortSv.Binding);
- end else begin
- SendPort(LPortSv.Binding);
- end;
- if AResume then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ADest.Position), [350]); {do not localize}
- end;
- // RLebeau 5/15/2020: there are some FTP servers (vsFTPd, etc) that will try to
- // establish the transfer connection as soon as they receive the STOR/STOU/APPE
- // command and before sending a response, thus causing SendCmd() to hang and the
- // connection to fail. Per RFC 959 Section 3.2:
- //
- // "The passive data transfer process (this may be a user-DTP or a second server-DTP)
- // shall "listen" on the data port prior to sending a transfer request command. The
- // FTP request command determines the direction of the data transfer. The server,
- // upon receiving the transfer request, will initiate the data connection to the port.
- // When the connection is established, the data transfer begins between DTP's, and the
- // server-PI sends a confirming reply to the user-PI."
- //
- // So, since we have now seen cases where a server sends a reply first and then opens
- // the connection, and cases where a server opens the connection first and then sends
- // a reply, we need to monitor both ports simultaneously and act accordingly...
- //SendCmd(ACommand, [125, 150, 154]); //APR: Ericsson Switch FTP);
- LSocketList := TIdSocketList.CreateSocketList;
- try
- LDataSocket := LPortSv.Binding.Handle;
- LSocketList.Add(Socket.Binding.Handle);
- LSocketList.Add(LDataSocket);
- IOHandler.WriteLn(ACommand);
- LReadList := nil;
- if not LSocketList.SelectReadList(LReadList, ListenTimeout) then begin
- raise EIdAcceptTimeout.Create(RSAcceptTimeout);
- end;
- try
- if LReadList.ContainsSocket(LDataSocket) then
- begin
- LPortSv.Listen(0);
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([125, 150, 154]);
- end else
- begin
- Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([125, 150, 154]);
- LPortSv.Listen(ListenTimeout); // TODO: minus elapsed time already used by SelectReadList()
- end;
- finally
- LReadList.Free;
- end;
- finally
- LSocketList.Free;
- end;
- if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin
- TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False;
- end;
- if Assigned(LCompressor) then begin
- LCompressor.DecompressFTPFromIO(LPortSv.IOHandler, ADest, FZLibWindowBits);
- end else begin
- FDataChannel.IOHandler.ReadStream(ADest, -1, True);
- end;
- end else
- begin
- // TODO:
- {
- if FUsingExtDataPort then begin
- SendEPort(?);
- end else begin
- SendPort(?);
- end;
- }
- if AResume then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ADest.Position), [350]); {do not localize}
- end;
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(ACommand, [125, 150, 154]); //APR: Ericsson Switch FTP);
- end;
- finally
- FinalizeDataOperation;
- end;
- end;
- // ToDo: Change that to properly handle response code (not just success or except)
- // 226 = download successful, 225 = Abort successful}
- //commented out in case we need to revert back to this.
- { LResponse := ($IFDEF OVERLOADED_OPENARRAY_BUG)GetResponseArr($ELSE)GetResponse($ENDIF)([225, 226, 250, 426, 450]);
- if (LResponse = 426) or (LResponse = 450) then begin
- ($IFDEF OVERLOADED_OPENARRAY_BUG)GetResponseArr($ELSE)GetResponse($ENDIF)([226, 225]);
- ($IFDEF OVERLOADED_OPENARRAY_BUG)DoStatusArr($ELSE)DoStatus($ENDIF)(ftpAborted, [RSFTPStatusAbortTransfer]);
- end; }
- end;
- procedure TIdFTP.DoOnDataChannelCreate;
- begin
- // While the Control Channel is idle, Enable/disable TCP/IP keepalives.
- // They're very small (40-byte) packages and will be sent every
- // NATKeepAlive.IntervalMS after the connection has been idle for
- // NATKeepAlive.IdleTimeMS. Prior to Windows 2000, the idle and
- // timeout values are system wide and have to be set in the registry;
- // the default is idle = 2 hours, interval = 1 second.
- if (Socket <> nil) and NATKeepAlive.UseKeepAlive then begin
- Socket.Binding.SetKeepAliveValues(True, NATKeepAlive.IdleTimeMS, NATKeepAlive.IntervalMS);
- end;
- if Assigned(FOnDataChannelCreate) then begin
- OnDataChannelCreate(Self, FDataChannel);
- end;
- end;
- procedure TIdFTP.DoOnDataChannelDestroy;
- begin
- if Assigned(FOnDataChannelDestroy) then begin
- OnDataChannelDestroy(Self, FDataChannel);
- end;
- if (Socket <> nil) and NATKeepAlive.UseKeepAlive then begin
- Socket.Binding.SetKeepAliveValues(False, 0, 0);
- end;
- end;
- procedure TIdFTP.SetNATKeepAlive(AValue: TIdFTPKeepAlive);
- begin
- FNATKeepAlive.Assign(AValue);
- end;
- { TIdFtpKeepAlive }
- procedure TIdFtpKeepAlive.Assign(Source: TPersistent);
- var
- LSource: TIdFTPKeepAlive;
- begin
- if Source is TIdFTPKeepAlive then begin
- LSource := TIdFTPKeepAlive(Source);
- FUseKeepAlive := LSource.UseKeepAlive;
- FIdleTimeMS := LSource.IdleTimeMS;
- FIntervalMS := LSource.IntervalMS;
- end else begin
- inherited Assign(Source);
- end;
- end;
- procedure TIdFTP.DisconnectNotifyPeer;
- begin
- inherited DisconnectNotifyPeer;
- IOHandler.WriteLn('QUIT'); {do not localize}
- IOHandler.CheckForDataOnSource(100);
- if not IOHandler.InputBufferIsEmpty then begin
- GetInternalResponse;
- end;
- end;
- {$I IdDeprecatedImplBugOff.inc}
- procedure TIdFTP.Quit;
- {$I IdDeprecatedImplBugOn.inc}
- begin
- Disconnect;
- end;
- procedure TIdFTP.KillDataChannel;
- begin
- // Had kill the data channel ()
- if Assigned(FDataChannel) then begin
- FDataChannel.Disconnect(False); //FDataChannel.IOHandler.DisconnectSocket; {//BGO}
- end;
- end;
- // IMPORTANT!!! THis is for later reference.
- //
- // Note that we do not send the Telnet IP and Sync as suggestedc by RFC 959.
- // We do not do so because some servers will mistakenly assume that the sequences
- // are part of the command and than give a syntax error.
- // I noticed this with FTPSERVE IBM VM Level 510, Microsoft FTP Service (Version 5.0),
- // GlobalSCAPE Secure FTP Server (v. 2.0), and Pure-FTPd [privsep] [TLS].
- //
- // Thus, I feel that sending sequences is just going to aggravate this situation.
- // It is probably the reason why some FTP clients no longer are sending Telnet IP
- // and Sync with the ABOR command.
- procedure TIdFTP.Abort;
- begin
- // only send the abort command. The Data channel is supposed to disconnect
- if Connected then begin
- IOHandler.WriteLn('ABOR'); {do not localize}
- end;
- // Kill the data channel: usually, the server doesn't close it by itself
- KillDataChannel;
- if Assigned(FDataChannel) then begin
- FAbortFlag.Value := True;
- end else begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([]);
- end;
- end;
- procedure TIdFTP.SendPort(AHandle: TIdSocketHandle);
- begin
- if FExternalIP <> '' then begin
- SendPort(FExternalIP, AHandle.Port);
- end else begin
- SendPort(AHandle.IP, AHandle.Port);
- end;
- end;
- procedure TIdFTP.SendPort(const AIP: String; const APort: TIdPort);
- begin
- SendDataSettings;
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PORT ' + ReplaceAll(AIP, '.', ',') {do not localize}
- + ',' + IntToStr(APort div 256) + ',' + IntToStr(APort mod 256), [200]); {do not localize}
- end;
- procedure TIdFTP.InitDataChannel;
- var
- LIOHandler : TIdIOHandler;
- begin
- if FDataChannel = nil then begin
- Exit;
- end;
- if FDataPortProtection = ftpdpsPrivate then begin
- LIOHandler := TIdSSLIOHandlerSocketBase(IOHandler).Clone;
- {$IFDEF USE_OBJECT_ARC}
- // under ARC, the TIdTCPConnection.IOHandler property is a weak reference.
- // TIdSSLIOHandlerSocketBase.Clone() returns an IOHandler with no Owner
- // assigned, so lets make FDataChannel become the Owner in order to keep
- // the IOHandler alive when this method exits.
- //
- // TODO: should we assign Ownership unconditionally on all platforms?
- //
- // TODO: add an AOwner parameter to Clone()
- //
- FDataChannel.InsertComponent(LIOHandler);
- {$ENDIF}
- //we have to delay the actual negotiation until we get the reply and
- //just before the readString
- TIdSSLIOHandlerSocketBase(LIOHandler).PassThrough := True;
- end else begin
- LIOHandler := TIdIOHandler.MakeDefaultIOHandler(FDataChannel);
- end;
- FDataChannel.IOHandler := LIOHandler;
- FDataChannel.ManagedIOHandler := True;
- if FDataChannel is TIdTCPClient then
- begin
- TIdTCPClient(FDataChannel).IPVersion := IPVersion;
- TIdTCPClient(FDataChannel).ReadTimeout := FTransferTimeout;
- //Now SocksInfo are multi-thread safe
- FDataChannel.IOHandler.ConnectTimeout := IOHandler.ConnectTimeout;
- end
- else if FDataChannel is TIdSimpleServer then
- begin
- TIdSimpleServer(FDataChannel).IPVersion := IPVersion;
- end;
- if Assigned(FDataChannel.Socket) and Assigned(Socket) then
- begin
- FDataChannel.Socket.TransparentProxy := Socket.TransparentProxy;
- end;
- FDataChannel.IOHandler.ReadTimeout := FTransferTimeout;
- FDataChannel.IOHandler.SendBufferSize := IOHandler.SendBufferSize;
- FDataChannel.IOHandler.RecvBufferSize := IOHandler.RecvBufferSize;
- FDataChannel.IOHandler.LargeStream := True;
- // FDataChannel.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
- // FDataChannel.IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
- FDataChannel.WorkTarget := Self;
- end;
- procedure TIdFTP.Put(const ASource: TStream; const ADestFile: string;
- const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1);
- begin
- if ADestFile = '' then begin
- raise EIdFTPUploadFileNameCanNotBeEmpty.Create(RSFTPFileNameCanNotBeEmpty);
- end;
- if AStartPos > -1 then begin
- ASource.Position := AStartPos;
- end;
- DoBeforePut(ASource); //APR);
- if AAppend then begin
- InternalPut('APPE ' + ADestFile, ASource, False, False); {Do not localize}
- end else begin
- InternalPut('STOR ' + ADestFile, ASource, AStartPos = -1, AStartPos > -1); {Do not localize}
- end;
- DoAfterPut;
- end;
- procedure TIdFTP.Put(const ASourceFile: string; const ADestFile: string = '';
- const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1);
- var
- LSourceStream: TStream;
- LDestFileName : String;
- begin
- LDestFileName := ADestFile;
- if LDestFileName = '' then begin
- LDestFileName := ExtractFileName(ASourceFile);
- end;
- LSourceStream := TIdReadFileNonExclusiveStream.Create(ASourceFile);
- try
- Put(LSourceStream, LDestFileName, AAppend, AStartPos);
- finally
- FreeAndNil(LSourceStream);
- end;
- end;
- procedure TIdFTP.StoreUnique(const ASource: TStream; const AStartPos: TIdStreamSize = -1);
- begin
- if AStartPos > -1 then begin
- ASource.Position := AStartPos;
- end;
- DoBeforePut(ASource);
- InternalPut('STOU', ASource, AStartPos = -1, False); {Do not localize}
- DoAfterPut;
- end;
- procedure TIdFTP.StoreUnique(const ASourceFile: string; const AStartPos: TIdStreamSize = -1);
- var
- LSourceStream: TStream;
- begin
- LSourceStream := TIdReadFileExclusiveStream.Create(ASourceFile);
- try
- StoreUnique(LSourceStream, AStartPos);
- finally
- FreeAndNil(LSourceStream);
- end;
- end;
- procedure TIdFTP.SendInternalPassive(const ACmd: String; var VIP: string;
- var VPort: TIdPort);
- function IsRoutableAddress(AIP: string): Boolean;
- begin
- Result := not TextStartsWith(AIP, '127') and // Loopback 127.0.0.0-127.255.255.255
- not TextStartsWith(AIP, '10.') and // Private 10.0.0.0-10.255.255.255
- not TextStartsWith(AIP, '169.254') and // Link-local 169.254.0.0-169.254.255.255
- not TextStartsWith(AIP, '192.168') and // Private 192.168.0.0-192.168.255.255
- not (TextStartsWith(AIP, '172') and (AIP[7] = '.') and // Private 172.16.0.0-172.31.255.255
- (IndyStrToInt(Copy(AIP, 5, 2)) in [16..31]))
- end;
- var
- i, bLeft, bRight: integer;
- s: string;
- begin
- SendDataSettings;
- SendCmd(ACmd, 227); {do not localize}
- s := Trim(LastCmdResult.Text[0]);
- // Case 1 (Normal)
- // 227 Entering passive mode(100,1,1,1,23,45)
- bLeft := IndyPos('(', s); {do not localize}
- bRight := IndyPos(')', s); {do not localize}
- // Microsoft FTP Service may include a leading ( but not a trailing ),
- // so handle any combination of "(..)", "(..", "..)", and ".."
- if bLeft = 0 then bLeft := RPos(#32, S);
- if bRight = 0 then bRight := Length(S) + 1;
- S := Copy(S, bLeft + 1, bRight - bLeft - 1);
- VIP := ''; {do not localize}
- for i := 1 to 4 do begin
- VIP := VIP + '.' + Fetch(s, ','); {do not localize}
- end;
- IdDelete(VIP, 1, 1);
- // Server sent an unroutable address (private/reserved/etc). Use the IP we
- // connected to instead
- if not IsRoutableAddress(VIP) and IsRoutableAddress(Socket.Binding.PeerIP) then begin
- VIP := Socket.Binding.PeerIP;
- end;
- // Determine port
- VPort := TIdPort(IndyStrToInt(Fetch(s, ',')) and $FF) shl 8; {do not localize}
- //use trim as one server sends something like this:
- //"227 Passive mode OK (195,92,195,164,4,99 )"
- VPort := VPort or TIdPort(IndyStrToInt(Fetch(s, ',')) and $FF); {Do not translate}
- end;
- procedure TIdFTP.SendPassive(var VIP: string; var VPort: TIdPort);
- begin
- SendInternalPassive('PASV', VIP, VPort); {do not localize}
- end;
- procedure TIdFTP.SendCPassive(var VIP: string; var VPort: TIdPort);
- begin
- SendInternalPassive('CPSV', VIP, VPort); {do not localize}
- end;
- procedure TIdFTP.Noop;
- begin
- SendCmd('NOOP', 200); {do not localize}
- end;
- procedure TIdFTP.MakeDir(const ADirName: string);
- begin
- SendCmd('MKD ' + ADirName, 257); {do not localize}
- end;
- function TIdFTP.RetrieveCurrentDir: string;
- begin
- SendCmd('PWD', 257); {do not localize}
- Result := LastCmdResult.Text[0];
- IdDelete(Result, 1, IndyPos('"', Result)); // Remove first doublequote {do not localize}
- Result := Copy(Result, 1, IndyPos('"', Result) - 1); // Remove anything from second doublequote {do not localize} // to end of line
- // TODO: handle embedded quotation marks. RFC 959 allows them to be present
- end;
- procedure TIdFTP.RemoveDir(const ADirName: string);
- begin
- SendCmd('RMD ' + ADirName, 250); {do not localize}
- end;
- procedure TIdFTP.Delete(const AFilename: string);
- begin
- // Linksys NSLU2 NAS returns 200, Ultimodule IDAL returns 257
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('DELE ' + AFilename, [200, 250, 257]); {do not localize}
- end;
- (*
- CHANGE WORKING DIRECTORY (CWD)
- This command allows the user to work with a different
- directory or dataset for file storage or retrieval without
- altering his login or accounting information. Transfer
- parameters are similarly unchanged. The argument is a
- pathname specifying a directory or other system dependent
- file group designator.
- CWD
- 250
- 500, 501, 502, 421, 530, 550
- *)
- procedure TIdFTP.ChangeDir(const ADirName: string);
- begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('CWD ' + ADirName, [200, 250, 257]); //APR: Ericsson Switch FTP {do not localize}
- end;
- (*
- CHANGE TO PARENT DIRECTORY (CDUP)
- This command is a special case of CWD, and is included to
- simplify the implementation of programs for transferring
- directory trees between operating systems having different
- syntaxes for naming the parent directory. The reply codes
- shall be identical to the reply codes of CWD. See
- Appendix II for further details.
- CDUP
- 200
- 500, 501, 502, 421, 530, 550
- *)
- procedure TIdFTP.ChangeDirUp;
- begin
- // RFC lists 200 as the proper response, but in another section says that it can return the
- // same as CWD, which expects 250. That is it contradicts itself.
- // MS in their infinite wisdom chnaged IIS 5 FTP to return 250.
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('CDUP', [200, 250]); {do not localize}
- end;
- procedure TIdFTP.Site(const ACommand: string);
- begin
- SendCmd('SITE ' + ACommand, 200); {do not localize}
- end;
- procedure TIdFTP.Rename(const ASourceFile, ADestFile: string);
- begin
- SendCmd('RNFR ' + ASourceFile, 350); {do not localize}
- SendCmd('RNTO ' + ADestFile, 250); {do not localize}
- end;
- function TIdFTP.Size(const AFileName: String): Int64;
- var
- LTrans : TIdFTPTransferType;
- SizeStr: String;
- begin
- Result := -1;
- // RLebeau 03/13/2009: some servers refuse to accept the SIZE command in
- // ASCII mode, returning a "550 SIZE not allowed in ASCII mode" reply.
- // We put the connection in BINARY mode, even though no data connection is
- // actually being used. We restore it if the original mode was not BINARY.
- // It's a good idea to do this anyway because some other clients do this
- // as well.
- LTrans := TransferType;
- if LTrans <> ftBinary then begin
- Self.TransferType := ftBinary;
- end;
- try
- if SendCmd('SIZE ' + AFileName) = 213 then begin {do not localize}
- SizeStr := Trim(LastCmdResult.Text.Text);
- IdDelete(SizeStr, 1, IndyPos(' ', SizeStr)); // delete the response {do not localize}
- Result := IndyStrToInt64(SizeStr, -1);
- end;
- finally
- if LTrans <> ftBinary then begin
- TransferType := LTrans;
- end;
- end;
- end;
- //Added by SP
- procedure TIdFTP.ReInitialize(ADelay: UInt32 = 10);
- begin
- IndySleep(ADelay); //Added
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REIN', [120, 220, 500]) <> 500 then begin {do not localize}
- FLoginMsg.Clear;
- FCanResume := False;
- if Assigned(FDirectoryListing) then begin
- FDirectoryListing.Clear;
- end;
- FUsername := ''; {do not localize}
- FPassword := ''; {do not localize}
- FPassive := Id_TIdFTP_Passive;
- FCanResume := False;
- FResumeTested := False;
- FSystemDesc := '';
- FTransferType := Id_TIdFTP_TransferType;
- IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
- {$IFDEF STRING_IS_ANSI}
- IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
- {$ENDIF}
- if FUsingSFTP and (FUseTLS <> utUseImplicitTLS) then begin
- (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True;
- FUsingSFTP := False;
- FUseCCC := False;
- end;
- end;
- end;
- procedure TIdFTP.Allocate(AAllocateBytes: Integer);
- begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ALLO ' + IntToStr(AAllocateBytes), [200]); {do not localize}
- end;
- procedure TIdFTP.Status(AStatusList: TStrings);
- begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('STAT', [211, 212, 213, 500]) <> 500 then begin {do not localize}
- AStatusList.Text := LastCmdResult.Text.Text;
- end;
- end;
- procedure TIdFTP.Help(AHelpContents: TStrings; ACommand: String = ''); {do not localize}
- begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(TrimRight('HELP ' + ACommand), [211, 214, 500]) <> 500 then begin {do not localize}
- AHelpContents.Text := LastCmdResult.Text.Text;
- end;
- end;
- function TIdFTP.CheckAccount: Boolean;
- begin
- if (FAccount = '') and Assigned(FOnNeedAccount) then begin
- FOnNeedAccount(Self, FAccount);
- end;
- Result := FAccount <> '';
- end;
- procedure TIdFTP.StructureMount(APath: String);
- begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('SMNT ' + APath, [202, 250, 500]); {do not localize}
- end;
- procedure TIdFTP.FileStructure(AStructure: TIdFTPDataStructure);
- const
- StructureTypes: array[TIdFTPDataStructure] of String = ('F', 'R', 'P'); {do not localize}
- begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('STRU ' + StructureTypes[AStructure], [200, 500]); {do not localize}
- { TODO: Needs to be finished }
- end;
- procedure TIdFTP.TransferMode(ATransferMode: TIdFTPTransferMode);
- var
- s: String;
- begin
- if FCurrentTransferMode <> ATransferMode then begin
- s := '';
- case ATransferMode of
- // dmBlock: begin
- // s := 'B'; {do not localize}
- // end;
- // dmCompressed: begin
- // s := 'C'; {do not localize}
- // end;
- dmStream: begin
- s := 'S'; {do not localize}
- end;
- dmDeflate: begin
- if not Assigned(FCompressor) then begin
- raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor);
- end;
- if Self.IsCompressionSupported then begin
- s := 'Z'; {Do not localize}
- end;
- end;
- end;
- if s = '' then begin
- raise EIdFTPUnsupportedTransferMode.Create(RSFTPUnsupportedTransferMode);
- end;
- SendCmd('MODE ' + s, 200); {do not localize}
- FCurrentTransferMode := ATransferMode;
- end;
- end;
- destructor TIdFTP.Destroy;
- begin
- FreeAndNil(FClientInfo);
- FreeAndNil(FServerInfo);
- FreeAndNil(FListResult);
- FreeAndNil(FLoginMsg);
- FreeAndNil(FDirectoryListing);
- FreeAndNil(FLangsSupported);
- FreeAndNil(FProxySettings); //APR
- FreeAndNil(FTZInfo);
- FreeAndNil(FAbortFlag);
- FreeAndNil(FNATKeepAlive);
- inherited Destroy;
- end;
- function TIdFTP.Quote(const ACommand: String): Int16;
- begin
- Result := SendCmd(ACommand);
- end;
- procedure TIdFTP.IssueFEAT;
- var
- LBuf : String;
- i : Integer;
- begin
- //Feat data
- SendCmd('FEAT'); {do not localize}
- FCapabilities.Clear;
- //Ipswitch's FTP WS-FTP Server may issue 221 as success
- if LastCmdResult.NumericCode in [211,221] then begin
- FCapabilities.AddStrings(LastCmdResult.Text);
- //we remove the first and last lines because we only want the list
- if FCapabilities.Count > 0 then begin
- FCapabilities.Delete(0);
- end;
- if FCapabilities.Count > 0 then begin
- FCapabilities.Delete(FCapabilities.Count-1);
- end;
- end;
- if FUsingExtDataPort then begin
- FUsingExtDataPort := IsExtSupported('EPRT') and IsExtSupported('EPSV'); {do not localize}
- end;
- FCanUseMLS := IsExtSupported('MLSD') or IsExtSupported('MLST'); {do not localize}
- ExtractFeatFacts('LANG', FLangsSupported); {do not localize}
- //see if compression is supported.
- //we parse this way because IxExtensionSupported can only work
- //with one word.
- FIsCompressionSupported := False;
- for i := 0 to FCapabilities.Count-1 do begin
- LBuf := Trim(FCapabilities[i]);
- if LBuf = 'MODE Z' then begin {do not localize}
- FIsCompressionSupported := True;
- Break;
- end;
- end;
- // identify the client before sending the OPTS UTF8 command.
- // some servers need this in order to work around a bug in
- // Microsoft Internet Explorer's UTF-8 handling
- FServerInfo.Clear;
- if IsExtSupported('CSID') then begin {do not localize}
- if SendCmd('CSID ' + FClientInfo.CSIDParams) = 200 then begin {do not localize}
- FServerInfo.CSIDParams := LastCmdResult.Text.Text;
- end;
- end
- else if IsExtSupported('CLNT') then begin {do not localize}
- SendCmd('CLNT ' + FClientInfo.CLNTParams); {do not localize}
- end;
- // RLebeau 4/26/2019: per RFC 2640, if the server reports the 'UTF8'
- // capability, it is REQUIRED to detect and accept UTF-8 encoded
- // paths/filenames in commands. But, it is not REQUIRED to send UTF-8
- // in responses and directory listings. For that, we need to use the
- // OPTS command to inform the server that we actually want UTF-8...
- if IsExtSupported('UTF8') then begin {do not localize}
- // trying non-standard UTF-8 extension first, many servers use this...
- // Cerberus and RaidenFTP return 220, but TitanFTP and Gene6 return 200 instead...
- if (SendCmd('OPTS UTF8 ON') div 100) = 2 then begin {do not localize}
- IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
- end
- // trying draft-ietf-ftpext-utf-8-option-00.txt next...
- else if SendCmd('OPTS UTF-8 NLST') = 200 then begin {do not localize}
- IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
- end;
- end;
- end;
- procedure TIdFTP.Login;
- var
- i : Integer;
- LResp : Word;
- LCmd : String;
- function FtpHost: String;
- begin
- if FPort = IDPORT_FTP then begin
- Result := FHost;
- end else begin
- Result := FHost + Id_TIdFTP_HostPortDelimiter + IntToStr(FPort);
- end;
- end;
- begin
- //TLS part
- if UseTLS in ExplicitTLSVals then begin
- //This has to be here because the Rein command clears encryption.
- //RFC 4217
- FUsingSFTP := False;
- if FAUTHCmd = tAuto then begin
- {Note that we can not call SupportsTLS at all. That depends upon the FEAT response
- and unfortunately, some servers such as WS_FTP Server 4.0.0 (78162662)
- will not accept a FEAT command until you login. In other words, you have to do
- this by trial and error.
- }
- //334 has to be accepted because of a broekn implementation
- //see: http://www.ford-hutchinson.com/~fh-1-pfh/ftps-ext.html#bad
- {Note that we have to try several commands because some servers use AUTH TLS while others use
- AUTH SSL. GlobalScape's FTP Server only uses AUTH SSL while IpSwitch's uses AUTH TLS (the correct behavior).
- We try two other commands for historical reasons.
- }
- for i := 0 to 3 do begin
- LResp := SendCmd('AUTH ' + TLS_AUTH_NAMES[i]); {do not localize}
- if (LResp = 234) or (LResp = 334) then begin
- //okay. do the handshake
- TLSHandshake;
- FUsingSFTP := True;
- //we are done with the negotiation, let's close this.
- Break;
- end;
- //see if the error was not any type of syntax error code
- //if it wasn't, we fail the command.
- if (LResp div 500) <> 1 then begin
- ProcessTLSNegCmdFailed;
- Break;
- end;
- end;
- end else begin
- LResp := SendCmd('AUTH ' + TLS_AUTH_NAMES[Ord(FAUTHCmd)-1]); {do not localize}
- if (LResp = 234) or (LResp = 334) then begin
- //okay. do the handshake
- TLSHandshake;
- FUsingSFTP := True;
- end else begin
- ProcessTLSNegCmdFailed;
- end;
- end;
- if not FUsingSFTP then begin
- ProcessTLSNotAvail;
- end;
- end
- else if UseTLS = utUseImplicitTLS then begin
- FUsingSFTP := True;
- end
- else begin
- FUsingSFTP := False;
- end;
- //login
- case ProxySettings.ProxyType of
- fpcmNone:
- begin
- LCmd := MakeXAUTCmd(Greeting.Text.Text, FUserName, GetLoginPassword);
- if (LCmd <> '') and (not GetFIPSMode) then
- begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(LCmd, [230, 232, 331]) = 331 then begin
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end
- else if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName, [230, 232, 331]) = 331 then {do not localize}
- begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 332]); {do not localize}
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- fpcmUserSite:
- begin
- //This also supports WinProxy
- if Length(ProxySettings.UserName) > 0 then begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + ProxySettings.UserName, [230, 331]) = 331 then {do not localize}
- begin
- SendCmd('PASS ' + ProxySettings.Password, 230); {do not localize}
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName + '@' + FtpHost, [230, 232, 331]) = 331 then {do not localize}
- begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 331]); {do not localize}
- if IsAccountNeeded then
- begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- fpcmSite:
- begin
- if Length(ProxySettings.UserName) > 0 then begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize}
- SendCmd('PASS ' + ProxySettings.Password, 230); {do not localize}
- end;
- end;
- SendCmd('SITE ' + FtpHost); // ? Server Reply? 220? {do not localize}
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize}
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 332]); {do not localize}
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- fpcmOpen:
- begin
- if Length(ProxySettings.UserName) > 0 then begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize}
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 332]); {do not localize}
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- SendCmd('OPEN ' + FtpHost);//? Server Reply? 220? {do not localize}
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize}
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 332]); {do not localize}
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- fpcmUserPass: //USER user@firewalluser@hostname / PASS pass@firewallpass
- begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(IndyFormat('USER %s@%s@%s',
- [FUserName, ProxySettings.UserName, FtpHost]), [230, 232, 331]) = 331 then begin {do not localize}
- if Length(ProxySettings.Password) > 0 then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword + '@' + ProxySettings.Password, [230, 332]); {do not localize}
- end else begin
- //// needs otp ////
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230,332]); {do not localize}
- end;
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- fpcmTransparent:
- begin
- //I think fpcmTransparent means to connect to the regular host and the firewalll
- //intercepts the login information.
- if Length(ProxySettings.UserName) > 0 then begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize}
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + ProxySettings.Password, [230,332]); {do not localize}
- end;
- end;
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize}
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230,332]); {do not localize}
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]);
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- fpcmUserHostFireWallID : //USER hostuserId@hostname firewallUsername
- begin
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(TrimRight('USER ' + Username + '@' + FtpHost + ' ' + ProxySettings.UserName), [230, 331]) = 331 then begin {do not localize}
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230,232,202,332]) = 332 then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + ProxySettings.Password, [230,232,332]);
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- end;
- fpcmNovellBorder : //Novell Border PRoxy
- begin
- {Done like this:
- USER ProxyUserName$ DestFTPUserName$DestFTPHostName
- PASS UsereDirectoryPassword$ DestFTPPassword
- Novell BorderManager 3.8 Proxy and Firewall Overview and Planning Guide
- Copyright © 1997-1998, 2001, 2002-2003, 2004 Novell, Inc. All rights reserved.
- ===
- From a WS-FTP Pro firescript at:
- http://support.ipswitch.com/kb/WS-20050315-DM01.htm
- send ("USER %FwUserId$%HostUserId$%HostAddress")
- //send ("PASS %FwPassword$%HostPassword")
- }
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(TrimRight('USER ' + ProxySettings.UserName + '$' + Username + '$' + FtpHost), [230, 331]) = 331 then begin {do not localize}
- if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + ProxySettings.UserName + '$' + GetLoginPassword, [230,232,202,332]) = 332 then begin
- if IsAccountNeeded then begin
- if CheckAccount then begin
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
- end else begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- end;
- end;
- end;
- fpcmHttpProxyWithFtp :
- begin
- {GET ftp://XXX:[email protected]/ HTTP/1.0
- Host: indy.nevrona.com
- User-Agent: Mozilla/4.0 (compatible; Wincmd; Windows NT)
- Proxy-Authorization: Basic B64EncodedUserPass==
- Connection: close}
- raise EIdSocksServerCommandError.Create(RSSocksServerCommandError);
- end;//fpcmHttpProxyWithFtp
- fpcmCustomProxy :
- begin
- DoCustomFTPProxy;
- end;
- end;//case
- FLoginMsg.Assign(LastCmdResult);
- DoOnBannerAfterLogin(FLoginMsg.FormattedReply);
- //should be here because this can be issued more than once per connection.
- if FAutoIssueFEAT then begin
- IssueFEAT;
- end;
- SendTransferType(FTransferType);
- end;
- procedure TIdFTP.DoAfterLogin;
- begin
- if Assigned(FOnAfterClientLogin) then begin
- OnAfterClientLogin(Self);
- end;
- end;
- procedure TIdFTP.DoFTPList;
- begin
- if Assigned(FOnCreateFTPList) then begin
- FOnCreateFTPList(Self, FDirectoryListing);
- end;
- end;
- function TIdFTP.GetDirectoryListing: TIdFTPListItems;
- begin
- if FDirectoryListing = nil then begin
- if Assigned(FOnDirParseStart) then begin
- FOnDirParseStart(Self);
- end;
- ConstructDirListing;
- ParseFTPList;
- end;
- Result := FDirectoryListing;
- end;
- procedure TIdFTP.SetProxySettings(const Value: TIdFtpProxySettings);
- begin
- FProxySettings.Assign(Value);
- end;
- { TIdFtpProxySettings }
- procedure TIdFtpProxySettings.Assign(Source: TPersistent);
- var
- LSource: TIdFtpProxySettings;
- begin
- if Source is TIdFtpProxySettings then begin
- LSource := TIdFtpProxySettings(Source);
- FProxyType := LSource.ProxyType;
- FHost := LSource.Host;
- FUserName := LSource.UserName;
- FPassword := LSource.Password;
- FPort := LSource.Port;
- end else begin
- inherited Assign(Source);
- end;
- end;
- procedure TIdFTP.SendPBSZ;
- begin
- {NOte that PBSZ - protection buffer size must always be zero for FTP TLS}
- if FUsingSFTP or (FUseTLS = utUseImplicitTLS) then begin
- //protection buffer size
- SendCmd('PBSZ 0'); {do not localize}
- end;
- end;
- procedure TIdFTP.SendPROT;
- begin
- case FDataPortProtection of
- ftpdpsClear : SendCmd('PROT C', 200); //'C' - Clear - neither Integrity nor Privacy {do not localize}
- // NOT USED - 'S' - Safe - Integrity without Privacy
- // NOT USED - 'E' - Confidential - Privacy without Integrity
- // 'P' - Private - Integrity and Privacy
- ftpdpsPrivate : SendCmd('PROT P', 200); {do not localize}
- end;
- end;
- procedure TIdFTP.SendDataSettings;
- begin
- if FUsingSFTP then begin
- if not FDataSettingsSent then begin
- FDataSettingsSent := True;
- SendPBSZ;
- SendPROT;
- if FUseCCC then begin
- FUsingCCC := (SendCmd('CCC') div 100) = 2; {do not localize}
- if FUsingCCC then begin
- (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True;
- // TODO: uncomment this? Reinitialize() resets them after setting PassThrough=True...
- {FUsingSFTP := False;
- FUseCCC := False;}
- end;
- end;
- end;
- end;
- end;
- procedure TIdFTP.SetIOHandler(AValue: TIdIOHandler);
- begin
- inherited SetIOHandler(AValue);
- // UseExtensionDataPort must be true for IPv6 connections.
- // PORT and PASV can not communicate IPv6 Addresses
- if Socket <> nil then begin
- if Socket.IPVersion = Id_IPv6 then begin
- FUseExtensionDataPort := True;
- end;
- end;
- end;
- procedure TIdFTP.SetUseExtensionDataPort(const AValue: Boolean);
- begin
- if (not AValue) and (IPVersion = Id_IPv6) then begin
- raise EIdFTPMustUseExtWithIPv6.Create(RSFTPMustUseExtWithIPv6);
- end;
- if TryNATFastTrack then begin
- raise EIdFTPMustUseExtWithNATFastTrack.Create(RSFTPMustUseExtWithNATFastTrack);
- end;
- FUseExtensionDataPort := AValue;
- end;
- procedure TIdFTP.ParseEPSV(const AReply : String; var VIP : String; var VPort : TIdPort);
- var
- bLeft, bRight, LPort: Integer;
- delim : Char;
- s : String;
- begin
- s := Trim(AReply);
- // "229 Entering Extended Passive Mode (|||59028|)"
- bLeft := IndyPos('(', s); {do not localize}
- bRight := IndyPos(')', s); {do not localize}
- s := Copy(s, bLeft + 1, bRight - bLeft - 1);
- delim := s[1]; // normally is | but the RFC say it may be different
- Fetch(S, delim);
- Fetch(S, delim);
- VIP := Fetch(S, delim);
- if VIP = '' then begin
- VIP := Host;
- end;
- s := Trim(Fetch(S, delim));
- LPort := IndyStrToInt(s, 0);
- if (LPort < 1) or (LPort > 65535) then begin
- raise EIdFTPServerSentInvalidPort.CreateFmt(RSFTPServerSentInvalidPort, [s]);
- end;
- VPort := TIdPort(LPort and $FFFF);
- end;
- procedure TIdFTP.SendEPassive(var VIP: string; var VPort: TIdPort);
- begin
- SendDataSettings;
- //Note that for FTP Proxies, it is not desirable for the server to choose
- //the EPSV data port IP connection type. We try to if we can.
- if FProxySettings.ProxyType <> fpcmNone then begin
- if SendCMD('EPSV ' + cIPVersions[IPVersion]) <> 229 then begin {do not localize}
- //Raidon and maybe a few others may honor EPSV but not with the proto numbers
- SendCMD('EPSV'); {do not localize}
- end;
- end else begin
- SendCMD('EPSV'); {do not localize}
- end;
- if LastCmdResult.NumericCode <> 229 then begin
- SendPassive(VIP, VPort);
- FUsingExtDataPort := False;
- Exit;
- end;
- try
- ParseEPSV(LastCmdResult.Text[0], VIP, VPort);
- except
- SendCmd('ABOR'); {do not localize}
- raise;
- end;
- end;
- procedure TIdFTP.SendEPort(AHandle: TIdSocketHandle);
- begin
- SendDataSettings;
- if FExternalIP <> '' then begin
- SendEPort(FExternalIP, AHandle.Port, AHandle.IPVersion);
- end else begin
- SendEPort(AHandle.IP, AHandle.Port, AHandle.IPVersion);
- end;
- end;
- procedure TIdFTP.SendEPort(const AIP: String; const APort: TIdPort; const AIPVersion: TIdIPVersion);
- begin
- if SendCmd('EPRT |' + cIPVersions[AIPVersion] + '|' + AIP + '|' + IntToStr(APort) + '|') <> 200 then begin {do not localize}
- SendPort(AIP, APort);
- FUsingExtDataPort := False;
- end;
- end;
- procedure TIdFTP.SetPassive(const AValue: Boolean);
- begin
- if (not AValue) and TryNATFastTrack then begin
- raise EIdFTPPassiveMustBeTrueWithNATFT.Create(RSFTPFTPPassiveMustBeTrueWithNATFT);
- end;
- FPassive := AValue;
- end;
- procedure TIdFTP.SetTryNATFastTrack(const AValue: Boolean);
- begin
- FTryNATFastTrack := AValue;
- if FTryNATFastTrack then begin
- FPassive := True;
- FUseExtensionDataPort := True;
- end;
- end;
- procedure TIdFTP.DoTryNATFastTrack;
- begin
- if IsExtSupported('EPSV') then begin {do not localize}
- if SendCmd('EPSV ALL') = 229 then begin {do not localize}
- //Surge FTP treats EPSV ALL as if it were a standard EPSV
- //We send ABOR in that case so it can close the data connection it created
- SendCmd('ABOR'); {do not localize}
- end;
- FUsingNATFastTrack := True;
- end;
- end;
- procedure TIdFTP.SetCmdOpt(const ACmd, AOptions: String);
- begin
- // RLebeau 4/26/2019: the only official success reply allowed for OPTS
- // is 200, but for OPTS UTF8 ON, Cerberus and RaidenFTP return 220 instead.
- // So lets just accept any 2xx reply...
- if (SendCmd(TrimRight('OPTS ' + ACmd + ' ' + AOptions)) div 100) <> 2 then begin
- RaiseExceptionForLastCmdResult;
- end;
- end;
- procedure TIdFTP.ExtListDir(ADest: TStrings = nil; const ADirectory: string = '');
- var
- LDest: TMemoryStream;
- LEncoding: IIdTextEncoding;
- begin
- // RLebeau 6/4/2009: According to RFC 3659 Section 7.2:
- //
- // The data connection opened for a MLSD response shall be a connection
- // as if the "TYPE L 8", "MODE S", and "STRU F" commands had been given,
- // whatever FTP transfer type, mode and structure had actually been set,
- // and without causing those settings to be altered for future commands.
- // That is, this transfer type shall be set for the duration of the data
- // connection established for this command only. While the content of
- // the data sent can be viewed as a series of lines, implementations
- // should note that there is no maximum line length defined.
- // Implementations should be prepared to deal with arbitrarily long
- // lines.
- LDest := TMemoryStream.Create;
- try
- InternalGet(TrimRight('MLSD ' + ADirectory), LDest); {do not localize}
- FreeAndNil(FDirectoryListing);
- FDirFormat := '';
- LDest.Position := 0;
- // RLebeau: using IndyTextEncoding_8Bit here. TIdFTPListParseBase will
- // decode UTF-8 sequences later on...
- LEncoding := IndyTextEncoding_8Bit;
- FListResult.Text := ReadStringFromStream(LDest, -1, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF});
- LEncoding := nil;
- TIdFTPListResult(FListResult).FDetails := True;
- TIdFTPListResult(FListResult).FUsedMLS := True;
- FDirFormat := MLST;
- finally
- FreeAndNil(LDest);
- end;
- if Assigned(ADest) then begin //APR: User can use ListResult and DirectoryListing
- ADest.Assign(FListResult);
- end;
- DoOnRetrievedDir;
- end;
- procedure TIdFTP.ExtListItem(ADest: TStrings; AFList : TIdFTPListItems; const AItem: string);
- var
- i : Integer;
- begin
- ADest.BeginUpdate;
- try
- ADest.Clear;
- IOHandler.WriteLn(TrimRight('MLST ' + AItem)); {do not localize}
- GetResponse(250, IndyTextEncoding_8Bit);
- for i := 0 to LastCmdResult.Text.Count -1 do begin
- if IndyPos(';', LastCmdResult.Text[i]) > 0 then begin
- ADest.Add(LastCmdResult.Text[i]);
- end;
- end;
- finally
- ADest.EndUpdate;
- end;
- if Assigned(AFList) then begin
- IdFTPListParseBase.ParseListing(ADest, AFList, 'MLST'); {do not localize}
- end;
- end;
- procedure TIdFTP.ExtListItem(ADest: TStrings; const AItem: string);
- begin
- ExtListItem(ADest, nil, AItem);
- end;
- procedure TIdFTP.ExtListItem(AFList: TIdFTPListItems; const AItem: String);
- var
- LBuf : TStrings;
- begin
- LBuf := TStringList.Create;
- try
- ExtListItem(LBuf, AFList, AItem);
- finally
- FreeAndNil(LBuf);
- end;
- end;
- function TIdFTP.IsExtSupported(const ACmd: String): Boolean;
- var
- i : Integer;
- LBuf : String;
- begin
- Result := False;
- for i := 0 to FCapabilities.Count -1 do begin
- LBuf := TrimLeft(FCapabilities[i]);
- if TextIsSame(Fetch(LBuf), ACmd) then begin
- Result := True;
- Exit;
- end;
- end;
- end;
- function TIdFTP.FileDate(const AFileName: String; const AsGMT: Boolean): TDateTime;
- var
- LBuf : String;
- begin
- //Do not use the FEAT list because some servers
- //may support it even if FEAT isn't supported
- if SendCmd('MDTM ' + AFileName) = 213 then begin {do not localize}
- LBuf := LastCmdResult.Text[0];
- LBuf := Trim(LBuf);
- if AsGMT then begin
- Result := FTPMLSToGMTDateTime(LBuf);
- end else begin
- Result := FTPMLSToLocalDateTime(LBuf);
- end;
- end else begin
- Result := 0;
- end;
- end;
- procedure TIdFTP.SiteToSiteUpload(const AToSite : TIdFTP; const ASourceFile : String;
- const ADestFile : String = '');
- {
- SiteToSiteUpload
- From: PASV To: PORT - ATargetUsesPasv = False
- From: RETR To: STOR
- SiteToSiteDownload
- From: PORT To: PASV - ATargetUsesPasv = True
- From: RETR To: STOR
- }
- begin
- if ValidateInternalIsTLSFXP(Self, AToSite, True) then begin
- InternalEncryptedTLSFXP(Self, AToSite, ASourceFile, ADestFile, True);
- end else begin
- InternalUnencryptedFXP(Self, AToSite, ASourceFile, ADestFile, True);
- end;
- end;
- procedure TIdFTP.SiteToSiteDownload(const AFromSite: TIdFTP; const ASourceFile : String;
- const ADestFile : String = '');
- {
- The only use of this function is to get the passive mode on the other connection.
- Because not all hosts allow it. This way you get a second chance.
- If uploading from host A doesn't work, try downloading from host B
- }
- begin
- if ValidateInternalIsTLSFXP(AFromSite, Self, True) then begin
- InternalEncryptedTLSFXP(AFromSite, Self, ASourceFile, ADestFile, False);
- end else begin
- InternalUnencryptedFXP(AFromSite, Self, ASourceFile, ADestFile, False);
- end;
- end;
- procedure TIdFTP.ExtractFeatFacts(const ACmd: String; AResults: TStrings);
- var
- i : Integer;
- LBuf, LFact : String;
- begin
- AResults.BeginUpdate;
- try
- AResults.Clear;
- for i := 0 to FCapabilities.Count -1 do begin
- LBuf := FCapabilities[i];
- if TextIsSame(Fetch(LBuf), ACmd) then begin
- LBuf := Trim(LBuf);
- while LBuf <> '' do begin
- LFact := Trim(Fetch(LBuf, ';'));
- if LFact <> '' then begin
- AResults.Add(LFact);
- end;
- end;
- Exit;
- end;
- end;
- finally
- AResults.EndUpdate;
- end;
- end;
- procedure TIdFTP.SetLang(const ALangTag: String);
- begin
- if IsExtSupported('LANG') then begin {do not localize}
- SendCmd(TrimRight('LANG ' + ALangTag), 200); {do not localize}
- end;
- end;
- function TIdFTP.CRC(const AFIleName : String; const AStartPoint : Int64 = 0;
- const AEndPoint : Int64 = 0) : Int64;
- var
- LCmd : String;
- LCRC : String;
- begin
- Result := -1;
- if IsExtSupported('XCRC') then begin {do not localize}
- LCmd := 'XCRC "' + AFileName + '"'; {do not localize}
- if AStartPoint <> 0 then begin
- LCmd := LCmd + ' ' + IntToStr(AStartPoint);
- if AEndPoint <> 0 then begin
- LCmd := LCmd + ' ' + IntToStr(AEndPoint);
- end;
- end;
- if SendCMD(LCMD) = 250 then begin
- LCRC := Trim(LastCmdResult.Text.Text);
- IdDelete(LCRC, 1, IndyPos(' ', LCRC)); // delete the response
- Result := IndyStrToInt64('$' + LCRC, -1);
- end;
- end;
- end;
- procedure TIdFTP.CombineFiles(const ATargetFile: String; AFileParts: TStrings);
- var
- i : Integer;
- LCmd: String;
- begin
- if IsExtSupported('COMB') and (AFileParts.Count > 0) then begin {do not localize}
- LCmd := 'COMB "' + ATargetFile + '"'; {do not localize}
- for i := 0 to AFileParts.Count -1 do begin
- LCmd := LCmd + ' ' + AFileParts[i];
- end;
- SendCmd(LCmd, 250);
- end;
- end;
- procedure TIdFTP.ParseFTPList;
- begin
- DoOnDirParseStart;
- try
- // Parse directory listing
- if FListResult.Count > 0 then begin
- if TIdFTPListResult(FListResult).UsedMLS then begin
- FDirFormat := MLST;
- // TODO: set the FListParserClass as well..
- IdFTPListParseBase.ParseListing(FListResult, FDirectoryListing, MLST);
- end else begin
- CheckListParseCapa(FListResult, FDirectoryListing, FDirFormat,
- FListParserClass, SystemDesc, TIdFTPListResult(FListResult).Details);
- end;
- end else begin
- FDirFormat := '';
- end;
- finally
- DoOnDirParseEnd;
- end;
- end;
- function TIdFTP.GetSupportsTLS: Boolean;
- begin
- Result := (FindAuthCmd <> '');
- end;
- function TIdFTP.FindAuthCmd: String;
- var
- i : Integer;
- LBuf : String;
- LWord : String;
- begin
- Result := '';
- for i := 0 to FCapabilities.Count -1 do begin
- LBuf := TrimLeft(FCapabilities[i]);
- if TextIsSame(Fetch(LBuf), 'AUTH') then begin {do not localize}
- repeat
- LWord := Trim(Fetch(LBuf, ';'));
- if PosInStrArray(LWord, TLS_AUTH_NAMES, False) > -1 then begin
- Result := 'AUTH ' + LWord; {do not localize}
- Exit;
- end;
- until LBuf = '';
- Break;
- end;
- end;
- end;
- procedure TIdFTP.DoCustomFTPProxy;
- begin
- if Assigned(FOnCustomFTPProxy) then begin
- FOnCustomFTPProxy(Self);
- end else begin
- raise EIdFTPOnCustomFTPProxyRequired.Create(RSFTPOnCustomFTPProxyReq);
- end;
- end;
- function TIdFTP.GetLoginPassword: String;
- begin
- Result := GetLoginPassword(LastCmdResult.Text.Text);
- end;
- function TIdFTP.GetLoginPassword(const APrompt: String): String;
- begin
- if TIdOTPCalculator.IsValidOTPString(APrompt) then begin
- TIdOTPCalculator.GenerateSixWordKey(APrompt, FPassword, Result);
- end else begin
- Result := FPassword;
- end;
- end;
- function TIdFTP.SetSSCNToOn : Boolean;
- begin
- Result := FUsingSFTP;
- if not Result then begin
- Exit;
- end;
- Result := (DataPortProtection = ftpdpsPrivate);
- if not Result then begin
- Exit;
- end;
- Result := not IsExtSupported(SCCN_FEAT);
- if not Result then begin
- Exit;
- end;
- if not FSSCNOn then begin
- SendCmd(SSCN_ON, SSCN_OK_REPLY);
- FSSCNOn := True;
- end;
- end;
- procedure TIdFTP.ClearSSCN;
- begin
- if FSSCNOn then begin
- SendCmd(SSCN_OFF, SSCN_OK_REPLY);
- end;
- end;
- procedure TIdFTP.SetClientInfo(const AValue: TIdFTPClientIdentifier);
- begin
- FClientInfo.Assign(AValue);
- end;
- procedure TIdFTP.SetCompressor(AValue: TIdZLibCompressorBase);
- var
- // under ARC, convert a weak reference to a strong reference before working with it
- LCompressor: TIdZLibCompressorBase;
- begin
- LCompressor := FCompressor;
- if LCompressor <> AValue then begin
- // under ARC, all weak references to a freed object get nil'ed automatically
- {$IFNDEF USE_OBJECT_ARC}
- if Assigned(LCompressor) then begin
- LCompressor.RemoveFreeNotification(Self);
- end;
- {$ENDIF}
- FCompressor := AValue;
- if Assigned(AValue) then begin
- {$IFNDEF USE_OBJECT_ARC}
- AValue.FreeNotification(Self);
- {$ENDIF}
- end
- else if Connected then begin
- TransferMode(dmStream);
- end;
- end;
- end;
- procedure TIdFTP.GetInternalResponse(AEncoding: IIdTextEncoding = nil);
- var
- LLine: string;
- LResponse: TStringList;
- LReplyCode: string;
- begin
- CheckConnected;
- LResponse := TStringList.Create;
- try
- // Some servers with bugs send blank lines before reply. Dont remember
- // which ones, but I do remember we changed this for a reason
- //
- // RLebeau 9/14/06: this can happen in between lines of the reply as well
- // RLebeau 3/9/09: according to RFC 959, when reading a multi-line reply,
- // we are supposed to look at the first line's reply code and then keep
- // reading until that specific reply code is encountered again, and
- // everything in between is the text. So, do not just look for arbitrary
- // 3-digit values on each line, but instead look for the specific reply
- // code...
- LLine := IOHandler.ReadLnWait(MaxInt, AEncoding);
- LResponse.Add(LLine);
- if CharEquals(LLine, 4, '-') then begin
- LReplyCode := Copy(LLine, 1, 3);
- repeat
- LLine := IOHandler.ReadLnWait(MaxInt, AEncoding);
- LResponse.Add(LLine);
- until TIdReplyFTP(FLastCmdResult).IsEndReply(LReplyCode, LLine);
- end;
- //Note that FormattedReply uses an assign in it's property set method.
- FLastCmdResult.FormattedReply := LResponse;
- finally
- FreeAndNil(LResponse);
- end;
- end;
- function TIdFTP.{$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(
- const AResponse: Int16; const AAllowedResponses: array of Int16): Int16;
- begin
- // any FTP command can return a 421 reply if the server is going to shut
- // down the command connection. This way, we can close the connection
- // immediately instead of waiting for a future action that would raise
- // an EIdConnClosedGracefully exception instead...
- if AResponse = 421 then
- begin
- // check if the caller explicitally wants to handle 421 replies...
- if High(AAllowedResponses) > -1 then begin
- if PosInSmallIntArray(AResponse, AAllowedResponses) <> -1 then begin
- Result := AResponse;
- Exit;
- end;
- end;
- Disconnect(False);
- if IOHandler <> nil then begin
- IOHandler.InputBuffer.Clear;
- end;
- RaiseExceptionForLastCmdResult;
- end;
- Result := inherited {$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(AResponse, AAllowedResponses);
- end;
- function TIdFTP.GetReplyClass: TIdReplyClass;
- begin
- Result := TIdReplyFTP;
- end;
- procedure TIdFTP.SetIPVersion(const AValue: TIdIPVersion);
- begin
- if AValue <> FIPVersion then begin
- inherited SetIPVersion(AValue);
- if IPVersion = Id_IPv6 then begin
- UseExtensionDataPort := True;
- end;
- end;
- end;
- class function TIdFTP.InternalEncryptedTLSFXP(AFromSite, AToSite: TIdFTP;
- const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean;
- {
- SiteToSiteUpload
- From: PASV To: PORT - ATargetUsesPasv = False
- From: RETR To: STOR
- SiteToSiteDownload
- From: PORT To: PASV - ATargetUsesPasv = True
- From: RETR To: STOR
- To do FXP transfers with TLS FTP, you have to have one computer do the
- TLS handshake as a client (ssl_connect). Thus, one of the following conditions must be meet.
- 1) SSCN must be supported on one of the FTP servers
- or
- 2) If IPv4 is used, the computer receiving a "PASV" command must support
- CPSV. CPSV will NOT work with IPv6.
- IMAO, when doing FXP transfers, you should use SSCN whenever possible as
- SSCN will support IPv6 and SSCN may be in wider use than CPSV. CPSV should
- only be used as a fallback if SSCN isn't supported by both servers and IPv4
- is being used.
- }
- var
- LIP : String;
- LPort : TIdPort;
- begin
- Result := True;
- if AFromSite.SetSSCNToOn then begin
- AToSite.ClearSSCN;
- end
- else if AToSite.SetSSCNToOn then begin
- AFromSite.ClearSSCN;
- end
- else if AToSite.IPVersion = Id_IPv4 then begin
- if ATargetUsesPasv then begin
- AToSite.SendCPassive(LIP, LPort);
- AFromSite.SendPort(LIP, LPort);
- end else begin
- AFromSite.SendCPassive(LIP, LPort);
- AToSite.SendPort(LIP, LPort);
- end;
- end;
- FXPSendFile(AFromSite, AToSite, ASourceFile, ADestFile);
- end;
- class function TIdFTP.InternalUnencryptedFXP(AFromSite, AToSite: TIdFTP;
- const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean;
- {
- SiteToSiteUpload
- From: PASV To: PORT - ATargetUsesPasv = False
- From: RETR To: STOR
- SiteToSiteDownload
- From: PORT To: PASV - ATargetUsesPasv = True
- From: RETR To: STOR
- }
- begin
- FXPSetTransferPorts(AFromSite, AToSite, ATargetUsesPasv);
- FXPSendFile(AFromSite, AToSite, ASourceFile, ADestFile);
- Result := True;
- end;
- class function TIdFTP.ValidateInternalIsTLSFXP(AFromSite, AToSite: TIdFTP;
- const ATargetUsesPasv : Boolean): Boolean;
- {
- SiteToSiteUpload
- From: PASV To: PORT - ATargetUsesPasv = False
- From: RETR To: STOR
- SiteToSiteDownload
- From: PORT To: PASV - ATargetUsesPasv = True
- From: RETR To: STOR
- This will raise an exception if FXP can not be done. Result = True for encrypted
- or False for unencrypted.
- Note:
- The following is required:
- SiteToSiteUpload
- Source must do P
- }
- begin
- if ATargetUsesPasv then begin
- if AToSite.UsingNATFastTrack then begin
- raise EIdFTPSToSNATFastTrack.Create(RSFTPNoSToSWithNATFastTrack);
- end;
- end else begin
- if AFromSite.UsingNATFastTrack then begin
- raise EIdFTPSToSNATFastTrack.Create(RSFTPNoSToSWithNATFastTrack);
- end;
- end;
- if AFromSite.IPVersion <> AToSite.IPVersion then begin
- raise EIdFTPStoSIPProtoMustBeSame.Create(RSFTPSToSProtosMustBeSame);
- end;
- if AFromSite.CurrentTransferMode <> AToSite.CurrentTransferMode then begin
- raise EIdFTPSToSTransModesMustBeSame.Create(RSFTPSToSTransferModesMusbtSame);
- end;
- if AFromSite.FUsingSFTP <> AToSite.FUsingSFTP then begin
- raise EIdFTPSToSNoDataProtection.Create(RSFTPSToSNoDataProtection);
- end;
- Result := AFromSite.FUsingSFTP and AToSite.FUsingSFTP;
- if Result then begin
- if not (AFromSite.IsExtSupported('SSCN') or AToSite.IsExtSupported('SSCN')) then begin {do not localize}
- //Second chance fallback, is CPSV supported on the server where PASV would
- // be sent
- if AToSite.IPVersion = Id_IPv4 then begin
- if ATargetUsesPasv then begin
- if not AToSite.IsExtSupported('CPSV') then begin {do not localize}
- raise EIdFTPSToSNATFastTrack.Create(RSFTPSToSSSCNNotSupported);
- end;
- end else begin
- if not AFromSite.IsExtSupported('CPSV') then begin {do not localize}
- raise EIdFTPSToSNATFastTrack.Create(RSFTPSToSSSCNNotSupported);
- end;
- end;
- end;
- end;
- end;
- end;
- class procedure TIdFTP.FXPSendFile(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String);
- var
- LDestFile : String;
- begin
- LDestFile := ADestFile;
- if LDestFile = '' then begin
- LDestFile := ASourceFile;
- end;
- AToSite.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('STOR ' + LDestFile, [110, 125, 150]); {do not localize}
- try
- AFromSite.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('RETR ' + ASourceFile, [110, 125, 150]); {do not localize}
- except
- AToSite.Abort;
- raise;
- end;
- AToSite.GetInternalResponse;
- AFromSite.GetInternalResponse;
- AToSite.{$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(AToSite.LastCmdResult.NumericCode, [225, 226, 250]);
- AFromSite.{$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(AFromSite.LastCmdResult.NumericCode, [225, 226, 250]);
- end;
- class procedure TIdFTP.FXPSetTransferPorts(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv: Boolean);
- var
- LIP : String;
- LPort : TIdPort;
- {
- {
- SiteToSiteUpload
- From: PASV To: PORT - ATargetUsesPasv = False
- From: RETR To: STOR
- SiteToSiteDownload
- From: PORT To: PASV - ATargetUsesPasv = True
- From: RETR To: STOR
- }
- begin
- if ATargetUsesPasv then begin
- if AToSite.UsingExtDataPort then begin
- AToSite.SendEPassive(LIP, LPort);
- end else begin
- AToSite.SendPassive(LIP, LPort);
- end;
- if AFromSite.UsingExtDataPort then begin
- AFromSite.SendEPort(LIP, LPort, AToSite.IPVersion);
- end else begin
- AFromSite.SendPort(LIP, LPort);
- end;
- end else begin
- if AFromSite.UsingExtDataPort then begin
- AFromSite.SendEPassive(LIP, LPort);
- end else begin
- AFromSite.SendPassive(LIP, LPort);
- end;
- if AToSite.UsingExtDataPort then begin
- AToSite.SendEPort(LIP, LPort, AFromSite.IPVersion);
- end else begin
- AToSite.SendPort(LIP, LPort);
- end;
- end;
- end;
- {Note about SetTime procedures:
- The first syntax is one used by current Serv-U versions and servers that report "MDTM YYYYMMDDHHMMSS[+-TZ];filename " in their FEAT replies is:
- 1) MDTM [Time in GMT format] Filename
- some Bullete Proof FTPD versions, Indy's FTP Server component, and servers reporting "MDTM YYYYMMDDHHMMSS[+-TZ] filename" in their FEAT replies uses an older Syntax which is:
- 2) MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename
- and then there is the classic
- 3) MDTM [local timestamp] Filename
- So for example, if I was a file dated Jan 3, 5:00:00 pm from my computer in the Eastern Standard Time (-5 hours from Universal Time), the 3 syntaxes
- Indy would use are:
- Syntax 1:
- 1) MDTM 0103220000 MyFile.exe (notice the 22 hour)
- Syntax 2:
- 2) MDTM 0103170000-300 MyFile.exe (notice the 17 hour and the -300 offset)
- Syntax 3;
- 3) MDTM 0103170000 MyFile.exe (notice the 17 hour)
- Note from:
- http://www.ftpvoyager.com/releasenotes10x.asp
- ====
- Added support for RFC change and the MDTM. MDTM requires sending the server
- GMT (UTC) instead of a "fixed" date and time. FTP Voyager supports this with
- Serv-U automatically by checking the Serv-U version number and by checking the
- response to the FEAT command for MDTM. Servers returning "MDTM" or
- "MDTM YYYYMMDDHHMMSS[+-TZ] filename" will use the old method. Servers
- returning "MDTM YYYYMMDDHHMMSS" only will use the new method where the date a
- and time is GMT (UTC).
- ===
- }
- procedure TIdFTP.SetModTime(const AFileName: String; const ALocalTime: TDateTime);
- var
- LCmd: String;
- begin
- //use MFMT instead of MDTM because that always takes the time as Universal
- //time (the most accurate).
- if IsExtSupported('MFMT') then begin {do not localize}
- LCmd := 'MFMT ' + FTPLocalDateTimeToMLS(ALocalTime, False) + ' ' + AFileName; {do not localize}
- end
- //Syntax 1 - MDTM [Time in GMT format] Filename
- else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ];filename') > 0) or IsIIS then begin {do not localize}
- //we use the new method
- LCmd := 'MDTM ' + FTPLocalDateTimeToMLS(ALocalTime, False) + ' ' + AFileName; {do not localize}
- end
- //Syntax 2 - MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename
- //use old method for old versions of Serv-U and BPFTP Server
- else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ] filename') > 0) or IsOldServU or IsBPFTP then begin {do not localize}
- LCmd := 'MDTM '+ FTPDateTimeToMDTMD(ALocalTime, False, True) + ' ' + AFileName; {do not localize}
- end
- //syntax 3 - MDTM [local timestamp] Filename
- else if FTZInfo.FGMTOffsetAvailable then begin
- //send it relative to the server's time-zone
- LCmd := 'MDTM '+ FTPDateTimeToMDTMD(LocalTimeToUTCTime(ALocalTime) + FTZInfo.FGMTOffset, False, False) + ' ' + AFileName; {do not localize}
- end
-
- else begin
- LCmd := 'MDTM '+ FTPDateTimeToMDTMD(ALocalTime, False, False) + ' ' + AFileName; {do not localize}
- end;
- // When using MDTM, Titan FTP 5 returns 200 and vsFTPd returns 213
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(LCmd, [200, 213, 253]);
- end;
- {
- Note from:
- http://www.ftpvoyager.com/releasenotes10x.asp
- ====
- Added support for RFC change and the MDTM. MDTM requires sending the server
- GMT (UTC) instead of a "fixed" date and time. FTP Voyager supports this with
- Serv-U automatically by checking the Serv-U version number and by checking the
- response to the FEAT command for MDTM. Servers returning "MDTM" or
- "MDTM YYYYMMDDHHMMSS[+-TZ] filename" will use the old method. Servers
- returning "MDTM YYYYMMDDHHMMSS" only will use the new method where the date a
- and time is GMT (UTC).
- ===
- }
- procedure TIdFTP.SetModTimeGMT(const AFileName : String; const AGMTTime: TDateTime);
- var
- LCmd: String;
- begin
- //use MFMT instead of MDTM because that always takes the time as Universal
- //time (the most accurate).
- if IsExtSupported('MFMT') then begin {do not localize}
- LCmd := 'MFMT ' + FTPGMTDateTimeToMLS(AGMTTime) + ' ' + AFileName; {do not localize}
- end
- //Syntax 1 - MDTM [Time in GMT format] Filename
- else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ];filename') > 0) or IsIIS then begin {do not localize}
- //we use the new method
- LCmd := 'MDTM ' + FTPGMTDateTimeToMLS(AGMTTime, False) + ' ' + AFileName; {do not localize}
- end
-
- //Syntax 2 - MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename
- //use old method for old versions of Serv-U and BPFTP Server
- else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ] filename') > 0) or IsOldServU or IsBPFTP then begin {do not localize}
- LCmd := 'MDTM '+ FTPDateTimeToMDTMD(UTCTimeToLocalTime(AGMTTime), False, True) + ' ' + AFileName; {do not localize}
- end
-
- //syntax 3 - MDTM [local timestamp] Filename
- else if FTZInfo.FGMTOffsetAvailable then begin
- //send it relative to the server's time-zone
- LCmd := 'MDTM '+ FTPDateTimeToMDTMD(AGMTTime + FTZInfo.FGMTOffset, False, False) + ' ' + AFileName; {do not localize}
- end
- else begin
- LCmd := 'MDTM '+ FTPDateTimeToMDTMD(UTCTimeToLocalTime(AGMTTime), False, False) + ' ' + AFileName; {do not localize}
- end;
- // When using MDTM, Titan FTP 5 returns 200 and vsFTPd returns 213
- {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(LCmd, [200, 213, 253]);
- end;
- {Improvement from Tobias Giesen http://www.superflexible.com
- His notation is below:
- "here's a fix for TIdFTP.IndexOfFeatLine. It does not work the
- way it is used in TIdFTP.SetModTime, because it only
- compares the first word of the FeatLine." }
- function TIdFTP.IndexOfFeatLine(const AFeatLine: String): Integer;
- var
- LBuf : String;
- LNoSpaces: Boolean;
- begin
- LNoSpaces := IndyPos(' ', AFeatLine) = 0;
- for Result := 0 to FCapabilities.Count -1 do begin
- LBuf := TrimLeft(FCapabilities[Result]);
- // RLebeau: why Fetch() if no spaces are present?
- if LNoSpaces then begin
- LBuf := Fetch(LBuf);
- end;
- if TextIsSame(AFeatLine, LBuf) then begin
- Exit;
- end;
- end;
- Result := -1;
- end;
- { TIdFTPTZInfo }
- procedure TIdFTPTZInfo.Assign(Source: TPersistent);
- var
- LSource: TIdFTPTZInfo;
- begin
- if Source is TIdFTPTZInfo then begin
- LSource := TIdFTPTZInfo(Source);
- FGMTOffset := LSource.GMTOffset;
- FGMTOffsetAvailable := LSource.GMTOffsetAvailable;
- end else begin
- inherited Assign(Source);
- end;
- end;
- function TIdFTP.IsSiteZONESupported: Boolean;
- var
- LFacts : TStrings;
- i : Integer;
- begin
- Result := False;
- if IsServerMDTZAndListTForm then begin
- Result := True;
- Exit;
- end;
- LFacts := TStringList.Create;
- try
- ExtractFeatFacts('SITE', LFacts);
- for i := 0 to LFacts.Count-1 do begin
- if TextIsSame(LFacts[i], 'ZONE') then begin {do not localize}
- Result := True;
- Exit;
- end;
- end;
- finally
- FreeAndNil(LFacts);
- end;
- end;
- procedure TIdFTP.SetTZInfo(const Value: TIdFTPTZInfo);
- begin
- FTZInfo.Assign(Value);
- end;
- function TIdFTP.IsOldServU: Boolean;
- begin
- Result := TextStartsWith(FServerDesc, 'Serv-U '); {do not localize}
- end;
- function TIdFTP.IsBPFTP : Boolean;
- begin
- Result := TextStartsWith(FServerDesc, 'BPFTP Server '); {do not localize}
- end;
- function TIdFTP.IsTitan : Boolean;
- begin
- Result := TextStartsWith(FServerDesc, 'TitanFTP server ') or {do not localize}
- TextStartsWith(FServerDesc, 'Titan FTP Server '); {do not localize}
- end;
- function TIdFTP.IsWSFTP : Boolean;
- begin
- Result := IndyPos('WS_FTP Server', FServerDesc) > 0; {do not localize}
- end;
- function TIdFTP.IsIIS: Boolean;
- begin
- Result := TextStartsWith(FServerDesc, 'Microsoft FTP Service'); {do not localize}
- end;
- function TIdFTP.IsServerMDTZAndListTForm: Boolean;
- begin
- Result := IsOldServU or IsBPFTP or IsTitan;
- end;
- procedure TIdFTP.Notification(AComponent: TComponent; Operation: TOperation);
- begin
- if (Operation = opRemove) and (AComponent = FCompressor) then begin
- SetCompressor(nil);
- end;
- inherited Notification(AComponent, Operation);
- end;
- procedure TIdFTP.SendPret(const ACommand: String);
- begin
- if IsExtSupported('PRET') then begin {do not localize}
- //note that we don't check for success or failure here
- //as some servers might fail and then succede with the transfer.
- //Pret might not work for some commands.
- SendCmd('PRET ' + ACommand); {do not localize}
- end;
- end;
- procedure TIdFTP.List;
- begin
- List(nil);
- end;
- procedure TIdFTP.List(const ASpecifier: string; ADetails: Boolean);
- begin
- List(nil, ASpecifier, ADetails);
- end;
- procedure TIdFTP.DoOnBannerAfterLogin(AText: TStrings);
- begin
- if Assigned(OnBannerAfterLogin) then begin
- OnBannerAfterLogin(Self, AText.Text);
- end;
- end;
- procedure TIdFTP.DoOnBannerBeforeLogin(AText: TStrings);
- begin
- if Assigned(OnBannerBeforeLogin) then begin
- OnBannerBeforeLogin(Self, AText.Text);
- end;
- end;
- procedure TIdFTP.DoOnBannerWarning(AText: TStrings);
- begin
- if Assigned(OnBannerWarning) then begin
- OnBannerWarning(Self, AText.Text);
- end;
- end;
- procedure TIdFTP.SetDataPortProtection(AValue: TIdFTPDataPortSecurity);
- begin
- if IsLoading then begin
- FDataPortProtection := AValue;
- Exit;
- end;
- if FDataPortProtection <> AValue then begin
- if FUseTLS = utNoTLSSupport then begin
- raise EIdFTPNoDataPortProtectionWOEncryption.Create(RSFTPNoDataPortProtectionWOEncryption);
- end;
- if FUsingCCC then begin
- raise EIdFTPNoDataPortProtectionAfterCCC.Create(RSFTPNoDataPortProtectionAfterCCC);
- end;
- FDataPortProtection := AValue;
- end;
- end;
- procedure TIdFTP.SetAUTHCmd(const AValue : TAuthCmd);
- begin
- if IsLoading then begin
- FAUTHCmd := AValue;
- Exit;
- end;
- if FAUTHCmd <> AValue then begin
- if FUseTLS = utNoTLSSupport then begin
- raise EIdFTPNoAUTHWOSSL.Create(RSFTPNoAUTHWOSSL);
- end;
- if FUsingSFTP then begin
- raise EIdFTPCanNotSetAUTHCon.Create(RSFTPNoAUTHCon);
- end;
- FAUTHCmd := AValue;
- end;
- end;
- procedure TIdFTP.SetDefStringEncoding(AValue: IIdTextEncoding);
- begin
- FDefStringEncoding := AValue;
- if IOHandler <> nil then begin
- IOHandler.DefStringEncoding := FDefStringEncoding;
- end;
- end;
- procedure TIdFTP.SetUseTLS(AValue: TIdUseTLS);
- begin
- inherited SetUseTLS(AValue);
- if IsLoading then begin
- Exit;
- end;
- if AValue = utNoTLSSupport then begin
- FDataPortProtection := Id_TIdFTP_DataPortProtection;
- FUseCCC := DEF_Id_FTP_UseCCC;
- FAUTHCmd := DEF_Id_FTP_AUTH_CMD;
- end;
- end;
- procedure TIdFTP.SetUseCCC(const AValue: Boolean);
- begin
- if (not IsLoading) and (FUseTLS = utNoTLSSupport) then begin
- raise EIdFTPNoCCCWOEncryption.Create(RSFTPNoCCCWOEncryption);
- end;
- FUseCCC := AValue;
- end;
- procedure TIdFTP.DoOnRetrievedDir;
- begin
- if Assigned(OnRetrievedDir) then begin
- OnRetrievedDir(Self);
- end;
- end;
- procedure TIdFTP.DoOnDirParseEnd;
- begin
- if Assigned(FOnDirParseEnd) then begin
- FOnDirParseEnd(Self);
- end;
- end;
- procedure TIdFTP.DoOnDirParseStart;
- begin
- if Assigned(FOnDirParseStart) then begin
- FOnDirParseStart(Self);
- end;
- end;
- //we do this to match some WS-FTP Pro firescripts I saw
- function TIdFTP.IsAccountNeeded: Boolean;
- begin
- Result := LastCmdResult.NumericCode = 332;
- if not Result then begin
- if IndyPos('ACCOUNT', LastCmdResult.Text.Text) > 0 then begin {do not localize}
- Result := FAccount <> '';
- end;
- end;
- end;
- //we can use one of three commands for verifying a file or stream
- function TIdFTP.GetSupportsVerification: Boolean;
- begin
- Result := Connected;
- if Result then begin
- Result := TIdHashSHA512.IsAvailable and IsExtSupported('XSHA512');
- if not Result then begin
- Result := TIdHashSHA256.IsAvailable and IsExtSupported('XSHA256');
- end;
- if not Result then begin
- Result := IsExtSupported('XSHA1') or
- (IsExtSupported('XMD5') and (not GetFIPSMode)) or
- IsExtSupported('XCRC');
- end;
- end;
- end;
- function TIdFTP.VerifyFile(const ALocalFile, ARemoteFile: String; const AStartPoint, AByteCount: TIdStreamSize): Boolean;
- var
- LLocalStream: TStream;
- LRemoteFileName : String;
- begin
- LRemoteFileName := ARemoteFile;
- if LRemoteFileName = '' then begin
- LRemoteFileName := ExtractFileName(ALocalFile);
- end;
- LLocalStream := TIdReadFileExclusiveStream.Create(ALocalFile);
- try
- Result := VerifyFile(LLocalStream, LRemoteFileName, AStartPoint, AByteCount);
- finally
- FreeAndNil(LLocalStream);
- end;
- end;
- {
- This procedure can use three possible commands to verify file integriety and the
- syntax does very amoung these. The commands are:
- XSHA1 - get SHA1 checksum for a file or file part
- XMD5 - get MD5 checksum for a file or file part
- XCRC - get CRC32 checksum
- The command preference is from first to last (going from longest length to shortest).
- }
- function TIdFTP.VerifyFile(ALocalFile: TStream; const ARemoteFile: String;
- const AStartPoint, AByteCount: TIdStreamSize): Boolean;
- var
- LRemoteCRC : String;
- LLocalCRC : String;
- LCmd : String;
- LRemoteFile: String;
- LStartPoint : TIdStreamSize;
- LByteCount : TIdStreamSize; //used instead of AByteCount so we don't exceed the file size
- LHashClass: TIdHashClass;
- LHash: TIdHash;
- begin
- LLocalCRC := '';
- LRemoteCRC := '';
- if AStartPoint > -1 then begin
- ALocalFile.Position := AStartPoint;
- end;
- LStartPoint := ALocalFile.Position;
- LByteCount := ALocalFile.Size - LStartPoint;
- if (LByteCount > AByteCount) and (AByteCount > 0) then begin
- LByteCount := AByteCount;
- end;
- //just in case the server doesn't support file names in quotes.
- if IndyPos(' ', ARemoteFile) > 0 then begin
- LRemoteFile := '"' + ARemoteFile + '"';
- end else begin
- LRemoteFile := ARemoteFile;
- end;
- if TIdHashSHA512.IsAvailable and IsExtSupported('XSHA512') then begin
- //XSHA256 <sp> pathname [<sp> startposition <sp> endposition]
- LCmd := 'XSHA512 ' + LRemoteFile;
- if AByteCount > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
- end
- else if AStartPoint > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint);
- end;
- LHashClass := TIdHashSHA512;
- end
- else if TIdHashSHA256.IsAvailable and IsExtSupported('XSHA256') then begin
- //XSHA256 <sp> pathname [<sp> startposition <sp> endposition]
- LCmd := 'XSHA256 ' + LRemoteFile;
- if AByteCount > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
- end
- else if AStartPoint > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint);
- end;
- LHashClass := TIdHashSHA256;
- end
- else if IsExtSupported('XSHA1') then begin
- //XMD5 "filename" startpos endpos
- //I think there's two syntaxes to this:
- //
- //Raiden Syntax if FEAT line contains " XMD5 filename;start;end"
- //
- //or what's used by some other servers if "FEAT line contains XMD5"
- //
- //XCRC "filename" [startpos] [number of bytes to calc]
- if IndexOfFeatLine('XSHA1 filename;start;end') > -1 then begin
- LCmd := 'XSHA1 ' + LRemoteFile + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LStartPoint + LByteCount-1);
- end else
- begin
- //BlackMoon FTP Server uses this one.
- LCmd := 'XSHA1 ' + LRemoteFile;
- if AByteCount > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
- end
- else if AStartPoint > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint);
- end;
- end;
- LHashClass := TIdHashSHA1;
- end
- else if IsExtSupported('XMD5') and (not GetFIPSMode) then begin
- //XMD5 "filename" startpos endpos
- //I think there's two syntaxes to this:
- //
- //Raiden Syntax if FEAT line contains " XMD5 filename;start;end"
- //
- //or what's used by some other servers if "FEAT line contains XMD5"
- //
- //XCRC "filename" [startpos] [number of bytes to calc]
- if IndexOfFeatLine('XMD5 filename;start;end') > -1 then begin
- LCmd := 'XMD5 ' + LRemoteFile + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LStartPoint + LByteCount-1);
- end else
- begin
- //BlackMoon FTP Server uses this one.
- LCmd := 'XMD5 ' + LRemoteFile;
- if AByteCount > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
- end
- else if AStartPoint > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint);
- end;
- end;
- LHashClass := TIdHashMessageDigest5;
- end else
- begin
- LCmd := 'XCRC ' + LRemoteFile;
- if AByteCount > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
- end
- else if AStartPoint > 0 then begin
- LCmd := LCmd + ' ' + IntToStr(LStartPoint);
- end;
- LHashClass := TIdHashCRC32;
- end;
- LHash := LHashClass.Create;
- try
- LLocalCRC := LHash.HashStreamAsHex(ALocalFile, LStartPoint, LByteCount);
- finally
- LHash.Free;
- end;
- if SendCmd(LCmd) = 250 then begin
- LRemoteCRC := Trim(LastCmdResult.Text.Text);
- IdDelete(LRemoteCRC, 1, IndyPos(' ', LRemoteCRC)); // delete the response
- Result := TextIsSame(LLocalCRC, LRemoteCRC);
- end else begin
- Result := False;
- end;
- end;
- end.
|