FtpFileSystem.cpp 153 KB

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