FtpFileSystem.cpp 113 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #ifndef NO_FILEZILLA
  5. //---------------------------------------------------------------------------
  6. #include <list>
  7. #define MPEXT
  8. #include "FtpFileSystem.h"
  9. #include "FileZillaIntf.h"
  10. #include "Common.h"
  11. #include "Exceptions.h"
  12. #include "Terminal.h"
  13. #include "TextsCore.h"
  14. #include "TextsFileZilla.h"
  15. #include "HelpCore.h"
  16. #define OPENSSL_NO_EC
  17. #define OPENSSL_NO_ECDSA
  18. #define OPENSSL_NO_ECDH
  19. #include <openssl/x509_vfy.h>
  20. //---------------------------------------------------------------------------
  21. #pragma package(smart_init)
  22. //---------------------------------------------------------------------------
  23. #define FILE_OPERATION_LOOP_EX(ALLOW_SKIP, MESSAGE, OPERATION) \
  24. FILE_OPERATION_LOOP_CUSTOM(FTerminal, ALLOW_SKIP, MESSAGE, OPERATION, L"")
  25. //---------------------------------------------------------------------------
  26. const int DummyCodeClass = 8;
  27. const int DummyTimeoutCode = 801;
  28. const int DummyCancelCode = 802;
  29. const int DummyDisconnectCode = 803;
  30. //---------------------------------------------------------------------------
  31. class TFileZillaImpl : public TFileZillaIntf
  32. {
  33. public:
  34. __fastcall TFileZillaImpl(TFTPFileSystem * FileSystem);
  35. virtual const wchar_t * __fastcall Option(int OptionID) const;
  36. virtual int __fastcall OptionVal(int OptionID) const;
  37. protected:
  38. virtual bool __fastcall DoPostMessage(TMessageType Type, WPARAM wParam, LPARAM lParam);
  39. virtual bool __fastcall HandleStatus(const wchar_t * Status, int Type);
  40. virtual bool __fastcall HandleAsynchRequestOverwrite(
  41. wchar_t * FileName1, size_t FileName1Len, const wchar_t * FileName2,
  42. const wchar_t * Path1, const wchar_t * Path2,
  43. __int64 Size1, __int64 Size2, time_t LocalTime,
  44. bool HasLocalTime, const TRemoteFileTime & RemoteTime, void * UserData, int & RequestResult);
  45. virtual bool __fastcall HandleAsynchRequestVerifyCertificate(
  46. const TFtpsCertificateData & Data, int & RequestResult);
  47. virtual bool __fastcall HandleAsynchRequestNeedPass(
  48. struct TNeedPassRequestData & Data, int & RequestResult);
  49. virtual bool __fastcall HandleListData(const wchar_t * Path, const TListDataEntry * Entries,
  50. unsigned int Count);
  51. virtual bool __fastcall HandleTransferStatus(bool Valid, __int64 TransferSize,
  52. __int64 Bytes, int Percent, int TimeElapsed, int TimeLeft, int TransferRate,
  53. bool FileTransfer);
  54. virtual bool __fastcall HandleReply(int Command, unsigned int Reply);
  55. virtual bool __fastcall HandleCapabilities(TFTPServerCapabilities * ServerCapabilities);
  56. virtual bool __fastcall CheckError(int ReturnCode, const wchar_t * Context);
  57. virtual void PreserveDownloadFileTime(HANDLE Handle, void * UserData);
  58. virtual bool GetFileModificationTimeInUtc(const wchar_t * FileName, struct tm & Time);
  59. private:
  60. TFTPFileSystem * FFileSystem;
  61. };
  62. //---------------------------------------------------------------------------
  63. __fastcall TFileZillaImpl::TFileZillaImpl(TFTPFileSystem * FileSystem) :
  64. TFileZillaIntf(),
  65. FFileSystem(FileSystem)
  66. {
  67. }
  68. //---------------------------------------------------------------------------
  69. const wchar_t * __fastcall TFileZillaImpl::Option(int OptionID) const
  70. {
  71. return FFileSystem->GetOption(OptionID);
  72. }
  73. //---------------------------------------------------------------------------
  74. int __fastcall TFileZillaImpl::OptionVal(int OptionID) const
  75. {
  76. return FFileSystem->GetOptionVal(OptionID);
  77. }
  78. //---------------------------------------------------------------------------
  79. bool __fastcall TFileZillaImpl::DoPostMessage(TMessageType Type, WPARAM wParam, LPARAM lParam)
  80. {
  81. return FFileSystem->PostMessage(Type, wParam, lParam);
  82. }
  83. //---------------------------------------------------------------------------
  84. bool __fastcall TFileZillaImpl::HandleStatus(const wchar_t * Status, int Type)
  85. {
  86. return FFileSystem->HandleStatus(Status, Type);
  87. }
  88. //---------------------------------------------------------------------------
  89. bool __fastcall TFileZillaImpl::HandleAsynchRequestOverwrite(
  90. wchar_t * FileName1, size_t FileName1Len, const wchar_t * FileName2,
  91. const wchar_t * Path1, const wchar_t * Path2,
  92. __int64 Size1, __int64 Size2, time_t LocalTime,
  93. bool HasLocalTime, const TRemoteFileTime & RemoteTime, void * UserData, int & RequestResult)
  94. {
  95. return FFileSystem->HandleAsynchRequestOverwrite(
  96. FileName1, FileName1Len, FileName2, Path1, Path2, Size1, Size2, LocalTime,
  97. HasLocalTime, RemoteTime, UserData, RequestResult);
  98. }
  99. //---------------------------------------------------------------------------
  100. bool __fastcall TFileZillaImpl::HandleAsynchRequestVerifyCertificate(
  101. const TFtpsCertificateData & Data, int & RequestResult)
  102. {
  103. return FFileSystem->HandleAsynchRequestVerifyCertificate(Data, RequestResult);
  104. }
  105. //---------------------------------------------------------------------------
  106. bool __fastcall TFileZillaImpl::HandleAsynchRequestNeedPass(
  107. struct TNeedPassRequestData & Data, int & RequestResult)
  108. {
  109. return FFileSystem->HandleAsynchRequestNeedPass(Data, RequestResult);
  110. }
  111. //---------------------------------------------------------------------------
  112. bool __fastcall TFileZillaImpl::HandleListData(const wchar_t * Path,
  113. const TListDataEntry * Entries, unsigned int Count)
  114. {
  115. return FFileSystem->HandleListData(Path, Entries, Count);
  116. }
  117. //---------------------------------------------------------------------------
  118. bool __fastcall TFileZillaImpl::HandleTransferStatus(bool Valid, __int64 TransferSize,
  119. __int64 Bytes, int Percent, int TimeElapsed, int TimeLeft, int TransferRate,
  120. bool FileTransfer)
  121. {
  122. return FFileSystem->HandleTransferStatus(Valid, TransferSize, Bytes, Percent,
  123. TimeElapsed, TimeLeft, TransferRate, FileTransfer);
  124. }
  125. //---------------------------------------------------------------------------
  126. bool __fastcall TFileZillaImpl::HandleReply(int Command, unsigned int Reply)
  127. {
  128. return FFileSystem->HandleReply(Command, Reply);
  129. }
  130. //---------------------------------------------------------------------------
  131. bool __fastcall TFileZillaImpl::HandleCapabilities(TFTPServerCapabilities * ServerCapabilities)
  132. {
  133. return FFileSystem->HandleCapabilities(ServerCapabilities);
  134. }
  135. //---------------------------------------------------------------------------
  136. bool __fastcall TFileZillaImpl::CheckError(int ReturnCode, const wchar_t * Context)
  137. {
  138. return FFileSystem->CheckError(ReturnCode, Context);
  139. }
  140. //---------------------------------------------------------------------------
  141. void TFileZillaImpl::PreserveDownloadFileTime(HANDLE Handle, void * UserData)
  142. {
  143. return FFileSystem->PreserveDownloadFileTime(Handle, UserData);
  144. }
  145. //---------------------------------------------------------------------------
  146. bool TFileZillaImpl::GetFileModificationTimeInUtc(const wchar_t * FileName, struct tm & Time)
  147. {
  148. return FFileSystem->GetFileModificationTimeInUtc(FileName, Time);
  149. }
  150. //---------------------------------------------------------------------------
  151. //---------------------------------------------------------------------------
  152. class TMessageQueue : public std::list<std::pair<WPARAM, LPARAM> >
  153. {
  154. };
  155. //---------------------------------------------------------------------------
  156. //---------------------------------------------------------------------------
  157. struct TFileTransferData
  158. {
  159. TFileTransferData()
  160. {
  161. Params = 0;
  162. AutoResume = false;
  163. OverwriteResult = -1;
  164. CopyParam = NULL;
  165. }
  166. UnicodeString FileName;
  167. int Params;
  168. bool AutoResume;
  169. int OverwriteResult;
  170. const TCopyParamType * CopyParam;
  171. TDateTime Modification;
  172. };
  173. //---------------------------------------------------------------------------
  174. const int tfFirstLevel = 0x01;
  175. const int tfAutoResume = 0x02;
  176. const wchar_t CertificateStorageKey[] = L"FtpsCertificates";
  177. //---------------------------------------------------------------------------
  178. struct TSinkFileParams
  179. {
  180. UnicodeString TargetDir;
  181. const TCopyParamType * CopyParam;
  182. int Params;
  183. TFileOperationProgressType * OperationProgress;
  184. bool Skipped;
  185. unsigned int Flags;
  186. };
  187. //---------------------------------------------------------------------------
  188. class TFileListHelper
  189. {
  190. public:
  191. TFileListHelper(TFTPFileSystem * FileSystem, TRemoteFileList * FileList,
  192. bool IgnoreFileList) :
  193. FFileSystem(FileSystem),
  194. FFileList(FFileSystem->FFileList),
  195. FIgnoreFileList(FFileSystem->FIgnoreFileList)
  196. {
  197. FFileSystem->FFileList = FileList;
  198. FFileSystem->FIgnoreFileList = IgnoreFileList;
  199. }
  200. ~TFileListHelper()
  201. {
  202. FFileSystem->FFileList = FFileList;
  203. FFileSystem->FIgnoreFileList = FIgnoreFileList;
  204. }
  205. private:
  206. TFTPFileSystem * FFileSystem;
  207. TRemoteFileList * FFileList;
  208. bool FIgnoreFileList;
  209. };
  210. //---------------------------------------------------------------------------
  211. __fastcall TFTPFileSystem::TFTPFileSystem(TTerminal * ATerminal):
  212. TCustomFileSystem(ATerminal),
  213. FFileZillaIntf(NULL),
  214. FQueueCriticalSection(new TCriticalSection),
  215. FTransferStatusCriticalSection(new TCriticalSection),
  216. FQueueEvent(CreateEvent(NULL, true, false, NULL)),
  217. FQueue(new TMessageQueue),
  218. FReply(0),
  219. FCommandReply(0),
  220. FLastCommand(CMD_UNKNOWN),
  221. FLastResponse(new TStringList()),
  222. FLastErrorResponse(new TStringList()),
  223. FLastError(new TStringList()),
  224. FFeatures(new TStringList()),
  225. FFileList(NULL),
  226. FFileListCache(NULL),
  227. FActive(false),
  228. FOpening(false),
  229. FWaitingForReply(false),
  230. FIgnoreFileList(false),
  231. FOnCaptureOutput(NULL),
  232. FFileSystemInfoValid(false),
  233. FDoListAll(false),
  234. FServerCapabilities(NULL)
  235. {
  236. ResetReply();
  237. FListAll = FTerminal->SessionData->FtpListAll;
  238. FFileSystemInfo.ProtocolBaseName = L"FTP";
  239. FFileSystemInfo.ProtocolName = FFileSystemInfo.ProtocolBaseName;
  240. FTimeoutStatus = LoadStr(IDS_ERRORMSG_TIMEOUT);
  241. FDisconnectStatus = LoadStr(IDS_STATUSMSG_DISCONNECTED);
  242. FServerCapabilities = new TFTPServerCapabilities();
  243. }
  244. //---------------------------------------------------------------------------
  245. __fastcall TFTPFileSystem::~TFTPFileSystem()
  246. {
  247. assert(FFileList == NULL);
  248. FFileZillaIntf->Destroying();
  249. // to release memory associated with the messages
  250. DiscardMessages();
  251. delete FFileZillaIntf;
  252. FFileZillaIntf = NULL;
  253. delete FQueue;
  254. FQueue = NULL;
  255. CloseHandle(FQueueEvent);
  256. delete FQueueCriticalSection;
  257. FQueueCriticalSection = NULL;
  258. delete FTransferStatusCriticalSection;
  259. FTransferStatusCriticalSection = NULL;
  260. delete FLastResponse;
  261. FLastResponse = NULL;
  262. delete FLastErrorResponse;
  263. FLastErrorResponse = NULL;
  264. delete FLastError;
  265. FLastError = NULL;
  266. delete FFeatures;
  267. FFeatures = NULL;
  268. delete FServerCapabilities;
  269. FServerCapabilities = NULL;
  270. ResetCaches();
  271. }
  272. //---------------------------------------------------------------------------
  273. void __fastcall TFTPFileSystem::Open()
  274. {
  275. // on reconnect, typically there may be pending status messages from previous session
  276. DiscardMessages();
  277. ResetCaches();
  278. FCurrentDirectory = L"";
  279. FHomeDirectory = L"";
  280. TSessionData * Data = FTerminal->SessionData;
  281. FSessionInfo.LoginTime = Now();
  282. switch (Data->Ftps)
  283. {
  284. case ftpsNone:
  285. // noop;
  286. break;
  287. case ftpsImplicit:
  288. FSessionInfo.SecurityProtocolName = LoadStr(FTPS_IMPLICIT);
  289. break;
  290. case ftpsExplicitSsl:
  291. FSessionInfo.SecurityProtocolName = LoadStr(FTPS_EXPLICIT_SSL);
  292. break;
  293. case ftpsExplicitTls:
  294. FSessionInfo.SecurityProtocolName = LoadStr(FTPS_EXPLICIT_TLS);
  295. break;
  296. default:
  297. FAIL;
  298. break;
  299. }
  300. FLastDataSent = Now();
  301. FMultineResponse = false;
  302. // initialize FZAPI on the first connect only
  303. if (FFileZillaIntf == NULL)
  304. {
  305. FFileZillaIntf = new TFileZillaImpl(this);
  306. try
  307. {
  308. TFileZillaIntf::TLogLevel LogLevel;
  309. switch (FTerminal->Configuration->ActualLogProtocol)
  310. {
  311. default:
  312. case 0:
  313. case 1:
  314. LogLevel = TFileZillaIntf::LOG_WARNING;
  315. break;
  316. case 2:
  317. LogLevel = TFileZillaIntf::LOG_INFO;
  318. break;
  319. }
  320. FFileZillaIntf->SetDebugLevel(LogLevel);
  321. FFileZillaIntf->Init();
  322. }
  323. catch(...)
  324. {
  325. delete FFileZillaIntf;
  326. FFileZillaIntf = NULL;
  327. throw;
  328. }
  329. }
  330. UnicodeString HostName = Data->HostNameExpanded;
  331. UnicodeString UserName = Data->UserNameExpanded;
  332. UnicodeString Password = Data->Password;
  333. UnicodeString Account = Data->FtpAccount;
  334. UnicodeString Path = Data->RemoteDirectory;
  335. int ServerType;
  336. switch (Data->Ftps)
  337. {
  338. case ftpsImplicit:
  339. ServerType = TFileZillaIntf::SERVER_FTP_SSL_IMPLICIT;
  340. break;
  341. case ftpsExplicitSsl:
  342. ServerType = TFileZillaIntf::SERVER_FTP_SSL_EXPLICIT;
  343. break;
  344. case ftpsExplicitTls:
  345. ServerType = TFileZillaIntf::SERVER_FTP_TLS_EXPLICIT;
  346. break;
  347. default:
  348. assert(Data->Ftps == ftpsNone);
  349. ServerType = TFileZillaIntf::SERVER_FTP;
  350. break;
  351. }
  352. int Pasv = (Data->FtpPasvMode ? 1 : 2);
  353. int TimeZoneOffset = TimeToMinutes(Data->TimeDifference);
  354. int UTF8 = 0;
  355. switch (Data->NotUtf)
  356. {
  357. case asOn:
  358. UTF8 = 2;
  359. break;
  360. case asOff:
  361. UTF8 = 1;
  362. break;
  363. case asAuto:
  364. UTF8 = 0;
  365. break;
  366. }
  367. FPasswordFailed = false;
  368. bool PromptedForCredentials = false;
  369. do
  370. {
  371. FSystem = L"";
  372. FFeatures->Clear();
  373. FFileSystemInfoValid = false;
  374. // TODO: the same for account? it ever used?
  375. // ask for username if it was not specified in advance, even on retry,
  376. // but keep previous one as default,
  377. if (Data->UserNameExpanded.IsEmpty())
  378. {
  379. FTerminal->LogEvent(L"Username prompt (no username provided)");
  380. if (!FPasswordFailed && !PromptedForCredentials)
  381. {
  382. FTerminal->Information(LoadStr(FTP_CREDENTIAL_PROMPT), false);
  383. PromptedForCredentials = true;
  384. }
  385. if (!FTerminal->PromptUser(Data, pkUserName, LoadStr(USERNAME_TITLE), L"",
  386. LoadStr(USERNAME_PROMPT2), true, 0, UserName))
  387. {
  388. FTerminal->FatalError(NULL, LoadStr(AUTHENTICATION_FAILED));
  389. }
  390. else
  391. {
  392. FUserName = UserName;
  393. }
  394. }
  395. // on retry ask for password
  396. if (FPasswordFailed)
  397. {
  398. FTerminal->LogEvent(L"Password prompt (last login attempt failed)");
  399. // on retry ask for new password
  400. Password = L"";
  401. if (!FTerminal->PromptUser(Data, pkPassword, LoadStr(PASSWORD_TITLE), L"",
  402. LoadStr(PASSWORD_PROMPT), false, 0, Password))
  403. {
  404. FTerminal->FatalError(NULL, LoadStr(AUTHENTICATION_FAILED));
  405. }
  406. }
  407. FPasswordFailed = false;
  408. TValueRestorer<bool> OpeningRestorer(FOpening);
  409. FOpening = true;
  410. FActive = FFileZillaIntf->Connect(
  411. HostName.c_str(), Data->PortNumber, UserName.c_str(),
  412. Password.c_str(), Account.c_str(), false, Path.c_str(),
  413. ServerType, Pasv, TimeZoneOffset, UTF8, Data->FtpForcePasvIp,
  414. Data->FtpUseMlsd);
  415. assert(FActive);
  416. try
  417. {
  418. // do not wait for FTP response code as Connect is complex operation
  419. GotReply(WaitForCommandReply(false), REPLY_CONNECT, LoadStr(CONNECTION_FAILED));
  420. Shred(Password);
  421. // we have passed, even if we got 530 on the way (if it is possible at all),
  422. // ignore it
  423. assert(!FPasswordFailed);
  424. FPasswordFailed = false;
  425. }
  426. catch(...)
  427. {
  428. if (FPasswordFailed)
  429. {
  430. FTerminal->Information(
  431. LoadStr(Password.IsEmpty() ? FTP_ACCESS_DENIED_EMPTY_PASSWORD : FTP_ACCESS_DENIED),
  432. false);
  433. }
  434. else
  435. {
  436. // see handling of REPLY_CONNECT in GotReply
  437. FTerminal->Closed();
  438. throw;
  439. }
  440. }
  441. }
  442. while (FPasswordFailed);
  443. FSessionInfo.CSCipher = FFileZillaIntf->GetCipherName().c_str();
  444. FSessionInfo.SCCipher = FSessionInfo.CSCipher;
  445. UnicodeString TlsVersionStr = FFileZillaIntf->GetTlsVersionStr().c_str();
  446. AddToList(FSessionInfo.SecurityProtocolName, TlsVersionStr, L", ");
  447. }
  448. //---------------------------------------------------------------------------
  449. void __fastcall TFTPFileSystem::Close()
  450. {
  451. assert(FActive);
  452. bool Result;
  453. if (FFileZillaIntf->Close(FOpening))
  454. {
  455. CHECK(FLAGSET(WaitForCommandReply(false), TFileZillaIntf::REPLY_DISCONNECTED));
  456. Result = true;
  457. }
  458. else
  459. {
  460. // See TFileZillaIntf::Close
  461. Result = FOpening;
  462. }
  463. if (ALWAYS_TRUE(Result))
  464. {
  465. assert(FActive);
  466. Discard();
  467. FTerminal->Closed();
  468. }
  469. }
  470. //---------------------------------------------------------------------------
  471. bool __fastcall TFTPFileSystem::GetActive()
  472. {
  473. return FActive;
  474. }
  475. //---------------------------------------------------------------------------
  476. void __fastcall TFTPFileSystem::CollectUsage()
  477. {
  478. if (FFileZillaIntf->UsingMlsd())
  479. {
  480. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPMLSD");
  481. }
  482. else
  483. {
  484. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPLIST");
  485. }
  486. if (FFileZillaIntf->UsingUtf8())
  487. {
  488. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPUTF8");
  489. }
  490. else
  491. {
  492. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPNonUTF8");
  493. }
  494. if (!CurrentDirectory.IsEmpty() && (CurrentDirectory[1] != L'/'))
  495. {
  496. if (IsUnixStyleWindowsPath(CurrentDirectory))
  497. {
  498. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPWindowsPath");
  499. }
  500. else if ((CurrentDirectory.Length() >= 3) && IsLetter(CurrentDirectory[1]) && (CurrentDirectory[2] == L':') && (CurrentDirectory[3] == L'/'))
  501. {
  502. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPRealWindowsPath");
  503. }
  504. else
  505. {
  506. FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPOtherPath");
  507. }
  508. }
  509. }
  510. //---------------------------------------------------------------------------
  511. void __fastcall TFTPFileSystem::Idle()
  512. {
  513. if (FActive && !FWaitingForReply)
  514. {
  515. PoolForFatalNonCommandReply();
  516. // Keep session alive
  517. if ((FTerminal->SessionData->FtpPingType != ptOff) &&
  518. (double(Now() - FLastDataSent) > double(FTerminal->SessionData->FtpPingIntervalDT) * 4))
  519. {
  520. FLastDataSent = Now();
  521. TRemoteDirectory * Files = new TRemoteDirectory(FTerminal);
  522. try
  523. {
  524. try
  525. {
  526. Files->Directory = CurrentDirectory;
  527. DoReadDirectory(Files);
  528. }
  529. catch(...)
  530. {
  531. // ignore non-fatal errors
  532. // (i.e. current directory may not exist anymore)
  533. if (!FTerminal->Active)
  534. {
  535. throw;
  536. }
  537. }
  538. }
  539. __finally
  540. {
  541. delete Files;
  542. }
  543. }
  544. }
  545. }
  546. //---------------------------------------------------------------------------
  547. void __fastcall TFTPFileSystem::Discard()
  548. {
  549. // remove all pending messages, to get complete log
  550. // note that we need to retry discard on reconnect, as there still may be another
  551. // "disconnect/timeout/..." status messages coming
  552. DiscardMessages();
  553. assert(FActive);
  554. FActive = false;
  555. }
  556. //---------------------------------------------------------------------------
  557. UnicodeString __fastcall TFTPFileSystem::AbsolutePath(UnicodeString Path, bool /*Local*/)
  558. {
  559. // TODO: improve (handle .. etc.)
  560. if (UnixIsAbsolutePath(Path))
  561. {
  562. return Path;
  563. }
  564. else
  565. {
  566. return ::AbsolutePath(FCurrentDirectory, Path);
  567. }
  568. }
  569. //---------------------------------------------------------------------------
  570. UnicodeString __fastcall TFTPFileSystem::ActualCurrentDirectory()
  571. {
  572. wchar_t CurrentPath[1024];
  573. FFileZillaIntf->GetCurrentPath(CurrentPath, LENOF(CurrentPath));
  574. return UnixExcludeTrailingBackslash(CurrentPath);
  575. }
  576. //---------------------------------------------------------------------------
  577. void __fastcall TFTPFileSystem::EnsureLocation()
  578. {
  579. // if we do not know what's the current directory, do nothing
  580. if (!FCurrentDirectory.IsEmpty())
  581. {
  582. // Make sure that the FZAPI current working directory,
  583. // is actually our working directory.
  584. // It may not be because:
  585. // 1) We did cached directory change
  586. // 2) Listing was requested for non-current directory, which
  587. // makes FZAPI change its current directory (and not restoring it back afterwards)
  588. if (!UnixComparePaths(ActualCurrentDirectory(), FCurrentDirectory))
  589. {
  590. FTerminal->LogEvent(FORMAT(L"Synchronizing current directory \"%s\".",
  591. (FCurrentDirectory)));
  592. DoChangeDirectory(FCurrentDirectory);
  593. // make sure FZAPI is aware that we changed current working directory
  594. FFileZillaIntf->SetCurrentPath(FCurrentDirectory.c_str());
  595. }
  596. }
  597. }
  598. //---------------------------------------------------------------------------
  599. void __fastcall TFTPFileSystem::AnyCommand(const UnicodeString Command,
  600. TCaptureOutputEvent OutputEvent)
  601. {
  602. // end-user has right to expect that client current directory is really
  603. // current directory for the server
  604. EnsureLocation();
  605. assert(FOnCaptureOutput == NULL);
  606. FOnCaptureOutput = OutputEvent;
  607. try
  608. {
  609. FFileZillaIntf->CustomCommand(Command.c_str());
  610. GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_3XX_CODE);
  611. }
  612. __finally
  613. {
  614. FOnCaptureOutput = NULL;
  615. }
  616. }
  617. //---------------------------------------------------------------------------
  618. void __fastcall TFTPFileSystem::ResetCaches()
  619. {
  620. delete FFileListCache;
  621. FFileListCache = NULL;
  622. }
  623. //---------------------------------------------------------------------------
  624. void __fastcall TFTPFileSystem::AnnounceFileListOperation()
  625. {
  626. ResetCaches();
  627. }
  628. //---------------------------------------------------------------------------
  629. void __fastcall TFTPFileSystem::DoChangeDirectory(const UnicodeString & Directory)
  630. {
  631. UnicodeString Command = FORMAT(L"CWD %s", (Directory));
  632. FFileZillaIntf->CustomCommand(Command.c_str());
  633. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  634. }
  635. //---------------------------------------------------------------------------
  636. void __fastcall TFTPFileSystem::ChangeDirectory(const UnicodeString ADirectory)
  637. {
  638. UnicodeString Directory = ADirectory;
  639. try
  640. {
  641. // For changing directory, we do not make paths absolute, instead we
  642. // delegate this to the server, hence we synchronize current working
  643. // directory with the server and only then we ask for the change with
  644. // relative path.
  645. // But if synchronization fails, typically because current working directory
  646. // no longer exists, we fall back to out own resolution, to give
  647. // user chance to leave the non-existing directory.
  648. EnsureLocation();
  649. }
  650. catch(...)
  651. {
  652. if (FTerminal->Active)
  653. {
  654. Directory = AbsolutePath(Directory, false);
  655. }
  656. else
  657. {
  658. throw;
  659. }
  660. }
  661. DoChangeDirectory(Directory);
  662. // make next ReadCurrentDirectory retrieve actual server-side current directory
  663. FCurrentDirectory = L"";
  664. }
  665. //---------------------------------------------------------------------------
  666. void __fastcall TFTPFileSystem::CachedChangeDirectory(const UnicodeString Directory)
  667. {
  668. FCurrentDirectory = UnixExcludeTrailingBackslash(Directory);
  669. }
  670. //---------------------------------------------------------------------------
  671. void __fastcall TFTPFileSystem::ChangeFileProperties(const UnicodeString AFileName,
  672. const TRemoteFile * File, const TRemoteProperties * Properties,
  673. TChmodSessionAction & Action)
  674. {
  675. assert(Properties);
  676. assert(!Properties->Valid.Contains(vpGroup));
  677. assert(!Properties->Valid.Contains(vpOwner));
  678. assert(!Properties->Valid.Contains(vpLastAccess));
  679. assert(!Properties->Valid.Contains(vpModification));
  680. if (Properties->Valid.Contains(vpRights))
  681. {
  682. TRemoteFile * OwnedFile = NULL;
  683. try
  684. {
  685. UnicodeString FileName = AbsolutePath(AFileName, false);
  686. if (File == NULL)
  687. {
  688. ReadFile(FileName, OwnedFile);
  689. File = OwnedFile;
  690. }
  691. if ((File != NULL) && File->IsDirectory && !File->IsSymLink && Properties->Recursive)
  692. {
  693. try
  694. {
  695. FTerminal->ProcessDirectory(AFileName, FTerminal->ChangeFileProperties,
  696. (void*)Properties);
  697. }
  698. catch(...)
  699. {
  700. Action.Cancel();
  701. throw;
  702. }
  703. }
  704. TRights Rights;
  705. if (File != NULL)
  706. {
  707. Rights = *File->Rights;
  708. }
  709. Rights |= Properties->Rights.NumberSet;
  710. Rights &= (unsigned short)~Properties->Rights.NumberUnset;
  711. if ((File != NULL) && File->IsDirectory && Properties->AddXToDirectories)
  712. {
  713. Rights.AddExecute();
  714. }
  715. Action.Rights(Rights);
  716. UnicodeString FileNameOnly = UnixExtractFileName(FileName);
  717. UnicodeString FilePath = UnixExtractFilePath(FileName);
  718. // FZAPI wants octal number represented as decadic
  719. FFileZillaIntf->Chmod(Rights.NumberDecadic, FileNameOnly.c_str(), FilePath.c_str());
  720. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  721. }
  722. __finally
  723. {
  724. delete OwnedFile;
  725. }
  726. }
  727. else
  728. {
  729. Action.Cancel();
  730. }
  731. }
  732. //---------------------------------------------------------------------------
  733. bool __fastcall TFTPFileSystem::LoadFilesProperties(TStrings * /*FileList*/)
  734. {
  735. assert(false);
  736. return false;
  737. }
  738. //---------------------------------------------------------------------------
  739. void __fastcall TFTPFileSystem::CalculateFilesChecksum(const UnicodeString & /*Alg*/,
  740. TStrings * /*FileList*/, TStrings * /*Checksums*/,
  741. TCalculatedChecksumEvent /*OnCalculatedChecksum*/)
  742. {
  743. assert(false);
  744. }
  745. //---------------------------------------------------------------------------
  746. bool __fastcall TFTPFileSystem::ConfirmOverwrite(UnicodeString & FileName,
  747. TOverwriteMode & OverwriteMode, TFileOperationProgressType * OperationProgress,
  748. const TOverwriteFileParams * FileParams, const TCopyParamType * CopyParam,
  749. int Params, bool AutoResume)
  750. {
  751. bool Result;
  752. bool CanAutoResume = FLAGSET(Params, cpNoConfirmation) && AutoResume;
  753. bool DestIsSmaller = (FileParams != NULL) && (FileParams->DestSize < FileParams->SourceSize);
  754. bool DestIsSame = (FileParams != NULL) && (FileParams->DestSize == FileParams->SourceSize);
  755. bool CanResume =
  756. !OperationProgress->AsciiTransfer &&
  757. // when resuming transfer after interrupted connection,
  758. // do nothing (dummy resume) when the files has the same size.
  759. // this is workaround for servers that strangely fails just after successful
  760. // upload.
  761. (DestIsSmaller || (DestIsSame && CanAutoResume));
  762. unsigned int Answer;
  763. if (CanAutoResume && CanResume)
  764. {
  765. if (DestIsSame)
  766. {
  767. assert(CanAutoResume);
  768. Answer = qaSkip;
  769. }
  770. else
  771. {
  772. Answer = qaRetry;
  773. }
  774. }
  775. else
  776. {
  777. // retry = "resume"
  778. // all = "yes to newer"
  779. // ignore = "rename"
  780. int Answers = qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll | qaAll | qaIgnore;
  781. if (CanResume)
  782. {
  783. Answers |= qaRetry;
  784. }
  785. TQueryButtonAlias Aliases[5];
  786. Aliases[0].Button = qaRetry;
  787. Aliases[0].Alias = LoadStr(RESUME_BUTTON);
  788. Aliases[0].GroupWith = qaNo;
  789. Aliases[0].GrouppedShiftState = TShiftState() << ssAlt;
  790. Aliases[1].Button = qaAll;
  791. Aliases[1].Alias = LoadStr(YES_TO_NEWER_BUTTON);
  792. Aliases[1].GroupWith = qaYes;
  793. Aliases[1].GrouppedShiftState = TShiftState() << ssCtrl;
  794. Aliases[2].Button = qaIgnore;
  795. Aliases[2].Alias = LoadStr(RENAME_BUTTON);
  796. Aliases[2].GroupWith = qaNo;
  797. Aliases[2].GrouppedShiftState = TShiftState() << ssCtrl;
  798. Aliases[3].Button = qaYesToAll;
  799. Aliases[3].GroupWith = qaYes;
  800. Aliases[3].GrouppedShiftState = TShiftState() << ssShift;
  801. Aliases[4].Button = qaNoToAll;
  802. Aliases[4].GroupWith = qaNo;
  803. Aliases[4].GrouppedShiftState = TShiftState() << ssShift;
  804. TQueryParams QueryParams(qpNeverAskAgainCheck);
  805. QueryParams.Aliases = Aliases;
  806. QueryParams.AliasesCount = LENOF(Aliases);
  807. SUSPEND_OPERATION
  808. (
  809. Answer = FTerminal->ConfirmFileOverwrite(FileName, FileParams,
  810. Answers, &QueryParams,
  811. OperationProgress->Side == osLocal ? osRemote : osLocal,
  812. CopyParam, Params, OperationProgress);
  813. )
  814. }
  815. Result = true;
  816. switch (Answer)
  817. {
  818. // resume
  819. case qaRetry:
  820. OverwriteMode = omResume;
  821. assert(FileParams != NULL);
  822. assert(CanResume);
  823. FFileTransferResumed = FileParams->DestSize;
  824. break;
  825. // rename
  826. case qaIgnore:
  827. if (FTerminal->PromptUser(FTerminal->SessionData, pkFileName,
  828. LoadStr(RENAME_TITLE), L"", LoadStr(RENAME_PROMPT2), true, 0, FileName))
  829. {
  830. OverwriteMode = omOverwrite;
  831. }
  832. else
  833. {
  834. if (!OperationProgress->Cancel)
  835. {
  836. OperationProgress->Cancel = csCancel;
  837. }
  838. FFileTransferAbort = ftaCancel;
  839. Result = false;
  840. }
  841. break;
  842. case qaYes:
  843. OverwriteMode = omOverwrite;
  844. break;
  845. case qaCancel:
  846. if (!OperationProgress->Cancel)
  847. {
  848. OperationProgress->Cancel = csCancel;
  849. }
  850. FFileTransferAbort = ftaCancel;
  851. Result = false;
  852. break;
  853. case qaNo:
  854. FFileTransferAbort = ftaSkip;
  855. Result = false;
  856. break;
  857. case qaSkip:
  858. OverwriteMode = omComplete;
  859. break;
  860. default:
  861. assert(false);
  862. Result = false;
  863. break;
  864. }
  865. return Result;
  866. }
  867. //---------------------------------------------------------------------------
  868. void __fastcall TFTPFileSystem::ResetFileTransfer()
  869. {
  870. FFileTransferAbort = ftaNone;
  871. FFileTransferCancelled = false;
  872. FFileTransferResumed = 0;
  873. }
  874. //---------------------------------------------------------------------------
  875. void __fastcall TFTPFileSystem::ReadDirectoryProgress(__int64 Bytes)
  876. {
  877. // with FTP we do not know exactly how many entries we have received,
  878. // instead we know number of bytes received only.
  879. // so we report approximation based on average size of entry.
  880. int Progress = int(Bytes / 80);
  881. if (Progress - FLastReadDirectoryProgress >= 10)
  882. {
  883. bool Cancel = false;
  884. FLastReadDirectoryProgress = Progress;
  885. FTerminal->DoReadDirectoryProgress(Progress, Cancel);
  886. if (Cancel)
  887. {
  888. FTerminal->DoReadDirectoryProgress(-2, Cancel);
  889. FFileZillaIntf->Cancel();
  890. }
  891. }
  892. }
  893. //---------------------------------------------------------------------------
  894. void __fastcall TFTPFileSystem::DoFileTransferProgress(__int64 TransferSize,
  895. __int64 Bytes)
  896. {
  897. TFileOperationProgressType * OperationProgress = FTerminal->OperationProgress;
  898. OperationProgress->SetTransferSize(TransferSize);
  899. if (FFileTransferResumed > 0)
  900. {
  901. OperationProgress->AddResumed(FFileTransferResumed);
  902. FFileTransferResumed = 0;
  903. }
  904. __int64 Diff = Bytes - OperationProgress->TransferedSize;
  905. if (ALWAYS_TRUE(Diff >= 0))
  906. {
  907. OperationProgress->AddTransfered(Diff);
  908. }
  909. if (OperationProgress->Cancel == csCancel)
  910. {
  911. FFileTransferCancelled = true;
  912. FFileTransferAbort = ftaCancel;
  913. FFileZillaIntf->Cancel();
  914. }
  915. if (FFileTransferCPSLimit != OperationProgress->CPSLimit)
  916. {
  917. FFileTransferCPSLimit = OperationProgress->CPSLimit;
  918. }
  919. }
  920. //---------------------------------------------------------------------------
  921. void __fastcall TFTPFileSystem::FileTransferProgress(__int64 TransferSize,
  922. __int64 Bytes)
  923. {
  924. TGuard Guard(FTransferStatusCriticalSection);
  925. DoFileTransferProgress(TransferSize, Bytes);
  926. }
  927. //---------------------------------------------------------------------------
  928. void __fastcall TFTPFileSystem::FileTransfer(const UnicodeString & FileName,
  929. const UnicodeString & LocalFile, const UnicodeString & RemoteFile,
  930. const UnicodeString & RemotePath, bool Get, __int64 Size, int Type,
  931. TFileTransferData & UserData, TFileOperationProgressType * OperationProgress)
  932. {
  933. FILE_OPERATION_LOOP(FMTLOAD(TRANSFER_ERROR, (FileName)),
  934. FFileZillaIntf->FileTransfer(LocalFile.c_str(), RemoteFile.c_str(),
  935. RemotePath.c_str(), Get, Size, Type, &UserData);
  936. // we may actually catch response code of the listing
  937. // command (when checking for existence of the remote file)
  938. unsigned int Reply = WaitForCommandReply();
  939. GotReply(Reply, FLAGMASK(FFileTransferCancelled, REPLY_ALLOW_CANCEL));
  940. );
  941. switch (FFileTransferAbort)
  942. {
  943. case ftaSkip:
  944. THROW_SKIP_FILE_NULL;
  945. case ftaCancel:
  946. Abort();
  947. break;
  948. }
  949. if (!FFileTransferCancelled)
  950. {
  951. // show completion of transfer
  952. // call non-guarded variant to avoid deadlock with keepalives
  953. // (we are not waiting for reply anymore so keepalives are free to proceed)
  954. DoFileTransferProgress(OperationProgress->TransferSize, OperationProgress->TransferSize);
  955. }
  956. }
  957. //---------------------------------------------------------------------------
  958. void __fastcall TFTPFileSystem::CopyToLocal(TStrings * FilesToCopy,
  959. const UnicodeString TargetDir, const TCopyParamType * CopyParam,
  960. int Params, TFileOperationProgressType * OperationProgress,
  961. TOnceDoneOperation & OnceDoneOperation)
  962. {
  963. Params &= ~cpAppend;
  964. UnicodeString FullTargetDir = IncludeTrailingBackslash(TargetDir);
  965. int Index = 0;
  966. while (Index < FilesToCopy->Count && !OperationProgress->Cancel)
  967. {
  968. UnicodeString FileName = FilesToCopy->Strings[Index];
  969. const TRemoteFile * File = dynamic_cast<const TRemoteFile *>(FilesToCopy->Objects[Index]);
  970. bool Success = false;
  971. try
  972. {
  973. try
  974. {
  975. SinkRobust(AbsolutePath(FileName, false), File, FullTargetDir, CopyParam, Params,
  976. OperationProgress, tfFirstLevel);
  977. Success = true;
  978. FLastDataSent = Now();
  979. }
  980. catch(EScpSkipFile & E)
  981. {
  982. SUSPEND_OPERATION (
  983. if (!FTerminal->HandleException(&E)) throw;
  984. );
  985. }
  986. }
  987. __finally
  988. {
  989. OperationProgress->Finish(FileName, Success, OnceDoneOperation);
  990. }
  991. Index++;
  992. }
  993. }
  994. //---------------------------------------------------------------------------
  995. void __fastcall TFTPFileSystem::SinkRobust(const UnicodeString FileName,
  996. const TRemoteFile * File, const UnicodeString TargetDir,
  997. const TCopyParamType * CopyParam, int Params,
  998. TFileOperationProgressType * OperationProgress, unsigned int Flags)
  999. {
  1000. // the same in TSFTPFileSystem
  1001. bool Retry;
  1002. TDownloadSessionAction Action(FTerminal->ActionLog);
  1003. do
  1004. {
  1005. Retry = false;
  1006. try
  1007. {
  1008. Sink(FileName, File, TargetDir, CopyParam, Params, OperationProgress,
  1009. Flags, Action);
  1010. }
  1011. catch(Exception & E)
  1012. {
  1013. Retry = true;
  1014. if (FTerminal->Active ||
  1015. !FTerminal->QueryReopen(&E, ropNoReadDirectory, OperationProgress))
  1016. {
  1017. FTerminal->RollbackAction(Action, OperationProgress, &E);
  1018. throw;
  1019. }
  1020. }
  1021. if (Retry)
  1022. {
  1023. OperationProgress->RollbackTransfer();
  1024. Action.Restart();
  1025. assert(File != NULL);
  1026. if (!File->IsDirectory)
  1027. {
  1028. // prevent overwrite confirmations
  1029. Params |= cpNoConfirmation;
  1030. Flags |= tfAutoResume;
  1031. }
  1032. }
  1033. }
  1034. while (Retry);
  1035. }
  1036. //---------------------------------------------------------------------------
  1037. void __fastcall TFTPFileSystem::Sink(const UnicodeString FileName,
  1038. const TRemoteFile * File, const UnicodeString TargetDir,
  1039. const TCopyParamType * CopyParam, int Params,
  1040. TFileOperationProgressType * OperationProgress, unsigned int Flags,
  1041. TDownloadSessionAction & Action)
  1042. {
  1043. UnicodeString OnlyFileName = UnixExtractFileName(FileName);
  1044. Action.FileName(FileName);
  1045. TFileMasks::TParams MaskParams;
  1046. assert(File);
  1047. MaskParams.Size = File->Size;
  1048. MaskParams.Modification = File->Modification;
  1049. if (!CopyParam->AllowTransfer(FileName, osRemote, File->IsDirectory, MaskParams))
  1050. {
  1051. FTerminal->LogEvent(FORMAT(L"File \"%s\" excluded from transfer", (FileName)));
  1052. THROW_SKIP_FILE_NULL;
  1053. }
  1054. FTerminal->LogFileDetails(FileName, File->Modification, File->Size);
  1055. OperationProgress->SetFile(OnlyFileName);
  1056. UnicodeString DestFileName = CopyParam->ChangeFileName(OnlyFileName,
  1057. osRemote, FLAGSET(Flags, tfFirstLevel));
  1058. UnicodeString DestFullName = TargetDir + DestFileName;
  1059. if (File->IsDirectory)
  1060. {
  1061. Action.Cancel();
  1062. if (!File->IsSymLink)
  1063. {
  1064. FILE_OPERATION_LOOP (FMTLOAD(NOT_DIRECTORY_ERROR, (DestFullName)),
  1065. int Attrs = FileGetAttr(DestFullName);
  1066. if (FLAGCLEAR(Attrs, faDirectory))
  1067. {
  1068. EXCEPTION;
  1069. }
  1070. );
  1071. FILE_OPERATION_LOOP (FMTLOAD(CREATE_DIR_ERROR, (DestFullName)),
  1072. THROWOSIFFALSE(ForceDirectories(DestFullName));
  1073. );
  1074. TSinkFileParams SinkFileParams;
  1075. SinkFileParams.TargetDir = IncludeTrailingBackslash(DestFullName);
  1076. SinkFileParams.CopyParam = CopyParam;
  1077. SinkFileParams.Params = Params;
  1078. SinkFileParams.OperationProgress = OperationProgress;
  1079. SinkFileParams.Skipped = false;
  1080. SinkFileParams.Flags = Flags & ~(tfFirstLevel | tfAutoResume);
  1081. FTerminal->ProcessDirectory(FileName, SinkFile, &SinkFileParams);
  1082. // Do not delete directory if some of its files were skip.
  1083. // Throw "skip file" for the directory to avoid attempt to deletion
  1084. // of any parent directory
  1085. if (FLAGSET(Params, cpDelete) && SinkFileParams.Skipped)
  1086. {
  1087. THROW_SKIP_FILE_NULL;
  1088. }
  1089. }
  1090. else
  1091. {
  1092. // file is symlink to directory, currently do nothing, but it should be
  1093. // reported to user
  1094. }
  1095. }
  1096. else
  1097. {
  1098. FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to local directory started.", (FileName)));
  1099. // Will we use ASCII of BINARY file transfer?
  1100. OperationProgress->SetAsciiTransfer(
  1101. CopyParam->UseAsciiTransfer(FileName, osRemote, MaskParams));
  1102. FTerminal->LogEvent(UnicodeString((OperationProgress->AsciiTransfer ? L"Ascii" : L"Binary")) +
  1103. L" transfer mode selected.");
  1104. // Suppose same data size to transfer as to write
  1105. OperationProgress->SetTransferSize(File->Size);
  1106. OperationProgress->SetLocalSize(OperationProgress->TransferSize);
  1107. int Attrs;
  1108. FILE_OPERATION_LOOP (FMTLOAD(NOT_FILE_ERROR, (DestFullName)),
  1109. Attrs = FileGetAttr(DestFullName);
  1110. if ((Attrs >= 0) && FLAGSET(Attrs, faDirectory))
  1111. {
  1112. EXCEPTION;
  1113. }
  1114. );
  1115. OperationProgress->TransferingFile = false; // not set with FTP protocol
  1116. ResetFileTransfer();
  1117. TFileTransferData UserData;
  1118. UnicodeString FilePath = UnixExtractFilePath(FileName);
  1119. unsigned int TransferType = (OperationProgress->AsciiTransfer ? 1 : 2);
  1120. {
  1121. // ignore file list
  1122. TFileListHelper Helper(this, NULL, true);
  1123. FFileTransferCPSLimit = OperationProgress->CPSLimit;
  1124. FFileTransferPreserveTime = CopyParam->PreserveTime;
  1125. // not used for downloads anyway
  1126. FFileTransferRemoveBOM = CopyParam->RemoveBOM;
  1127. UserData.FileName = DestFileName;
  1128. UserData.Params = Params;
  1129. UserData.AutoResume = FLAGSET(Flags, tfAutoResume);
  1130. UserData.CopyParam = CopyParam;
  1131. UserData.Modification = File->Modification;
  1132. FileTransfer(FileName, DestFullName, OnlyFileName,
  1133. FilePath, true, File->Size, TransferType, UserData, OperationProgress);
  1134. }
  1135. // in case dest filename is changed from overwrite dialog
  1136. if (DestFileName != UserData.FileName)
  1137. {
  1138. DestFullName = TargetDir + UserData.FileName;
  1139. Attrs = FileGetAttr(DestFullName);
  1140. }
  1141. Action.Destination(ExpandUNCFileName(DestFullName));
  1142. if (Attrs == -1)
  1143. {
  1144. Attrs = faArchive;
  1145. }
  1146. int NewAttrs = CopyParam->LocalFileAttrs(*File->Rights);
  1147. if ((NewAttrs & Attrs) != NewAttrs)
  1148. {
  1149. FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DestFullName)),
  1150. THROWOSIFFALSE(FileSetAttr(DestFullName, Attrs | NewAttrs) == 0);
  1151. );
  1152. }
  1153. }
  1154. if (FLAGSET(Params, cpDelete))
  1155. {
  1156. // If file is directory, do not delete it recursively, because it should be
  1157. // empty already. If not, it should not be deleted (some files were
  1158. // skipped or some new files were copied to it, while we were downloading)
  1159. int Params = dfNoRecursive;
  1160. FTerminal->DeleteFile(FileName, File, &Params);
  1161. }
  1162. }
  1163. //---------------------------------------------------------------------------
  1164. void __fastcall TFTPFileSystem::SinkFile(UnicodeString FileName,
  1165. const TRemoteFile * File, void * Param)
  1166. {
  1167. TSinkFileParams * Params = (TSinkFileParams *)Param;
  1168. assert(Params->OperationProgress);
  1169. try
  1170. {
  1171. SinkRobust(FileName, File, Params->TargetDir, Params->CopyParam,
  1172. Params->Params, Params->OperationProgress, Params->Flags);
  1173. }
  1174. catch(EScpSkipFile & E)
  1175. {
  1176. TFileOperationProgressType * OperationProgress = Params->OperationProgress;
  1177. Params->Skipped = true;
  1178. SUSPEND_OPERATION (
  1179. if (!FTerminal->HandleException(&E))
  1180. {
  1181. throw;
  1182. }
  1183. );
  1184. if (OperationProgress->Cancel)
  1185. {
  1186. Abort();
  1187. }
  1188. }
  1189. }
  1190. //---------------------------------------------------------------------------
  1191. void __fastcall TFTPFileSystem::CopyToRemote(TStrings * FilesToCopy,
  1192. const UnicodeString ATargetDir, const TCopyParamType * CopyParam,
  1193. int Params, TFileOperationProgressType * OperationProgress,
  1194. TOnceDoneOperation & OnceDoneOperation)
  1195. {
  1196. assert((FilesToCopy != NULL) && (OperationProgress != NULL));
  1197. Params &= ~cpAppend;
  1198. UnicodeString FileName, FileNameOnly;
  1199. UnicodeString TargetDir = AbsolutePath(ATargetDir, false);
  1200. UnicodeString FullTargetDir = UnixIncludeTrailingBackslash(TargetDir);
  1201. int Index = 0;
  1202. while ((Index < FilesToCopy->Count) && !OperationProgress->Cancel)
  1203. {
  1204. bool Success = false;
  1205. FileName = FilesToCopy->Strings[Index];
  1206. FileNameOnly = ExtractFileName(FileName);
  1207. try
  1208. {
  1209. try
  1210. {
  1211. if (FTerminal->SessionData->CacheDirectories)
  1212. {
  1213. FTerminal->DirectoryModified(TargetDir, false);
  1214. if (DirectoryExists(FileName))
  1215. {
  1216. FTerminal->DirectoryModified(FullTargetDir + FileNameOnly, true);
  1217. }
  1218. }
  1219. SourceRobust(FileName, FullTargetDir, CopyParam, Params, OperationProgress,
  1220. tfFirstLevel);
  1221. Success = true;
  1222. FLastDataSent = Now();
  1223. }
  1224. catch(EScpSkipFile & E)
  1225. {
  1226. SUSPEND_OPERATION (
  1227. if (!FTerminal->HandleException(&E)) throw;
  1228. );
  1229. }
  1230. }
  1231. __finally
  1232. {
  1233. OperationProgress->Finish(FileName, Success, OnceDoneOperation);
  1234. }
  1235. Index++;
  1236. }
  1237. }
  1238. //---------------------------------------------------------------------------
  1239. void __fastcall TFTPFileSystem::SourceRobust(const UnicodeString FileName,
  1240. const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
  1241. TFileOperationProgressType * OperationProgress, unsigned int Flags)
  1242. {
  1243. // the same in TSFTPFileSystem
  1244. bool Retry;
  1245. TUploadSessionAction Action(FTerminal->ActionLog);
  1246. do
  1247. {
  1248. Retry = false;
  1249. try
  1250. {
  1251. Source(FileName, TargetDir, CopyParam, Params, OperationProgress,
  1252. Flags, Action);
  1253. }
  1254. catch(Exception & E)
  1255. {
  1256. Retry = true;
  1257. if (FTerminal->Active ||
  1258. !FTerminal->QueryReopen(&E, ropNoReadDirectory, OperationProgress))
  1259. {
  1260. FTerminal->RollbackAction(Action, OperationProgress, &E);
  1261. throw;
  1262. }
  1263. }
  1264. if (Retry)
  1265. {
  1266. OperationProgress->RollbackTransfer();
  1267. Action.Restart();
  1268. // prevent overwrite confirmations
  1269. // (should not be set for directories!)
  1270. Params |= cpNoConfirmation;
  1271. Flags |= tfAutoResume;
  1272. }
  1273. }
  1274. while (Retry);
  1275. }
  1276. //---------------------------------------------------------------------------
  1277. void __fastcall TFTPFileSystem::Source(const UnicodeString FileName,
  1278. const UnicodeString TargetDir, const TCopyParamType * CopyParam, int Params,
  1279. TFileOperationProgressType * OperationProgress, unsigned int Flags,
  1280. TUploadSessionAction & Action)
  1281. {
  1282. Action.FileName(ExpandUNCFileName(FileName));
  1283. OperationProgress->SetFile(FileName, false);
  1284. if (!FTerminal->AllowLocalFileTransfer(FileName, CopyParam))
  1285. {
  1286. FTerminal->LogEvent(FORMAT(L"File \"%s\" excluded from transfer", (FileName)));
  1287. THROW_SKIP_FILE_NULL;
  1288. }
  1289. __int64 Size;
  1290. int Attrs;
  1291. FTerminal->OpenLocalFile(FileName, GENERIC_READ, &Attrs,
  1292. NULL, NULL, NULL, NULL, &Size);
  1293. OperationProgress->SetFileInProgress();
  1294. bool Dir = FLAGSET(Attrs, faDirectory);
  1295. if (Dir)
  1296. {
  1297. Action.Cancel();
  1298. DirectorySource(IncludeTrailingBackslash(FileName), TargetDir,
  1299. Attrs, CopyParam, Params, OperationProgress, Flags);
  1300. }
  1301. else
  1302. {
  1303. UnicodeString DestFileName = CopyParam->ChangeFileName(ExtractFileName(FileName),
  1304. osLocal, FLAGSET(Flags, tfFirstLevel));
  1305. FTerminal->LogEvent(FORMAT(L"Copying \"%s\" to remote directory started.", (FileName)));
  1306. OperationProgress->SetLocalSize(Size);
  1307. // Suppose same data size to transfer as to read
  1308. // (not true with ASCII transfer)
  1309. OperationProgress->SetTransferSize(OperationProgress->LocalSize);
  1310. OperationProgress->TransferingFile = false;
  1311. TDateTime Modification;
  1312. // Inspired by SysUtils::FileAge
  1313. WIN32_FIND_DATA FindData;
  1314. HANDLE Handle = FindFirstFile(FileName.c_str(), &FindData);
  1315. if (Handle != INVALID_HANDLE_VALUE)
  1316. {
  1317. Modification =
  1318. UnixToDateTime(
  1319. ConvertTimestampToUnixSafe(FindData.ftLastWriteTime, dstmUnix),
  1320. dstmUnix);
  1321. FindClose(Handle);
  1322. }
  1323. // Will we use ASCII of BINARY file transfer?
  1324. TFileMasks::TParams MaskParams;
  1325. MaskParams.Size = Size;
  1326. MaskParams.Modification = Modification;
  1327. OperationProgress->SetAsciiTransfer(
  1328. CopyParam->UseAsciiTransfer(FileName, osLocal, MaskParams));
  1329. FTerminal->LogEvent(
  1330. UnicodeString(OperationProgress->AsciiTransfer ? L"Ascii" : L"Binary") +
  1331. L" transfer mode selected.");
  1332. ResetFileTransfer();
  1333. TFileTransferData UserData;
  1334. unsigned int TransferType = (OperationProgress->AsciiTransfer ? 1 : 2);
  1335. {
  1336. // ignore file list
  1337. TFileListHelper Helper(this, NULL, true);
  1338. FFileTransferCPSLimit = OperationProgress->CPSLimit;
  1339. // not used for uploads anyway
  1340. FFileTransferPreserveTime = CopyParam->PreserveTime;
  1341. FFileTransferRemoveBOM = CopyParam->RemoveBOM;
  1342. // not used for uploads, but we get new name (if any) back in this field
  1343. UserData.FileName = DestFileName;
  1344. UserData.Params = Params;
  1345. UserData.AutoResume = FLAGSET(Flags, tfAutoResume);
  1346. UserData.CopyParam = CopyParam;
  1347. UserData.Modification = Modification;
  1348. FileTransfer(FileName, FileName, DestFileName,
  1349. TargetDir, false, Size, TransferType, UserData, OperationProgress);
  1350. }
  1351. UnicodeString DestFullName = TargetDir + UserData.FileName;
  1352. // only now, we know the final destination
  1353. Action.Destination(DestFullName);
  1354. // we are not able to tell if setting timestamp succeeded,
  1355. // so we log it always (if supported)
  1356. if (FFileTransferPreserveTime &&
  1357. (FServerCapabilities->GetCapability(mfmt_command) == yes))
  1358. {
  1359. TTouchSessionAction TouchAction(FTerminal->ActionLog, DestFullName, Modification);
  1360. }
  1361. }
  1362. /* TODO : Delete also read-only files. */
  1363. if (FLAGSET(Params, cpDelete))
  1364. {
  1365. if (!Dir)
  1366. {
  1367. FILE_OPERATION_LOOP (FMTLOAD(DELETE_LOCAL_FILE_ERROR, (FileName)),
  1368. THROWOSIFFALSE(Sysutils::DeleteFile(FileName));
  1369. )
  1370. }
  1371. }
  1372. else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
  1373. {
  1374. FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (FileName)),
  1375. THROWOSIFFALSE(FileSetAttr(FileName, Attrs & ~faArchive) == 0);
  1376. )
  1377. }
  1378. }
  1379. //---------------------------------------------------------------------------
  1380. void __fastcall TFTPFileSystem::DirectorySource(const UnicodeString DirectoryName,
  1381. const UnicodeString TargetDir, int Attrs, const TCopyParamType * CopyParam,
  1382. int Params, TFileOperationProgressType * OperationProgress, unsigned int Flags)
  1383. {
  1384. UnicodeString DestDirectoryName = CopyParam->ChangeFileName(
  1385. ExtractFileName(ExcludeTrailingBackslash(DirectoryName)), osLocal,
  1386. FLAGSET(Flags, tfFirstLevel));
  1387. UnicodeString DestFullName = UnixIncludeTrailingBackslash(TargetDir + DestDirectoryName);
  1388. OperationProgress->SetFile(DirectoryName);
  1389. int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
  1390. TSearchRec SearchRec;
  1391. bool FindOK;
  1392. FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (DirectoryName)),
  1393. FindOK = (bool)(FindFirstChecked(DirectoryName + L"*.*",
  1394. FindAttrs, SearchRec) == 0);
  1395. );
  1396. bool CreateDir = true;
  1397. try
  1398. {
  1399. while (FindOK && !OperationProgress->Cancel)
  1400. {
  1401. UnicodeString FileName = DirectoryName + SearchRec.Name;
  1402. try
  1403. {
  1404. if ((SearchRec.Name != L".") && (SearchRec.Name != L".."))
  1405. {
  1406. SourceRobust(FileName, DestFullName, CopyParam, Params, OperationProgress,
  1407. Flags & ~(tfFirstLevel | tfAutoResume));
  1408. // if any file got uploaded (i.e. there were any file in the
  1409. // directory and at least one was not skipped),
  1410. // do not try to create the directory,
  1411. // as it should be already created by FZAPI during upload
  1412. CreateDir = false;
  1413. }
  1414. }
  1415. catch (EScpSkipFile &E)
  1416. {
  1417. // If ESkipFile occurs, just log it and continue with next file
  1418. SUSPEND_OPERATION (
  1419. // here a message to user was displayed, which was not appropriate
  1420. // when user refused to overwrite the file in subdirectory.
  1421. // hopefuly it won't be missing in other situations.
  1422. if (!FTerminal->HandleException(&E)) throw;
  1423. );
  1424. }
  1425. FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (DirectoryName)),
  1426. FindOK = (FindNextChecked(SearchRec) == 0);
  1427. );
  1428. }
  1429. }
  1430. __finally
  1431. {
  1432. FindClose(SearchRec);
  1433. }
  1434. if (CreateDir)
  1435. {
  1436. TRemoteProperties Properties;
  1437. if (CopyParam->PreserveRights)
  1438. {
  1439. Properties.Valid = TValidProperties() << vpRights;
  1440. Properties.Rights = CopyParam->RemoteFileRights(Attrs);
  1441. }
  1442. try
  1443. {
  1444. FTerminal->ExceptionOnFail = true;
  1445. try
  1446. {
  1447. FTerminal->CreateDirectory(DestFullName, &Properties);
  1448. }
  1449. __finally
  1450. {
  1451. FTerminal->ExceptionOnFail = false;
  1452. }
  1453. }
  1454. catch(...)
  1455. {
  1456. TRemoteFile * File = NULL;
  1457. // ignore non-fatal error when the directory already exists
  1458. bool Rethrow =
  1459. !FTerminal->Active ||
  1460. !FTerminal->FileExists(UnixExcludeTrailingBackslash(DestFullName), &File) ||
  1461. !File->IsDirectory;
  1462. delete File;
  1463. if (Rethrow)
  1464. {
  1465. throw;
  1466. }
  1467. }
  1468. }
  1469. /* TODO : Delete also read-only directories. */
  1470. /* TODO : Show error message on failure. */
  1471. if (!OperationProgress->Cancel)
  1472. {
  1473. if (FLAGSET(Params, cpDelete))
  1474. {
  1475. RemoveDir(DirectoryName);
  1476. }
  1477. else if (CopyParam->ClearArchive && FLAGSET(Attrs, faArchive))
  1478. {
  1479. FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (DirectoryName)),
  1480. THROWOSIFFALSE(FileSetAttr(DirectoryName, Attrs & ~faArchive) == 0);
  1481. )
  1482. }
  1483. }
  1484. }
  1485. //---------------------------------------------------------------------------
  1486. void __fastcall TFTPFileSystem::CreateDirectory(const UnicodeString ADirName)
  1487. {
  1488. UnicodeString DirName = AbsolutePath(ADirName, false);
  1489. {
  1490. // ignore file list
  1491. TFileListHelper Helper(this, NULL, true);
  1492. FFileZillaIntf->MakeDir(DirName.c_str());
  1493. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  1494. }
  1495. }
  1496. //---------------------------------------------------------------------------
  1497. void __fastcall TFTPFileSystem::CreateLink(const UnicodeString /*FileName*/,
  1498. const UnicodeString /*PointTo*/, bool /*Symbolic*/)
  1499. {
  1500. assert(false);
  1501. }
  1502. //---------------------------------------------------------------------------
  1503. void __fastcall TFTPFileSystem::DeleteFile(const UnicodeString AFileName,
  1504. const TRemoteFile * File, int Params, TRmSessionAction & Action)
  1505. {
  1506. UnicodeString FileName = AbsolutePath(AFileName, false);
  1507. UnicodeString FileNameOnly = UnixExtractFileName(FileName);
  1508. UnicodeString FilePath = UnixExtractFilePath(FileName);
  1509. bool Dir = (File != NULL) && File->IsDirectory && !File->IsSymLink;
  1510. if (Dir && FLAGCLEAR(Params, dfNoRecursive))
  1511. {
  1512. try
  1513. {
  1514. FTerminal->ProcessDirectory(FileName, FTerminal->DeleteFile, &Params);
  1515. }
  1516. catch(...)
  1517. {
  1518. Action.Cancel();
  1519. throw;
  1520. }
  1521. }
  1522. {
  1523. // ignore file list
  1524. TFileListHelper Helper(this, NULL, true);
  1525. if (Dir)
  1526. {
  1527. // If current remote directory is in the directory being removed,
  1528. // some servers may refuse to delete it
  1529. // This is common as ProcessDirectory above would CWD to
  1530. // the directory to LIST it.
  1531. // EnsureLocation should reset actual current directory to user's working directory.
  1532. // If user's working directory is still below deleted directory, it is
  1533. // perfectly correct to report an error.
  1534. if (UnixIsChildPath(ActualCurrentDirectory(), FileName))
  1535. {
  1536. EnsureLocation();
  1537. }
  1538. FFileZillaIntf->RemoveDir(FileNameOnly.c_str(), FilePath.c_str());
  1539. }
  1540. else
  1541. {
  1542. FFileZillaIntf->Delete(FileNameOnly.c_str(), FilePath.c_str());
  1543. }
  1544. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  1545. }
  1546. }
  1547. //---------------------------------------------------------------------------
  1548. void __fastcall TFTPFileSystem::CustomCommandOnFile(const UnicodeString /*FileName*/,
  1549. const TRemoteFile * /*File*/, UnicodeString /*Command*/, int /*Params*/,
  1550. TCaptureOutputEvent /*OutputEvent*/)
  1551. {
  1552. // if ever implemented, do not forget to add EnsureLocation,
  1553. // see AnyCommand for a reason why
  1554. assert(false);
  1555. }
  1556. //---------------------------------------------------------------------------
  1557. void __fastcall TFTPFileSystem::DoStartup()
  1558. {
  1559. TStrings * PostLoginCommands = new TStringList();
  1560. try
  1561. {
  1562. PostLoginCommands->Text = FTerminal->SessionData->PostLoginCommands;
  1563. for (int Index = 0; Index < PostLoginCommands->Count; Index++)
  1564. {
  1565. UnicodeString Command = PostLoginCommands->Strings[Index];
  1566. if (!Command.IsEmpty())
  1567. {
  1568. FFileZillaIntf->CustomCommand(Command.c_str());
  1569. GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_3XX_CODE);
  1570. }
  1571. }
  1572. }
  1573. __finally
  1574. {
  1575. delete PostLoginCommands;
  1576. }
  1577. // retrieve initialize working directory to save it as home directory
  1578. ReadCurrentDirectory();
  1579. FHomeDirectory = FCurrentDirectory;
  1580. }
  1581. //---------------------------------------------------------------------------
  1582. void __fastcall TFTPFileSystem::HomeDirectory()
  1583. {
  1584. // FHomeDirectory is an absolute path, so avoid unnecessary overhead
  1585. // of ChangeDirectory, such as EnsureLocation
  1586. DoChangeDirectory(FHomeDirectory);
  1587. FCurrentDirectory = FHomeDirectory;
  1588. // make sure FZAPI is aware that we changed current working directory
  1589. FFileZillaIntf->SetCurrentPath(FCurrentDirectory.c_str());
  1590. }
  1591. //---------------------------------------------------------------------------
  1592. bool __fastcall TFTPFileSystem::IsCapable(int Capability) const
  1593. {
  1594. assert(FTerminal);
  1595. switch (Capability)
  1596. {
  1597. case fcResolveSymlink: // sic
  1598. case fcTextMode:
  1599. case fcModeChanging: // but not fcModeChangingUpload
  1600. case fcNewerOnlyUpload:
  1601. case fcAnyCommand: // but not fcShellAnyCommand
  1602. case fcRename:
  1603. case fcRemoteMove:
  1604. case fcRemoveBOMUpload:
  1605. return true;
  1606. case fcPreservingTimestampUpload:
  1607. return (FServerCapabilities->GetCapability(mfmt_command) == yes);
  1608. case fcModeChangingUpload:
  1609. case fcLoadingAdditionalProperties:
  1610. case fcShellAnyCommand:
  1611. case fcCalculatingChecksum:
  1612. case fcHardLink:
  1613. case fcSymbolicLink:
  1614. case fcCheckingSpaceAvailable:
  1615. case fcUserGroupListing:
  1616. case fcGroupChanging:
  1617. case fcOwnerChanging:
  1618. case fcGroupOwnerChangingByID:
  1619. case fcSecondaryShell:
  1620. case fcRemoteCopy:
  1621. case fcNativeTextMode:
  1622. case fcTimestampChanging:
  1623. case fcIgnorePermErrors:
  1624. case fcRemoveCtrlZUpload:
  1625. return false;
  1626. default:
  1627. assert(false);
  1628. return false;
  1629. }
  1630. }
  1631. //---------------------------------------------------------------------------
  1632. void __fastcall TFTPFileSystem::LookupUsersGroups()
  1633. {
  1634. assert(false);
  1635. }
  1636. //---------------------------------------------------------------------------
  1637. void __fastcall TFTPFileSystem::ReadCurrentDirectory()
  1638. {
  1639. // ask the server for current directory on startup only
  1640. // and immediately after call to CWD,
  1641. // later our current directory may be not synchronized with FZAPI current
  1642. // directory anyway, see comments in EnsureLocation
  1643. if (FCurrentDirectory.IsEmpty())
  1644. {
  1645. FFileZillaIntf->CustomCommand(L"PWD");
  1646. unsigned int Code;
  1647. TStrings * Response = NULL;
  1648. GotReply(WaitForCommandReply(), REPLY_2XX_CODE, L"", &Code, &Response);
  1649. try
  1650. {
  1651. assert(Response != NULL);
  1652. bool Result = false;
  1653. // the only allowed 2XX code to "PWD"
  1654. if ((Code == 257) &&
  1655. (Response->Count == 1))
  1656. {
  1657. UnicodeString Path = Response->Text;
  1658. int P = Path.Pos(L"\"");
  1659. if (P == 0)
  1660. {
  1661. // some systems use single quotes, be tolerant
  1662. P = Path.Pos(L"'");
  1663. }
  1664. if (P != 0)
  1665. {
  1666. Path.Delete(1, P - 1);
  1667. if (Unquote(Path))
  1668. {
  1669. FCurrentDirectory = UnixExcludeTrailingBackslash(Path);
  1670. Result = true;
  1671. }
  1672. }
  1673. }
  1674. if (Result)
  1675. {
  1676. FFileZillaIntf->SetCurrentPath(FCurrentDirectory.c_str());
  1677. }
  1678. else
  1679. {
  1680. throw Exception(FMTLOAD(FTP_PWD_RESPONSE_ERROR, (Response->Text)));
  1681. }
  1682. }
  1683. __finally
  1684. {
  1685. delete Response;
  1686. }
  1687. }
  1688. }
  1689. //---------------------------------------------------------------------------
  1690. void __fastcall TFTPFileSystem::DoReadDirectory(TRemoteFileList * FileList)
  1691. {
  1692. FileList->Reset();
  1693. // FZAPI does not list parent directory, add it
  1694. FileList->AddFile(new TRemoteParentDirectory(FTerminal));
  1695. FLastReadDirectoryProgress = 0;
  1696. TFileListHelper Helper(this, FileList, false);
  1697. // always specify path to list, do not attempt to list "current" dir as:
  1698. // 1) List() lists again the last listed directory, not the current working directory
  1699. // 2) we handle this way the cached directory change
  1700. UnicodeString Directory = AbsolutePath(FileList->Directory, false);
  1701. FFileZillaIntf->List(Directory.c_str());
  1702. GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_ALLOW_CANCEL);
  1703. FLastDataSent = Now();
  1704. }
  1705. //---------------------------------------------------------------------------
  1706. void __fastcall TFTPFileSystem::ReadDirectory(TRemoteFileList * FileList)
  1707. {
  1708. // whole below "-a" logic is for LIST,
  1709. // if we know we are going to use MLSD, skip it
  1710. if (FFileZillaIntf->UsingMlsd())
  1711. {
  1712. DoReadDirectory(FileList);
  1713. }
  1714. else
  1715. {
  1716. bool GotNoFilesForAll = false;
  1717. bool Repeat;
  1718. do
  1719. {
  1720. Repeat = false;
  1721. try
  1722. {
  1723. FDoListAll = (FListAll == asAuto) || (FListAll == asOn);
  1724. DoReadDirectory(FileList);
  1725. // We got no files with "-a", but again no files w/o "-a",
  1726. // so it was not "-a"'s problem, revert to auto and let it decide the next time
  1727. if (GotNoFilesForAll && (FileList->Count == 0))
  1728. {
  1729. assert(FListAll == asOff);
  1730. FListAll = asAuto;
  1731. }
  1732. else if (FListAll == asAuto)
  1733. {
  1734. // some servers take "-a" as a mask and return empty directory listing
  1735. // (note that it's actually never empty here, there's always at least parent directory,
  1736. // added explicitly by DoReadDirectory)
  1737. if ((FileList->Count == 0) ||
  1738. ((FileList->Count == 1) && FileList->Files[0]->IsParentDirectory))
  1739. {
  1740. Repeat = true;
  1741. FListAll = asOff;
  1742. GotNoFilesForAll = true;
  1743. FTerminal->LogEvent(L"LIST with -a switch returned empty directory listing, will try pure LIST");
  1744. }
  1745. else
  1746. {
  1747. // reading first directory has succeeded, always use "-a"
  1748. FListAll = asOn;
  1749. }
  1750. }
  1751. // use "-a" even for implicit directory reading by FZAPI?
  1752. // (e.g. before file transfer)
  1753. FDoListAll = (FListAll == asOn);
  1754. }
  1755. catch(Exception & E)
  1756. {
  1757. FDoListAll = false;
  1758. // reading the first directory has failed,
  1759. // further try without "-a" only as the server may not support it
  1760. if (FListAll == asAuto)
  1761. {
  1762. FTerminal->LogEvent(L"LIST with -a failed, walling back to pure LIST");
  1763. if (!FTerminal->Active)
  1764. {
  1765. FTerminal->Reopen(ropNoReadDirectory);
  1766. }
  1767. FListAll = asOff;
  1768. Repeat = true;
  1769. }
  1770. else
  1771. {
  1772. throw;
  1773. }
  1774. }
  1775. }
  1776. while (Repeat);
  1777. }
  1778. }
  1779. //---------------------------------------------------------------------------
  1780. void __fastcall TFTPFileSystem::DoReadFile(const UnicodeString & FileName,
  1781. TRemoteFile *& AFile)
  1782. {
  1783. // end-user has right to expect that client current directory is really
  1784. // current directory for the server
  1785. EnsureLocation();
  1786. TRemoteFileList * FileList = new TRemoteFileList();
  1787. try
  1788. {
  1789. TFileListHelper Helper(this, FileList, false);
  1790. FFileZillaIntf->ListFile(FileName.c_str());
  1791. GotReply(WaitForCommandReply(), REPLY_2XX_CODE | REPLY_ALLOW_CANCEL);
  1792. TRemoteFile * File = FileList->FindFile(UnixExtractFileName(FileName));
  1793. if (File != NULL)
  1794. {
  1795. AFile = File->Duplicate();
  1796. }
  1797. FLastDataSent = Now();
  1798. }
  1799. __finally
  1800. {
  1801. delete FileList;
  1802. }
  1803. }
  1804. //---------------------------------------------------------------------------
  1805. void __fastcall TFTPFileSystem::ReadFile(const UnicodeString FileName,
  1806. TRemoteFile *& File)
  1807. {
  1808. UnicodeString Path = UnixExtractFilePath(FileName);
  1809. UnicodeString NameOnly = UnixExtractFileName(FileName);
  1810. TRemoteFile *AFile = NULL;
  1811. bool Own;
  1812. if (FServerCapabilities->GetCapability(mlsd_command) == yes)
  1813. {
  1814. DoReadFile(FileName, AFile);
  1815. Own = true;
  1816. }
  1817. else
  1818. {
  1819. // FZAPI does not have efficient way to read properties of one file.
  1820. // In case we need properties of set of files from the same directory,
  1821. // cache the file list for future
  1822. if ((FFileListCache != NULL) &&
  1823. UnixComparePaths(Path, FFileListCache->Directory) &&
  1824. (UnixIsAbsolutePath(FFileListCache->Directory) ||
  1825. (FFileListCachePath == CurrentDirectory)))
  1826. {
  1827. AFile = FFileListCache->FindFile(NameOnly);
  1828. }
  1829. // if cache is invalid or file is not in cache, (re)read the directory
  1830. if (AFile == NULL)
  1831. {
  1832. TRemoteFileList * FileListCache = new TRemoteFileList();
  1833. FileListCache->Directory = Path;
  1834. try
  1835. {
  1836. ReadDirectory(FileListCache);
  1837. }
  1838. catch(...)
  1839. {
  1840. delete FileListCache;
  1841. throw;
  1842. }
  1843. // set only after we successfully read the directory,
  1844. // otherwise, when we reconnect from ReadDirectory,
  1845. // the FFileListCache is reset from ResetCache.
  1846. delete FFileListCache;
  1847. FFileListCache = FileListCache;
  1848. FFileListCachePath = GetCurrentDirectory();
  1849. AFile = FFileListCache->FindFile(NameOnly);
  1850. }
  1851. Own = false;
  1852. }
  1853. if (AFile == NULL)
  1854. {
  1855. File = NULL;
  1856. throw Exception(FMTLOAD(FILE_NOT_EXISTS, (FileName)));
  1857. }
  1858. assert(AFile != NULL);
  1859. File = Own ? AFile : AFile->Duplicate();
  1860. }
  1861. //---------------------------------------------------------------------------
  1862. void __fastcall TFTPFileSystem::ReadSymlink(TRemoteFile * SymlinkFile,
  1863. TRemoteFile *& File)
  1864. {
  1865. // Resolving symlinks over FTP is big overhead
  1866. // (involves opening TCPIP connection for retrieving "directory listing").
  1867. // Moreover FZAPI does not support that anyway.
  1868. // Note that while we could use MLST to read the symlink,
  1869. // it's hardly of any use as, if MLST is supported, we use MLSD to
  1870. // retrieve directory listing and from MLSD we cannot atm detect that
  1871. // the file is symlink anyway.
  1872. File = new TRemoteFile(SymlinkFile);
  1873. try
  1874. {
  1875. File->Terminal = FTerminal;
  1876. File->FileName = UnixExtractFileName(SymlinkFile->LinkTo);
  1877. // FZAPI treats all symlink target as directories
  1878. File->Type = FILETYPE_DIRECTORY;
  1879. }
  1880. catch(...)
  1881. {
  1882. delete File;
  1883. File = NULL;
  1884. throw;
  1885. }
  1886. }
  1887. //---------------------------------------------------------------------------
  1888. void __fastcall TFTPFileSystem::RenameFile(const UnicodeString AFileName,
  1889. const UnicodeString ANewName)
  1890. {
  1891. UnicodeString FileName = AbsolutePath(AFileName, false);
  1892. UnicodeString NewName = AbsolutePath(ANewName, false);
  1893. UnicodeString FileNameOnly = UnixExtractFileName(FileName);
  1894. UnicodeString FilePathOnly = UnixExtractFilePath(FileName);
  1895. UnicodeString NewNameOnly = UnixExtractFileName(NewName);
  1896. UnicodeString NewPathOnly = UnixExtractFilePath(NewName);
  1897. {
  1898. // ignore file list
  1899. TFileListHelper Helper(this, NULL, true);
  1900. FFileZillaIntf->Rename(FileNameOnly.c_str(), NewNameOnly.c_str(),
  1901. FilePathOnly.c_str(), NewPathOnly.c_str());
  1902. GotReply(WaitForCommandReply(), REPLY_2XX_CODE);
  1903. }
  1904. }
  1905. //---------------------------------------------------------------------------
  1906. void __fastcall TFTPFileSystem::CopyFile(const UnicodeString FileName,
  1907. const UnicodeString NewName)
  1908. {
  1909. assert(false);
  1910. }
  1911. //---------------------------------------------------------------------------
  1912. UnicodeString __fastcall TFTPFileSystem::FileUrl(const UnicodeString FileName)
  1913. {
  1914. UnicodeString Protocol = (FTerminal->SessionData->Ftps == ftpsImplicit) ? FtpsProtocol : FtpProtocol;
  1915. return FTerminal->FileUrl(Protocol, FileName);
  1916. }
  1917. //---------------------------------------------------------------------------
  1918. TStrings * __fastcall TFTPFileSystem::GetFixedPaths()
  1919. {
  1920. return NULL;
  1921. }
  1922. //---------------------------------------------------------------------------
  1923. void __fastcall TFTPFileSystem::SpaceAvailable(const UnicodeString /*Path*/,
  1924. TSpaceAvailable & /*ASpaceAvailable*/)
  1925. {
  1926. assert(false);
  1927. }
  1928. //---------------------------------------------------------------------------
  1929. const TSessionInfo & __fastcall TFTPFileSystem::GetSessionInfo()
  1930. {
  1931. return FSessionInfo;
  1932. }
  1933. //---------------------------------------------------------------------------
  1934. const TFileSystemInfo & __fastcall TFTPFileSystem::GetFileSystemInfo(bool /*Retrieve*/)
  1935. {
  1936. if (!FFileSystemInfoValid)
  1937. {
  1938. FFileSystemInfo.RemoteSystem = FSystem;
  1939. FFileSystemInfo.RemoteSystem.Unique();
  1940. if (FFeatures->Count == 0)
  1941. {
  1942. FFileSystemInfo.AdditionalInfo = LoadStr(FTP_NO_FEATURE_INFO);
  1943. }
  1944. else
  1945. {
  1946. FFileSystemInfo.AdditionalInfo =
  1947. FORMAT(L"%s\r\n", (LoadStr(FTP_FEATURE_INFO)));
  1948. for (int Index = 0; Index < FFeatures->Count; Index++)
  1949. {
  1950. FFileSystemInfo.AdditionalInfo += FORMAT(L" %s\r\n", (FFeatures->Strings[Index]));
  1951. }
  1952. }
  1953. for (int Index = 0; Index < fcCount; Index++)
  1954. {
  1955. FFileSystemInfo.IsCapable[Index] = IsCapable((TFSCapability)Index);
  1956. }
  1957. FFileSystemInfoValid = true;
  1958. }
  1959. return FFileSystemInfo;
  1960. }
  1961. //---------------------------------------------------------------------------
  1962. bool __fastcall TFTPFileSystem::TemporaryTransferFile(const UnicodeString & /*FileName*/)
  1963. {
  1964. return false;
  1965. }
  1966. //---------------------------------------------------------------------------
  1967. bool __fastcall TFTPFileSystem::GetStoredCredentialsTried()
  1968. {
  1969. return !FTerminal->SessionData->Password.IsEmpty();
  1970. }
  1971. //---------------------------------------------------------------------------
  1972. UnicodeString __fastcall TFTPFileSystem::GetUserName()
  1973. {
  1974. return FUserName;
  1975. }
  1976. //---------------------------------------------------------------------------
  1977. UnicodeString __fastcall TFTPFileSystem::GetCurrentDirectory()
  1978. {
  1979. return FCurrentDirectory;
  1980. }
  1981. //---------------------------------------------------------------------------
  1982. const wchar_t * __fastcall TFTPFileSystem::GetOption(int OptionID) const
  1983. {
  1984. TSessionData * Data = FTerminal->SessionData;
  1985. switch (OptionID)
  1986. {
  1987. case OPTION_PROXYHOST:
  1988. case OPTION_FWHOST:
  1989. FOptionScratch = Data->ProxyHost;
  1990. break;
  1991. case OPTION_PROXYUSER:
  1992. case OPTION_FWUSER:
  1993. FOptionScratch = Data->ProxyUsername;
  1994. break;
  1995. case OPTION_PROXYPASS:
  1996. case OPTION_FWPASS:
  1997. FOptionScratch = Data->ProxyPassword;
  1998. break;
  1999. case OPTION_TRANSFERIP:
  2000. FOptionScratch = FTerminal->Configuration->ExternalIpAddress;
  2001. break;
  2002. case OPTION_ANONPWD:
  2003. case OPTION_TRANSFERIP6:
  2004. FOptionScratch = L"";
  2005. break;
  2006. default:
  2007. assert(false);
  2008. FOptionScratch = L"";
  2009. }
  2010. return FOptionScratch.c_str();
  2011. }
  2012. //---------------------------------------------------------------------------
  2013. int __fastcall TFTPFileSystem::GetOptionVal(int OptionID) const
  2014. {
  2015. TSessionData * Data = FTerminal->SessionData;
  2016. int Result;
  2017. switch (OptionID)
  2018. {
  2019. case OPTION_PROXYTYPE:
  2020. switch (Data->ProxyMethod)
  2021. {
  2022. case ::pmNone:
  2023. Result = 0; // PROXYTYPE_NOPROXY;
  2024. break;
  2025. case pmSocks4:
  2026. Result = 2; // PROXYTYPE_SOCKS4A
  2027. break;
  2028. case pmSocks5:
  2029. Result = 3; // PROXYTYPE_SOCKS5
  2030. break;
  2031. case pmHTTP:
  2032. Result = 4; // PROXYTYPE_HTTP11
  2033. break;
  2034. case pmTelnet:
  2035. case pmCmd:
  2036. default:
  2037. assert(false);
  2038. Result = 0; // PROXYTYPE_NOPROXY;
  2039. break;
  2040. }
  2041. break;
  2042. case OPTION_PROXYPORT:
  2043. case OPTION_FWPORT:
  2044. Result = Data->ProxyPort;
  2045. break;
  2046. case OPTION_PROXYUSELOGON:
  2047. Result = !Data->ProxyUsername.IsEmpty();
  2048. break;
  2049. case OPTION_LOGONTYPE:
  2050. Result = Data->FtpProxyLogonType;
  2051. break;
  2052. case OPTION_TIMEOUTLENGTH:
  2053. Result = Data->Timeout;
  2054. break;
  2055. case OPTION_DEBUGSHOWLISTING:
  2056. Result = true;
  2057. break;
  2058. case OPTION_PASV:
  2059. // should never get here t_server.nPasv being nonzero
  2060. assert(false);
  2061. Result = FALSE;
  2062. break;
  2063. case OPTION_PRESERVEDOWNLOADFILETIME:
  2064. case OPTION_MPEXT_PRESERVEUPLOADFILETIME:
  2065. Result = FFileTransferPreserveTime ? TRUE : FALSE;
  2066. break;
  2067. case OPTION_LIMITPORTRANGE:
  2068. Result = FALSE;
  2069. break;
  2070. case OPTION_PORTRANGELOW:
  2071. case OPTION_PORTRANGEHIGH:
  2072. // should never get here OPTION_LIMITPORTRANGE being zero
  2073. assert(false);
  2074. Result = 0;
  2075. break;
  2076. case OPTION_ENABLE_IPV6:
  2077. Result = ((Data->AddressFamily == afIPv6) ? TRUE : FALSE);
  2078. break;
  2079. case OPTION_KEEPALIVE:
  2080. Result = ((Data->FtpPingType != ptOff) ? TRUE : FALSE);
  2081. break;
  2082. case OPTION_INTERVALLOW:
  2083. case OPTION_INTERVALHIGH:
  2084. Result = Data->FtpPingInterval;
  2085. break;
  2086. case OPTION_VMSALLREVISIONS:
  2087. Result = FALSE;
  2088. break;
  2089. case OPTION_SPEEDLIMIT_DOWNLOAD_TYPE:
  2090. case OPTION_SPEEDLIMIT_UPLOAD_TYPE:
  2091. Result = (FFileTransferCPSLimit == 0 ? 0 : 1);
  2092. break;
  2093. case OPTION_SPEEDLIMIT_DOWNLOAD_VALUE:
  2094. case OPTION_SPEEDLIMIT_UPLOAD_VALUE:
  2095. Result = (FFileTransferCPSLimit / 1024); // FZAPI expects KiB/s
  2096. break;
  2097. case OPTION_MPEXT_SHOWHIDDEN:
  2098. Result = (FDoListAll ? TRUE : FALSE);
  2099. break;
  2100. case OPTION_MPEXT_SSLSESSIONREUSE:
  2101. Result = (Data->SslSessionReuse ? TRUE : FALSE);
  2102. break;
  2103. case OPTION_MPEXT_MIN_TLS_VERSION:
  2104. Result = Data->MinTlsVersion;
  2105. break;
  2106. case OPTION_MPEXT_MAX_TLS_VERSION:
  2107. Result = Data->MaxTlsVersion;
  2108. break;
  2109. case OPTION_MPEXT_SNDBUF:
  2110. Result = Data->SendBuf;
  2111. break;
  2112. case OPTION_MPEXT_TRANSFER_ACTIVE_IMMEDIATELLY:
  2113. Result = Data->FtpTransferActiveImmediatelly;
  2114. break;
  2115. case OPTION_MPEXT_REMOVE_BOM:
  2116. Result = FFileTransferRemoveBOM ? TRUE : FALSE;
  2117. break;
  2118. default:
  2119. assert(false);
  2120. Result = FALSE;
  2121. break;
  2122. }
  2123. return Result;
  2124. }
  2125. //---------------------------------------------------------------------------
  2126. bool __fastcall TFTPFileSystem::PostMessage(unsigned int Type, WPARAM wParam, LPARAM lParam)
  2127. {
  2128. if (Type == TFileZillaIntf::MSG_TRANSFERSTATUS)
  2129. {
  2130. // Stop here if FileTransferProgress is proceeding,
  2131. // it makes "pause" in queue work.
  2132. // Paused queue item stops in some of the TFileOperationProgressType
  2133. // methods called from FileTransferProgress
  2134. TGuard Guard(FTransferStatusCriticalSection);
  2135. }
  2136. TGuard Guard(FQueueCriticalSection);
  2137. FQueue->push_back(TMessageQueue::value_type(wParam, lParam));
  2138. SetEvent(FQueueEvent);
  2139. return true;
  2140. }
  2141. //---------------------------------------------------------------------------
  2142. bool __fastcall TFTPFileSystem::ProcessMessage()
  2143. {
  2144. bool Result;
  2145. TMessageQueue::value_type Message;
  2146. {
  2147. TGuard Guard(FQueueCriticalSection);
  2148. Result = !FQueue->empty();
  2149. if (Result)
  2150. {
  2151. Message = FQueue->front();
  2152. FQueue->pop_front();
  2153. }
  2154. else
  2155. {
  2156. // now we are perfectly sure that the queue is empty as it is locked,
  2157. // so reset the event
  2158. ResetEvent(FQueueEvent);
  2159. }
  2160. }
  2161. if (Result)
  2162. {
  2163. FFileZillaIntf->HandleMessage(Message.first, Message.second);
  2164. }
  2165. return Result;
  2166. }
  2167. //---------------------------------------------------------------------------
  2168. void __fastcall TFTPFileSystem::DiscardMessages()
  2169. {
  2170. while (ProcessMessage());
  2171. }
  2172. //---------------------------------------------------------------------------
  2173. void __fastcall TFTPFileSystem::WaitForMessages()
  2174. {
  2175. unsigned int Result = WaitForSingleObject(FQueueEvent, INFINITE);
  2176. if (Result != WAIT_OBJECT_0)
  2177. {
  2178. FTerminal->FatalError(NULL, FMTLOAD(INTERNAL_ERROR, (L"ftp#1", IntToStr(int(Result)))));
  2179. }
  2180. }
  2181. //---------------------------------------------------------------------------
  2182. void __fastcall TFTPFileSystem::PoolForFatalNonCommandReply()
  2183. {
  2184. assert(FReply == 0);
  2185. assert(FCommandReply == 0);
  2186. assert(!FWaitingForReply);
  2187. FWaitingForReply = true;
  2188. unsigned int Reply;
  2189. try
  2190. {
  2191. // discard up to one reply
  2192. // (it should not happen here that two replies are posted anyway)
  2193. while (ProcessMessage() && (FReply == 0));
  2194. Reply = FReply;
  2195. }
  2196. __finally
  2197. {
  2198. FReply = 0;
  2199. assert(FCommandReply == 0);
  2200. FCommandReply = 0;
  2201. assert(FWaitingForReply);
  2202. FWaitingForReply = false;
  2203. }
  2204. if (Reply != 0)
  2205. {
  2206. // throws
  2207. GotNonCommandReply(Reply);
  2208. }
  2209. }
  2210. //---------------------------------------------------------------------------
  2211. bool __fastcall TFTPFileSystem::NoFinalLastCode()
  2212. {
  2213. return (FLastCodeClass == 0) || (FLastCodeClass == 1);
  2214. }
  2215. //---------------------------------------------------------------------------
  2216. bool __fastcall TFTPFileSystem::KeepWaitingForReply(unsigned int & ReplyToAwait, bool WantLastCode)
  2217. {
  2218. // to keep waiting,
  2219. // non-command reply must be unset,
  2220. // the reply we wait for must be unset or
  2221. // last code must be unset (if we wait for it)
  2222. return
  2223. (FReply == 0) &&
  2224. ((ReplyToAwait == 0) ||
  2225. (WantLastCode && NoFinalLastCode()));
  2226. }
  2227. //---------------------------------------------------------------------------
  2228. void __fastcall TFTPFileSystem::DoWaitForReply(unsigned int & ReplyToAwait, bool WantLastCode)
  2229. {
  2230. try
  2231. {
  2232. while (KeepWaitingForReply(ReplyToAwait, WantLastCode))
  2233. {
  2234. WaitForMessages();
  2235. // wait for the first reply only,
  2236. // i.e. in case two replies are posted get the first only.
  2237. // e.g. when server closes the connection, but posts error message before,
  2238. // sometime it happens that command (like download) fails because of the error
  2239. // and does not catch the disconnection. then asynchronous "disconnect reply"
  2240. // is posted immediately afterwards. leave detection of that to Idle()
  2241. while (ProcessMessage() && KeepWaitingForReply(ReplyToAwait, WantLastCode));
  2242. }
  2243. if (FReply != 0)
  2244. {
  2245. // throws
  2246. GotNonCommandReply(FReply);
  2247. }
  2248. }
  2249. catch(...)
  2250. {
  2251. // even if non-fatal error happens, we must process pending message,
  2252. // so that we "eat" the reply message, so that it gets not mistakenly
  2253. // associated with future connect
  2254. if (FTerminal->Active)
  2255. {
  2256. DoWaitForReply(ReplyToAwait, WantLastCode);
  2257. }
  2258. throw;
  2259. }
  2260. }
  2261. //---------------------------------------------------------------------------
  2262. unsigned int __fastcall TFTPFileSystem::WaitForReply(bool Command, bool WantLastCode)
  2263. {
  2264. assert(FReply == 0);
  2265. assert(FCommandReply == 0);
  2266. assert(!FWaitingForReply);
  2267. ResetReply();
  2268. FWaitingForReply = true;
  2269. unsigned int Reply;
  2270. try
  2271. {
  2272. unsigned int & ReplyToAwait = (Command ? FCommandReply : FReply);
  2273. DoWaitForReply(ReplyToAwait, WantLastCode);
  2274. Reply = ReplyToAwait;
  2275. }
  2276. __finally
  2277. {
  2278. FReply = 0;
  2279. FCommandReply = 0;
  2280. assert(FWaitingForReply);
  2281. FWaitingForReply = false;
  2282. }
  2283. return Reply;
  2284. }
  2285. //---------------------------------------------------------------------------
  2286. unsigned int __fastcall TFTPFileSystem::WaitForCommandReply(bool WantLastCode)
  2287. {
  2288. return WaitForReply(true, WantLastCode);
  2289. }
  2290. //---------------------------------------------------------------------------
  2291. void __fastcall TFTPFileSystem::WaitForFatalNonCommandReply()
  2292. {
  2293. WaitForReply(false, false);
  2294. assert(false);
  2295. }
  2296. //---------------------------------------------------------------------------
  2297. void __fastcall TFTPFileSystem::ResetReply()
  2298. {
  2299. FLastCode = 0;
  2300. FLastCodeClass = 0;
  2301. assert(FLastResponse != NULL);
  2302. FLastResponse->Clear();
  2303. assert(FLastErrorResponse != NULL);
  2304. FLastErrorResponse->Clear();
  2305. assert(FLastError != NULL);
  2306. FLastError->Clear();
  2307. }
  2308. //---------------------------------------------------------------------------
  2309. void __fastcall TFTPFileSystem::GotNonCommandReply(unsigned int Reply)
  2310. {
  2311. assert(FLAGSET(Reply, TFileZillaIntf::REPLY_DISCONNECTED));
  2312. GotReply(Reply);
  2313. // should never get here as GotReply should raise fatal exception
  2314. assert(false);
  2315. }
  2316. //---------------------------------------------------------------------------
  2317. void __fastcall TFTPFileSystem::GotReply(unsigned int Reply, unsigned int Flags,
  2318. UnicodeString Error, unsigned int * Code, TStrings ** Response)
  2319. {
  2320. try
  2321. {
  2322. if (FLAGSET(Reply, TFileZillaIntf::REPLY_OK))
  2323. {
  2324. assert(Reply == TFileZillaIntf::REPLY_OK);
  2325. // With REPLY_2XX_CODE treat "OK" non-2xx code like an error.
  2326. // REPLY_3XX_CODE has to be always used along with REPLY_2XX_CODE.
  2327. if ((FLAGSET(Flags, REPLY_2XX_CODE) && (FLastCodeClass != 2)) &&
  2328. ((FLAGCLEAR(Flags, REPLY_3XX_CODE) || (FLastCodeClass != 3))))
  2329. {
  2330. GotReply(TFileZillaIntf::REPLY_ERROR, Flags, Error);
  2331. }
  2332. }
  2333. else if (FLAGSET(Reply, TFileZillaIntf::REPLY_CANCEL) &&
  2334. FLAGSET(Flags, REPLY_ALLOW_CANCEL))
  2335. {
  2336. assert(
  2337. (Reply == (TFileZillaIntf::REPLY_CANCEL | TFileZillaIntf::REPLY_ERROR)) ||
  2338. (Reply == (TFileZillaIntf::REPLY_ABORTED | TFileZillaIntf::REPLY_CANCEL | TFileZillaIntf::REPLY_ERROR)));
  2339. // noop
  2340. }
  2341. // we do not expect these with our usage of FZ
  2342. else if (Reply &
  2343. (TFileZillaIntf::REPLY_WOULDBLOCK | TFileZillaIntf::REPLY_OWNERNOTSET |
  2344. TFileZillaIntf::REPLY_INVALIDPARAM | TFileZillaIntf::REPLY_ALREADYCONNECTED |
  2345. TFileZillaIntf::REPLY_IDLE | TFileZillaIntf::REPLY_NOTINITIALIZED |
  2346. TFileZillaIntf::REPLY_ALREADYINIZIALIZED))
  2347. {
  2348. FTerminal->FatalError(NULL, FMTLOAD(INTERNAL_ERROR, (L"ftp#2", FORMAT(L"0x%x", (int(Reply))))));
  2349. }
  2350. else
  2351. {
  2352. // everything else must be an error or disconnect notification
  2353. assert(
  2354. FLAGSET(Reply, TFileZillaIntf::REPLY_ERROR) ||
  2355. FLAGSET(Reply, TFileZillaIntf::REPLY_DISCONNECTED));
  2356. // TODO: REPLY_CRITICALERROR ignored
  2357. // REPLY_NOTCONNECTED happens if connection is closed between moment
  2358. // when FZAPI interface method dispatches the command to FZAPI thread
  2359. // and moment when FZAPI thread receives the command
  2360. bool Disconnected =
  2361. FLAGSET(Reply, TFileZillaIntf::REPLY_DISCONNECTED) ||
  2362. FLAGSET(Reply, TFileZillaIntf::REPLY_NOTCONNECTED);
  2363. AnsiString HelpKeyword;
  2364. TStrings * MoreMessages = new TStringList();
  2365. try
  2366. {
  2367. if (Disconnected)
  2368. {
  2369. if (FLAGCLEAR(Flags, REPLY_CONNECT))
  2370. {
  2371. MoreMessages->Add(LoadStr(LOST_CONNECTION));
  2372. Discard();
  2373. FTerminal->Closed();
  2374. }
  2375. else
  2376. {
  2377. // For connection failure, do not report that connection was lost,
  2378. // its obvious.
  2379. // Also do not report to terminal that we are closed as
  2380. // that turns terminal into closed mode, but we want to
  2381. // pretend (at least with failed authentication) to retry
  2382. // with the same connection (as with SSH), so we explicitly
  2383. // close terminal in Open() only after we give up
  2384. Discard();
  2385. }
  2386. }
  2387. if (FLAGSET(Reply, TFileZillaIntf::REPLY_ABORTED))
  2388. {
  2389. MoreMessages->Add(LoadStr(USER_TERMINATED));
  2390. }
  2391. if (FLAGSET(Reply, TFileZillaIntf::REPLY_NOTSUPPORTED))
  2392. {
  2393. MoreMessages->Add(LoadStr(FZ_NOTSUPPORTED));
  2394. }
  2395. if (FLastCode == 530)
  2396. {
  2397. MoreMessages->Add(LoadStr(AUTHENTICATION_FAILED));
  2398. }
  2399. if (FLastCode == 425)
  2400. {
  2401. if (!FTerminal->SessionData->FtpPasvMode)
  2402. {
  2403. MoreMessages->Add(LoadStr(FTP_CANNOT_OPEN_ACTIVE_CONNECTION2));
  2404. HelpKeyword = HELP_FTP_CANNOT_OPEN_ACTIVE_CONNECTION;
  2405. }
  2406. }
  2407. if (FLastCode == DummyTimeoutCode)
  2408. {
  2409. HelpKeyword = HELP_ERRORMSG_TIMEOUT;
  2410. }
  2411. MoreMessages->AddStrings(FLastError);
  2412. // already cleared from WaitForReply, but GotReply can be also called
  2413. // from Closed. then make sure that error from previous command not
  2414. // associated with session closure is not reused
  2415. FLastError->Clear();
  2416. MoreMessages->AddStrings(FLastErrorResponse);
  2417. // see comment for FLastError
  2418. FLastResponse->Clear();
  2419. FLastErrorResponse->Clear();
  2420. if (MoreMessages->Count == 0)
  2421. {
  2422. delete MoreMessages;
  2423. MoreMessages = NULL;
  2424. }
  2425. }
  2426. catch(...)
  2427. {
  2428. delete MoreMessages;
  2429. throw;
  2430. }
  2431. if (Error.IsEmpty() && (MoreMessages != NULL))
  2432. {
  2433. assert(MoreMessages->Count > 0);
  2434. // bit too generic assigning of main instructions, let's see how it works
  2435. Error = MainInstructions(MoreMessages->Strings[0]);
  2436. MoreMessages->Delete(0);
  2437. }
  2438. if (Disconnected)
  2439. {
  2440. // for fatal error, it is essential that there is some message
  2441. assert(!Error.IsEmpty());
  2442. ExtException * E = new ExtException(Error, MoreMessages, true, HelpKeyword);
  2443. try
  2444. {
  2445. FTerminal->FatalError(E, L"");
  2446. }
  2447. __finally
  2448. {
  2449. delete E;
  2450. }
  2451. }
  2452. else
  2453. {
  2454. throw ExtException(Error, MoreMessages, true, HelpKeyword);
  2455. }
  2456. }
  2457. if ((Code != NULL) && (FLastCodeClass != DummyCodeClass))
  2458. {
  2459. *Code = FLastCode;
  2460. }
  2461. if (Response != NULL)
  2462. {
  2463. *Response = FLastResponse;
  2464. FLastResponse = new TStringList();
  2465. // just to be consistent
  2466. delete FLastErrorResponse;
  2467. FLastErrorResponse = new TStringList();
  2468. }
  2469. }
  2470. __finally
  2471. {
  2472. ResetReply();
  2473. }
  2474. }
  2475. //---------------------------------------------------------------------------
  2476. void __fastcall TFTPFileSystem::SetLastCode(int Code)
  2477. {
  2478. FLastCode = Code;
  2479. FLastCodeClass = (Code / 100);
  2480. }
  2481. //---------------------------------------------------------------------------
  2482. void __fastcall TFTPFileSystem::StoreLastResponse(const UnicodeString & Text)
  2483. {
  2484. FLastResponse->Add(Text);
  2485. if (FLastCodeClass >= 4)
  2486. {
  2487. FLastErrorResponse->Add(Text);
  2488. }
  2489. }
  2490. //---------------------------------------------------------------------------
  2491. void __fastcall TFTPFileSystem::HandleReplyStatus(UnicodeString Response)
  2492. {
  2493. int Code;
  2494. if (FOnCaptureOutput != NULL)
  2495. {
  2496. FOnCaptureOutput(Response, false);
  2497. }
  2498. // Two forms of multiline responses were observed
  2499. // (the first is according to the RFC 959):
  2500. // 211-Features:
  2501. // MDTM
  2502. // REST STREAM
  2503. // SIZE
  2504. // 211 End
  2505. // 211-Features:
  2506. // 211-MDTM
  2507. // 211-REST STREAM
  2508. // 211-SIZE
  2509. // 211-AUTH TLS
  2510. // 211-PBSZ
  2511. // 211-PROT
  2512. // 211 End
  2513. bool HasCodePrefix =
  2514. (Response.Length() >= 3) &&
  2515. TryStrToInt(Response.SubString(1, 3), Code) &&
  2516. (Code >= 100) && (Code <= 599) &&
  2517. ((Response.Length() == 3) || (Response[4] == L' ') || (Response[4] == L'-'));
  2518. if (HasCodePrefix && !FMultineResponse)
  2519. {
  2520. FMultineResponse = (Response.Length() >= 4) && (Response[4] == L'-');
  2521. FLastResponse->Clear();
  2522. FLastErrorResponse->Clear();
  2523. SetLastCode(Code);
  2524. if (Response.Length() >= 5)
  2525. {
  2526. StoreLastResponse(Response.SubString(5, Response.Length() - 4));
  2527. }
  2528. }
  2529. else
  2530. {
  2531. int Start;
  2532. // response with code prefix
  2533. if (HasCodePrefix && (FLastCode == Code))
  2534. {
  2535. // End of multiline response?
  2536. if ((Response.Length() <= 3) || (Response[4] == L' '))
  2537. {
  2538. FMultineResponse = false;
  2539. }
  2540. Start = 5;
  2541. }
  2542. else
  2543. {
  2544. Start = (((Response.Length() >= 1) && (Response[1] == L' ')) ? 2 : 1);
  2545. }
  2546. // Intermediate empty lines are being added
  2547. if (FMultineResponse || (Response.Length() >= Start))
  2548. {
  2549. StoreLastResponse(Response.SubString(Start, Response.Length() - Start + 1));
  2550. }
  2551. }
  2552. if (!FMultineResponse)
  2553. {
  2554. if (FLastCode == 220)
  2555. {
  2556. if (FTerminal->Configuration->ShowFtpWelcomeMessage)
  2557. {
  2558. FTerminal->DisplayBanner(FLastResponse->Text);
  2559. }
  2560. }
  2561. else if (FLastCommand == PASS)
  2562. {
  2563. // 530 = "Not logged in."
  2564. if (FLastCode == 530)
  2565. {
  2566. FPasswordFailed = true;
  2567. }
  2568. }
  2569. else if (FLastCommand == SYST)
  2570. {
  2571. assert(FSystem.IsEmpty());
  2572. // Positive reply to "SYST" must be 215, see RFC 959
  2573. if (FLastCode == 215)
  2574. {
  2575. FSystem = FLastResponse->Text.TrimRight();
  2576. // full name is "Personal FTP Server PRO K6.0"
  2577. if ((FListAll == asAuto) &&
  2578. (FSystem.Pos(L"Personal FTP Server") > 0))
  2579. {
  2580. FTerminal->LogEvent(L"Server is known not to support LIST -a");
  2581. FListAll = asOff;
  2582. }
  2583. }
  2584. else
  2585. {
  2586. FSystem = L"";
  2587. }
  2588. }
  2589. else if (FLastCommand == FEAT)
  2590. {
  2591. // Response to FEAT must be multiline, where leading and trailing line
  2592. // is "meaningless". See RFC 2389.
  2593. if ((FLastCode == 211) && (FLastResponse->Count > 2))
  2594. {
  2595. FLastResponse->Delete(0);
  2596. FLastResponse->Delete(FLastResponse->Count - 1);
  2597. FFeatures->Assign(FLastResponse);
  2598. }
  2599. else
  2600. {
  2601. FFeatures->Clear();
  2602. }
  2603. }
  2604. }
  2605. }
  2606. //---------------------------------------------------------------------------
  2607. UnicodeString __fastcall TFTPFileSystem::ExtractStatusMessage(UnicodeString Status)
  2608. {
  2609. // CApiLog::LogMessage
  2610. // (note that the formatting may not be present when LogMessageRaw is used)
  2611. int P1 = Status.Pos(L"): ");
  2612. if (P1 > 0)
  2613. {
  2614. int P2 = Status.Pos(L".cpp(");
  2615. if ((P2 > 0) && (P2 < P1))
  2616. {
  2617. int P3 = Status.Pos(L" caller=0x");
  2618. if ((P3 > 0) && (P3 > P1))
  2619. {
  2620. Status = Status.SubString(P1 + 3, P3 - P1 - 3);
  2621. }
  2622. }
  2623. }
  2624. return Status;
  2625. }
  2626. //---------------------------------------------------------------------------
  2627. bool __fastcall TFTPFileSystem::HandleStatus(const wchar_t * AStatus, int Type)
  2628. {
  2629. TLogLineType LogType = (TLogLineType)-1;
  2630. UnicodeString Status(AStatus);
  2631. switch (Type)
  2632. {
  2633. case TFileZillaIntf::LOG_STATUS:
  2634. FTerminal->Information(Status, true);
  2635. LogType = llMessage;
  2636. break;
  2637. case TFileZillaIntf::LOG_COMMAND:
  2638. if (Status == L"SYST")
  2639. {
  2640. FLastCommand = SYST;
  2641. }
  2642. else if (Status == L"FEAT")
  2643. {
  2644. FLastCommand = FEAT;
  2645. }
  2646. else if (Status.SubString(1, 5) == L"PASS ")
  2647. {
  2648. FLastCommand = PASS;
  2649. }
  2650. else
  2651. {
  2652. FLastCommand = CMD_UNKNOWN;
  2653. }
  2654. LogType = llInput;
  2655. break;
  2656. case TFileZillaIntf::LOG_ERROR:
  2657. case TFileZillaIntf::LOG_APIERROR:
  2658. case TFileZillaIntf::LOG_WARNING:
  2659. // when timeout message occurs, break loop waiting for response code
  2660. // by setting dummy one
  2661. if (Type == TFileZillaIntf::LOG_ERROR)
  2662. {
  2663. if (Status == FTimeoutStatus)
  2664. {
  2665. if (NoFinalLastCode())
  2666. {
  2667. SetLastCode(DummyTimeoutCode);
  2668. }
  2669. }
  2670. else if (Status == FDisconnectStatus)
  2671. {
  2672. if (NoFinalLastCode())
  2673. {
  2674. SetLastCode(DummyDisconnectCode);
  2675. }
  2676. }
  2677. }
  2678. // there can be multiple error messages associated with single failure
  2679. // (such as "cannot open local file..." followed by "download failed")
  2680. Status = ExtractStatusMessage(Status);
  2681. FLastError->Add(Status);
  2682. LogType = llMessage;
  2683. break;
  2684. case TFileZillaIntf::LOG_REPLY:
  2685. HandleReplyStatus(AStatus);
  2686. LogType = llOutput;
  2687. break;
  2688. case TFileZillaIntf::LOG_INFO:
  2689. Status = ExtractStatusMessage(Status);
  2690. LogType = llMessage;
  2691. break;
  2692. case TFileZillaIntf::LOG_DEBUG:
  2693. // used for directory listing only
  2694. LogType = llMessage;
  2695. break;
  2696. default:
  2697. assert(false);
  2698. break;
  2699. }
  2700. if (FTerminal->Log->Logging && (LogType != (TLogLineType)-1))
  2701. {
  2702. FTerminal->Log->Add(LogType, Status);
  2703. }
  2704. return true;
  2705. }
  2706. //---------------------------------------------------------------------------
  2707. TDateTime __fastcall TFTPFileSystem::ConvertLocalTimestamp(time_t Time)
  2708. {
  2709. // This reverses how FZAPI converts FILETIME to time_t,
  2710. // before passing it to FZ_ASYNCREQUEST_OVERWRITE.
  2711. __int64 Timestamp;
  2712. tm * Tm = localtime(&Time);
  2713. if (Tm != NULL)
  2714. {
  2715. SYSTEMTIME SystemTime;
  2716. SystemTime.wYear = static_cast<WORD>(Tm->tm_year + 1900);
  2717. SystemTime.wMonth = static_cast<WORD>(Tm->tm_mon + 1);
  2718. SystemTime.wDayOfWeek = 0;
  2719. SystemTime.wDay = static_cast<WORD>(Tm->tm_mday);
  2720. SystemTime.wHour = static_cast<WORD>(Tm->tm_hour);
  2721. SystemTime.wMinute = static_cast<WORD>(Tm->tm_min);
  2722. SystemTime.wSecond = static_cast<WORD>(Tm->tm_sec);
  2723. SystemTime.wMilliseconds = 0;
  2724. FILETIME LocalTime;
  2725. SystemTimeToFileTime(&SystemTime, &LocalTime);
  2726. FILETIME FileTime;
  2727. LocalFileTimeToFileTime(&LocalTime, &FileTime);
  2728. Timestamp = ConvertTimestampToUnixSafe(FileTime, dstmUnix);
  2729. }
  2730. else
  2731. {
  2732. // incorrect, but at least something
  2733. Timestamp = Time;
  2734. }
  2735. return UnixToDateTime(Timestamp, dstmUnix);
  2736. }
  2737. //---------------------------------------------------------------------------
  2738. bool __fastcall TFTPFileSystem::HandleAsynchRequestOverwrite(
  2739. wchar_t * FileName1, size_t FileName1Len, const wchar_t * /*FileName2*/,
  2740. const wchar_t * /*Path1*/, const wchar_t * /*Path2*/,
  2741. __int64 Size1, __int64 Size2, time_t LocalTime,
  2742. bool /*HasLocalTime*/, const TRemoteFileTime & RemoteTime, void * AUserData, int & RequestResult)
  2743. {
  2744. if (!FActive)
  2745. {
  2746. return false;
  2747. }
  2748. else
  2749. {
  2750. TFileTransferData & UserData = *((TFileTransferData *)AUserData);
  2751. if (UserData.OverwriteResult >= 0)
  2752. {
  2753. // on retry, use the same answer as on the first attempt
  2754. RequestResult = UserData.OverwriteResult;
  2755. }
  2756. else
  2757. {
  2758. TFileOperationProgressType * OperationProgress = FTerminal->OperationProgress;
  2759. UnicodeString FileName = FileName1;
  2760. assert(UserData.FileName == FileName);
  2761. TOverwriteMode OverwriteMode = omOverwrite;
  2762. TOverwriteFileParams FileParams;
  2763. bool NoFileParams =
  2764. (Size1 < 0) || (LocalTime == 0) ||
  2765. (Size2 < 0) || !RemoteTime.HasDate;
  2766. if (!NoFileParams)
  2767. {
  2768. FileParams.SourceSize = Size2;
  2769. FileParams.DestSize = Size1;
  2770. if (OperationProgress->Side == osLocal)
  2771. {
  2772. FileParams.SourceTimestamp = ConvertLocalTimestamp(LocalTime);
  2773. RemoteFileTimeToDateTimeAndPrecision(RemoteTime, FileParams.DestTimestamp, FileParams.DestPrecision);
  2774. }
  2775. else
  2776. {
  2777. FileParams.DestTimestamp = ConvertLocalTimestamp(LocalTime);
  2778. RemoteFileTimeToDateTimeAndPrecision(RemoteTime, FileParams.SourceTimestamp, FileParams.SourcePrecision);
  2779. }
  2780. }
  2781. if (ConfirmOverwrite(FileName, OverwriteMode, OperationProgress,
  2782. (NoFileParams ? NULL : &FileParams), UserData.CopyParam, UserData.Params,
  2783. UserData.AutoResume && UserData.CopyParam->AllowResume(FileParams.SourceSize)))
  2784. {
  2785. switch (OverwriteMode)
  2786. {
  2787. case omOverwrite:
  2788. if (FileName != FileName1)
  2789. {
  2790. wcsncpy(FileName1, FileName.c_str(), FileName1Len);
  2791. FileName1[FileName1Len - 1] = L'\0';
  2792. UserData.FileName = FileName1;
  2793. RequestResult = TFileZillaIntf::FILEEXISTS_RENAME;
  2794. }
  2795. else
  2796. {
  2797. RequestResult = TFileZillaIntf::FILEEXISTS_OVERWRITE;
  2798. }
  2799. break;
  2800. case omResume:
  2801. RequestResult = TFileZillaIntf::FILEEXISTS_RESUME;
  2802. break;
  2803. case omComplete:
  2804. FTerminal->LogEvent(L"File transfer was completed before disconnect");
  2805. RequestResult = TFileZillaIntf::FILEEXISTS_COMPLETE;
  2806. break;
  2807. default:
  2808. assert(false);
  2809. RequestResult = TFileZillaIntf::FILEEXISTS_OVERWRITE;
  2810. break;
  2811. }
  2812. }
  2813. else
  2814. {
  2815. RequestResult = TFileZillaIntf::FILEEXISTS_SKIP;
  2816. }
  2817. }
  2818. // remember the answer for the retries
  2819. UserData.OverwriteResult = RequestResult;
  2820. if (RequestResult == TFileZillaIntf::FILEEXISTS_SKIP)
  2821. {
  2822. // when user chooses not to overwrite, break loop waiting for response code
  2823. // by setting dummy one, as FZAPI won't do anything then
  2824. SetLastCode(DummyTimeoutCode);
  2825. }
  2826. return true;
  2827. }
  2828. }
  2829. //---------------------------------------------------------------------------
  2830. struct TClipboardHandler
  2831. {
  2832. UnicodeString Text;
  2833. void __fastcall Copy(TObject * /*Sender*/)
  2834. {
  2835. TInstantOperationVisualizer Visualizer;
  2836. CopyToClipboard(Text);
  2837. }
  2838. };
  2839. //---------------------------------------------------------------------------
  2840. UnicodeString __fastcall FormatContactList(UnicodeString Entry1, UnicodeString Entry2)
  2841. {
  2842. if (!Entry1.IsEmpty() && !Entry2.IsEmpty())
  2843. {
  2844. return FORMAT(L"%s, %s", (Entry1, Entry2));
  2845. }
  2846. else
  2847. {
  2848. return Entry1 + Entry2;
  2849. }
  2850. }
  2851. //---------------------------------------------------------------------------
  2852. UnicodeString __fastcall FormatContact(const TFtpsCertificateData::TContact & Contact)
  2853. {
  2854. UnicodeString Result =
  2855. FORMAT(LoadStrPart(VERIFY_CERT_CONTACT, 1),
  2856. (FormatContactList(FormatContactList(FormatContactList(
  2857. Contact.Organization, Contact.Unit), Contact.CommonName), Contact.Mail)));
  2858. if ((wcslen(Contact.Country) > 0) ||
  2859. (wcslen(Contact.StateProvince) > 0) ||
  2860. (wcslen(Contact.Town) > 0))
  2861. {
  2862. Result +=
  2863. FORMAT(LoadStrPart(VERIFY_CERT_CONTACT, 2),
  2864. (FormatContactList(FormatContactList(
  2865. Contact.Country, Contact.StateProvince), Contact.Town)));
  2866. }
  2867. if (wcslen(Contact.Other) > 0)
  2868. {
  2869. Result += FORMAT(LoadStrPart(VERIFY_CERT_CONTACT, 3), (Contact.Other));
  2870. }
  2871. return Result;
  2872. }
  2873. //---------------------------------------------------------------------------
  2874. UnicodeString __fastcall FormatValidityTime(const TFtpsCertificateData::TValidityTime & ValidityTime)
  2875. {
  2876. return FormatDateTime(L"ddddd tt",
  2877. EncodeDateVerbose(
  2878. (unsigned short)ValidityTime.Year, (unsigned short)ValidityTime.Month,
  2879. (unsigned short)ValidityTime.Day) +
  2880. EncodeTimeVerbose(
  2881. (unsigned short)ValidityTime.Hour, (unsigned short)ValidityTime.Min,
  2882. (unsigned short)ValidityTime.Sec, 0));
  2883. }
  2884. //---------------------------------------------------------------------------
  2885. bool __fastcall TFTPFileSystem::HandleAsynchRequestVerifyCertificate(
  2886. const TFtpsCertificateData & Data, int & RequestResult)
  2887. {
  2888. if (!FActive)
  2889. {
  2890. return false;
  2891. }
  2892. else
  2893. {
  2894. FSessionInfo.CertificateFingerprint =
  2895. BytesToHex(RawByteString((const char*)Data.Hash, Data.HashLen), false, L':');
  2896. int VerificationResultStr;
  2897. switch (Data.VerificationResult)
  2898. {
  2899. case X509_V_OK:
  2900. VerificationResultStr = CERT_OK;
  2901. break;
  2902. case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
  2903. VerificationResultStr = CERT_ERR_UNABLE_TO_GET_ISSUER_CERT;
  2904. break;
  2905. case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
  2906. VerificationResultStr = CERT_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE;
  2907. break;
  2908. case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
  2909. VerificationResultStr = CERT_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY;
  2910. break;
  2911. case X509_V_ERR_CERT_SIGNATURE_FAILURE:
  2912. VerificationResultStr = CERT_ERR_CERT_SIGNATURE_FAILURE;
  2913. break;
  2914. case X509_V_ERR_CERT_NOT_YET_VALID:
  2915. VerificationResultStr = CERT_ERR_CERT_NOT_YET_VALID;
  2916. break;
  2917. case X509_V_ERR_CERT_HAS_EXPIRED:
  2918. VerificationResultStr = CERT_ERR_CERT_HAS_EXPIRED;
  2919. break;
  2920. case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
  2921. VerificationResultStr = CERT_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD;
  2922. break;
  2923. case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
  2924. VerificationResultStr = CERT_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD;
  2925. break;
  2926. case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
  2927. VerificationResultStr = CERT_ERR_DEPTH_ZERO_SELF_SIGNED_CERT;
  2928. break;
  2929. case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
  2930. VerificationResultStr = CERT_ERR_SELF_SIGNED_CERT_IN_CHAIN;
  2931. break;
  2932. case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
  2933. VerificationResultStr = CERT_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY;
  2934. break;
  2935. case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
  2936. VerificationResultStr = CERT_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE;
  2937. break;
  2938. case X509_V_ERR_INVALID_CA:
  2939. VerificationResultStr = CERT_ERR_INVALID_CA;
  2940. break;
  2941. case X509_V_ERR_PATH_LENGTH_EXCEEDED:
  2942. VerificationResultStr = CERT_ERR_PATH_LENGTH_EXCEEDED;
  2943. break;
  2944. case X509_V_ERR_INVALID_PURPOSE:
  2945. VerificationResultStr = CERT_ERR_INVALID_PURPOSE;
  2946. break;
  2947. case X509_V_ERR_CERT_UNTRUSTED:
  2948. VerificationResultStr = CERT_ERR_CERT_UNTRUSTED;
  2949. break;
  2950. case X509_V_ERR_CERT_REJECTED:
  2951. VerificationResultStr = CERT_ERR_CERT_REJECTED;
  2952. break;
  2953. case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
  2954. VerificationResultStr = CERT_ERR_KEYUSAGE_NO_CERTSIGN;
  2955. break;
  2956. case X509_V_ERR_CERT_CHAIN_TOO_LONG:
  2957. VerificationResultStr = CERT_ERR_CERT_CHAIN_TOO_LONG;
  2958. break;
  2959. default:
  2960. VerificationResultStr = CERT_ERR_UNKNOWN;
  2961. break;
  2962. }
  2963. UnicodeString Summary = LoadStr(VerificationResultStr);
  2964. if (Data.VerificationResult != X509_V_OK)
  2965. {
  2966. Summary += L" " + FMTLOAD(CERT_ERRDEPTH, (Data.VerificationDepth + 1));
  2967. }
  2968. FSessionInfo.Certificate =
  2969. FMTLOAD(CERT_TEXT, (
  2970. FormatContact(Data.Issuer),
  2971. FormatContact(Data.Subject),
  2972. FormatValidityTime(Data.ValidFrom),
  2973. FormatValidityTime(Data.ValidUntil),
  2974. FSessionInfo.CertificateFingerprint,
  2975. Summary));
  2976. RequestResult = 0;
  2977. THierarchicalStorage * Storage =
  2978. FTerminal->Configuration->CreateScpStorage(false);
  2979. try
  2980. {
  2981. Storage->AccessMode = smRead;
  2982. if (Storage->OpenSubKey(CertificateStorageKey, false) &&
  2983. Storage->ValueExists(FSessionInfo.CertificateFingerprint))
  2984. {
  2985. RequestResult = 1;
  2986. }
  2987. }
  2988. __finally
  2989. {
  2990. delete Storage;
  2991. }
  2992. if (RequestResult == 0)
  2993. {
  2994. UnicodeString Buf = FTerminal->SessionData->HostKey;
  2995. while ((RequestResult == 0) && !Buf.IsEmpty())
  2996. {
  2997. UnicodeString ExpectedKey = CutToChar(Buf, L';', false);
  2998. if (ExpectedKey == L"*")
  2999. {
  3000. UnicodeString Message = LoadStr(ANY_CERTIFICATE);
  3001. FTerminal->Information(Message, true);
  3002. FTerminal->Log->Add(llException, Message);
  3003. RequestResult = 1;
  3004. }
  3005. else if (ExpectedKey == FSessionInfo.CertificateFingerprint)
  3006. {
  3007. RequestResult = 1;
  3008. }
  3009. }
  3010. }
  3011. if (RequestResult == 0)
  3012. {
  3013. TClipboardHandler ClipboardHandler;
  3014. ClipboardHandler.Text = FSessionInfo.CertificateFingerprint;
  3015. TQueryButtonAlias Aliases[1];
  3016. Aliases[0].Button = qaRetry;
  3017. Aliases[0].Alias = LoadStr(COPY_KEY_BUTTON);
  3018. Aliases[0].OnClick = &ClipboardHandler.Copy;
  3019. TQueryParams Params;
  3020. Params.HelpKeyword = HELP_VERIFY_CERTIFICATE;
  3021. Params.NoBatchAnswers = qaYes | qaRetry;
  3022. Params.Aliases = Aliases;
  3023. Params.AliasesCount = LENOF(Aliases);
  3024. unsigned int Answer = FTerminal->QueryUser(
  3025. FMTLOAD(VERIFY_CERT_PROMPT3, (FSessionInfo.Certificate)),
  3026. NULL, qaYes | qaNo | qaCancel | qaRetry, &Params, qtWarning);
  3027. switch (Answer)
  3028. {
  3029. case qaYes:
  3030. // 2 = always, as used by FZ's VerifyCertDlg.cpp,
  3031. // however FZAPI takes all non-zero values equally
  3032. RequestResult = 2;
  3033. break;
  3034. case qaNo:
  3035. RequestResult = 1;
  3036. break;
  3037. case qaCancel:
  3038. RequestResult = 0;
  3039. break;
  3040. default:
  3041. assert(false);
  3042. RequestResult = 0;
  3043. break;
  3044. }
  3045. if (RequestResult == 2)
  3046. {
  3047. THierarchicalStorage * Storage =
  3048. FTerminal->Configuration->CreateScpStorage(false);
  3049. try
  3050. {
  3051. Storage->AccessMode = smReadWrite;
  3052. if (Storage->OpenSubKey(CertificateStorageKey, true))
  3053. {
  3054. Storage->WriteString(FSessionInfo.CertificateFingerprint, L"");
  3055. }
  3056. }
  3057. __finally
  3058. {
  3059. delete Storage;
  3060. }
  3061. }
  3062. }
  3063. return true;
  3064. }
  3065. }
  3066. //---------------------------------------------------------------------------
  3067. bool __fastcall TFTPFileSystem::HandleAsynchRequestNeedPass(
  3068. struct TNeedPassRequestData & Data, int & RequestResult)
  3069. {
  3070. if (!FActive)
  3071. {
  3072. return false;
  3073. }
  3074. else
  3075. {
  3076. UnicodeString Password = L"";
  3077. if (FTerminal->PromptUser(FTerminal->SessionData, pkPassword, LoadStr(PASSWORD_TITLE), L"",
  3078. LoadStr(PASSWORD_PROMPT), false, 0, Password))
  3079. {
  3080. Data.Password = _wcsdup(Password.c_str());
  3081. RequestResult = TFileZillaIntf::REPLY_OK;
  3082. }
  3083. else
  3084. {
  3085. RequestResult = TFileZillaIntf::REPLY_ABORTED;
  3086. }
  3087. return true;
  3088. }
  3089. }
  3090. //---------------------------------------------------------------------------
  3091. void __fastcall TFTPFileSystem::RemoteFileTimeToDateTimeAndPrecision(const TRemoteFileTime & Source, TDateTime & DateTime, TModificationFmt & ModificationFmt)
  3092. {
  3093. // ModificationFmt must be set after Modification
  3094. if (Source.HasDate)
  3095. {
  3096. DateTime =
  3097. EncodeDateVerbose((unsigned short)Source.Year, (unsigned short)Source.Month,
  3098. (unsigned short)Source.Day);
  3099. if (Source.HasTime)
  3100. {
  3101. DateTime = DateTime +
  3102. EncodeTimeVerbose((unsigned short)Source.Hour, (unsigned short)Source.Minute,
  3103. (unsigned short)Source.Second, 0);
  3104. // not exact as we got year as well, but it is most probably
  3105. // guessed by FZAPI anyway
  3106. ModificationFmt = Source.HasSeconds ? mfFull : mfMDHM;
  3107. }
  3108. else
  3109. {
  3110. ModificationFmt = mfMDY;
  3111. }
  3112. if (Source.Utc)
  3113. {
  3114. DateTime = ConvertTimestampFromUTC(DateTime);
  3115. }
  3116. }
  3117. else
  3118. {
  3119. // With SCP we estimate date to be today, if we have at least time
  3120. DateTime = double(0);
  3121. ModificationFmt = mfNone;
  3122. }
  3123. }
  3124. //---------------------------------------------------------------------------
  3125. bool __fastcall TFTPFileSystem::HandleListData(const wchar_t * Path,
  3126. const TListDataEntry * Entries, unsigned int Count)
  3127. {
  3128. if (!FActive)
  3129. {
  3130. return false;
  3131. }
  3132. else if (FIgnoreFileList)
  3133. {
  3134. // directory listing provided implicitly by FZAPI during certain operations is ignored
  3135. assert(FFileList == NULL);
  3136. return false;
  3137. }
  3138. else
  3139. {
  3140. assert(FFileList != NULL);
  3141. // this can actually fail in real life,
  3142. // when connected to server with case insensitive paths
  3143. assert(UnixComparePaths(AbsolutePath(FFileList->Directory, false), Path));
  3144. USEDPARAM(Path);
  3145. for (unsigned int Index = 0; Index < Count; Index++)
  3146. {
  3147. const TListDataEntry * Entry = &Entries[Index];
  3148. TRemoteFile * File = new TRemoteFile();
  3149. try
  3150. {
  3151. File->Terminal = FTerminal;
  3152. File->FileName = Entry->Name;
  3153. try
  3154. {
  3155. int PermissionsLen = wcslen(Entry->Permissions);
  3156. if (PermissionsLen >= 10)
  3157. {
  3158. File->Rights->Text = Entry->Permissions + 1;
  3159. }
  3160. else if ((PermissionsLen == 3) || (PermissionsLen == 4))
  3161. {
  3162. File->Rights->Octal = Entry->Permissions;
  3163. }
  3164. }
  3165. catch(...)
  3166. {
  3167. // ignore permissions errors with FTP
  3168. }
  3169. const wchar_t * Space = wcschr(Entry->OwnerGroup, L' ');
  3170. if (Space != NULL)
  3171. {
  3172. File->Owner.Name = UnicodeString(Entry->OwnerGroup, Space - Entry->OwnerGroup);
  3173. File->Group.Name = Space + 1;
  3174. }
  3175. else
  3176. {
  3177. File->Owner.Name = Entry->OwnerGroup;
  3178. }
  3179. File->Size = Entry->Size;
  3180. if (Entry->Link)
  3181. {
  3182. File->Type = FILETYPE_SYMLINK;
  3183. }
  3184. else if (Entry->Dir)
  3185. {
  3186. File->Type = FILETYPE_DIRECTORY;
  3187. }
  3188. else
  3189. {
  3190. File->Type = L'-';
  3191. }
  3192. TDateTime Modification;
  3193. TModificationFmt ModificationFmt;
  3194. RemoteFileTimeToDateTimeAndPrecision(Entry->Time, Modification, ModificationFmt);
  3195. File->Modification = Modification;
  3196. File->ModificationFmt = ModificationFmt;
  3197. File->LastAccess = File->Modification;
  3198. File->LinkTo = Entry->LinkTarget;
  3199. File->Complete();
  3200. }
  3201. catch (Exception & E)
  3202. {
  3203. delete File;
  3204. UnicodeString EntryData =
  3205. FORMAT(L"%s/%s/%s/%s/%d/%d/%d/%d/%d/%d/%d/%d/%d/%d",
  3206. (Entry->Name, Entry->Permissions, Entry->OwnerGroup, IntToStr(Entry->Size),
  3207. int(Entry->Dir), int(Entry->Link), Entry->Time.Year, Entry->Time.Month, Entry->Time.Day,
  3208. Entry->Time.Hour, Entry->Time.Minute, int(Entry->Time.HasTime),
  3209. int(Entry->Time.HasSeconds), int(Entry->Time.HasDate)));
  3210. throw ETerminal(&E, FMTLOAD(LIST_LINE_ERROR, (EntryData)), HELP_LIST_LINE_ERROR);
  3211. }
  3212. FFileList->AddFile(File);
  3213. }
  3214. return true;
  3215. }
  3216. }
  3217. //---------------------------------------------------------------------------
  3218. bool __fastcall TFTPFileSystem::HandleTransferStatus(bool Valid, __int64 TransferSize,
  3219. __int64 Bytes, int /*Percent*/, int /*TimeElapsed*/, int /*TimeLeft*/, int /*TransferRate*/,
  3220. bool FileTransfer)
  3221. {
  3222. if (!FActive)
  3223. {
  3224. return false;
  3225. }
  3226. else if (!Valid)
  3227. {
  3228. }
  3229. else if (FileTransfer)
  3230. {
  3231. FileTransferProgress(TransferSize, Bytes);
  3232. }
  3233. else
  3234. {
  3235. ReadDirectoryProgress(Bytes);
  3236. }
  3237. return true;
  3238. }
  3239. //---------------------------------------------------------------------------
  3240. bool __fastcall TFTPFileSystem::HandleReply(int Command, unsigned int Reply)
  3241. {
  3242. if (!FActive)
  3243. {
  3244. return false;
  3245. }
  3246. else
  3247. {
  3248. if (FTerminal->Configuration->ActualLogProtocol >= 1)
  3249. {
  3250. FTerminal->LogEvent(FORMAT(L"Got reply %x to the command %d", (int(Reply), Command)));
  3251. }
  3252. // reply with Command 0 is not associated with current operation
  3253. // so do not treat is as a reply
  3254. // (it is typically used asynchronously to notify about disconnects)
  3255. if (Command != 0)
  3256. {
  3257. assert(FCommandReply == 0);
  3258. FCommandReply = Reply;
  3259. }
  3260. else
  3261. {
  3262. assert(FReply == 0);
  3263. FReply = Reply;
  3264. }
  3265. return true;
  3266. }
  3267. }
  3268. //---------------------------------------------------------------------------
  3269. bool __fastcall TFTPFileSystem::HandleCapabilities(
  3270. TFTPServerCapabilities * ServerCapabilities)
  3271. {
  3272. FServerCapabilities->Assign(ServerCapabilities);
  3273. FFileSystemInfoValid = false;
  3274. return true;
  3275. }
  3276. //---------------------------------------------------------------------------
  3277. bool __fastcall TFTPFileSystem::CheckError(int ReturnCode, const wchar_t * Context)
  3278. {
  3279. // we do not expect any FZAPI call to fail as it generally can fail only due to:
  3280. // - invalid parameters
  3281. // - busy FZAPI core
  3282. // the only exception is REPLY_NOTCONNECTED that can happen if
  3283. // connection is closed just between the last call to Idle()
  3284. // and call to any FZAPI command
  3285. // in such case reply without associated command is posted,
  3286. // which we are going to wait for unless we are already waiting
  3287. // on higher level (this typically happens if connection is lost while
  3288. // waiting for user interaction and is detected within call to
  3289. // SetAsyncRequestResult)
  3290. if (FLAGSET(ReturnCode, TFileZillaIntf::REPLY_NOTCONNECTED))
  3291. {
  3292. if (!FWaitingForReply)
  3293. {
  3294. // throws
  3295. WaitForFatalNonCommandReply();
  3296. }
  3297. }
  3298. else
  3299. {
  3300. FTerminal->FatalError(NULL,
  3301. FMTLOAD(INTERNAL_ERROR, (FORMAT(L"fz#%s", (Context)), IntToHex(ReturnCode, 4))));
  3302. assert(false);
  3303. }
  3304. return false;
  3305. }
  3306. //---------------------------------------------------------------------------
  3307. bool __fastcall TFTPFileSystem::Unquote(UnicodeString & Str)
  3308. {
  3309. enum
  3310. {
  3311. INIT,
  3312. QUOTE,
  3313. QUOTED,
  3314. DONE
  3315. } State;
  3316. State = INIT;
  3317. assert((Str.Length() > 0) && ((Str[1] == L'"') || (Str[1] == L'\'')));
  3318. int Index = 1;
  3319. wchar_t Quote;
  3320. while (Index <= Str.Length())
  3321. {
  3322. switch (State)
  3323. {
  3324. case INIT:
  3325. if ((Str[Index] == L'"') || (Str[Index] == L'\''))
  3326. {
  3327. Quote = Str[Index];
  3328. State = QUOTED;
  3329. Str.Delete(Index, 1);
  3330. }
  3331. else
  3332. {
  3333. assert(false);
  3334. // no quoted string
  3335. Str.SetLength(0);
  3336. }
  3337. break;
  3338. case QUOTED:
  3339. if (Str[Index] == Quote)
  3340. {
  3341. State = QUOTE;
  3342. Str.Delete(Index, 1);
  3343. }
  3344. else
  3345. {
  3346. Index++;
  3347. }
  3348. break;
  3349. case QUOTE:
  3350. if (Str[Index] == Quote)
  3351. {
  3352. Index++;
  3353. }
  3354. else
  3355. {
  3356. // end of quoted string, trim the rest
  3357. Str.SetLength(Index - 1);
  3358. State = DONE;
  3359. }
  3360. break;
  3361. }
  3362. }
  3363. return (State == DONE);
  3364. }
  3365. //---------------------------------------------------------------------------
  3366. void __fastcall TFTPFileSystem::PreserveDownloadFileTime(HANDLE Handle, void * UserData)
  3367. {
  3368. TFileTransferData * Data = static_cast<TFileTransferData *>(UserData);
  3369. FILETIME WrTime = DateTimeToFileTime(Data->Modification, dstmUnix);
  3370. SetFileTime(Handle, NULL, NULL, &WrTime);
  3371. }
  3372. //---------------------------------------------------------------------------
  3373. bool __fastcall TFTPFileSystem::GetFileModificationTimeInUtc(const wchar_t * FileName, struct tm & Time)
  3374. {
  3375. bool Result;
  3376. try
  3377. {
  3378. // error-handling-free and DST-mode-inaware copy of TTerminal::OpenLocalFile
  3379. HANDLE Handle = CreateFile(FileName, GENERIC_READ,
  3380. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
  3381. if (Handle == INVALID_HANDLE_VALUE)
  3382. {
  3383. Result = false;
  3384. }
  3385. else
  3386. {
  3387. FILETIME MTime;
  3388. if (!GetFileTime(Handle, NULL, NULL, &MTime))
  3389. {
  3390. Result = false;
  3391. }
  3392. else
  3393. {
  3394. TDateTime Modification = ConvertTimestampToUTC(FileTimeToDateTime(MTime));
  3395. unsigned short Year;
  3396. unsigned short Month;
  3397. unsigned short Day;
  3398. Modification.DecodeDate(&Year, &Month, &Day);
  3399. Time.tm_year = Year - 1900;
  3400. Time.tm_mon = Month - 1;
  3401. Time.tm_mday = Day;
  3402. unsigned short Hour;
  3403. unsigned short Min;
  3404. unsigned short Sec;
  3405. unsigned short MSec;
  3406. Modification.DecodeTime(&Hour, &Min, &Sec, &MSec);
  3407. Time.tm_hour = Hour;
  3408. Time.tm_min = Min;
  3409. Time.tm_sec = Sec;
  3410. Result = true;
  3411. }
  3412. CloseHandle(Handle);
  3413. }
  3414. }
  3415. catch (...)
  3416. {
  3417. Result = false;
  3418. }
  3419. return Result;
  3420. }
  3421. //---------------------------------------------------------------------------
  3422. #endif NO_FILEZILLA