FtpFileSystem.cpp 142 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #ifndef NO_FILEZILLA
  5. //---------------------------------------------------------------------------
  6. #include <list>
  7. #define MPEXT
  8. #include "FtpFileSystem.h"
  9. #include "FileZillaIntf.h"
  10. #include "Common.h"
  11. #include "Exceptions.h"
  12. #include "Terminal.h"
  13. #include "TextsCore.h"
  14. #include "TextsFileZilla.h"
  15. #include "HelpCore.h"
  16. #include "Security.h"
  17. #include <StrUtils.hpp>
  18. #include <DateUtils.hpp>
  19. #include <openssl/x509_vfy.h>
  20. //---------------------------------------------------------------------------
  21. #pragma package(smart_init)
  22. //---------------------------------------------------------------------------
  23. #define FILE_OPERATION_LOOP_TERMINAL FTerminal
  24. //---------------------------------------------------------------------------
  25. const int DummyCodeClass = 8;
  26. const int DummyTimeoutCode = 801;
  27. const int DummyCancelCode = 802;
  28. const int DummyDisconnectCode = 803;
  29. //---------------------------------------------------------------------------
  30. class TFileZillaImpl : public TFileZillaIntf
  31. {
  32. public:
  33. __fastcall TFileZillaImpl(TFTPFileSystem * FileSystem);
  34. virtual const wchar_t * __fastcall Option(int OptionID) const;
  35. virtual int __fastcall OptionVal(int OptionID) const;
  36. protected:
  37. virtual bool __fastcall DoPostMessage(TMessageType Type, WPARAM wParam, LPARAM lParam);
  38. virtual bool __fastcall HandleStatus(const wchar_t * Status, int Type);
  39. virtual bool __fastcall HandleAsynchRequestOverwrite(
  40. wchar_t * FileName1, size_t FileName1Len, const wchar_t * FileName2,
  41. const wchar_t * Path1, const wchar_t * Path2,
  42. __int64 Size1, __int64 Size2, time_t LocalTime,
  43. bool HasLocalTime, const TRemoteFileTime & RemoteTime, void * UserData, int & RequestResult);
  44. virtual bool __fastcall HandleAsynchRequestVerifyCertificate(
  45. const TFtpsCertificateData & Data, int & RequestResult);
  46. virtual bool __fastcall HandleAsynchRequestNeedPass(
  47. struct TNeedPassRequestData & Data, int & RequestResult);
  48. virtual bool __fastcall HandleListData(const wchar_t * Path, const TListDataEntry * Entries,
  49. unsigned int Count);
  50. virtual bool __fastcall HandleTransferStatus(bool Valid, __int64 TransferSize,
  51. __int64 Bytes, int Percent, int TimeElapsed, int TimeLeft, int TransferRate,
  52. bool FileTransfer);
  53. virtual bool __fastcall HandleReply(int Command, unsigned int Reply);
  54. virtual bool __fastcall HandleCapabilities(TFTPServerCapabilities * ServerCapabilities);
  55. virtual bool __fastcall CheckError(int ReturnCode, const wchar_t * Context);
  56. virtual void PreserveDownloadFileTime(HANDLE Handle, void * UserData);
  57. virtual bool GetFileModificationTimeInUtc(const wchar_t * FileName, struct tm & Time);
  58. virtual wchar_t * LastSysErrorMessage();
  59. private:
  60. TFTPFileSystem * FFileSystem;
  61. };
  62. //---------------------------------------------------------------------------
  63. __fastcall TFileZillaImpl::TFileZillaImpl(TFTPFileSystem * FileSystem) :
  64. TFileZillaIntf(),
  65. FFileSystem(FileSystem)
  66. {
  67. }
  68. //---------------------------------------------------------------------------
  69. const wchar_t * __fastcall TFileZillaImpl::Option(int OptionID) const
  70. {
  71. return FFileSystem->GetOption(OptionID);
  72. }
  73. //---------------------------------------------------------------------------
  74. int __fastcall TFileZillaImpl::OptionVal(int OptionID) const
  75. {
  76. return FFileSystem->GetOptionVal(OptionID);
  77. }
  78. //---------------------------------------------------------------------------
  79. bool __fastcall TFileZillaImpl::DoPostMessage(TMessageType Type, WPARAM wParam, LPARAM lParam)
  80. {
  81. return FFileSystem->PostMessage(Type, wParam, lParam);
  82. }
  83. //---------------------------------------------------------------------------
  84. bool __fastcall TFileZillaImpl::HandleStatus(const wchar_t * Status, int Type)
  85. {
  86. return FFileSystem->HandleStatus(Status, Type);
  87. }
  88. //---------------------------------------------------------------------------
  89. bool __fastcall TFileZillaImpl::HandleAsynchRequestOverwrite(
  90. wchar_t * FileName1, size_t FileName1Len, const wchar_t * FileName2,
  91. const wchar_t * Path1, const wchar_t * Path2,
  92. __int64 Size1, __int64 Size2, time_t LocalTime,
  93. bool HasLocalTime, const TRemoteFileTime & RemoteTime, void * UserData, int & RequestResult)
  94. {
  95. return FFileSystem->HandleAsynchRequestOverwrite(
  96. FileName1, FileName1Len, FileName2, Path1, Path2, Size1, Size2, LocalTime,
  97. HasLocalTime, RemoteTime, UserData, RequestResult);
  98. }
  99. //---------------------------------------------------------------------------
  100. bool __fastcall TFileZillaImpl::HandleAsynchRequestVerifyCertificate(
  101. const TFtpsCertificateData & Data, int & RequestResult)
  102. {
  103. return FFileSystem->HandleAsynchRequestVerifyCertificate(Data, RequestResult);
  104. }
  105. //---------------------------------------------------------------------------
  106. bool __fastcall TFileZillaImpl::HandleAsynchRequestNeedPass(
  107. struct TNeedPassRequestData & Data, int & RequestResult)
  108. {
  109. return FFileSystem->HandleAsynchRequestNeedPass(Data, RequestResult);
  110. }
  111. //---------------------------------------------------------------------------
  112. bool __fastcall TFileZillaImpl::HandleListData(const wchar_t * Path,
  113. const TListDataEntry * Entries, unsigned int Count)
  114. {
  115. return FFileSystem->HandleListData(Path, Entries, Count);
  116. }
  117. //---------------------------------------------------------------------------
  118. bool __fastcall TFileZillaImpl::HandleTransferStatus(bool Valid, __int64 TransferSize,
  119. __int64 Bytes, int Percent, int TimeElapsed, int TimeLeft, int TransferRate,
  120. bool FileTransfer)
  121. {
  122. return FFileSystem->HandleTransferStatus(Valid, TransferSize, Bytes, Percent,
  123. TimeElapsed, TimeLeft, TransferRate, FileTransfer);
  124. }
  125. //---------------------------------------------------------------------------
  126. bool __fastcall TFileZillaImpl::HandleReply(int Command, unsigned int Reply)
  127. {
  128. return FFileSystem->HandleReply(Command, Reply);
  129. }
  130. //---------------------------------------------------------------------------
  131. bool __fastcall TFileZillaImpl::HandleCapabilities(TFTPServerCapabilities * ServerCapabilities)
  132. {
  133. return FFileSystem->HandleCapabilities(ServerCapabilities);
  134. }
  135. //---------------------------------------------------------------------------
  136. bool __fastcall TFileZillaImpl::CheckError(int ReturnCode, const wchar_t * Context)
  137. {
  138. return FFileSystem->CheckError(ReturnCode, Context);
  139. }
  140. //---------------------------------------------------------------------------
  141. void TFileZillaImpl::PreserveDownloadFileTime(HANDLE Handle, void * UserData)
  142. {
  143. return FFileSystem->PreserveDownloadFileTime(Handle, UserData);
  144. }
  145. //---------------------------------------------------------------------------
  146. bool TFileZillaImpl::GetFileModificationTimeInUtc(const wchar_t * FileName, struct tm & Time)
  147. {
  148. return FFileSystem->GetFileModificationTimeInUtc(FileName, Time);
  149. }
  150. //---------------------------------------------------------------------------
  151. wchar_t * TFileZillaImpl::LastSysErrorMessage()
  152. {
  153. return _wcsdup(::LastSysErrorMessage().c_str());
  154. }
  155. //---------------------------------------------------------------------------
  156. //---------------------------------------------------------------------------
  157. class TMessageQueue : public std::list<std::pair<WPARAM, LPARAM> >
  158. {
  159. };
  160. //---------------------------------------------------------------------------
  161. //---------------------------------------------------------------------------
  162. struct TFileTransferData
  163. {
  164. TFileTransferData()
  165. {
  166. Params = 0;
  167. AutoResume = false;
  168. OverwriteResult = -1;
  169. CopyParam = NULL;
  170. }
  171. UnicodeString FileName;
  172. int Params;
  173. bool AutoResume;
  174. int OverwriteResult;
  175. const TCopyParamType * CopyParam;
  176. TDateTime Modification;
  177. };
  178. //---------------------------------------------------------------------------
  179. const int tfFirstLevel = 0x01;
  180. const int tfAutoResume = 0x02;
  181. const wchar_t CertificateStorageKey[] = L"FtpsCertificates";
  182. const UnicodeString SiteCommand(L"SITE");
  183. const UnicodeString SymlinkSiteCommand(L"SYMLINK");
  184. const UnicodeString CopySiteCommand(L"COPY");
  185. const UnicodeString HashCommand(L"HASH"); // Cerberos + FileZilla servers
  186. const UnicodeString AvblCommand(L"AVBL");
  187. const UnicodeString XQuotaCommand(L"XQUOTA");
  188. //---------------------------------------------------------------------------
  189. struct TSinkFileParams
  190. {
  191. UnicodeString TargetDir;
  192. const TCopyParamType * CopyParam;
  193. int Params;
  194. TFileOperationProgressType * OperationProgress;
  195. bool Skipped;
  196. unsigned int Flags;
  197. };
  198. //---------------------------------------------------------------------------
  199. class TFileListHelper
  200. {
  201. public:
  202. TFileListHelper(TFTPFileSystem * FileSystem, TRemoteFileList * FileList,
  203. bool IgnoreFileList) :
  204. FFileSystem(FileSystem),
  205. FFileList(FFileSystem->FFileList),
  206. FIgnoreFileList(FFileSystem->FIgnoreFileList)
  207. {
  208. FFileSystem->FFileList = FileList;
  209. FFileSystem->FIgnoreFileList = IgnoreFileList;
  210. }
  211. ~TFileListHelper()
  212. {
  213. FFileSystem->FFileList = FFileList;
  214. FFileSystem->FIgnoreFileList = FIgnoreFileList;
  215. }
  216. private:
  217. TFTPFileSystem * FFileSystem;
  218. TRemoteFileList * FFileList;
  219. bool FIgnoreFileList;
  220. };
  221. //---------------------------------------------------------------------------
  222. __fastcall TFTPFileSystem::TFTPFileSystem(TTerminal * ATerminal):
  223. TCustomFileSystem(ATerminal),
  224. FFileZillaIntf(NULL),
  225. FQueueCriticalSection(new TCriticalSection),
  226. FTransferStatusCriticalSection(new TCriticalSection),
  227. FQueueEvent(CreateEvent(NULL, true, false, NULL)),
  228. FQueue(new TMessageQueue),
  229. FReply(0),
  230. FCommandReply(0),
  231. FLastCommand(CMD_UNKNOWN),
  232. FLastResponse(new TStringList()),
  233. FLastErrorResponse(new TStringList()),
  234. FLastError(new TStringList()),
  235. FFeatures(new TStringList()),
  236. FFileList(NULL),
  237. FFileListCache(NULL),
  238. FActive(false),
  239. FOpening(false),
  240. FWaitingForReply(false),
  241. FIgnoreFileList(false),
  242. FOnCaptureOutput(NULL),
  243. FFileSystemInfoValid(false),
  244. FDoListAll(false),
  245. FServerCapabilities(NULL),
  246. FReadCurrentDirectory(false)
  247. {
  248. ResetReply();
  249. FListAll = FTerminal->SessionData->FtpListAll;
  250. FFileSystemInfo.ProtocolBaseName = L"FTP";
  251. FFileSystemInfo.ProtocolName = FFileSystemInfo.ProtocolBaseName;
  252. FTimeoutStatus = LoadStr(IDS_ERRORMSG_TIMEOUT);
  253. FDisconnectStatus = LoadStr(IDS_STATUSMSG_DISCONNECTED);
  254. FServerCapabilities = new TFTPServerCapabilities();
  255. FHashAlgs.reset(new TStringList());
  256. FSupportedCommands.reset(CreateSortedStringList());
  257. FSupportedSiteCommands.reset(CreateSortedStringList());
  258. FChecksumAlgs.reset(new TStringList());
  259. FChecksumCommands.reset(new TStringList());
  260. RegisterChecksumAlgCommand(Sha1ChecksumAlg, L"XSHA1"); // e.g. Cerberos FTP
  261. RegisterChecksumAlgCommand(Sha256ChecksumAlg, L"XSHA256"); // e.g. Cerberos FTP
  262. RegisterChecksumAlgCommand(Sha512ChecksumAlg, L"XSHA512"); // e.g. Cerberos FTP
  263. RegisterChecksumAlgCommand(Md5ChecksumAlg, L"XMD5"); // e.g. Cerberos FTP
  264. RegisterChecksumAlgCommand(Md5ChecksumAlg, L"MD5"); // e.g. Apache FTP
  265. RegisterChecksumAlgCommand(Crc32ChecksumAlg, L"XCRC"); // e.g. Cerberos FTP
  266. }
  267. //---------------------------------------------------------------------------
  268. __fastcall TFTPFileSystem::~TFTPFileSystem()
  269. {
  270. assert(FFileList == NULL);
  271. FFileZillaIntf->Destroying();
  272. // to release memory associated with the messages
  273. DiscardMessages();
  274. delete FFileZillaIntf;
  275. FFileZillaIntf = NULL;
  276. delete FQueue;
  277. FQueue = NULL;
  278. CloseHandle(FQueueEvent);
  279. delete FQueueCriticalSection;
  280. FQueueCriticalSection = NULL;
  281. delete FTransferStatusCriticalSection;
  282. FTransferStatusCriticalSection = NULL;
  283. delete FLastResponse;
  284. FLastResponse = NULL;
  285. delete FLastErrorResponse;
  286. FLastErrorResponse = NULL;
  287. delete FLastError;
  288. FLastError = NULL;
  289. delete FFeatures;
  290. FFeatures = NULL;
  291. delete FServerCapabilities;
  292. FServerCapabilities = NULL;
  293. ResetCaches();
  294. }
  295. //---------------------------------------------------------------------------
  296. void __fastcall TFTPFileSystem::Open()
  297. {
  298. // on reconnect, typically there may be pending status messages from previous session
  299. DiscardMessages();
  300. ResetCaches();
  301. FReadCurrentDirectory = true;
  302. FHomeDirectory = L"";
  303. TSessionData * Data = FTerminal->SessionData;
  304. FSessionInfo.LoginTime = Now();
  305. switch (Data->Ftps)
  306. {
  307. case ftpsNone:
  308. // noop;
  309. break;
  310. case ftpsImplicit:
  311. FSessionInfo.SecurityProtocolName = LoadStr(FTPS_IMPLICIT);
  312. break;
  313. case ftpsExplicitSsl:
  314. case ftpsExplicitTls:
  315. FSessionInfo.SecurityProtocolName = LoadStr(FTPS_EXPLICIT);
  316. break;
  317. default:
  318. FAIL;
  319. break;
  320. }
  321. FLastDataSent = Now();
  322. FMultineResponse = false;
  323. // initialize FZAPI on the first connect only
  324. if (FFileZillaIntf == NULL)
  325. {
  326. FFileZillaIntf = new TFileZillaImpl(this);
  327. try
  328. {
  329. TFileZillaIntf::TLogLevel LogLevel;
  330. switch (FTerminal->Configuration->ActualLogProtocol)
  331. {
  332. default:
  333. case 0:
  334. case 1:
  335. LogLevel = TFileZillaIntf::LOG_PROGRESS;
  336. break;
  337. case 2:
  338. LogLevel = TFileZillaIntf::LOG_INFO;
  339. break;
  340. }
  341. FFileZillaIntf->SetDebugLevel(LogLevel);
  342. FFileZillaIntf->Init();
  343. }
  344. catch(...)
  345. {
  346. delete FFileZillaIntf;
  347. FFileZillaIntf = NULL;
  348. throw;
  349. }
  350. }
  351. UnicodeString HostName = Data->HostNameExpanded;
  352. UnicodeString UserName = Data->UserNameExpanded;
  353. UnicodeString Password = Data->Password;
  354. UnicodeString Account = Data->FtpAccount;
  355. UnicodeString Path = Data->RemoteDirectory;
  356. int ServerType;
  357. switch (Data->Ftps)
  358. {
  359. case ftpsImplicit:
  360. ServerType = TFileZillaIntf::SERVER_FTP_SSL_IMPLICIT;
  361. break;
  362. case ftpsExplicitSsl:
  363. ServerType = TFileZillaIntf::SERVER_FTP_SSL_EXPLICIT;
  364. break;
  365. case ftpsExplicitTls:
  366. ServerType = TFileZillaIntf::SERVER_FTP_TLS_EXPLICIT;
  367. break;
  368. default:
  369. assert(Data->Ftps == ftpsNone);
  370. ServerType = TFileZillaIntf::SERVER_FTP;
  371. break;
  372. }
  373. int Pasv = (Data->FtpPasvMode ? 1 : 2);
  374. int TimeZoneOffset = Data->TimeDifferenceAuto ? 0 : TimeToMinutes(Data->TimeDifference);
  375. int UTF8 = 0;
  376. switch (Data->NotUtf)
  377. {
  378. case asOn:
  379. UTF8 = 2;
  380. break;
  381. case asOff:
  382. UTF8 = 1;
  383. break;
  384. case asAuto:
  385. UTF8 = 0;
  386. break;
  387. }
  388. FPasswordFailed = false;
  389. bool PromptedForCredentials = false;
  390. do
  391. {
  392. FDetectTimeDifference = Data->TimeDifferenceAuto;
  393. FTimeDifference = 0;
  394. ResetFeatures();
  395. FSystem = L"";
  396. FWelcomeMessage = L"";
  397. FFileSystemInfoValid = false;
  398. // TODO: the same for account? it ever used?
  399. // ask for username if it was not specified in advance, even on retry,
  400. // but keep previous one as default,
  401. if (Data->UserNameExpanded.IsEmpty())
  402. {
  403. FTerminal->LogEvent(L"Username prompt (no username provided)");
  404. if (!PromptedForCredentials)
  405. {
  406. FTerminal->Information(LoadStr(FTP_CREDENTIAL_PROMPT), false);
  407. PromptedForCredentials = true;
  408. }
  409. if (!FTerminal->PromptUser(Data, pkUserName, LoadStr(USERNAME_TITLE), L"",
  410. LoadStr(USERNAME_PROMPT2), true, 0, UserName))
  411. {
  412. FTerminal->FatalError(NULL, LoadStr(AUTHENTICATION_FAILED));
  413. }
  414. else
  415. {
  416. FUserName = UserName;
  417. }
  418. }
  419. // On retry ask for password.
  420. // This is particularly important, when stored password is no longer valid,
  421. // so we do not blindly try keep trying it in a loop (possibly causing account lockout)
  422. if (FPasswordFailed)
  423. {
  424. FTerminal->LogEvent(L"Password prompt (last login attempt failed)");
  425. Password = L"";
  426. if (!FTerminal->PromptUser(Data, pkPassword, LoadStr(PASSWORD_TITLE), L"",
  427. LoadStr(PASSWORD_PROMPT), false, 0, Password))
  428. {
  429. FTerminal->FatalError(NULL, LoadStr(AUTHENTICATION_FAILED));
  430. }
  431. }
  432. FPasswordFailed = false;
  433. TAutoFlag OpeningFlag(FOpening);
  434. FActive = FFileZillaIntf->Connect(
  435. HostName.c_str(), Data->PortNumber, UserName.c_str(),
  436. Password.c_str(), Account.c_str(), false, Path.c_str(),
  437. ServerType, Pasv, TimeZoneOffset, UTF8, Data->FtpForcePasvIp,
  438. Data->FtpUseMlsd);
  439. assert(FActive);
  440. try
  441. {
  442. // do not wait for FTP response code as Connect is complex operation
  443. GotReply(WaitForCommandReply(false), REPLY_CONNECT, LoadStr(CONNECTION_FAILED));
  444. Shred(Password);
  445. // we have passed, even if we got 530 on the way (if it is possible at all),
  446. // ignore it
  447. assert(!FPasswordFailed);
  448. FPasswordFailed = false;
  449. }
  450. catch(...)
  451. {
  452. if (FPasswordFailed)
  453. {
  454. FTerminal->Information(LoadStr(FTP_ACCESS_DENIED), false);
  455. }
  456. else
  457. {
  458. // see handling of REPLY_CONNECT in GotReply
  459. FTerminal->Closed();
  460. throw;
  461. }
  462. }
  463. }
  464. while (FPasswordFailed);
  465. // see also TWebDAVFileSystem::CollectTLSSessionInfo()
  466. FSessionInfo.CSCipher = FFileZillaIntf->GetCipherName().c_str();
  467. FSessionInfo.SCCipher = FSessionInfo.CSCipher;
  468. UnicodeString TlsVersionStr = FFileZillaIntf->GetTlsVersionStr().c_str();
  469. AddToList(FSessionInfo.SecurityProtocolName, TlsVersionStr, L", ");
  470. }
  471. //---------------------------------------------------------------------------
  472. void __fastcall TFTPFileSystem::Close()
  473. {
  474. assert(FActive);
  475. bool Result;
  476. if (FFileZillaIntf->Close(FOpening))
  477. {
  478. CHECK(FLAGSET(WaitForCommandReply(false), TFileZillaIntf::REPLY_DISCONNECTED));
  479. Result = true;
  480. }
  481. else
  482. {
  483. // See TFileZillaIntf::Close
  484. Result = FOpening;
  485. }
  486. if (ALWAYS_TRUE(Result))
  487. {
  488. assert(FActive);
  489. Discard();
  490. FTerminal->Closed();
  491. }
  492. }
  493. //---------------------------------------------------------------------------
  494. bool __fastcall TFTPFileSystem::GetActive()
  495. {
  496. return FActive;
  497. }
  498. //---------------------------------------------------------------------------
  499. void __fastcall TFTPFileSystem::CollectUsage()
  500. {
  501. switch (FTerminal->SessionData->Ftps)
  502. {
  503. case ftpsNone:
  504. // noop
  505. break;
  506. case ftpsImplicit:
  507. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPSImplicit");
  508. break;
  509. case ftpsExplicitSsl:
  510. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPSExplicitSSL");
  511. break;
  512. case ftpsExplicitTls:
  513. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPSExplicitTLS");
  514. break;
  515. default:
  516. FAIL;
  517. break;
  518. }
  519. if (FFileZillaIntf->UsingMlsd())
  520. {
  521. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPMLSD");
  522. }
  523. else
  524. {
  525. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPLIST");
  526. }
  527. if (FFileZillaIntf->UsingUtf8())
  528. {
  529. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPUTF8");
  530. }
  531. else
  532. {
  533. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPNonUTF8");
  534. }
  535. if (!CurrentDirectory.IsEmpty() && (CurrentDirectory[1] != L'/'))
  536. {
  537. if (IsUnixStyleWindowsPath(CurrentDirectory))
  538. {
  539. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPWindowsPath");
  540. }
  541. else if ((CurrentDirectory.Length() >= 3) && IsLetter(CurrentDirectory[1]) && (CurrentDirectory[2] == L':') && (CurrentDirectory[3] == L'/'))
  542. {
  543. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPRealWindowsPath");
  544. }
  545. else
  546. {
  547. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPOtherPath");
  548. }
  549. }
  550. UnicodeString TlsVersionStr = FFileZillaIntf->GetTlsVersionStr().c_str();
  551. if (!TlsVersionStr.IsEmpty())
  552. {
  553. FTerminal->CollectTlsUsage(TlsVersionStr);
  554. }
  555. // 220-FileZilla Server version 0.9.43 beta
  556. // 220-written by Tim Kosse ([email protected])
  557. // 220 Please visit http://sourceforge.net/projects/filezilla/
  558. // SYST
  559. // 215 UNIX emulated by FileZilla
  560. // (Welcome message is configurable)
  561. if (ContainsText(FSystem, L"FileZilla"))
  562. {
  563. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPFileZilla");
  564. }
  565. // 220 ProFTPD 1.3.4a Server (Debian) [::ffff:192.168.179.137]
  566. // SYST
  567. // 215 UNIX Type: L8
  568. else if (ContainsText(FWelcomeMessage, L"ProFTPD"))
  569. {
  570. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPProFTPD");
  571. }
  572. // 220 Microsoft FTP Service
  573. // SYST
  574. // 215 Windows_NT
  575. else if (ContainsText(FWelcomeMessage, L"Microsoft FTP Service"))
  576. {
  577. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPIIS");
  578. }
  579. // 220 (vsFTPd 3.0.2)
  580. // SYST
  581. // 215 UNIX Type: L8
  582. // (Welcome message is configurable)
  583. else if (ContainsText(FWelcomeMessage, L"vsFTPd"))
  584. {
  585. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPvsFTPd");
  586. }
  587. // 220 Welcome to Pure-FTPd.
  588. // ...
  589. // SYST
  590. // 215 UNIX Type: L8
  591. else if (ContainsText(FWelcomeMessage, L"Pure-FTPd"))
  592. {
  593. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPPureFTPd");
  594. }
  595. // 220 Titan FTP Server 10.47.1892 Ready.
  596. // ...
  597. // SYST
  598. // 215 UNIX Type: L8
  599. else if (ContainsText(FWelcomeMessage, L"Titan FTP Server"))
  600. {
  601. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPTitan");
  602. }
  603. // 220-Cerberus FTP Server - Home Edition
  604. // 220-This is the UNLICENSED Home Edition and may be used for home, personal use only
  605. // 220-Welcome to Cerberus FTP Server
  606. // 220 Created by Cerberus, LLC
  607. else if (ContainsText(FWelcomeMessage, L"Cerberus FTP Server"))
  608. {
  609. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPCerberus");
  610. }
  611. // 220 Serv-U FTP Server v15.1 ready...
  612. else if (ContainsText(FWelcomeMessage, L"Serv-U FTP Server"))
  613. {
  614. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPServU");
  615. }
  616. else if (ContainsText(FWelcomeMessage, L"WS_FTP"))
  617. {
  618. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPWSFTP");
  619. }
  620. // 220 Welcome to the most popular FTP hosting service! Save on hardware, software, hosting and admin. Share files/folders with read-write permission. Visit http://www.drivehq.com/ftp/;
  621. // ...
  622. // SYST
  623. // 215 UNIX emulated by DriveHQ FTP Server.
  624. else if (ContainsText(FSystem, L"DriveHQ"))
  625. {
  626. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPDriveHQ");
  627. }
  628. // 220 GlobalSCAPE EFT Server (v. 6.0) * UNREGISTERED COPY *
  629. // ...
  630. // SYST
  631. // 215 UNIX Type: L8
  632. else if (ContainsText(FWelcomeMessage, L"GlobalSCAPE"))
  633. {
  634. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPGlobalScape");
  635. }
  636. // 220-<custom message>
  637. // 220 CompleteFTP v 8.1.3
  638. // ...
  639. // SYST
  640. // UNIX Type: L8
  641. else if (ContainsText(FWelcomeMessage, L"CompleteFTP"))
  642. {
  643. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPComplete");
  644. }
  645. // 220 Core FTP Server Version 1.2, build 567, 64-bit, installed 8 days ago Unregistered
  646. // ...
  647. // SYST
  648. // 215 UNIX Type: L8
  649. else if (ContainsText(FWelcomeMessage, L"Core FTP Server"))
  650. {
  651. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPCore");
  652. }
  653. // 220 Service ready for new user.
  654. // ..
  655. // SYST
  656. // 215 UNIX Type: Apache FtpServer
  657. // (e.g. brickftp.com)
  658. else if (ContainsText(FSystem, L"Apache FtpServer"))
  659. {
  660. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPApache");
  661. }
  662. else
  663. {
  664. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPOther");
  665. }
  666. }
  667. //---------------------------------------------------------------------------
  668. void __fastcall TFTPFileSystem::Idle()
  669. {
  670. if (FActive && !FWaitingForReply)
  671. {
  672. PoolForFatalNonCommandReply();
  673. // Keep session alive
  674. if ((FTerminal->SessionData->FtpPingType != ptOff) &&
  675. (double(Now() - FLastDataSent) > double(FTerminal->SessionData->FtpPingIntervalDT) * 4))
  676. {
  677. FTerminal->LogEvent(L"Dummy directory read to keep session alive.");
  678. FLastDataSent = Now();
  679. TRemoteDirectory * Files = new TRemoteDirectory(FTerminal);
  680. try
  681. {
  682. try
  683. {
  684. Files->Directory = CurrentDirectory;
  685. DoReadDirectory(Files);
  686. }
  687. catch(...)
  688. {
  689. // ignore non-fatal errors
  690. // (i.e. current directory may not exist anymore)
  691. if (!FTerminal->Active)
  692. {
  693. throw;
  694. }
  695. }
  696. }
  697. __finally
  698. {
  699. delete Files;
  700. }
  701. }
  702. }
  703. }
  704. //---------------------------------------------------------------------------
  705. void __fastcall TFTPFileSystem::Discard()
  706. {
  707. // remove all pending messages, to get complete log
  708. // note that we need to retry discard on reconnect, as there still may be another
  709. // "disconnect/timeout/..." status messages coming
  710. DiscardMessages();
  711. assert(FActive);
  712. FActive = false;
  713. }
  714. //---------------------------------------------------------------------------
  715. UnicodeString __fastcall TFTPFileSystem::AbsolutePath(UnicodeString Path, bool /*Local*/)
  716. {
  717. // TODO: improve (handle .. etc.)
  718. if (UnixIsAbsolutePath(Path))
  719. {
  720. return Path;
  721. }
  722. else
  723. {
  724. return ::AbsolutePath(FCurrentDirectory, Path);
  725. }
  726. }
  727. //---------------------------------------------------------------------------
  728. UnicodeString __fastcall TFTPFileSystem::ActualCurrentDirectory()
  729. {
  730. wchar_t CurrentPath[1024];
  731. FFileZillaIntf->GetCurrentPath(CurrentPath, LENOF(CurrentPath));
  732. return UnixExcludeTrailingBackslash(CurrentPath);
  733. }
  734. //---------------------------------------------------------------------------
  735. void __fastcall TFTPFileSystem::EnsureLocation()
  736. {
  737. // if we do not know what's the current directory, do nothing
  738. if (!FCurrentDirectory.IsEmpty())
  739. {
  740. // Make sure that the FZAPI current working directory,
  741. // is actually our working directory.
  742. // It may not be because:
  743. // 1) We did cached directory change
  744. // 2) Listing was requested for non-current directory, which
  745. // makes FZAPI change its current directory (and not restoring it back afterwards)
  746. if (!UnixSamePath(ActualCurrentDirectory(), FCurrentDirectory))
  747. {
  748. FTerminal->LogEvent(FORMAT(L"Synchronizing current directory \"%s\".",
  749. (FCurrentDirectory)));
  750. DoChangeDirectory(FCurrentDirectory);
  751. // make sure FZAPI is aware that we changed current working directory
  752. FFileZillaIntf->SetCurrentPath(FCurrentDirectory.c_str());
  753. }
  754. }
  755. }
  756. //---------------------------------------------------------------------------
  757. void __fastcall TFTPFileSystem::AnyCommand(const UnicodeString Command,
  758. TCaptureOutputEvent OutputEvent)
  759. {
  760. // end-user has right to expect that client current directory is really
  761. // current directory for the server
  762. EnsureLocation();
  763. assert(FOnCaptureOutput == NULL);
  764. FOnCaptureOutput = OutputEvent;
  765. try
  766. {
  767. SendCommand(Command);
  768. GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_3XX_CODE);
  769. }
  770. __finally
  771. {
  772. FOnCaptureOutput = NULL;
  773. }
  774. }
  775. //---------------------------------------------------------------------------
  776. void __fastcall TFTPFileSystem::ResetCaches()
  777. {
  778. delete FFileListCache;
  779. FFileListCache = NULL;
  780. }
  781. //---------------------------------------------------------------------------
  782. void __fastcall TFTPFileSystem::AnnounceFileListOperation()
  783. {
  784. ResetCaches();
  785. }
  786. //---------------------------------------------------------------------------
  787. void __fastcall TFTPFileSystem::DoChangeDirectory(const UnicodeString & Directory)
  788. {
  789. UnicodeString Command = FORMAT(L"CWD %s", (Directory));
  790. SendCommand(Command);
  791. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  792. }
  793. //---------------------------------------------------------------------------
  794. void __fastcall TFTPFileSystem::ChangeDirectory(const UnicodeString ADirectory)
  795. {
  796. UnicodeString Directory = ADirectory;
  797. try
  798. {
  799. // For changing directory, we do not make paths absolute, instead we
  800. // delegate this to the server, hence we synchronize current working
  801. // directory with the server and only then we ask for the change with
  802. // relative path.
  803. // But if synchronization fails, typically because current working directory
  804. // no longer exists, we fall back to out own resolution, to give
  805. // user chance to leave the non-existing directory.
  806. EnsureLocation();
  807. }
  808. catch(...)
  809. {
  810. if (FTerminal->Active)
  811. {
  812. Directory = AbsolutePath(Directory, false);
  813. }
  814. else
  815. {
  816. throw;
  817. }
  818. }
  819. DoChangeDirectory(Directory);
  820. // make next ReadCurrentDirectory retrieve actual server-side current directory
  821. FReadCurrentDirectory = true;
  822. }
  823. //---------------------------------------------------------------------------
  824. void __fastcall TFTPFileSystem::CachedChangeDirectory(const UnicodeString Directory)
  825. {
  826. FCurrentDirectory = UnixExcludeTrailingBackslash(Directory);
  827. FReadCurrentDirectory = false;
  828. }
  829. //---------------------------------------------------------------------------
  830. void __fastcall TFTPFileSystem::ChangeFileProperties(const UnicodeString AFileName,
  831. const TRemoteFile * File, const TRemoteProperties * Properties,
  832. TChmodSessionAction & Action)
  833. {
  834. assert(Properties);
  835. assert(!Properties->Valid.Contains(vpGroup));
  836. assert(!Properties->Valid.Contains(vpOwner));
  837. assert(!Properties->Valid.Contains(vpLastAccess));
  838. assert(!Properties->Valid.Contains(vpModification));
  839. if (Properties->Valid.Contains(vpRights))
  840. {
  841. TRemoteFile * OwnedFile = NULL;
  842. try
  843. {
  844. UnicodeString FileName = AbsolutePath(AFileName, false);
  845. if (File == NULL)
  846. {
  847. ReadFile(FileName, OwnedFile);
  848. File = OwnedFile;
  849. }
  850. if ((File != NULL) && File->IsDirectory && !File->IsSymLink && Properties->Recursive)
  851. {
  852. try
  853. {
  854. FTerminal->ProcessDirectory(AFileName, FTerminal->ChangeFileProperties,
  855. (void*)Properties);
  856. }
  857. catch(...)
  858. {
  859. Action.Cancel();
  860. throw;
  861. }
  862. }
  863. TRights Rights;
  864. if (File != NULL)
  865. {
  866. Rights = *File->Rights;
  867. }
  868. Rights |= Properties->Rights.NumberSet;
  869. Rights &= (unsigned short)~Properties->Rights.NumberUnset;
  870. if ((File != NULL) && File->IsDirectory && Properties->AddXToDirectories)
  871. {
  872. Rights.AddExecute();
  873. }
  874. Action.Rights(Rights);
  875. UnicodeString FileNameOnly = UnixExtractFileName(FileName);
  876. UnicodeString FilePath = UnixExtractFilePath(FileName);
  877. // FZAPI wants octal number represented as decadic
  878. FFileZillaIntf->Chmod(Rights.NumberDecadic, FileNameOnly.c_str(), FilePath.c_str());
  879. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  880. }
  881. __finally
  882. {
  883. delete OwnedFile;
  884. }
  885. }
  886. else
  887. {
  888. Action.Cancel();
  889. }
  890. }
  891. //---------------------------------------------------------------------------
  892. bool __fastcall TFTPFileSystem::LoadFilesProperties(TStrings * /*FileList*/)
  893. {
  894. FAIL;
  895. return false;
  896. }
  897. //---------------------------------------------------------------------------
  898. UnicodeString __fastcall TFTPFileSystem::DoCalculateFileChecksum(
  899. bool UsingHashCommand, const UnicodeString & Alg, TRemoteFile * File)
  900. {
  901. // Overview of server supporting various hash commands is at:
  902. // https://tools.ietf.org/html/draft-ietf-ftpext2-hash-03#appendix-B
  903. UnicodeString CommandName;
  904. if (UsingHashCommand)
  905. {
  906. CommandName = HashCommand;
  907. }
  908. else
  909. {
  910. int Index = FChecksumAlgs->IndexOf(Alg);
  911. if (Index < 0)
  912. {
  913. FAIL;
  914. EXCEPTION;
  915. }
  916. else
  917. {
  918. CommandName = FChecksumCommands->Strings[Index];
  919. }
  920. }
  921. UnicodeString FileName = File->FullFileName;
  922. // FTP way is not to quote.
  923. // But as Serv-U, GlobalSCAPE and possibly others allow
  924. // additional parameters (SP ER range), they need to quote file name.
  925. // Cerberus and FileZilla Server on the other hand can do without quotes
  926. // (but they can handle them, not sure about other servers)
  927. // Quoting:
  928. // FileZilla Server simply checks if argument starts and ends with double-quote
  929. // and strips them, no double-quote escaping is possible.
  930. // That's for all commands, not just HASH
  931. // ProFTPD: TODO: Check how "SITE SYMLINK target link" is parsed
  932. // We can possibly autodetect this from announced command format:
  933. // XCRC filename;start;end
  934. // XMD5 filename;start;end
  935. // XSHA1 filename;start;end
  936. // XSHA256 filename;start;end
  937. // XSHA512 filename;start;end
  938. if (FileName.Pos(L" ") > 0)
  939. {
  940. FileName = FORMAT(L"\"%s\"", (FileName));
  941. }
  942. UnicodeString Command = FORMAT(L"%s %s", (CommandName, FileName));
  943. SendCommand(Command);
  944. UnicodeString ResponseText = GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_SINGLE_LINE);
  945. UnicodeString Hash;
  946. if (UsingHashCommand)
  947. {
  948. // Code should be 213, but let's be tolerant and accept any 2xx
  949. // ("213" SP) hashname SP start-point "-" end-point SP filehash SP <pathname> (CRLF)
  950. UnicodeString Buf = ResponseText;
  951. // skip alg
  952. CutToChar(Buf, L' ', true);
  953. // skip range
  954. UnicodeString Range = CutToChar(Buf, L' ', true);
  955. // This should be range (SP-EP), but if it does not conform to the format,
  956. // it's likely because the server uses version of the HASH spec
  957. // before draft-ietf-ftpext2-hash-01
  958. // (including draft-bryan-ftp-hash-06 implemented by FileZilla server; or Cerberus),
  959. // that did not have the "range" part.
  960. // The FileZilla Server even omits the file name.
  961. // The latest draft as of implementing this is draft-bryan-ftpext-hash-02.
  962. if (Range.Pos(L"-") > 0)
  963. {
  964. Hash = CutToChar(Buf, L' ', true);
  965. }
  966. else
  967. {
  968. Hash = Range;
  969. }
  970. }
  971. else // All hash-specific commands
  972. {
  973. // Accepting any 2xx response. Most servers use 213,
  974. // but for example WS_FTP uses non-sense code 220 (Service ready for new user)
  975. // MD5 response according to a draft-twine-ftpmd5-00 includes a file name
  976. // (implemented by Apache FtpServer).
  977. // Other commands (X<hash>) return the hash only.
  978. ResponseText = ResponseText.Trim();
  979. int P = ResponseText.LastDelimiter(L" ");
  980. if (P > 0)
  981. {
  982. ResponseText.Delete(1, P);
  983. }
  984. Hash = ResponseText;
  985. }
  986. if (Hash.IsEmpty())
  987. {
  988. throw Exception(FMTLOAD(FTP_RESPONSE_ERROR, (CommandName, ResponseText)));
  989. }
  990. return LowerCase(Hash);
  991. }
  992. //---------------------------------------------------------------------------
  993. void __fastcall TFTPFileSystem::DoCalculateFilesChecksum(bool UsingHashCommand,
  994. const UnicodeString & Alg, TStrings * FileList, TStrings * Checksums,
  995. TCalculatedChecksumEvent OnCalculatedChecksum,
  996. TFileOperationProgressType * OperationProgress, bool FirstLevel)
  997. {
  998. TOnceDoneOperation OnceDoneOperation; // not used
  999. int Index = 0;
  1000. while ((Index < FileList->Count) && !OperationProgress->Cancel)
  1001. {
  1002. TRemoteFile * File = (TRemoteFile *)FileList->Objects[Index];
  1003. assert(File != NULL);
  1004. if (File->IsDirectory)
  1005. {
  1006. if (!File->IsSymLink &&
  1007. !File->IsParentDirectory && !File->IsThisDirectory &&
  1008. // recurse into subdirectories only if we have callback function
  1009. (OnCalculatedChecksum != NULL))
  1010. {
  1011. OperationProgress->SetFile(File->FileName);
  1012. TRemoteFileList * SubFiles =
  1013. FTerminal->CustomReadDirectoryListing(File->FullFileName, false);
  1014. if (SubFiles != NULL)
  1015. {
  1016. TStrings * SubFileList = new TStringList();
  1017. bool Success = false;
  1018. try
  1019. {
  1020. OperationProgress->SetFile(File->FileName);
  1021. for (int Index = 0; Index < SubFiles->Count; Index++)
  1022. {
  1023. TRemoteFile * SubFile = SubFiles->Files[Index];
  1024. SubFileList->AddObject(SubFile->FullFileName, SubFile);
  1025. }
  1026. // do not collect checksums for files in subdirectories,
  1027. // only send back checksums via callback
  1028. DoCalculateFilesChecksum(UsingHashCommand, Alg, SubFileList, NULL,
  1029. OnCalculatedChecksum, OperationProgress, false);
  1030. Success = true;
  1031. }
  1032. __finally
  1033. {
  1034. delete SubFiles;
  1035. delete SubFileList;
  1036. if (FirstLevel)
  1037. {
  1038. OperationProgress->Finish(File->FileName, Success, OnceDoneOperation);
  1039. }
  1040. }
  1041. }
  1042. }
  1043. }
  1044. else
  1045. {
  1046. TChecksumSessionAction Action(FTerminal->ActionLog);
  1047. try
  1048. {
  1049. OperationProgress->SetFile(File->FileName);
  1050. Action.FileName(FTerminal->AbsolutePath(File->FullFileName, true));
  1051. UnicodeString Checksum = DoCalculateFileChecksum(UsingHashCommand, Alg, File);
  1052. if (OnCalculatedChecksum != NULL)
  1053. {
  1054. OnCalculatedChecksum(File->FileName, Alg, Checksum);
  1055. }
  1056. Action.Checksum(Alg, Checksum);
  1057. if (Checksums != NULL)
  1058. {
  1059. Checksums->Add(Checksum);
  1060. }
  1061. }
  1062. catch (Exception & E)
  1063. {
  1064. FTerminal->RollbackAction(Action, OperationProgress, &E);
  1065. // Error formatting expanded from inline to avoid strange exceptions
  1066. UnicodeString Error =
  1067. FMTLOAD(CHECKSUM_ERROR,
  1068. (File != NULL ? File->FullFileName : UnicodeString(L"")));
  1069. FTerminal->CommandError(&E, Error);
  1070. // Abort loop.
  1071. // TODO: retries? resume?
  1072. Index = FileList->Count;
  1073. }
  1074. }
  1075. Index++;
  1076. }
  1077. }
  1078. //---------------------------------------------------------------------------
  1079. void __fastcall TFTPFileSystem::CalculateFilesChecksum(const UnicodeString & Alg,
  1080. TStrings * FileList, TStrings * Checksums,
  1081. TCalculatedChecksumEvent OnCalculatedChecksum)
  1082. {
  1083. TFileOperationProgressType Progress(&FTerminal->DoProgress, &FTerminal->DoFinished);
  1084. Progress.Start(foCalculateChecksum, osRemote, FileList->Count);
  1085. FTerminal->FOperationProgress = &Progress;
  1086. try
  1087. {
  1088. UnicodeString NormalizedAlg = FindIdent(FindIdent(Alg, FHashAlgs.get()), FChecksumAlgs.get());
  1089. bool UsingHashCommand = (FHashAlgs->IndexOf(NormalizedAlg) >= 0);
  1090. if (UsingHashCommand)
  1091. {
  1092. // The server should understand lowercase alg name by spec,
  1093. // but we should use uppercase anyway
  1094. SendCommand(FORMAT(L"OPTS %s %s", (HashCommand, UpperCase(NormalizedAlg))));
  1095. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  1096. }
  1097. else if (FChecksumAlgs->IndexOf(NormalizedAlg) >= 0)
  1098. {
  1099. // will use algorithm-specific command
  1100. }
  1101. else
  1102. {
  1103. throw Exception(FMTLOAD(UNKNOWN_CHECKSUM, (Alg)));
  1104. }
  1105. DoCalculateFilesChecksum(UsingHashCommand, NormalizedAlg, FileList, Checksums, OnCalculatedChecksum,
  1106. &Progress, true);
  1107. }
  1108. __finally
  1109. {
  1110. FTerminal->FOperationProgress = NULL;
  1111. Progress.Stop();
  1112. }
  1113. }
  1114. //---------------------------------------------------------------------------
  1115. bool __fastcall TFTPFileSystem::ConfirmOverwrite(
  1116. const UnicodeString & FullFileName, UnicodeString & FileName,
  1117. TOverwriteMode & OverwriteMode, TFileOperationProgressType * OperationProgress,
  1118. const TOverwriteFileParams * FileParams, const TCopyParamType * CopyParam,
  1119. int Params, bool AutoResume)
  1120. {
  1121. bool Result;
  1122. bool CanAutoResume = FLAGSET(Params, cpNoConfirmation) && AutoResume;
  1123. bool DestIsSmaller = (FileParams != NULL) && (FileParams->DestSize < FileParams->SourceSize);
  1124. bool DestIsSame = (FileParams != NULL) && (FileParams->DestSize == FileParams->SourceSize);
  1125. bool CanResume =
  1126. !OperationProgress->AsciiTransfer &&
  1127. // when resuming transfer after interrupted connection,
  1128. // do nothing (dummy resume) when the files has the same size.
  1129. // this is workaround for servers that strangely fails just after successful
  1130. // upload.
  1131. (DestIsSmaller || (DestIsSame && CanAutoResume));
  1132. unsigned int Answer;
  1133. if (CanAutoResume && CanResume)
  1134. {
  1135. if (DestIsSame)
  1136. {
  1137. assert(CanAutoResume);
  1138. Answer = qaSkip;
  1139. }
  1140. else
  1141. {
  1142. Answer = qaRetry;
  1143. }
  1144. }
  1145. else
  1146. {
  1147. // retry = "resume"
  1148. // all = "yes to newer"
  1149. // ignore = "rename"
  1150. int Answers = qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll | qaAll | qaIgnore;
  1151. if (CanResume)
  1152. {
  1153. Answers |= qaRetry;
  1154. }
  1155. TQueryButtonAlias Aliases[5];
  1156. Aliases[0].Button = qaRetry;
  1157. Aliases[0].Alias = LoadStr(RESUME_BUTTON);
  1158. Aliases[0].GroupWith = qaNo;
  1159. Aliases[0].GrouppedShiftState = TShiftState() << ssAlt;
  1160. Aliases[1].Button = qaAll;
  1161. Aliases[1].Alias = LoadStr(YES_TO_NEWER_BUTTON);
  1162. Aliases[1].GroupWith = qaYes;
  1163. Aliases[1].GrouppedShiftState = TShiftState() << ssCtrl;
  1164. Aliases[2].Button = qaIgnore;
  1165. Aliases[2].Alias = LoadStr(RENAME_BUTTON);
  1166. Aliases[2].GroupWith = qaNo;
  1167. Aliases[2].GrouppedShiftState = TShiftState() << ssCtrl;
  1168. Aliases[3].Button = qaYesToAll;
  1169. Aliases[3].GroupWith = qaYes;
  1170. Aliases[3].GrouppedShiftState = TShiftState() << ssShift;
  1171. Aliases[4].Button = qaNoToAll;
  1172. Aliases[4].GroupWith = qaNo;
  1173. Aliases[4].GrouppedShiftState = TShiftState() << ssShift;
  1174. TQueryParams QueryParams(qpNeverAskAgainCheck);
  1175. QueryParams.Aliases = Aliases;
  1176. QueryParams.AliasesCount = LENOF(Aliases);
  1177. {
  1178. TSuspendFileOperationProgress Suspend(OperationProgress);
  1179. Answer = FTerminal->ConfirmFileOverwrite(FullFileName, FileParams,
  1180. Answers, &QueryParams,
  1181. OperationProgress->Side == osLocal ? osRemote : osLocal,
  1182. CopyParam, Params, OperationProgress);
  1183. }
  1184. }
  1185. Result = true;
  1186. switch (Answer)
  1187. {
  1188. // resume
  1189. case qaRetry:
  1190. OverwriteMode = omResume;
  1191. assert(FileParams != NULL);
  1192. assert(CanResume);
  1193. FFileTransferResumed = FileParams->DestSize;
  1194. break;
  1195. // rename
  1196. case qaIgnore:
  1197. if (FTerminal->PromptUser(FTerminal->SessionData, pkFileName,
  1198. LoadStr(RENAME_TITLE), L"", LoadStr(RENAME_PROMPT2), true, 0, FileName))
  1199. {
  1200. OverwriteMode = omOverwrite;
  1201. }
  1202. else
  1203. {
  1204. if (!OperationProgress->Cancel)
  1205. {
  1206. OperationProgress->Cancel = csCancel;
  1207. }
  1208. FFileTransferAbort = ftaCancel;
  1209. Result = false;
  1210. }
  1211. break;
  1212. case qaYes:
  1213. OverwriteMode = omOverwrite;
  1214. break;
  1215. case qaCancel:
  1216. if (!OperationProgress->Cancel)
  1217. {
  1218. OperationProgress->Cancel = csCancel;
  1219. }
  1220. FFileTransferAbort = ftaCancel;
  1221. Result = false;
  1222. break;
  1223. case qaNo:
  1224. FFileTransferAbort = ftaSkip;
  1225. Result = false;
  1226. break;
  1227. case qaSkip:
  1228. OverwriteMode = omComplete;
  1229. break;
  1230. default:
  1231. FAIL;
  1232. Result = false;
  1233. break;
  1234. }
  1235. return Result;
  1236. }
  1237. //---------------------------------------------------------------------------
  1238. void __fastcall TFTPFileSystem::ResetFileTransfer()
  1239. {
  1240. FFileTransferAbort = ftaNone;
  1241. FFileTransferCancelled = false;
  1242. FFileTransferResumed = 0;
  1243. }
  1244. //---------------------------------------------------------------------------
  1245. void __fastcall TFTPFileSystem::ReadDirectoryProgress(__int64 Bytes)
  1246. {
  1247. // with FTP we do not know exactly how many entries we have received,
  1248. // instead we know number of bytes received only.
  1249. // so we report approximation based on average size of entry.
  1250. int Progress = int(Bytes / 80);
  1251. if (Progress - FLastReadDirectoryProgress >= 10)
  1252. {
  1253. bool Cancel = false;
  1254. FLastReadDirectoryProgress = Progress;
  1255. FTerminal->DoReadDirectoryProgress(Progress, 0, Cancel);
  1256. if (Cancel)
  1257. {
  1258. FTerminal->DoReadDirectoryProgress(-2, 0, Cancel);
  1259. FFileZillaIntf->Cancel();
  1260. }
  1261. }
  1262. }
  1263. //---------------------------------------------------------------------------
  1264. void __fastcall TFTPFileSystem::DoFileTransferProgress(__int64 TransferSize,
  1265. __int64 Bytes)
  1266. {
  1267. TFileOperationProgressType * OperationProgress = FTerminal->OperationProgress;
  1268. OperationProgress->SetTransferSize(TransferSize);
  1269. if (FFileTransferResumed > 0)
  1270. {
  1271. OperationProgress->AddResumed(FFileTransferResumed);
  1272. FFileTransferResumed = 0;
  1273. }
  1274. __int64 Diff = Bytes - OperationProgress->TransferedSize;
  1275. if (ALWAYS_TRUE(Diff >= 0))
  1276. {
  1277. OperationProgress->AddTransfered(Diff);
  1278. }
  1279. if (OperationProgress->Cancel == csCancel)
  1280. {
  1281. FFileTransferCancelled = true;
  1282. FFileTransferAbort = ftaCancel;
  1283. FFileZillaIntf->Cancel();
  1284. }
  1285. if (FFileTransferCPSLimit != OperationProgress->CPSLimit)
  1286. {
  1287. SetCPSLimit(OperationProgress);
  1288. }
  1289. }
  1290. //---------------------------------------------------------------------------
  1291. void __fastcall TFTPFileSystem::SetCPSLimit(TFileOperationProgressType * OperationProgress)
  1292. {
  1293. // Any reason we use separate field intead of directly using OperationProgress->CPSLimit?
  1294. // Maybe thread-safety?
  1295. FFileTransferCPSLimit = OperationProgress->CPSLimit;
  1296. OperationProgress->SetSpeedCounters();
  1297. }
  1298. //---------------------------------------------------------------------------
  1299. void __fastcall TFTPFileSystem::FileTransferProgress(__int64 TransferSize,
  1300. __int64 Bytes)
  1301. {
  1302. TGuard Guard(FTransferStatusCriticalSection);
  1303. DoFileTransferProgress(TransferSize, Bytes);
  1304. }
  1305. //---------------------------------------------------------------------------
  1306. void __fastcall TFTPFileSystem::FileTransfer(const UnicodeString & FileName,
  1307. const UnicodeString & LocalFile, const UnicodeString & RemoteFile,
  1308. const UnicodeString & RemotePath, bool Get, __int64 Size, int Type,
  1309. TFileTransferData & UserData, TFileOperationProgressType * OperationProgress)
  1310. {
  1311. FILE_OPERATION_LOOP_BEGIN
  1312. {
  1313. FFileZillaIntf->FileTransfer(LocalFile.c_str(), RemoteFile.c_str(),
  1314. RemotePath.c_str(), Get, Size, Type, &UserData);
  1315. // we may actually catch response code of the listing
  1316. // command (when checking for existence of the remote file)
  1317. unsigned int Reply = WaitForCommandReply();
  1318. GotReply(Reply, FLAGMASK(FFileTransferCancelled, REPLY_ALLOW_CANCEL));
  1319. }
  1320. FILE_OPERATION_LOOP_END(FMTLOAD(TRANSFER_ERROR, (FileName)));
  1321. switch (FFileTransferAbort)
  1322. {
  1323. case ftaSkip:
  1324. THROW_SKIP_FILE_NULL;
  1325. case ftaCancel:
  1326. Abort();
  1327. break;
  1328. }
  1329. if (!FFileTransferCancelled)
  1330. {
  1331. // show completion of transfer
  1332. // call non-guarded variant to avoid deadlock with keepalives
  1333. // (we are not waiting for reply anymore so keepalives are free to proceed)
  1334. DoFileTransferProgress(OperationProgress->TransferSize, OperationProgress->TransferSize);
  1335. }
  1336. }
  1337. //---------------------------------------------------------------------------
  1338. void __fastcall TFTPFileSystem::CopyToLocal(TStrings * FilesToCopy,
  1339. const UnicodeString TargetDir, const TCopyParamType * CopyParam,
  1340. int Params, TFileOperationProgressType * OperationProgress,
  1341. TOnceDoneOperation & OnceDoneOperation)
  1342. {
  1343. Params &= ~cpAppend;
  1344. UnicodeString FullTargetDir = IncludeTrailingBackslash(TargetDir);
  1345. int Index = 0;
  1346. while (Index < FilesToCopy->Count && !OperationProgress->Cancel)
  1347. {
  1348. UnicodeString FileName = FilesToCopy->Strings[Index];
  1349. const TRemoteFile * File = dynamic_cast<const TRemoteFile *>(FilesToCopy->Objects[Index]);
  1350. bool Success = false;
  1351. try
  1352. {
  1353. try
  1354. {
  1355. SinkRobust(AbsolutePath(FileName, false), File, FullTargetDir, CopyParam, Params,
  1356. OperationProgress, tfFirstLevel);
  1357. Success = true;
  1358. FLastDataSent = Now();
  1359. }
  1360. catch(EScpSkipFile & E)
  1361. {
  1362. TSuspendFileOperationProgress Suspend(OperationProgress);
  1363. if (!FTerminal->HandleException(&E))
  1364. {
  1365. throw;
  1366. }
  1367. }
  1368. }
  1369. __finally
  1370. {
  1371. OperationProgress->Finish(FileName, Success, OnceDoneOperation);
  1372. }
  1373. Index++;
  1374. }
  1375. }
  1376. //---------------------------------------------------------------------------
  1377. void __fastcall TFTPFileSystem::SinkRobust(const UnicodeString FileName,
  1378. const TRemoteFile * File, const UnicodeString TargetDir,
  1379. const TCopyParamType * CopyParam, int Params,
  1380. TFileOperationProgressType * OperationProgress, unsigned int Flags)
  1381. {
  1382. // the same in TSFTPFileSystem
  1383. TDownloadSessionAction Action(FTerminal->ActionLog);
  1384. TRobustOperationLoop RobustLoop(FTerminal, OperationProgress);
  1385. do
  1386. {
  1387. try
  1388. {
  1389. Sink(FileName, File, TargetDir, CopyParam, Params, OperationProgress,
  1390. Flags, Action);
  1391. }
  1392. catch(Exception & E)
  1393. {
  1394. if (!RobustLoop.TryReopen(E))
  1395. {
  1396. FTerminal->RollbackAction(Action, OperationProgress, &E);
  1397. throw;
  1398. }
  1399. }
  1400. if (RobustLoop.ShouldRetry())
  1401. {
  1402. OperationProgress->RollbackTransfer();
  1403. Action.Restart();
  1404. assert(File != NULL);
  1405. if (!File->IsDirectory)
  1406. {
  1407. // prevent overwrite confirmations
  1408. Params |= cpNoConfirmation;
  1409. Flags |= tfAutoResume;
  1410. }
  1411. }
  1412. }
  1413. while (RobustLoop.Retry());
  1414. }
  1415. //---------------------------------------------------------------------------
  1416. void __fastcall TFTPFileSystem::Sink(const UnicodeString FileName,
  1417. const TRemoteFile * File, const UnicodeString TargetDir,
  1418. const TCopyParamType * CopyParam, int Params,
  1419. TFileOperationProgressType * OperationProgress, unsigned int Flags,
  1420. TDownloadSessionAction & Action)
  1421. {
  1422. UnicodeString OnlyFileName = UnixExtractFileName(FileName);
  1423. Action.FileName(FileName);
  1424. TFileMasks::TParams MaskParams;
  1425. assert(File);
  1426. MaskParams.Size = File->Size;
  1427. MaskParams.Modification = File->Modification;
  1428. if (!CopyParam->AllowTransfer(FileName, osRemote, File->IsDirectory, MaskParams))
  1429. {
  1430. FTerminal->LogEvent(FORMAT(L"File \"%s\" excluded from transfer", (FileName)));
  1431. THROW_SKIP_FILE_NULL;
  1432. }
  1433. if (CopyParam->SkipTransfer(FileName, File->IsDirectory))
  1434. {
  1435. OperationProgress->AddSkippedFileSize(File->Size);
  1436. THROW_SKIP_FILE_NULL;
  1437. }
  1438. FTerminal->LogFileDetails(FileName, File->Modification, File->Size);
  1439. OperationProgress->SetFile(FileName);
  1440. UnicodeString DestFileName = CopyParam->ChangeFileName(OnlyFileName,
  1441. osRemote, FLAGSET(Flags, tfFirstLevel));
  1442. UnicodeString DestFullName = TargetDir + DestFileName;
  1443. if (File->IsDirectory)
  1444. {
  1445. Action.Cancel();
  1446. if (!File->IsSymLink)
  1447. {
  1448. FILE_OPERATION_LOOP_BEGIN
  1449. {
  1450. int Attrs = FileGetAttr(ApiPath(DestFullName));
  1451. if (FLAGCLEAR(Attrs, faDirectory))
  1452. {
  1453. EXCEPTION;
  1454. }
  1455. }
  1456. FILE_OPERATION_LOOP_END(FMTLOAD(NOT_DIRECTORY_ERROR, (DestFullName)));
  1457. FILE_OPERATION_LOOP_BEGIN
  1458. {
  1459. THROWOSIFFALSE(ForceDirectories(ApiPath(DestFullName)));
  1460. }
  1461. FILE_OPERATION_LOOP_END(FMTLOAD(CREATE_DIR_ERROR, (DestFullName)));
  1462. TSinkFileParams SinkFileParams;
  1463. SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
  1464. SinkFileParams.CopyParam = CopyParam;
  1465. SinkFileParams.Params = Params;
  1466. SinkFileParams.OperationProgress = OperationProgress;
  1467. SinkFileParams.Skipped = false;
  1468. SinkFileParams.Flags = Flags & ~(tfFirstLevel | tfAutoResume);
  1469. FTerminal->ProcessDirectory(FileName, SinkFile, &SinkFileParams);
  1470. // Do not delete directory if some of its files were skip.
  1471. // Throw "skip file" for the directory to avoid attempt to deletion
  1472. // of any parent directory
  1473. if (FLAGSET(Params, cpDelete) && SinkFileParams.Skipped)
  1474. {
  1475. THROW_SKIP_FILE_NULL;
  1476. }
  1477. }
  1478. else
  1479. {
  1480. FTerminal->LogEvent(FORMAT(L"Skipping symlink to directory \"%s\".", (FileName)));
  1481. }
  1482. }
  1483. else
  1484. {
  1485. FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to local directory started.", (FileName)));
  1486. // Will we use ASCII of BINARY file transfer?
  1487. OperationProgress->SetAsciiTransfer(
  1488. CopyParam->UseAsciiTransfer(FileName, osRemote, MaskParams));
  1489. FTerminal->LogEvent(UnicodeString((OperationProgress->AsciiTransfer ? L"Ascii" : L"Binary")) +
  1490. L" transfer mode selected.");
  1491. // Suppose same data size to transfer as to write
  1492. OperationProgress->SetTransferSize(File->Size);
  1493. OperationProgress->SetLocalSize(OperationProgress->TransferSize);
  1494. int Attrs;
  1495. FILE_OPERATION_LOOP_BEGIN
  1496. {
  1497. Attrs = FileGetAttr(ApiPath(DestFullName));
  1498. if ((Attrs >= 0) && FLAGSET(Attrs, faDirectory))
  1499. {
  1500. EXCEPTION;
  1501. }
  1502. }
  1503. FILE_OPERATION_LOOP_END(FMTLOAD(NOT_FILE_ERROR, (DestFullName)));
  1504. OperationProgress->TransferingFile = false; // not set with FTP protocol
  1505. ResetFileTransfer();
  1506. TFileTransferData UserData;
  1507. UnicodeString FilePath = UnixExtractFilePath(FileName);
  1508. unsigned int TransferType = (OperationProgress->AsciiTransfer ? 1 : 2);
  1509. {
  1510. // ignore file list
  1511. TFileListHelper Helper(this, NULL, true);
  1512. SetCPSLimit(OperationProgress);
  1513. FFileTransferPreserveTime = CopyParam->PreserveTime;
  1514. // not used for downloads anyway
  1515. FFileTransferRemoveBOM = CopyParam->RemoveBOM;
  1516. UserData.FileName = DestFileName;
  1517. UserData.Params = Params;
  1518. UserData.AutoResume = FLAGSET(Flags, tfAutoResume);
  1519. UserData.CopyParam = CopyParam;
  1520. UserData.Modification = File->Modification;
  1521. FileTransfer(FileName, DestFullName, OnlyFileName,
  1522. FilePath, true, File->Size, TransferType, UserData, OperationProgress);
  1523. }
  1524. // in case dest filename is changed from overwrite dialog
  1525. if (DestFileName != UserData.FileName)
  1526. {
  1527. DestFullName = TargetDir + UserData.FileName;
  1528. Attrs = FileGetAttr(ApiPath(DestFullName));
  1529. }
  1530. Action.Destination(ExpandUNCFileName(DestFullName));
  1531. if (Attrs == -1)
  1532. {
  1533. Attrs = faArchive;
  1534. }
  1535. int NewAttrs = CopyParam->LocalFileAttrs(*File->Rights);
  1536. if ((NewAttrs & Attrs) != NewAttrs)
  1537. {
  1538. FILE_OPERATION_LOOP_BEGIN
  1539. {
  1540. THROWOSIFFALSE(FileSetAttr(ApiPath(DestFullName), Attrs | NewAttrs) == 0);
  1541. }
  1542. FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DestFullName)));
  1543. }
  1544. FTerminal->LogFileDone(OperationProgress);
  1545. }
  1546. if (FLAGSET(Params, cpDelete))
  1547. {
  1548. // If file is directory, do not delete it recursively, because it should be
  1549. // empty already. If not, it should not be deleted (some files were
  1550. // skipped or some new files were copied to it, while we were downloading)
  1551. int Params = dfNoRecursive;
  1552. FTerminal->DeleteFile(FileName, File, &Params);
  1553. }
  1554. }
  1555. //---------------------------------------------------------------------------
  1556. void __fastcall TFTPFileSystem::SinkFile(UnicodeString FileName,
  1557. const TRemoteFile * File, void * Param)
  1558. {
  1559. TSinkFileParams * Params = (TSinkFileParams *)Param;
  1560. assert(Params->OperationProgress);
  1561. try
  1562. {
  1563. SinkRobust(FileName, File, Params->TargetDir, Params->CopyParam,
  1564. Params->Params, Params->OperationProgress, Params->Flags);
  1565. }
  1566. catch(EScpSkipFile & E)
  1567. {
  1568. TFileOperationProgressType * OperationProgress = Params->OperationProgress;
  1569. Params->Skipped = true;
  1570. {
  1571. TSuspendFileOperationProgress Suspend(OperationProgress);
  1572. if (!FTerminal->HandleException(&E))
  1573. {
  1574. throw;
  1575. }
  1576. }
  1577. if (OperationProgress->Cancel)
  1578. {
  1579. Abort();
  1580. }
  1581. }
  1582. }
  1583. //---------------------------------------------------------------------------
  1584. void __fastcall TFTPFileSystem::CopyToRemote(TStrings * FilesToCopy,
  1585. const UnicodeString ATargetDir, const TCopyParamType * CopyParam,
  1586. int Params, TFileOperationProgressType * OperationProgress,
  1587. TOnceDoneOperation & OnceDoneOperation)
  1588. {
  1589. assert((FilesToCopy != NULL) && (OperationProgress != NULL));
  1590. Params &= ~cpAppend;
  1591. UnicodeString FileName, FileNameOnly;
  1592. UnicodeString TargetDir = AbsolutePath(ATargetDir, false);
  1593. UnicodeString FullTargetDir = UnixIncludeTrailingBackslash(TargetDir);
  1594. int Index = 0;
  1595. while ((Index < FilesToCopy->Count) && !OperationProgress->Cancel)
  1596. {
  1597. bool Success = false;
  1598. FileName = FilesToCopy->Strings[Index];
  1599. FileNameOnly = ExtractFileName(FileName);
  1600. try
  1601. {
  1602. try
  1603. {
  1604. if (FTerminal->SessionData->CacheDirectories)
  1605. {
  1606. FTerminal->DirectoryModified(TargetDir, false);
  1607. if (DirectoryExists(ApiPath(FileName)))
  1608. {
  1609. FTerminal->DirectoryModified(FullTargetDir + FileNameOnly, true);
  1610. }
  1611. }
  1612. SourceRobust(FileName, FullTargetDir, CopyParam, Params, OperationProgress,
  1613. tfFirstLevel);
  1614. Success = true;
  1615. FLastDataSent = Now();
  1616. }
  1617. catch(EScpSkipFile & E)
  1618. {
  1619. TSuspendFileOperationProgress Suspend(OperationProgress);
  1620. if (!FTerminal->HandleException(&E))
  1621. {
  1622. throw;
  1623. }
  1624. }
  1625. }
  1626. __finally
  1627. {
  1628. OperationProgress->Finish(FileName, Success, OnceDoneOperation);
  1629. }
  1630. Index++;
  1631. }
  1632. }
  1633. //---------------------------------------------------------------------------
  1634. void __fastcall TFTPFileSystem::SourceRobust(const UnicodeString FileName,
  1635. const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
  1636. TFileOperationProgressType * OperationProgress, unsigned int Flags)
  1637. {
  1638. // the same in TSFTPFileSystem
  1639. TUploadSessionAction Action(FTerminal->ActionLog);
  1640. TRobustOperationLoop RobustLoop(FTerminal, OperationProgress);
  1641. do
  1642. {
  1643. try
  1644. {
  1645. Source(FileName, TargetDir, CopyParam, Params, OperationProgress,
  1646. Flags, Action);
  1647. }
  1648. catch(Exception & E)
  1649. {
  1650. if (!RobustLoop.TryReopen(E))
  1651. {
  1652. FTerminal->RollbackAction(Action, OperationProgress, &E);
  1653. throw;
  1654. }
  1655. }
  1656. if (RobustLoop.ShouldRetry())
  1657. {
  1658. OperationProgress->RollbackTransfer();
  1659. Action.Restart();
  1660. // prevent overwrite confirmations
  1661. // (should not be set for directories!)
  1662. Params |= cpNoConfirmation;
  1663. Flags |= tfAutoResume;
  1664. }
  1665. }
  1666. while (RobustLoop.Retry());
  1667. }
  1668. //---------------------------------------------------------------------------
  1669. void __fastcall TFTPFileSystem::Source(const UnicodeString FileName,
  1670. const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
  1671. TFileOperationProgressType * OperationProgress, unsigned int Flags,
  1672. TUploadSessionAction & Action)
  1673. {
  1674. Action.FileName(ExpandUNCFileName(FileName));
  1675. OperationProgress->SetFile(FileName, false);
  1676. if (!FTerminal->AllowLocalFileTransfer(FileName, CopyParam, OperationProgress))
  1677. {
  1678. THROW_SKIP_FILE_NULL;
  1679. }
  1680. __int64 Size;
  1681. int Attrs;
  1682. FTerminal->OpenLocalFile(FileName, GENERIC_READ, &Attrs,
  1683. NULL, NULL, NULL, NULL, &Size);
  1684. OperationProgress->SetFileInProgress();
  1685. bool Dir = FLAGSET(Attrs, faDirectory);
  1686. if (Dir)
  1687. {
  1688. Action.Cancel();
  1689. DirectorySource(IncludeTrailingBackslash(FileName), TargetDir,
  1690. Attrs, CopyParam, Params, OperationProgress, Flags);
  1691. }
  1692. else
  1693. {
  1694. UnicodeString DestFileName = CopyParam->ChangeFileName(ExtractFileName(FileName),
  1695. osLocal, FLAGSET(Flags, tfFirstLevel));
  1696. FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to remote directory started.", (FileName)));
  1697. OperationProgress->SetLocalSize(Size);
  1698. // Suppose same data size to transfer as to read
  1699. // (not true with ASCII transfer)
  1700. OperationProgress->SetTransferSize(OperationProgress->LocalSize);
  1701. OperationProgress->TransferingFile = false;
  1702. TDateTime Modification;
  1703. // Inspired by SysUtils::FileAge
  1704. WIN32_FIND_DATA FindData;
  1705. HANDLE Handle = FindFirstFile(FileName.c_str(), &FindData);
  1706. if (Handle != INVALID_HANDLE_VALUE)
  1707. {
  1708. Modification =
  1709. UnixToDateTime(
  1710. ConvertTimestampToUnixSafe(FindData.ftLastWriteTime, dstmUnix),
  1711. dstmUnix);
  1712. FindClose(Handle);
  1713. }
  1714. // Will we use ASCII of BINARY file transfer?
  1715. TFileMasks::TParams MaskParams;
  1716. MaskParams.Size = Size;
  1717. MaskParams.Modification = Modification;
  1718. OperationProgress->SetAsciiTransfer(
  1719. CopyParam->UseAsciiTransfer(FileName, osLocal, MaskParams));
  1720. FTerminal->LogEvent(
  1721. UnicodeString(OperationProgress->AsciiTransfer ? L"Ascii" : L"Binary") +
  1722. L" transfer mode selected.");
  1723. ResetFileTransfer();
  1724. TFileTransferData UserData;
  1725. unsigned int TransferType = (OperationProgress->AsciiTransfer ? 1 : 2);
  1726. {
  1727. // ignore file list
  1728. TFileListHelper Helper(this, NULL, true);
  1729. SetCPSLimit(OperationProgress);
  1730. // not used for uploads anyway
  1731. FFileTransferPreserveTime = CopyParam->PreserveTime;
  1732. FFileTransferRemoveBOM = CopyParam->RemoveBOM;
  1733. // not used for uploads, but we get new name (if any) back in this field
  1734. UserData.FileName = DestFileName;
  1735. UserData.Params = Params;
  1736. UserData.AutoResume = FLAGSET(Flags, tfAutoResume);
  1737. UserData.CopyParam = CopyParam;
  1738. UserData.Modification = Modification;
  1739. FileTransfer(FileName, FileName, DestFileName,
  1740. TargetDir, false, Size, TransferType, UserData, OperationProgress);
  1741. }
  1742. UnicodeString DestFullName = TargetDir + UserData.FileName;
  1743. // only now, we know the final destination
  1744. Action.Destination(DestFullName);
  1745. // We are not able to tell if setting timestamp succeeded,
  1746. // so we log it always (if supported).
  1747. // Support for MDTM does not necessarily mean that the server supports
  1748. // non-standard hack of setting timestamp using
  1749. // MFMT-like (two argument) call to MDTM.
  1750. // IIS definitelly does.
  1751. if (FFileTransferPreserveTime &&
  1752. ((FServerCapabilities->GetCapability(mfmt_command) == yes) ||
  1753. ((FServerCapabilities->GetCapability(mdtm_command) == yes))))
  1754. {
  1755. TTouchSessionAction TouchAction(FTerminal->ActionLog, DestFullName, Modification);
  1756. }
  1757. FTerminal->LogFileDone(OperationProgress);
  1758. }
  1759. /* TODO : Delete also read-only files. */
  1760. if (FLAGSET(Params, cpDelete))
  1761. {
  1762. if (!Dir)
  1763. {
  1764. FILE_OPERATION_LOOP_BEGIN
  1765. {
  1766. THROWOSIFFALSE(Sysutils::DeleteFile(ApiPath(FileName)));
  1767. }
  1768. FILE_OPERATION_LOOP_END(FMTLOAD(DELETE_LOCAL_FILE_ERROR, (FileName)));
  1769. }
  1770. }
  1771. else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
  1772. {
  1773. FILE_OPERATION_LOOP_BEGIN
  1774. {
  1775. THROWOSIFFALSE(FileSetAttr(ApiPath(FileName), Attrs & ~faArchive) == 0);
  1776. }
  1777. FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (FileName)));
  1778. }
  1779. }
  1780. //---------------------------------------------------------------------------
  1781. void __fastcall TFTPFileSystem::DirectorySource(const UnicodeString DirectoryName,
  1782. const UnicodeString TargetDir, int Attrs, const TCopyParamType * CopyParam,
  1783. int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags)
  1784. {
  1785. UnicodeString DestDirectoryName = CopyParam->ChangeFileName(
  1786. ExtractFileName(ExcludeTrailingBackslash(DirectoryName)), osLocal,
  1787. FLAGSET(Flags, tfFirstLevel));
  1788. UnicodeString DestFullName = UnixIncludeTrailingBackslash(TargetDir + DestDirectoryName);
  1789. OperationProgress->SetFile(DirectoryName);
  1790. int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
  1791. TSearchRecChecked SearchRec;
  1792. bool FindOK;
  1793. FILE_OPERATION_LOOP_BEGIN
  1794. {
  1795. FindOK =
  1796. (FindFirstChecked(DirectoryName + L"*.*", FindAttrs, SearchRec) == 0);
  1797. }
  1798. FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
  1799. bool CreateDir = true;
  1800. try
  1801. {
  1802. while (FindOK && !OperationProgress->Cancel)
  1803. {
  1804. UnicodeString FileName = DirectoryName + SearchRec.Name;
  1805. try
  1806. {
  1807. if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
  1808. {
  1809. SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
  1810. Flags & ~(tfFirstLevel | tfAutoResume));
  1811. // if any file got uploaded (i.e. there were any file in the
  1812. // directory and at least one was not skipped),
  1813. // do not try to create the directory,
  1814. // as it should be already created by FZAPI during upload
  1815. CreateDir = false;
  1816. }
  1817. }
  1818. catch (EScpSkipFile &E)
  1819. {
  1820. // If ESkipFile occurs, just log it and continue with next file
  1821. TSuspendFileOperationProgress Suspend(OperationProgress);
  1822. // here a message to user was displayed, which was not appropriate
  1823. // when user refused to overwrite the file in subdirectory.
  1824. // hopefully it won't be missing in other situations.
  1825. if (!FTerminal->HandleException(&E))
  1826. {
  1827. throw;
  1828. }
  1829. }
  1830. FILE_OPERATION_LOOP_BEGIN
  1831. {
  1832. FindOK = (FindNextChecked(SearchRec) == 0);
  1833. }
  1834. FILE_OPERATION_LOOP_END(FMTLOAD(LIST_DIR_ERROR, (DirectoryName)));
  1835. }
  1836. }
  1837. __finally
  1838. {
  1839. FindClose(SearchRec);
  1840. }
  1841. if (CreateDir)
  1842. {
  1843. TRemoteProperties Properties;
  1844. if (CopyParam->PreserveRights)
  1845. {
  1846. Properties.Valid = TValidProperties() << vpRights;
  1847. Properties.Rights = CopyParam->RemoteFileRights(Attrs);
  1848. }
  1849. try
  1850. {
  1851. FTerminal->ExceptionOnFail = true;
  1852. try
  1853. {
  1854. FTerminal->CreateDirectory(DestFullName, &Properties);
  1855. }
  1856. __finally
  1857. {
  1858. FTerminal->ExceptionOnFail = false;
  1859. }
  1860. }
  1861. catch(...)
  1862. {
  1863. TRemoteFile * File = NULL;
  1864. // ignore non-fatal error when the directory already exists
  1865. bool Rethrow =
  1866. !FTerminal->Active ||
  1867. !FTerminal->FileExists(UnixExcludeTrailingBackslash(DestFullName), &File) ||
  1868. !File->IsDirectory;
  1869. delete File;
  1870. if (Rethrow)
  1871. {
  1872. throw;
  1873. }
  1874. }
  1875. }
  1876. /* TODO : Delete also read-only directories. */
  1877. /* TODO : Show error message on failure. */
  1878. if (!OperationProgress->Cancel)
  1879. {
  1880. if (FLAGSET(Params, cpDelete))
  1881. {
  1882. RemoveDir(ApiPath(DirectoryName));
  1883. }
  1884. else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
  1885. {
  1886. FILE_OPERATION_LOOP_BEGIN
  1887. {
  1888. THROWOSIFFALSE(FileSetAttr(ApiPath(DirectoryName), Attrs & ~faArchive) == 0);
  1889. }
  1890. FILE_OPERATION_LOOP_END(FMTLOAD(CANT_SET_ATTRS, (DirectoryName)));
  1891. }
  1892. }
  1893. }
  1894. //---------------------------------------------------------------------------
  1895. void __fastcall TFTPFileSystem::CreateDirectory(const UnicodeString ADirName)
  1896. {
  1897. UnicodeString DirName = AbsolutePath(ADirName, false);
  1898. {
  1899. // ignore file list
  1900. TFileListHelper Helper(this, NULL, true);
  1901. FFileZillaIntf->MakeDir(DirName.c_str());
  1902. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  1903. }
  1904. }
  1905. //---------------------------------------------------------------------------
  1906. void __fastcall TFTPFileSystem::CreateLink(const UnicodeString FileName,
  1907. const UnicodeString PointTo, bool Symbolic)
  1908. {
  1909. assert(SupportsSiteCommand(SymlinkSiteCommand));
  1910. if (ALWAYS_TRUE(Symbolic))
  1911. {
  1912. EnsureLocation();
  1913. UnicodeString Command = FORMAT(L"%s %s %s %s", (SiteCommand, SymlinkSiteCommand, PointTo, FileName));
  1914. SendCommand(Command);
  1915. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  1916. }
  1917. }
  1918. //---------------------------------------------------------------------------
  1919. void __fastcall TFTPFileSystem::DeleteFile(const UnicodeString AFileName,
  1920. const TRemoteFile * File, int Params, TRmSessionAction & Action)
  1921. {
  1922. UnicodeString FileName = AbsolutePath(AFileName, false);
  1923. UnicodeString FileNameOnly = UnixExtractFileName(FileName);
  1924. UnicodeString FilePath = UnixExtractFilePath(FileName);
  1925. bool Dir = (File != NULL) && File->IsDirectory && !File->IsSymLink;
  1926. if (Dir && FLAGCLEAR(Params, dfNoRecursive))
  1927. {
  1928. try
  1929. {
  1930. FTerminal->ProcessDirectory(FileName, FTerminal->DeleteFile, &Params);
  1931. }
  1932. catch(...)
  1933. {
  1934. Action.Cancel();
  1935. throw;
  1936. }
  1937. }
  1938. {
  1939. // ignore file list
  1940. TFileListHelper Helper(this, NULL, true);
  1941. if (Dir)
  1942. {
  1943. // If current remote directory is in the directory being removed,
  1944. // some servers may refuse to delete it
  1945. // This is common as ProcessDirectory above would CWD to
  1946. // the directory to LIST it.
  1947. // EnsureLocation should reset actual current directory to user's working directory.
  1948. // If user's working directory is still below deleted directory, it is
  1949. // perfectly correct to report an error.
  1950. if (UnixIsChildPath(ActualCurrentDirectory(), FileName))
  1951. {
  1952. EnsureLocation();
  1953. }
  1954. FFileZillaIntf->RemoveDir(FileNameOnly.c_str(), FilePath.c_str());
  1955. }
  1956. else
  1957. {
  1958. FFileZillaIntf->Delete(FileNameOnly.c_str(), FilePath.c_str());
  1959. }
  1960. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  1961. }
  1962. }
  1963. //---------------------------------------------------------------------------
  1964. void __fastcall TFTPFileSystem::CustomCommandOnFile(const UnicodeString /*FileName*/,
  1965. const TRemoteFile * /*File*/, UnicodeString /*Command*/, int /*Params*/,
  1966. TCaptureOutputEvent /*OutputEvent*/)
  1967. {
  1968. // if ever implemented, do not forget to add EnsureLocation,
  1969. // see AnyCommand for a reason why
  1970. FAIL;
  1971. }
  1972. //---------------------------------------------------------------------------
  1973. void __fastcall TFTPFileSystem::DoStartup()
  1974. {
  1975. TStrings * PostLoginCommands = new TStringList();
  1976. try
  1977. {
  1978. PostLoginCommands->Text = FTerminal->SessionData->PostLoginCommands;
  1979. for (int Index = 0; Index < PostLoginCommands->Count; Index++)
  1980. {
  1981. UnicodeString Command = PostLoginCommands->Strings[Index];
  1982. if (!Command.IsEmpty())
  1983. {
  1984. SendCommand(Command);
  1985. GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_3XX_CODE);
  1986. }
  1987. }
  1988. }
  1989. __finally
  1990. {
  1991. delete PostLoginCommands;
  1992. }
  1993. // retrieve initialize working directory to save it as home directory
  1994. ReadCurrentDirectory();
  1995. FHomeDirectory = FCurrentDirectory;
  1996. }
  1997. //---------------------------------------------------------------------------
  1998. void __fastcall TFTPFileSystem::HomeDirectory()
  1999. {
  2000. // FHomeDirectory is an absolute path, so avoid unnecessary overhead
  2001. // of ChangeDirectory, such as EnsureLocation
  2002. DoChangeDirectory(FHomeDirectory);
  2003. FCurrentDirectory = FHomeDirectory;
  2004. FReadCurrentDirectory = false;
  2005. // make sure FZAPI is aware that we changed current working directory
  2006. FFileZillaIntf->SetCurrentPath(FCurrentDirectory.c_str());
  2007. }
  2008. //---------------------------------------------------------------------------
  2009. bool __fastcall TFTPFileSystem::IsCapable(int Capability) const
  2010. {
  2011. assert(FTerminal);
  2012. switch (Capability)
  2013. {
  2014. case fcResolveSymlink: // sic
  2015. case fcTextMode:
  2016. case fcModeChanging: // but not fcModeChangingUpload
  2017. case fcNewerOnlyUpload:
  2018. case fcAnyCommand: // but not fcShellAnyCommand
  2019. case fcRename:
  2020. case fcRemoteMove:
  2021. case fcRemoveBOMUpload:
  2022. case fcMoveToQueue:
  2023. return true;
  2024. case fcPreservingTimestampUpload:
  2025. return (FServerCapabilities->GetCapability(mfmt_command) == yes);
  2026. case fcRemoteCopy:
  2027. return SupportsSiteCommand(CopySiteCommand);
  2028. case fcSymbolicLink:
  2029. return SupportsSiteCommand(SymlinkSiteCommand);
  2030. case fcCalculatingChecksum:
  2031. return FSupportsAnyChecksumFeature;
  2032. case fcCheckingSpaceAvailable:
  2033. return SupportsCommand(AvblCommand) || SupportsCommand(XQuotaCommand);
  2034. case fcModeChangingUpload:
  2035. case fcLoadingAdditionalProperties:
  2036. case fcShellAnyCommand:
  2037. case fcHardLink:
  2038. case fcUserGroupListing:
  2039. case fcGroupChanging:
  2040. case fcOwnerChanging:
  2041. case fcGroupOwnerChangingByID:
  2042. case fcSecondaryShell:
  2043. case fcNativeTextMode:
  2044. case fcTimestampChanging:
  2045. case fcIgnorePermErrors:
  2046. case fcRemoveCtrlZUpload:
  2047. return false;
  2048. default:
  2049. FAIL;
  2050. return false;
  2051. }
  2052. }
  2053. //---------------------------------------------------------------------------
  2054. void __fastcall TFTPFileSystem::LookupUsersGroups()
  2055. {
  2056. FAIL;
  2057. }
  2058. //---------------------------------------------------------------------------
  2059. void __fastcall TFTPFileSystem::ReadCurrentDirectory()
  2060. {
  2061. // ask the server for current directory on startup only
  2062. // and immediately after call to CWD,
  2063. // later our current directory may be not synchronized with FZAPI current
  2064. // directory anyway, see comments in EnsureLocation
  2065. if (FReadCurrentDirectory || ALWAYS_FALSE(FCurrentDirectory.IsEmpty()))
  2066. {
  2067. UnicodeString Command = L"PWD";
  2068. SendCommand(Command);
  2069. unsigned int Code;
  2070. TStrings * Response = NULL;
  2071. GotReply(WaitForCommandReply(), REPLY_2XX_CODE, L"", &Code, &Response);
  2072. try
  2073. {
  2074. assert(Response != NULL);
  2075. bool Result = false;
  2076. // the only allowed 2XX code to "PWD"
  2077. if ((Code == 257) &&
  2078. (Response->Count == 1))
  2079. {
  2080. UnicodeString Path = Response->Text;
  2081. int P = Path.Pos(L"\"");
  2082. if (P == 0)
  2083. {
  2084. // some systems use single quotes, be tolerant
  2085. P = Path.Pos(L"'");
  2086. }
  2087. if (P != 0)
  2088. {
  2089. Path.Delete(1, P - 1);
  2090. if (Unquote(Path))
  2091. {
  2092. Result = true;
  2093. }
  2094. }
  2095. else
  2096. {
  2097. P = Path.Pos(L" ");
  2098. Path.Delete(P, Path.Length() - P + 1);
  2099. Result = true;
  2100. }
  2101. if (Result)
  2102. {
  2103. FCurrentDirectory = UnixExcludeTrailingBackslash(Path);
  2104. FReadCurrentDirectory = false;
  2105. }
  2106. }
  2107. if (Result)
  2108. {
  2109. FFileZillaIntf->SetCurrentPath(FCurrentDirectory.c_str());
  2110. }
  2111. else
  2112. {
  2113. throw Exception(FMTLOAD(FTP_RESPONSE_ERROR, (Command, Response->Text)));
  2114. }
  2115. }
  2116. __finally
  2117. {
  2118. delete Response;
  2119. }
  2120. }
  2121. }
  2122. //---------------------------------------------------------------------------
  2123. void __fastcall TFTPFileSystem::DoReadDirectory(TRemoteFileList * FileList)
  2124. {
  2125. FileList->Reset();
  2126. // FZAPI does not list parent directory, add it
  2127. FileList->AddFile(new TRemoteParentDirectory(FTerminal));
  2128. FLastReadDirectoryProgress = 0;
  2129. TFileListHelper Helper(this, FileList, false);
  2130. // always specify path to list, do not attempt to list "current" dir as:
  2131. // 1) List() lists again the last listed directory, not the current working directory
  2132. // 2) we handle this way the cached directory change
  2133. UnicodeString Directory = AbsolutePath(FileList->Directory, false);
  2134. FFileZillaIntf->List(Directory.c_str());
  2135. GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_ALLOW_CANCEL);
  2136. AutoDetectTimeDifference(FileList);
  2137. if (FTimeDifference != 0) // optimization
  2138. {
  2139. for (int Index = 0; Index < FileList->Count; Index++)
  2140. {
  2141. ApplyTimeDifference(FileList->Files[Index]);
  2142. }
  2143. }
  2144. FLastDataSent = Now();
  2145. }
  2146. //---------------------------------------------------------------------------
  2147. bool __fastcall TFTPFileSystem::TimeZoneDifferenceApplicable(TModificationFmt ModificationFmt)
  2148. {
  2149. // Full precision is available for MLST only, so we would not be here.
  2150. return (ModificationFmt == mfMDHM) || ALWAYS_FALSE(ModificationFmt == mfFull);
  2151. }
  2152. //---------------------------------------------------------------------------
  2153. void __fastcall TFTPFileSystem::ApplyTimeDifference(TRemoteFile * File)
  2154. {
  2155. // FTimeDifference is not only optimization, but also prevents assertion failing
  2156. // in TimeZoneDifferenceApplicable when the file has full precision
  2157. if ((FTimeDifference != 0) &&
  2158. TimeZoneDifferenceApplicable(File->ModificationFmt))
  2159. {
  2160. assert(File->Modification == File->LastAccess);
  2161. File->Modification = IncSecond(File->Modification, FTimeDifference);
  2162. File->LastAccess = IncSecond(File->LastAccess, FTimeDifference);
  2163. }
  2164. }
  2165. //---------------------------------------------------------------------------
  2166. void __fastcall TFTPFileSystem::AutoDetectTimeDifference(TRemoteFileList * FileList)
  2167. {
  2168. if (FDetectTimeDifference &&
  2169. // Does not support MLST/MLSD, but supports MDTM at least
  2170. !FFileZillaIntf->UsingMlsd() && SupportsReadingFile())
  2171. {
  2172. for (int Index = 0; Index < FileList->Count; Index++)
  2173. {
  2174. TRemoteFile * File = FileList->Files[Index];
  2175. // For directories, we do not do MDTM in ReadFile
  2176. // (it should not be problem to use them otherwise).
  2177. // We are also not interested in files with day precision only.
  2178. if (!File->IsDirectory && !File->IsSymLink &&
  2179. TimeZoneDifferenceApplicable(File->ModificationFmt))
  2180. {
  2181. FDetectTimeDifference = false;
  2182. TRemoteFile * UtcFile = NULL;
  2183. try
  2184. {
  2185. ReadFile(File->FullFileName, UtcFile);
  2186. }
  2187. catch (Exception & E)
  2188. {
  2189. if (!FTerminal->Active)
  2190. {
  2191. throw;
  2192. }
  2193. break;
  2194. }
  2195. TDateTime UtcModification = UtcFile->Modification;
  2196. delete UtcFile;
  2197. FTimeDifference = SecondsBetween(UtcModification, File->Modification);
  2198. UnicodeString LogMessage;
  2199. if (FTimeDifference == 0)
  2200. {
  2201. LogMessage = FORMAT(L"No timezone difference detected using file %s", (File->FullFileName));
  2202. }
  2203. else
  2204. {
  2205. LogMessage = FORMAT(L"Timezone difference of %s detected using file %s", (FormatTimeZone(FTimeDifference), File->FullFileName));
  2206. }
  2207. FTerminal->LogEvent(LogMessage);
  2208. break;
  2209. }
  2210. }
  2211. }
  2212. }
  2213. //---------------------------------------------------------------------------
  2214. void __fastcall TFTPFileSystem::ReadDirectory(TRemoteFileList * FileList)
  2215. {
  2216. // whole below "-a" logic is for LIST,
  2217. // if we know we are going to use MLSD, skip it
  2218. if (FFileZillaIntf->UsingMlsd())
  2219. {
  2220. DoReadDirectory(FileList);
  2221. }
  2222. else
  2223. {
  2224. bool GotNoFilesForAll = false;
  2225. bool Repeat;
  2226. do
  2227. {
  2228. Repeat = false;
  2229. try
  2230. {
  2231. FDoListAll = (FListAll == asAuto) || (FListAll == asOn);
  2232. DoReadDirectory(FileList);
  2233. // We got no files with "-a", but again no files w/o "-a",
  2234. // so it was not "-a"'s problem, revert to auto and let it decide the next time
  2235. if (GotNoFilesForAll && (FileList->Count == 0))
  2236. {
  2237. assert(FListAll == asOff);
  2238. FListAll = asAuto;
  2239. }
  2240. else if (FListAll == asAuto)
  2241. {
  2242. // some servers take "-a" as a mask and return empty directory listing
  2243. // (note that it's actually never empty here, there's always at least parent directory,
  2244. // added explicitly by DoReadDirectory)
  2245. if ((FileList->Count == 0) ||
  2246. ((FileList->Count == 1) && FileList->Files[0]->IsParentDirectory))
  2247. {
  2248. Repeat = true;
  2249. FListAll = asOff;
  2250. GotNoFilesForAll = true;
  2251. FTerminal->LogEvent(L"LIST with -a switch returned empty directory listing, will try pure LIST");
  2252. }
  2253. else
  2254. {
  2255. // reading first directory has succeeded, always use "-a"
  2256. FListAll = asOn;
  2257. }
  2258. }
  2259. // use "-a" even for implicit directory reading by FZAPI?
  2260. // (e.g. before file transfer)
  2261. FDoListAll = (FListAll == asOn);
  2262. }
  2263. catch(Exception & E)
  2264. {
  2265. FDoListAll = false;
  2266. // reading the first directory has failed,
  2267. // further try without "-a" only as the server may not support it
  2268. if (FListAll == asAuto)
  2269. {
  2270. FTerminal->LogEvent(L"LIST with -a failed, will try pure LIST");
  2271. if (!FTerminal->Active)
  2272. {
  2273. FTerminal->Reopen(ropNoReadDirectory);
  2274. }
  2275. FListAll = asOff;
  2276. Repeat = true;
  2277. }
  2278. else
  2279. {
  2280. throw;
  2281. }
  2282. }
  2283. }
  2284. while (Repeat);
  2285. }
  2286. }
  2287. //---------------------------------------------------------------------------
  2288. void __fastcall TFTPFileSystem::DoReadFile(const UnicodeString & AFileName,
  2289. TRemoteFile *& AFile)
  2290. {
  2291. UnicodeString FileName = AbsolutePath(AFileName, false);
  2292. UnicodeString FileNameOnly = UnixExtractFileName(FileName);
  2293. UnicodeString FilePath = UnixExtractFilePath(FileName);
  2294. TRemoteFileList * FileList = new TRemoteFileList();
  2295. try
  2296. {
  2297. // Duplicate() call below would use this to compose FullFileName
  2298. FileList->Directory = FilePath;
  2299. TFileListHelper Helper(this, FileList, false);
  2300. FFileZillaIntf->ListFile(FileNameOnly.c_str(), FilePath.c_str());
  2301. GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_ALLOW_CANCEL);
  2302. TRemoteFile * File = FileList->FindFile(FileNameOnly);
  2303. if (File != NULL)
  2304. {
  2305. AFile = File->Duplicate();
  2306. }
  2307. ApplyTimeDifference(AFile);
  2308. FLastDataSent = Now();
  2309. }
  2310. __finally
  2311. {
  2312. delete FileList;
  2313. }
  2314. }
  2315. //---------------------------------------------------------------------------
  2316. bool __fastcall TFTPFileSystem::SupportsReadingFile()
  2317. {
  2318. return
  2319. FFileZillaIntf->UsingMlsd() ||
  2320. ((FServerCapabilities->GetCapability(mdtm_command) == yes) &&
  2321. (FServerCapabilities->GetCapability(size_command) == yes));
  2322. }
  2323. //---------------------------------------------------------------------------
  2324. void __fastcall TFTPFileSystem::ReadFile(const UnicodeString FileName,
  2325. TRemoteFile *& File)
  2326. {
  2327. UnicodeString Path = UnixExtractFilePath(FileName);
  2328. UnicodeString NameOnly = UnixExtractFileName(FileName);
  2329. TRemoteFile *AFile = NULL;
  2330. bool Own;
  2331. if (SupportsReadingFile())
  2332. {
  2333. DoReadFile(FileName, AFile);
  2334. Own = true;
  2335. }
  2336. else
  2337. {
  2338. // FZAPI does not have efficient way to read properties of one file.
  2339. // In case we need properties of set of files from the same directory,
  2340. // cache the file list for future
  2341. if ((FFileListCache != NULL) &&
  2342. UnixSamePath(Path, FFileListCache->Directory) &&
  2343. (UnixIsAbsolutePath(FFileListCache->Directory) ||
  2344. (FFileListCachePath == CurrentDirectory)))
  2345. {
  2346. AFile = FFileListCache->FindFile(NameOnly);
  2347. }
  2348. // if cache is invalid or file is not in cache, (re)read the directory
  2349. if (AFile == NULL)
  2350. {
  2351. TRemoteFileList * FileListCache = new TRemoteFileList();
  2352. FileListCache->Directory = Path;
  2353. try
  2354. {
  2355. ReadDirectory(FileListCache);
  2356. }
  2357. catch(...)
  2358. {
  2359. delete FileListCache;
  2360. throw;
  2361. }
  2362. // set only after we successfully read the directory,
  2363. // otherwise, when we reconnect from ReadDirectory,
  2364. // the FFileListCache is reset from ResetCache.
  2365. delete FFileListCache;
  2366. FFileListCache = FileListCache;
  2367. FFileListCachePath = GetCurrentDirectory();
  2368. AFile = FFileListCache->FindFile(NameOnly);
  2369. }
  2370. Own = false;
  2371. }
  2372. if (AFile == NULL)
  2373. {
  2374. File = NULL;
  2375. throw Exception(FMTLOAD(FILE_NOT_EXISTS, (FileName)));
  2376. }
  2377. assert(AFile != NULL);
  2378. File = Own ? AFile : AFile->Duplicate();
  2379. }
  2380. //---------------------------------------------------------------------------
  2381. void __fastcall TFTPFileSystem::ReadSymlink(TRemoteFile * SymlinkFile,
  2382. TRemoteFile *& File)
  2383. {
  2384. // Resolving symlinks over FTP is big overhead
  2385. // (involves opening TCPIP connection for retrieving "directory listing").
  2386. // Moreover FZAPI does not support that anyway.
  2387. // Though nowadays we could use MLST to read the symlink.
  2388. File = new TRemoteFile(SymlinkFile);
  2389. try
  2390. {
  2391. File->Terminal = FTerminal;
  2392. File->FileName = UnixExtractFileName(SymlinkFile->LinkTo);
  2393. // FZAPI treats all symlink target as directories
  2394. File->Type = FILETYPE_DIRECTORY;
  2395. }
  2396. catch(...)
  2397. {
  2398. delete File;
  2399. File = NULL;
  2400. throw;
  2401. }
  2402. }
  2403. //---------------------------------------------------------------------------
  2404. void __fastcall TFTPFileSystem::RenameFile(const UnicodeString AFileName,
  2405. const UnicodeString ANewName)
  2406. {
  2407. UnicodeString FileName = AbsolutePath(AFileName, false);
  2408. UnicodeString NewName = AbsolutePath(ANewName, false);
  2409. UnicodeString FileNameOnly = UnixExtractFileName(FileName);
  2410. UnicodeString FilePathOnly = UnixExtractFilePath(FileName);
  2411. UnicodeString NewNameOnly = UnixExtractFileName(NewName);
  2412. UnicodeString NewPathOnly = UnixExtractFilePath(NewName);
  2413. {
  2414. // ignore file list
  2415. TFileListHelper Helper(this, NULL, true);
  2416. FFileZillaIntf->Rename(FileNameOnly.c_str(), NewNameOnly.c_str(),
  2417. FilePathOnly.c_str(), NewPathOnly.c_str());
  2418. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  2419. }
  2420. }
  2421. //---------------------------------------------------------------------------
  2422. void __fastcall TFTPFileSystem::CopyFile(const UnicodeString FileName,
  2423. const UnicodeString NewName)
  2424. {
  2425. assert(SupportsSiteCommand(CopySiteCommand));
  2426. EnsureLocation();
  2427. UnicodeString Command;
  2428. Command = FORMAT(L"%s CPFR %s", (SiteCommand, FileName));
  2429. SendCommand(Command);
  2430. GotReply(WaitForCommandReply(), REPLY_3XX_CODE);
  2431. Command = FORMAT(L"%s CPTO %s", (SiteCommand, NewName));
  2432. SendCommand(Command);
  2433. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  2434. }
  2435. //---------------------------------------------------------------------------
  2436. TStrings * __fastcall TFTPFileSystem::GetFixedPaths()
  2437. {
  2438. return NULL;
  2439. }
  2440. //---------------------------------------------------------------------------
  2441. void __fastcall TFTPFileSystem::SpaceAvailable(const UnicodeString Path,
  2442. TSpaceAvailable & ASpaceAvailable)
  2443. {
  2444. if (SupportsCommand(XQuotaCommand))
  2445. {
  2446. // WS_FTP:
  2447. // XQUOTA
  2448. // 213-File and disk usage
  2449. // File count: 3
  2450. // File limit: 10000
  2451. // Disk usage: 1532791
  2452. // Disk limit: 2048000
  2453. // 213 File and disk usage end
  2454. // XQUOTA is global not path-specific
  2455. UnicodeString Command = XQuotaCommand;
  2456. SendCommand(Command);
  2457. TStrings * Response = NULL;
  2458. GotReply(WaitForCommandReply(), REPLY_2XX_CODE, L"", NULL, &Response);
  2459. std::unique_ptr<TStrings> ResponseOwner(Response);
  2460. __int64 UsedBytes = -1;
  2461. for (int Index = 0; Index < Response->Count; Index++)
  2462. {
  2463. // trimming padding
  2464. UnicodeString Line = Trim(Response->Strings[Index]);
  2465. UnicodeString Label = CutToChar(Line, L':', true);
  2466. if (SameText(Label, L"Disk usage"))
  2467. {
  2468. UsedBytes = StrToInt64(Line);
  2469. }
  2470. else if (SameText(Label, L"Disk limit") && !SameText(Line, L"unlimited"))
  2471. {
  2472. ASpaceAvailable.BytesAvailableToUser = StrToInt64(Line);
  2473. }
  2474. }
  2475. if ((UsedBytes >= 0) && (ASpaceAvailable.BytesAvailableToUser > 0))
  2476. {
  2477. ASpaceAvailable.UnusedBytesAvailableToUser = ASpaceAvailable.BytesAvailableToUser - UsedBytes;
  2478. }
  2479. }
  2480. else if (SupportsCommand(AvblCommand))
  2481. {
  2482. // draft-peterson-streamlined-ftp-command-extensions-10
  2483. // Implemented by Serv-U.
  2484. UnicodeString Command = FORMAT(L"%s %s", (AvblCommand, Path));
  2485. SendCommand(Command);
  2486. UnicodeString Response = GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_SINGLE_LINE);
  2487. ASpaceAvailable.UnusedBytesAvailableToUser = StrToInt64(Response);
  2488. }
  2489. }
  2490. //---------------------------------------------------------------------------
  2491. const TSessionInfo & __fastcall TFTPFileSystem::GetSessionInfo()
  2492. {
  2493. return FSessionInfo;
  2494. }
  2495. //---------------------------------------------------------------------------
  2496. const TFileSystemInfo & __fastcall TFTPFileSystem::GetFileSystemInfo(bool /*Retrieve*/)
  2497. {
  2498. if (!FFileSystemInfoValid)
  2499. {
  2500. FFileSystemInfo.RemoteSystem = FSystem;
  2501. FFileSystemInfo.RemoteSystem.Unique();
  2502. if (FFeatures->Count == 0)
  2503. {
  2504. FFileSystemInfo.AdditionalInfo = LoadStr(FTP_NO_FEATURE_INFO);
  2505. }
  2506. else
  2507. {
  2508. FFileSystemInfo.AdditionalInfo =
  2509. FORMAT(L"%s\r\n", (LoadStr(FTP_FEATURE_INFO)));
  2510. for (int Index = 0; Index < FFeatures->Count; Index++)
  2511. {
  2512. // For TrimLeft, refer to HandleFeatReply
  2513. FFileSystemInfo.AdditionalInfo += FORMAT(L" %s\r\n", (TrimLeft(FFeatures->Strings[Index])));
  2514. }
  2515. }
  2516. FTerminal->SaveCapabilities(FFileSystemInfo);
  2517. FFileSystemInfoValid = true;
  2518. }
  2519. return FFileSystemInfo;
  2520. }
  2521. //---------------------------------------------------------------------------
  2522. bool __fastcall TFTPFileSystem::TemporaryTransferFile(const UnicodeString & /*FileName*/)
  2523. {
  2524. return false;
  2525. }
  2526. //---------------------------------------------------------------------------
  2527. bool __fastcall TFTPFileSystem::GetStoredCredentialsTried()
  2528. {
  2529. return !FTerminal->SessionData->Password.IsEmpty();
  2530. }
  2531. //---------------------------------------------------------------------------
  2532. UnicodeString __fastcall TFTPFileSystem::GetUserName()
  2533. {
  2534. return FUserName;
  2535. }
  2536. //---------------------------------------------------------------------------
  2537. UnicodeString __fastcall TFTPFileSystem::GetCurrentDirectory()
  2538. {
  2539. return FCurrentDirectory;
  2540. }
  2541. //---------------------------------------------------------------------------
  2542. const wchar_t * __fastcall TFTPFileSystem::GetOption(int OptionID) const
  2543. {
  2544. TSessionData * Data = FTerminal->SessionData;
  2545. switch (OptionID)
  2546. {
  2547. case OPTION_PROXYHOST:
  2548. case OPTION_FWHOST:
  2549. FOptionScratch = Data->ProxyHost;
  2550. break;
  2551. case OPTION_PROXYUSER:
  2552. case OPTION_FWUSER:
  2553. FOptionScratch = Data->ProxyUsername;
  2554. break;
  2555. case OPTION_PROXYPASS:
  2556. case OPTION_FWPASS:
  2557. FOptionScratch = Data->ProxyPassword;
  2558. break;
  2559. case OPTION_TRANSFERIP:
  2560. FOptionScratch = FTerminal->Configuration->ExternalIpAddress;
  2561. break;
  2562. case OPTION_ANONPWD:
  2563. case OPTION_TRANSFERIP6:
  2564. FOptionScratch = L"";
  2565. break;
  2566. default:
  2567. FAIL;
  2568. FOptionScratch = L"";
  2569. }
  2570. return FOptionScratch.c_str();
  2571. }
  2572. //---------------------------------------------------------------------------
  2573. int __fastcall TFTPFileSystem::GetOptionVal(int OptionID) const
  2574. {
  2575. TSessionData * Data = FTerminal->SessionData;
  2576. int Result;
  2577. switch (OptionID)
  2578. {
  2579. case OPTION_PROXYTYPE:
  2580. switch (Data->ProxyMethod)
  2581. {
  2582. case ::pmNone:
  2583. Result = 0; // PROXYTYPE_NOPROXY;
  2584. break;
  2585. case pmSocks4:
  2586. Result = 2; // PROXYTYPE_SOCKS4A
  2587. break;
  2588. case pmSocks5:
  2589. Result = 3; // PROXYTYPE_SOCKS5
  2590. break;
  2591. case pmHTTP:
  2592. Result = 4; // PROXYTYPE_HTTP11
  2593. break;
  2594. case pmTelnet:
  2595. case pmCmd:
  2596. default:
  2597. FAIL;
  2598. Result = 0; // PROXYTYPE_NOPROXY;
  2599. break;
  2600. }
  2601. break;
  2602. case OPTION_PROXYPORT:
  2603. case OPTION_FWPORT:
  2604. Result = Data->ProxyPort;
  2605. break;
  2606. case OPTION_PROXYUSELOGON:
  2607. Result = !Data->ProxyUsername.IsEmpty();
  2608. break;
  2609. case OPTION_LOGONTYPE:
  2610. Result = Data->FtpProxyLogonType;
  2611. break;
  2612. case OPTION_TIMEOUTLENGTH:
  2613. Result = Data->Timeout;
  2614. break;
  2615. case OPTION_DEBUGSHOWLISTING:
  2616. Result = true;
  2617. break;
  2618. case OPTION_PASV:
  2619. // should never get here t_server.nPasv being nonzero
  2620. FAIL;
  2621. Result = FALSE;
  2622. break;
  2623. case OPTION_PRESERVEDOWNLOADFILETIME:
  2624. case OPTION_MPEXT_PRESERVEUPLOADFILETIME:
  2625. Result = FFileTransferPreserveTime ? TRUE : FALSE;
  2626. break;
  2627. case OPTION_LIMITPORTRANGE:
  2628. Result = FALSE;
  2629. break;
  2630. case OPTION_PORTRANGELOW:
  2631. case OPTION_PORTRANGEHIGH:
  2632. // should never get here OPTION_LIMITPORTRANGE being zero
  2633. FAIL;
  2634. Result = 0;
  2635. break;
  2636. case OPTION_ENABLE_IPV6:
  2637. Result = ((Data->AddressFamily == afIPv6) ? TRUE : FALSE);
  2638. break;
  2639. case OPTION_KEEPALIVE:
  2640. Result = ((Data->FtpPingType != ptOff) ? TRUE : FALSE);
  2641. break;
  2642. case OPTION_INTERVALLOW:
  2643. case OPTION_INTERVALHIGH:
  2644. Result = Data->FtpPingInterval;
  2645. break;
  2646. case OPTION_VMSALLREVISIONS:
  2647. Result = FALSE;
  2648. break;
  2649. case OPTION_SPEEDLIMIT_DOWNLOAD_TYPE:
  2650. case OPTION_SPEEDLIMIT_UPLOAD_TYPE:
  2651. Result = (FFileTransferCPSLimit == 0 ? 0 : 1);
  2652. break;
  2653. case OPTION_SPEEDLIMIT_DOWNLOAD_VALUE:
  2654. case OPTION_SPEEDLIMIT_UPLOAD_VALUE:
  2655. Result = (FFileTransferCPSLimit / 1024); // FZAPI expects KB/s
  2656. break;
  2657. case OPTION_MPEXT_SHOWHIDDEN:
  2658. Result = (FDoListAll ? TRUE : FALSE);
  2659. break;
  2660. case OPTION_MPEXT_SSLSESSIONREUSE:
  2661. Result = (Data->SslSessionReuse ? TRUE : FALSE);
  2662. break;
  2663. case OPTION_MPEXT_MIN_TLS_VERSION:
  2664. Result = Data->MinTlsVersion;
  2665. break;
  2666. case OPTION_MPEXT_MAX_TLS_VERSION:
  2667. Result = Data->MaxTlsVersion;
  2668. break;
  2669. case OPTION_MPEXT_SNDBUF:
  2670. Result = Data->SendBuf;
  2671. break;
  2672. case OPTION_MPEXT_TRANSFER_ACTIVE_IMMEDIATELY:
  2673. Result = Data->FtpTransferActiveImmediately;
  2674. break;
  2675. case OPTION_MPEXT_REMOVE_BOM:
  2676. Result = FFileTransferRemoveBOM ? TRUE : FALSE;
  2677. break;
  2678. case OPTION_MPEXT_LOG_SENSITIVE:
  2679. Result = FTerminal->Configuration->LogSensitive ? TRUE : FALSE;
  2680. break;
  2681. case OPTION_MPEXT_HOST:
  2682. Result = (Data->FtpHost == asOn);
  2683. break;
  2684. case OPTION_MPEXT_NODELAY:
  2685. Result = Data->TcpNoDelay;
  2686. break;
  2687. default:
  2688. FAIL;
  2689. Result = FALSE;
  2690. break;
  2691. }
  2692. return Result;
  2693. }
  2694. //---------------------------------------------------------------------------
  2695. bool __fastcall TFTPFileSystem::PostMessage(unsigned int Type, WPARAM wParam, LPARAM lParam)
  2696. {
  2697. if (Type == TFileZillaIntf::MSG_TRANSFERSTATUS)
  2698. {
  2699. // Stop here if FileTransferProgress is proceeding,
  2700. // it makes "pause" in queue work.
  2701. // Paused queue item stops in some of the TFileOperationProgressType
  2702. // methods called from FileTransferProgress
  2703. TGuard Guard(FTransferStatusCriticalSection);
  2704. }
  2705. TGuard Guard(FQueueCriticalSection);
  2706. FQueue->push_back(TMessageQueue::value_type(wParam, lParam));
  2707. SetEvent(FQueueEvent);
  2708. return true;
  2709. }
  2710. //---------------------------------------------------------------------------
  2711. bool __fastcall TFTPFileSystem::ProcessMessage()
  2712. {
  2713. bool Result;
  2714. TMessageQueue::value_type Message;
  2715. {
  2716. TGuard Guard(FQueueCriticalSection);
  2717. Result = !FQueue->empty();
  2718. if (Result)
  2719. {
  2720. Message = FQueue->front();
  2721. FQueue->pop_front();
  2722. }
  2723. else
  2724. {
  2725. // now we are perfectly sure that the queue is empty as it is locked,
  2726. // so reset the event
  2727. ResetEvent(FQueueEvent);
  2728. }
  2729. }
  2730. if (Result)
  2731. {
  2732. FFileZillaIntf->HandleMessage(Message.first, Message.second);
  2733. }
  2734. return Result;
  2735. }
  2736. //---------------------------------------------------------------------------
  2737. void __fastcall TFTPFileSystem::DiscardMessages()
  2738. {
  2739. while (ProcessMessage());
  2740. }
  2741. //---------------------------------------------------------------------------
  2742. void __fastcall TFTPFileSystem::WaitForMessages()
  2743. {
  2744. unsigned int Result;
  2745. do
  2746. {
  2747. Result = WaitForSingleObject(FQueueEvent, GUIUpdateInterval);
  2748. FTerminal->ProcessGUI();
  2749. } while (Result == WAIT_TIMEOUT);
  2750. if (Result != WAIT_OBJECT_0)
  2751. {
  2752. FTerminal->FatalError(NULL, FMTLOAD(INTERNAL_ERROR, (L"ftp#1", IntToStr(int(Result)))));
  2753. }
  2754. }
  2755. //---------------------------------------------------------------------------
  2756. void __fastcall TFTPFileSystem::PoolForFatalNonCommandReply()
  2757. {
  2758. assert(FReply == 0);
  2759. assert(FCommandReply == 0);
  2760. assert(!FWaitingForReply);
  2761. FWaitingForReply = true;
  2762. unsigned int Reply;
  2763. try
  2764. {
  2765. // discard up to one reply
  2766. // (it should not happen here that two replies are posted anyway)
  2767. while (ProcessMessage() && (FReply == 0));
  2768. Reply = FReply;
  2769. }
  2770. __finally
  2771. {
  2772. FReply = 0;
  2773. assert(FCommandReply == 0);
  2774. FCommandReply = 0;
  2775. assert(FWaitingForReply);
  2776. FWaitingForReply = false;
  2777. }
  2778. if (Reply != 0)
  2779. {
  2780. // throws
  2781. GotNonCommandReply(Reply);
  2782. }
  2783. }
  2784. //---------------------------------------------------------------------------
  2785. bool __fastcall TFTPFileSystem::NoFinalLastCode()
  2786. {
  2787. return (FLastCodeClass == 0) || (FLastCodeClass == 1);
  2788. }
  2789. //---------------------------------------------------------------------------
  2790. bool __fastcall TFTPFileSystem::KeepWaitingForReply(unsigned int & ReplyToAwait, bool WantLastCode)
  2791. {
  2792. // to keep waiting,
  2793. // non-command reply must be unset,
  2794. // the reply we wait for must be unset or
  2795. // last code must be unset (if we wait for it)
  2796. return
  2797. (FReply == 0) &&
  2798. ((ReplyToAwait == 0) ||
  2799. (WantLastCode && NoFinalLastCode()));
  2800. }
  2801. //---------------------------------------------------------------------------
  2802. void __fastcall TFTPFileSystem::DoWaitForReply(unsigned int & ReplyToAwait, bool WantLastCode)
  2803. {
  2804. try
  2805. {
  2806. while (KeepWaitingForReply(ReplyToAwait, WantLastCode))
  2807. {
  2808. WaitForMessages();
  2809. // wait for the first reply only,
  2810. // i.e. in case two replies are posted get the first only.
  2811. // e.g. when server closes the connection, but posts error message before,
  2812. // sometime it happens that command (like download) fails because of the error
  2813. // and does not catch the disconnection. then asynchronous "disconnect reply"
  2814. // is posted immediately afterwards. leave detection of that to Idle()
  2815. while (ProcessMessage() && KeepWaitingForReply(ReplyToAwait, WantLastCode));
  2816. }
  2817. if (FReply != 0)
  2818. {
  2819. // throws
  2820. GotNonCommandReply(FReply);
  2821. }
  2822. }
  2823. catch(...)
  2824. {
  2825. // even if non-fatal error happens, we must process pending message,
  2826. // so that we "eat" the reply message, so that it gets not mistakenly
  2827. // associated with future connect
  2828. if (FTerminal->Active)
  2829. {
  2830. DoWaitForReply(ReplyToAwait, WantLastCode);
  2831. }
  2832. throw;
  2833. }
  2834. }
  2835. //---------------------------------------------------------------------------
  2836. unsigned int __fastcall TFTPFileSystem::WaitForReply(bool Command, bool WantLastCode)
  2837. {
  2838. assert(FReply == 0);
  2839. assert(FCommandReply == 0);
  2840. assert(!FWaitingForReply);
  2841. ResetReply();
  2842. FWaitingForReply = true;
  2843. unsigned int Reply;
  2844. try
  2845. {
  2846. unsigned int & ReplyToAwait = (Command ? FCommandReply : FReply);
  2847. DoWaitForReply(ReplyToAwait, WantLastCode);
  2848. Reply = ReplyToAwait;
  2849. }
  2850. __finally
  2851. {
  2852. FReply = 0;
  2853. FCommandReply = 0;
  2854. assert(FWaitingForReply);
  2855. FWaitingForReply = false;
  2856. }
  2857. return Reply;
  2858. }
  2859. //---------------------------------------------------------------------------
  2860. unsigned int __fastcall TFTPFileSystem::WaitForCommandReply(bool WantLastCode)
  2861. {
  2862. return WaitForReply(true, WantLastCode);
  2863. }
  2864. //---------------------------------------------------------------------------
  2865. void __fastcall TFTPFileSystem::WaitForFatalNonCommandReply()
  2866. {
  2867. WaitForReply(false, false);
  2868. FAIL;
  2869. }
  2870. //---------------------------------------------------------------------------
  2871. void __fastcall TFTPFileSystem::ResetReply()
  2872. {
  2873. FLastCode = 0;
  2874. FLastCodeClass = 0;
  2875. assert(FLastResponse != NULL);
  2876. FLastResponse->Clear();
  2877. assert(FLastErrorResponse != NULL);
  2878. FLastErrorResponse->Clear();
  2879. assert(FLastError != NULL);
  2880. FLastError->Clear();
  2881. }
  2882. //---------------------------------------------------------------------------
  2883. void __fastcall TFTPFileSystem::GotNonCommandReply(unsigned int Reply)
  2884. {
  2885. assert(FLAGSET(Reply, TFileZillaIntf::REPLY_DISCONNECTED));
  2886. GotReply(Reply);
  2887. // should never get here as GotReply should raise fatal exception
  2888. FAIL;
  2889. }
  2890. //---------------------------------------------------------------------------
  2891. UnicodeString __fastcall TFTPFileSystem::GotReply(unsigned int Reply, unsigned int Flags,
  2892. UnicodeString Error, unsigned int * Code, TStrings ** Response)
  2893. {
  2894. UnicodeString Result;
  2895. try
  2896. {
  2897. if (FLAGSET(Reply, TFileZillaIntf::REPLY_OK))
  2898. {
  2899. assert(Reply == TFileZillaIntf::REPLY_OK);
  2900. // With REPLY_2XX_CODE treat "OK" non-2xx code like an error.
  2901. // REPLY_3XX_CODE has to be always used along with REPLY_2XX_CODE.
  2902. if ((FLAGSET(Flags, REPLY_2XX_CODE) && (FLastCodeClass != 2)) &&
  2903. ((FLAGCLEAR(Flags, REPLY_3XX_CODE) || (FLastCodeClass != 3))))
  2904. {
  2905. GotReply(TFileZillaIntf::REPLY_ERROR, Flags, Error);
  2906. }
  2907. }
  2908. else if (FLAGSET(Reply, TFileZillaIntf::REPLY_CANCEL) &&
  2909. FLAGSET(Flags, REPLY_ALLOW_CANCEL))
  2910. {
  2911. assert(
  2912. (Reply == (TFileZillaIntf::REPLY_CANCEL | TFileZillaIntf::REPLY_ERROR)) ||
  2913. (Reply == (TFileZillaIntf::REPLY_ABORTED | TFileZillaIntf::REPLY_CANCEL | TFileZillaIntf::REPLY_ERROR)));
  2914. // noop
  2915. }
  2916. // we do not expect these with our usage of FZ
  2917. else if (Reply &
  2918. (TFileZillaIntf::REPLY_WOULDBLOCK | TFileZillaIntf::REPLY_OWNERNOTSET |
  2919. TFileZillaIntf::REPLY_INVALIDPARAM | TFileZillaIntf::REPLY_ALREADYCONNECTED |
  2920. TFileZillaIntf::REPLY_IDLE | TFileZillaIntf::REPLY_NOTINITIALIZED |
  2921. TFileZillaIntf::REPLY_ALREADYINIZIALIZED))
  2922. {
  2923. FTerminal->FatalError(NULL, FMTLOAD(INTERNAL_ERROR, (L"ftp#2", FORMAT(L"0x%x", (int(Reply))))));
  2924. }
  2925. else
  2926. {
  2927. // everything else must be an error or disconnect notification
  2928. assert(
  2929. FLAGSET(Reply, TFileZillaIntf::REPLY_ERROR) ||
  2930. FLAGSET(Reply, TFileZillaIntf::REPLY_DISCONNECTED));
  2931. // TODO: REPLY_CRITICALERROR ignored
  2932. // REPLY_NOTCONNECTED happens if connection is closed between moment
  2933. // when FZAPI interface method dispatches the command to FZAPI thread
  2934. // and moment when FZAPI thread receives the command
  2935. bool Disconnected =
  2936. FLAGSET(Reply, TFileZillaIntf::REPLY_DISCONNECTED) ||
  2937. FLAGSET(Reply, TFileZillaIntf::REPLY_NOTCONNECTED);
  2938. AnsiString HelpKeyword;
  2939. TStrings * MoreMessages = new TStringList();
  2940. try
  2941. {
  2942. if (Disconnected)
  2943. {
  2944. if (FLAGCLEAR(Flags, REPLY_CONNECT))
  2945. {
  2946. MoreMessages->Add(LoadStr(LOST_CONNECTION));
  2947. Discard();
  2948. FTerminal->Closed();
  2949. }
  2950. else
  2951. {
  2952. // For connection failure, do not report that connection was lost,
  2953. // its obvious.
  2954. // Also do not report to terminal that we are closed as
  2955. // that turns terminal into closed mode, but we want to
  2956. // pretend (at least with failed authentication) to retry
  2957. // with the same connection (as with SSH), so we explicitly
  2958. // close terminal in Open() only after we give up
  2959. Discard();
  2960. }
  2961. }
  2962. if (FLAGSET(Reply, TFileZillaIntf::REPLY_ABORTED))
  2963. {
  2964. MoreMessages->Add(LoadStr(USER_TERMINATED));
  2965. }
  2966. if (FLAGSET(Reply, TFileZillaIntf::REPLY_NOTSUPPORTED))
  2967. {
  2968. MoreMessages->Add(LoadStr(NOTSUPPORTED));
  2969. }
  2970. if (FLastCode == 530)
  2971. {
  2972. // Serv-U also uses this code in response to "SITE PSWD"
  2973. MoreMessages->Add(LoadStr(AUTHENTICATION_FAILED));
  2974. }
  2975. if (FLastCode == 425)
  2976. {
  2977. if (!FTerminal->SessionData->FtpPasvMode)
  2978. {
  2979. MoreMessages->Add(LoadStr(FTP_CANNOT_OPEN_ACTIVE_CONNECTION2));
  2980. HelpKeyword = HELP_FTP_CANNOT_OPEN_ACTIVE_CONNECTION;
  2981. }
  2982. }
  2983. if (FLastCode == DummyTimeoutCode)
  2984. {
  2985. HelpKeyword = HELP_ERRORMSG_TIMEOUT;
  2986. }
  2987. if (FLastCode == DummyDisconnectCode)
  2988. {
  2989. HelpKeyword = HELP_STATUSMSG_DISCONNECTED;
  2990. }
  2991. MoreMessages->AddStrings(FLastError);
  2992. // already cleared from WaitForReply, but GotReply can be also called
  2993. // from Closed. then make sure that error from previous command not
  2994. // associated with session closure is not reused
  2995. FLastError->Clear();
  2996. MoreMessages->AddStrings(FLastErrorResponse);
  2997. // see comment for FLastError
  2998. FLastResponse->Clear();
  2999. FLastErrorResponse->Clear();
  3000. if (MoreMessages->Count == 0)
  3001. {
  3002. delete MoreMessages;
  3003. MoreMessages = NULL;
  3004. }
  3005. }
  3006. catch(...)
  3007. {
  3008. delete MoreMessages;
  3009. throw;
  3010. }
  3011. if (Error.IsEmpty() && (MoreMessages != NULL))
  3012. {
  3013. assert(MoreMessages->Count > 0);
  3014. // bit too generic assigning of main instructions, let's see how it works
  3015. Error = MainInstructions(MoreMessages->Strings[0]);
  3016. MoreMessages->Delete(0);
  3017. }
  3018. if (Disconnected)
  3019. {
  3020. // for fatal error, it is essential that there is some message
  3021. assert(!Error.IsEmpty());
  3022. ExtException * E = new ExtException(Error, MoreMessages, true, HelpKeyword);
  3023. try
  3024. {
  3025. FTerminal->FatalError(E, L"");
  3026. }
  3027. __finally
  3028. {
  3029. delete E;
  3030. }
  3031. }
  3032. else
  3033. {
  3034. throw ExtException(Error, MoreMessages, true, HelpKeyword);
  3035. }
  3036. }
  3037. if ((Code != NULL) && (FLastCodeClass != DummyCodeClass))
  3038. {
  3039. *Code = FLastCode;
  3040. }
  3041. if (FLAGSET(Flags, REPLY_SINGLE_LINE))
  3042. {
  3043. if (FLastResponse->Count != 1)
  3044. {
  3045. throw Exception(FMTLOAD(FTP_RESPONSE_ERROR, (FLastCommandSent, FLastResponse->Text)));
  3046. }
  3047. Result = FLastResponse->Strings[0];
  3048. }
  3049. if (Response != NULL)
  3050. {
  3051. *Response = FLastResponse;
  3052. FLastResponse = new TStringList();
  3053. // just to be consistent
  3054. delete FLastErrorResponse;
  3055. FLastErrorResponse = new TStringList();
  3056. }
  3057. }
  3058. __finally
  3059. {
  3060. ResetReply();
  3061. }
  3062. return Result;
  3063. }
  3064. //---------------------------------------------------------------------------
  3065. void __fastcall TFTPFileSystem::SendCommand(const UnicodeString & Command)
  3066. {
  3067. FFileZillaIntf->CustomCommand(Command.c_str());
  3068. FLastCommandSent = CopyToChar(Command, L' ', false);
  3069. }
  3070. //---------------------------------------------------------------------------
  3071. void __fastcall TFTPFileSystem::SetLastCode(int Code)
  3072. {
  3073. FLastCode = Code;
  3074. FLastCodeClass = (Code / 100);
  3075. }
  3076. //---------------------------------------------------------------------------
  3077. void __fastcall TFTPFileSystem::StoreLastResponse(const UnicodeString & Text)
  3078. {
  3079. FLastResponse->Add(Text);
  3080. if (FLastCodeClass >= 4)
  3081. {
  3082. FLastErrorResponse->Add(Text);
  3083. }
  3084. }
  3085. //---------------------------------------------------------------------------
  3086. void __fastcall TFTPFileSystem::HandleReplyStatus(UnicodeString Response)
  3087. {
  3088. int Code;
  3089. if (FOnCaptureOutput != NULL)
  3090. {
  3091. FOnCaptureOutput(Response, cotOutput);
  3092. }
  3093. // Two forms of multiline responses were observed
  3094. // (the first is according to the RFC 959):
  3095. // 211-Features:
  3096. // MDTM
  3097. // REST STREAM
  3098. // SIZE
  3099. // 211 End
  3100. // 211-Features:
  3101. // 211-MDTM
  3102. // 211-REST STREAM
  3103. // 211-SIZE
  3104. // 211-AUTH TLS
  3105. // 211-PBSZ
  3106. // 211-PROT
  3107. // 211 End
  3108. // IIS 2003:
  3109. // 211-FEAT
  3110. // SIZE
  3111. // MDTM
  3112. // 211 END
  3113. bool HasCodePrefix =
  3114. (Response.Length() >= 3) &&
  3115. TryStrToInt(Response.SubString(1, 3), Code) &&
  3116. (Code >= 100) && (Code <= 599) &&
  3117. ((Response.Length() == 3) || (Response[4] == L' ') || (Response[4] == L'-'));
  3118. if (HasCodePrefix && !FMultineResponse)
  3119. {
  3120. FMultineResponse = (Response.Length() >= 4) && (Response[4] == L'-');
  3121. FLastResponse->Clear();
  3122. FLastErrorResponse->Clear();
  3123. SetLastCode(Code);
  3124. if (Response.Length() >= 5)
  3125. {
  3126. StoreLastResponse(Response.SubString(5, Response.Length() - 4));
  3127. }
  3128. }
  3129. else
  3130. {
  3131. int Start;
  3132. // response with code prefix
  3133. if (HasCodePrefix && (FLastCode == Code))
  3134. {
  3135. // End of multiline response?
  3136. if ((Response.Length() <= 3) || (Response[4] == L' '))
  3137. {
  3138. FMultineResponse = false;
  3139. }
  3140. Start = 5;
  3141. }
  3142. else
  3143. {
  3144. Start = (((Response.Length() >= 1) && (Response[1] == L' ')) ? 2 : 1);
  3145. }
  3146. // Intermediate empty lines are being added
  3147. if (FMultineResponse || (Response.Length() >= Start))
  3148. {
  3149. StoreLastResponse(Response.SubString(Start, Response.Length() - Start + 1));
  3150. }
  3151. }
  3152. if (!FMultineResponse)
  3153. {
  3154. if (FLastCode == 220)
  3155. {
  3156. // HOST command also uses 220 response.
  3157. // Neither our use of welcome messagfe is prepared for changing it
  3158. // during the session, so we keep the initial message only.
  3159. // Theoretically the welcome message can be host-specific,
  3160. // but IIS uses "220 Host accepted", and we are not interested in that anyway.
  3161. // Serv-U repeats the initial welcome message.
  3162. // WS_FTP uses "200 Command HOST succeed"
  3163. if (FWelcomeMessage.IsEmpty())
  3164. {
  3165. FWelcomeMessage = FLastResponse->Text;
  3166. if (FTerminal->Configuration->ShowFtpWelcomeMessage)
  3167. {
  3168. FTerminal->DisplayBanner(FWelcomeMessage);
  3169. }
  3170. }
  3171. }
  3172. else if (FLastCommand == PASS)
  3173. {
  3174. // 530 = "Not logged in."
  3175. if (FLastCode == 530)
  3176. {
  3177. FPasswordFailed = true;
  3178. }
  3179. }
  3180. else if (FLastCommand == SYST)
  3181. {
  3182. assert(FSystem.IsEmpty());
  3183. // Positive reply to "SYST" must be 215, see RFC 959
  3184. if (FLastCode == 215)
  3185. {
  3186. FSystem = FLastResponse->Text.TrimRight();
  3187. // full name is "Personal FTP Server PRO K6.0"
  3188. if ((FListAll == asAuto) &&
  3189. (FSystem.Pos(L"Personal FTP Server") > 0))
  3190. {
  3191. FTerminal->LogEvent(L"Server is known not to support LIST -a");
  3192. FListAll = asOff;
  3193. }
  3194. }
  3195. else
  3196. {
  3197. FSystem = L"";
  3198. }
  3199. }
  3200. else if (FLastCommand == FEAT)
  3201. {
  3202. HandleFeatReply();
  3203. }
  3204. }
  3205. }
  3206. //---------------------------------------------------------------------------
  3207. void __fastcall TFTPFileSystem::ResetFeatures()
  3208. {
  3209. FFeatures->Clear();
  3210. FSupportedCommands->Clear();
  3211. FSupportedSiteCommands->Clear();
  3212. FHashAlgs->Clear();
  3213. FSupportsAnyChecksumFeature = false;
  3214. }
  3215. //---------------------------------------------------------------------------
  3216. void __fastcall TFTPFileSystem::HandleFeatReply()
  3217. {
  3218. ResetFeatures();
  3219. // Response to FEAT must be multiline, where leading and trailing line
  3220. // is "meaningless". See RFC 2389.
  3221. if ((FLastCode == 211) && (FLastResponse->Count > 2))
  3222. {
  3223. FLastResponse->Delete(0);
  3224. FLastResponse->Delete(FLastResponse->Count - 1);
  3225. FFeatures->Assign(FLastResponse);
  3226. for (int Index = 0; Index < FFeatures->Count; Index++)
  3227. {
  3228. // IIS 2003 indents response by 4 spaces, instead of one,
  3229. // see example in HandleReplyStatus
  3230. UnicodeString Feature = TrimLeft(FFeatures->Strings[Index]);
  3231. UnicodeString Args = Feature;
  3232. UnicodeString Command = CutToChar(Args, L' ', true);
  3233. // Serv-U lists Xalg commands like:
  3234. // XSHA1 filename;start;end
  3235. FSupportedCommands->Add(Command);
  3236. if (SameText(Command, SiteCommand))
  3237. {
  3238. // Serv-U lists all SITE commands in one line like:
  3239. // SITE PSWD;SET;ZONE;CHMOD;MSG;EXEC;HELP
  3240. // But ProFTPD lists them separatelly:
  3241. // SITE UTIME
  3242. // SITE RMDIR
  3243. // SITE COPY
  3244. // SITE MKDIR
  3245. // SITE SYMLINK
  3246. while (!Args.IsEmpty())
  3247. {
  3248. UnicodeString Arg = CutToChar(Args, L';', true);
  3249. FSupportedSiteCommands->Add(Arg);
  3250. }
  3251. }
  3252. else if (SameText(Command, HashCommand))
  3253. {
  3254. while (!Args.IsEmpty())
  3255. {
  3256. UnicodeString Alg = CutToChar(Args, L';', true);
  3257. if ((Alg.Length() > 0) && (Alg[Alg.Length()] == L'*'))
  3258. {
  3259. Alg.Delete(Alg.Length(), 1);
  3260. }
  3261. // FTP HASH alg names follow IANA as we do,
  3262. // but using uppercase and we use lowercase
  3263. FHashAlgs->Add(LowerCase(Alg));
  3264. FSupportsAnyChecksumFeature = true;
  3265. }
  3266. }
  3267. if (FChecksumCommands->IndexOf(Command) >= 0)
  3268. {
  3269. FSupportsAnyChecksumFeature = true;
  3270. }
  3271. }
  3272. }
  3273. }
  3274. //---------------------------------------------------------------------------
  3275. UnicodeString __fastcall TFTPFileSystem::ExtractStatusMessage(UnicodeString Status)
  3276. {
  3277. // CApiLog::LogMessage
  3278. // (note that the formatting may not be present when LogMessageRaw is used)
  3279. int P1 = Status.Pos(L"): ");
  3280. if (P1 > 0)
  3281. {
  3282. int P2 = Status.Pos(L".cpp(");
  3283. if ((P2 > 0) && (P2 < P1))
  3284. {
  3285. int P3 = Status.Pos(L" caller=0x");
  3286. if ((P3 > 0) && (P3 > P1))
  3287. {
  3288. Status = Status.SubString(P1 + 3, P3 - P1 - 3);
  3289. }
  3290. }
  3291. }
  3292. return Status;
  3293. }
  3294. //---------------------------------------------------------------------------
  3295. bool __fastcall TFTPFileSystem::HandleStatus(const wchar_t * AStatus, int Type)
  3296. {
  3297. TLogLineType LogType = (TLogLineType)-1;
  3298. UnicodeString Status(AStatus);
  3299. switch (Type)
  3300. {
  3301. case TFileZillaIntf::LOG_STATUS:
  3302. FTerminal->Information(Status, true);
  3303. LogType = llMessage;
  3304. break;
  3305. case TFileZillaIntf::LOG_COMMAND:
  3306. if (Status == L"SYST")
  3307. {
  3308. // not to trigger the assert in HandleReplyStatus,
  3309. // when SYST command is used by the user
  3310. FSystem = "";
  3311. FLastCommand = SYST;
  3312. }
  3313. else if (Status == L"FEAT")
  3314. {
  3315. FLastCommand = FEAT;
  3316. }
  3317. else if (Status.SubString(1, 5) == L"PASS ")
  3318. {
  3319. FLastCommand = PASS;
  3320. }
  3321. else
  3322. {
  3323. FLastCommand = CMD_UNKNOWN;
  3324. }
  3325. LogType = llInput;
  3326. break;
  3327. case TFileZillaIntf::LOG_ERROR:
  3328. case TFileZillaIntf::LOG_APIERROR:
  3329. case TFileZillaIntf::LOG_WARNING:
  3330. // when timeout message occurs, break loop waiting for response code
  3331. // by setting dummy one
  3332. if (Type == TFileZillaIntf::LOG_ERROR)
  3333. {
  3334. if (StartsStr(FTimeoutStatus, Status))
  3335. {
  3336. if (NoFinalLastCode())
  3337. {
  3338. SetLastCode(DummyTimeoutCode);
  3339. }
  3340. }
  3341. else if (Status == FDisconnectStatus)
  3342. {
  3343. if (NoFinalLastCode())
  3344. {
  3345. SetLastCode(DummyDisconnectCode);
  3346. }
  3347. }
  3348. }
  3349. // there can be multiple error messages associated with single failure
  3350. // (such as "cannot open local file..." followed by "download failed")
  3351. Status = ExtractStatusMessage(Status);
  3352. FLastError->Add(Status);
  3353. LogType = llMessage;
  3354. break;
  3355. case TFileZillaIntf::LOG_PROGRESS:
  3356. LogType = llMessage;
  3357. break;
  3358. case TFileZillaIntf::LOG_REPLY:
  3359. HandleReplyStatus(AStatus);
  3360. LogType = llOutput;
  3361. break;
  3362. case TFileZillaIntf::LOG_INFO:
  3363. Status = ExtractStatusMessage(Status);
  3364. LogType = llMessage;
  3365. break;
  3366. case TFileZillaIntf::LOG_DEBUG:
  3367. // used for directory listing only
  3368. LogType = llMessage;
  3369. break;
  3370. default:
  3371. FAIL;
  3372. break;
  3373. }
  3374. if (FTerminal->Log->Logging && (LogType != (TLogLineType)-1))
  3375. {
  3376. FTerminal->Log->Add(LogType, Status);
  3377. }
  3378. return true;
  3379. }
  3380. //---------------------------------------------------------------------------
  3381. TDateTime __fastcall TFTPFileSystem::ConvertLocalTimestamp(time_t Time)
  3382. {
  3383. // This reverses how FZAPI converts FILETIME to time_t,
  3384. // before passing it to FZ_ASYNCREQUEST_OVERWRITE.
  3385. __int64 Timestamp;
  3386. tm * Tm = localtime(&Time);
  3387. if (Tm != NULL)
  3388. {
  3389. SYSTEMTIME SystemTime;
  3390. SystemTime.wYear = static_cast<WORD>(Tm->tm_year + 1900);
  3391. SystemTime.wMonth = static_cast<WORD>(Tm->tm_mon + 1);
  3392. SystemTime.wDayOfWeek = 0;
  3393. SystemTime.wDay = static_cast<WORD>(Tm->tm_mday);
  3394. SystemTime.wHour = static_cast<WORD>(Tm->tm_hour);
  3395. SystemTime.wMinute = static_cast<WORD>(Tm->tm_min);
  3396. SystemTime.wSecond = static_cast<WORD>(Tm->tm_sec);
  3397. SystemTime.wMilliseconds = 0;
  3398. FILETIME LocalTime;
  3399. SystemTimeToFileTime(&SystemTime, &LocalTime);
  3400. FILETIME FileTime;
  3401. LocalFileTimeToFileTime(&LocalTime, &FileTime);
  3402. Timestamp = ConvertTimestampToUnixSafe(FileTime, dstmUnix);
  3403. }
  3404. else
  3405. {
  3406. // incorrect, but at least something
  3407. Timestamp = Time;
  3408. }
  3409. return UnixToDateTime(Timestamp, dstmUnix);
  3410. }
  3411. //---------------------------------------------------------------------------
  3412. bool __fastcall TFTPFileSystem::HandleAsynchRequestOverwrite(
  3413. wchar_t * FileName1, size_t FileName1Len, const wchar_t * /*FileName2*/,
  3414. const wchar_t * /*Path1*/, const wchar_t * Path2,
  3415. __int64 Size1, __int64 Size2, time_t LocalTime,
  3416. bool /*HasLocalTime*/, const TRemoteFileTime & RemoteTime, void * AUserData, int & RequestResult)
  3417. {
  3418. if (!FActive)
  3419. {
  3420. return false;
  3421. }
  3422. else
  3423. {
  3424. TFileTransferData & UserData = *((TFileTransferData *)AUserData);
  3425. if (UserData.OverwriteResult >= 0)
  3426. {
  3427. // on retry, use the same answer as on the first attempt
  3428. RequestResult = UserData.OverwriteResult;
  3429. }
  3430. else
  3431. {
  3432. TFileOperationProgressType * OperationProgress = FTerminal->OperationProgress;
  3433. UnicodeString FileName = FileName1;
  3434. assert(UserData.FileName == FileName);
  3435. TOverwriteMode OverwriteMode = omOverwrite;
  3436. TOverwriteFileParams FileParams;
  3437. bool NoFileParams =
  3438. (Size1 < 0) || (LocalTime == 0) ||
  3439. (Size2 < 0) || !RemoteTime.HasDate;
  3440. if (!NoFileParams)
  3441. {
  3442. FileParams.SourceSize = Size2;
  3443. FileParams.DestSize = Size1;
  3444. if (OperationProgress->Side == osLocal)
  3445. {
  3446. FileParams.SourceTimestamp = ConvertLocalTimestamp(LocalTime);
  3447. RemoteFileTimeToDateTimeAndPrecision(RemoteTime, FileParams.DestTimestamp, FileParams.DestPrecision);
  3448. }
  3449. else
  3450. {
  3451. FileParams.DestTimestamp = ConvertLocalTimestamp(LocalTime);
  3452. RemoteFileTimeToDateTimeAndPrecision(RemoteTime, FileParams.SourceTimestamp, FileParams.SourcePrecision);
  3453. }
  3454. }
  3455. UnicodeString FullFileName = Path2;
  3456. if (OperationProgress->Side == osLocal)
  3457. {
  3458. FullFileName = IncludeTrailingBackslash(FullFileName);
  3459. }
  3460. else
  3461. {
  3462. FullFileName = UnixIncludeTrailingBackslash(FullFileName);
  3463. }
  3464. FullFileName += FileName;
  3465. if (ConfirmOverwrite(FullFileName, FileName, OverwriteMode, OperationProgress,
  3466. (NoFileParams ? NULL : &FileParams), UserData.CopyParam, UserData.Params,
  3467. UserData.AutoResume && UserData.CopyParam->AllowResume(FileParams.SourceSize)))
  3468. {
  3469. switch (OverwriteMode)
  3470. {
  3471. case omOverwrite:
  3472. if (FileName != FileName1)
  3473. {
  3474. wcsncpy(FileName1, FileName.c_str(), FileName1Len);
  3475. FileName1[FileName1Len - 1] = L'\0';
  3476. UserData.FileName = FileName1;
  3477. RequestResult = TFileZillaIntf::FILEEXISTS_RENAME;
  3478. }
  3479. else
  3480. {
  3481. RequestResult = TFileZillaIntf::FILEEXISTS_OVERWRITE;
  3482. }
  3483. break;
  3484. case omResume:
  3485. RequestResult = TFileZillaIntf::FILEEXISTS_RESUME;
  3486. break;
  3487. case omComplete:
  3488. FTerminal->LogEvent(L"File transfer was completed before disconnect");
  3489. RequestResult = TFileZillaIntf::FILEEXISTS_COMPLETE;
  3490. break;
  3491. default:
  3492. FAIL;
  3493. RequestResult = TFileZillaIntf::FILEEXISTS_OVERWRITE;
  3494. break;
  3495. }
  3496. }
  3497. else
  3498. {
  3499. RequestResult = TFileZillaIntf::FILEEXISTS_SKIP;
  3500. }
  3501. }
  3502. // remember the answer for the retries
  3503. UserData.OverwriteResult = RequestResult;
  3504. if (RequestResult == TFileZillaIntf::FILEEXISTS_SKIP)
  3505. {
  3506. // when user chooses not to overwrite, break loop waiting for response code
  3507. // by setting dummy one, as FZAPI won't do anything then
  3508. SetLastCode(DummyTimeoutCode);
  3509. }
  3510. return true;
  3511. }
  3512. }
  3513. //---------------------------------------------------------------------------
  3514. UnicodeString __fastcall FormatContactList(UnicodeString Entry1, UnicodeString Entry2)
  3515. {
  3516. if (!Entry1.IsEmpty() && !Entry2.IsEmpty())
  3517. {
  3518. return FORMAT(L"%s, %s", (Entry1, Entry2));
  3519. }
  3520. else
  3521. {
  3522. return Entry1 + Entry2;
  3523. }
  3524. }
  3525. //---------------------------------------------------------------------------
  3526. UnicodeString __fastcall FormatContact(const TFtpsCertificateData::TContact & Contact)
  3527. {
  3528. UnicodeString Result =
  3529. FORMAT(LoadStrPart(VERIFY_CERT_CONTACT, 1),
  3530. (FormatContactList(FormatContactList(FormatContactList(
  3531. Contact.Organization, Contact.Unit), Contact.CommonName), Contact.Mail)));
  3532. if ((wcslen(Contact.Country) > 0) ||
  3533. (wcslen(Contact.StateProvince) > 0) ||
  3534. (wcslen(Contact.Town) > 0))
  3535. {
  3536. Result +=
  3537. FORMAT(LoadStrPart(VERIFY_CERT_CONTACT, 2),
  3538. (FormatContactList(FormatContactList(
  3539. Contact.Country, Contact.StateProvince), Contact.Town)));
  3540. }
  3541. if (wcslen(Contact.Other) > 0)
  3542. {
  3543. Result += FORMAT(LoadStrPart(VERIFY_CERT_CONTACT, 3), (Contact.Other));
  3544. }
  3545. return Result;
  3546. }
  3547. //---------------------------------------------------------------------------
  3548. UnicodeString __fastcall FormatValidityTime(const TFtpsCertificateData::TValidityTime & ValidityTime)
  3549. {
  3550. return FormatDateTime(L"ddddd tt",
  3551. EncodeDateVerbose(
  3552. (unsigned short)ValidityTime.Year, (unsigned short)ValidityTime.Month,
  3553. (unsigned short)ValidityTime.Day) +
  3554. EncodeTimeVerbose(
  3555. (unsigned short)ValidityTime.Hour, (unsigned short)ValidityTime.Min,
  3556. (unsigned short)ValidityTime.Sec, 0));
  3557. }
  3558. //---------------------------------------------------------------------------
  3559. bool __fastcall VerifyNameMask(UnicodeString Name, UnicodeString Mask)
  3560. {
  3561. bool Result = true;
  3562. int Pos;
  3563. while (Result && (Pos = Mask.Pos(L"*")) > 0)
  3564. {
  3565. // Pos will typically be 1 here, so not actual comparison is done
  3566. Result = SameText(Mask.SubString(1, Pos - 1), Name.SubString(1, Pos - 1));
  3567. if (Result)
  3568. {
  3569. Mask.Delete(1, Pos); // including *
  3570. Name.Delete(1, Pos - 1);
  3571. // remove everything until the next dot
  3572. Pos = Name.Pos(L".");
  3573. if (Pos == 0)
  3574. {
  3575. Pos = Name.Length() + 1;
  3576. }
  3577. Name.Delete(1, Pos - 1);
  3578. }
  3579. }
  3580. if (Result)
  3581. {
  3582. Result = SameText(Mask, Name);
  3583. }
  3584. return Result;
  3585. }
  3586. //---------------------------------------------------------------------------
  3587. bool __fastcall TFTPFileSystem::VerifyCertificateHostName(const TFtpsCertificateData & Data)
  3588. {
  3589. UnicodeString HostName = FTerminal->SessionData->HostNameExpanded;
  3590. UnicodeString CommonName = Data.Subject.CommonName;
  3591. bool NoMask = CommonName.IsEmpty();
  3592. bool Result = !NoMask && VerifyNameMask(HostName, CommonName);
  3593. if (Result)
  3594. {
  3595. FTerminal->LogEvent(FORMAT(L"Certificate common name \"%s\" matches hostname", (CommonName)));
  3596. }
  3597. else
  3598. {
  3599. if (!NoMask && (FTerminal->Configuration->ActualLogProtocol >= 1))
  3600. {
  3601. FTerminal->LogEvent(FORMAT(L"Certificate common name \"%s\" does not match hostname", (CommonName)));
  3602. }
  3603. UnicodeString SubjectAltName = Data.SubjectAltName;
  3604. while (!Result && !SubjectAltName.IsEmpty())
  3605. {
  3606. UnicodeString Entry = CutToChar(SubjectAltName, L',', true);
  3607. UnicodeString EntryName = CutToChar(Entry, L':', true);
  3608. if (SameText(EntryName, L"DNS"))
  3609. {
  3610. NoMask = false;
  3611. Result = VerifyNameMask(HostName, Entry);
  3612. if (Result)
  3613. {
  3614. FTerminal->LogEvent(FORMAT(L"Certificate subject alternative name \"%s\" matches hostname", (Entry)));
  3615. }
  3616. else
  3617. {
  3618. if (FTerminal->Configuration->ActualLogProtocol >= 1)
  3619. {
  3620. FTerminal->LogEvent(FORMAT(L"Certificate subject alternative name \"%s\" does not match hostname", (Entry)));
  3621. }
  3622. }
  3623. }
  3624. }
  3625. }
  3626. if (!Result && NoMask)
  3627. {
  3628. FTerminal->LogEvent(L"Certificate has no common name nor subject alternative name, not verifying hostname");
  3629. Result = true;
  3630. }
  3631. return Result;
  3632. }
  3633. //---------------------------------------------------------------------------
  3634. bool __fastcall TFTPFileSystem::HandleAsynchRequestVerifyCertificate(
  3635. const TFtpsCertificateData & Data, int & RequestResult)
  3636. {
  3637. if (!FActive)
  3638. {
  3639. return false;
  3640. }
  3641. else
  3642. {
  3643. FSessionInfo.CertificateFingerprint =
  3644. BytesToHex(RawByteString((const char*)Data.Hash, Data.HashLen), false, L':');
  3645. UnicodeString CertificateSubject = Data.Subject.Organization;
  3646. FTerminal->LogEvent(FORMAT(L"Verifying certificate for \"%s\" with fingerprint %s and %d failures", (CertificateSubject, FSessionInfo.CertificateFingerprint, Data.VerificationResult)));
  3647. bool VerificationResult = false;
  3648. bool TryWindowsSystemCertificateStore = false;
  3649. UnicodeString VerificationResultStr;
  3650. switch (Data.VerificationResult)
  3651. {
  3652. case X509_V_OK:
  3653. VerificationResult = true;
  3654. break;
  3655. case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
  3656. VerificationResultStr = LoadStr(CERT_ERR_UNABLE_TO_GET_ISSUER_CERT);
  3657. TryWindowsSystemCertificateStore = true;
  3658. break;
  3659. case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
  3660. VerificationResultStr = LoadStr(CERT_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE);
  3661. break;
  3662. case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
  3663. VerificationResultStr = LoadStr(CERT_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY);
  3664. break;
  3665. case X509_V_ERR_CERT_SIGNATURE_FAILURE:
  3666. VerificationResultStr = LoadStr(CERT_ERR_CERT_SIGNATURE_FAILURE);
  3667. break;
  3668. case X509_V_ERR_CERT_NOT_YET_VALID:
  3669. VerificationResultStr = LoadStr(CERT_ERR_CERT_NOT_YET_VALID);
  3670. break;
  3671. case X509_V_ERR_CERT_HAS_EXPIRED:
  3672. VerificationResultStr = LoadStr(CERT_ERR_CERT_HAS_EXPIRED);
  3673. break;
  3674. case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
  3675. VerificationResultStr = LoadStr(CERT_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD);
  3676. break;
  3677. case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
  3678. VerificationResultStr = LoadStr(CERT_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD);
  3679. break;
  3680. case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
  3681. VerificationResultStr = LoadStr(CERT_ERR_DEPTH_ZERO_SELF_SIGNED_CERT);
  3682. TryWindowsSystemCertificateStore = true;
  3683. break;
  3684. case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
  3685. VerificationResultStr = LoadStr(CERT_ERR_SELF_SIGNED_CERT_IN_CHAIN);
  3686. TryWindowsSystemCertificateStore = true;
  3687. break;
  3688. case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
  3689. VerificationResultStr = LoadStr(CERT_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY);
  3690. TryWindowsSystemCertificateStore = true;
  3691. break;
  3692. case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
  3693. VerificationResultStr = LoadStr(CERT_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
  3694. TryWindowsSystemCertificateStore = true;
  3695. break;
  3696. case X509_V_ERR_INVALID_CA:
  3697. VerificationResultStr = LoadStr(CERT_ERR_INVALID_CA);
  3698. break;
  3699. case X509_V_ERR_PATH_LENGTH_EXCEEDED:
  3700. VerificationResultStr = LoadStr(CERT_ERR_PATH_LENGTH_EXCEEDED);
  3701. break;
  3702. case X509_V_ERR_INVALID_PURPOSE:
  3703. VerificationResultStr = LoadStr(CERT_ERR_INVALID_PURPOSE);
  3704. break;
  3705. case X509_V_ERR_CERT_UNTRUSTED:
  3706. VerificationResultStr = LoadStr(CERT_ERR_CERT_UNTRUSTED);
  3707. TryWindowsSystemCertificateStore = true;
  3708. break;
  3709. case X509_V_ERR_CERT_REJECTED:
  3710. VerificationResultStr = LoadStr(CERT_ERR_CERT_REJECTED);
  3711. break;
  3712. case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
  3713. VerificationResultStr = LoadStr(CERT_ERR_KEYUSAGE_NO_CERTSIGN);
  3714. break;
  3715. case X509_V_ERR_CERT_CHAIN_TOO_LONG:
  3716. VerificationResultStr = LoadStr(CERT_ERR_CERT_CHAIN_TOO_LONG);
  3717. break;
  3718. default:
  3719. VerificationResultStr =
  3720. FORMAT(L"%s (%s)",
  3721. (LoadStr(CERT_ERR_UNKNOWN), X509_verify_cert_error_string(Data.VerificationResult)));
  3722. break;
  3723. }
  3724. // TryWindowsSystemCertificateStore is set for the same set of failures
  3725. // as trigger NE_SSL_UNTRUSTED flag in ne_openssl.c's verify_callback()
  3726. if (!VerificationResult && TryWindowsSystemCertificateStore)
  3727. {
  3728. if (WindowsValidateCertificate(Data.Certificate, Data.CertificateLen))
  3729. {
  3730. FTerminal->LogEvent(L"Certificate verified against Windows certificate store");
  3731. VerificationResult = true;
  3732. }
  3733. }
  3734. UnicodeString Summary;
  3735. if (!VerificationResult)
  3736. {
  3737. Summary = VerificationResultStr + L" " + FMTLOAD(CERT_ERRDEPTH, (Data.VerificationDepth + 1));
  3738. }
  3739. if (!VerifyCertificateHostName(Data))
  3740. {
  3741. VerificationResult = false;
  3742. AddToList(Summary, FMTLOAD(CERT_NAME_MISMATCH, (FTerminal->SessionData->HostNameExpanded)), L"\n\n");
  3743. }
  3744. if (VerificationResult)
  3745. {
  3746. Summary = LoadStr(CERT_OK);
  3747. }
  3748. FSessionInfo.Certificate =
  3749. FMTLOAD(CERT_TEXT, (
  3750. FormatContact(Data.Issuer),
  3751. FormatContact(Data.Subject),
  3752. FormatValidityTime(Data.ValidFrom),
  3753. FormatValidityTime(Data.ValidUntil),
  3754. FSessionInfo.CertificateFingerprint,
  3755. Summary));
  3756. RequestResult = 0;
  3757. if (VerificationResult)
  3758. {
  3759. RequestResult = 1;
  3760. }
  3761. if (RequestResult == 0)
  3762. {
  3763. if (FTerminal->VerifyCertificate(CertificateStorageKey,
  3764. FSessionInfo.CertificateFingerprint, CertificateSubject, Data.VerificationResult))
  3765. {
  3766. RequestResult = 1;
  3767. }
  3768. }
  3769. if (RequestResult == 0)
  3770. {
  3771. TClipboardHandler ClipboardHandler;
  3772. ClipboardHandler.Text = FSessionInfo.CertificateFingerprint;
  3773. TQueryButtonAlias Aliases[1];
  3774. Aliases[0].Button = qaRetry;
  3775. Aliases[0].Alias = LoadStr(COPY_KEY_BUTTON);
  3776. Aliases[0].OnClick = &ClipboardHandler.Copy;
  3777. TQueryParams Params;
  3778. Params.HelpKeyword = HELP_VERIFY_CERTIFICATE;
  3779. Params.NoBatchAnswers = qaYes | qaRetry;
  3780. Params.Aliases = Aliases;
  3781. Params.AliasesCount = LENOF(Aliases);
  3782. unsigned int Answer = FTerminal->QueryUser(
  3783. FMTLOAD(VERIFY_CERT_PROMPT3, (FSessionInfo.Certificate)),
  3784. NULL, qaYes | qaNo | qaCancel | qaRetry, &Params, qtWarning);
  3785. switch (Answer)
  3786. {
  3787. case qaYes:
  3788. // 2 = always, as used by FZ's VerifyCertDlg.cpp,
  3789. // however FZAPI takes all non-zero values equally
  3790. RequestResult = 2;
  3791. break;
  3792. case qaNo:
  3793. RequestResult = 1;
  3794. break;
  3795. case qaCancel:
  3796. FTerminal->Configuration->Usage->Inc(L"HostNotVerified");
  3797. RequestResult = 0;
  3798. break;
  3799. default:
  3800. FAIL;
  3801. RequestResult = 0;
  3802. break;
  3803. }
  3804. if (RequestResult == 2)
  3805. {
  3806. FTerminal->CacheCertificate(
  3807. CertificateStorageKey, FSessionInfo.CertificateFingerprint, Data.VerificationResult);
  3808. }
  3809. }
  3810. return true;
  3811. }
  3812. }
  3813. //---------------------------------------------------------------------------
  3814. bool __fastcall TFTPFileSystem::HandleAsynchRequestNeedPass(
  3815. struct TNeedPassRequestData & Data, int & RequestResult)
  3816. {
  3817. if (!FActive)
  3818. {
  3819. return false;
  3820. }
  3821. else
  3822. {
  3823. UnicodeString Password = L"";
  3824. if (FTerminal->PromptUser(FTerminal->SessionData, pkPassword, LoadStr(PASSWORD_TITLE), L"",
  3825. LoadStr(PASSWORD_PROMPT), false, 0, Password))
  3826. {
  3827. Data.Password = _wcsdup(Password.c_str());
  3828. RequestResult = TFileZillaIntf::REPLY_OK;
  3829. }
  3830. else
  3831. {
  3832. RequestResult = TFileZillaIntf::REPLY_ABORTED;
  3833. }
  3834. return true;
  3835. }
  3836. }
  3837. //---------------------------------------------------------------------------
  3838. void __fastcall TFTPFileSystem::RemoteFileTimeToDateTimeAndPrecision(const TRemoteFileTime & Source, TDateTime & DateTime, TModificationFmt & ModificationFmt)
  3839. {
  3840. // ModificationFmt must be set after Modification
  3841. if (Source.HasDate)
  3842. {
  3843. DateTime =
  3844. EncodeDateVerbose((unsigned short)Source.Year, (unsigned short)Source.Month,
  3845. (unsigned short)Source.Day);
  3846. if (Source.HasTime)
  3847. {
  3848. DateTime = DateTime +
  3849. EncodeTimeVerbose((unsigned short)Source.Hour, (unsigned short)Source.Minute,
  3850. (unsigned short)Source.Second, 0);
  3851. // not exact as we got year as well, but it is most probably
  3852. // guessed by FZAPI anyway
  3853. ModificationFmt = Source.HasSeconds ? mfFull : mfMDHM;
  3854. }
  3855. else
  3856. {
  3857. ModificationFmt = mfMDY;
  3858. }
  3859. if (Source.Utc)
  3860. {
  3861. DateTime = ConvertTimestampFromUTC(DateTime);
  3862. }
  3863. }
  3864. else
  3865. {
  3866. // With SCP we estimate date to be today, if we have at least time
  3867. DateTime = double(0);
  3868. ModificationFmt = mfNone;
  3869. }
  3870. }
  3871. //---------------------------------------------------------------------------
  3872. bool __fastcall TFTPFileSystem::HandleListData(const wchar_t * Path,
  3873. const TListDataEntry * Entries, unsigned int Count)
  3874. {
  3875. if (!FActive)
  3876. {
  3877. return false;
  3878. }
  3879. else if (FIgnoreFileList)
  3880. {
  3881. // directory listing provided implicitly by FZAPI during certain operations is ignored
  3882. assert(FFileList == NULL);
  3883. return false;
  3884. }
  3885. else
  3886. {
  3887. assert(FFileList != NULL);
  3888. // This can actually fail in real life,
  3889. // when connected to server with case insensitive paths.
  3890. // Is empty when called from DoReadFile
  3891. assert(FFileList->Directory.IsEmpty() || UnixSamePath(AbsolutePath(FFileList->Directory, false), Path));
  3892. USEDPARAM(Path);
  3893. for (unsigned int Index = 0; Index < Count; Index++)
  3894. {
  3895. const TListDataEntry * Entry = &Entries[Index];
  3896. TRemoteFile * File = new TRemoteFile();
  3897. try
  3898. {
  3899. File->Terminal = FTerminal;
  3900. File->FileName = Entry->Name;
  3901. try
  3902. {
  3903. int PermissionsLen = wcslen(Entry->Permissions);
  3904. if (PermissionsLen >= 10)
  3905. {
  3906. File->Rights->Text = Entry->Permissions + 1;
  3907. }
  3908. else if ((PermissionsLen == 3) || (PermissionsLen == 4))
  3909. {
  3910. File->Rights->Octal = Entry->Permissions;
  3911. }
  3912. }
  3913. catch(...)
  3914. {
  3915. // ignore permissions errors with FTP
  3916. }
  3917. File->HumanRights = Entry->HumanPerm;
  3918. const wchar_t * Space = wcschr(Entry->OwnerGroup, L' ');
  3919. if (Space != NULL)
  3920. {
  3921. File->Owner.Name = UnicodeString(Entry->OwnerGroup, Space - Entry->OwnerGroup);
  3922. File->Group.Name = Space + 1;
  3923. }
  3924. else
  3925. {
  3926. File->Owner.Name = Entry->OwnerGroup;
  3927. }
  3928. File->Size = Entry->Size;
  3929. if (Entry->Link)
  3930. {
  3931. File->Type = FILETYPE_SYMLINK;
  3932. }
  3933. else if (Entry->Dir)
  3934. {
  3935. File->Type = FILETYPE_DIRECTORY;
  3936. }
  3937. else
  3938. {
  3939. File->Type = FILETYPE_DEFAULT;
  3940. }
  3941. TDateTime Modification;
  3942. TModificationFmt ModificationFmt;
  3943. RemoteFileTimeToDateTimeAndPrecision(Entry->Time, Modification, ModificationFmt);
  3944. File->Modification = Modification;
  3945. File->ModificationFmt = ModificationFmt;
  3946. File->LastAccess = File->Modification;
  3947. File->LinkTo = Entry->LinkTarget;
  3948. File->Complete();
  3949. }
  3950. catch (Exception & E)
  3951. {
  3952. delete File;
  3953. UnicodeString EntryData =
  3954. FORMAT(L"%s/%s/%s/%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
  3955. (Entry->Name, Entry->Permissions, Entry->HumanPerm, Entry->OwnerGroup, IntToStr(Entry->Size),
  3956. int(Entry->Dir), int(Entry->Link), Entry->Time.Year, Entry->Time.Month, Entry->Time.Day,
  3957. Entry->Time.Hour, Entry->Time.Minute, int(Entry->Time.HasTime),
  3958. int(Entry->Time.HasSeconds), int(Entry->Time.HasDate)));
  3959. throw ETerminal(&E, FMTLOAD(LIST_LINE_ERROR, (EntryData)), HELP_LIST_LINE_ERROR);
  3960. }
  3961. FFileList->AddFile(File);
  3962. }
  3963. return true;
  3964. }
  3965. }
  3966. //---------------------------------------------------------------------------
  3967. bool __fastcall TFTPFileSystem::HandleTransferStatus(bool Valid, __int64 TransferSize,
  3968. __int64 Bytes, int /*Percent*/, int /*TimeElapsed*/, int /*TimeLeft*/, int /*TransferRate*/,
  3969. bool FileTransfer)
  3970. {
  3971. if (!FActive)
  3972. {
  3973. return false;
  3974. }
  3975. else if (!Valid)
  3976. {
  3977. }
  3978. else if (FileTransfer)
  3979. {
  3980. FileTransferProgress(TransferSize, Bytes);
  3981. }
  3982. else
  3983. {
  3984. ReadDirectoryProgress(Bytes);
  3985. }
  3986. return true;
  3987. }
  3988. //---------------------------------------------------------------------------
  3989. bool __fastcall TFTPFileSystem::HandleReply(int Command, unsigned int Reply)
  3990. {
  3991. if (!FActive)
  3992. {
  3993. return false;
  3994. }
  3995. else
  3996. {
  3997. if (FTerminal->Configuration->ActualLogProtocol >= 1)
  3998. {
  3999. FTerminal->LogEvent(FORMAT(L"Got reply %x to the command %d", (int(Reply), Command)));
  4000. }
  4001. // reply with Command 0 is not associated with current operation
  4002. // so do not treat is as a reply
  4003. // (it is typically used asynchronously to notify about disconnects)
  4004. if (Command != 0)
  4005. {
  4006. assert(FCommandReply == 0);
  4007. FCommandReply = Reply;
  4008. }
  4009. else
  4010. {
  4011. assert(FReply == 0);
  4012. FReply = Reply;
  4013. }
  4014. return true;
  4015. }
  4016. }
  4017. //---------------------------------------------------------------------------
  4018. bool __fastcall TFTPFileSystem::HandleCapabilities(
  4019. TFTPServerCapabilities * ServerCapabilities)
  4020. {
  4021. FServerCapabilities->Assign(ServerCapabilities);
  4022. FFileSystemInfoValid = false;
  4023. return true;
  4024. }
  4025. //---------------------------------------------------------------------------
  4026. bool __fastcall TFTPFileSystem::CheckError(int ReturnCode, const wchar_t * Context)
  4027. {
  4028. // we do not expect any FZAPI call to fail as it generally can fail only due to:
  4029. // - invalid parameters
  4030. // - busy FZAPI core
  4031. // the only exception is REPLY_NOTCONNECTED that can happen if
  4032. // connection is closed just between the last call to Idle()
  4033. // and call to any FZAPI command
  4034. // in such case reply without associated command is posted,
  4035. // which we are going to wait for unless we are already waiting
  4036. // on higher level (this typically happens if connection is lost while
  4037. // waiting for user interaction and is detected within call to
  4038. // SetAsyncRequestResult)
  4039. if (FLAGSET(ReturnCode, TFileZillaIntf::REPLY_NOTCONNECTED))
  4040. {
  4041. if (!FWaitingForReply)
  4042. {
  4043. // throws
  4044. WaitForFatalNonCommandReply();
  4045. }
  4046. }
  4047. else
  4048. {
  4049. FTerminal->FatalError(NULL,
  4050. FMTLOAD(INTERNAL_ERROR, (FORMAT(L"fz#%s", (Context)), IntToHex(ReturnCode, 4))));
  4051. FAIL;
  4052. }
  4053. return false;
  4054. }
  4055. //---------------------------------------------------------------------------
  4056. bool __fastcall TFTPFileSystem::Unquote(UnicodeString & Str)
  4057. {
  4058. enum
  4059. {
  4060. INIT,
  4061. QUOTE,
  4062. QUOTED,
  4063. DONE
  4064. } State;
  4065. State = INIT;
  4066. assert((Str.Length() > 0) && ((Str[1] == L'"') || (Str[1] == L'\'')));
  4067. int Index = 1;
  4068. wchar_t Quote;
  4069. while (Index <= Str.Length())
  4070. {
  4071. switch (State)
  4072. {
  4073. case INIT:
  4074. if ((Str[Index] == L'"') || (Str[Index] == L'\''))
  4075. {
  4076. Quote = Str[Index];
  4077. State = QUOTED;
  4078. Str.Delete(Index, 1);
  4079. }
  4080. else
  4081. {
  4082. FAIL;
  4083. // no quoted string
  4084. Str.SetLength(0);
  4085. }
  4086. break;
  4087. case QUOTED:
  4088. if (Str[Index] == Quote)
  4089. {
  4090. State = QUOTE;
  4091. Str.Delete(Index, 1);
  4092. }
  4093. else
  4094. {
  4095. Index++;
  4096. }
  4097. break;
  4098. case QUOTE:
  4099. if (Str[Index] == Quote)
  4100. {
  4101. Index++;
  4102. }
  4103. else
  4104. {
  4105. // end of quoted string, trim the rest
  4106. Str.SetLength(Index - 1);
  4107. State = DONE;
  4108. }
  4109. break;
  4110. }
  4111. }
  4112. return (State == DONE);
  4113. }
  4114. //---------------------------------------------------------------------------
  4115. void __fastcall TFTPFileSystem::PreserveDownloadFileTime(HANDLE Handle, void * UserData)
  4116. {
  4117. TFileTransferData * Data = static_cast<TFileTransferData *>(UserData);
  4118. FILETIME WrTime = DateTimeToFileTime(Data->Modification, dstmUnix);
  4119. SetFileTime(Handle, NULL, NULL, &WrTime);
  4120. }
  4121. //---------------------------------------------------------------------------
  4122. bool __fastcall TFTPFileSystem::GetFileModificationTimeInUtc(const wchar_t * FileName, struct tm & Time)
  4123. {
  4124. bool Result;
  4125. try
  4126. {
  4127. // error-handling-free and DST-mode-inaware copy of TTerminal::OpenLocalFile
  4128. HANDLE Handle = CreateFile(ApiPath(FileName).c_str(), GENERIC_READ,
  4129. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
  4130. if (Handle == INVALID_HANDLE_VALUE)
  4131. {
  4132. Result = false;
  4133. }
  4134. else
  4135. {
  4136. FILETIME MTime;
  4137. if (!GetFileTime(Handle, NULL, NULL, &MTime))
  4138. {
  4139. Result = false;
  4140. }
  4141. else
  4142. {
  4143. TDateTime Modification = ConvertTimestampToUTC(FileTimeToDateTime(MTime));
  4144. unsigned short Year;
  4145. unsigned short Month;
  4146. unsigned short Day;
  4147. Modification.DecodeDate(&Year, &Month, &Day);
  4148. Time.tm_year = Year - 1900;
  4149. Time.tm_mon = Month - 1;
  4150. Time.tm_mday = Day;
  4151. unsigned short Hour;
  4152. unsigned short Min;
  4153. unsigned short Sec;
  4154. unsigned short MSec;
  4155. Modification.DecodeTime(&Hour, &Min, &Sec, &MSec);
  4156. Time.tm_hour = Hour;
  4157. Time.tm_min = Min;
  4158. Time.tm_sec = Sec;
  4159. Result = true;
  4160. }
  4161. CloseHandle(Handle);
  4162. }
  4163. }
  4164. catch (...)
  4165. {
  4166. Result = false;
  4167. }
  4168. return Result;
  4169. }
  4170. //---------------------------------------------------------------------------
  4171. void __fastcall TFTPFileSystem::RegisterChecksumAlgCommand(const UnicodeString & Alg, const UnicodeString & Command)
  4172. {
  4173. FChecksumAlgs->Add(Alg);
  4174. FChecksumCommands->Add(Command);
  4175. }
  4176. //---------------------------------------------------------------------------
  4177. void __fastcall TFTPFileSystem::GetSupportedChecksumAlgs(TStrings * Algs)
  4178. {
  4179. for (int Index = 0; Index < FHashAlgs->Count; Index++)
  4180. {
  4181. Algs->Add(FHashAlgs->Strings[Index]);
  4182. }
  4183. for (int Index = 0; Index < FChecksumAlgs->Count; Index++)
  4184. {
  4185. UnicodeString Alg = FChecksumAlgs->Strings[Index];
  4186. UnicodeString Command = FChecksumCommands->Strings[Index];
  4187. if (SupportsCommand(Command) && (Algs->IndexOf(Alg) < 0))
  4188. {
  4189. Algs->Add(Alg);
  4190. }
  4191. }
  4192. }
  4193. //---------------------------------------------------------------------------
  4194. bool __fastcall TFTPFileSystem::SupportsSiteCommand(const UnicodeString & Command) const
  4195. {
  4196. return (FSupportedSiteCommands->IndexOf(Command) >= 0);
  4197. }
  4198. //---------------------------------------------------------------------------
  4199. bool __fastcall TFTPFileSystem::SupportsCommand(const UnicodeString & Command) const
  4200. {
  4201. return (FSupportedCommands->IndexOf(Command) >= 0);
  4202. }
  4203. //---------------------------------------------------------------------------
  4204. UnicodeString __fastcall GetOpenSSLVersionText()
  4205. {
  4206. return OPENSSL_VERSION_TEXT;
  4207. }
  4208. //---------------------------------------------------------------------------
  4209. #endif NO_FILEZILLA