IdFTP.pas 160 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.126 4/28/2005 BTaylor
  18. Changed .Size to use Int64
  19. Rev 1.125 4/15/2005 9:10:10 AM JPMugaas
  20. Changed the default timeout in TIdFTP to one minute and made a comment about
  21. this.
  22. Some firewalls don't handle control connections properly during long data
  23. transfers. They will timeout the control connection because it's idle and
  24. making it worse is that they will chop off a connection instead of closing it
  25. causing TIdFTP to wait forever for nothing.
  26. Rev 1.124 3/20/2005 10:42:44 PM JPMugaas
  27. Marked TIdFTP.Quit as deprecated. We need to keep it only for compatibility.
  28. Rev 1.123 3/20/2005 2:44:08 PM JPMugaas
  29. Should now send quit. Verified here.
  30. Rev 1.122 3/12/2005 6:57:12 PM JPMugaas
  31. Attempt to add ACCT support for firewalls. I also used some logic from some
  32. WS-FTP Pro about ACCT to be more consistant with those Firescripts.
  33. Rev 1.121 3/10/2005 2:41:12 PM JPMugaas
  34. Removed the UseTelnetAbort property. It turns out that sending the sequence
  35. is causing problems on a few servers. I have made a comment about this in
  36. the source-code so someone later on will know why I decided not to send
  37. those.
  38. Rev 1.120 3/9/2005 10:05:54 PM JPMugaas
  39. Minor changes for Indy conventions.
  40. Rev 1.119 3/9/2005 9:15:46 PM JPMugaas
  41. Changes submitted by Craig Peterson, Scooter Software He noted this:
  42. "We had a user who's FTP server prompted for account info after a
  43. regular login, so I had to add an explicit Account string property and
  44. an OnNeedAccount event that we could use for a prompt." This does break any
  45. code using TIdFTP.Account.
  46. TODO: See about integrating Account Info into the proxy login sequences.
  47. Rev 1.118 3/9/2005 10:40:16 AM JPMugaas
  48. Made comment explaining why I had made a workaround in a procedure.
  49. Rev 1.117 3/9/2005 10:28:32 AM JPMugaas
  50. Fix for Abort problem when uploading. A workaround I made for WS-FTP Pro
  51. Server was not done correctly.
  52. Rev 1.116 3/9/2005 1:21:38 AM JPMugaas
  53. Made refinement to Abort and the data transfers to follow what Kudzu had
  54. originally done in Indy 8. I also fixed a problem with ABOR at
  55. ftp.ipswitch.com and I fixed a regression at ftp.marist.edu that occured when
  56. getting a directory.
  57. Rev 1.115 3/8/2005 12:14:50 PM JPMugaas
  58. Renamed UseOOBAbort to UseTelnetAbort because that's more accurate. We still
  59. don't support Out of Band Data (hopefully, we'll never have to do that).
  60. Rev 1.114 3/7/2005 10:40:10 PM JPMugaas
  61. Improvements:
  62. 1) Removed some duplicate code.
  63. 2) ABOR should now be properly handled outside of a data operation.
  64. 3) I added a UseOOBAbort read-write public property for controlling how the
  65. ABOR command is sent. If true, the Telnet sequences are sent or if false,
  66. the ABOR without sequences is sent. This is set to false by default because
  67. one FTP client (SmartFTP recently removed the Telnet sequences from their
  68. program).
  69. This code is expiriemental.
  70. Rev 1.113 3/7/2005 5:46:34 PM JPMugaas
  71. Reworked FTP Abort code to make it more threadsafe and make abort work. This
  72. is PRELIMINARY.
  73. Rev 1.112 3/5/2005 3:33:56 PM JPMugaas
  74. Fix for some compiler warnings having to do with TStream.Read being platform
  75. specific. This was fixed by changing the Compressor API to use TIdStreamVCL
  76. instead of TStream. I also made appropriate adjustments to other units for
  77. this.
  78. Rev 1.111 2/24/2005 6:46:36 AM JPMugaas
  79. Clarrified remarks I made and added a few more comments about syntax in
  80. particular cases in the set modified file date procedures.
  81. That's really been a ball....NOT!!!!
  82. Rev 1.110 2/24/2005 6:25:08 AM JPMugaas
  83. Attempt to fix problem setting Date with Titan FTP Server. I had made an
  84. incorrect assumption about MDTM on that system. It uses Syntax 3 (see my
  85. earlier note above the File Date Set problem.
  86. Rev 1.109 2/23/2005 6:32:54 PM JPMugaas
  87. Made note about MDTM syntax inconsistancy. There's a discussion about it.
  88. Rev 1.108 2/12/2005 8:08:04 AM JPMugaas
  89. Attempt to fix MDTM bug where msec was being sent.
  90. Rev 1.107 1/12/2005 11:26:44 AM JPMugaas
  91. Memory Leak fix when processing MLSD output and some minor tweeks Remy had
  92. E-Mailed me last night.
  93. Rev 1.106 11/18/2004 2:39:32 PM JPMugaas
  94. Support for another FTP Proxy type.
  95. Rev 1.105 11/18/2004 12:18:50 AM JPMugaas
  96. Fixed compile error.
  97. Rev 1.104 11/17/2004 3:59:22 PM JPMugaas
  98. Fixed a TODO item about FTP Proxy support with a "Transparent" proxy. I
  99. think you connect to the regular host and the firewall will intercept its
  100. login information.
  101. Rev 1.103 11/16/2004 7:31:52 AM JPMugaas
  102. Made a comment noting that UserSite is the same as USER after login for later
  103. reference.
  104. Rev 1.102 11/5/2004 1:54:42 AM JPMugaas
  105. Minor adjustment - should not detect TitanFTPD better (tested at:
  106. ftp.southrivertech.com).
  107. If MLSD is being used, SITE ZONE will not be issued. It's not needed because
  108. the MLSD spec indicates the time is based on GMT.
  109. Rev 1.101 10/27/2004 12:58:08 AM JPMugaas
  110. Improvement from Tobias Giesen http://www.superflexible.com
  111. His notation is below:
  112. "here's a fix for TIdFTP.IndexOfFeatLine. It does not work the
  113. way it is used in TIdFTP.SetModTime, because it only
  114. compares the first word of the FeatLine."
  115. Rev 1.100 10/26/2004 9:19:10 PM JPMugaas
  116. Fixed references.
  117. Rev 1.99 9/16/2004 3:24:04 AM JPMugaas
  118. TIdFTP now compresses to the IOHandler and decompresses from the IOHandler.
  119. Noted some that the ZLib code is based was taken from ZLibEx.
  120. Rev 1.98 9/13/2004 12:15:42 AM JPMugaas
  121. Now should be able to handle some values better as suggested by Michael J.
  122. Leave.
  123. Rev 1.97 9/11/2004 10:58:06 AM JPMugaas
  124. FTP now decompresses output directly to the IOHandler.
  125. Rev 1.96 9/10/2004 7:37:42 PM JPMugaas
  126. Fixed a bug. We needed to set Passthrough instead of calling StartSSL. This
  127. was causing a SSL problem with upload.
  128. Rev 1.95 8/2/04 5:56:16 PM RLebeau
  129. Tweaks to TIdFTP.InitDataChannel()
  130. Rev 1.94 7/30/2004 1:55:04 AM DSiders
  131. Corrected DoOnRetrievedDir naming.
  132. Rev 1.93 7/30/2004 12:36:32 AM DSiders
  133. Corrected spelling in OnRetrievedDir, DoOnRetrievedDir declarations.
  134. Rev 1.92 7/29/2004 2:15:28 AM JPMugaas
  135. New property for controlling what AUTH command is sent. Fixed some minor
  136. issues with FTP properties. Some were not set to defaults causing
  137. unpredictable results -- OOPS!!!
  138. Rev 1.91 7/29/2004 12:04:40 AM JPMugaas
  139. New events for Get and Put as suggested by Don Sides and to complement an
  140. event done by APR.
  141. Rev 1.90 7/28/2004 10:16:14 AM JPMugaas
  142. New events for determining when a listing is finished and when the dir
  143. parsing begins and ends. Dir parsing is done sometimes when DirectoryListing
  144. is referenced.
  145. Rev 1.89 7/27/2004 2:03:54 AM JPMugaas
  146. New property:
  147. ExternalIP - used to specify an IP address for the PORT and EPRT commands.
  148. This should be blank unless you are behind a NAT and you need to use PORT
  149. transfers with SSL. You would set ExternalIP to the NAT's IP address on the
  150. Internet.
  151. The idea is this:
  152. 1) You set up your NAT to forward a range ports ports to your computer behind
  153. the NAT.
  154. 2) You specify that a port range with the DataPortMin and DataPortMin
  155. properties.
  156. 3) You set ExternalIP to the NAT's Internet IP address.
  157. I have verified this with Indy and WS FTP Pro behind a NAT router.
  158. Rev 1.88 7/23/04 7:09:50 PM RLebeau
  159. Bug fix for TFileStream access rights in Get()
  160. Rev 1.87 7/18/2004 3:00:12 PM DSiders
  161. Added localization comments.
  162. Rev 1.86 7/16/2004 4:28:40 AM JPMugaas
  163. CCC Support in TIdFTP to complement that capability in TIdFTPServer.
  164. Rev 1.85 7/13/04 6:48:14 PM RLebeau
  165. Added support for new DataPort and DataPortMin/Max properties
  166. Rev 1.84 7/6/2004 4:51:46 PM DSiders
  167. Corrected spelling of Challenge in properties, methods, types.
  168. Rev 1.83 7/3/2004 3:15:50 AM JPMugaas
  169. Checked in so everyone else can work on stuff while I'm away.
  170. Rev 1.82 6/27/2004 1:45:38 AM JPMugaas
  171. Can now optionally support LastAccessTime like Smartftp's FTP Server could.
  172. I also made the MLST listing object and parser support this as well.
  173. Rev 1.81 6/20/2004 8:31:58 PM JPMugaas
  174. New events for reporting greeting and after login banners during the login
  175. sequence.
  176. Rev 1.80 6/20/2004 6:56:42 PM JPMugaas
  177. Start oin attempt to support FXP with Deflate compression. More work will
  178. need to be done.
  179. Rev 1.79 6/17/2004 3:42:32 PM JPMugaas
  180. Adjusted code for removal of dmBlock and dmCompressed. Made TransferMode a
  181. property. Note that the Set method is odd because I am trying to keep
  182. compatibility with older Indy versions.
  183. Rev 1.78 6/14/2004 6:19:02 PM JPMugaas
  184. This now refers to TIdStreamVCL when downloading isntead of directly to a
  185. memory stream when compressing data.
  186. Rev 1.77 6/14/2004 8:34:52 AM JPMugaas
  187. Fix for AV on Put with Passive := True.
  188. Rev 1.76 6/11/2004 9:34:12 AM DSiders
  189. Added "Do not Localize" comments.
  190. Rev 1.75 2004.05.20 11:37:16 AM czhower
  191. IdStreamVCL
  192. Rev 1.74 5/6/2004 6:54:26 PM JPMugaas
  193. FTP Port transfers with TransparentProxies is enabled. This only works if
  194. the TransparentProxy supports a "bind" request.
  195. Rev 1.73 5/4/2004 11:16:28 AM JPMugaas
  196. TransferTimeout property added and enabled (Bug 96).
  197. Rev 1.72 5/4/2004 11:07:12 AM JPMugaas
  198. Timeouts should now be reenabled in TIdFTP.
  199. Rev 1.71 4/19/2004 5:05:02 PM JPMugaas
  200. Class rework Kudzu wanted.
  201. Rev 1.70 2004.04.16 9:31:42 PM czhower
  202. Remove unnecessary duplicate string parsing and replaced with .assign.
  203. Rev 1.69 2004.04.15 7:09:04 PM czhower
  204. .NET overloads
  205. Rev 1.68 4/15/2004 9:46:48 AM JPMugaas
  206. List no longer requires a TStrings. It turns out that it was an optional
  207. parameter.
  208. Rev 1.67 2004.04.15 2:03:28 PM czhower
  209. Removed login param from connect and made it a prop like POP3.
  210. Rev 1.66 3/3/2004 5:57:40 AM JPMugaas
  211. Some IFDEF excluses were removed because the functionality is now in DotNET.
  212. Rev 1.65 2004.03.03 11:54:26 AM czhower
  213. IdStream change
  214. Rev 1.64 2/20/2004 1:01:06 PM JPMugaas
  215. Preliminary FTP PRET command support for using PASV with a distributed FTP
  216. server (Distributed PASV -
  217. http://drftpd.org/wiki/wiki.phtml?title=Distributed_PASV).
  218. Rev 1.63 2/17/2004 12:25:52 PM JPMugaas
  219. The client now supports MODE Z (deflate) uploads and downloads as specified
  220. by http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt
  221. Rev 1.62 2004.02.03 5:45:10 PM czhower
  222. Name changes
  223. Rev 1.61 2004.02.03 2:12:06 PM czhower
  224. $I path change
  225. Rev 1.60 1/27/2004 10:17:10 PM JPMugaas
  226. Fix from Steve Loft for a server that sends something like this:
  227. "227 Passive mode OK (195,92,195,164,4,99 )"
  228. Rev 1.59 1/27/2004 3:59:28 PM SPerry
  229. StringStream ->IdStringStream
  230. Rev 1.58 24/01/2004 19:13:58 CCostelloe
  231. Cleaned up warnings
  232. Rev 1.57 1/21/2004 2:27:50 PM JPMugaas
  233. Bullete Proof FTPD and Titan FTP support SITE ZONE. Saw this in a command
  234. database in StaffFTP.
  235. InitComponent.
  236. Rev 1.56 1/19/2004 9:05:38 PM JPMugaas
  237. Fixes to FTP Set Date functionality.
  238. Introduced properties for Time Zone information from the server. The way it
  239. works is this, if TIdFTP detects you are using "Serv-U" or SITE ZONE is
  240. listed in the FEAT reply, Indy obtains the time zone information with the
  241. SITE ZONE command and makes the appropriate calculation. Indy then uses this
  242. information to calculate a timestamp to send to the server with the MDTM
  243. command. You can also use the Time Zone information yourself to convert the
  244. FTP directory listing item timestamps into GMT and than convert that to your
  245. local time.
  246. FTP Voyager uses SITE ZONE as I've described.
  247. Rev 1.55 1/19/2004 4:39:08 AM JPMugaas
  248. You can now set the time for a file on the server. Note that these methods
  249. try to treat the time as relative to GMT.
  250. Rev 1.54 1/17/2004 9:09:30 PM JPMugaas
  251. Should now compile.
  252. Rev 1.53 1/17/2004 7:48:02 PM JPMugaas
  253. FXP site to site transfer code was redone for improvements with FXP with TLS.
  254. It actually works and I verified with RaidenFTPD
  255. (http://www.raidenftpd.com/) and the Indy FTP server components. I also
  256. lowered the requirements for TLS FXP transfers. The requirements now are:
  257. 1) Only server (either the recipient or the sendor) has to support SSCN
  258. or
  259. 2) The server receiving a PASV must support CPSV and the transfer is done
  260. with IPv4.
  261. Rev 1.52 1/9/2004 2:51:26 PM JPMugaas
  262. Started IPv6 support.
  263. Rev 1.51 11/27/2003 4:55:28 AM JPMugaas
  264. Made STOU functionality separate from PUT functionality. Put now requires a
  265. destination filename except where a source-file name is given. In that case,
  266. the default is the filename from the source string.
  267. Rev 1.50 10/26/2003 04:28:50 PM JPMugaas
  268. Reworked Status.
  269. The old one was problematic because it assumed that STAT was a request to
  270. send a directory listing through the control channel. This assumption is not
  271. correct. It provides a way to get a freeform status report from a server.
  272. With a Path parameter, it should work like a LIST command except that the
  273. control connection is used. We don't support that feature and you should use
  274. our LIst method to get the directory listing anyway, IMAO.
  275. Rev 1.49 10/26/2003 9:17:46 PM BGooijen
  276. Compiles in DotNet, and partially works there
  277. Rev 1.48 10/24/2003 12:43:48 PM JPMugaas
  278. Should work again.
  279. Rev 1.47 2003.10.24 10:43:04 AM czhower
  280. TIdSTream to dos
  281. Rev 1.46 10/20/2003 03:06:10 PM JPMugaas
  282. SHould now work.
  283. Rev 1.45 10/20/2003 01:00:38 PM JPMugaas
  284. EIdException no longer raised. Some things were being gutted needlessly.
  285. Rev 1.44 10/19/2003 12:58:20 PM DSiders
  286. Added localization comments.
  287. Rev 1.43 2003.10.14 9:56:50 PM czhower
  288. Compile todos
  289. Rev 1.42 2003.10.12 3:50:40 PM czhower
  290. Compile todos
  291. Rev 1.41 10/10/2003 11:32:26 PM SPerry
  292. -
  293. Rev 1.40 10/9/2003 10:17:02 AM JPMugaas
  294. Added overload for GetLoginPassword for providing a challanage string which
  295. doesn't have to the last command reply.
  296. Added CLNT support.
  297. Rev 1.39 10/7/2003 05:46:20 AM JPMugaas
  298. SSCN Support added.
  299. Rev 1.38 10/6/2003 08:56:44 PM JPMugaas
  300. Reworked the FTP list parsing framework so that the user can obtain the list
  301. of capabilities from a parser class with TIdFTP. This should permit the user
  302. to present a directory listing differently for each parser (some FTP list
  303. parsers do have different capabilities).
  304. Rev 1.37 10/1/2003 12:51:18 AM JPMugaas
  305. SSL with active (PORT) transfers now should work again.
  306. Rev 1.36 9/30/2003 09:50:38 PM JPMugaas
  307. FTP with TLS should work better. It turned out that we were negotiating it
  308. several times causing a hang. I also made sure that we send PBSZ 0 and PROT
  309. P for both implicit and explicit TLS. Data ports should work in PASV again.
  310. Rev 1.35 9/28/2003 11:41:06 PM JPMugaas
  311. Reworked Eldos's proposed FTP fix as suggested by Henrick Hellström by moving
  312. all of the IOHandler creation code to InitDataChannel. This should reduce
  313. the likelihood of error.
  314. Rev 1.33 9/18/2003 11:22:40 AM JPMugaas
  315. Removed a temporary workaround for an OnWork bug that was in the Indy Core.
  316. That bug was fixed so there's no sense in keeping a workaround here.
  317. Rev 1.32 9/12/2003 08:05:30 PM JPMugaas
  318. A temporary fix for OnWork events not firing. The bug is that OnWork events
  319. aren't used in IOHandler where ReadStream really is located.
  320. Rev 1.31 9/8/2003 02:33:00 AM JPMugaas
  321. OnCustomFTPProxy added to allow Indy to support custom FTP proxies. When
  322. using this event, you are responsible for programming the FTP Proxy and FTP
  323. Server login sequence.
  324. GetLoginPassword method function for returning the password used when logging
  325. into a FTP server which handles OTP calculation. This way, custom firewall
  326. support can handle One-Time-Password system transparently. You do have to
  327. send the User ID before calling this function because the OTP challenge is
  328. part of the reply.
  329. Rev 1.30 6/10/2003 11:10:00 PM JPMugaas
  330. Made comments about our loop that tries several AUTH command variations.
  331. Some servers may only accept AUTH SSL while other servers only accept AUTH
  332. TLS.
  333. Rev 1.29 5/26/2003 12:21:54 PM JPMugaas
  334. Rev 1.28 5/25/2003 03:54:20 AM JPMugaas
  335. Rev 1.27 5/19/2003 08:11:32 PM JPMugaas
  336. Now should compile properly with new code in Core.
  337. Rev 1.26 5/8/2003 11:27:42 AM JPMugaas
  338. Moved feature negoation properties down to the ExplicitTLSClient level as
  339. feature negotiation goes hand in hand with explicit TLS support.
  340. Rev 1.25 4/5/2003 02:06:34 PM JPMugaas
  341. TLS handshake itself can now be handled.
  342. Rev 1.24 4/4/2003 8:01:32 PM BGooijen
  343. now creates iohandler for dataconnection
  344. Rev 1.23 3/31/2003 08:40:18 AM JPMugaas
  345. Fixed problem with QUIT command.
  346. Rev 1.22 3/27/2003 3:41:28 PM BGooijen
  347. Changed because some properties are moved to IOHandler
  348. Rev 1.21 3/27/2003 05:46:24 AM JPMugaas
  349. Updated framework with an event if the TLS negotiation command fails.
  350. Cleaned up some duplicate code in the clients.
  351. Rev 1.20 3/26/2003 04:19:20 PM JPMugaas
  352. Cleaned-up some code and illiminated some duplicate things.
  353. Rev 1.19 3/24/2003 04:56:10 AM JPMugaas
  354. A typecast was incorrect and could cause a potential source of instability if
  355. a TIdIOHandlerStack was not used.
  356. Rev 1.18 3/16/2003 06:09:58 PM JPMugaas
  357. Fixed port setting bug.
  358. Rev 1.17 3/16/2003 02:40:16 PM JPMugaas
  359. FTP client with new design.
  360. Rev 1.16 3/16/2003 1:02:44 AM BGooijen
  361. Added 2 events to give the user more control to the dataconnection, moved
  362. SendTransferType, enabled ssl
  363. Rev 1.15 3/13/2003 09:48:58 AM JPMugaas
  364. Now uses an abstract SSL base class instead of OpenSSL so 3rd-party vendors
  365. can plug-in their products.
  366. Rev 1.14 3/7/2003 11:51:52 AM JPMugaas
  367. Fixed a writeln bug and an IOError issue.
  368. Rev 1.13 3/3/2003 07:06:26 PM JPMugaas
  369. FFreeIOHandlerOnDisconnect to FreeIOHandlerOnDisconnect at Bas's instruction
  370. Rev 1.12 2/21/2003 06:54:36 PM JPMugaas
  371. The FTP list processing has been restructured so that Directory output is not
  372. done by IdFTPList. This now also uses the IdFTPListParserBase for parsing so
  373. that the code is more scalable.
  374. Rev 1.11 2/17/2003 04:45:36 PM JPMugaas
  375. Now temporarily change the transfer mode to ASCII when requesting a DIR.
  376. TOPS20 does not like transfering dirs in binary mode and it might be a good
  377. idea to do it anyway.
  378. Rev 1.10 2/16/2003 03:22:20 PM JPMugaas
  379. Removed the Data Connection assurance stuff. We figure things out from the
  380. draft specificaiton, the only servers we found would not send any data after
  381. the new commands were sent, and there were only 2 server types that supported
  382. it anyway.
  383. Rev 1.9 2/16/2003 10:51:08 AM JPMugaas
  384. Attempt to implement:
  385. http://www.ietf.org/internet-drafts/draft-ietf-ftpext-data-connection-assuranc
  386. e-00.txt
  387. Currently commented out because it does not work.
  388. Rev 1.8 2/14/2003 11:40:16 AM JPMugaas
  389. Fixed compile error.
  390. Rev 1.7 2/14/2003 10:38:32 AM JPMugaas
  391. Removed a problematic override for GetInternelResponse. It was messing up
  392. processing of the FEAT.
  393. Rev 1.6 12-16-2002 20:48:10 BGooijen
  394. now uses TIdIOHandler.ConstructIOHandler to construct iohandlers
  395. IPv6 works again
  396. Independant of TIdIOHandlerStack again
  397. Rev 1.5 12-15-2002 23:27:26 BGooijen
  398. now compiles on Indy 10, but some things like IPVersion still need to be
  399. changed
  400. Rev 1.4 12/15/2002 04:07:02 PM JPMugaas
  401. Started port to Indy 10. Still can not complete it though.
  402. Rev 1.3 12/6/2002 05:29:38 PM JPMugaas
  403. Now decend from TIdTCPClientCustom instead of TIdTCPClient.
  404. Rev 1.2 12/1/2002 04:18:02 PM JPMugaas
  405. Moved all dir parsing code to one place. Reworked to use more than one line
  406. for determining dir format type along with flfNextLine dir format type.
  407. Rev 1.1 11/14/2002 04:02:58 PM JPMugaas
  408. Removed cludgy code that was a workaround for the RFC Reply limitation. That
  409. is no longer limited.
  410. Rev 1.0 11/14/2002 02:20:00 PM JPMugaas
  411. 2002-10-25 - J. Peter Mugaas
  412. - added XCRC support - specified by "GlobalSCAPE Secure FTP Server User’s Guide"
  413. which is available at http://www.globalscape.com
  414. and also explained at http://www.southrivertech.com/support/titanftp/webhelp/titanftp.htm
  415. - added COMB support - specified by "GlobalSCAPE Secure FTP Server User’s Guide"
  416. which is available at http://www.globalscape.com
  417. and also explained at http://www.southrivertech.com/support/titanftp/webhelp/titanftp.htm
  418. 2002-10-24 - J. Peter Mugaas
  419. - now supports RFC 2640 - FTP Internalization
  420. 2002-09-18
  421. _ added AFromBeginning parameter to InternalPut to correctly honor the AAppend parameter of Put
  422. 2002-09-05 - J. Peter Mugaas
  423. - now complies with RFC 2389 - Feature negotiation mechanism for the File Transfer Protocol
  424. - now complies with RFC 2428 - FTP Extensions for IPv6 and NATs
  425. 2002-08-27 - Andrew P.Rybin
  426. - proxy support fix (non-standard ftp port's)
  427. 2002-01-xx - Andrew P.Rybin
  428. - Proxy support, OnAfterGet (ex:decrypt, set srv timestamp)
  429. - J.Peter Mugaas: not readonly ProxySettings
  430. A Neillans - 10/17/2001
  431. Merged changes submitted by Andrew P.Rybin
  432. Correct command case problems - some servers expect commands in Uppercase only.
  433. SP - 06/08/2001
  434. Added a few more functions
  435. Doychin - 02/18/2001
  436. OnAfterLogin event handler and Login method
  437. OnAfterLogin is executed after successfull login but before setting up the
  438. connection properties. This event can be used to provide FTP proxy support
  439. from the user application. Look at the FTP demo program for more information
  440. on how to provide such support.
  441. Doychin - 02/17/2001
  442. New onFTPStatus event
  443. New Quote method for executing commands not implemented by the compoent
  444. -CleanDir contributed by Amedeo Lanza
  445. }
  446. unit IdFTP;
  447. {
  448. TODO: Change the FTP demo to demonstrate the use of the new events and add proxy support
  449. }
  450. interface
  451. {$i IdCompilerDefines.inc}
  452. uses
  453. Classes,
  454. IdAssignedNumbers, IdGlobal, IdExceptionCore,
  455. IdExplicitTLSClientServerBase, IdFTPCommon, IdFTPList, IdFTPListParseBase,
  456. IdException, IdIOHandler, IdIOHandlerSocket, IdReply, IdReplyFTP, IdBaseComponent,
  457. IdSocketHandle, IdTCPConnection, IdTCPClient,
  458. IdThreadSafe, IdZLibCompressorBase;
  459. type
  460. //APR 011216:
  461. TIdFtpProxyType = (
  462. fpcmNone,//Connect method:
  463. fpcmUserSite, //Send command USER user@hostname - USER after login (see: http://isservices.tcd.ie/internet/command_config.php)
  464. fpcmSite, //Send command SITE (with logon)
  465. fpcmOpen, //Send command OPEN
  466. fpcmUserPass,//USER user@firewalluser@hostname / PASS pass@firewallpass
  467. fpcmTransparent, //First use the USER and PASS command with the firewall username and password, and then with the target host username and password.
  468. fpcmUserHostFireWallID, //USER hostuserId@hostname firewallUsername
  469. fpcmNovellBorder, //Novell BorderManager Proxy
  470. fpcmHttpProxyWithFtp, //HTTP Proxy with FTP support. Will be supported in Indy 10
  471. fpcmCustomProxy // use OnCustomFTPProxy to customize the proxy login
  472. ); //TIdFtpProxyType
  473. //This has to be in the same order as TLS_AUTH_NAMES
  474. TAuthCmd = (tAuto, tAuthTLS, tAuthSSL, tAuthTLSC, tAuthTLSP);
  475. const
  476. Id_TIdFTP_TransferType = {ftBinary} ftASCII; // RLebeau 1/22/08: per RFC 959
  477. Id_TIdFTP_Passive = False;
  478. Id_TIdFTP_UseNATFastTrack = False;
  479. Id_TIdFTP_HostPortDelimiter = ':';
  480. Id_TIdFTP_DataConAssurance = False;
  481. Id_TIdFTP_DataPortProtection = ftpdpsClear;
  482. //
  483. DEF_Id_TIdFTP_Implicit = False;
  484. DEF_Id_FTP_UseExtendedDataPort = False;
  485. DEF_Id_TIdFTP_UseExtendedData = False;
  486. DEF_Id_TIdFTP_UseMIS = True;
  487. DEF_Id_FTP_UseCCC = False;
  488. DEF_Id_FTP_AUTH_CMD = tAuto;
  489. DEF_Id_FTP_ListenTimeout = 10000; // ten seconds
  490. {
  491. Soem firewalls don't handle control connections properly during long data transfers.
  492. They will timeout the control connection because it's idle and making it worse is that they
  493. will chop off a connection instead of closing it causing TIdFTP to wait forever for nothing.
  494. }
  495. DEF_Id_FTP_READTIMEOUT = 60000; //one minute
  496. DEF_Id_FTP_UseHOST = True;
  497. DEF_Id_FTP_PassiveUseControlHost = False;
  498. DEF_Id_FTP_AutoIssueFEAT = True;
  499. DEF_Id_FTP_AutoLogin = True;
  500. type
  501. //Added by SP
  502. TIdCreateFTPList = procedure(ASender: TObject; var VFTPList: TIdFTPListItems) of object;
  503. //TIdCheckListFormat = procedure(ASender: TObject; const ALine: String; var VListFormat: TIdFTPListFormat) of object;
  504. TOnAfterClientLogin = TNotifyEvent;
  505. TIdFtpAfterGet = procedure(ASender: TObject; AStream: TStream) of object; //APR
  506. TIdOnDataChannelCreate = procedure(ASender: TObject; ADataChannel: TIdTCPConnection) of object;
  507. TIdOnDataChannelDestroy = procedure(ASender: TObject; ADataChannel: TIdTCPConnection) of object;
  508. TIdNeedAccountEvent = procedure(ASender: TObject; var VAcct: string) of object;
  509. TIdFTPBannerEvent = procedure (ASender: TObject; const AMsg : String) of object;
  510. TIdFtpProxySettings = class (TPersistent)
  511. protected
  512. FHost, FUserName, FPassword: String;
  513. FProxyType: TIdFtpProxyType;
  514. FPort: TIdPort;
  515. public
  516. procedure Assign(Source: TPersistent); override;
  517. published
  518. property ProxyType: TIdFtpProxyType read FProxyType write FProxyType;
  519. property Host: String read FHost write FHost;
  520. property UserName: String read FUserName write FUserName;
  521. property Password: String read FPassword write FPassword;
  522. property Port: TIdPort read FPort write FPort;
  523. end;
  524. TIdFTPTZInfo = class(TPersistent)
  525. protected
  526. FGMTOffset : TDateTime;
  527. FGMTOffsetAvailable : Boolean;
  528. public
  529. procedure Assign(Source: TPersistent); override;
  530. published
  531. property GMTOffset : TDateTime read FGMTOffset write FGMTOffset;
  532. property GMTOffsetAvailable : Boolean read FGMTOffsetAvailable write FGMTOffsetAvailable;
  533. end;
  534. TIdFTPKeepAlive = class(TPersistent)
  535. protected
  536. FUseKeepAlive: Boolean;
  537. FIdleTimeMS: Integer;
  538. FIntervalMS: Integer;
  539. public
  540. procedure Assign(Source: TPersistent); override;
  541. published
  542. // TODO: replace UseKeepAlive with an enum/set that allows keepalives to
  543. // be enabled on the command connection for its entire lifetime, not just
  544. // during transfers, and maybe also add an option to enable keepalives on
  545. // the data connections as well...
  546. property UseKeepAlive: Boolean read FUseKeepAlive write FUseKeepAlive;
  547. property IdleTimeMS: Integer read FIdleTimeMS write FIdleTimeMS;
  548. property IntervalMS: Integer read FIntervalMS write FIntervalMS;
  549. end;
  550. TIdFTP = class(TIdExplicitTLSClient)
  551. protected
  552. FAutoLogin: Boolean;
  553. FAutoIssueFEAT : Boolean;
  554. FCurrentTransferMode : TIdFTPTransferMode;
  555. FClientInfo : TIdFTPClientIdentifier;
  556. FServerInfo : TIdFTPServerIdentifier;
  557. FDataSettingsSent: Boolean; // only send SSL data settings once per connection
  558. FUsingSFTP : Boolean; //enable SFTP internel flag
  559. FUsingCCC : Boolean; //are we using FTP with SSL on a clear control channel?
  560. FUseHOST: Boolean;
  561. FServerHOST: String;
  562. FCanUseMLS : Boolean; //can we use MLISx instead of LIST
  563. FUsingExtDataPort : Boolean; //are NAT Extensions (RFC 2428 available) flag
  564. FUsingNATFastTrack : Boolean;//are we using NAT fastrack feature
  565. FCanResume: Boolean;
  566. FListResult: TStrings;
  567. FLoginMsg: TIdReplyFTP;
  568. FPassive: Boolean;
  569. FPassiveUseControlHost: Boolean;
  570. FDataPortProtection : TIdFTPDataPortSecurity;
  571. FAUTHCmd : TAuthCmd;
  572. FDataPort: TIdPort;
  573. FDataPortMin: TIdPort;
  574. FDataPortMax: TIdPort;
  575. FDefStringEncoding: IIdTextEncoding;
  576. FExternalIP : String;
  577. FResumeTested: Boolean;
  578. FServerDesc: string;
  579. FSystemDesc: string;
  580. FTransferType: TIdFTPTransferType;
  581. FTransferTimeout : Integer;
  582. FListenTimeout : Integer;
  583. FDataChannel: TIdTCPConnection;
  584. FDirectoryListing: TIdFTPListItems;
  585. FDirFormat : String;
  586. FListParserClass : TIdFTPListParseClass;
  587. FOnAfterClientLogin: TNotifyEvent;
  588. FOnCreateFTPList: TIdCreateFTPList;
  589. FOnBeforeGet: TNotifyEvent;
  590. FOnBeforePut: TIdFtpAfterGet;
  591. //in case someone needs to do something special with the data being uploaded
  592. FOnAfterGet: TIdFtpAfterGet; //APR
  593. FOnAfterPut: TNotifyEvent; //JPM at Don Sider's suggestion
  594. FOnNeedAccount: TIdNeedAccountEvent;
  595. FOnCustomFTPProxy : TNotifyEvent;
  596. FOnDataChannelCreate: TIdOnDataChannelCreate;
  597. FOnDataChannelDestroy: TIdOnDataChannelDestroy;
  598. FProxySettings: TIdFtpProxySettings;
  599. FUseExtensionDataPort : Boolean;
  600. FTryNATFastTrack : Boolean;
  601. FUseMLIS : Boolean;
  602. FLangsSupported : TStrings;
  603. FUseCCC: Boolean;
  604. //is the SSCN Client method on for this connection?
  605. FSSCNOn : Boolean;
  606. FIsCompressionSupported : Boolean;
  607. FOnBannerBeforeLogin : TIdFTPBannerEvent;
  608. FOnBannerAfterLogin : TIdFTPBannerEvent;
  609. FOnBannerWarning : TIdFTPBannerEvent;
  610. FTZInfo : TIdFTPTZInfo;
  611. {$IFDEF USE_OBJECT_ARC}[Weak]{$ENDIF} FCompressor : TIdZLibCompressorBase;
  612. //ZLib settings
  613. FZLibCompressionLevel : Integer; //7
  614. FZLibWindowBits : Integer; //-15
  615. FZLibMemLevel : Integer; //8
  616. FZLibStratagy : Integer; //0 - default
  617. //dir events for some GUI programs.
  618. //The directory was Retrieved from the FTP server.
  619. FOnRetrievedDir : TNotifyEvent;
  620. //parsing is done only when DirectoryListing is referenced
  621. FOnDirParseStart : TNotifyEvent;
  622. FOnDirParseEnd : TNotifyEvent;
  623. //we probably need an Abort flag so we know when an abort is sent.
  624. //It turns out that one server will send a 550 or 451 error followed by an
  625. //ABOR successfull
  626. FAbortFlag : TIdThreadSafeBoolean;
  627. FAccount: string;
  628. FNATKeepAlive: TIdFTPKeepAlive;
  629. //
  630. procedure DoOnDataChannelCreate;
  631. procedure DoOnDataChannelDestroy;
  632. procedure DoOnRetrievedDir;
  633. procedure DoOnDirParseStart;
  634. procedure DoOnDirParseEnd;
  635. procedure FinalizeDataOperation;
  636. procedure SetTZInfo(const Value: TIdFTPTZInfo);
  637. function IsSiteZONESupported : Boolean;
  638. function IndexOfFeatLine(const AFeatLine : String):Integer;
  639. procedure ClearSSCN;
  640. function SetSSCNToOn : Boolean;
  641. procedure SendInternalPassive(const ACmd : String; var VIP: string; var VPort: TIdPort);
  642. procedure SendCPassive(var VIP: string; var VPort: TIdPort);
  643. function FindAuthCmd : String;
  644. //
  645. function GetReplyClass: TIdReplyClass; override;
  646. //
  647. procedure ParseFTPList;
  648. procedure SetPassive(const AValue : Boolean);
  649. procedure SetTryNATFastTrack(const AValue: Boolean);
  650. procedure DoTryNATFastTrack;
  651. procedure SetUseExtensionDataPort(const AValue: Boolean);
  652. procedure SetIPVersion(const AValue: TIdIPVersion); override;
  653. procedure SetIOHandler(AValue: TIdIOHandler); override;
  654. function GetSupportsTLS: Boolean; override;
  655. procedure ConstructDirListing;
  656. procedure DoAfterLogin;
  657. procedure DoFTPList;
  658. procedure DoCustomFTPProxy;
  659. procedure DoOnBannerAfterLogin(AText : TStrings);
  660. procedure DoOnBannerBeforeLogin(AText : TStrings);
  661. procedure DoOnBannerWarning(AText : TStrings);
  662. procedure SendPBSZ; //protection buffer size
  663. procedure SendPROT; //data port protection
  664. procedure SendDataSettings; //this is for the extensions only;
  665. // procedure DoCheckListFormat(const ALine: String);
  666. function GetDirectoryListing: TIdFTPListItems;
  667. // function GetOnParseCustomListFormat: TIdOnParseCustomListFormat;
  668. procedure InitDataChannel;
  669. //PRET is to help distributed FTP systems by letting them know what you will do
  670. //before issuing a PASV. See: http://drftpd.mog.se/wiki/wiki.phtml?title=Distributed_PASV#PRE_Transfer_Command_for_Distributed_PASV_Transfers
  671. //for a discussion.
  672. procedure SendPret(const ACommand : String);
  673. procedure InternalGet(const ACommand: string; ADest: TStream; AResume: Boolean = false);
  674. procedure InternalPut(const ACommand: string; ASource: TStream; AFromBeginning: Boolean = True; AResume: Boolean = False);
  675. // procedure SetOnParseCustomListFormat(const AValue: TIdOnParseCustomListFormat);
  676. procedure SendPassive(var VIP: string; var VPort: TIdPort);
  677. procedure SendPort(AHandle: TIdSocketHandle); overload;
  678. procedure SendPort(const AIP : String; const APort : TIdPort); overload;
  679. procedure ParseEPSV(const AReply : String; var VIP : String; var VPort : TIdPort);
  680. //These two are for RFC 2428.txt
  681. procedure SendEPort(AHandle: TIdSocketHandle); overload;
  682. procedure SendEPort(const AIP : String; const APort : TIdPort; const AIPVersion : TIdIPVersion); overload;
  683. procedure SendEPassive(var VIP: string; var VPort: TIdPort);
  684. function SendHost: Int16;
  685. procedure SetProxySettings(const Value: TIdFtpProxySettings);
  686. procedure SetClientInfo(const AValue: TIdFTPClientIdentifier);
  687. procedure SetCompressor(AValue: TIdZLibCompressorBase);
  688. procedure SendTransferType(AValue: TIdFTPTransferType);
  689. procedure SetTransferType(AValue: TIdFTPTransferType);
  690. procedure DoBeforeGet; virtual;
  691. procedure DoBeforePut(AStream: TStream); virtual;
  692. procedure DoAfterGet(AStream: TStream); virtual; //APR
  693. procedure DoAfterPut; virtual;
  694. class procedure FXPSetTransferPorts(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv : Boolean);
  695. class procedure FXPSendFile(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String);
  696. class function InternalEncryptedTLSFXP(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean) : Boolean;
  697. class function InternalUnencryptedFXP(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean;
  698. class function ValidateInternalIsTLSFXP(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv : Boolean): Boolean;
  699. procedure InitComponent; override;
  700. procedure SetUseTLS(AValue : TIdUseTLS); override;
  701. procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  702. procedure SetDataPortProtection(AValue : TIdFTPDataPortSecurity);
  703. procedure SetAUTHCmd(const AValue : TAuthCmd);
  704. procedure SetDefStringEncoding(AValue: IIdTextEncoding);
  705. procedure SetUseCCC(const AValue: Boolean);
  706. procedure SetNATKeepAlive(AValue: TIdFTPKeepAlive);
  707. procedure IssueFEAT;
  708. //specific server detection
  709. function IsOldServU: Boolean;
  710. function IsBPFTP : Boolean;
  711. function IsTitan : Boolean;
  712. function IsWSFTP : Boolean;
  713. function IsIIS: Boolean;
  714. function CheckAccount: Boolean;
  715. function IsAccountNeeded : Boolean;
  716. function GetSupportsVerification : Boolean;
  717. public
  718. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  719. constructor Create(AOwner: TComponent); reintroduce; overload;
  720. {$ENDIF}
  721. procedure GetInternalResponse(AEncoding: IIdTextEncoding = nil); override;
  722. function {$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(
  723. const AResponse: Int16; const AAllowedResponses: array of Int16): Int16; override;
  724. function IsExtSupported(const ACmd : String):Boolean;
  725. procedure ExtractFeatFacts(const ACmd : String; AResults : TStrings);
  726. //this function transparantly handles OTP based on the Last command response
  727. //so it needs to be called only after the USER command or equivilent.
  728. function GetLoginPassword : String; overload;
  729. function GetLoginPassword(const APrompt : String) : String; overload;
  730. procedure Abort; virtual;
  731. procedure Allocate(AAllocateBytes: Integer);
  732. procedure ChangeDir(const ADirName: string);
  733. procedure ChangeDirUp;
  734. procedure Connect; override;
  735. destructor Destroy; override;
  736. procedure Delete(const AFilename: string);
  737. procedure FileStructure(AStructure: TIdFTPDataStructure);
  738. procedure Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = false); overload;
  739. procedure Get(const ASourceFile, ADestFile: string; const ACanOverwrite: boolean = false; AResume: Boolean = false); overload;
  740. procedure Help(AHelpContents: TStrings; ACommand: String = '');
  741. procedure KillDataChannel; virtual;
  742. //.NET Overload
  743. procedure List; overload;
  744. //.NET Overload
  745. procedure List(const ASpecifier: string; ADetails: Boolean = True); overload;
  746. procedure List(ADest: TStrings; const ASpecifier: string = ''; ADetails: Boolean = True); overload;
  747. procedure ExtListDir(ADest: TStrings = nil; const ADirectory: string = '');
  748. procedure ExtListItem(ADest: TStrings; AFList : TIdFTPListItems; const AItem: string=''); overload;
  749. procedure ExtListItem(ADest: TStrings; const AItem: string = ''); overload;
  750. procedure ExtListItem(AFList : TIdFTPListItems; const AItem : String= ''); overload;
  751. function FileDate(const AFileName : String; const AsGMT : Boolean = False): TDateTime;
  752. procedure Login;
  753. procedure MakeDir(const ADirName: string);
  754. procedure Noop;
  755. procedure SetCmdOpt(const ACMD, AOptions : String);
  756. procedure Put(const ASource: TStream; const ADestFile: string;
  757. const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1); overload;
  758. procedure Put(const ASourceFile: string; const ADestFile: string = '';
  759. const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1); overload;
  760. procedure StoreUnique(const ASource: TStream; const AStartPos: TIdStreamSize = -1); overload;
  761. procedure StoreUnique(const ASourceFile: string; const AStartPos: TIdStreamSize = -1); overload;
  762. procedure SiteToSiteUpload(const AToSite : TIdFTP; const ASourceFile : String; const ADestFile : String = '');
  763. procedure SiteToSiteDownload(const AFromSite: TIdFTP; const ASourceFile : String; const ADestFile : String = '');
  764. procedure DisconnectNotifyPeer; override;
  765. procedure Quit; {$IFDEF HAS_DEPRECATED}deprecated{$IFDEF HAS_DEPECATED_MSG} 'Use Disconnect() instead'{$ENDIF};{$ENDIF}
  766. function Quote(const ACommand: String): Int16;
  767. procedure RemoveDir(const ADirName: string);
  768. procedure Rename(const ASourceFile, ADestFile: string);
  769. function ResumeSupported: Boolean;
  770. function RetrieveCurrentDir: string;
  771. procedure Site(const ACommand: string);
  772. function Size(const AFileName: String): Int64;
  773. procedure Status(AStatusList: TStrings);
  774. procedure StructureMount(APath: String);
  775. procedure TransferMode(ATransferMode: TIdFTPTransferMode);
  776. procedure ReInitialize(ADelay: UInt32 = 10);
  777. procedure SetLang(const ALangTag : String);
  778. function CRC(const AFIleName : String; const AStartPoint : Int64 = 0; const AEndPoint : Int64=0) : Int64;
  779. //verify file was uploaded, this is more comprehensive than the above
  780. function VerifyFile(ALocalFile : TStream; const ARemoteFile : String;
  781. const AStartPoint : TIdStreamSize = 0; const AByteCount : TIdStreamSize = 0) : Boolean; overload;
  782. function VerifyFile(const ALocalFile, ARemoteFile : String;
  783. const AStartPoint : TIdStreamSize = 0; const AByteCount : TIdStreamSize = 0) : Boolean; overload;
  784. //file parts must be in order in TStrings parameter
  785. //GlobalScape FTP Pro uses this for multipart simultanious file uploading
  786. procedure CombineFiles(const ATargetFile : String; AFileParts : TStrings);
  787. //Set modified file time.
  788. procedure SetModTime(const AFileName: String; const ALocalTime: TDateTime);
  789. procedure SetModTimeGMT(const AFileName : String; const AGMTTime: TDateTime);
  790. // servers that support MDTM yyyymmddhhmmss[+-xxx] and also support LIST -T
  791. //This is true for servers that are known to support these even if they aren't
  792. //listed in the FEAT reply.
  793. function IsServerMDTZAndListTForm : Boolean;
  794. property IsCompressionSupported : Boolean read FIsCompressionSupported;
  795. //
  796. property SupportsVerification : Boolean read GetSupportsVerification;
  797. property CanResume: Boolean read ResumeSupported;
  798. property CanUseMLS : Boolean read FCanUseMLS;
  799. property DirectoryListing: TIdFTPListItems read GetDirectoryListing;
  800. property DirFormat : String read FDirFormat;
  801. property LangsSupported : TStrings read FLangsSupported;
  802. property ListParserClass : TIdFTPListParseClass read FListParserClass write FListParserClass;
  803. property LoginMsg: TIdReplyFTP read FLoginMsg;
  804. property ListResult: TStrings read FListResult;
  805. property SystemDesc: string read FSystemDesc;
  806. property TZInfo : TIdFTPTZInfo read FTZInfo write SetTZInfo;
  807. property UsingExtDataPort : Boolean read FUsingExtDataPort;
  808. property UsingNATFastTrack : Boolean read FUsingNATFastTrack;
  809. property UsingSFTP : Boolean read FUsingSFTP;
  810. property CurrentTransferMode : TIdFTPTransferMode read FCurrentTransferMode write TransferMode;
  811. property DefStringEncoding : IIdTextEncoding read FDefStringEncoding write SetDefStringEncoding;
  812. property ServerInfo : TIdFTPServerIdentifier read FServerInfo;
  813. published
  814. {$IFDEF DOTNET}
  815. {$IFDEF DOTNET_2_OR_ABOVE}
  816. property IPVersion default ID_DEFAULT_IP_VERSION;
  817. {$ENDIF}
  818. {$ELSE}
  819. property IPVersion default ID_DEFAULT_IP_VERSION;
  820. {$ENDIF}
  821. property AutoIssueFEAT : Boolean read FAutoIssueFEAT write FAutoIssueFEAT default DEF_Id_FTP_AutoIssueFEAT;
  822. property AutoLogin: Boolean read FAutoLogin write FAutoLogin default DEF_Id_FTP_AutoLogin;
  823. // This is an object that can compress and decompress FTP Deflate encoding
  824. property Compressor : TIdZLibCompressorBase read FCompressor write SetCompressor;
  825. property Host;
  826. property UseCCC : Boolean read FUseCCC write SetUseCCC default DEF_Id_FTP_UseCCC;
  827. property Passive: boolean read FPassive write SetPassive default Id_TIdFTP_Passive;
  828. property PassiveUseControlHost: Boolean read FPassiveUseControlHost write FPassiveUseControlHost default DEF_Id_FTP_PassiveUseControlHost;
  829. property DataPortProtection : TIdFTPDataPortSecurity read FDataPortProtection write SetDataPortProtection default Id_TIdFTP_DataPortProtection;
  830. property AUTHCmd : TAuthCmd read FAUTHCmd write SetAUTHCmd default DEF_Id_FTP_AUTH_CMD;
  831. property ConnectTimeout;
  832. property DataPort: TIdPort read FDataPort write FDataPort default 0;
  833. property DataPortMin: TIdPort read FDataPortMin write FDataPortMin default 0;
  834. property DataPortMax: TIdPort read FDataPortMax write FDataPortMax default 0;
  835. property ExternalIP : String read FExternalIP write FExternalIP;
  836. property Password;
  837. property TransferType: TIdFTPTransferType read FTransferType write SetTransferType default Id_TIdFTP_TransferType;
  838. property TransferTimeout: Integer read FTransferTimeout write FTransferTimeout default IdDefTimeout;
  839. property ListenTimeout : Integer read FListenTimeout write FListenTimeout default DEF_Id_FTP_ListenTimeout;
  840. property Username;
  841. property Port default IDPORT_FTP;
  842. property UseExtensionDataPort : Boolean read FUseExtensionDataPort write SetUseExtensionDataPort default DEF_Id_TIdFTP_UseExtendedData;
  843. property UseMLIS : Boolean read FUseMLIS write FUseMLIS default DEF_Id_TIdFTP_UseMIS;
  844. property TryNATFastTrack : Boolean read FTryNATFastTrack write SetTryNATFastTrack default Id_TIdFTP_UseNATFastTrack;
  845. property NATKeepAlive: TIdFTPKeepAlive read FNATKeepAlive write SetNATKeepAlive;
  846. property ProxySettings: TIdFtpProxySettings read FProxySettings write SetProxySettings;
  847. property Account: string read FAccount write FAccount;
  848. property ClientInfo : TIdFTPClientIdentifier read FClientInfo write SetClientInfo;
  849. property UseHOST: Boolean read FUseHOST write FUseHOST default DEF_Id_FTP_UseHOST;
  850. property ServerHOST: String read FServerHOST write FServerHOST;
  851. property UseTLS;
  852. property OnTLSNotAvailable;
  853. property OnBannerBeforeLogin : TIdFTPBannerEvent read FOnBannerBeforeLogin write FOnBannerBeforeLogin;
  854. property OnBannerAfterLogin : TIdFTPBannerEvent read FOnBannerAfterLogin write FOnBannerAfterLogin;
  855. property OnBannerWarning : TIdFTPBannerEvent read FOnBannerWarning write FOnBannerWarning;
  856. property OnBeforeGet: TNotifyEvent read FOnBeforeGet write FOnBeforeGet;
  857. property OnBeforePut: TIdFtpAfterGet read FOnBeforePut write FOnBeforePut;
  858. property OnAfterClientLogin: TOnAfterClientLogin read FOnAfterClientLogin write FOnAfterClientLogin;
  859. property OnCreateFTPList: TIdCreateFTPList read FOnCreateFTPList write FOnCreateFTPList;
  860. property OnAfterGet: TIdFtpAfterGet read FOnAfterGet write FOnAfterGet; //APR
  861. property OnAfterPut: TNotifyEvent read FOnAfterPut write FOnAfterPut;
  862. property OnNeedAccount: TIdNeedAccountEvent read FOnNeedAccount write FOnNeedAccount;
  863. property OnCustomFTPProxy : TNotifyEvent read FOnCustomFTPProxy write FOnCustomFTPProxy;
  864. property OnDataChannelCreate: TIdOnDataChannelCreate read FOnDataChannelCreate write FOnDataChannelCreate;
  865. property OnDataChannelDestroy: TIdOnDataChannelDestroy read FOnDataChannelDestroy write FOnDataChannelDestroy;
  866. //The directory was Retrieved from the FTP server.
  867. property OnRetrievedDir : TNotifyEvent read FOnRetrievedDir write FOnRetrievedDir;
  868. //parsing is done only when DirectoryLiusting is referenced
  869. property OnDirParseStart : TNotifyEvent read FOnDirParseStart write FOnDirParseStart;
  870. property OnDirParseEnd : TNotifyEvent read FOnDirParseEnd write FOnDirParseEnd;
  871. property ReadTimeout default DEF_Id_FTP_READTIMEOUT;
  872. end;
  873. EIdFTPException = class(EIdException);
  874. EIdFTPFileAlreadyExists = class(EIdFTPException);
  875. EIdFTPMustUseExtWithIPv6 = class(EIdFTPException);
  876. EIdFTPMustUseExtWithNATFastTrack = class(EIdFTPException);
  877. EIdFTPPassiveMustBeTrueWithNATFT = class(EIdFTPException);
  878. EIdFTPServerSentInvalidPort = class(EIdFTPException);
  879. EIdFTPSiteToSiteTransfer = class(EIdFTPException);
  880. EIdFTPSToSNATFastTrack = class(EIdFTPSiteToSiteTransfer);
  881. EIdFTPSToSNoDataProtection = class(EIdFTPSiteToSiteTransfer);
  882. EIdFTPSToSIPProtoMustBeSame = class(EIdFTPSiteToSiteTransfer);
  883. EIdFTPSToSBothMostSupportSSCN = class(EIdFTPSiteToSiteTransfer);
  884. EIdFTPSToSTransModesMustBeSame = class(EIdFTPSiteToSiteTransfer);
  885. EIdFTPOnCustomFTPProxyRequired = class(EIdFTPException);
  886. EIdFTPConnAssuranceFailure = class(EIdFTPException);
  887. EIdFTPWrongIOHandler = class(EIdFTPException);
  888. EIdFTPUploadFileNameCanNotBeEmpty = class(EIdFTPException);
  889. EIdFTPDataPortProtection = class(EIdFTPException);
  890. EIdFTPNoDataPortProtectionAfterCCC = class(EIdFTPDataPortProtection);
  891. EIdFTPNoDataPortProtectionWOEncryption = class(EIdFTPDataPortProtection);
  892. EIdFTPNoCCCWOEncryption = class(EIdFTPException);
  893. EIdFTPAUTHException = class(EIdFTPException);
  894. EIdFTPNoAUTHWOSSL = class(EIdFTPAUTHException);
  895. EIdFTPCanNotSetAUTHCon = class(EIdFTPAUTHException);
  896. EIdFTPMissingCompressor = class(EIdFTPException);
  897. EIdFTPCompressorNotReady = class(EIdFTPException);
  898. EIdFTPUnsupportedTransferMode = class(EIdFTPException);
  899. EIdFTPUnsupportedTransferType = class(EIdFTPException);
  900. implementation
  901. uses
  902. //facilitate inlining only.
  903. {$IFDEF KYLIXCOMPAT}
  904. Libc,
  905. {$ENDIF}
  906. {$IFDEF USE_VCL_POSIX}
  907. Posix.SysSelect,
  908. Posix.SysTime,
  909. Posix.Unistd,
  910. {$ENDIF}
  911. {$IFDEF DOTNET}
  912. {$IFDEF USE_INLINE}
  913. System.IO,
  914. System.Threading,
  915. {$ENDIF}
  916. {$ENDIF}
  917. IdComponent,
  918. IdFIPS,
  919. IdResourceStringsCore, IdIOHandlerStack, IdResourceStringsProtocols,
  920. IdSSL, IdGlobalProtocols, IdHash, IdHashCRC, IdHashSHA, IdHashMessageDigest,
  921. IdStack, IdStackConsts, IdSimpleServer, IdOTPCalculator, SysUtils;
  922. const
  923. cIPVersions: array[TIdIPVersion] of String = ('1', '2'); {do not localize}
  924. type
  925. TIdFTPListResult = class(TStringList)
  926. private
  927. FDetails: Boolean; //Did the developer use the NLST command for the last list command
  928. FUsedMLS : Boolean; //Did the developer use MLSx commands for the last list command
  929. public
  930. property Details: Boolean read FDetails;
  931. property UsedMLS: Boolean read FUsedMLS;
  932. end;
  933. {$IFDEF WORKAROUND_INLINE_CONSTRUCTORS}
  934. constructor TIdFTP.Create(AOwner: TComponent);
  935. begin
  936. inherited Create(AOwner);
  937. end;
  938. {$ENDIF}
  939. procedure TIdFTP.InitComponent;
  940. begin
  941. inherited InitComponent;
  942. //
  943. FIPVersion := ID_DEFAULT_IP_VERSION;
  944. //
  945. FAutoLogin := DEF_Id_FTP_AutoLogin;
  946. FRegularProtPort := IdPORT_FTP;
  947. FImplicitTLSProtPort := IdPORT_ftps;
  948. FExplicitTLSProtPort := IdPORT_FTP;
  949. //
  950. Port := IDPORT_FTP;
  951. Passive := Id_TIdFTP_Passive;
  952. FPassiveUseControlHost := DEF_Id_FTP_PassiveUseControlHost;
  953. FDataPortProtection := Id_TIdFTP_DataPortProtection;
  954. FUseCCC := DEF_Id_FTP_UseCCC;
  955. FAUTHCmd := DEF_Id_FTP_AUTH_CMD;
  956. FUseHOST := DEF_Id_FTP_UseHOST;
  957. FDataPort := 0;
  958. FDataPortMin := 0;
  959. FDataPortMax := 0;
  960. FDefStringEncoding := IndyTextEncoding_8Bit;
  961. FUseExtensionDataPort := DEF_Id_TIdFTP_UseExtendedData;
  962. FTryNATFastTrack := Id_TIdFTP_UseNATFastTrack;
  963. FTransferType := Id_TIdFTP_TransferType;
  964. FTransferTimeout := IdDefTimeout;
  965. FListenTimeout := DEF_Id_FTP_ListenTimeout;
  966. FLoginMsg := TIdReplyFTP.Create(nil);
  967. FListResult := TIdFTPListResult.Create;
  968. FLangsSupported := TStringList.Create;
  969. FCanResume := False;
  970. FResumeTested := False;
  971. FProxySettings:= TIdFtpProxySettings.Create; //APR
  972. FClientInfo := TIdFTPClientIdentifier.Create;
  973. FServerInfo := TIdFTPServerIdentifier.Create;
  974. FTZInfo := TIdFTPTZInfo.Create;
  975. FTZInfo.FGMTOffsetAvailable := False;
  976. FUseMLIS := DEF_Id_TIdFTP_UseMIS;
  977. FCanUseMLS := False; //initialize MLIS flags
  978. //Settings specified by
  979. // http://www.ietf.org/internet-drafts/draft-preston-ftpext-deflate-00.txt
  980. FZLibCompressionLevel := DEF_ZLIB_COMP_LEVEL;
  981. FZLibWindowBits := DEF_ZLIB_WINDOW_BITS; //-15 - no extra headers
  982. FZLibMemLevel := DEF_ZLIB_MEM_LEVEL;
  983. FZLibStratagy := DEF_ZLIB_STRATAGY; // - default
  984. //
  985. FAbortFlag := TIdThreadSafeBoolean.Create;
  986. FAbortFlag.Value := False;
  987. {
  988. Some firewalls don't handle control connections properly during long
  989. data transfers. They will timeout the control connection because it
  990. is idle and making it worse is that they will chop off a connection
  991. instead of closing it, causing TIdFTP to wait forever for nothing.
  992. }
  993. FNATKeepAlive := TIdFTPKeepAlive.Create;
  994. ReadTimeout := DEF_Id_FTP_READTIMEOUT;
  995. FAutoIssueFEAT := DEF_Id_FTP_AutoIssueFEAT;
  996. end;
  997. {$IFNDEF HAS_TryEncodeTime}
  998. // TODO: move this to IdGlobal or IdGlobalProtocols...
  999. function TryEncodeTime(Hour, Min, Sec, MSec: Word; out VTime: TDateTime): Boolean;
  1000. begin
  1001. try
  1002. VTime := EncodeTime(Hour, Min, Sec, MSec);
  1003. Result := True;
  1004. except
  1005. Result := False;
  1006. end;
  1007. end;
  1008. {$ENDIF}
  1009. {$IFNDEF HAS_TryStrToInt}
  1010. // TODO: use the implementation already in IdGlobalProtocols...
  1011. function TryStrToInt(const S: string; out Value: Integer): Boolean;
  1012. {$IFDEF USE_INLINE}inline;{$ENDIF}
  1013. var
  1014. E: Integer;
  1015. begin
  1016. Val(S, Value, E);
  1017. Result := E = 0;
  1018. end;
  1019. {$ENDIF}
  1020. procedure TIdFTP.Connect;
  1021. var
  1022. LHost: String;
  1023. LPort: TIdPort;
  1024. LBuf : String;
  1025. LSendQuitOnError: Boolean;
  1026. LOffs: Integer;
  1027. LRetryWithoutHOST: Boolean;
  1028. begin
  1029. LSendQuitOnError := False;
  1030. FCurrentTransferMode := dmStream;
  1031. FTZInfo.FGMTOffsetAvailable := False;
  1032. //FSSCNOn should be set to false to prevent problems.
  1033. FSSCNOn := False;
  1034. FUsingSFTP := False;
  1035. FUsingCCC := False;
  1036. FDataSettingsSent := False;
  1037. if FUseExtensionDataPort then begin
  1038. FUsingExtDataPort := True;
  1039. end;
  1040. FUsingNATFastTrack := False;
  1041. FCapabilities.Clear;
  1042. try
  1043. //APR 011216: proxy support
  1044. LHost := FHost;
  1045. LPort := FPort;
  1046. try
  1047. //I think fpcmTransparent means to connect to the regular host and the firewalll
  1048. //intercepts the login information.
  1049. if (ProxySettings.ProxyType <> fpcmNone) and (ProxySettings.ProxyType <> fpcmTransparent) and
  1050. (Length(ProxySettings.Host) > 0) then begin
  1051. FHost := ProxySettings.Host;
  1052. FPort := ProxySettings.Port;
  1053. end;
  1054. if FUseTLS = utUseImplicitTLS then begin
  1055. //at this point, we treat implicit FTP as if it were explicit FTP with TLS
  1056. FUsingSFTP := True;
  1057. end;
  1058. inherited Connect;
  1059. finally
  1060. FHost := LHost;
  1061. FPort := LPort;
  1062. end;
  1063. // RLebeau: must not send/receive UTF-8 before negotiating for it...
  1064. IOHandler.DefStringEncoding := FDefStringEncoding;
  1065. {$IFDEF STRING_IS_ANSI}
  1066. IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
  1067. {$ENDIF}
  1068. // RLebeau: RFC 959 says that the greeting can be preceeded by a 1xx
  1069. // reply and that the client should wait for the 220 reply when this
  1070. // happens. Also, the RFC says that 120 should be used, but some
  1071. // servers use other 1xx codes, such as 130, so handle 1xx generically
  1072. // calling GetInternalResponse() directly to avoid duplicate calls
  1073. // to CheckResponse() for the initial response if it is not 1xx
  1074. GetInternalResponse;
  1075. if (LastCmdResult.NumericCode div 100) = 1 then begin
  1076. DoOnBannerWarning(LastCmdResult.FormattedReply);
  1077. GetResponse(220);
  1078. end else begin
  1079. {$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(LastCmdResult.NumericCode, [220]);
  1080. end;
  1081. LSendQuitOnError := True;
  1082. FGreeting.Assign(LastCmdResult);
  1083. // Save initial greeting for server identification in case FGreeting changes
  1084. // in response to the HOST command
  1085. if FGreeting.Text.Count > 0 then begin
  1086. FServerDesc := FGreeting.Text[0];
  1087. end else begin
  1088. FServerDesc := '';
  1089. end;
  1090. // Implement HOST command as specified by
  1091. // http://tools.ietf.org/html/draft-hethmon-mcmurray-ftp-hosts-01
  1092. // Do not check the response for failures. The draft suggests allowing
  1093. // 220 (success) and 500/502 (unsupported), but vsftpd returns 530, and
  1094. // whatever ftp.microsoft.com is running returns 504.
  1095. if UseHOST then begin
  1096. // RLebeau: WS_FTP Server 5.x disconnects if the command fails,
  1097. // whereas WS_FTP Server 6+ does not. If the server disconnected
  1098. // here, let's mimic FTP Voyager by reconnecting without using
  1099. // the HOST command again...
  1100. //
  1101. // RLebeau 11/18/2013: some other servers also disconnect on a failed
  1102. // HOST command, so no longer retrying connect for WSFTP exclusively...
  1103. //
  1104. // RLebeau 11/22/2014: encountered one case where the server disconnects
  1105. // before the reply is received. So checking for that as well...
  1106. //
  1107. LRetryWithoutHOST := False;
  1108. try
  1109. if SendHost() <> 220 then begin
  1110. IOHandler.CheckForDisconnect(True, True);
  1111. end;
  1112. except
  1113. on E: EIdConnClosedGracefully do begin
  1114. LRetryWithoutHOST := True;
  1115. end;
  1116. on E: EIdSocketError do begin
  1117. if (E.LastError = Id_WSAECONNABORTED) or (E.LastError = Id_WSAECONNRESET) then begin
  1118. LRetryWithoutHOST := True;
  1119. end else begin
  1120. raise;
  1121. end;
  1122. end;
  1123. end;
  1124. if LRetryWithoutHOST then
  1125. begin
  1126. Disconnect(False);
  1127. if Assigned(IOHandler) then begin
  1128. IOHandler.InputBuffer.Clear;
  1129. end;
  1130. UseHOST := False;
  1131. try
  1132. Connect;
  1133. finally
  1134. UseHOST := True;
  1135. end;
  1136. Exit;
  1137. end;
  1138. end else begin
  1139. FGreeting.Assign(LastCmdResult);
  1140. end;
  1141. DoOnBannerBeforeLogin (FGreeting.FormattedReply);
  1142. // RLebeau: having an AutoIssueFeat property doesn't make sense to
  1143. // me. There are commands below that require FEAT's response, but
  1144. // if the user sets AutoIssueFeat to False, these commands will not
  1145. // be allowed to execute!
  1146. if AutoLogin then begin
  1147. Login;
  1148. DoAfterLogin;
  1149. //Fast track is set only one time per connection and no more, even
  1150. //with REINIT
  1151. if TryNATFastTrack then begin
  1152. DoTryNATFastTrack;
  1153. end;
  1154. if FUseTLS = utUseImplicitTLS then begin
  1155. //at this point, we treat implicit FTP as if it were explicit FTP with TLS
  1156. FUsingSFTP := True;
  1157. end;
  1158. // OpenVMS 7.1 replies with 200 instead of 215 - What does the RFC say about this?
  1159. // if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('SYST', [200, 215, 500]) = 500 then begin {do not localize}
  1160. //Do not fault if SYST was not understood by the server. Novel Netware FTP
  1161. //may not understand SYST.
  1162. if SendCmd('SYST') = 500 then begin {do not localize}
  1163. FSystemDesc := RSFTPUnknownHost;
  1164. end else begin
  1165. FSystemDesc := LastCmdResult.Text[0];
  1166. end;
  1167. if IsSiteZONESupported then begin
  1168. if SendCmd('SITE ZONE') = 210 then begin {do not localize}
  1169. if LastCmdResult.Text.Count > 0 then begin
  1170. LBuf := LastCmdResult.Text[0];
  1171. // some servers (Serv-U, etc) use a 'UTC' offset string, ie
  1172. // "UTC-300", specifying the number of minutes from UTC. Other
  1173. // servers (Apache) use a GMT offset string instead, ie "-0300".
  1174. if TextStartsWith(LBuf, 'UTC-') then begin {do not localize}
  1175. // Titan FTP 6.26.634 incorrectly returns UTC-2147483647 when it's
  1176. // first installed.
  1177. FTZInfo.FGMTOffsetAvailable :=
  1178. TryStrToInt(Copy(LBuf, 4, MaxInt), LOffs) and
  1179. TryEncodeTime(Abs(LOffs) div 60, Abs(LOffs) mod 60, 0, 0, FTZInfo.FGMTOffset);
  1180. if FTZInfo.FGMTOffsetAvailable and (LOffs < 0) then
  1181. FTZInfo.FGMTOffset := -FTZInfo.FGMTOffset
  1182. end else begin
  1183. FTZInfo.FGMTOffsetAvailable := True;
  1184. FTZInfo.GMTOffset := GmtOffsetStrToDateTime(LBuf);
  1185. end;
  1186. end;
  1187. end;
  1188. end;
  1189. SendTransferType(FTransferType);
  1190. {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpReady, [RSFTPStatusReady]);
  1191. end else begin
  1192. // OpenVMS 7.1 replies with 200 instead of 215 - What does the RFC say about this?
  1193. // if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('SYST', [200, 215, 500]) = 500 then begin {do not localize}
  1194. //Do not fault if SYST was not understood by the server. Novel Netware FTP
  1195. //may not understand SYST.
  1196. if SendCmd('SYST') = 500 then begin {do not localize}
  1197. FSystemDesc := RSFTPUnknownHost;
  1198. end else begin
  1199. FSystemDesc := LastCmdResult.Text[0];
  1200. end;
  1201. if FAutoIssueFEAT then begin
  1202. IssueFEAT;
  1203. end;
  1204. end;
  1205. except
  1206. Disconnect(LSendQuitOnError); // RLebeau: do not send the QUIT command if the greeting was not received
  1207. raise;
  1208. end;
  1209. end;
  1210. function TIdFTP.SendHost: Int16;
  1211. var
  1212. LHost: String;
  1213. begin
  1214. LHost := FServerHOST;
  1215. if LHost = '' then begin
  1216. LHost := FHost;
  1217. end;
  1218. if Socket <> nil then begin
  1219. if (IPVersion = Id_IPv6) and (MakeCanonicalIPv6Address(LHost) <> '') then begin
  1220. LHost := '[' + LHost + ']'; {do not localize}
  1221. end;
  1222. end;
  1223. Result := SendCmd('HOST ' + LHost); {do not localize}
  1224. end;
  1225. procedure TIdFTP.SetTransferType(AValue: TIdFTPTransferType);
  1226. begin
  1227. if AValue <> FTransferType then begin
  1228. if not Assigned(FDataChannel) then begin
  1229. if Connected then begin
  1230. SendTransferType(AValue);
  1231. end;
  1232. FTransferType := AValue;
  1233. end;
  1234. end;
  1235. end;
  1236. procedure TIdFTP.SendTransferType(AValue: TIdFTPTransferType);
  1237. var
  1238. s: string;
  1239. begin
  1240. s := '';
  1241. case AValue of
  1242. ftAscii: s := 'A'; {do not localize}
  1243. ftBinary: s := 'I'; {do not localize}
  1244. else
  1245. raise EIdFTPUnsupportedTransferType.Create(RSFTPUnsupportedTransferType);
  1246. end;
  1247. SendCmd('TYPE ' + s, 200); {do not localize}
  1248. end;
  1249. function TIdFTP.ResumeSupported: Boolean;
  1250. begin
  1251. if not FResumeTested then begin
  1252. FResumeTested := True;
  1253. FCanResume := Quote('REST 1') = 350; {do not localize}
  1254. Quote('REST 0'); {do not localize}
  1255. end;
  1256. Result := FCanResume;
  1257. end;
  1258. procedure TIdFTP.Get(const ASourceFile: string; ADest: TStream; AResume: Boolean = False);
  1259. begin
  1260. //for SSL FXP, we have to do it here because InternalGet is used by the LIST command
  1261. //where SSCN is ignored.
  1262. ClearSSCN;
  1263. AResume := AResume and CanResume;
  1264. DoBeforeGet;
  1265. // RLebeau 7/26/06: do not do this! It breaks the ability to resume files
  1266. // ADest.Position := 0;
  1267. InternalGet('RETR ' + ASourceFile, ADest, AResume);
  1268. DoAfterGet(ADest);
  1269. end;
  1270. procedure TIdFTP.Get(const ASourceFile, ADestFile: string; const ACanOverwrite: Boolean = False;
  1271. AResume: Boolean = False);
  1272. var
  1273. LDestStream: TStream;
  1274. begin
  1275. AResume := AResume and CanResume;
  1276. if ACanOverwrite and (not AResume) then begin
  1277. SysUtils.DeleteFile(ADestFile);
  1278. LDestStream := TIdFileCreateStream.Create(ADestFile);
  1279. end
  1280. else if (not ACanOverwrite) and AResume then begin
  1281. LDestStream := TIdAppendFileStream.Create(ADestFile);
  1282. end
  1283. else if not FileExists(ADestFile) then begin
  1284. LDestStream := TIdFileCreateStream.Create(ADestFile);
  1285. end
  1286. else begin
  1287. raise EIdFTPFileAlreadyExists.Create(RSDestinationFileAlreadyExists);
  1288. end;
  1289. try
  1290. Get(ASourceFile, LDestStream, AResume);
  1291. finally
  1292. FreeAndNil(LDestStream);
  1293. end;
  1294. end;
  1295. procedure TIdFTP.DoBeforeGet;
  1296. begin
  1297. if Assigned(FOnBeforeGet) then begin
  1298. FOnBeforeGet(Self);
  1299. end;
  1300. end;
  1301. procedure TIdFTP.DoBeforePut(AStream: TStream);
  1302. begin
  1303. if Assigned(FOnBeforePut) then begin
  1304. FOnBeforePut(Self, AStream);
  1305. end;
  1306. end;
  1307. procedure TIdFTP.DoAfterGet(AStream: TStream);//APR
  1308. begin
  1309. if Assigned(FOnAfterGet) then begin
  1310. FOnAfterGet(Self, AStream);
  1311. end;
  1312. end;
  1313. procedure TIdFTP.DoAfterPut;
  1314. begin
  1315. if Assigned(FOnAfterPut) then begin
  1316. FOnAfterPut(Self);
  1317. end;
  1318. end;
  1319. procedure TIdFTP.ConstructDirListing;
  1320. begin
  1321. if not Assigned(FDirectoryListing) then begin
  1322. if not IsDesignTime then begin
  1323. DoFTPList;
  1324. end;
  1325. if not Assigned(FDirectoryListing) then begin
  1326. FDirectoryListing := TIdFTPListItems.Create;
  1327. end;
  1328. end else begin
  1329. FDirectoryListing.Clear;
  1330. end;
  1331. end;
  1332. procedure TIdFTP.List(ADest: TStrings; const ASpecifier: string = ''; ADetails: Boolean = True); {do not localize}
  1333. var
  1334. LDest: TMemoryStream;
  1335. LTrans : TIdFTPTransferType;
  1336. begin
  1337. if ADetails and UseMLIS and FCanUseMLS then begin
  1338. ExtListDir(ADest, ASpecifier);
  1339. Exit;
  1340. end;
  1341. // Note that for LIST, it might be best to put the connection in ASCII mode
  1342. // because some old servers such as TOPS20 might require this. We restore
  1343. // it if the original mode was not ASCII. It's a good idea to do this
  1344. // anyway because some clients still do this such as WS_FTP Pro and
  1345. // Microsoft's FTP Client.
  1346. LTrans := TransferType;
  1347. if LTrans <> ftASCII then begin
  1348. Self.TransferType := ftASCII;
  1349. end;
  1350. try
  1351. LDest := TMemoryStream.Create;
  1352. try
  1353. InternalGet(TrimRight(iif(ADetails, 'LIST', 'NLST') + ' ' + ASpecifier), LDest); {do not localize}
  1354. FreeAndNil(FDirectoryListing);
  1355. FDirFormat := '';
  1356. LDest.Position := 0;
  1357. FListResult.Text := ReadStringFromStream(LDest, -1, IOHandler.DefStringEncoding{$IFDEF STRING_IS_ANSI}, IOHandler.DefAnsiEncoding{$ENDIF});
  1358. TIdFTPListResult(FListResult).FDetails := ADetails;
  1359. TIdFTPListResult(FListResult).FUsedMLS := False;
  1360. // FDirFormat will be updated in ParseFTPList...
  1361. finally
  1362. FreeAndNil(LDest);
  1363. end;
  1364. if ADest <> nil then begin
  1365. ADest.Assign(FListResult);
  1366. end;
  1367. DoOnRetrievedDir;
  1368. finally
  1369. if LTrans <> ftASCII then begin
  1370. TransferType := LTrans;
  1371. end;
  1372. end;
  1373. end;
  1374. const
  1375. AbortedReplies : array [0..5] of Int16 =
  1376. (226,426, 450,451,425,550);
  1377. //226 was added because one server will return that twice if you aborted
  1378. //during an upload.
  1379. AcceptableAbortReplies : array [0..8] of Int16 =
  1380. (225, 226, 250, 426, 450,451,425,550,552);
  1381. //GlobalScape Secure FTP Server returns a 552 for an aborted file
  1382. procedure TIdFTP.FinalizeDataOperation;
  1383. var
  1384. LResponse : Int16;
  1385. begin
  1386. DoOnDataChannelDestroy;
  1387. if FDataChannel <> nil then begin
  1388. FDataChannel.IOHandler := nil;
  1389. FreeAndNil(FDataChannel);
  1390. end;
  1391. {
  1392. This is a bug fix for servers will do something like this:
  1393. [2] Mon 06Jun05 13:33:28 - (000007) PASV
  1394. [6] Mon 06Jun05 13:33:28 - (000007) 227 Entering Passive Mode (192,168,1,107,4,22)
  1395. [2] Mon 06Jun05 13:33:28 - (000007) RETR test.txt.txt
  1396. [6] Mon 06Jun05 13:33:28 - (000007) 550 /test.txt.txt: No such file or directory.
  1397. [2] Mon 06Jun05 13:34:28 - (000007) QUIT
  1398. [6] Mon 06Jun05 13:34:28 - (000007) 221 Goodbye!
  1399. [5] Mon 06Jun05 13:34:28 - (000007) Closing connection for user TEST (00:01:08 connected)
  1400. }
  1401. if (LastCmdResult.NumericCode div 100) > 2 then
  1402. begin
  1403. {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpAborted, [RSFTPStatusAbortTransfer]);
  1404. Exit;
  1405. end;
  1406. {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpReady, [RSFTPStatusDoneTransfer]);
  1407. // 226 = download successful, 225 = Abort successful}
  1408. if FAbortFlag.Value then begin
  1409. LResponse := {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}(AcceptableAbortReplies);
  1410. //Experimental -
  1411. if PosInSmallIntArray(LResponse,AbortedReplies) > -1 then begin
  1412. {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([226, 225]);
  1413. end;
  1414. //IMPORTANT!!! KEEP THIS COMMENT!!!
  1415. //
  1416. //This is a workaround for a problem. When uploading a file on
  1417. //one FTP server and aborting that upload, I got this:
  1418. //
  1419. //Sent 3/9/2005 10:34:58 AM: STOR --------
  1420. //Recv 3/9/2005 10:34:58 AM: 150 Opening BINARY mode data connection for [3513]Red_Glas.zip
  1421. //Sent 3/9/2005 10:34:59 AM: ABOR
  1422. //Recv 3/9/2005 10:35:00 AM: 226 Transfer complete.
  1423. //Recv 3/9/2005 10:35:00 AM: 226 Abort successful
  1424. //
  1425. //but at ftp.ipswitch.com (a WS_FTP Server 5.0.4 (2555009845) server ),
  1426. //I was getting this when aborting a download
  1427. //
  1428. //Sent 3/9/2005 12:43:41 AM: RETR imail6.pdf
  1429. //Recv 3/9/2005 12:43:41 AM: 150 Opening BINARY data connection for imail6.pdf (2150082 bytes)
  1430. //Sent 3/9/2005 12:43:42 AM: ABOR
  1431. //Recv 3/9/2005 12:43:42 AM: 226 abort successful
  1432. //Recv 3/9/2005 12:43:43 AM: 425 transfer canceled
  1433. //
  1434. if LResponse = 226 then begin
  1435. if IOHandler.Readable(10) then begin
  1436. {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}(AbortedReplies);
  1437. end;
  1438. end;
  1439. {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpAborted, [RSFTPStatusAbortTransfer]);
  1440. //end experimental section
  1441. end else begin
  1442. //ftp.marist.edu returns 250
  1443. {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([226, 225, 250]);
  1444. end;
  1445. end;
  1446. procedure TIdFTP.InternalPut(const ACommand: string; ASource: TStream;
  1447. AFromBeginning: Boolean = True; AResume: Boolean = False);
  1448. {$IFNDEF MSWINDOWS}
  1449. procedure WriteStreamFromBeginning;
  1450. var
  1451. LBuffer: TIdBytes;
  1452. LBufSize: Integer;
  1453. begin
  1454. // Copy entire stream without relying on ASource.Size so Unix-to-DOS
  1455. // conversion can be done on the fly.
  1456. BeginWork(wmWrite, ASource.Size);
  1457. try
  1458. SetLength(LBuffer, FDataChannel.IOHandler.SendBufferSize);
  1459. while True do begin
  1460. LBufSize := ASource.Read(LBuffer[0], Length(LBuffer));
  1461. if LBufSize > 0 then
  1462. FDataChannel.IOHandler.Write(LBuffer, LBufSize)
  1463. else
  1464. Break;
  1465. end;
  1466. finally
  1467. EndWork(wmWrite);
  1468. end;
  1469. end;
  1470. {$ENDIF}
  1471. var
  1472. LIP: string;
  1473. LPort: TIdPort;
  1474. LPasvCl : TIdTCPClient;
  1475. LPortSv : TIdSimpleServer;
  1476. LSocketList, LReadList: TIdSocketList;
  1477. LDataSocket: TIdStackSocketHandle;
  1478. // under ARC, convert a weak reference to a strong reference before working with it
  1479. LCompressor : TIdZLibCompressorBase;
  1480. begin
  1481. FAbortFlag.Value := False;
  1482. LCompressor := nil;
  1483. if FCurrentTransferMode = dmDeflate then begin
  1484. LCompressor := FCompressor;
  1485. if not Assigned(LCompressor) then begin
  1486. raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor);
  1487. end;
  1488. if not LCompressor.IsReady then begin
  1489. raise EIdFTPCompressorNotReady.Create(RSFTPCompressorNotReady);
  1490. end;
  1491. end;
  1492. //for SSL FXP, we have to do it here because there is no command were a client
  1493. //submits data through a data port where the SSCN setting is ignored.
  1494. ClearSSCN;
  1495. {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpTransfer, [RSFTPStatusStartTransfer]);
  1496. // try
  1497. if FPassive then begin
  1498. SendPret(ACommand);
  1499. if FUsingExtDataPort then begin
  1500. SendEPassive(LIP, LPort);
  1501. end else begin
  1502. SendPassive(LIP, LPort);
  1503. end;
  1504. // TODO: InternalGet() does not send these commands until after the data channel
  1505. // is established, should we be doing the same here?
  1506. if AResume then begin
  1507. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ASource.Position), [350]); {do not localize}
  1508. end;
  1509. IOHandler.WriteLn(ACommand);
  1510. //
  1511. if Socket <> nil then begin
  1512. FDataChannel := TIdTCPClient.Create(nil);
  1513. end else begin
  1514. FDataChannel := nil;
  1515. end;
  1516. LPasvCl := TIdTCPClient(FDataChannel);
  1517. try
  1518. InitDataChannel;
  1519. if (Self.Socket <> nil) and PassiveUseControlHost then begin
  1520. //Do not use an assignment from Self.Host
  1521. //because a DNS name may not resolve to the same
  1522. //IP address every time. This is the case where
  1523. //the workload is distributed around several servers.
  1524. //Besides, we already know the Peer's IP address so
  1525. //why waste time querying it.
  1526. LIP := Self.Socket.Binding.PeerIP;
  1527. end;
  1528. if LPasvCl <> nil then begin
  1529. LPasvCl.Host := LIP;
  1530. LPasvCl.Port := LPort;
  1531. DoOnDataChannelCreate;
  1532. // TODO: if Connect() fails and PassiveUseControlHost is false, try connecting to the command host...
  1533. LPasvCl.Connect;
  1534. end;
  1535. try
  1536. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([110, 125, 150]);
  1537. try
  1538. if FDataChannel <> nil then begin
  1539. if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin
  1540. TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False;
  1541. end;
  1542. if Assigned(LCompressor) then begin
  1543. LCompressor.CompressFTPToIO(ASource, FDataChannel.IOHandler,
  1544. FZLibCompressionLevel, FZLibWindowBits, FZLibMemLevel, FZLibStratagy);
  1545. end else begin
  1546. if AFromBeginning then begin
  1547. {$IFNDEF MSWINDOWS}
  1548. WriteStreamFromBeginning;
  1549. {$ELSE}
  1550. FDataChannel.IOHandler.Write(ASource, 0, False); // from beginning
  1551. {$ENDIF}
  1552. end else begin
  1553. FDataChannel.IOHandler.Write(ASource, -1, False); // from current position
  1554. end;
  1555. end;
  1556. end;
  1557. except
  1558. on E: EIdSocketError do
  1559. begin
  1560. // If 10038 - abort was called. Server will return 225
  1561. if E.LastError <> 10038 then begin
  1562. raise;
  1563. end;
  1564. end;
  1565. end;
  1566. finally
  1567. if LPasvCl <> nil then begin
  1568. LPasvCl.Disconnect(False);
  1569. end;
  1570. end;
  1571. finally
  1572. FinalizeDataOperation;
  1573. end;
  1574. end else begin
  1575. if Socket <> nil then begin
  1576. FDataChannel := TIdSimpleServer.Create(nil);
  1577. end else begin
  1578. FDataChannel := nil;
  1579. end;
  1580. LPortSv := TIdSimpleServer(FDataChannel);
  1581. try
  1582. InitDataChannel;
  1583. if LPortSv <> nil then begin
  1584. LPortSv.BoundIP := Self.Socket.Binding.IP;
  1585. LPortSv.BoundPort := FDataPort;
  1586. LPortSv.BoundPortMin := FDataPortMin;
  1587. LPortSv.BoundPortMax := FDataPortMax;
  1588. DoOnDataChannelCreate;
  1589. LPortSv.BeginListen;
  1590. if FUsingExtDataPort then begin
  1591. SendEPort(LPortSv.Binding);
  1592. end else begin
  1593. SendPort(LPortSv.Binding);
  1594. end;
  1595. if AResume then begin
  1596. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ASource.Position), [350]); {do not localize}
  1597. end;
  1598. // RLebeau 5/15/2020: there are some FTP servers (vsFTPd, etc) that will try to
  1599. // establish the transfer connection as soon as they receive the STOR/STOU/APPE
  1600. // command and before sending a response, thus causing SendCmd() to hang and the
  1601. // connection to fail. Per RFC 959 Section 3.2:
  1602. //
  1603. // "The passive data transfer process (this may be a user-DTP or a second server-DTP)
  1604. // shall "listen" on the data port prior to sending a transfer request command. The
  1605. // FTP request command determines the direction of the data transfer. The server,
  1606. // upon receiving the transfer request, will initiate the data connection to the port.
  1607. // When the connection is established, the data transfer begins between DTP's, and the
  1608. // server-PI sends a confirming reply to the user-PI."
  1609. //
  1610. // So, since we have now seen cases where a server sends a reply first and then opens
  1611. // the connection, and cases where a server opens the connection first and then sends
  1612. // a reply, we need to monitor both ports simultaneously and act accordingly...
  1613. //Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(ACommand, [125, 150]);
  1614. LSocketList := TIdSocketList.CreateSocketList;
  1615. try
  1616. LDataSocket := LPortSv.Binding.Handle;
  1617. LSocketList.Add(Socket.Binding.Handle);
  1618. LSocketList.Add(LDataSocket);
  1619. IOHandler.WriteLn(ACommand);
  1620. LReadList := nil;
  1621. if not LSocketList.SelectReadList(LReadList, ListenTimeout) then begin
  1622. raise EIdAcceptTimeout.Create(RSAcceptTimeout);
  1623. end;
  1624. try
  1625. if LReadList.ContainsSocket(LDataSocket) then
  1626. begin
  1627. LPortSv.Listen(0);
  1628. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([125, 150]);
  1629. end else
  1630. begin
  1631. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([125, 150]);
  1632. LPortSv.Listen(ListenTimeout); // TODO: minus elapsed time already used by SelectReadList()
  1633. end;
  1634. finally
  1635. LReadList.Free;
  1636. end;
  1637. finally
  1638. LSocketList.Free;
  1639. end;
  1640. if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin
  1641. TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False;
  1642. end;
  1643. if Assigned(LCompressor) then begin
  1644. LCompressor.CompressFTPToIO(ASource, FDataChannel.IOHandler,
  1645. FZLibCompressionLevel, FZLibWindowBits, FZLibMemLevel, FZLibStratagy);
  1646. end
  1647. else if AFromBeginning then begin
  1648. {$IFNDEF MSWINDOWS}
  1649. WriteStreamFromBeginning;
  1650. {$ELSE}
  1651. FDataChannel.IOHandler.Write(ASource, 0, False); // from beginning
  1652. {$ENDIF}
  1653. end else begin
  1654. FDataChannel.IOHandler.Write(ASource, -1, False); // from current position
  1655. end;
  1656. end else
  1657. begin
  1658. // TODO:
  1659. {
  1660. if FUsingExtDataPort then begin
  1661. SendEPort(?);
  1662. end else begin
  1663. SendPort(?);
  1664. end;
  1665. }
  1666. if AResume then begin
  1667. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ASource.Position), [350]); {do not localize}
  1668. end;
  1669. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(ACommand, [125, 150]);
  1670. end;
  1671. finally
  1672. FinalizeDataOperation;
  1673. end;
  1674. end;
  1675. { This will silently ignore the STOR request if the server has forcibly disconnected
  1676. (kicked or timed out) before the request starts
  1677. except
  1678. //Note that you are likely to get an exception you abort a transfer
  1679. //hopefully, this will make things work better.
  1680. on E: EIdConnClosedGracefully do begin
  1681. end;
  1682. end;}
  1683. { commented out because we might need to revert back to this
  1684. if new code fails.
  1685. if (LResponse = 426) or (LResponse = 450) then
  1686. begin
  1687. // some servers respond with 226 on ABOR
  1688. ($IFDEF OVERLOADED_OPENARRAY_BUG)GetResponseArr($ELSE)GetResponse($ENDIF)([226, 225]);
  1689. ($IFDEF OVERLOADED_OPENARRAY_BUG)DoStatusArr($ELSE)DoStatus($ENDIF)(ftpAborted, [RSFTPStatusAbortTransfer]);
  1690. end;
  1691. }
  1692. end;
  1693. procedure TIdFTP.InternalGet(const ACommand: string; ADest: TStream; AResume: Boolean = false);
  1694. var
  1695. LIP: string;
  1696. LPort: TIdPort;
  1697. LPasvCl : TIdTCPClient;
  1698. LPortSv : TIdSimpleServer;
  1699. LSocketList, LReadList: TIdSocketList;
  1700. LDataSocket: TIdStackSocketHandle;
  1701. // under ARC, convert a weak reference to a strong reference before working with it
  1702. LCompressor: TIdZLibCompressorBase;
  1703. begin
  1704. FAbortFlag.Value := False;
  1705. LCompressor := nil;
  1706. if FCurrentTransferMode = dmDeflate then begin
  1707. LCompressor := FCompressor;
  1708. if not Assigned(LCompressor) then begin
  1709. raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor);
  1710. end;
  1711. if not LCompressor.IsReady then begin
  1712. raise EIdFTPCompressorNotReady.Create(RSFTPCompressorNotReady);
  1713. end;
  1714. end;
  1715. {$IFDEF OVERLOADED_OPENARRAY_BUG}DoStatusArr{$ELSE}DoStatus{$ENDIF}(ftpTransfer, [RSFTPStatusStartTransfer]);
  1716. if FPassive then begin
  1717. SendPret(ACommand);
  1718. //PASV or EPSV
  1719. if FUsingExtDataPort then begin
  1720. SendEPassive(LIP, LPort);
  1721. end else begin
  1722. SendPassive(LIP, LPort);
  1723. end;
  1724. if Socket <> nil then begin
  1725. FDataChannel := TIdTCPClient.Create(nil);
  1726. end else begin
  1727. FDataChannel := nil;
  1728. end;
  1729. LPasvCl := TIdTCPClient(FDataChannel);
  1730. try
  1731. InitDataChannel;
  1732. if (Self.Socket <> nil) and PassiveUseControlHost then begin
  1733. //Do not use an assignment from Self.Host
  1734. //because a DNS name may not resolve to the same
  1735. //IP address every time. This is the case where
  1736. //the workload is distributed around several servers.
  1737. //Besides, we already know the Peer's IP address so
  1738. //why waste time querying it.
  1739. LIP := Self.Socket.Binding.PeerIP;
  1740. end;
  1741. if LPasvCl <> nil then begin
  1742. LPasvCl.Host := LIP;
  1743. LPasvCl.Port := LPort;
  1744. DoOnDataChannelCreate;
  1745. // TODO: if Connect() fails and PassiveUseControlHost is false, try connecting to the command host...
  1746. LPasvCl.Connect;
  1747. end;
  1748. try
  1749. if AResume then begin
  1750. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ADest.Position), [350]); {do not localize}
  1751. end;
  1752. // APR: Ericsson Switch FTP
  1753. //
  1754. // RLebeau: some servers send 450 when no files are
  1755. // present, so do not read the stream in that case
  1756. if Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(ACommand, [125, 150, 154, 450]) <> 450 then
  1757. begin
  1758. if LPasvCl <> nil then begin
  1759. if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin
  1760. TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False;
  1761. end;
  1762. if Assigned(LCompressor) then begin
  1763. LCompressor.DecompressFTPFromIO(LPasvCl.IOHandler, ADest, FZLibWindowBits);
  1764. end else begin
  1765. LPasvCl.IOHandler.ReadStream(ADest, -1, True);
  1766. end;
  1767. end;
  1768. end;
  1769. finally
  1770. if LPasvCl <> nil then begin
  1771. LPasvCl.Disconnect(False);
  1772. end;
  1773. end;
  1774. finally
  1775. FinalizeDataOperation;
  1776. end;
  1777. end else begin
  1778. // PORT or EPRT
  1779. if Socket <> nil then begin
  1780. FDataChannel := TIdSimpleServer.Create(nil);
  1781. end else begin
  1782. FDataChannel := nil;
  1783. end;
  1784. LPortSv := TIdSimpleServer(FDataChannel);
  1785. try
  1786. InitDataChannel;
  1787. if LPortSv <> nil then begin
  1788. LPortSv.BoundIP := Self.Socket.Binding.IP;
  1789. LPortSv.BoundPort := FDataPort;
  1790. LPortSv.BoundPortMin := FDataPortMin;
  1791. LPortSv.BoundPortMax := FDataPortMax;
  1792. DoOnDataChannelCreate;
  1793. LPortSv.BeginListen;
  1794. if FUsingExtDataPort then begin
  1795. SendEPort(LPortSv.Binding);
  1796. end else begin
  1797. SendPort(LPortSv.Binding);
  1798. end;
  1799. if AResume then begin
  1800. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ADest.Position), [350]); {do not localize}
  1801. end;
  1802. // RLebeau 5/15/2020: there are some FTP servers (vsFTPd, etc) that will try to
  1803. // establish the transfer connection as soon as they receive the STOR/STOU/APPE
  1804. // command and before sending a response, thus causing SendCmd() to hang and the
  1805. // connection to fail. Per RFC 959 Section 3.2:
  1806. //
  1807. // "The passive data transfer process (this may be a user-DTP or a second server-DTP)
  1808. // shall "listen" on the data port prior to sending a transfer request command. The
  1809. // FTP request command determines the direction of the data transfer. The server,
  1810. // upon receiving the transfer request, will initiate the data connection to the port.
  1811. // When the connection is established, the data transfer begins between DTP's, and the
  1812. // server-PI sends a confirming reply to the user-PI."
  1813. //
  1814. // So, since we have now seen cases where a server sends a reply first and then opens
  1815. // the connection, and cases where a server opens the connection first and then sends
  1816. // a reply, we need to monitor both ports simultaneously and act accordingly...
  1817. //SendCmd(ACommand, [125, 150, 154]); //APR: Ericsson Switch FTP);
  1818. LSocketList := TIdSocketList.CreateSocketList;
  1819. try
  1820. LDataSocket := LPortSv.Binding.Handle;
  1821. LSocketList.Add(Socket.Binding.Handle);
  1822. LSocketList.Add(LDataSocket);
  1823. IOHandler.WriteLn(ACommand);
  1824. LReadList := nil;
  1825. if not LSocketList.SelectReadList(LReadList, ListenTimeout) then begin
  1826. raise EIdAcceptTimeout.Create(RSAcceptTimeout);
  1827. end;
  1828. try
  1829. if LReadList.ContainsSocket(LDataSocket) then
  1830. begin
  1831. LPortSv.Listen(0);
  1832. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([125, 150, 154]);
  1833. end else
  1834. begin
  1835. Self.{$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([125, 150, 154]);
  1836. LPortSv.Listen(ListenTimeout); // TODO: minus elapsed time already used by SelectReadList()
  1837. end;
  1838. finally
  1839. LReadList.Free;
  1840. end;
  1841. finally
  1842. LSocketList.Free;
  1843. end;
  1844. if FUsingSFTP and (FDataPortProtection = ftpdpsPrivate) then begin
  1845. TIdSSLIOHandlerSocketBase(FDataChannel.IOHandler).PassThrough := False;
  1846. end;
  1847. if Assigned(LCompressor) then begin
  1848. LCompressor.DecompressFTPFromIO(LPortSv.IOHandler, ADest, FZLibWindowBits);
  1849. end else begin
  1850. FDataChannel.IOHandler.ReadStream(ADest, -1, True);
  1851. end;
  1852. end else
  1853. begin
  1854. // TODO:
  1855. {
  1856. if FUsingExtDataPort then begin
  1857. SendEPort(?);
  1858. end else begin
  1859. SendPort(?);
  1860. end;
  1861. }
  1862. if AResume then begin
  1863. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REST ' + IntToStr(ADest.Position), [350]); {do not localize}
  1864. end;
  1865. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(ACommand, [125, 150, 154]); //APR: Ericsson Switch FTP);
  1866. end;
  1867. finally
  1868. FinalizeDataOperation;
  1869. end;
  1870. end;
  1871. // ToDo: Change that to properly handle response code (not just success or except)
  1872. // 226 = download successful, 225 = Abort successful}
  1873. //commented out in case we need to revert back to this.
  1874. { LResponse := ($IFDEF OVERLOADED_OPENARRAY_BUG)GetResponseArr($ELSE)GetResponse($ENDIF)([225, 226, 250, 426, 450]);
  1875. if (LResponse = 426) or (LResponse = 450) then begin
  1876. ($IFDEF OVERLOADED_OPENARRAY_BUG)GetResponseArr($ELSE)GetResponse($ENDIF)([226, 225]);
  1877. ($IFDEF OVERLOADED_OPENARRAY_BUG)DoStatusArr($ELSE)DoStatus($ENDIF)(ftpAborted, [RSFTPStatusAbortTransfer]);
  1878. end; }
  1879. end;
  1880. procedure TIdFTP.DoOnDataChannelCreate;
  1881. begin
  1882. // While the Control Channel is idle, Enable/disable TCP/IP keepalives.
  1883. // They're very small (40-byte) packages and will be sent every
  1884. // NATKeepAlive.IntervalMS after the connection has been idle for
  1885. // NATKeepAlive.IdleTimeMS. Prior to Windows 2000, the idle and
  1886. // timeout values are system wide and have to be set in the registry;
  1887. // the default is idle = 2 hours, interval = 1 second.
  1888. if (Socket <> nil) and NATKeepAlive.UseKeepAlive then begin
  1889. Socket.Binding.SetKeepAliveValues(True, NATKeepAlive.IdleTimeMS, NATKeepAlive.IntervalMS);
  1890. end;
  1891. if Assigned(FOnDataChannelCreate) then begin
  1892. OnDataChannelCreate(Self, FDataChannel);
  1893. end;
  1894. end;
  1895. procedure TIdFTP.DoOnDataChannelDestroy;
  1896. begin
  1897. if Assigned(FOnDataChannelDestroy) then begin
  1898. OnDataChannelDestroy(Self, FDataChannel);
  1899. end;
  1900. if (Socket <> nil) and NATKeepAlive.UseKeepAlive then begin
  1901. Socket.Binding.SetKeepAliveValues(False, 0, 0);
  1902. end;
  1903. end;
  1904. procedure TIdFTP.SetNATKeepAlive(AValue: TIdFTPKeepAlive);
  1905. begin
  1906. FNATKeepAlive.Assign(AValue);
  1907. end;
  1908. { TIdFtpKeepAlive }
  1909. procedure TIdFtpKeepAlive.Assign(Source: TPersistent);
  1910. var
  1911. LSource: TIdFTPKeepAlive;
  1912. begin
  1913. if Source is TIdFTPKeepAlive then begin
  1914. LSource := TIdFTPKeepAlive(Source);
  1915. FUseKeepAlive := LSource.UseKeepAlive;
  1916. FIdleTimeMS := LSource.IdleTimeMS;
  1917. FIntervalMS := LSource.IntervalMS;
  1918. end else begin
  1919. inherited Assign(Source);
  1920. end;
  1921. end;
  1922. procedure TIdFTP.DisconnectNotifyPeer;
  1923. begin
  1924. inherited DisconnectNotifyPeer;
  1925. IOHandler.WriteLn('QUIT'); {do not localize}
  1926. IOHandler.CheckForDataOnSource(100);
  1927. if not IOHandler.InputBufferIsEmpty then begin
  1928. GetInternalResponse;
  1929. end;
  1930. end;
  1931. {$I IdDeprecatedImplBugOff.inc}
  1932. procedure TIdFTP.Quit;
  1933. {$I IdDeprecatedImplBugOn.inc}
  1934. begin
  1935. Disconnect;
  1936. end;
  1937. procedure TIdFTP.KillDataChannel;
  1938. begin
  1939. // Had kill the data channel ()
  1940. if Assigned(FDataChannel) then begin
  1941. FDataChannel.Disconnect(False); //FDataChannel.IOHandler.DisconnectSocket; {//BGO}
  1942. end;
  1943. end;
  1944. // IMPORTANT!!! THis is for later reference.
  1945. //
  1946. // Note that we do not send the Telnet IP and Sync as suggestedc by RFC 959.
  1947. // We do not do so because some servers will mistakenly assume that the sequences
  1948. // are part of the command and than give a syntax error.
  1949. // I noticed this with FTPSERVE IBM VM Level 510, Microsoft FTP Service (Version 5.0),
  1950. // GlobalSCAPE Secure FTP Server (v. 2.0), and Pure-FTPd [privsep] [TLS].
  1951. //
  1952. // Thus, I feel that sending sequences is just going to aggravate this situation.
  1953. // It is probably the reason why some FTP clients no longer are sending Telnet IP
  1954. // and Sync with the ABOR command.
  1955. procedure TIdFTP.Abort;
  1956. begin
  1957. // only send the abort command. The Data channel is supposed to disconnect
  1958. if Connected then begin
  1959. IOHandler.WriteLn('ABOR'); {do not localize}
  1960. end;
  1961. // Kill the data channel: usually, the server doesn't close it by itself
  1962. KillDataChannel;
  1963. if Assigned(FDataChannel) then begin
  1964. FAbortFlag.Value := True;
  1965. end else begin
  1966. {$IFDEF OVERLOADED_OPENARRAY_BUG}GetResponseArr{$ELSE}GetResponse{$ENDIF}([]);
  1967. end;
  1968. end;
  1969. procedure TIdFTP.SendPort(AHandle: TIdSocketHandle);
  1970. begin
  1971. if FExternalIP <> '' then begin
  1972. SendPort(FExternalIP, AHandle.Port);
  1973. end else begin
  1974. SendPort(AHandle.IP, AHandle.Port);
  1975. end;
  1976. end;
  1977. procedure TIdFTP.SendPort(const AIP: String; const APort: TIdPort);
  1978. begin
  1979. SendDataSettings;
  1980. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PORT ' + ReplaceAll(AIP, '.', ',') {do not localize}
  1981. + ',' + IntToStr(APort div 256) + ',' + IntToStr(APort mod 256), [200]); {do not localize}
  1982. end;
  1983. procedure TIdFTP.InitDataChannel;
  1984. var
  1985. LIOHandler : TIdIOHandler;
  1986. begin
  1987. if FDataChannel = nil then begin
  1988. Exit;
  1989. end;
  1990. if FDataPortProtection = ftpdpsPrivate then begin
  1991. LIOHandler := TIdSSLIOHandlerSocketBase(IOHandler).Clone;
  1992. {$IFDEF USE_OBJECT_ARC}
  1993. // under ARC, the TIdTCPConnection.IOHandler property is a weak reference.
  1994. // TIdSSLIOHandlerSocketBase.Clone() returns an IOHandler with no Owner
  1995. // assigned, so lets make FDataChannel become the Owner in order to keep
  1996. // the IOHandler alive when this method exits.
  1997. //
  1998. // TODO: should we assign Ownership unconditionally on all platforms?
  1999. //
  2000. // TODO: add an AOwner parameter to Clone()
  2001. //
  2002. FDataChannel.InsertComponent(LIOHandler);
  2003. {$ENDIF}
  2004. //we have to delay the actual negotiation until we get the reply and
  2005. //just before the readString
  2006. TIdSSLIOHandlerSocketBase(LIOHandler).PassThrough := True;
  2007. end else begin
  2008. LIOHandler := TIdIOHandler.MakeDefaultIOHandler(FDataChannel);
  2009. end;
  2010. FDataChannel.IOHandler := LIOHandler;
  2011. FDataChannel.ManagedIOHandler := True;
  2012. if FDataChannel is TIdTCPClient then
  2013. begin
  2014. TIdTCPClient(FDataChannel).IPVersion := IPVersion;
  2015. TIdTCPClient(FDataChannel).ReadTimeout := FTransferTimeout;
  2016. //Now SocksInfo are multi-thread safe
  2017. FDataChannel.IOHandler.ConnectTimeout := IOHandler.ConnectTimeout;
  2018. end
  2019. else if FDataChannel is TIdSimpleServer then
  2020. begin
  2021. TIdSimpleServer(FDataChannel).IPVersion := IPVersion;
  2022. end;
  2023. if Assigned(FDataChannel.Socket) and Assigned(Socket) then
  2024. begin
  2025. FDataChannel.Socket.TransparentProxy := Socket.TransparentProxy;
  2026. end;
  2027. FDataChannel.IOHandler.ReadTimeout := FTransferTimeout;
  2028. FDataChannel.IOHandler.SendBufferSize := IOHandler.SendBufferSize;
  2029. FDataChannel.IOHandler.RecvBufferSize := IOHandler.RecvBufferSize;
  2030. FDataChannel.IOHandler.LargeStream := True;
  2031. // FDataChannel.IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
  2032. // FDataChannel.IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
  2033. FDataChannel.WorkTarget := Self;
  2034. end;
  2035. procedure TIdFTP.Put(const ASource: TStream; const ADestFile: string;
  2036. const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1);
  2037. begin
  2038. if ADestFile = '' then begin
  2039. raise EIdFTPUploadFileNameCanNotBeEmpty.Create(RSFTPFileNameCanNotBeEmpty);
  2040. end;
  2041. if AStartPos > -1 then begin
  2042. ASource.Position := AStartPos;
  2043. end;
  2044. DoBeforePut(ASource); //APR);
  2045. if AAppend then begin
  2046. InternalPut('APPE ' + ADestFile, ASource, False, False); {Do not localize}
  2047. end else begin
  2048. InternalPut('STOR ' + ADestFile, ASource, AStartPos = -1, AStartPos > -1); {Do not localize}
  2049. end;
  2050. DoAfterPut;
  2051. end;
  2052. procedure TIdFTP.Put(const ASourceFile: string; const ADestFile: string = '';
  2053. const AAppend: Boolean = False; const AStartPos: TIdStreamSize = -1);
  2054. var
  2055. LSourceStream: TStream;
  2056. LDestFileName : String;
  2057. begin
  2058. LDestFileName := ADestFile;
  2059. if LDestFileName = '' then begin
  2060. LDestFileName := ExtractFileName(ASourceFile);
  2061. end;
  2062. LSourceStream := TIdReadFileNonExclusiveStream.Create(ASourceFile);
  2063. try
  2064. Put(LSourceStream, LDestFileName, AAppend, AStartPos);
  2065. finally
  2066. FreeAndNil(LSourceStream);
  2067. end;
  2068. end;
  2069. procedure TIdFTP.StoreUnique(const ASource: TStream; const AStartPos: TIdStreamSize = -1);
  2070. begin
  2071. if AStartPos > -1 then begin
  2072. ASource.Position := AStartPos;
  2073. end;
  2074. DoBeforePut(ASource);
  2075. InternalPut('STOU', ASource, AStartPos = -1, False); {Do not localize}
  2076. DoAfterPut;
  2077. end;
  2078. procedure TIdFTP.StoreUnique(const ASourceFile: string; const AStartPos: TIdStreamSize = -1);
  2079. var
  2080. LSourceStream: TStream;
  2081. begin
  2082. LSourceStream := TIdReadFileExclusiveStream.Create(ASourceFile);
  2083. try
  2084. StoreUnique(LSourceStream, AStartPos);
  2085. finally
  2086. FreeAndNil(LSourceStream);
  2087. end;
  2088. end;
  2089. procedure TIdFTP.SendInternalPassive(const ACmd: String; var VIP: string;
  2090. var VPort: TIdPort);
  2091. function IsRoutableAddress(AIP: string): Boolean;
  2092. begin
  2093. Result := not TextStartsWith(AIP, '127') and // Loopback 127.0.0.0-127.255.255.255
  2094. not TextStartsWith(AIP, '10.') and // Private 10.0.0.0-10.255.255.255
  2095. not TextStartsWith(AIP, '169.254') and // Link-local 169.254.0.0-169.254.255.255
  2096. not TextStartsWith(AIP, '192.168') and // Private 192.168.0.0-192.168.255.255
  2097. not (TextStartsWith(AIP, '172') and (AIP[7] = '.') and // Private 172.16.0.0-172.31.255.255
  2098. (IndyStrToInt(Copy(AIP, 5, 2)) in [16..31]))
  2099. end;
  2100. var
  2101. i, bLeft, bRight: integer;
  2102. s: string;
  2103. begin
  2104. SendDataSettings;
  2105. SendCmd(ACmd, 227); {do not localize}
  2106. s := Trim(LastCmdResult.Text[0]);
  2107. // Case 1 (Normal)
  2108. // 227 Entering passive mode(100,1,1,1,23,45)
  2109. bLeft := IndyPos('(', s); {do not localize}
  2110. bRight := IndyPos(')', s); {do not localize}
  2111. // Microsoft FTP Service may include a leading ( but not a trailing ),
  2112. // so handle any combination of "(..)", "(..", "..)", and ".."
  2113. if bLeft = 0 then bLeft := RPos(#32, S);
  2114. if bRight = 0 then bRight := Length(S) + 1;
  2115. S := Copy(S, bLeft + 1, bRight - bLeft - 1);
  2116. VIP := ''; {do not localize}
  2117. for i := 1 to 4 do begin
  2118. VIP := VIP + '.' + Fetch(s, ','); {do not localize}
  2119. end;
  2120. IdDelete(VIP, 1, 1);
  2121. // Server sent an unroutable address (private/reserved/etc). Use the IP we
  2122. // connected to instead
  2123. if not IsRoutableAddress(VIP) and IsRoutableAddress(Socket.Binding.PeerIP) then begin
  2124. VIP := Socket.Binding.PeerIP;
  2125. end;
  2126. // Determine port
  2127. VPort := TIdPort(IndyStrToInt(Fetch(s, ',')) and $FF) shl 8; {do not localize}
  2128. //use trim as one server sends something like this:
  2129. //"227 Passive mode OK (195,92,195,164,4,99 )"
  2130. VPort := VPort or TIdPort(IndyStrToInt(Fetch(s, ',')) and $FF); {Do not translate}
  2131. end;
  2132. procedure TIdFTP.SendPassive(var VIP: string; var VPort: TIdPort);
  2133. begin
  2134. SendInternalPassive('PASV', VIP, VPort); {do not localize}
  2135. end;
  2136. procedure TIdFTP.SendCPassive(var VIP: string; var VPort: TIdPort);
  2137. begin
  2138. SendInternalPassive('CPSV', VIP, VPort); {do not localize}
  2139. end;
  2140. procedure TIdFTP.Noop;
  2141. begin
  2142. SendCmd('NOOP', 200); {do not localize}
  2143. end;
  2144. procedure TIdFTP.MakeDir(const ADirName: string);
  2145. begin
  2146. SendCmd('MKD ' + ADirName, 257); {do not localize}
  2147. end;
  2148. function TIdFTP.RetrieveCurrentDir: string;
  2149. begin
  2150. SendCmd('PWD', 257); {do not localize}
  2151. Result := LastCmdResult.Text[0];
  2152. IdDelete(Result, 1, IndyPos('"', Result)); // Remove first doublequote {do not localize}
  2153. Result := Copy(Result, 1, IndyPos('"', Result) - 1); // Remove anything from second doublequote {do not localize} // to end of line
  2154. // TODO: handle embedded quotation marks. RFC 959 allows them to be present
  2155. end;
  2156. procedure TIdFTP.RemoveDir(const ADirName: string);
  2157. begin
  2158. SendCmd('RMD ' + ADirName, 250); {do not localize}
  2159. end;
  2160. procedure TIdFTP.Delete(const AFilename: string);
  2161. begin
  2162. // Linksys NSLU2 NAS returns 200, Ultimodule IDAL returns 257
  2163. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('DELE ' + AFilename, [200, 250, 257]); {do not localize}
  2164. end;
  2165. (*
  2166. CHANGE WORKING DIRECTORY (CWD)
  2167. This command allows the user to work with a different
  2168. directory or dataset for file storage or retrieval without
  2169. altering his login or accounting information. Transfer
  2170. parameters are similarly unchanged. The argument is a
  2171. pathname specifying a directory or other system dependent
  2172. file group designator.
  2173. CWD
  2174. 250
  2175. 500, 501, 502, 421, 530, 550
  2176. *)
  2177. procedure TIdFTP.ChangeDir(const ADirName: string);
  2178. begin
  2179. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('CWD ' + ADirName, [200, 250, 257]); //APR: Ericsson Switch FTP {do not localize}
  2180. end;
  2181. (*
  2182. CHANGE TO PARENT DIRECTORY (CDUP)
  2183. This command is a special case of CWD, and is included to
  2184. simplify the implementation of programs for transferring
  2185. directory trees between operating systems having different
  2186. syntaxes for naming the parent directory. The reply codes
  2187. shall be identical to the reply codes of CWD. See
  2188. Appendix II for further details.
  2189. CDUP
  2190. 200
  2191. 500, 501, 502, 421, 530, 550
  2192. *)
  2193. procedure TIdFTP.ChangeDirUp;
  2194. begin
  2195. // RFC lists 200 as the proper response, but in another section says that it can return the
  2196. // same as CWD, which expects 250. That is it contradicts itself.
  2197. // MS in their infinite wisdom chnaged IIS 5 FTP to return 250.
  2198. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('CDUP', [200, 250]); {do not localize}
  2199. end;
  2200. procedure TIdFTP.Site(const ACommand: string);
  2201. begin
  2202. SendCmd('SITE ' + ACommand, 200); {do not localize}
  2203. end;
  2204. procedure TIdFTP.Rename(const ASourceFile, ADestFile: string);
  2205. begin
  2206. SendCmd('RNFR ' + ASourceFile, 350); {do not localize}
  2207. SendCmd('RNTO ' + ADestFile, 250); {do not localize}
  2208. end;
  2209. function TIdFTP.Size(const AFileName: String): Int64;
  2210. var
  2211. LTrans : TIdFTPTransferType;
  2212. SizeStr: String;
  2213. begin
  2214. Result := -1;
  2215. // RLebeau 03/13/2009: some servers refuse to accept the SIZE command in
  2216. // ASCII mode, returning a "550 SIZE not allowed in ASCII mode" reply.
  2217. // We put the connection in BINARY mode, even though no data connection is
  2218. // actually being used. We restore it if the original mode was not BINARY.
  2219. // It's a good idea to do this anyway because some other clients do this
  2220. // as well.
  2221. LTrans := TransferType;
  2222. if LTrans <> ftBinary then begin
  2223. Self.TransferType := ftBinary;
  2224. end;
  2225. try
  2226. if SendCmd('SIZE ' + AFileName) = 213 then begin {do not localize}
  2227. SizeStr := Trim(LastCmdResult.Text.Text);
  2228. IdDelete(SizeStr, 1, IndyPos(' ', SizeStr)); // delete the response {do not localize}
  2229. Result := IndyStrToInt64(SizeStr, -1);
  2230. end;
  2231. finally
  2232. if LTrans <> ftBinary then begin
  2233. TransferType := LTrans;
  2234. end;
  2235. end;
  2236. end;
  2237. //Added by SP
  2238. procedure TIdFTP.ReInitialize(ADelay: UInt32 = 10);
  2239. begin
  2240. IndySleep(ADelay); //Added
  2241. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('REIN', [120, 220, 500]) <> 500 then begin {do not localize}
  2242. FLoginMsg.Clear;
  2243. FCanResume := False;
  2244. if Assigned(FDirectoryListing) then begin
  2245. FDirectoryListing.Clear;
  2246. end;
  2247. FUsername := ''; {do not localize}
  2248. FPassword := ''; {do not localize}
  2249. FPassive := Id_TIdFTP_Passive;
  2250. FCanResume := False;
  2251. FResumeTested := False;
  2252. FSystemDesc := '';
  2253. FTransferType := Id_TIdFTP_TransferType;
  2254. IOHandler.DefStringEncoding := IndyTextEncoding_8Bit;
  2255. {$IFDEF STRING_IS_ANSI}
  2256. IOHandler.DefAnsiEncoding := IndyTextEncoding_OSDefault;
  2257. {$ENDIF}
  2258. if FUsingSFTP and (FUseTLS <> utUseImplicitTLS) then begin
  2259. (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True;
  2260. FUsingSFTP := False;
  2261. FUseCCC := False;
  2262. end;
  2263. end;
  2264. end;
  2265. procedure TIdFTP.Allocate(AAllocateBytes: Integer);
  2266. begin
  2267. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ALLO ' + IntToStr(AAllocateBytes), [200]); {do not localize}
  2268. end;
  2269. procedure TIdFTP.Status(AStatusList: TStrings);
  2270. begin
  2271. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('STAT', [211, 212, 213, 500]) <> 500 then begin {do not localize}
  2272. AStatusList.Text := LastCmdResult.Text.Text;
  2273. end;
  2274. end;
  2275. procedure TIdFTP.Help(AHelpContents: TStrings; ACommand: String = ''); {do not localize}
  2276. begin
  2277. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(TrimRight('HELP ' + ACommand), [211, 214, 500]) <> 500 then begin {do not localize}
  2278. AHelpContents.Text := LastCmdResult.Text.Text;
  2279. end;
  2280. end;
  2281. function TIdFTP.CheckAccount: Boolean;
  2282. begin
  2283. if (FAccount = '') and Assigned(FOnNeedAccount) then begin
  2284. FOnNeedAccount(Self, FAccount);
  2285. end;
  2286. Result := FAccount <> '';
  2287. end;
  2288. procedure TIdFTP.StructureMount(APath: String);
  2289. begin
  2290. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('SMNT ' + APath, [202, 250, 500]); {do not localize}
  2291. end;
  2292. procedure TIdFTP.FileStructure(AStructure: TIdFTPDataStructure);
  2293. const
  2294. StructureTypes: array[TIdFTPDataStructure] of String = ('F', 'R', 'P'); {do not localize}
  2295. begin
  2296. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('STRU ' + StructureTypes[AStructure], [200, 500]); {do not localize}
  2297. { TODO: Needs to be finished }
  2298. end;
  2299. procedure TIdFTP.TransferMode(ATransferMode: TIdFTPTransferMode);
  2300. var
  2301. s: String;
  2302. begin
  2303. if FCurrentTransferMode <> ATransferMode then begin
  2304. s := '';
  2305. case ATransferMode of
  2306. // dmBlock: begin
  2307. // s := 'B'; {do not localize}
  2308. // end;
  2309. // dmCompressed: begin
  2310. // s := 'C'; {do not localize}
  2311. // end;
  2312. dmStream: begin
  2313. s := 'S'; {do not localize}
  2314. end;
  2315. dmDeflate: begin
  2316. if not Assigned(FCompressor) then begin
  2317. raise EIdFTPMissingCompressor.Create(RSFTPMissingCompressor);
  2318. end;
  2319. if Self.IsCompressionSupported then begin
  2320. s := 'Z'; {Do not localize}
  2321. end;
  2322. end;
  2323. end;
  2324. if s = '' then begin
  2325. raise EIdFTPUnsupportedTransferMode.Create(RSFTPUnsupportedTransferMode);
  2326. end;
  2327. SendCmd('MODE ' + s, 200); {do not localize}
  2328. FCurrentTransferMode := ATransferMode;
  2329. end;
  2330. end;
  2331. destructor TIdFTP.Destroy;
  2332. begin
  2333. FreeAndNil(FClientInfo);
  2334. FreeAndNil(FServerInfo);
  2335. FreeAndNil(FListResult);
  2336. FreeAndNil(FLoginMsg);
  2337. FreeAndNil(FDirectoryListing);
  2338. FreeAndNil(FLangsSupported);
  2339. FreeAndNil(FProxySettings); //APR
  2340. FreeAndNil(FTZInfo);
  2341. FreeAndNil(FAbortFlag);
  2342. FreeAndNil(FNATKeepAlive);
  2343. inherited Destroy;
  2344. end;
  2345. function TIdFTP.Quote(const ACommand: String): Int16;
  2346. begin
  2347. Result := SendCmd(ACommand);
  2348. end;
  2349. procedure TIdFTP.IssueFEAT;
  2350. var
  2351. LBuf : String;
  2352. i : Integer;
  2353. begin
  2354. //Feat data
  2355. SendCmd('FEAT'); {do not localize}
  2356. FCapabilities.Clear;
  2357. //Ipswitch's FTP WS-FTP Server may issue 221 as success
  2358. if LastCmdResult.NumericCode in [211,221] then begin
  2359. FCapabilities.AddStrings(LastCmdResult.Text);
  2360. //we remove the first and last lines because we only want the list
  2361. if FCapabilities.Count > 0 then begin
  2362. FCapabilities.Delete(0);
  2363. end;
  2364. if FCapabilities.Count > 0 then begin
  2365. FCapabilities.Delete(FCapabilities.Count-1);
  2366. end;
  2367. end;
  2368. if FUsingExtDataPort then begin
  2369. FUsingExtDataPort := IsExtSupported('EPRT') and IsExtSupported('EPSV'); {do not localize}
  2370. end;
  2371. FCanUseMLS := IsExtSupported('MLSD') or IsExtSupported('MLST'); {do not localize}
  2372. ExtractFeatFacts('LANG', FLangsSupported); {do not localize}
  2373. //see if compression is supported.
  2374. //we parse this way because IxExtensionSupported can only work
  2375. //with one word.
  2376. FIsCompressionSupported := False;
  2377. for i := 0 to FCapabilities.Count-1 do begin
  2378. LBuf := Trim(FCapabilities[i]);
  2379. if LBuf = 'MODE Z' then begin {do not localize}
  2380. FIsCompressionSupported := True;
  2381. Break;
  2382. end;
  2383. end;
  2384. // identify the client before sending the OPTS UTF8 command.
  2385. // some servers need this in order to work around a bug in
  2386. // Microsoft Internet Explorer's UTF-8 handling
  2387. FServerInfo.Clear;
  2388. if IsExtSupported('CSID') then begin {do not localize}
  2389. if SendCmd('CSID ' + FClientInfo.CSIDParams) = 200 then begin {do not localize}
  2390. FServerInfo.CSIDParams := LastCmdResult.Text.Text;
  2391. end;
  2392. end
  2393. else if IsExtSupported('CLNT') then begin {do not localize}
  2394. SendCmd('CLNT ' + FClientInfo.CLNTParams); {do not localize}
  2395. end;
  2396. // RLebeau 4/26/2019: per RFC 2640, if the server reports the 'UTF8'
  2397. // capability, it is REQUIRED to detect and accept UTF-8 encoded
  2398. // paths/filenames in commands. But, it is not REQUIRED to send UTF-8
  2399. // in responses and directory listings. For that, we need to use the
  2400. // OPTS command to inform the server that we actually want UTF-8...
  2401. if IsExtSupported('UTF8') then begin {do not localize}
  2402. // trying non-standard UTF-8 extension first, many servers use this...
  2403. // Cerberus and RaidenFTP return 220, but TitanFTP and Gene6 return 200 instead...
  2404. if (SendCmd('OPTS UTF8 ON') div 100) = 2 then begin {do not localize}
  2405. IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
  2406. end
  2407. // trying draft-ietf-ftpext-utf-8-option-00.txt next...
  2408. else if SendCmd('OPTS UTF-8 NLST') = 200 then begin {do not localize}
  2409. IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
  2410. end;
  2411. end;
  2412. end;
  2413. procedure TIdFTP.Login;
  2414. var
  2415. i : Integer;
  2416. LResp : Word;
  2417. LCmd : String;
  2418. function FtpHost: String;
  2419. begin
  2420. if FPort = IDPORT_FTP then begin
  2421. Result := FHost;
  2422. end else begin
  2423. Result := FHost + Id_TIdFTP_HostPortDelimiter + IntToStr(FPort);
  2424. end;
  2425. end;
  2426. begin
  2427. //TLS part
  2428. if UseTLS in ExplicitTLSVals then begin
  2429. //This has to be here because the Rein command clears encryption.
  2430. //RFC 4217
  2431. FUsingSFTP := False;
  2432. if FAUTHCmd = tAuto then begin
  2433. {Note that we can not call SupportsTLS at all. That depends upon the FEAT response
  2434. and unfortunately, some servers such as WS_FTP Server 4.0.0 (78162662)
  2435. will not accept a FEAT command until you login. In other words, you have to do
  2436. this by trial and error.
  2437. }
  2438. //334 has to be accepted because of a broekn implementation
  2439. //see: http://www.ford-hutchinson.com/~fh-1-pfh/ftps-ext.html#bad
  2440. {Note that we have to try several commands because some servers use AUTH TLS while others use
  2441. AUTH SSL. GlobalScape's FTP Server only uses AUTH SSL while IpSwitch's uses AUTH TLS (the correct behavior).
  2442. We try two other commands for historical reasons.
  2443. }
  2444. for i := 0 to 3 do begin
  2445. LResp := SendCmd('AUTH ' + TLS_AUTH_NAMES[i]); {do not localize}
  2446. if (LResp = 234) or (LResp = 334) then begin
  2447. //okay. do the handshake
  2448. TLSHandshake;
  2449. FUsingSFTP := True;
  2450. //we are done with the negotiation, let's close this.
  2451. Break;
  2452. end;
  2453. //see if the error was not any type of syntax error code
  2454. //if it wasn't, we fail the command.
  2455. if (LResp div 500) <> 1 then begin
  2456. ProcessTLSNegCmdFailed;
  2457. Break;
  2458. end;
  2459. end;
  2460. end else begin
  2461. LResp := SendCmd('AUTH ' + TLS_AUTH_NAMES[Ord(FAUTHCmd)-1]); {do not localize}
  2462. if (LResp = 234) or (LResp = 334) then begin
  2463. //okay. do the handshake
  2464. TLSHandshake;
  2465. FUsingSFTP := True;
  2466. end else begin
  2467. ProcessTLSNegCmdFailed;
  2468. end;
  2469. end;
  2470. if not FUsingSFTP then begin
  2471. ProcessTLSNotAvail;
  2472. end;
  2473. end
  2474. else if UseTLS = utUseImplicitTLS then begin
  2475. FUsingSFTP := True;
  2476. end
  2477. else begin
  2478. FUsingSFTP := False;
  2479. end;
  2480. //login
  2481. case ProxySettings.ProxyType of
  2482. fpcmNone:
  2483. begin
  2484. LCmd := MakeXAUTCmd(Greeting.Text.Text, FUserName, GetLoginPassword);
  2485. if (LCmd <> '') and (not GetFIPSMode) then
  2486. begin
  2487. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(LCmd, [230, 232, 331]) = 331 then begin
  2488. if IsAccountNeeded then begin
  2489. if CheckAccount then begin
  2490. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2491. end else begin
  2492. RaiseExceptionForLastCmdResult;
  2493. end;
  2494. end;
  2495. end;
  2496. end
  2497. else if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName, [230, 232, 331]) = 331 then {do not localize}
  2498. begin
  2499. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 332]); {do not localize}
  2500. if IsAccountNeeded then begin
  2501. if CheckAccount then begin
  2502. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2503. end else begin
  2504. RaiseExceptionForLastCmdResult;
  2505. end;
  2506. end;
  2507. end;
  2508. end;
  2509. fpcmUserSite:
  2510. begin
  2511. //This also supports WinProxy
  2512. if Length(ProxySettings.UserName) > 0 then begin
  2513. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + ProxySettings.UserName, [230, 331]) = 331 then {do not localize}
  2514. begin
  2515. SendCmd('PASS ' + ProxySettings.Password, 230); {do not localize}
  2516. if IsAccountNeeded then begin
  2517. if CheckAccount then begin
  2518. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2519. end else begin
  2520. RaiseExceptionForLastCmdResult;
  2521. end;
  2522. end;
  2523. end;
  2524. end;
  2525. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName + '@' + FtpHost, [230, 232, 331]) = 331 then {do not localize}
  2526. begin
  2527. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 331]); {do not localize}
  2528. if IsAccountNeeded then
  2529. begin
  2530. if CheckAccount then begin
  2531. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2532. end else begin
  2533. RaiseExceptionForLastCmdResult;
  2534. end;
  2535. end;
  2536. end;
  2537. end;
  2538. fpcmSite:
  2539. begin
  2540. if Length(ProxySettings.UserName) > 0 then begin
  2541. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize}
  2542. SendCmd('PASS ' + ProxySettings.Password, 230); {do not localize}
  2543. end;
  2544. end;
  2545. SendCmd('SITE ' + FtpHost); // ? Server Reply? 220? {do not localize}
  2546. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize}
  2547. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 332]); {do not localize}
  2548. if IsAccountNeeded then begin
  2549. if CheckAccount then begin
  2550. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2551. end else begin
  2552. RaiseExceptionForLastCmdResult;
  2553. end;
  2554. end;
  2555. end;
  2556. end;
  2557. fpcmOpen:
  2558. begin
  2559. if Length(ProxySettings.UserName) > 0 then begin
  2560. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize}
  2561. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 332]); {do not localize}
  2562. if IsAccountNeeded then begin
  2563. if CheckAccount then begin
  2564. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2565. end else begin
  2566. RaiseExceptionForLastCmdResult;
  2567. end;
  2568. end;
  2569. end;
  2570. end;
  2571. SendCmd('OPEN ' + FtpHost);//? Server Reply? 220? {do not localize}
  2572. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize}
  2573. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230, 332]); {do not localize}
  2574. if IsAccountNeeded then begin
  2575. if CheckAccount then begin
  2576. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2577. end else begin
  2578. RaiseExceptionForLastCmdResult;
  2579. end;
  2580. end;
  2581. end;
  2582. end;
  2583. fpcmUserPass: //USER user@firewalluser@hostname / PASS pass@firewallpass
  2584. begin
  2585. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(IndyFormat('USER %s@%s@%s',
  2586. [FUserName, ProxySettings.UserName, FtpHost]), [230, 232, 331]) = 331 then begin {do not localize}
  2587. if Length(ProxySettings.Password) > 0 then begin
  2588. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword + '@' + ProxySettings.Password, [230, 332]); {do not localize}
  2589. end else begin
  2590. //// needs otp ////
  2591. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230,332]); {do not localize}
  2592. end;
  2593. if IsAccountNeeded then begin
  2594. if CheckAccount then begin
  2595. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2596. end else begin
  2597. RaiseExceptionForLastCmdResult;
  2598. end;
  2599. end;
  2600. end;
  2601. end;
  2602. fpcmTransparent:
  2603. begin
  2604. //I think fpcmTransparent means to connect to the regular host and the firewalll
  2605. //intercepts the login information.
  2606. if Length(ProxySettings.UserName) > 0 then begin
  2607. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + ProxySettings.UserName, [230, 331]) = 331 then begin {do not localize}
  2608. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + ProxySettings.Password, [230,332]); {do not localize}
  2609. end;
  2610. end;
  2611. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('USER ' + FUserName, [230, 232, 331]) = 331 then begin {do not localize}
  2612. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230,332]); {do not localize}
  2613. if IsAccountNeeded then begin
  2614. if CheckAccount then begin
  2615. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]);
  2616. end else begin
  2617. RaiseExceptionForLastCmdResult;
  2618. end;
  2619. end;
  2620. end;
  2621. end;
  2622. fpcmUserHostFireWallID : //USER hostuserId@hostname firewallUsername
  2623. begin
  2624. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(TrimRight('USER ' + Username + '@' + FtpHost + ' ' + ProxySettings.UserName), [230, 331]) = 331 then begin {do not localize}
  2625. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + GetLoginPassword, [230,232,202,332]) = 332 then begin
  2626. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + ProxySettings.Password, [230,232,332]);
  2627. if IsAccountNeeded then begin
  2628. if CheckAccount then begin
  2629. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2630. end else begin
  2631. RaiseExceptionForLastCmdResult;
  2632. end;
  2633. end;
  2634. end;
  2635. end;
  2636. end;
  2637. fpcmNovellBorder : //Novell Border PRoxy
  2638. begin
  2639. {Done like this:
  2640. USER ProxyUserName$ DestFTPUserName$DestFTPHostName
  2641. PASS UsereDirectoryPassword$ DestFTPPassword
  2642. Novell BorderManager 3.8 Proxy and Firewall Overview and Planning Guide
  2643. Copyright © 1997-1998, 2001, 2002-2003, 2004 Novell, Inc. All rights reserved.
  2644. ===
  2645. From a WS-FTP Pro firescript at:
  2646. http://support.ipswitch.com/kb/WS-20050315-DM01.htm
  2647. send ("USER %FwUserId$%HostUserId$%HostAddress")
  2648. //send ("PASS %FwPassword$%HostPassword")
  2649. }
  2650. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(TrimRight('USER ' + ProxySettings.UserName + '$' + Username + '$' + FtpHost), [230, 331]) = 331 then begin {do not localize}
  2651. if {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('PASS ' + ProxySettings.UserName + '$' + GetLoginPassword, [230,232,202,332]) = 332 then begin
  2652. if IsAccountNeeded then begin
  2653. if CheckAccount then begin
  2654. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('ACCT ' + FAccount, [202, 230, 500]); {do not localize}
  2655. end else begin
  2656. RaiseExceptionForLastCmdResult;
  2657. end;
  2658. end;
  2659. end;
  2660. end;
  2661. end;
  2662. fpcmHttpProxyWithFtp :
  2663. begin
  2664. {GET ftp://XXX:[email protected]/ HTTP/1.0
  2665. Host: indy.nevrona.com
  2666. User-Agent: Mozilla/4.0 (compatible; Wincmd; Windows NT)
  2667. Proxy-Authorization: Basic B64EncodedUserPass==
  2668. Connection: close}
  2669. raise EIdSocksServerCommandError.Create(RSSocksServerCommandError);
  2670. end;//fpcmHttpProxyWithFtp
  2671. fpcmCustomProxy :
  2672. begin
  2673. DoCustomFTPProxy;
  2674. end;
  2675. end;//case
  2676. FLoginMsg.Assign(LastCmdResult);
  2677. DoOnBannerAfterLogin(FLoginMsg.FormattedReply);
  2678. //should be here because this can be issued more than once per connection.
  2679. if FAutoIssueFEAT then begin
  2680. IssueFEAT;
  2681. end;
  2682. SendTransferType(FTransferType);
  2683. end;
  2684. procedure TIdFTP.DoAfterLogin;
  2685. begin
  2686. if Assigned(FOnAfterClientLogin) then begin
  2687. OnAfterClientLogin(Self);
  2688. end;
  2689. end;
  2690. procedure TIdFTP.DoFTPList;
  2691. begin
  2692. if Assigned(FOnCreateFTPList) then begin
  2693. FOnCreateFTPList(Self, FDirectoryListing);
  2694. end;
  2695. end;
  2696. function TIdFTP.GetDirectoryListing: TIdFTPListItems;
  2697. begin
  2698. if FDirectoryListing = nil then begin
  2699. if Assigned(FOnDirParseStart) then begin
  2700. FOnDirParseStart(Self);
  2701. end;
  2702. ConstructDirListing;
  2703. ParseFTPList;
  2704. end;
  2705. Result := FDirectoryListing;
  2706. end;
  2707. procedure TIdFTP.SetProxySettings(const Value: TIdFtpProxySettings);
  2708. begin
  2709. FProxySettings.Assign(Value);
  2710. end;
  2711. { TIdFtpProxySettings }
  2712. procedure TIdFtpProxySettings.Assign(Source: TPersistent);
  2713. var
  2714. LSource: TIdFtpProxySettings;
  2715. begin
  2716. if Source is TIdFtpProxySettings then begin
  2717. LSource := TIdFtpProxySettings(Source);
  2718. FProxyType := LSource.ProxyType;
  2719. FHost := LSource.Host;
  2720. FUserName := LSource.UserName;
  2721. FPassword := LSource.Password;
  2722. FPort := LSource.Port;
  2723. end else begin
  2724. inherited Assign(Source);
  2725. end;
  2726. end;
  2727. procedure TIdFTP.SendPBSZ;
  2728. begin
  2729. {NOte that PBSZ - protection buffer size must always be zero for FTP TLS}
  2730. if FUsingSFTP or (FUseTLS = utUseImplicitTLS) then begin
  2731. //protection buffer size
  2732. SendCmd('PBSZ 0'); {do not localize}
  2733. end;
  2734. end;
  2735. procedure TIdFTP.SendPROT;
  2736. begin
  2737. case FDataPortProtection of
  2738. ftpdpsClear : SendCmd('PROT C', 200); //'C' - Clear - neither Integrity nor Privacy {do not localize}
  2739. // NOT USED - 'S' - Safe - Integrity without Privacy
  2740. // NOT USED - 'E' - Confidential - Privacy without Integrity
  2741. // 'P' - Private - Integrity and Privacy
  2742. ftpdpsPrivate : SendCmd('PROT P', 200); {do not localize}
  2743. end;
  2744. end;
  2745. procedure TIdFTP.SendDataSettings;
  2746. begin
  2747. if FUsingSFTP then begin
  2748. if not FDataSettingsSent then begin
  2749. FDataSettingsSent := True;
  2750. SendPBSZ;
  2751. SendPROT;
  2752. if FUseCCC then begin
  2753. FUsingCCC := (SendCmd('CCC') div 100) = 2; {do not localize}
  2754. if FUsingCCC then begin
  2755. (IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True;
  2756. // TODO: uncomment this? Reinitialize() resets them after setting PassThrough=True...
  2757. {FUsingSFTP := False;
  2758. FUseCCC := False;}
  2759. end;
  2760. end;
  2761. end;
  2762. end;
  2763. end;
  2764. procedure TIdFTP.SetIOHandler(AValue: TIdIOHandler);
  2765. begin
  2766. inherited SetIOHandler(AValue);
  2767. // UseExtensionDataPort must be true for IPv6 connections.
  2768. // PORT and PASV can not communicate IPv6 Addresses
  2769. if Socket <> nil then begin
  2770. if Socket.IPVersion = Id_IPv6 then begin
  2771. FUseExtensionDataPort := True;
  2772. end;
  2773. end;
  2774. end;
  2775. procedure TIdFTP.SetUseExtensionDataPort(const AValue: Boolean);
  2776. begin
  2777. if (not AValue) and (IPVersion = Id_IPv6) then begin
  2778. raise EIdFTPMustUseExtWithIPv6.Create(RSFTPMustUseExtWithIPv6);
  2779. end;
  2780. if TryNATFastTrack then begin
  2781. raise EIdFTPMustUseExtWithNATFastTrack.Create(RSFTPMustUseExtWithNATFastTrack);
  2782. end;
  2783. FUseExtensionDataPort := AValue;
  2784. end;
  2785. procedure TIdFTP.ParseEPSV(const AReply : String; var VIP : String; var VPort : TIdPort);
  2786. var
  2787. bLeft, bRight, LPort: Integer;
  2788. delim : Char;
  2789. s : String;
  2790. begin
  2791. s := Trim(AReply);
  2792. // "229 Entering Extended Passive Mode (|||59028|)"
  2793. bLeft := IndyPos('(', s); {do not localize}
  2794. bRight := IndyPos(')', s); {do not localize}
  2795. s := Copy(s, bLeft + 1, bRight - bLeft - 1);
  2796. delim := s[1]; // normally is | but the RFC say it may be different
  2797. Fetch(S, delim);
  2798. Fetch(S, delim);
  2799. VIP := Fetch(S, delim);
  2800. if VIP = '' then begin
  2801. VIP := Host;
  2802. end;
  2803. s := Trim(Fetch(S, delim));
  2804. LPort := IndyStrToInt(s, 0);
  2805. if (LPort < 1) or (LPort > 65535) then begin
  2806. raise EIdFTPServerSentInvalidPort.CreateFmt(RSFTPServerSentInvalidPort, [s]);
  2807. end;
  2808. VPort := TIdPort(LPort and $FFFF);
  2809. end;
  2810. procedure TIdFTP.SendEPassive(var VIP: string; var VPort: TIdPort);
  2811. begin
  2812. SendDataSettings;
  2813. //Note that for FTP Proxies, it is not desirable for the server to choose
  2814. //the EPSV data port IP connection type. We try to if we can.
  2815. if FProxySettings.ProxyType <> fpcmNone then begin
  2816. if SendCMD('EPSV ' + cIPVersions[IPVersion]) <> 229 then begin {do not localize}
  2817. //Raidon and maybe a few others may honor EPSV but not with the proto numbers
  2818. SendCMD('EPSV'); {do not localize}
  2819. end;
  2820. end else begin
  2821. SendCMD('EPSV'); {do not localize}
  2822. end;
  2823. if LastCmdResult.NumericCode <> 229 then begin
  2824. SendPassive(VIP, VPort);
  2825. FUsingExtDataPort := False;
  2826. Exit;
  2827. end;
  2828. try
  2829. ParseEPSV(LastCmdResult.Text[0], VIP, VPort);
  2830. except
  2831. SendCmd('ABOR'); {do not localize}
  2832. raise;
  2833. end;
  2834. end;
  2835. procedure TIdFTP.SendEPort(AHandle: TIdSocketHandle);
  2836. begin
  2837. SendDataSettings;
  2838. if FExternalIP <> '' then begin
  2839. SendEPort(FExternalIP, AHandle.Port, AHandle.IPVersion);
  2840. end else begin
  2841. SendEPort(AHandle.IP, AHandle.Port, AHandle.IPVersion);
  2842. end;
  2843. end;
  2844. procedure TIdFTP.SendEPort(const AIP: String; const APort: TIdPort; const AIPVersion: TIdIPVersion);
  2845. begin
  2846. if SendCmd('EPRT |' + cIPVersions[AIPVersion] + '|' + AIP + '|' + IntToStr(APort) + '|') <> 200 then begin {do not localize}
  2847. SendPort(AIP, APort);
  2848. FUsingExtDataPort := False;
  2849. end;
  2850. end;
  2851. procedure TIdFTP.SetPassive(const AValue: Boolean);
  2852. begin
  2853. if (not AValue) and TryNATFastTrack then begin
  2854. raise EIdFTPPassiveMustBeTrueWithNATFT.Create(RSFTPFTPPassiveMustBeTrueWithNATFT);
  2855. end;
  2856. FPassive := AValue;
  2857. end;
  2858. procedure TIdFTP.SetTryNATFastTrack(const AValue: Boolean);
  2859. begin
  2860. FTryNATFastTrack := AValue;
  2861. if FTryNATFastTrack then begin
  2862. FPassive := True;
  2863. FUseExtensionDataPort := True;
  2864. end;
  2865. end;
  2866. procedure TIdFTP.DoTryNATFastTrack;
  2867. begin
  2868. if IsExtSupported('EPSV') then begin {do not localize}
  2869. if SendCmd('EPSV ALL') = 229 then begin {do not localize}
  2870. //Surge FTP treats EPSV ALL as if it were a standard EPSV
  2871. //We send ABOR in that case so it can close the data connection it created
  2872. SendCmd('ABOR'); {do not localize}
  2873. end;
  2874. FUsingNATFastTrack := True;
  2875. end;
  2876. end;
  2877. procedure TIdFTP.SetCmdOpt(const ACmd, AOptions: String);
  2878. begin
  2879. // RLebeau 4/26/2019: the only official success reply allowed for OPTS
  2880. // is 200, but for OPTS UTF8 ON, Cerberus and RaidenFTP return 220 instead.
  2881. // So lets just accept any 2xx reply...
  2882. if (SendCmd(TrimRight('OPTS ' + ACmd + ' ' + AOptions)) div 100) <> 2 then begin
  2883. RaiseExceptionForLastCmdResult;
  2884. end;
  2885. end;
  2886. procedure TIdFTP.ExtListDir(ADest: TStrings = nil; const ADirectory: string = '');
  2887. var
  2888. LDest: TMemoryStream;
  2889. LEncoding: IIdTextEncoding;
  2890. begin
  2891. // RLebeau 6/4/2009: According to RFC 3659 Section 7.2:
  2892. //
  2893. // The data connection opened for a MLSD response shall be a connection
  2894. // as if the "TYPE L 8", "MODE S", and "STRU F" commands had been given,
  2895. // whatever FTP transfer type, mode and structure had actually been set,
  2896. // and without causing those settings to be altered for future commands.
  2897. // That is, this transfer type shall be set for the duration of the data
  2898. // connection established for this command only. While the content of
  2899. // the data sent can be viewed as a series of lines, implementations
  2900. // should note that there is no maximum line length defined.
  2901. // Implementations should be prepared to deal with arbitrarily long
  2902. // lines.
  2903. LDest := TMemoryStream.Create;
  2904. try
  2905. InternalGet(TrimRight('MLSD ' + ADirectory), LDest); {do not localize}
  2906. FreeAndNil(FDirectoryListing);
  2907. FDirFormat := '';
  2908. LDest.Position := 0;
  2909. // RLebeau: using IndyTextEncoding_8Bit here. TIdFTPListParseBase will
  2910. // decode UTF-8 sequences later on...
  2911. LEncoding := IndyTextEncoding_8Bit;
  2912. FListResult.Text := ReadStringFromStream(LDest, -1, LEncoding{$IFDEF STRING_IS_ANSI}, LEncoding{$ENDIF});
  2913. LEncoding := nil;
  2914. TIdFTPListResult(FListResult).FDetails := True;
  2915. TIdFTPListResult(FListResult).FUsedMLS := True;
  2916. FDirFormat := MLST;
  2917. finally
  2918. FreeAndNil(LDest);
  2919. end;
  2920. if Assigned(ADest) then begin //APR: User can use ListResult and DirectoryListing
  2921. ADest.Assign(FListResult);
  2922. end;
  2923. DoOnRetrievedDir;
  2924. end;
  2925. procedure TIdFTP.ExtListItem(ADest: TStrings; AFList : TIdFTPListItems; const AItem: string);
  2926. var
  2927. i : Integer;
  2928. begin
  2929. ADest.BeginUpdate;
  2930. try
  2931. ADest.Clear;
  2932. IOHandler.WriteLn(TrimRight('MLST ' + AItem)); {do not localize}
  2933. GetResponse(250, IndyTextEncoding_8Bit);
  2934. for i := 0 to LastCmdResult.Text.Count -1 do begin
  2935. if IndyPos(';', LastCmdResult.Text[i]) > 0 then begin
  2936. ADest.Add(LastCmdResult.Text[i]);
  2937. end;
  2938. end;
  2939. finally
  2940. ADest.EndUpdate;
  2941. end;
  2942. if Assigned(AFList) then begin
  2943. IdFTPListParseBase.ParseListing(ADest, AFList, 'MLST'); {do not localize}
  2944. end;
  2945. end;
  2946. procedure TIdFTP.ExtListItem(ADest: TStrings; const AItem: string);
  2947. begin
  2948. ExtListItem(ADest, nil, AItem);
  2949. end;
  2950. procedure TIdFTP.ExtListItem(AFList: TIdFTPListItems; const AItem: String);
  2951. var
  2952. LBuf : TStrings;
  2953. begin
  2954. LBuf := TStringList.Create;
  2955. try
  2956. ExtListItem(LBuf, AFList, AItem);
  2957. finally
  2958. FreeAndNil(LBuf);
  2959. end;
  2960. end;
  2961. function TIdFTP.IsExtSupported(const ACmd: String): Boolean;
  2962. var
  2963. i : Integer;
  2964. LBuf : String;
  2965. begin
  2966. Result := False;
  2967. for i := 0 to FCapabilities.Count -1 do begin
  2968. LBuf := TrimLeft(FCapabilities[i]);
  2969. if TextIsSame(Fetch(LBuf), ACmd) then begin
  2970. Result := True;
  2971. Exit;
  2972. end;
  2973. end;
  2974. end;
  2975. function TIdFTP.FileDate(const AFileName: String; const AsGMT: Boolean): TDateTime;
  2976. var
  2977. LBuf : String;
  2978. begin
  2979. //Do not use the FEAT list because some servers
  2980. //may support it even if FEAT isn't supported
  2981. if SendCmd('MDTM ' + AFileName) = 213 then begin {do not localize}
  2982. LBuf := LastCmdResult.Text[0];
  2983. LBuf := Trim(LBuf);
  2984. if AsGMT then begin
  2985. Result := FTPMLSToGMTDateTime(LBuf);
  2986. end else begin
  2987. Result := FTPMLSToLocalDateTime(LBuf);
  2988. end;
  2989. end else begin
  2990. Result := 0;
  2991. end;
  2992. end;
  2993. procedure TIdFTP.SiteToSiteUpload(const AToSite : TIdFTP; const ASourceFile : String;
  2994. const ADestFile : String = '');
  2995. {
  2996. SiteToSiteUpload
  2997. From: PASV To: PORT - ATargetUsesPasv = False
  2998. From: RETR To: STOR
  2999. SiteToSiteDownload
  3000. From: PORT To: PASV - ATargetUsesPasv = True
  3001. From: RETR To: STOR
  3002. }
  3003. begin
  3004. if ValidateInternalIsTLSFXP(Self, AToSite, True) then begin
  3005. InternalEncryptedTLSFXP(Self, AToSite, ASourceFile, ADestFile, True);
  3006. end else begin
  3007. InternalUnencryptedFXP(Self, AToSite, ASourceFile, ADestFile, True);
  3008. end;
  3009. end;
  3010. procedure TIdFTP.SiteToSiteDownload(const AFromSite: TIdFTP; const ASourceFile : String;
  3011. const ADestFile : String = '');
  3012. {
  3013. The only use of this function is to get the passive mode on the other connection.
  3014. Because not all hosts allow it. This way you get a second chance.
  3015. If uploading from host A doesn't work, try downloading from host B
  3016. }
  3017. begin
  3018. if ValidateInternalIsTLSFXP(AFromSite, Self, True) then begin
  3019. InternalEncryptedTLSFXP(AFromSite, Self, ASourceFile, ADestFile, False);
  3020. end else begin
  3021. InternalUnencryptedFXP(AFromSite, Self, ASourceFile, ADestFile, False);
  3022. end;
  3023. end;
  3024. procedure TIdFTP.ExtractFeatFacts(const ACmd: String; AResults: TStrings);
  3025. var
  3026. i : Integer;
  3027. LBuf, LFact : String;
  3028. begin
  3029. AResults.BeginUpdate;
  3030. try
  3031. AResults.Clear;
  3032. for i := 0 to FCapabilities.Count -1 do begin
  3033. LBuf := FCapabilities[i];
  3034. if TextIsSame(Fetch(LBuf), ACmd) then begin
  3035. LBuf := Trim(LBuf);
  3036. while LBuf <> '' do begin
  3037. LFact := Trim(Fetch(LBuf, ';'));
  3038. if LFact <> '' then begin
  3039. AResults.Add(LFact);
  3040. end;
  3041. end;
  3042. Exit;
  3043. end;
  3044. end;
  3045. finally
  3046. AResults.EndUpdate;
  3047. end;
  3048. end;
  3049. procedure TIdFTP.SetLang(const ALangTag: String);
  3050. begin
  3051. if IsExtSupported('LANG') then begin {do not localize}
  3052. SendCmd(TrimRight('LANG ' + ALangTag), 200); {do not localize}
  3053. end;
  3054. end;
  3055. function TIdFTP.CRC(const AFIleName : String; const AStartPoint : Int64 = 0;
  3056. const AEndPoint : Int64 = 0) : Int64;
  3057. var
  3058. LCmd : String;
  3059. LCRC : String;
  3060. begin
  3061. Result := -1;
  3062. if IsExtSupported('XCRC') then begin {do not localize}
  3063. LCmd := 'XCRC "' + AFileName + '"'; {do not localize}
  3064. if AStartPoint <> 0 then begin
  3065. LCmd := LCmd + ' ' + IntToStr(AStartPoint);
  3066. if AEndPoint <> 0 then begin
  3067. LCmd := LCmd + ' ' + IntToStr(AEndPoint);
  3068. end;
  3069. end;
  3070. if SendCMD(LCMD) = 250 then begin
  3071. LCRC := Trim(LastCmdResult.Text.Text);
  3072. IdDelete(LCRC, 1, IndyPos(' ', LCRC)); // delete the response
  3073. Result := IndyStrToInt64('$' + LCRC, -1);
  3074. end;
  3075. end;
  3076. end;
  3077. procedure TIdFTP.CombineFiles(const ATargetFile: String; AFileParts: TStrings);
  3078. var
  3079. i : Integer;
  3080. LCmd: String;
  3081. begin
  3082. if IsExtSupported('COMB') and (AFileParts.Count > 0) then begin {do not localize}
  3083. LCmd := 'COMB "' + ATargetFile + '"'; {do not localize}
  3084. for i := 0 to AFileParts.Count -1 do begin
  3085. LCmd := LCmd + ' ' + AFileParts[i];
  3086. end;
  3087. SendCmd(LCmd, 250);
  3088. end;
  3089. end;
  3090. procedure TIdFTP.ParseFTPList;
  3091. begin
  3092. DoOnDirParseStart;
  3093. try
  3094. // Parse directory listing
  3095. if FListResult.Count > 0 then begin
  3096. if TIdFTPListResult(FListResult).UsedMLS then begin
  3097. FDirFormat := MLST;
  3098. // TODO: set the FListParserClass as well..
  3099. IdFTPListParseBase.ParseListing(FListResult, FDirectoryListing, MLST);
  3100. end else begin
  3101. CheckListParseCapa(FListResult, FDirectoryListing, FDirFormat,
  3102. FListParserClass, SystemDesc, TIdFTPListResult(FListResult).Details);
  3103. end;
  3104. end else begin
  3105. FDirFormat := '';
  3106. end;
  3107. finally
  3108. DoOnDirParseEnd;
  3109. end;
  3110. end;
  3111. function TIdFTP.GetSupportsTLS: Boolean;
  3112. begin
  3113. Result := (FindAuthCmd <> '');
  3114. end;
  3115. function TIdFTP.FindAuthCmd: String;
  3116. var
  3117. i : Integer;
  3118. LBuf : String;
  3119. LWord : String;
  3120. begin
  3121. Result := '';
  3122. for i := 0 to FCapabilities.Count -1 do begin
  3123. LBuf := TrimLeft(FCapabilities[i]);
  3124. if TextIsSame(Fetch(LBuf), 'AUTH') then begin {do not localize}
  3125. repeat
  3126. LWord := Trim(Fetch(LBuf, ';'));
  3127. if PosInStrArray(LWord, TLS_AUTH_NAMES, False) > -1 then begin
  3128. Result := 'AUTH ' + LWord; {do not localize}
  3129. Exit;
  3130. end;
  3131. until LBuf = '';
  3132. Break;
  3133. end;
  3134. end;
  3135. end;
  3136. procedure TIdFTP.DoCustomFTPProxy;
  3137. begin
  3138. if Assigned(FOnCustomFTPProxy) then begin
  3139. FOnCustomFTPProxy(Self);
  3140. end else begin
  3141. raise EIdFTPOnCustomFTPProxyRequired.Create(RSFTPOnCustomFTPProxyReq);
  3142. end;
  3143. end;
  3144. function TIdFTP.GetLoginPassword: String;
  3145. begin
  3146. Result := GetLoginPassword(LastCmdResult.Text.Text);
  3147. end;
  3148. function TIdFTP.GetLoginPassword(const APrompt: String): String;
  3149. begin
  3150. if TIdOTPCalculator.IsValidOTPString(APrompt) then begin
  3151. TIdOTPCalculator.GenerateSixWordKey(APrompt, FPassword, Result);
  3152. end else begin
  3153. Result := FPassword;
  3154. end;
  3155. end;
  3156. function TIdFTP.SetSSCNToOn : Boolean;
  3157. begin
  3158. Result := FUsingSFTP;
  3159. if not Result then begin
  3160. Exit;
  3161. end;
  3162. Result := (DataPortProtection = ftpdpsPrivate);
  3163. if not Result then begin
  3164. Exit;
  3165. end;
  3166. Result := not IsExtSupported(SCCN_FEAT);
  3167. if not Result then begin
  3168. Exit;
  3169. end;
  3170. if not FSSCNOn then begin
  3171. SendCmd(SSCN_ON, SSCN_OK_REPLY);
  3172. FSSCNOn := True;
  3173. end;
  3174. end;
  3175. procedure TIdFTP.ClearSSCN;
  3176. begin
  3177. if FSSCNOn then begin
  3178. SendCmd(SSCN_OFF, SSCN_OK_REPLY);
  3179. end;
  3180. end;
  3181. procedure TIdFTP.SetClientInfo(const AValue: TIdFTPClientIdentifier);
  3182. begin
  3183. FClientInfo.Assign(AValue);
  3184. end;
  3185. procedure TIdFTP.SetCompressor(AValue: TIdZLibCompressorBase);
  3186. var
  3187. // under ARC, convert a weak reference to a strong reference before working with it
  3188. LCompressor: TIdZLibCompressorBase;
  3189. begin
  3190. LCompressor := FCompressor;
  3191. if LCompressor <> AValue then begin
  3192. // under ARC, all weak references to a freed object get nil'ed automatically
  3193. {$IFNDEF USE_OBJECT_ARC}
  3194. if Assigned(LCompressor) then begin
  3195. LCompressor.RemoveFreeNotification(Self);
  3196. end;
  3197. {$ENDIF}
  3198. FCompressor := AValue;
  3199. if Assigned(AValue) then begin
  3200. {$IFNDEF USE_OBJECT_ARC}
  3201. AValue.FreeNotification(Self);
  3202. {$ENDIF}
  3203. end
  3204. else if Connected then begin
  3205. TransferMode(dmStream);
  3206. end;
  3207. end;
  3208. end;
  3209. procedure TIdFTP.GetInternalResponse(AEncoding: IIdTextEncoding = nil);
  3210. var
  3211. LLine: string;
  3212. LResponse: TStringList;
  3213. LReplyCode: string;
  3214. begin
  3215. CheckConnected;
  3216. LResponse := TStringList.Create;
  3217. try
  3218. // Some servers with bugs send blank lines before reply. Dont remember
  3219. // which ones, but I do remember we changed this for a reason
  3220. //
  3221. // RLebeau 9/14/06: this can happen in between lines of the reply as well
  3222. // RLebeau 3/9/09: according to RFC 959, when reading a multi-line reply,
  3223. // we are supposed to look at the first line's reply code and then keep
  3224. // reading until that specific reply code is encountered again, and
  3225. // everything in between is the text. So, do not just look for arbitrary
  3226. // 3-digit values on each line, but instead look for the specific reply
  3227. // code...
  3228. LLine := IOHandler.ReadLnWait(MaxInt, AEncoding);
  3229. LResponse.Add(LLine);
  3230. if CharEquals(LLine, 4, '-') then begin
  3231. LReplyCode := Copy(LLine, 1, 3);
  3232. repeat
  3233. LLine := IOHandler.ReadLnWait(MaxInt, AEncoding);
  3234. LResponse.Add(LLine);
  3235. until TIdReplyFTP(FLastCmdResult).IsEndReply(LReplyCode, LLine);
  3236. end;
  3237. //Note that FormattedReply uses an assign in it's property set method.
  3238. FLastCmdResult.FormattedReply := LResponse;
  3239. finally
  3240. FreeAndNil(LResponse);
  3241. end;
  3242. end;
  3243. function TIdFTP.{$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(
  3244. const AResponse: Int16; const AAllowedResponses: array of Int16): Int16;
  3245. begin
  3246. // any FTP command can return a 421 reply if the server is going to shut
  3247. // down the command connection. This way, we can close the connection
  3248. // immediately instead of waiting for a future action that would raise
  3249. // an EIdConnClosedGracefully exception instead...
  3250. if AResponse = 421 then
  3251. begin
  3252. // check if the caller explicitally wants to handle 421 replies...
  3253. if High(AAllowedResponses) > -1 then begin
  3254. if PosInSmallIntArray(AResponse, AAllowedResponses) <> -1 then begin
  3255. Result := AResponse;
  3256. Exit;
  3257. end;
  3258. end;
  3259. Disconnect(False);
  3260. if IOHandler <> nil then begin
  3261. IOHandler.InputBuffer.Clear;
  3262. end;
  3263. RaiseExceptionForLastCmdResult;
  3264. end;
  3265. Result := inherited {$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(AResponse, AAllowedResponses);
  3266. end;
  3267. function TIdFTP.GetReplyClass: TIdReplyClass;
  3268. begin
  3269. Result := TIdReplyFTP;
  3270. end;
  3271. procedure TIdFTP.SetIPVersion(const AValue: TIdIPVersion);
  3272. begin
  3273. if AValue <> FIPVersion then begin
  3274. inherited SetIPVersion(AValue);
  3275. if IPVersion = Id_IPv6 then begin
  3276. UseExtensionDataPort := True;
  3277. end;
  3278. end;
  3279. end;
  3280. class function TIdFTP.InternalEncryptedTLSFXP(AFromSite, AToSite: TIdFTP;
  3281. const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean;
  3282. {
  3283. SiteToSiteUpload
  3284. From: PASV To: PORT - ATargetUsesPasv = False
  3285. From: RETR To: STOR
  3286. SiteToSiteDownload
  3287. From: PORT To: PASV - ATargetUsesPasv = True
  3288. From: RETR To: STOR
  3289. To do FXP transfers with TLS FTP, you have to have one computer do the
  3290. TLS handshake as a client (ssl_connect). Thus, one of the following conditions must be meet.
  3291. 1) SSCN must be supported on one of the FTP servers
  3292. or
  3293. 2) If IPv4 is used, the computer receiving a "PASV" command must support
  3294. CPSV. CPSV will NOT work with IPv6.
  3295. IMAO, when doing FXP transfers, you should use SSCN whenever possible as
  3296. SSCN will support IPv6 and SSCN may be in wider use than CPSV. CPSV should
  3297. only be used as a fallback if SSCN isn't supported by both servers and IPv4
  3298. is being used.
  3299. }
  3300. var
  3301. LIP : String;
  3302. LPort : TIdPort;
  3303. begin
  3304. Result := True;
  3305. if AFromSite.SetSSCNToOn then begin
  3306. AToSite.ClearSSCN;
  3307. end
  3308. else if AToSite.SetSSCNToOn then begin
  3309. AFromSite.ClearSSCN;
  3310. end
  3311. else if AToSite.IPVersion = Id_IPv4 then begin
  3312. if ATargetUsesPasv then begin
  3313. AToSite.SendCPassive(LIP, LPort);
  3314. AFromSite.SendPort(LIP, LPort);
  3315. end else begin
  3316. AFromSite.SendCPassive(LIP, LPort);
  3317. AToSite.SendPort(LIP, LPort);
  3318. end;
  3319. end;
  3320. FXPSendFile(AFromSite, AToSite, ASourceFile, ADestFile);
  3321. end;
  3322. class function TIdFTP.InternalUnencryptedFXP(AFromSite, AToSite: TIdFTP;
  3323. const ASourceFile, ADestFile: String; const ATargetUsesPasv : Boolean): Boolean;
  3324. {
  3325. SiteToSiteUpload
  3326. From: PASV To: PORT - ATargetUsesPasv = False
  3327. From: RETR To: STOR
  3328. SiteToSiteDownload
  3329. From: PORT To: PASV - ATargetUsesPasv = True
  3330. From: RETR To: STOR
  3331. }
  3332. begin
  3333. FXPSetTransferPorts(AFromSite, AToSite, ATargetUsesPasv);
  3334. FXPSendFile(AFromSite, AToSite, ASourceFile, ADestFile);
  3335. Result := True;
  3336. end;
  3337. class function TIdFTP.ValidateInternalIsTLSFXP(AFromSite, AToSite: TIdFTP;
  3338. const ATargetUsesPasv : Boolean): Boolean;
  3339. {
  3340. SiteToSiteUpload
  3341. From: PASV To: PORT - ATargetUsesPasv = False
  3342. From: RETR To: STOR
  3343. SiteToSiteDownload
  3344. From: PORT To: PASV - ATargetUsesPasv = True
  3345. From: RETR To: STOR
  3346. This will raise an exception if FXP can not be done. Result = True for encrypted
  3347. or False for unencrypted.
  3348. Note:
  3349. The following is required:
  3350. SiteToSiteUpload
  3351. Source must do P
  3352. }
  3353. begin
  3354. if ATargetUsesPasv then begin
  3355. if AToSite.UsingNATFastTrack then begin
  3356. raise EIdFTPSToSNATFastTrack.Create(RSFTPNoSToSWithNATFastTrack);
  3357. end;
  3358. end else begin
  3359. if AFromSite.UsingNATFastTrack then begin
  3360. raise EIdFTPSToSNATFastTrack.Create(RSFTPNoSToSWithNATFastTrack);
  3361. end;
  3362. end;
  3363. if AFromSite.IPVersion <> AToSite.IPVersion then begin
  3364. raise EIdFTPStoSIPProtoMustBeSame.Create(RSFTPSToSProtosMustBeSame);
  3365. end;
  3366. if AFromSite.CurrentTransferMode <> AToSite.CurrentTransferMode then begin
  3367. raise EIdFTPSToSTransModesMustBeSame.Create(RSFTPSToSTransferModesMusbtSame);
  3368. end;
  3369. if AFromSite.FUsingSFTP <> AToSite.FUsingSFTP then begin
  3370. raise EIdFTPSToSNoDataProtection.Create(RSFTPSToSNoDataProtection);
  3371. end;
  3372. Result := AFromSite.FUsingSFTP and AToSite.FUsingSFTP;
  3373. if Result then begin
  3374. if not (AFromSite.IsExtSupported('SSCN') or AToSite.IsExtSupported('SSCN')) then begin {do not localize}
  3375. //Second chance fallback, is CPSV supported on the server where PASV would
  3376. // be sent
  3377. if AToSite.IPVersion = Id_IPv4 then begin
  3378. if ATargetUsesPasv then begin
  3379. if not AToSite.IsExtSupported('CPSV') then begin {do not localize}
  3380. raise EIdFTPSToSNATFastTrack.Create(RSFTPSToSSSCNNotSupported);
  3381. end;
  3382. end else begin
  3383. if not AFromSite.IsExtSupported('CPSV') then begin {do not localize}
  3384. raise EIdFTPSToSNATFastTrack.Create(RSFTPSToSSSCNNotSupported);
  3385. end;
  3386. end;
  3387. end;
  3388. end;
  3389. end;
  3390. end;
  3391. class procedure TIdFTP.FXPSendFile(AFromSite, AToSite: TIdFTP; const ASourceFile, ADestFile: String);
  3392. var
  3393. LDestFile : String;
  3394. begin
  3395. LDestFile := ADestFile;
  3396. if LDestFile = '' then begin
  3397. LDestFile := ASourceFile;
  3398. end;
  3399. AToSite.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('STOR ' + LDestFile, [110, 125, 150]); {do not localize}
  3400. try
  3401. AFromSite.{$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}('RETR ' + ASourceFile, [110, 125, 150]); {do not localize}
  3402. except
  3403. AToSite.Abort;
  3404. raise;
  3405. end;
  3406. AToSite.GetInternalResponse;
  3407. AFromSite.GetInternalResponse;
  3408. AToSite.{$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(AToSite.LastCmdResult.NumericCode, [225, 226, 250]);
  3409. AFromSite.{$IFDEF OVERLOADED_OPENARRAY_BUG}CheckResponseArr{$ELSE}CheckResponse{$ENDIF}(AFromSite.LastCmdResult.NumericCode, [225, 226, 250]);
  3410. end;
  3411. class procedure TIdFTP.FXPSetTransferPorts(AFromSite, AToSite: TIdFTP; const ATargetUsesPasv: Boolean);
  3412. var
  3413. LIP : String;
  3414. LPort : TIdPort;
  3415. {
  3416. {
  3417. SiteToSiteUpload
  3418. From: PASV To: PORT - ATargetUsesPasv = False
  3419. From: RETR To: STOR
  3420. SiteToSiteDownload
  3421. From: PORT To: PASV - ATargetUsesPasv = True
  3422. From: RETR To: STOR
  3423. }
  3424. begin
  3425. if ATargetUsesPasv then begin
  3426. if AToSite.UsingExtDataPort then begin
  3427. AToSite.SendEPassive(LIP, LPort);
  3428. end else begin
  3429. AToSite.SendPassive(LIP, LPort);
  3430. end;
  3431. if AFromSite.UsingExtDataPort then begin
  3432. AFromSite.SendEPort(LIP, LPort, AToSite.IPVersion);
  3433. end else begin
  3434. AFromSite.SendPort(LIP, LPort);
  3435. end;
  3436. end else begin
  3437. if AFromSite.UsingExtDataPort then begin
  3438. AFromSite.SendEPassive(LIP, LPort);
  3439. end else begin
  3440. AFromSite.SendPassive(LIP, LPort);
  3441. end;
  3442. if AToSite.UsingExtDataPort then begin
  3443. AToSite.SendEPort(LIP, LPort, AFromSite.IPVersion);
  3444. end else begin
  3445. AToSite.SendPort(LIP, LPort);
  3446. end;
  3447. end;
  3448. end;
  3449. {Note about SetTime procedures:
  3450. The first syntax is one used by current Serv-U versions and servers that report "MDTM YYYYMMDDHHMMSS[+-TZ];filename " in their FEAT replies is:
  3451. 1) MDTM [Time in GMT format] Filename
  3452. 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:
  3453. 2) MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename
  3454. and then there is the classic
  3455. 3) MDTM [local timestamp] Filename
  3456. 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
  3457. Indy would use are:
  3458. Syntax 1:
  3459. 1) MDTM 0103220000 MyFile.exe  (notice the 22 hour)
  3460. Syntax 2:
  3461. 2) MDTM 0103170000-300 MyFile.exe (notice the 17 hour and the -300 offset)
  3462. Syntax 3;
  3463. 3) MDTM 0103170000 MyFile.exe (notice the 17 hour)
  3464. Note from:
  3465. http://www.ftpvoyager.com/releasenotes10x.asp
  3466. ====
  3467. Added support for RFC change and the MDTM. MDTM requires sending the server
  3468. GMT (UTC) instead of a "fixed" date and time. FTP Voyager supports this with
  3469. Serv-U automatically by checking the Serv-U version number and by checking the
  3470. response to the FEAT command for MDTM. Servers returning "MDTM" or
  3471. "MDTM YYYYMMDDHHMMSS[+-TZ] filename" will use the old method. Servers
  3472. returning "MDTM YYYYMMDDHHMMSS" only will use the new method where the date a
  3473. and time is GMT (UTC).
  3474. ===
  3475. }
  3476. procedure TIdFTP.SetModTime(const AFileName: String; const ALocalTime: TDateTime);
  3477. var
  3478. LCmd: String;
  3479. begin
  3480. //use MFMT instead of MDTM because that always takes the time as Universal
  3481. //time (the most accurate).
  3482. if IsExtSupported('MFMT') then begin {do not localize}
  3483. LCmd := 'MFMT ' + FTPLocalDateTimeToMLS(ALocalTime, False) + ' ' + AFileName; {do not localize}
  3484. end
  3485. //Syntax 1 - MDTM [Time in GMT format] Filename
  3486. else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ];filename') > 0) or IsIIS then begin {do not localize}
  3487. //we use the new method
  3488. LCmd := 'MDTM ' + FTPLocalDateTimeToMLS(ALocalTime, False) + ' ' + AFileName; {do not localize}
  3489. end
  3490. //Syntax 2 - MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename
  3491. //use old method for old versions of Serv-U and BPFTP Server
  3492. else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ] filename') > 0) or IsOldServU or IsBPFTP then begin {do not localize}
  3493. LCmd := 'MDTM '+ FTPDateTimeToMDTMD(ALocalTime, False, True) + ' ' + AFileName; {do not localize}
  3494. end
  3495. //syntax 3 - MDTM [local timestamp] Filename
  3496. else if FTZInfo.FGMTOffsetAvailable then begin
  3497. //send it relative to the server's time-zone
  3498. LCmd := 'MDTM '+ FTPDateTimeToMDTMD(LocalTimeToUTCTime(ALocalTime) + FTZInfo.FGMTOffset, False, False) + ' ' + AFileName; {do not localize}
  3499. end
  3500. else begin
  3501. LCmd := 'MDTM '+ FTPDateTimeToMDTMD(ALocalTime, False, False) + ' ' + AFileName; {do not localize}
  3502. end;
  3503. // When using MDTM, Titan FTP 5 returns 200 and vsFTPd returns 213
  3504. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(LCmd, [200, 213, 253]);
  3505. end;
  3506. {
  3507. Note from:
  3508. http://www.ftpvoyager.com/releasenotes10x.asp
  3509. ====
  3510. Added support for RFC change and the MDTM. MDTM requires sending the server
  3511. GMT (UTC) instead of a "fixed" date and time. FTP Voyager supports this with
  3512. Serv-U automatically by checking the Serv-U version number and by checking the
  3513. response to the FEAT command for MDTM. Servers returning "MDTM" or
  3514. "MDTM YYYYMMDDHHMMSS[+-TZ] filename" will use the old method. Servers
  3515. returning "MDTM YYYYMMDDHHMMSS" only will use the new method where the date a
  3516. and time is GMT (UTC).
  3517. ===
  3518. }
  3519. procedure TIdFTP.SetModTimeGMT(const AFileName : String; const AGMTTime: TDateTime);
  3520. var
  3521. LCmd: String;
  3522. begin
  3523. //use MFMT instead of MDTM because that always takes the time as Universal
  3524. //time (the most accurate).
  3525. if IsExtSupported('MFMT') then begin {do not localize}
  3526. LCmd := 'MFMT ' + FTPGMTDateTimeToMLS(AGMTTime) + ' ' + AFileName; {do not localize}
  3527. end
  3528. //Syntax 1 - MDTM [Time in GMT format] Filename
  3529. else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ];filename') > 0) or IsIIS then begin {do not localize}
  3530. //we use the new method
  3531. LCmd := 'MDTM ' + FTPGMTDateTimeToMLS(AGMTTime, False) + ' ' + AFileName; {do not localize}
  3532. end
  3533. //Syntax 2 - MDTM yyyymmddhhmmss[+-minutes from Universal Time] Filename
  3534. //use old method for old versions of Serv-U and BPFTP Server
  3535. else if (IndexOfFeatLine('MDTM YYYYMMDDHHMMSS[+-TZ] filename') > 0) or IsOldServU or IsBPFTP then begin {do not localize}
  3536. LCmd := 'MDTM '+ FTPDateTimeToMDTMD(UTCTimeToLocalTime(AGMTTime), False, True) + ' ' + AFileName; {do not localize}
  3537. end
  3538. //syntax 3 - MDTM [local timestamp] Filename
  3539. else if FTZInfo.FGMTOffsetAvailable then begin
  3540. //send it relative to the server's time-zone
  3541. LCmd := 'MDTM '+ FTPDateTimeToMDTMD(AGMTTime + FTZInfo.FGMTOffset, False, False) + ' ' + AFileName; {do not localize}
  3542. end
  3543. else begin
  3544. LCmd := 'MDTM '+ FTPDateTimeToMDTMD(UTCTimeToLocalTime(AGMTTime), False, False) + ' ' + AFileName; {do not localize}
  3545. end;
  3546. // When using MDTM, Titan FTP 5 returns 200 and vsFTPd returns 213
  3547. {$IFDEF OVERLOADED_OPENARRAY_BUG}SendCmdArr{$ELSE}SendCmd{$ENDIF}(LCmd, [200, 213, 253]);
  3548. end;
  3549. {Improvement from Tobias Giesen http://www.superflexible.com
  3550. His notation is below:
  3551. "here's a fix for TIdFTP.IndexOfFeatLine. It does not work the
  3552. way it is used in TIdFTP.SetModTime, because it only
  3553. compares the first word of the FeatLine." }
  3554. function TIdFTP.IndexOfFeatLine(const AFeatLine: String): Integer;
  3555. var
  3556. LBuf : String;
  3557. LNoSpaces: Boolean;
  3558. begin
  3559. LNoSpaces := IndyPos(' ', AFeatLine) = 0;
  3560. for Result := 0 to FCapabilities.Count -1 do begin
  3561. LBuf := TrimLeft(FCapabilities[Result]);
  3562. // RLebeau: why Fetch() if no spaces are present?
  3563. if LNoSpaces then begin
  3564. LBuf := Fetch(LBuf);
  3565. end;
  3566. if TextIsSame(AFeatLine, LBuf) then begin
  3567. Exit;
  3568. end;
  3569. end;
  3570. Result := -1;
  3571. end;
  3572. { TIdFTPTZInfo }
  3573. procedure TIdFTPTZInfo.Assign(Source: TPersistent);
  3574. var
  3575. LSource: TIdFTPTZInfo;
  3576. begin
  3577. if Source is TIdFTPTZInfo then begin
  3578. LSource := TIdFTPTZInfo(Source);
  3579. FGMTOffset := LSource.GMTOffset;
  3580. FGMTOffsetAvailable := LSource.GMTOffsetAvailable;
  3581. end else begin
  3582. inherited Assign(Source);
  3583. end;
  3584. end;
  3585. function TIdFTP.IsSiteZONESupported: Boolean;
  3586. var
  3587. LFacts : TStrings;
  3588. i : Integer;
  3589. begin
  3590. Result := False;
  3591. if IsServerMDTZAndListTForm then begin
  3592. Result := True;
  3593. Exit;
  3594. end;
  3595. LFacts := TStringList.Create;
  3596. try
  3597. ExtractFeatFacts('SITE', LFacts);
  3598. for i := 0 to LFacts.Count-1 do begin
  3599. if TextIsSame(LFacts[i], 'ZONE') then begin {do not localize}
  3600. Result := True;
  3601. Exit;
  3602. end;
  3603. end;
  3604. finally
  3605. FreeAndNil(LFacts);
  3606. end;
  3607. end;
  3608. procedure TIdFTP.SetTZInfo(const Value: TIdFTPTZInfo);
  3609. begin
  3610. FTZInfo.Assign(Value);
  3611. end;
  3612. function TIdFTP.IsOldServU: Boolean;
  3613. begin
  3614. Result := TextStartsWith(FServerDesc, 'Serv-U '); {do not localize}
  3615. end;
  3616. function TIdFTP.IsBPFTP : Boolean;
  3617. begin
  3618. Result := TextStartsWith(FServerDesc, 'BPFTP Server '); {do not localize}
  3619. end;
  3620. function TIdFTP.IsTitan : Boolean;
  3621. begin
  3622. Result := TextStartsWith(FServerDesc, 'TitanFTP server ') or {do not localize}
  3623. TextStartsWith(FServerDesc, 'Titan FTP Server '); {do not localize}
  3624. end;
  3625. function TIdFTP.IsWSFTP : Boolean;
  3626. begin
  3627. Result := IndyPos('WS_FTP Server', FServerDesc) > 0; {do not localize}
  3628. end;
  3629. function TIdFTP.IsIIS: Boolean;
  3630. begin
  3631. Result := TextStartsWith(FServerDesc, 'Microsoft FTP Service'); {do not localize}
  3632. end;
  3633. function TIdFTP.IsServerMDTZAndListTForm: Boolean;
  3634. begin
  3635. Result := IsOldServU or IsBPFTP or IsTitan;
  3636. end;
  3637. procedure TIdFTP.Notification(AComponent: TComponent; Operation: TOperation);
  3638. begin
  3639. if (Operation = opRemove) and (AComponent = FCompressor) then begin
  3640. SetCompressor(nil);
  3641. end;
  3642. inherited Notification(AComponent, Operation);
  3643. end;
  3644. procedure TIdFTP.SendPret(const ACommand: String);
  3645. begin
  3646. if IsExtSupported('PRET') then begin {do not localize}
  3647. //note that we don't check for success or failure here
  3648. //as some servers might fail and then succede with the transfer.
  3649. //Pret might not work for some commands.
  3650. SendCmd('PRET ' + ACommand); {do not localize}
  3651. end;
  3652. end;
  3653. procedure TIdFTP.List;
  3654. begin
  3655. List(nil);
  3656. end;
  3657. procedure TIdFTP.List(const ASpecifier: string; ADetails: Boolean);
  3658. begin
  3659. List(nil, ASpecifier, ADetails);
  3660. end;
  3661. procedure TIdFTP.DoOnBannerAfterLogin(AText: TStrings);
  3662. begin
  3663. if Assigned(OnBannerAfterLogin) then begin
  3664. OnBannerAfterLogin(Self, AText.Text);
  3665. end;
  3666. end;
  3667. procedure TIdFTP.DoOnBannerBeforeLogin(AText: TStrings);
  3668. begin
  3669. if Assigned(OnBannerBeforeLogin) then begin
  3670. OnBannerBeforeLogin(Self, AText.Text);
  3671. end;
  3672. end;
  3673. procedure TIdFTP.DoOnBannerWarning(AText: TStrings);
  3674. begin
  3675. if Assigned(OnBannerWarning) then begin
  3676. OnBannerWarning(Self, AText.Text);
  3677. end;
  3678. end;
  3679. procedure TIdFTP.SetDataPortProtection(AValue: TIdFTPDataPortSecurity);
  3680. begin
  3681. if IsLoading then begin
  3682. FDataPortProtection := AValue;
  3683. Exit;
  3684. end;
  3685. if FDataPortProtection <> AValue then begin
  3686. if FUseTLS = utNoTLSSupport then begin
  3687. raise EIdFTPNoDataPortProtectionWOEncryption.Create(RSFTPNoDataPortProtectionWOEncryption);
  3688. end;
  3689. if FUsingCCC then begin
  3690. raise EIdFTPNoDataPortProtectionAfterCCC.Create(RSFTPNoDataPortProtectionAfterCCC);
  3691. end;
  3692. FDataPortProtection := AValue;
  3693. end;
  3694. end;
  3695. procedure TIdFTP.SetAUTHCmd(const AValue : TAuthCmd);
  3696. begin
  3697. if IsLoading then begin
  3698. FAUTHCmd := AValue;
  3699. Exit;
  3700. end;
  3701. if FAUTHCmd <> AValue then begin
  3702. if FUseTLS = utNoTLSSupport then begin
  3703. raise EIdFTPNoAUTHWOSSL.Create(RSFTPNoAUTHWOSSL);
  3704. end;
  3705. if FUsingSFTP then begin
  3706. raise EIdFTPCanNotSetAUTHCon.Create(RSFTPNoAUTHCon);
  3707. end;
  3708. FAUTHCmd := AValue;
  3709. end;
  3710. end;
  3711. procedure TIdFTP.SetDefStringEncoding(AValue: IIdTextEncoding);
  3712. begin
  3713. FDefStringEncoding := AValue;
  3714. if IOHandler <> nil then begin
  3715. IOHandler.DefStringEncoding := FDefStringEncoding;
  3716. end;
  3717. end;
  3718. procedure TIdFTP.SetUseTLS(AValue: TIdUseTLS);
  3719. begin
  3720. inherited SetUseTLS(AValue);
  3721. if IsLoading then begin
  3722. Exit;
  3723. end;
  3724. if AValue = utNoTLSSupport then begin
  3725. FDataPortProtection := Id_TIdFTP_DataPortProtection;
  3726. FUseCCC := DEF_Id_FTP_UseCCC;
  3727. FAUTHCmd := DEF_Id_FTP_AUTH_CMD;
  3728. end;
  3729. end;
  3730. procedure TIdFTP.SetUseCCC(const AValue: Boolean);
  3731. begin
  3732. if (not IsLoading) and (FUseTLS = utNoTLSSupport) then begin
  3733. raise EIdFTPNoCCCWOEncryption.Create(RSFTPNoCCCWOEncryption);
  3734. end;
  3735. FUseCCC := AValue;
  3736. end;
  3737. procedure TIdFTP.DoOnRetrievedDir;
  3738. begin
  3739. if Assigned(OnRetrievedDir) then begin
  3740. OnRetrievedDir(Self);
  3741. end;
  3742. end;
  3743. procedure TIdFTP.DoOnDirParseEnd;
  3744. begin
  3745. if Assigned(FOnDirParseEnd) then begin
  3746. FOnDirParseEnd(Self);
  3747. end;
  3748. end;
  3749. procedure TIdFTP.DoOnDirParseStart;
  3750. begin
  3751. if Assigned(FOnDirParseStart) then begin
  3752. FOnDirParseStart(Self);
  3753. end;
  3754. end;
  3755. //we do this to match some WS-FTP Pro firescripts I saw
  3756. function TIdFTP.IsAccountNeeded: Boolean;
  3757. begin
  3758. Result := LastCmdResult.NumericCode = 332;
  3759. if not Result then begin
  3760. if IndyPos('ACCOUNT', LastCmdResult.Text.Text) > 0 then begin {do not localize}
  3761. Result := FAccount <> '';
  3762. end;
  3763. end;
  3764. end;
  3765. //we can use one of three commands for verifying a file or stream
  3766. function TIdFTP.GetSupportsVerification: Boolean;
  3767. begin
  3768. Result := Connected;
  3769. if Result then begin
  3770. Result := TIdHashSHA512.IsAvailable and IsExtSupported('XSHA512');
  3771. if not Result then begin
  3772. Result := TIdHashSHA256.IsAvailable and IsExtSupported('XSHA256');
  3773. end;
  3774. if not Result then begin
  3775. Result := IsExtSupported('XSHA1') or
  3776. (IsExtSupported('XMD5') and (not GetFIPSMode)) or
  3777. IsExtSupported('XCRC');
  3778. end;
  3779. end;
  3780. end;
  3781. function TIdFTP.VerifyFile(const ALocalFile, ARemoteFile: String; const AStartPoint, AByteCount: TIdStreamSize): Boolean;
  3782. var
  3783. LLocalStream: TStream;
  3784. LRemoteFileName : String;
  3785. begin
  3786. LRemoteFileName := ARemoteFile;
  3787. if LRemoteFileName = '' then begin
  3788. LRemoteFileName := ExtractFileName(ALocalFile);
  3789. end;
  3790. LLocalStream := TIdReadFileExclusiveStream.Create(ALocalFile);
  3791. try
  3792. Result := VerifyFile(LLocalStream, LRemoteFileName, AStartPoint, AByteCount);
  3793. finally
  3794. FreeAndNil(LLocalStream);
  3795. end;
  3796. end;
  3797. {
  3798. This procedure can use three possible commands to verify file integriety and the
  3799. syntax does very amoung these. The commands are:
  3800. XSHA1 - get SHA1 checksum for a file or file part
  3801. XMD5 - get MD5 checksum for a file or file part
  3802. XCRC - get CRC32 checksum
  3803. The command preference is from first to last (going from longest length to shortest).
  3804. }
  3805. function TIdFTP.VerifyFile(ALocalFile: TStream; const ARemoteFile: String;
  3806. const AStartPoint, AByteCount: TIdStreamSize): Boolean;
  3807. var
  3808. LRemoteCRC : String;
  3809. LLocalCRC : String;
  3810. LCmd : String;
  3811. LRemoteFile: String;
  3812. LStartPoint : TIdStreamSize;
  3813. LByteCount : TIdStreamSize; //used instead of AByteCount so we don't exceed the file size
  3814. LHashClass: TIdHashClass;
  3815. LHash: TIdHash;
  3816. begin
  3817. LLocalCRC := '';
  3818. LRemoteCRC := '';
  3819. if AStartPoint > -1 then begin
  3820. ALocalFile.Position := AStartPoint;
  3821. end;
  3822. LStartPoint := ALocalFile.Position;
  3823. LByteCount := ALocalFile.Size - LStartPoint;
  3824. if (LByteCount > AByteCount) and (AByteCount > 0) then begin
  3825. LByteCount := AByteCount;
  3826. end;
  3827. //just in case the server doesn't support file names in quotes.
  3828. if IndyPos(' ', ARemoteFile) > 0 then begin
  3829. LRemoteFile := '"' + ARemoteFile + '"';
  3830. end else begin
  3831. LRemoteFile := ARemoteFile;
  3832. end;
  3833. if TIdHashSHA512.IsAvailable and IsExtSupported('XSHA512') then begin
  3834. //XSHA256 <sp> pathname [<sp> startposition <sp> endposition]
  3835. LCmd := 'XSHA512 ' + LRemoteFile;
  3836. if AByteCount > 0 then begin
  3837. LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
  3838. end
  3839. else if AStartPoint > 0 then begin
  3840. LCmd := LCmd + ' ' + IntToStr(LStartPoint);
  3841. end;
  3842. LHashClass := TIdHashSHA512;
  3843. end
  3844. else if TIdHashSHA256.IsAvailable and IsExtSupported('XSHA256') then begin
  3845. //XSHA256 <sp> pathname [<sp> startposition <sp> endposition]
  3846. LCmd := 'XSHA256 ' + LRemoteFile;
  3847. if AByteCount > 0 then begin
  3848. LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
  3849. end
  3850. else if AStartPoint > 0 then begin
  3851. LCmd := LCmd + ' ' + IntToStr(LStartPoint);
  3852. end;
  3853. LHashClass := TIdHashSHA256;
  3854. end
  3855. else if IsExtSupported('XSHA1') then begin
  3856. //XMD5 "filename" startpos endpos
  3857. //I think there's two syntaxes to this:
  3858. //
  3859. //Raiden Syntax if FEAT line contains " XMD5 filename;start;end"
  3860. //
  3861. //or what's used by some other servers if "FEAT line contains XMD5"
  3862. //
  3863. //XCRC "filename" [startpos] [number of bytes to calc]
  3864. if IndexOfFeatLine('XSHA1 filename;start;end') > -1 then begin
  3865. LCmd := 'XSHA1 ' + LRemoteFile + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LStartPoint + LByteCount-1);
  3866. end else
  3867. begin
  3868. //BlackMoon FTP Server uses this one.
  3869. LCmd := 'XSHA1 ' + LRemoteFile;
  3870. if AByteCount > 0 then begin
  3871. LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
  3872. end
  3873. else if AStartPoint > 0 then begin
  3874. LCmd := LCmd + ' ' + IntToStr(LStartPoint);
  3875. end;
  3876. end;
  3877. LHashClass := TIdHashSHA1;
  3878. end
  3879. else if IsExtSupported('XMD5') and (not GetFIPSMode) then begin
  3880. //XMD5 "filename" startpos endpos
  3881. //I think there's two syntaxes to this:
  3882. //
  3883. //Raiden Syntax if FEAT line contains " XMD5 filename;start;end"
  3884. //
  3885. //or what's used by some other servers if "FEAT line contains XMD5"
  3886. //
  3887. //XCRC "filename" [startpos] [number of bytes to calc]
  3888. if IndexOfFeatLine('XMD5 filename;start;end') > -1 then begin
  3889. LCmd := 'XMD5 ' + LRemoteFile + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LStartPoint + LByteCount-1);
  3890. end else
  3891. begin
  3892. //BlackMoon FTP Server uses this one.
  3893. LCmd := 'XMD5 ' + LRemoteFile;
  3894. if AByteCount > 0 then begin
  3895. LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
  3896. end
  3897. else if AStartPoint > 0 then begin
  3898. LCmd := LCmd + ' ' + IntToStr(LStartPoint);
  3899. end;
  3900. end;
  3901. LHashClass := TIdHashMessageDigest5;
  3902. end else
  3903. begin
  3904. LCmd := 'XCRC ' + LRemoteFile;
  3905. if AByteCount > 0 then begin
  3906. LCmd := LCmd + ' ' + IntToStr(LStartPoint) + ' ' + IntToStr(LByteCount);
  3907. end
  3908. else if AStartPoint > 0 then begin
  3909. LCmd := LCmd + ' ' + IntToStr(LStartPoint);
  3910. end;
  3911. LHashClass := TIdHashCRC32;
  3912. end;
  3913. LHash := LHashClass.Create;
  3914. try
  3915. LLocalCRC := LHash.HashStreamAsHex(ALocalFile, LStartPoint, LByteCount);
  3916. finally
  3917. LHash.Free;
  3918. end;
  3919. if SendCmd(LCmd) = 250 then begin
  3920. LRemoteCRC := Trim(LastCmdResult.Text.Text);
  3921. IdDelete(LRemoteCRC, 1, IndyPos(' ', LRemoteCRC)); // delete the response
  3922. Result := TextIsSame(LLocalCRC, LRemoteCRC);
  3923. end else begin
  3924. Result := False;
  3925. end;
  3926. end;
  3927. end.