IdFTP.pas 153 KB

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