Terminal.cpp 94 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include "Terminal.h"
  5. #include <SysUtils.hpp>
  6. #include <FileCtrl.hpp>
  7. #include "Common.h"
  8. #include "FileBuffer.h"
  9. #include "Interface.h"
  10. #include "RemoteFiles.h"
  11. #include "ScpFileSystem.h"
  12. #include "SftpFileSystem.h"
  13. #include "TextsCore.h"
  14. #include "HelpCore.h"
  15. //---------------------------------------------------------------------------
  16. #pragma package(smart_init)
  17. //---------------------------------------------------------------------------
  18. #define COMMAND_ERROR_ARI(MESSAGE, REPEAT) \
  19. { \
  20. int Result = CommandError(&E, MESSAGE, qaRetry | qaSkip | qaAbort); \
  21. switch (Result) { \
  22. case qaRetry: { REPEAT; } break; \
  23. case qaAbort: Abort(); \
  24. } \
  25. }
  26. #define FILE_OPERATION_LOOP_EX(ALLOW_SKIP, MESSAGE, OPERATION) \
  27. FILE_OPERATION_LOOP_CUSTOM(this, ALLOW_SKIP, MESSAGE, OPERATION)
  28. //---------------------------------------------------------------------------
  29. struct TMoveFileParams
  30. {
  31. AnsiString Target;
  32. AnsiString FileMask;
  33. };
  34. //---------------------------------------------------------------------------
  35. TSynchronizeStats::TSynchronizeStats()
  36. {
  37. memset(this, 0, sizeof(*this));
  38. }
  39. //---------------------------------------------------------------------------
  40. TCalculateSizeStats::TCalculateSizeStats()
  41. {
  42. memset(this, 0, sizeof(*this));
  43. }
  44. //---------------------------------------------------------------------------
  45. TSynchronizeOptions::TSynchronizeOptions()
  46. {
  47. memset(this, 0, sizeof(*this));
  48. }
  49. //---------------------------------------------------------------------------
  50. TSynchronizeOptions::~TSynchronizeOptions()
  51. {
  52. delete Filter;
  53. }
  54. //---------------------------------------------------------------------------
  55. __fastcall TTerminal::TTerminal(): TSecureShell()
  56. {
  57. FFiles = new TRemoteDirectory(this);
  58. FExceptionOnFail = 0;
  59. FInTransaction = 0;
  60. FReadCurrentDirectoryPending = false;
  61. FReadDirectoryPending = false;
  62. FUsersGroupsLookedup = False;
  63. FGroups = new TUsersGroupsList();
  64. FUsers = new TUsersGroupsList();
  65. FOnProgress = NULL;
  66. FOnFinished = NULL;
  67. FOnDeleteLocalFile = NULL;
  68. FOnReadDirectoryProgress = NULL;
  69. FAdditionalInfo = NULL;
  70. FUseBusyCursor = True;
  71. FLockDirectory = "";
  72. FDirectoryCache = new TRemoteDirectoryCache();
  73. FDirectoryChangesCache = NULL;
  74. FFSProtocol = cfsUnknown;
  75. FCommandSession = NULL;
  76. FAutoReadDirectory = true;
  77. FReadingCurrentDirectory = false;
  78. }
  79. //---------------------------------------------------------------------------
  80. __fastcall TTerminal::~TTerminal()
  81. {
  82. SAFE_DESTROY(FCommandSession);
  83. if (SessionData->CacheDirectoryChanges && SessionData->PreserveDirectoryChanges &&
  84. (FDirectoryChangesCache != NULL))
  85. {
  86. Configuration->SaveDirectoryChangesCache(SessionData->SessionKey,
  87. FDirectoryChangesCache);
  88. }
  89. SAFE_DESTROY(FFileSystem);
  90. delete FFiles;
  91. delete FGroups;
  92. delete FUsers;
  93. delete FDirectoryCache;
  94. delete FDirectoryChangesCache;
  95. delete FAdditionalInfo;
  96. }
  97. //---------------------------------------------------------------------------
  98. void __fastcall TTerminal::Idle()
  99. {
  100. TSecureShell::Idle();
  101. if (CommandSessionOpened)
  102. {
  103. try
  104. {
  105. FCommandSession->Idle();
  106. }
  107. catch(Exception & E)
  108. {
  109. // If the secondary session is dropped, ignore the error and let
  110. // it be reconnected when needed.
  111. // BTW, non-fatal error can hardly happen here, that's why
  112. // it is displayed, because it can be useful to know.
  113. if (FCommandSession->Active)
  114. {
  115. FCommandSession->DoShowExtendedException(&E);
  116. }
  117. }
  118. }
  119. }
  120. //---------------------------------------------------------------------------
  121. void __fastcall TTerminal::KeepAlive()
  122. {
  123. if (SessionData->PingType == ptDummyCommand)
  124. {
  125. LogEvent("Executing dummy command to keep session alive.");
  126. assert(Active);
  127. assert(FFileSystem != NULL);
  128. try
  129. {
  130. FFileSystem->KeepAlive();
  131. }
  132. catch(Exception & E)
  133. {
  134. if (Active)
  135. {
  136. DoHandleExtendedException(&E);
  137. }
  138. else
  139. {
  140. throw;
  141. }
  142. }
  143. }
  144. else
  145. {
  146. TSecureShell::KeepAlive();
  147. }
  148. }
  149. //---------------------------------------------------------------------------
  150. bool __fastcall TTerminal::IsAbsolutePath(const AnsiString Path)
  151. {
  152. return !Path.IsEmpty() && Path[1] == '/';
  153. }
  154. //---------------------------------------------------------------------------
  155. AnsiString __fastcall TTerminal::ExpandFileName(AnsiString Path,
  156. const AnsiString BasePath)
  157. {
  158. Path = UnixExcludeTrailingBackslash(Path);
  159. if (!IsAbsolutePath(Path) && !BasePath.IsEmpty())
  160. {
  161. // TODO: Handle more complicated cases like "../../xxx"
  162. if (Path == "..")
  163. {
  164. Path = UnixExcludeTrailingBackslash(UnixExtractFilePath(
  165. UnixExcludeTrailingBackslash(BasePath)));
  166. }
  167. else
  168. {
  169. Path = UnixIncludeTrailingBackslash(BasePath) + Path;
  170. }
  171. }
  172. return Path;
  173. }
  174. //---------------------------------------------------------------------------
  175. AnsiString __fastcall TTerminal::GetProtocolName()
  176. {
  177. assert(FFileSystem);
  178. return FFileSystem->ProtocolName;
  179. }
  180. //---------------------------------------------------------------------------
  181. void __fastcall TTerminal::Close()
  182. {
  183. // file system cannot be destoryed here, moved to destructor
  184. TSecureShell::Close();
  185. if (CommandSessionOpened)
  186. {
  187. FCommandSession->Close();
  188. }
  189. }
  190. //---------------------------------------------------------------------------
  191. void __fastcall TTerminal::Open()
  192. {
  193. TSecureShell::Open();
  194. assert(!FFileSystem);
  195. if ((SessionData->FSProtocol == fsSCPonly) ||
  196. (SessionData->FSProtocol == fsSFTP && SshFallbackCmd()))
  197. {
  198. FFSProtocol = cfsSCP;
  199. FFileSystem = new TSCPFileSystem(this);
  200. LogEvent("Using SCP protocol.");
  201. }
  202. else
  203. {
  204. FFSProtocol = cfsSFTP;
  205. FFileSystem = new TSFTPFileSystem(this);
  206. LogEvent("Using SFTP protocol.");
  207. }
  208. if (SessionData->CacheDirectoryChanges)
  209. {
  210. FDirectoryChangesCache = new TRemoteDirectoryChangesCache();
  211. if (SessionData->PreserveDirectoryChanges)
  212. {
  213. Configuration->LoadDirectoryChangesCache(SessionData->SessionKey,
  214. FDirectoryChangesCache);
  215. }
  216. }
  217. }
  218. //---------------------------------------------------------------------------
  219. bool __fastcall TTerminal::GetIsCapable(TFSCapability Capability) const
  220. {
  221. assert(FFileSystem);
  222. return FFileSystem->IsCapable(Capability);
  223. }
  224. //---------------------------------------------------------------------------
  225. TStrings * __fastcall TTerminal::GetAdditionalInfo()
  226. {
  227. bool Initial = (FAdditionalInfo == NULL);
  228. if (Initial)
  229. {
  230. FAdditionalInfo = new TStringList();
  231. }
  232. assert(FFileSystem);
  233. FFileSystem->AdditionalInfo(FAdditionalInfo, Initial);
  234. return FAdditionalInfo;
  235. }
  236. //---------------------------------------------------------------------------
  237. AnsiString __fastcall TTerminal::AbsolutePath(AnsiString Path)
  238. {
  239. return FFileSystem->AbsolutePath(Path);
  240. }
  241. //---------------------------------------------------------------------------
  242. void __fastcall TTerminal::ReactOnCommand(int /*TFSCommand*/ Cmd)
  243. {
  244. bool ChangesDirectory = false;
  245. bool ModifiesFiles = false;
  246. switch ((TFSCommand)Cmd) {
  247. case fsChangeDirectory:
  248. case fsHomeDirectory:
  249. ChangesDirectory = true;
  250. break;
  251. case fsCopyToRemote:
  252. case fsDeleteFile:
  253. case fsRenameFile:
  254. case fsMoveFile:
  255. case fsCopyFile:
  256. case fsCreateDirectory:
  257. case fsChangeMode:
  258. case fsChangeGroup:
  259. case fsChangeOwner:
  260. case fsChangeProperties:
  261. ModifiesFiles = true;
  262. break;
  263. case fsAnyCommand:
  264. ChangesDirectory = true;
  265. ModifiesFiles = true;
  266. break;
  267. }
  268. if (ChangesDirectory)
  269. {
  270. if (!FInTransaction)
  271. {
  272. ReadCurrentDirectory();
  273. if (AutoReadDirectory)
  274. {
  275. ReadDirectory(false);
  276. }
  277. }
  278. else
  279. {
  280. FReadCurrentDirectoryPending = true;
  281. if (AutoReadDirectory)
  282. {
  283. FReadDirectoryPending = true;
  284. }
  285. }
  286. }
  287. else
  288. if (ModifiesFiles && AutoReadDirectory)
  289. {
  290. if (!FInTransaction) ReadDirectory(true);
  291. else FReadDirectoryPending = true;
  292. }
  293. }
  294. //---------------------------------------------------------------------------
  295. void __fastcall TTerminal::TerminalError(AnsiString Msg)
  296. {
  297. TerminalError(NULL, Msg);
  298. }
  299. //---------------------------------------------------------------------------
  300. void __fastcall TTerminal::TerminalError(Exception * E, AnsiString Msg)
  301. {
  302. throw ETerminal(E, Msg);
  303. }
  304. //---------------------------------------------------------------------------
  305. bool __fastcall TTerminal::FileOperationLoopQuery(Exception & E,
  306. TFileOperationProgressType * OperationProgress, const AnsiString Message,
  307. bool AllowSkip, AnsiString SpecialRetry)
  308. {
  309. bool Result = false;
  310. DoHandleExtendedException(&E);
  311. int Answer;
  312. if (AllowSkip && OperationProgress->SkipToAll)
  313. {
  314. Answer = qaSkip;
  315. }
  316. else
  317. {
  318. int Answers = qaRetry | qaAbort |
  319. FLAGMASK(AllowSkip, (qaSkip | qaAll)) |
  320. FLAGMASK(!SpecialRetry.IsEmpty(), qaYes);
  321. TQueryParams Params(qpAllowContinueOnError | FLAGMASK(!AllowSkip, qpFatalAbort));
  322. TQueryButtonAlias Aliases[2];
  323. int AliasCount = 0;
  324. if (FLAGSET(Answers, qaAll))
  325. {
  326. Aliases[AliasCount].Button = qaAll;
  327. Aliases[AliasCount].Alias = LoadStr(SKIP_ALL_BUTTON);
  328. AliasCount++;
  329. }
  330. if (FLAGSET(Answers, qaYes))
  331. {
  332. Aliases[AliasCount].Button = qaYes;
  333. Aliases[AliasCount].Alias = SpecialRetry;
  334. AliasCount++;
  335. }
  336. if (AliasCount > 0)
  337. {
  338. Params.Aliases = Aliases;
  339. Params.AliasesCount = AliasCount;
  340. }
  341. SUSPEND_OPERATION (
  342. Answer = DoQueryUser(Message, &E, Answers, &Params, qtError);
  343. );
  344. if (Answer == qaAll)
  345. {
  346. OperationProgress->SkipToAll = true;
  347. Answer = qaSkip;
  348. }
  349. if (Answer == qaYes)
  350. {
  351. Result = true;
  352. Answer = qaRetry;
  353. }
  354. }
  355. if (Answer != qaRetry)
  356. {
  357. if (Answer == qaAbort)
  358. {
  359. OperationProgress->Cancel = csCancel;
  360. }
  361. if (AllowSkip)
  362. {
  363. THROW_SKIP_FILE(&E, Message);
  364. }
  365. else
  366. {
  367. // this can happen only during file transfer with SCP
  368. throw ExtException(&E, Message);
  369. }
  370. }
  371. return Result;
  372. }
  373. //---------------------------------------------------------------------------
  374. int __fastcall TTerminal::FileOperationLoop(TFileOperationEvent CallBackFunc,
  375. TFileOperationProgressType * OperationProgress, bool AllowSkip,
  376. const AnsiString Message, void * Param1, void * Param2)
  377. {
  378. assert(CallBackFunc);
  379. int Result;
  380. FILE_OPERATION_LOOP_EX
  381. (
  382. AllowSkip, Message,
  383. Result = CallBackFunc(Param1, Param2);
  384. );
  385. return Result;
  386. }
  387. //---------------------------------------------------------------------------
  388. AnsiString __fastcall TTerminal::TranslateLockedPath(AnsiString Path, bool Lock)
  389. {
  390. if (!SessionData->LockInHome || Path.IsEmpty() || (Path[1] != '/'))
  391. return Path;
  392. if (Lock)
  393. {
  394. if (Path.SubString(1, FLockDirectory.Length()) == FLockDirectory)
  395. {
  396. Path.Delete(1, FLockDirectory.Length());
  397. if (Path.IsEmpty()) Path = "/";
  398. }
  399. }
  400. else
  401. {
  402. Path = UnixExcludeTrailingBackslash(FLockDirectory + Path);
  403. }
  404. return Path;
  405. }
  406. //---------------------------------------------------------------------------
  407. void __fastcall TTerminal::ClearCaches()
  408. {
  409. FDirectoryCache->Clear();
  410. if (FDirectoryChangesCache != NULL)
  411. {
  412. FDirectoryChangesCache->Clear();
  413. }
  414. }
  415. //---------------------------------------------------------------------------
  416. void __fastcall TTerminal::ClearCachedFileList(const AnsiString Path,
  417. bool SubDirs)
  418. {
  419. FDirectoryCache->ClearFileList(Path, SubDirs);
  420. }
  421. //---------------------------------------------------------------------------
  422. void __fastcall TTerminal::AddCachedFileList(TRemoteFileList * FileList)
  423. {
  424. FDirectoryCache->AddFileList(FileList);
  425. }
  426. //---------------------------------------------------------------------------
  427. bool __fastcall TTerminal::DirectoryFileList(const AnsiString Path,
  428. TRemoteFileList *& FileList, bool CanLoad)
  429. {
  430. bool Result = false;
  431. if (UnixComparePaths(FFiles->Directory, Path))
  432. {
  433. Result = (FileList == NULL) || (FileList->Timestamp < FFiles->Timestamp);
  434. if (Result)
  435. {
  436. if (FileList == NULL)
  437. {
  438. FileList = new TRemoteFileList();
  439. }
  440. FFiles->DuplicateTo(FileList);
  441. }
  442. }
  443. else
  444. {
  445. if (((FileList == NULL) && FDirectoryCache->HasFileList(Path)) ||
  446. ((FileList != NULL) && FDirectoryCache->HasNewerFileList(Path, FileList->Timestamp)))
  447. {
  448. bool Created = (FileList == NULL);
  449. if (Created)
  450. {
  451. FileList = new TRemoteFileList();
  452. }
  453. Result = FDirectoryCache->GetFileList(Path, FileList);
  454. if (!Result && Created)
  455. {
  456. SAFE_DESTROY(FileList);
  457. }
  458. }
  459. // do not attempt to load file list if there is cached version,
  460. // only absence of cached version indicates that we consider
  461. // the directory content obsolete
  462. else if (CanLoad && !FDirectoryCache->HasFileList(Path))
  463. {
  464. bool Created = (FileList == NULL);
  465. if (Created)
  466. {
  467. FileList = new TRemoteFileList();
  468. }
  469. FileList->Directory = Path;
  470. try
  471. {
  472. ReadDirectory(FileList);
  473. Result = true;
  474. }
  475. catch(...)
  476. {
  477. if (Created)
  478. {
  479. SAFE_DESTROY(FileList);
  480. }
  481. throw;
  482. }
  483. }
  484. }
  485. return Result;
  486. }
  487. //---------------------------------------------------------------------------
  488. void __fastcall TTerminal::SetCurrentDirectory(AnsiString value)
  489. {
  490. assert(FFileSystem);
  491. value = TranslateLockedPath(value, false);
  492. if (value != FFileSystem->CurrentDirectory)
  493. {
  494. ChangeDirectory(value);
  495. }
  496. }
  497. //---------------------------------------------------------------------------
  498. AnsiString __fastcall TTerminal::GetCurrentDirectory()
  499. {
  500. if (FFileSystem)
  501. {
  502. FCurrentDirectory = FFileSystem->CurrentDirectory;
  503. if (FCurrentDirectory.IsEmpty())
  504. {
  505. ReadCurrentDirectory();
  506. }
  507. }
  508. return TranslateLockedPath(FCurrentDirectory, true);
  509. }
  510. //---------------------------------------------------------------------------
  511. AnsiString __fastcall TTerminal::PeekCurrentDirectory()
  512. {
  513. if (FFileSystem)
  514. {
  515. FCurrentDirectory = FFileSystem->CurrentDirectory;
  516. }
  517. return TranslateLockedPath(FCurrentDirectory, true);
  518. }
  519. //---------------------------------------------------------------------------
  520. TUsersGroupsList * __fastcall TTerminal::GetGroups()
  521. {
  522. assert(FFileSystem);
  523. if (!FUsersGroupsLookedup && SessionData->LookupUserGroups &&
  524. IsCapable[fcUserGroupListing])
  525. {
  526. LookupUsersGroups();
  527. }
  528. return FGroups;
  529. }
  530. //---------------------------------------------------------------------------
  531. TUsersGroupsList * __fastcall TTerminal::GetUsers()
  532. {
  533. assert(FFileSystem);
  534. if (!FUsersGroupsLookedup && SessionData->LookupUserGroups &&
  535. IsCapable[fcUserGroupListing])
  536. {
  537. LookupUsersGroups();
  538. }
  539. return FUsers;
  540. }
  541. //---------------------------------------------------------------------------
  542. AnsiString __fastcall TTerminal::GetUserName() const
  543. {
  544. // in future might be implemented to detect username similar to GetUserGroups
  545. return SessionData->UserName;
  546. }
  547. //---------------------------------------------------------------------------
  548. bool __fastcall TTerminal::GetAreCachesEmpty() const
  549. {
  550. return FDirectoryCache->IsEmpty &&
  551. ((FDirectoryChangesCache == NULL) || FDirectoryChangesCache->IsEmpty);
  552. }
  553. //---------------------------------------------------------------------------
  554. void __fastcall TTerminal::DoChangeDirectory()
  555. {
  556. if (FOnChangeDirectory)
  557. {
  558. FOnChangeDirectory(this);
  559. }
  560. }
  561. //---------------------------------------------------------------------------
  562. void __fastcall TTerminal::DoReadDirectory(bool ReloadOnly)
  563. {
  564. if (FOnReadDirectory)
  565. {
  566. FOnReadDirectory(this, ReloadOnly);
  567. }
  568. }
  569. //---------------------------------------------------------------------------
  570. void __fastcall TTerminal::DoStartReadDirectory()
  571. {
  572. if (FOnStartReadDirectory)
  573. {
  574. FOnStartReadDirectory(this);
  575. }
  576. }
  577. //---------------------------------------------------------------------------
  578. void __fastcall TTerminal::DoReadDirectoryProgress(int Progress)
  579. {
  580. if (FReadingCurrentDirectory && (FOnReadDirectoryProgress != NULL))
  581. {
  582. FOnReadDirectoryProgress(this, Progress);
  583. }
  584. }
  585. //---------------------------------------------------------------------------
  586. void __fastcall TTerminal::BeginTransaction()
  587. {
  588. if (!FInTransaction)
  589. {
  590. FReadCurrentDirectoryPending = false;
  591. FReadDirectoryPending = false;
  592. }
  593. FInTransaction++;
  594. if (FCommandSession != NULL)
  595. {
  596. FCommandSession->BeginTransaction();
  597. }
  598. }
  599. //---------------------------------------------------------------------------
  600. void __fastcall TTerminal::EndTransaction()
  601. {
  602. // it connection was closed due to fatal error during transaction, do nothing
  603. if (Active)
  604. {
  605. if (!FInTransaction)
  606. TerminalError("Can't end transaction, not in transaction");
  607. assert(FInTransaction > 0);
  608. FInTransaction--;
  609. if (FInTransaction == 0)
  610. {
  611. try
  612. {
  613. if (FReadCurrentDirectoryPending) ReadCurrentDirectory();
  614. if (FReadDirectoryPending) ReadDirectory(!FReadCurrentDirectoryPending);
  615. }
  616. __finally
  617. {
  618. FReadCurrentDirectoryPending = false;
  619. FReadDirectoryPending = false;
  620. }
  621. }
  622. }
  623. if (FCommandSession != NULL)
  624. {
  625. FCommandSession->EndTransaction();
  626. }
  627. }
  628. //---------------------------------------------------------------------------
  629. void __fastcall TTerminal::SetExceptionOnFail(bool value)
  630. {
  631. if (value) FExceptionOnFail++;
  632. else
  633. {
  634. if (FExceptionOnFail == 0)
  635. throw Exception("ExceptionOnFail is already zero.");
  636. FExceptionOnFail--;
  637. }
  638. if (FCommandSession != NULL)
  639. {
  640. FCommandSession->FExceptionOnFail = FExceptionOnFail;
  641. }
  642. }
  643. //---------------------------------------------------------------------------
  644. bool __fastcall TTerminal::GetExceptionOnFail() const
  645. {
  646. return (bool)(FExceptionOnFail > 0);
  647. }
  648. //---------------------------------------------------------------------------
  649. void __fastcall TTerminal::CommandError(Exception * E, const AnsiString Msg)
  650. {
  651. CommandError(E, Msg, 0);
  652. }
  653. //---------------------------------------------------------------------------
  654. int __fastcall TTerminal::CommandError(Exception * E, const AnsiString Msg,
  655. int Answers)
  656. {
  657. int Result = 0;
  658. if (E && E->InheritsFrom(__classid(EFatal)))
  659. {
  660. FatalError(E, Msg);
  661. }
  662. else if (E && E->InheritsFrom(__classid(EAbort)))
  663. {
  664. // resent EAbort exception
  665. Abort();
  666. }
  667. else if (ExceptionOnFail)
  668. {
  669. throw ECommand(E, Msg);
  670. }
  671. else if (!Answers)
  672. {
  673. ECommand * ECmd = new ECommand(E, Msg);
  674. try
  675. {
  676. DoShowExtendedException(ECmd);
  677. }
  678. __finally
  679. {
  680. delete ECmd;
  681. }
  682. }
  683. else
  684. {
  685. // small hack to anable "skip to all" for COMMAND_ERROR_ARI
  686. bool CanSkip = FLAGSET(Answers, qaSkip) && (OperationProgress != NULL);
  687. if (CanSkip && OperationProgress->SkipToAll)
  688. {
  689. Result = qaSkip;
  690. }
  691. else
  692. {
  693. TQueryParams Params(qpAllowContinueOnError);
  694. TQueryButtonAlias Aliases[1];
  695. if (CanSkip)
  696. {
  697. Aliases[0].Button = qaAll;
  698. Aliases[0].Alias = LoadStr(SKIP_ALL_BUTTON);
  699. Params.Aliases = Aliases;
  700. Params.AliasesCount = LENOF(Aliases);
  701. Answers |= qaAll;
  702. }
  703. Result = DoQueryUser(Msg, E, Answers, &Params, qtError);
  704. if (Result == qaAll)
  705. {
  706. assert(OperationProgress != NULL);
  707. OperationProgress->SkipToAll = true;
  708. Result = qaSkip;
  709. }
  710. }
  711. }
  712. return Result;
  713. }
  714. //---------------------------------------------------------------------------
  715. bool __fastcall TTerminal::HandleException(Exception * E)
  716. {
  717. if (ExceptionOnFail)
  718. {
  719. return false;
  720. }
  721. else
  722. {
  723. DoHandleExtendedException(E);
  724. return true;
  725. }
  726. }
  727. //---------------------------------------------------------------------------
  728. void __fastcall TTerminal::CloseOnCompletion(const AnsiString Message)
  729. {
  730. LogEvent("Closing session after completed operation (as requested by user)");
  731. Close();
  732. throw ESshTerminate(NULL,
  733. Message.IsEmpty() ? LoadStr(CLOSED_ON_COMPLETION) : Message);
  734. }
  735. //---------------------------------------------------------------------------
  736. int __fastcall TTerminal::ConfirmFileOverwrite(const AnsiString FileName,
  737. const TOverwriteFileParams * FileParams, int Answers, const TQueryParams * Params,
  738. TOperationSide Side, TFileOperationProgressType * OperationProgress)
  739. {
  740. int Answer;
  741. int AnswerForNewer =
  742. (CompareFileTime(FileParams->SourceTimestamp, FileParams->DestTimestamp) > 0) ?
  743. qaYes : qaNo;
  744. if (OperationProgress->YesToNewer)
  745. {
  746. Answer = AnswerForNewer;
  747. }
  748. else
  749. {
  750. AnsiString Message = FMTLOAD((Side == osLocal ? LOCAL_FILE_OVERWRITE :
  751. REMOTE_FILE_OVERWRITE), (FileName));
  752. if (FileParams)
  753. {
  754. Message = FMTLOAD(FILE_OVERWRITE_DETAILS, (Message,
  755. IntToStr(FileParams->SourceSize),
  756. FormatDateTime("ddddd tt", FileParams->SourceTimestamp),
  757. IntToStr(FileParams->DestSize),
  758. FormatDateTime("ddddd tt", FileParams->DestTimestamp)));
  759. }
  760. Answer = DoQueryUser(Message, Answers, Params);
  761. switch (Answer)
  762. {
  763. case qaNeverAskAgain:
  764. Configuration->ConfirmOverwriting = false;
  765. Answer = qaYes;
  766. break;
  767. case qaYesToAll:
  768. OperationProgress->YesToAll = true;
  769. Answer = qaYes;
  770. break;
  771. case qaAll:
  772. OperationProgress->YesToNewer = true;
  773. Answer = AnswerForNewer;
  774. break;
  775. case qaNoToAll:
  776. OperationProgress->NoToAll = true;
  777. Answer = qaNo;
  778. }
  779. }
  780. return Answer;
  781. }
  782. //---------------------------------------------------------------------------
  783. void __fastcall TTerminal::FileModified(const TRemoteFile * File,
  784. const AnsiString FileName, bool ClearDirectoryChange)
  785. {
  786. AnsiString ParentDirectory;
  787. AnsiString Directory;
  788. if (SessionData->CacheDirectories || SessionData->CacheDirectoryChanges)
  789. {
  790. if ((File != NULL) && (File->Directory != NULL))
  791. {
  792. if (File->IsDirectory)
  793. {
  794. Directory = File->Directory->FullDirectory + File->FileName;
  795. }
  796. ParentDirectory = File->Directory->Directory;
  797. }
  798. else if (!FileName.IsEmpty())
  799. {
  800. ParentDirectory = UnixExtractFilePath(FileName);
  801. if (ParentDirectory.IsEmpty())
  802. {
  803. ParentDirectory = CurrentDirectory;
  804. }
  805. // this case for scripting
  806. if ((File != NULL) && File->IsDirectory)
  807. {
  808. Directory = UnixIncludeTrailingBackslash(ParentDirectory) +
  809. UnixExtractFileName(File->FileName);
  810. }
  811. }
  812. }
  813. if (SessionData->CacheDirectories)
  814. {
  815. if (!Directory.IsEmpty())
  816. {
  817. DirectoryModified(Directory, true);
  818. }
  819. if (!ParentDirectory.IsEmpty())
  820. {
  821. DirectoryModified(ParentDirectory, false);
  822. }
  823. }
  824. if (SessionData->CacheDirectoryChanges && ClearDirectoryChange)
  825. {
  826. if (!Directory.IsEmpty())
  827. {
  828. FDirectoryChangesCache->ClearDirectoryChangeTarget(Directory);
  829. }
  830. }
  831. }
  832. //---------------------------------------------------------------------------
  833. void __fastcall TTerminal::DoDirectoryModified(const AnsiString Path, bool SubDirs)
  834. {
  835. if (OnDirectoryModified != NULL)
  836. {
  837. OnDirectoryModified(this, Path, SubDirs);
  838. }
  839. }
  840. //---------------------------------------------------------------------------
  841. void __fastcall TTerminal::DirectoryModified(const AnsiString Path, bool SubDirs)
  842. {
  843. if (Path.IsEmpty())
  844. {
  845. ClearCachedFileList(CurrentDirectory, SubDirs);
  846. }
  847. else
  848. {
  849. ClearCachedFileList(Path, SubDirs);
  850. }
  851. DoDirectoryModified(Path, SubDirs);
  852. }
  853. //---------------------------------------------------------------------------
  854. void __fastcall TTerminal::DirectoryLoaded(TRemoteFileList * FileList)
  855. {
  856. AddCachedFileList(FileList);
  857. }
  858. //---------------------------------------------------------------------------
  859. void __fastcall TTerminal::ReloadDirectory()
  860. {
  861. if (SessionData->CacheDirectories)
  862. {
  863. DirectoryModified(CurrentDirectory, false);
  864. }
  865. if (SessionData->CacheDirectoryChanges)
  866. {
  867. assert(FDirectoryChangesCache != NULL);
  868. FDirectoryChangesCache->ClearDirectoryChange(CurrentDirectory);
  869. }
  870. ReadCurrentDirectory();
  871. FReadCurrentDirectoryPending = false;
  872. ReadDirectory(true);
  873. FReadDirectoryPending = false;
  874. }
  875. //---------------------------------------------------------------------------
  876. void __fastcall TTerminal::RefreshDirectory()
  877. {
  878. if (SessionData->CacheDirectories &&
  879. FDirectoryCache->HasNewerFileList(CurrentDirectory, FFiles->Timestamp))
  880. {
  881. // Second parameter was added to allow (rather force) using the cache.
  882. // Before, the directory was reloaded always, it seems useless,
  883. // has it any reason?
  884. ReadDirectory(true, true);
  885. FReadDirectoryPending = false;
  886. }
  887. }
  888. //---------------------------------------------------------------------------
  889. void __fastcall TTerminal::EnsureNonExistence(const AnsiString FileName)
  890. {
  891. // if filename doesn't contain path, we check for existence of file
  892. if ((UnixExtractFileDir(FileName).IsEmpty()) &&
  893. UnixComparePaths(CurrentDirectory, FFiles->Directory))
  894. {
  895. TRemoteFile *File = FFiles->FindFile(FileName);
  896. if (File)
  897. {
  898. if (File->IsDirectory) throw ECommand(NULL, FMTLOAD(RENAME_CREATE_DIR_EXISTS, (FileName)));
  899. else throw ECommand(NULL, FMTLOAD(RENAME_CREATE_FILE_EXISTS, (FileName)));
  900. }
  901. }
  902. }
  903. //---------------------------------------------------------------------------
  904. void __fastcall TTerminal::DoStartup()
  905. {
  906. LogEvent("Doing startup conversation with host.");
  907. BeginTransaction();
  908. try
  909. {
  910. UpdateStatus(sshStartup);
  911. // Make sure that directory would be loaded at last
  912. FReadCurrentDirectoryPending = true;
  913. FReadDirectoryPending = AutoReadDirectory;
  914. FFileSystem->DoStartup();
  915. if (SessionData->LookupUserGroups && IsCapable[fcUserGroupListing])
  916. {
  917. LookupUsersGroups();
  918. }
  919. UpdateStatus(sshOpenDirectory);
  920. if (!SessionData->RemoteDirectory.IsEmpty())
  921. {
  922. ChangeDirectory(SessionData->RemoteDirectory);
  923. }
  924. }
  925. __finally
  926. {
  927. EndTransaction();
  928. }
  929. LogEvent("Startup conversation with host finished.");
  930. UpdateStatus(sshReady);
  931. }
  932. //---------------------------------------------------------------------------
  933. void __fastcall TTerminal::ReadCurrentDirectory()
  934. {
  935. assert(FFileSystem);
  936. try
  937. {
  938. // reset flag is case we are called externally (like from console dialog)
  939. FReadCurrentDirectoryPending = false;
  940. LogEvent("Getting current directory name.");
  941. AnsiString OldDirectory = FFileSystem->CurrentDirectory;
  942. FFileSystem->ReadCurrentDirectory();
  943. ReactOnCommand(fsCurrentDirectory);
  944. if (SessionData->CacheDirectoryChanges)
  945. {
  946. assert(FDirectoryChangesCache != NULL);
  947. FDirectoryChangesCache->AddDirectoryChange(OldDirectory,
  948. FLastDirectoryChange, CurrentDirectory);
  949. // not to broke the cache, if the next directory change would not
  950. // be initialited by ChangeDirectory(), which sets it
  951. // (HomeDirectory() particularly)
  952. FLastDirectoryChange = "";
  953. }
  954. if (OldDirectory.IsEmpty())
  955. {
  956. FLockDirectory = (SessionData->LockInHome ?
  957. FFileSystem->CurrentDirectory : AnsiString(""));
  958. }
  959. if (OldDirectory != FFileSystem->CurrentDirectory) DoChangeDirectory();
  960. }
  961. catch (Exception &E)
  962. {
  963. CommandError(&E, LoadStr(READ_CURRENT_DIR_ERROR));
  964. }
  965. }
  966. //---------------------------------------------------------------------------
  967. void __fastcall TTerminal::ReadDirectory(bool ReloadOnly, bool ForceCache)
  968. {
  969. bool LoadedFromCache = false;
  970. if (SessionData->CacheDirectories && FDirectoryCache->HasFileList(CurrentDirectory))
  971. {
  972. if (ReloadOnly && !ForceCache)
  973. {
  974. LogEvent("Cached directory not reloaded.");
  975. }
  976. else
  977. {
  978. DoStartReadDirectory();
  979. try
  980. {
  981. LoadedFromCache = FDirectoryCache->GetFileList(CurrentDirectory, FFiles);
  982. }
  983. __finally
  984. {
  985. DoReadDirectory(ReloadOnly);
  986. }
  987. if (LoadedFromCache)
  988. {
  989. LogEvent("Directory content loaded from cache.");
  990. }
  991. else
  992. {
  993. LogEvent("Cached Directory content has been removed.");
  994. }
  995. }
  996. }
  997. if (!LoadedFromCache)
  998. {
  999. DoStartReadDirectory();
  1000. FReadingCurrentDirectory = true;
  1001. DoReadDirectoryProgress(0);
  1002. FFiles->Directory = CurrentDirectory;
  1003. try
  1004. {
  1005. try
  1006. {
  1007. CustomReadDirectory(FFiles);
  1008. }
  1009. __finally
  1010. {
  1011. DoReadDirectoryProgress(-1);
  1012. FReadingCurrentDirectory = false;
  1013. // this must be called before error is displayed, otherwise
  1014. // TUnixDirView would be drawn with invalid data (it keeps reference
  1015. // to already destoroyed old listing)
  1016. DoReadDirectory(ReloadOnly);
  1017. if (Active)
  1018. {
  1019. if (SessionData->CacheDirectories)
  1020. {
  1021. DirectoryLoaded(FFiles);
  1022. }
  1023. }
  1024. }
  1025. }
  1026. catch (Exception &E)
  1027. {
  1028. CommandError(&E, FmtLoadStr(LIST_DIR_ERROR, ARRAYOFCONST((FFiles->Directory))));
  1029. }
  1030. }
  1031. }
  1032. //---------------------------------------------------------------------------
  1033. void __fastcall TTerminal::CustomReadDirectory(TRemoteFileList * FileList)
  1034. {
  1035. assert(FileList);
  1036. assert(FFileSystem);
  1037. FFileSystem->ReadDirectory(FileList);
  1038. ReactOnCommand(fsListDirectory);
  1039. }
  1040. //---------------------------------------------------------------------------
  1041. TRemoteFileList * TTerminal::ReadDirectoryListing(AnsiString Directory, bool UseCache)
  1042. {
  1043. TRemoteFileList * FileList;
  1044. try
  1045. {
  1046. FileList = new TRemoteFileList();
  1047. try
  1048. {
  1049. bool Cache = UseCache && SessionData->CacheDirectories;
  1050. bool LoadedFromCache = Cache && FDirectoryCache->HasFileList(Directory);
  1051. if (LoadedFromCache)
  1052. {
  1053. LoadedFromCache = FDirectoryCache->GetFileList(Directory, FileList);
  1054. }
  1055. if (!LoadedFromCache)
  1056. {
  1057. FileList->Directory = Directory;
  1058. ExceptionOnFail = true;
  1059. try
  1060. {
  1061. ReadDirectory(FileList);
  1062. }
  1063. __finally
  1064. {
  1065. ExceptionOnFail = false;
  1066. }
  1067. if (Cache)
  1068. {
  1069. AddCachedFileList(FileList);
  1070. }
  1071. }
  1072. }
  1073. catch(...)
  1074. {
  1075. delete FileList;
  1076. FileList = NULL;
  1077. throw;
  1078. }
  1079. }
  1080. catch(Exception & E)
  1081. {
  1082. COMMAND_ERROR_ARI
  1083. (
  1084. "",
  1085. FileList = ReadDirectoryListing(Directory, UseCache);
  1086. );
  1087. }
  1088. return FileList;
  1089. }
  1090. //---------------------------------------------------------------------------
  1091. void __fastcall TTerminal::ProcessDirectory(const AnsiString DirName,
  1092. TProcessFileEvent CallBackFunc, void * Param, bool UseCache)
  1093. {
  1094. TRemoteFileList * FileList = ReadDirectoryListing(DirName, UseCache);
  1095. // skip if directory listing fails and user selects "skip"
  1096. if (FileList)
  1097. {
  1098. try
  1099. {
  1100. AnsiString Directory = UnixIncludeTrailingBackslash(DirName);
  1101. TRemoteFile * File;
  1102. for (int Index = 0; Index < FileList->Count; Index++)
  1103. {
  1104. File = FileList->Files[Index];
  1105. if (!File->IsParentDirectory && !File->IsThisDirectory)
  1106. {
  1107. CallBackFunc(Directory + File->FileName, File, Param);
  1108. }
  1109. }
  1110. }
  1111. __finally
  1112. {
  1113. delete FileList;
  1114. }
  1115. }
  1116. }
  1117. //---------------------------------------------------------------------------
  1118. void __fastcall TTerminal::ReadDirectory(TRemoteFileList * FileList)
  1119. {
  1120. try
  1121. {
  1122. CustomReadDirectory(FileList);
  1123. }
  1124. catch (Exception &E)
  1125. {
  1126. CommandError(&E, FmtLoadStr(LIST_DIR_ERROR, ARRAYOFCONST((FileList->Directory))));
  1127. }
  1128. }
  1129. //---------------------------------------------------------------------------
  1130. void __fastcall TTerminal::ReadSymlink(TRemoteFile * SymlinkFile,
  1131. TRemoteFile *& File)
  1132. {
  1133. assert(FFileSystem);
  1134. try
  1135. {
  1136. LogEvent(FORMAT("Reading symlink \"%s\".", (SymlinkFile->FileName)));
  1137. FFileSystem->ReadSymlink(SymlinkFile, File);
  1138. ReactOnCommand(fsReadSymlink);
  1139. }
  1140. catch (Exception &E)
  1141. {
  1142. CommandError(&E, FMTLOAD(READ_SYMLINK_ERROR, (SymlinkFile->FileName)));
  1143. }
  1144. }
  1145. //---------------------------------------------------------------------------
  1146. void __fastcall TTerminal::ReadFile(const AnsiString FileName,
  1147. TRemoteFile *& File)
  1148. {
  1149. assert(FFileSystem);
  1150. File = NULL;
  1151. try
  1152. {
  1153. LogEvent(FORMAT("Listing file \"%s\".", (FileName)));
  1154. FFileSystem->ReadFile(FileName, File);
  1155. ReactOnCommand(fsListFile);
  1156. }
  1157. catch (Exception &E)
  1158. {
  1159. if (File) delete File;
  1160. File = NULL;
  1161. CommandError(&E, FMTLOAD(CANT_GET_ATTRS, (FileName)));
  1162. }
  1163. }
  1164. //---------------------------------------------------------------------------
  1165. bool __fastcall TTerminal::ProcessFiles(TStrings * FileList,
  1166. TFileOperation Operation, TProcessFileEvent ProcessFile, void * Param,
  1167. TOperationSide Side, bool Ex)
  1168. {
  1169. assert(FFileSystem);
  1170. assert(FileList);
  1171. bool Result = false;
  1172. bool DisconnectWhenComplete = false;
  1173. try
  1174. {
  1175. TFileOperationProgressType Progress(FOnProgress, FOnFinished);
  1176. Progress.Start(Operation, Side, FileList->Count);
  1177. FOperationProgress = &Progress;
  1178. try
  1179. {
  1180. if (Side == osRemote)
  1181. {
  1182. BeginTransaction();
  1183. }
  1184. try
  1185. {
  1186. int Index = 0;
  1187. AnsiString FileName;
  1188. bool Success;
  1189. while ((Index < FileList->Count) && (Progress.Cancel == csContinue))
  1190. {
  1191. FileName = FileList->Strings[Index];
  1192. try
  1193. {
  1194. Success = false;
  1195. if (!Ex)
  1196. {
  1197. ProcessFile(FileName, (TRemoteFile *)FileList->Objects[Index], Param);
  1198. }
  1199. else
  1200. {
  1201. TProcessFileEventEx ProcessFileEx = (TProcessFileEventEx)ProcessFile;
  1202. ProcessFileEx(FileName, (TRemoteFile *)FileList->Objects[Index], Param, Index);
  1203. }
  1204. Success = true;
  1205. }
  1206. __finally
  1207. {
  1208. Progress.Finish(FileName, Success, DisconnectWhenComplete);
  1209. }
  1210. Index++;
  1211. }
  1212. }
  1213. __finally
  1214. {
  1215. if (Side == osRemote)
  1216. {
  1217. EndTransaction();
  1218. }
  1219. }
  1220. if (Progress.Cancel == csContinue)
  1221. {
  1222. Result = true;
  1223. }
  1224. }
  1225. __finally
  1226. {
  1227. FOperationProgress = NULL;
  1228. Progress.Stop();
  1229. }
  1230. }
  1231. catch (...)
  1232. {
  1233. DisconnectWhenComplete = false;
  1234. // this was missing here. was it by purpose?
  1235. // without it any error message is lost
  1236. throw;
  1237. }
  1238. if (DisconnectWhenComplete)
  1239. {
  1240. CloseOnCompletion();
  1241. }
  1242. return Result;
  1243. }
  1244. //---------------------------------------------------------------------------
  1245. bool __fastcall TTerminal::ProcessFilesEx(TStrings * FileList, TFileOperation Operation,
  1246. TProcessFileEventEx ProcessFile, void * Param, TOperationSide Side)
  1247. {
  1248. return ProcessFiles(FileList, Operation, TProcessFileEvent(ProcessFile),
  1249. Param, Side, true);
  1250. }
  1251. //---------------------------------------------------------------------------
  1252. TStrings * __fastcall TTerminal::GetFixedPaths()
  1253. {
  1254. assert(FFileSystem != NULL);
  1255. return FFileSystem->GetFixedPaths();
  1256. }
  1257. //---------------------------------------------------------------------------
  1258. bool __fastcall TTerminal::GetResolvingSymlinks()
  1259. {
  1260. return SessionData->ResolveSymlinks && IsCapable[fcResolveSymlink];
  1261. }
  1262. //---------------------------------------------------------------------------
  1263. bool __fastcall TTerminal::IsRecycledFile(AnsiString FileName)
  1264. {
  1265. AnsiString Path = UnixExtractFilePath(FileName);
  1266. if (Path.IsEmpty())
  1267. {
  1268. Path = CurrentDirectory;
  1269. }
  1270. return UnixComparePaths(Path, SessionData->RecycleBinPath);
  1271. }
  1272. //---------------------------------------------------------------------------
  1273. void __fastcall TTerminal::RecycleFile(AnsiString FileName,
  1274. const TRemoteFile * File)
  1275. {
  1276. if (FileName.IsEmpty())
  1277. {
  1278. assert(File != NULL);
  1279. FileName = File->FileName;
  1280. }
  1281. if (!IsRecycledFile(FileName))
  1282. {
  1283. LogEvent(FORMAT("Moving file \"%s\" to remote recycle bin '%s'.",
  1284. (FileName, SessionData->RecycleBinPath)));
  1285. TMoveFileParams Params;
  1286. Params.Target = SessionData->RecycleBinPath;
  1287. Params.FileMask = FORMAT("*-%s.*", (FormatDateTime("yyyymmdd-hhnnss", Now())));
  1288. MoveFile(FileName, File, &Params);
  1289. }
  1290. }
  1291. //---------------------------------------------------------------------------
  1292. void __fastcall TTerminal::DeleteFile(AnsiString FileName,
  1293. const TRemoteFile * File, void * Recursive)
  1294. {
  1295. if (FileName.IsEmpty() && File)
  1296. {
  1297. FileName = File->FileName;
  1298. }
  1299. if (OperationProgress && OperationProgress->Operation == foDelete)
  1300. {
  1301. if (OperationProgress->Cancel != csContinue) Abort();
  1302. OperationProgress->SetFile(FileName);
  1303. }
  1304. if (SessionData->DeleteToRecycleBin && !IsRecycledFile(FileName))
  1305. {
  1306. RecycleFile(FileName, File);
  1307. }
  1308. else
  1309. {
  1310. LogEvent(FORMAT("Deleting file \"%s\".", (FileName)));
  1311. if (File) FileModified(File, FileName, true);
  1312. DoDeleteFile(FileName, File, Recursive);
  1313. ReactOnCommand(fsDeleteFile);
  1314. }
  1315. }
  1316. //---------------------------------------------------------------------------
  1317. void __fastcall TTerminal::DoDeleteFile(const AnsiString FileName,
  1318. const TRemoteFile * File, void * Recursive)
  1319. {
  1320. try
  1321. {
  1322. assert(FFileSystem);
  1323. // 'File' parameter: SFTPFileSystem needs to know if file is file or directory
  1324. FFileSystem->DeleteFile(FileName, File,
  1325. Recursive ? *((bool*)Recursive) : true);
  1326. }
  1327. catch(Exception & E)
  1328. {
  1329. COMMAND_ERROR_ARI
  1330. (
  1331. FMTLOAD(DELETE_FILE_ERROR, (FileName)),
  1332. DoDeleteFile(FileName, File, Recursive)
  1333. );
  1334. }
  1335. }
  1336. //---------------------------------------------------------------------------
  1337. bool __fastcall TTerminal::DeleteFiles(TStrings * FilesToDelete, bool * Recursive)
  1338. {
  1339. // TODO: avoid resolving symlinks while reading subdirectories.
  1340. // Resolving does not work anyway for relative symlinks in subdirectories
  1341. // (at least for SFTP).
  1342. return ProcessFiles(FilesToDelete, foDelete, DeleteFile, Recursive);
  1343. }
  1344. //---------------------------------------------------------------------------
  1345. void __fastcall TTerminal::DeleteLocalFile(AnsiString FileName,
  1346. const TRemoteFile * /*File*/, void * /*Param*/)
  1347. {
  1348. if (OnDeleteLocalFile == NULL)
  1349. {
  1350. if (!RecursiveDeleteFile(FileName, false))
  1351. {
  1352. throw Exception(FMTLOAD(DELETE_FILE_ERROR, (FileName)));
  1353. }
  1354. }
  1355. else
  1356. {
  1357. OnDeleteLocalFile(FileName);
  1358. }
  1359. }
  1360. //---------------------------------------------------------------------------
  1361. bool __fastcall TTerminal::DeleteLocalFiles(TStrings * FileList)
  1362. {
  1363. return ProcessFiles(FileList, foDelete, DeleteLocalFile, NULL, osLocal);
  1364. }
  1365. //---------------------------------------------------------------------------
  1366. void __fastcall TTerminal::CustomCommandOnFile(AnsiString FileName,
  1367. const TRemoteFile * File, void * AParams)
  1368. {
  1369. TCustomCommandParams * Params = ((TCustomCommandParams *)AParams);
  1370. if (FileName.IsEmpty() && File)
  1371. {
  1372. FileName = File->FileName;
  1373. }
  1374. if (OperationProgress && OperationProgress->Operation == foCustomCommand)
  1375. {
  1376. if (OperationProgress->Cancel != csContinue) Abort();
  1377. OperationProgress->SetFile(FileName);
  1378. }
  1379. LogEvent(FORMAT("Executing custom command \"%s\" (%d) on file \"%s\".",
  1380. (Params->Command, Params->Params, FileName)));
  1381. if (File) FileModified(File, FileName);
  1382. DoCustomCommandOnFile(FileName, File, Params->Command, Params->Params,
  1383. Params->OutputEvent);
  1384. ReactOnCommand(fsAnyCommand);
  1385. }
  1386. //---------------------------------------------------------------------------
  1387. void __fastcall TTerminal::DoCustomCommandOnFile(AnsiString FileName,
  1388. const TRemoteFile * File, AnsiString Command, int Params,
  1389. TLogAddLineEvent OutputEvent)
  1390. {
  1391. try
  1392. {
  1393. if (IsCapable[fcAnyCommand])
  1394. {
  1395. assert(FFileSystem);
  1396. FFileSystem->CustomCommandOnFile(FileName, File, Command, Params, OutputEvent);
  1397. }
  1398. else
  1399. {
  1400. assert(CommandSessionOpened);
  1401. assert(FCommandSession->FSProtocol == cfsSCP);
  1402. LogEvent("Executing custom command on command session.");
  1403. assert(FCommandSession->Log->OnAddLine == NULL);
  1404. FCommandSession->Log->OnAddLine = Log->AddFromOtherLog;
  1405. try
  1406. {
  1407. FCommandSession->CurrentDirectory = CurrentDirectory;
  1408. FCommandSession->FFileSystem->CustomCommandOnFile(FileName, File, Command,
  1409. Params, OutputEvent);
  1410. }
  1411. __finally
  1412. {
  1413. FCommandSession->Log->OnAddLine = NULL;
  1414. }
  1415. }
  1416. }
  1417. catch(Exception & E)
  1418. {
  1419. COMMAND_ERROR_ARI
  1420. (
  1421. FMTLOAD(CUSTOM_COMMAND_ERROR, (Command, FileName)),
  1422. DoCustomCommandOnFile(FileName, File, Command, Params, OutputEvent)
  1423. );
  1424. }
  1425. }
  1426. //---------------------------------------------------------------------------
  1427. void __fastcall TTerminal::CustomCommandOnFiles(AnsiString Command,
  1428. int Params, TStrings * Files, TLogAddLineEvent OutputEvent)
  1429. {
  1430. if (!TRemoteCustomCommand().IsFileListCommand(Command))
  1431. {
  1432. TCustomCommandParams AParams;
  1433. AParams.Command = Command;
  1434. AParams.Params = Params;
  1435. AParams.OutputEvent = OutputEvent;
  1436. ProcessFiles(Files, foCustomCommand, CustomCommandOnFile, &AParams);
  1437. }
  1438. else
  1439. {
  1440. AnsiString FileList;
  1441. for (int i = 0; i < Files->Count; i++)
  1442. {
  1443. TRemoteFile * File = static_cast<TRemoteFile *>(Files->Objects[i]);
  1444. bool Dir = File->IsDirectory && !File->IsSymLink;
  1445. if (!Dir || FLAGSET(Params, ccApplyToDirectories))
  1446. {
  1447. if (!FileList.IsEmpty())
  1448. {
  1449. FileList += " ";
  1450. }
  1451. FileList += "\"" + ShellDelimitStr(Files->Strings[i], '"') + "\"";
  1452. }
  1453. }
  1454. AnsiString Cmd = TRemoteCustomCommand("", FileList).Complete(Command, true);
  1455. AnyCommand(Cmd, OutputEvent);
  1456. }
  1457. }
  1458. //---------------------------------------------------------------------------
  1459. void __fastcall TTerminal::ChangeFileProperties(AnsiString FileName,
  1460. const TRemoteFile * File, /*const TRemoteProperties*/ void * Properties)
  1461. {
  1462. TRemoteProperties * RProperties = (TRemoteProperties *)Properties;
  1463. assert(RProperties && !RProperties->Valid.Empty());
  1464. if (FileName.IsEmpty() && File)
  1465. {
  1466. FileName = File->FileName;
  1467. }
  1468. if (OperationProgress && OperationProgress->Operation == foSetProperties)
  1469. {
  1470. if (OperationProgress->Cancel != csContinue) Abort();
  1471. OperationProgress->SetFile(FileName);
  1472. }
  1473. if (IsLogging())
  1474. {
  1475. LogEvent(FORMAT("Changing properties of \"%s\" (%s)",
  1476. (FileName, BooleanToEngStr(RProperties->Recursive))));
  1477. if (RProperties->Valid.Contains(vpRights))
  1478. {
  1479. LogEvent(FORMAT(" - mode: \"%s\"", (RProperties->Rights.ModeStr)));
  1480. }
  1481. if (RProperties->Valid.Contains(vpGroup))
  1482. {
  1483. LogEvent(FORMAT(" - group: \"%s\"", (RProperties->Group)));
  1484. }
  1485. if (RProperties->Valid.Contains(vpOwner))
  1486. {
  1487. LogEvent(FORMAT(" - owner: \"%s\"", (RProperties->Owner)));
  1488. }
  1489. if (RProperties->Valid.Contains(vpModification))
  1490. {
  1491. LogEvent(FORMAT(" - modification: \"%s\"",
  1492. (FormatDateTime("dddddd tt",
  1493. UnixToDateTime(RProperties->Modification, SessionData->ConsiderDST)))));
  1494. }
  1495. if (RProperties->Valid.Contains(vpLastAccess))
  1496. {
  1497. LogEvent(FORMAT(" - last access: \"%s\"",
  1498. (FormatDateTime("dddddd tt",
  1499. UnixToDateTime(RProperties->LastAccess, SessionData->ConsiderDST)))));
  1500. }
  1501. }
  1502. if (File) FileModified(File, FileName);
  1503. DoChangeFileProperties(FileName, File, RProperties);
  1504. ReactOnCommand(fsChangeProperties);
  1505. }
  1506. //---------------------------------------------------------------------------
  1507. void __fastcall TTerminal::DoChangeFileProperties(const AnsiString FileName,
  1508. const TRemoteFile * File, const TRemoteProperties * Properties)
  1509. {
  1510. try
  1511. {
  1512. assert(FFileSystem);
  1513. FFileSystem->ChangeFileProperties(FileName, File, Properties);
  1514. }
  1515. catch(Exception & E)
  1516. {
  1517. COMMAND_ERROR_ARI
  1518. (
  1519. FMTLOAD(CHANGE_PROPERTIES_ERROR, (FileName)),
  1520. DoChangeFileProperties(FileName, File, Properties)
  1521. );
  1522. }
  1523. }
  1524. //---------------------------------------------------------------------------
  1525. void __fastcall TTerminal::ChangeFilesProperties(TStrings * FileList,
  1526. const TRemoteProperties * Properties)
  1527. {
  1528. ProcessFiles(FileList, foSetProperties, ChangeFileProperties, (void *)Properties);
  1529. }
  1530. //---------------------------------------------------------------------------
  1531. void __fastcall TTerminal::CalculateFileSize(AnsiString FileName,
  1532. const TRemoteFile * File, /*TCalculateSizeParams*/ void * Param)
  1533. {
  1534. assert(Param);
  1535. assert(File);
  1536. TCalculateSizeParams * AParams = static_cast<TCalculateSizeParams*>(Param);
  1537. if (FileName.IsEmpty())
  1538. {
  1539. FileName = File->FileName;
  1540. }
  1541. if ((AParams->CopyParam == NULL) ||
  1542. AParams->CopyParam->AllowTransfer(UnixExcludeTrailingBackslash(File->FullFileName),
  1543. osRemote, File->IsDirectory))
  1544. {
  1545. if (File->IsDirectory)
  1546. {
  1547. if (!File->IsSymLink)
  1548. {
  1549. LogEvent(FORMAT("Getting size of directory \"%s\"", (FileName)));
  1550. // pass in full path so we get it back in file list for AllowTransfer() exclusion
  1551. DoCalculateDirectorySize(File->FullFileName, File, AParams);
  1552. }
  1553. else
  1554. {
  1555. AParams->Size += File->Size;
  1556. }
  1557. if (AParams->Stats != NULL)
  1558. {
  1559. AParams->Stats->Directories++;
  1560. }
  1561. }
  1562. else
  1563. {
  1564. AParams->Size += File->Size;
  1565. if (AParams->Stats != NULL)
  1566. {
  1567. AParams->Stats->Files++;
  1568. }
  1569. }
  1570. if ((AParams->Stats != NULL) && File->IsSymLink)
  1571. {
  1572. AParams->Stats->SymLinks++;
  1573. }
  1574. }
  1575. if (OperationProgress && OperationProgress->Operation == foCalculateSize)
  1576. {
  1577. if (OperationProgress->Cancel != csContinue) Abort();
  1578. OperationProgress->SetFile(FileName);
  1579. }
  1580. }
  1581. //---------------------------------------------------------------------------
  1582. void __fastcall TTerminal::DoCalculateDirectorySize(const AnsiString FileName,
  1583. const TRemoteFile * File, TCalculateSizeParams * Params)
  1584. {
  1585. try
  1586. {
  1587. ProcessDirectory(FileName, CalculateFileSize, Params);
  1588. }
  1589. catch(Exception & E)
  1590. {
  1591. if (!Active || ((Params->Params & csIgnoreErrors) == 0))
  1592. {
  1593. COMMAND_ERROR_ARI
  1594. (
  1595. FMTLOAD(CALCULATE_SIZE_ERROR, (FileName)),
  1596. DoCalculateDirectorySize(FileName, File, Params)
  1597. );
  1598. }
  1599. }
  1600. }
  1601. //---------------------------------------------------------------------------
  1602. void __fastcall TTerminal::CalculateFilesSize(TStrings * FileList,
  1603. __int64 & Size, int Params, const TCopyParamType * CopyParam,
  1604. TCalculateSizeStats * Stats)
  1605. {
  1606. TCalculateSizeParams Param;
  1607. Param.Size = 0;
  1608. Param.Params = Params;
  1609. Param.CopyParam = CopyParam;
  1610. Param.Stats = Stats;
  1611. ProcessFiles(FileList, foCalculateSize, CalculateFileSize, &Param);
  1612. Size = Param.Size;
  1613. }
  1614. //---------------------------------------------------------------------------
  1615. void __fastcall TTerminal::RenameFile(const AnsiString FileName,
  1616. const AnsiString NewName)
  1617. {
  1618. LogEvent(FORMAT("Renaming file \"%s\" to \"%s\".", (FileName, NewName)));
  1619. DoRenameFile(FileName, NewName, false);
  1620. ReactOnCommand(fsRenameFile);
  1621. }
  1622. //---------------------------------------------------------------------------
  1623. void __fastcall TTerminal::RenameFile(const TRemoteFile * File,
  1624. const AnsiString NewName, bool CheckExistence)
  1625. {
  1626. assert(File && File->Directory == FFiles);
  1627. bool Proceed = true;
  1628. // if filename doesn't contain path, we check for existence of file
  1629. if ((File->FileName != NewName) && CheckExistence &&
  1630. Configuration->ConfirmOverwriting &&
  1631. UnixComparePaths(CurrentDirectory, FFiles->Directory))
  1632. {
  1633. TRemoteFile * DuplicateFile = FFiles->FindFile(NewName);
  1634. if (DuplicateFile)
  1635. {
  1636. AnsiString QuestionFmt;
  1637. if (DuplicateFile->IsDirectory) QuestionFmt = LoadStr(DIRECTORY_OVERWRITE);
  1638. else QuestionFmt = LoadStr(FILE_OVERWRITE);
  1639. int Result;
  1640. TQueryParams Params(qpNeverAskAgainCheck);
  1641. Result = DoQueryUser(FORMAT(QuestionFmt, (NewName)),
  1642. qaYes | qaNo, &Params);
  1643. if (Result == qaNeverAskAgain)
  1644. {
  1645. Proceed = true;
  1646. Configuration->ConfirmOverwriting = false;
  1647. }
  1648. else
  1649. {
  1650. Proceed = (Result == qaYes);
  1651. }
  1652. }
  1653. }
  1654. if (Proceed)
  1655. {
  1656. FileModified(File, File->FileName);
  1657. RenameFile(File->FileName, NewName);
  1658. }
  1659. }
  1660. //---------------------------------------------------------------------------
  1661. void __fastcall TTerminal::DoRenameFile(const AnsiString FileName,
  1662. const AnsiString NewName, bool Move)
  1663. {
  1664. try
  1665. {
  1666. assert(FFileSystem);
  1667. FFileSystem->RenameFile(FileName, NewName);
  1668. }
  1669. catch(Exception & E)
  1670. {
  1671. COMMAND_ERROR_ARI
  1672. (
  1673. FMTLOAD(Move ? MOVE_FILE_ERROR : RENAME_FILE_ERROR, (FileName, NewName)),
  1674. DoRenameFile(FileName, NewName, Move)
  1675. );
  1676. }
  1677. }
  1678. //---------------------------------------------------------------------------
  1679. void __fastcall TTerminal::MoveFile(const AnsiString FileName,
  1680. const TRemoteFile * File, /*const TMoveFileParams*/ void * Param)
  1681. {
  1682. if (OperationProgress &&
  1683. ((OperationProgress->Operation == foRemoteMove) ||
  1684. (OperationProgress->Operation == foDelete)))
  1685. {
  1686. if (OperationProgress->Cancel != csContinue) Abort();
  1687. OperationProgress->SetFile(FileName);
  1688. }
  1689. assert(Param != NULL);
  1690. const TMoveFileParams & Params = *static_cast<const TMoveFileParams*>(Param);
  1691. AnsiString NewName = UnixIncludeTrailingBackslash(Params.Target) +
  1692. MaskFileName(UnixExtractFileName(FileName), Params.FileMask);
  1693. LogEvent(FORMAT("Moving file \"%s\" to \"%s\".", (FileName, NewName)));
  1694. FileModified(File, FileName);
  1695. DoRenameFile(FileName, NewName, true);
  1696. ReactOnCommand(fsMoveFile);
  1697. }
  1698. //---------------------------------------------------------------------------
  1699. bool __fastcall TTerminal::MoveFiles(TStrings * FileList, const AnsiString Target,
  1700. const AnsiString FileMask)
  1701. {
  1702. TMoveFileParams Params;
  1703. Params.Target = Target;
  1704. Params.FileMask = FileMask;
  1705. DirectoryModified(Target, true);
  1706. return ProcessFiles(FileList, foRemoteMove, MoveFile, &Params);
  1707. }
  1708. //---------------------------------------------------------------------------
  1709. void __fastcall TTerminal::DoCopyFile(const AnsiString FileName,
  1710. const AnsiString NewName)
  1711. {
  1712. try
  1713. {
  1714. assert(FFileSystem);
  1715. if (IsCapable[fcRemoteCopy])
  1716. {
  1717. FFileSystem->CopyFile(FileName, NewName);
  1718. }
  1719. else
  1720. {
  1721. assert(CommandSessionOpened);
  1722. assert(FCommandSession->FSProtocol == cfsSCP);
  1723. LogEvent("Copying file on command session.");
  1724. FCommandSession->CurrentDirectory = CurrentDirectory;
  1725. FCommandSession->FFileSystem->CopyFile(FileName, NewName);
  1726. }
  1727. }
  1728. catch(Exception & E)
  1729. {
  1730. COMMAND_ERROR_ARI
  1731. (
  1732. FMTLOAD(COPY_FILE_ERROR, (FileName, NewName)),
  1733. DoCopyFile(FileName, NewName)
  1734. );
  1735. }
  1736. }
  1737. //---------------------------------------------------------------------------
  1738. void __fastcall TTerminal::CopyFile(const AnsiString FileName,
  1739. const TRemoteFile * /*File*/, /*const TMoveFileParams*/ void * Param)
  1740. {
  1741. if (OperationProgress && (OperationProgress->Operation == foRemoteCopy))
  1742. {
  1743. if (OperationProgress->Cancel != csContinue) Abort();
  1744. OperationProgress->SetFile(FileName);
  1745. }
  1746. assert(Param != NULL);
  1747. const TMoveFileParams & Params = *static_cast<const TMoveFileParams*>(Param);
  1748. AnsiString NewName = UnixIncludeTrailingBackslash(Params.Target) +
  1749. MaskFileName(UnixExtractFileName(FileName), Params.FileMask);
  1750. LogEvent(FORMAT("Copying file \"%s\" to \"%s\".", (FileName, NewName)));
  1751. DoCopyFile(FileName, NewName);
  1752. ReactOnCommand(fsCopyFile);
  1753. }
  1754. //---------------------------------------------------------------------------
  1755. bool __fastcall TTerminal::CopyFiles(TStrings * FileList, const AnsiString Target,
  1756. const AnsiString FileMask)
  1757. {
  1758. TMoveFileParams Params;
  1759. Params.Target = Target;
  1760. Params.FileMask = FileMask;
  1761. DirectoryModified(Target, true);
  1762. return ProcessFiles(FileList, foRemoteCopy, CopyFile, &Params);
  1763. }
  1764. //---------------------------------------------------------------------------
  1765. void __fastcall TTerminal::CreateDirectory(const AnsiString DirName,
  1766. const TRemoteProperties * Properties)
  1767. {
  1768. assert(FFileSystem);
  1769. EnsureNonExistence(DirName);
  1770. FileModified(NULL, DirName);
  1771. LogEvent(FORMAT("Creating directory \"%s\".", (DirName)));
  1772. DoCreateDirectory(DirName, Properties);
  1773. ReactOnCommand(fsCreateDirectory);
  1774. }
  1775. //---------------------------------------------------------------------------
  1776. void __fastcall TTerminal::DoCreateDirectory(const AnsiString DirName,
  1777. const TRemoteProperties * Properties)
  1778. {
  1779. try
  1780. {
  1781. assert(FFileSystem);
  1782. FFileSystem->CreateDirectory(DirName, Properties);
  1783. }
  1784. catch(Exception & E)
  1785. {
  1786. COMMAND_ERROR_ARI
  1787. (
  1788. FMTLOAD(CREATE_DIR_ERROR, (DirName)),
  1789. DoCreateDirectory(DirName, Properties)
  1790. );
  1791. }
  1792. }
  1793. //---------------------------------------------------------------------------
  1794. void __fastcall TTerminal::CreateLink(const AnsiString FileName,
  1795. const AnsiString PointTo, bool Symbolic)
  1796. {
  1797. assert(FFileSystem);
  1798. EnsureNonExistence(FileName);
  1799. if (SessionData->CacheDirectories)
  1800. {
  1801. DirectoryModified(CurrentDirectory, false);
  1802. }
  1803. LogEvent(FORMAT("Creating link \"%s\" to \"%s\" (symbolic: %s).",
  1804. (FileName, PointTo, BooleanToEngStr(Symbolic))));
  1805. DoCreateLink(FileName, PointTo, Symbolic);
  1806. ReactOnCommand(fsCreateDirectory);
  1807. }
  1808. //---------------------------------------------------------------------------
  1809. void __fastcall TTerminal::DoCreateLink(const AnsiString FileName,
  1810. const AnsiString PointTo, bool Symbolic)
  1811. {
  1812. try
  1813. {
  1814. assert(FFileSystem);
  1815. FFileSystem->CreateLink(FileName, PointTo, Symbolic);
  1816. }
  1817. catch(Exception & E)
  1818. {
  1819. COMMAND_ERROR_ARI
  1820. (
  1821. FMTLOAD(CREATE_LINK_ERROR, (FileName)),
  1822. DoCreateLink(FileName, PointTo, Symbolic);
  1823. );
  1824. }
  1825. }
  1826. //---------------------------------------------------------------------------
  1827. void __fastcall TTerminal::HomeDirectory()
  1828. {
  1829. assert(FFileSystem);
  1830. try
  1831. {
  1832. LogEvent("Changing directory to home directory.");
  1833. FFileSystem->HomeDirectory();
  1834. ReactOnCommand(fsHomeDirectory);
  1835. }
  1836. catch (Exception &E)
  1837. {
  1838. CommandError(&E, LoadStr(CHANGE_HOMEDIR_ERROR));
  1839. }
  1840. }
  1841. //---------------------------------------------------------------------------
  1842. void __fastcall TTerminal::ChangeDirectory(const AnsiString Directory)
  1843. {
  1844. assert(FFileSystem);
  1845. try
  1846. {
  1847. AnsiString CachedDirectory;
  1848. assert(!SessionData->CacheDirectoryChanges || (FDirectoryChangesCache != NULL));
  1849. // never use directory change cache during startup, this ensures, we never
  1850. // end-up initially in non-existing directory
  1851. if ((Status >= sshReady) &&
  1852. SessionData->CacheDirectoryChanges &&
  1853. FDirectoryChangesCache->GetDirectoryChange(PeekCurrentDirectory(),
  1854. Directory, CachedDirectory))
  1855. {
  1856. LogEvent(FORMAT("Cached directory change via \"%s\" to \"%s\".",
  1857. (Directory, CachedDirectory)));
  1858. FFileSystem->CachedChangeDirectory(CachedDirectory);
  1859. }
  1860. else
  1861. {
  1862. LogEvent(FORMAT("Changing directory to \"%s\".", (Directory)));
  1863. FFileSystem->ChangeDirectory(Directory);
  1864. }
  1865. FLastDirectoryChange = Directory;
  1866. ReactOnCommand(fsChangeDirectory);
  1867. }
  1868. catch (Exception &E)
  1869. {
  1870. CommandError(&E, FMTLOAD(CHANGE_DIR_ERROR, (Directory)));
  1871. }
  1872. }
  1873. //---------------------------------------------------------------------------
  1874. void __fastcall TTerminal::LookupUsersGroups()
  1875. {
  1876. assert(FFileSystem);
  1877. assert(IsCapable[fcUserGroupListing]);
  1878. try
  1879. {
  1880. FUsersGroupsLookedup = true;
  1881. LogEvent("Looking up groups and users.");
  1882. FFileSystem->LookupUsersGroups();
  1883. ReactOnCommand(fsLookupUsersGroups);
  1884. if (IsLogging())
  1885. {
  1886. if (FGroups->Count > 0)
  1887. {
  1888. LogEvent("Following groups found:");
  1889. for (int Index = 0; Index < FGroups->Count; Index++)
  1890. {
  1891. LogEvent(AnsiString(" ") + FGroups->Strings[Index]);
  1892. }
  1893. }
  1894. else
  1895. {
  1896. LogEvent("No groups found.");
  1897. }
  1898. if (FUsers->Count > 0)
  1899. {
  1900. LogEvent("Following users found:");
  1901. for (int Index = 0; Index < FUsers->Count; Index++)
  1902. {
  1903. LogEvent(AnsiString(" ") + FUsers->Strings[Index]);
  1904. }
  1905. }
  1906. else
  1907. {
  1908. LogEvent("No users found.");
  1909. }
  1910. }
  1911. }
  1912. catch (Exception &E)
  1913. {
  1914. CommandError(&E, LoadStr(LOOKUP_GROUPS_ERROR));
  1915. }
  1916. }
  1917. //---------------------------------------------------------------------------
  1918. bool __fastcall TTerminal::AllowedAnyCommand(const AnsiString Command)
  1919. {
  1920. return !Command.Trim().IsEmpty();
  1921. }
  1922. //---------------------------------------------------------------------------
  1923. bool __fastcall TTerminal::GetCommandSessionOpened()
  1924. {
  1925. // consider secodary terminal open in "ready" state only
  1926. // so we never do keepalives on it until it is completelly initialised
  1927. return (FCommandSession != NULL) &&
  1928. (FCommandSession->Status == sshReady);
  1929. }
  1930. //---------------------------------------------------------------------------
  1931. TTerminal * __fastcall TTerminal::GetCommandSession()
  1932. {
  1933. if ((FCommandSession != NULL) && !FCommandSession->Active)
  1934. {
  1935. SAFE_DESTROY(FCommandSession);
  1936. }
  1937. if (FCommandSession == NULL)
  1938. {
  1939. // transaction cannot be started yet to allow proper matching transation
  1940. // levels between main and command session
  1941. assert(!FInTransaction);
  1942. try
  1943. {
  1944. FCommandSession = new TSecondaryTerminal(this);
  1945. FCommandSession->AutoReadDirectory = false;
  1946. FCommandSession->Configuration = Configuration;
  1947. FCommandSession->SessionData = SessionData;
  1948. FCommandSession->SessionData->RemoteDirectory = CurrentDirectory;
  1949. FCommandSession->SessionData->FSProtocol = fsSCPonly;
  1950. FCommandSession->FExceptionOnFail = FExceptionOnFail;
  1951. FCommandSession->OnQueryUser = OnQueryUser;
  1952. FCommandSession->OnPromptUser = OnPromptUser;
  1953. FCommandSession->OnShowExtendedException = OnShowExtendedException;
  1954. FCommandSession->OnProgress = OnProgress;
  1955. FCommandSession->OnFinished = OnFinished;
  1956. // do not copy OnDisplayBanner to avoid it being displayed
  1957. }
  1958. catch(...)
  1959. {
  1960. SAFE_DESTROY(FCommandSession);
  1961. throw;
  1962. }
  1963. }
  1964. return FCommandSession;
  1965. }
  1966. //---------------------------------------------------------------------------
  1967. void __fastcall TTerminal::AnyCommand(const AnsiString Command,
  1968. TLogAddLineEvent OutputEvent)
  1969. {
  1970. assert(FFileSystem);
  1971. try
  1972. {
  1973. DirectoryModified(CurrentDirectory, false);
  1974. if (IsCapable[fcAnyCommand])
  1975. {
  1976. LogEvent("Executing user defined command.");
  1977. FFileSystem->AnyCommand(Command, OutputEvent);
  1978. }
  1979. else
  1980. {
  1981. assert(CommandSessionOpened);
  1982. assert(FCommandSession->FSProtocol == cfsSCP);
  1983. LogEvent("Executing user defined command on command session.");
  1984. assert(FCommandSession->Log->OnAddLine == NULL);
  1985. FCommandSession->Log->OnAddLine = Log->AddFromOtherLog;
  1986. try
  1987. {
  1988. FCommandSession->CurrentDirectory = CurrentDirectory;
  1989. FCommandSession->FFileSystem->AnyCommand(Command, OutputEvent);
  1990. FCommandSession->FFileSystem->ReadCurrentDirectory();
  1991. // synchronize pwd (by purpose we lose transaction optimalisation here)
  1992. ChangeDirectory(FCommandSession->CurrentDirectory);
  1993. }
  1994. __finally
  1995. {
  1996. FCommandSession->Log->OnAddLine = NULL;
  1997. }
  1998. }
  1999. ReactOnCommand(fsAnyCommand);
  2000. }
  2001. catch (Exception &E)
  2002. {
  2003. if (ExceptionOnFail || (E.InheritsFrom(__classid(EFatal)))) throw;
  2004. else DoShowExtendedException(&E);
  2005. }
  2006. }
  2007. //---------------------------------------------------------------------------
  2008. bool __fastcall TTerminal::CreateLocalFile(const AnsiString FileName,
  2009. TFileOperationProgressType * OperationProgress, HANDLE * AHandle,
  2010. bool NoConfirmation)
  2011. {
  2012. assert(AHandle);
  2013. bool Result = true;
  2014. FILE_OPERATION_LOOP (FMTLOAD(CREATE_FILE_ERROR, (FileName)),
  2015. bool Done;
  2016. unsigned int CreateAttr = FILE_ATTRIBUTE_NORMAL;
  2017. do
  2018. {
  2019. *AHandle = CreateFile(FileName.c_str(), GENERIC_WRITE, FILE_SHARE_READ,
  2020. NULL, CREATE_ALWAYS, CreateAttr, 0);
  2021. Done = (*AHandle != INVALID_HANDLE_VALUE);
  2022. if (!Done)
  2023. {
  2024. int FileAttr;
  2025. if (FileExists(FileName) &&
  2026. (((FileAttr = FileGetAttr(FileName)) & (faReadOnly | faHidden)) != 0))
  2027. {
  2028. if (FLAGSET(FileAttr, faReadOnly))
  2029. {
  2030. if (OperationProgress->NoToAll)
  2031. {
  2032. Result = false;
  2033. }
  2034. else if (!OperationProgress->YesToAll && !NoConfirmation)
  2035. {
  2036. int Answer;
  2037. SUSPEND_OPERATION
  2038. (
  2039. Answer = DoQueryUser(
  2040. FMTLOAD(READ_ONLY_OVERWRITE, (FileName)),
  2041. qaYes | qaNo | qaCancel | qaYesToAll | qaNoToAll, 0);
  2042. );
  2043. switch (Answer) {
  2044. case qaYesToAll: OperationProgress->YesToAll = true; break;
  2045. case qaCancel: OperationProgress->Cancel = csCancel; // continue on next case
  2046. case qaNoToAll: OperationProgress->NoToAll = true;
  2047. case qaNo: Result = false; break;
  2048. }
  2049. }
  2050. }
  2051. else
  2052. {
  2053. assert(FLAGSET(FileAttr, faHidden));
  2054. Result = true;
  2055. }
  2056. if (Result)
  2057. {
  2058. CreateAttr |=
  2059. FLAGMASK(FLAGSET(FileAttr, faHidden), FILE_ATTRIBUTE_HIDDEN) |
  2060. FLAGMASK(FLAGSET(FileAttr, faReadOnly), FILE_ATTRIBUTE_READONLY);
  2061. FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (FileName)),
  2062. if (FileSetAttr(FileName, FileAttr & ~(faReadOnly | faHidden)) != 0)
  2063. {
  2064. EXCEPTION;
  2065. }
  2066. );
  2067. }
  2068. else
  2069. {
  2070. Done = true;
  2071. }
  2072. }
  2073. else
  2074. {
  2075. EXCEPTION;
  2076. }
  2077. }
  2078. }
  2079. while (!Done);
  2080. );
  2081. return Result;
  2082. }
  2083. //---------------------------------------------------------------------------
  2084. void __fastcall TTerminal::OpenLocalFile(const AnsiString FileName,
  2085. int Access, int * AAttrs, HANDLE * AHandle, __int64 * ACTime,
  2086. __int64 * AMTime, __int64 * AATime, __int64 * ASize,
  2087. bool TryWriteReadOnly)
  2088. {
  2089. int Attrs = 0;
  2090. HANDLE Handle = 0;
  2091. FILE_OPERATION_LOOP (FMTLOAD(FILE_NOT_EXISTS, (FileName)),
  2092. Attrs = FileGetAttr(FileName);
  2093. if (Attrs == -1) EXCEPTION;
  2094. )
  2095. if ((Attrs & faDirectory) == 0)
  2096. {
  2097. bool NoHandle = false;
  2098. if (!TryWriteReadOnly && (Access == GENERIC_WRITE) &&
  2099. ((Attrs & faReadOnly) != 0))
  2100. {
  2101. Access = GENERIC_READ;
  2102. NoHandle = true;
  2103. }
  2104. FILE_OPERATION_LOOP (FMTLOAD(OPENFILE_ERROR, (FileName)),
  2105. Handle = CreateFile(FileName.c_str(), Access,
  2106. Access == GENERIC_READ ? FILE_SHARE_READ | FILE_SHARE_WRITE : FILE_SHARE_READ,
  2107. NULL, OPEN_EXISTING, 0, 0);
  2108. if (Handle == INVALID_HANDLE_VALUE)
  2109. {
  2110. Handle = 0;
  2111. EXCEPTION;
  2112. }
  2113. );
  2114. try
  2115. {
  2116. if (AATime || AMTime || ACTime)
  2117. {
  2118. // Get last file access and modification time
  2119. FILE_OPERATION_LOOP (FMTLOAD(CANT_GET_ATTRS, (FileName)),
  2120. FILETIME ATime;
  2121. FILETIME MTime;
  2122. FILETIME CTime;
  2123. if (!GetFileTime(Handle, &CTime, &ATime, &MTime)) EXCEPTION;
  2124. if (ACTime)
  2125. {
  2126. *ACTime = ConvertTimestampToUnix(CTime, SessionData->ConsiderDST);
  2127. }
  2128. if (AATime)
  2129. {
  2130. *AATime = ConvertTimestampToUnix(ATime, SessionData->ConsiderDST);
  2131. }
  2132. if (AMTime)
  2133. {
  2134. *AMTime = ConvertTimestampToUnix(MTime, SessionData->ConsiderDST);
  2135. }
  2136. );
  2137. }
  2138. if (ASize)
  2139. {
  2140. // Get file size
  2141. FILE_OPERATION_LOOP (FMTLOAD(CANT_GET_ATTRS, (FileName)),
  2142. unsigned long LSize;
  2143. unsigned long HSize;
  2144. LSize = GetFileSize(Handle, &HSize);
  2145. if ((LSize == 0xFFFFFFFF) && (GetLastError() != NO_ERROR)) EXCEPTION;
  2146. *ASize = (__int64(HSize) << 32) + LSize;
  2147. );
  2148. }
  2149. if ((AHandle == NULL) || NoHandle)
  2150. {
  2151. CloseHandle(Handle);
  2152. Handle = NULL;
  2153. }
  2154. }
  2155. catch(...)
  2156. {
  2157. CloseHandle(Handle);
  2158. throw;
  2159. }
  2160. }
  2161. if (AAttrs) *AAttrs = Attrs;
  2162. if (AHandle) *AHandle = Handle;
  2163. }
  2164. //---------------------------------------------------------------------------
  2165. AnsiString __fastcall TTerminal::FileUrl(const AnsiString FileName)
  2166. {
  2167. return FFileSystem->FileUrl(FileName);
  2168. }
  2169. //---------------------------------------------------------------------------
  2170. void __fastcall TTerminal::MakeLocalFileList(const AnsiString FileName,
  2171. const TSearchRec Rec, void * Param)
  2172. {
  2173. TMakeLocalFileListParams & Params = *static_cast<TMakeLocalFileListParams*>(Param);
  2174. bool Directory = FLAGSET(Rec.Attr, faDirectory);
  2175. if (Directory && Params.Recursive)
  2176. {
  2177. ProcessLocalDirectory(FileName, MakeLocalFileList, &Params);
  2178. }
  2179. if (!Directory || Params.IncludeDirs)
  2180. {
  2181. Params.FileList->Add(FileName);
  2182. }
  2183. }
  2184. //---------------------------------------------------------------------------
  2185. void __fastcall TTerminal::CalculateLocalFileSize(const AnsiString FileName,
  2186. const TSearchRec Rec, /*TCalculateSizeParams*/ void * Params)
  2187. {
  2188. TCalculateSizeParams * AParams = static_cast<TCalculateSizeParams*>(Params);
  2189. bool Dir = FLAGSET(Rec.Attr, faDirectory);
  2190. if ((AParams->CopyParam == NULL) ||
  2191. AParams->CopyParam->AllowTransfer(FileName, osLocal, Dir))
  2192. {
  2193. if (!Dir)
  2194. {
  2195. AParams->Size +=
  2196. (static_cast<__int64>(Rec.FindData.nFileSizeHigh) << 32) +
  2197. Rec.FindData.nFileSizeLow;
  2198. }
  2199. else
  2200. {
  2201. ProcessLocalDirectory(FileName, CalculateLocalFileSize, Params);
  2202. }
  2203. }
  2204. if (OperationProgress && OperationProgress->Operation == foCalculateSize)
  2205. {
  2206. if (OperationProgress->Cancel != csContinue) Abort();
  2207. OperationProgress->SetFile(FileName);
  2208. }
  2209. }
  2210. //---------------------------------------------------------------------------
  2211. void __fastcall TTerminal::CalculateLocalFilesSize(TStrings * FileList,
  2212. __int64 & Size, const TCopyParamType * CopyParam)
  2213. {
  2214. TFileOperationProgressType OperationProgress(FOnProgress, FOnFinished);
  2215. bool DisconnectWhenComplete = false;
  2216. OperationProgress.Start(foCalculateSize, osLocal, FileList->Count);
  2217. try
  2218. {
  2219. TCalculateSizeParams Params;
  2220. Params.Size = 0;
  2221. Params.Params = 0;
  2222. Params.CopyParam = CopyParam;
  2223. assert(!FOperationProgress);
  2224. FOperationProgress = &OperationProgress;
  2225. TSearchRec Rec;
  2226. for (int Index = 0; Index < FileList->Count; Index++)
  2227. {
  2228. AnsiString FileName = FileList->Strings[Index];
  2229. if (FileSearchRec(FileName, Rec))
  2230. {
  2231. CalculateLocalFileSize(FileName, Rec, &Params);
  2232. OperationProgress.Finish(FileName, true, DisconnectWhenComplete);
  2233. }
  2234. }
  2235. Size = Params.Size;
  2236. }
  2237. __finally
  2238. {
  2239. FOperationProgress = NULL;
  2240. OperationProgress.Stop();
  2241. }
  2242. if (DisconnectWhenComplete)
  2243. {
  2244. CloseOnCompletion();
  2245. }
  2246. }
  2247. //---------------------------------------------------------------------------
  2248. struct TSynchronizeFileData
  2249. {
  2250. int Time;
  2251. int Attr;
  2252. __int64 Size;
  2253. FILETIME LastWriteTime;
  2254. bool Modified;
  2255. bool New;
  2256. const TRemoteFile * MatchingRemoteFile;
  2257. };
  2258. //---------------------------------------------------------------------------
  2259. const int sfFirstLevel = 0x01;
  2260. struct TSynchronizeData
  2261. {
  2262. AnsiString LocalDirectory;
  2263. AnsiString RemoteDirectory;
  2264. TTerminal::TSynchronizeMode Mode;
  2265. int Params;
  2266. TSynchronizeDirectory OnSynchronizeDirectory;
  2267. TSynchronizeStats * Stats;
  2268. TSynchronizeOptions * Options;
  2269. int Flags;
  2270. TStringList * LocalFileList;
  2271. TStringList * ModifiedRemoteFileList;
  2272. TStringList * NewRemoteFileList;
  2273. const TCopyParamType * CopyParam;
  2274. TStringList * MatchingLocalFiles;
  2275. };
  2276. //---------------------------------------------------------------------------
  2277. void __fastcall TTerminal::Synchronize(const AnsiString LocalDirectory,
  2278. const AnsiString RemoteDirectory, TSynchronizeMode Mode,
  2279. const TCopyParamType * CopyParam, int Params,
  2280. TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats,
  2281. TSynchronizeOptions * Options)
  2282. {
  2283. assert(CopyParam != NULL);
  2284. TCopyParamType SyncCopyParam = *CopyParam;
  2285. SyncCopyParam.PreserveTime = true;
  2286. BeginTransaction();
  2287. try
  2288. {
  2289. DoSynchronizeDirectory(LocalDirectory, RemoteDirectory, Mode,
  2290. &SyncCopyParam, Params, OnSynchronizeDirectory, Stats, Options, sfFirstLevel);
  2291. }
  2292. __finally
  2293. {
  2294. EndTransaction();
  2295. }
  2296. }
  2297. //---------------------------------------------------------------------------
  2298. void __fastcall TTerminal::DoSynchronizeProgress(const TSynchronizeData & Data)
  2299. {
  2300. bool Continue = true;
  2301. Data.OnSynchronizeDirectory(Data.LocalDirectory, Data.RemoteDirectory, Continue);
  2302. if (!Continue)
  2303. {
  2304. Abort();
  2305. }
  2306. }
  2307. //---------------------------------------------------------------------------
  2308. void __fastcall TTerminal::DoSynchronizeDirectory(const AnsiString LocalDirectory,
  2309. const AnsiString RemoteDirectory, TSynchronizeMode Mode,
  2310. const TCopyParamType * CopyParam, int Params,
  2311. TSynchronizeDirectory OnSynchronizeDirectory, TSynchronizeStats * Stats,
  2312. TSynchronizeOptions * Options, int Flags)
  2313. {
  2314. TSynchronizeData Data;
  2315. Data.LocalDirectory = IncludeTrailingBackslash(LocalDirectory);
  2316. Data.RemoteDirectory = UnixIncludeTrailingBackslash(RemoteDirectory);
  2317. Data.Mode = Mode;
  2318. Data.Params = Params;
  2319. Data.OnSynchronizeDirectory = OnSynchronizeDirectory;
  2320. Data.LocalFileList = NULL;
  2321. Data.NewRemoteFileList = NULL;
  2322. Data.ModifiedRemoteFileList = NULL;
  2323. Data.CopyParam = CopyParam;
  2324. Data.Stats = Stats;
  2325. Data.Options = Options;
  2326. Data.Flags = Flags;
  2327. TStrings * LocalFileList = NULL;
  2328. LogEvent(FORMAT("Synchronizing local directory '%s' with remote directory '%s', "
  2329. "mode = %d, params = %d", (LocalDirectory, RemoteDirectory,
  2330. int(Mode), int(Params))));
  2331. if (FLAGCLEAR(Params, spDelayProgress))
  2332. {
  2333. DoSynchronizeProgress(Data);
  2334. }
  2335. try
  2336. {
  2337. bool Found;
  2338. TSearchRec SearchRec;
  2339. Data.LocalFileList = new TStringList();
  2340. Data.LocalFileList->Sorted = true;
  2341. Data.LocalFileList->CaseSensitive = false;
  2342. Data.NewRemoteFileList = new TStringList();
  2343. Data.ModifiedRemoteFileList = new TStringList();
  2344. Data.MatchingLocalFiles = new TStringList();
  2345. LocalFileList = new TStringList();
  2346. FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (LocalDirectory)),
  2347. int FindAttrs = faReadOnly | faHidden | faSysFile | faDirectory | faArchive;
  2348. Found = (FindFirst(Data.LocalDirectory + "*.*", FindAttrs, SearchRec) == 0);
  2349. );
  2350. if (Found)
  2351. {
  2352. try
  2353. {
  2354. AnsiString FileName;
  2355. while (Found)
  2356. {
  2357. FileName = SearchRec.Name;
  2358. // add dirs for recursive mode or when we are interested in newly
  2359. // added subdirs
  2360. int FoundIndex;
  2361. if ((FileName != ".") && (FileName != "..") &&
  2362. (FLAGCLEAR(SearchRec.Attr, faDirectory) ||
  2363. FLAGCLEAR(Params, spNoRecurse) || FLAGSET(Params, spSubDirs)) &&
  2364. CopyParam->AllowTransfer(Data.LocalDirectory + FileName, osLocal,
  2365. FLAGSET(SearchRec.Attr, faDirectory)) &&
  2366. (FLAGCLEAR(Flags, sfFirstLevel) ||
  2367. (Options == NULL) || (Options->Filter == NULL) ||
  2368. Options->Filter->Find(FileName, FoundIndex)))
  2369. {
  2370. TSynchronizeFileData * FileData = new TSynchronizeFileData;
  2371. FileData->Time = SearchRec.Time;
  2372. FileData->Size =
  2373. (static_cast<__int64>(SearchRec.FindData.nFileSizeHigh) << 32) +
  2374. SearchRec.FindData.nFileSizeLow;
  2375. FileData->Attr = SearchRec.Attr;
  2376. FileData->LastWriteTime = SearchRec.FindData.ftLastWriteTime;
  2377. if (FLAGSET(SearchRec.Attr, faDirectory) && FLAGSET(Params, spSubDirs) &&
  2378. FLAGSET(Params, spExistingOnly))
  2379. {
  2380. // when "existing only" is set to "keep up to date" (non-recursive sync),
  2381. // we must still add local directory to list, so that the remote one
  2382. // is not deleted as obsolete, but must clear its "new" attribute
  2383. // not to upload it.
  2384. FileData->New = false;
  2385. }
  2386. else
  2387. {
  2388. FileData->New = true;
  2389. }
  2390. FileData->Modified = false;
  2391. FileData->MatchingRemoteFile = NULL;
  2392. Data.LocalFileList->AddObject(FileName,
  2393. reinterpret_cast<TObject*>(FileData));
  2394. }
  2395. FILE_OPERATION_LOOP (FMTLOAD(LIST_DIR_ERROR, (LocalDirectory)),
  2396. Found = (FindNext(SearchRec) == 0);
  2397. );
  2398. }
  2399. }
  2400. __finally
  2401. {
  2402. FindClose(SearchRec);
  2403. }
  2404. // can we expect that ProcessDirectory would take so little time
  2405. // that we can pospone showing progress window until anything actually happens?
  2406. bool Cached = FLAGSET(Params, spUseCache) && SessionData->CacheDirectories &&
  2407. FDirectoryCache->HasFileList(RemoteDirectory);
  2408. if (!Cached && FLAGSET(Params, spDelayProgress))
  2409. {
  2410. DoSynchronizeProgress(Data);
  2411. }
  2412. ProcessDirectory(RemoteDirectory, SynchronizeFile, &Data,
  2413. FLAGSET(Params, spUseCache));
  2414. int LocalDirs = 0;
  2415. TSynchronizeFileData * FileData;
  2416. for (int Index = 0; Index < Data.LocalFileList->Count; Index++)
  2417. {
  2418. FileData = reinterpret_cast<TSynchronizeFileData *>
  2419. (Data.LocalFileList->Objects[Index]);
  2420. // add local file either if we are going to upload it
  2421. // (i.e. if it is updated or we want to upload even new files)
  2422. // or if we are going to delete it (i.e. all "new"=obsolete files)
  2423. if ((FileData->Modified && ((Mode == smBoth) || (Mode == smRemote))) ||
  2424. (FileData->New &&
  2425. ((Mode == smLocal) ||
  2426. (FLAGCLEAR(Params, spExistingOnly) && FLAGCLEAR(Params, spTimestamp)))))
  2427. {
  2428. LocalFileList->AddObject(Data.LocalDirectory + Data.LocalFileList->Strings[Index],
  2429. (FLAGSET(Params, spTimestamp) ? reinterpret_cast<TObject*>(FileData) : NULL));
  2430. if (FLAGSET(FileData->Attr, faDirectory))
  2431. {
  2432. LocalDirs++;
  2433. assert(FileData->New && !FileData->Modified);
  2434. }
  2435. }
  2436. }
  2437. if (((Mode == smBoth) || (Mode == smLocal)) &&
  2438. FLAGCLEAR(Params, spExistingOnly))
  2439. {
  2440. Data.ModifiedRemoteFileList->AddStrings(Data.NewRemoteFileList);
  2441. }
  2442. int NewRemoteDirectories = 0;
  2443. if (Stats != NULL)
  2444. {
  2445. for (int Index = 0; Index < Data.NewRemoteFileList->Count; Index++)
  2446. {
  2447. if (static_cast<TRemoteFile*>(Data.NewRemoteFileList->Objects[Index])->IsDirectory)
  2448. {
  2449. NewRemoteDirectories++;
  2450. }
  2451. }
  2452. }
  2453. bool Delete = FLAGSET(Params, spDelete) && FLAGCLEAR(Params, spTimestamp);
  2454. bool Upload = (LocalFileList->Count > 0) &&
  2455. ((Mode == smBoth) || (Mode == smRemote));
  2456. bool DeleteLocal = (LocalFileList->Count > 0) &&
  2457. (Mode == smLocal) && Delete;
  2458. bool Download = (Data.ModifiedRemoteFileList->Count > 0) &&
  2459. ((Mode == smBoth) || (Mode == smLocal));
  2460. bool ObsoleteRemote = (Data.NewRemoteFileList->Count > 0) && (Mode == smRemote);
  2461. bool DeleteRemote = ObsoleteRemote && Delete;
  2462. if (Upload || DeleteLocal || Download || DeleteRemote)
  2463. {
  2464. // Update progress whenever we did not before directory read.
  2465. // This makes progress show actual directory where the change occur,
  2466. // not the last directory scanned (subdirecttories).
  2467. if (FLAGCLEAR(Params, spDelayProgress) || Cached)
  2468. {
  2469. DoSynchronizeProgress(Data);
  2470. }
  2471. if (FLAGSET(Params, spTimestamp))
  2472. {
  2473. assert(!DeleteLocal);
  2474. assert(!DeleteRemote);
  2475. if (Download)
  2476. {
  2477. assert(Data.ModifiedRemoteFileList->Count == Data.MatchingLocalFiles->Count);
  2478. ProcessFilesEx(Data.ModifiedRemoteFileList, foSetProperties,
  2479. SynchronizeLocalTimestamp, &Data, osLocal);
  2480. }
  2481. if (Upload)
  2482. {
  2483. ProcessFiles(LocalFileList, foSetProperties,
  2484. SynchronizeRemoteTimestamp, &Data);
  2485. }
  2486. }
  2487. else
  2488. {
  2489. int CopyParams = (Params & spNoConfirmation) != 0 ? cpNoConfirmation : 0;
  2490. TQueryParams QueryParams(0, HELP_SYNCHRONIZE);
  2491. if (Upload)
  2492. {
  2493. if ((FLAGSET(Params, spPreviewChanges) &&
  2494. (DoQueryUser(FMTLOAD(SYNCHRONIZE_UPLOAD, (LocalFileList->Count, LocalDirectory, RemoteDirectory)),
  2495. LocalFileList, qaYes | qaNo, &QueryParams) == qaNo)) ||
  2496. !CopyToRemote(LocalFileList, RemoteDirectory, CopyParam, CopyParams))
  2497. {
  2498. Abort();
  2499. }
  2500. if (Stats != NULL)
  2501. {
  2502. Stats->NewDirectories += LocalDirs;
  2503. }
  2504. }
  2505. if (DeleteLocal &&
  2506. ((FLAGSET(Params, spPreviewChanges) &&
  2507. (DoQueryUser(FMTLOAD(SYNCHRONIZE_LOCAL_DELETE, (LocalFileList->Count, LocalDirectory)),
  2508. LocalFileList, qaYes | qaNo, &QueryParams) == qaNo)) ||
  2509. !DeleteLocalFiles(LocalFileList)))
  2510. {
  2511. Abort();
  2512. }
  2513. if (Download &&
  2514. ((FLAGSET(Params, spPreviewChanges) &&
  2515. (DoQueryUser(FMTLOAD(SYNCHRONIZE_DOWNLOAD, (Data.ModifiedRemoteFileList->Count, RemoteDirectory, LocalDirectory)),
  2516. Data.ModifiedRemoteFileList, qaYes | qaNo, &QueryParams) == qaNo)) ||
  2517. !CopyToLocal(Data.ModifiedRemoteFileList, LocalDirectory, CopyParam, CopyParams)))
  2518. {
  2519. Abort();
  2520. }
  2521. if (DeleteRemote)
  2522. {
  2523. if (Stats != NULL)
  2524. {
  2525. Stats->RemovedDirectories += NewRemoteDirectories;
  2526. }
  2527. if ((FLAGSET(Params, spPreviewChanges) &&
  2528. (DoQueryUser(FMTLOAD(SYNCHRONIZE_REMOTE_DELETE, (Data.NewRemoteFileList->Count, RemoteDirectory)),
  2529. Data.NewRemoteFileList, qaYes | qaNo, &QueryParams) == qaNo)) ||
  2530. !DeleteFiles(Data.NewRemoteFileList))
  2531. {
  2532. Abort();
  2533. }
  2534. }
  2535. }
  2536. }
  2537. if (ObsoleteRemote && !DeleteRemote && (Stats != NULL))
  2538. {
  2539. Stats->ObsoleteDirectories += NewRemoteDirectories;
  2540. }
  2541. }
  2542. }
  2543. __finally
  2544. {
  2545. if (Data.LocalFileList != NULL)
  2546. {
  2547. for (int Index = 0; Index < Data.LocalFileList->Count; Index++)
  2548. {
  2549. TSynchronizeFileData * FileData = reinterpret_cast<TSynchronizeFileData*>
  2550. (Data.LocalFileList->Objects[Index]);
  2551. delete FileData->MatchingRemoteFile;
  2552. delete FileData;
  2553. }
  2554. delete Data.LocalFileList;
  2555. }
  2556. TStringList * FileList = Data.NewRemoteFileList;
  2557. while (FileList != Data.ModifiedRemoteFileList)
  2558. {
  2559. if (FileList != NULL)
  2560. {
  2561. for (int Index = 0; Index < FileList->Count; Index++)
  2562. {
  2563. delete static_cast<TRemoteFile*>(FileList->Objects[Index]);
  2564. }
  2565. delete FileList;
  2566. }
  2567. FileList = Data.ModifiedRemoteFileList;
  2568. }
  2569. delete LocalFileList;
  2570. delete Data.MatchingLocalFiles;
  2571. }
  2572. }
  2573. //---------------------------------------------------------------------------
  2574. void __fastcall TTerminal::SynchronizeLocalTimestamp(const AnsiString FileName,
  2575. const TRemoteFile * File, void * Param, int Index)
  2576. {
  2577. TSynchronizeData * Data = static_cast<TSynchronizeData *>(Param);
  2578. assert(Data != NULL);
  2579. AnsiString LocalFile = Data->LocalDirectory + Data->MatchingLocalFiles->Strings[Index];
  2580. FILE_OPERATION_LOOP (FMTLOAD(CANT_SET_ATTRS, (LocalFile)),
  2581. HANDLE Handle;
  2582. OpenLocalFile(LocalFile, GENERIC_WRITE, NULL, &Handle,
  2583. NULL, NULL, NULL, NULL);
  2584. FILETIME WrTime = DateTimeToFileTime(File->Modification,
  2585. SessionData->ConsiderDST);
  2586. bool Result = SetFileTime(Handle, NULL, NULL, &WrTime);
  2587. CloseHandle(Handle);
  2588. if (!Result)
  2589. {
  2590. Abort();
  2591. }
  2592. );
  2593. }
  2594. //---------------------------------------------------------------------------
  2595. void __fastcall TTerminal::SynchronizeRemoteTimestamp(const AnsiString FileName,
  2596. const TRemoteFile * File, void * Param)
  2597. {
  2598. TSynchronizeData * Data = static_cast<TSynchronizeData *>(Param);
  2599. assert(Data != NULL);
  2600. const TSynchronizeFileData * FileData =
  2601. reinterpret_cast<const TSynchronizeFileData *>(File);
  2602. const TRemoteFile * RemoteFile = FileData->MatchingRemoteFile;
  2603. assert(RemoteFile != NULL);
  2604. TRemoteProperties Properties;
  2605. Properties.Valid << vpModification;
  2606. Properties.Modification = ConvertTimestampToUnix(FileData->LastWriteTime,
  2607. SessionData->ConsiderDST);
  2608. ChangeFileProperties(Data->RemoteDirectory + RemoteFile->FileName,
  2609. RemoteFile, &Properties);
  2610. }
  2611. //---------------------------------------------------------------------------
  2612. void __fastcall TTerminal::SynchronizeFile(const AnsiString FileName,
  2613. const TRemoteFile * File, /*TSynchronizeData*/ void * Param)
  2614. {
  2615. TSynchronizeData * Data = static_cast<TSynchronizeData *>(Param);
  2616. int FoundIndex;
  2617. if (Data->CopyParam->AllowTransfer(
  2618. UnixExcludeTrailingBackslash(File->FullFileName), osRemote,
  2619. File->IsDirectory) &&
  2620. (FLAGCLEAR(Data->Flags, sfFirstLevel) ||
  2621. (Data->Options == NULL) || (Data->Options->Filter == NULL) ||
  2622. Data->Options->Filter->Find(File->FileName, FoundIndex)))
  2623. {
  2624. bool Modified = false;
  2625. int LocalIndex = Data->LocalFileList->IndexOf(File->FileName);
  2626. bool New = (LocalIndex < 0);
  2627. if (!New)
  2628. {
  2629. TSynchronizeFileData * LocalData =
  2630. reinterpret_cast<TSynchronizeFileData *>(Data->LocalFileList->Objects[LocalIndex]);
  2631. LocalData->New = false;
  2632. bool LocalDirectory = (LocalData->Attr & faDirectory) != 0;
  2633. if (File->IsDirectory != LocalDirectory)
  2634. {
  2635. LogEvent(FORMAT("%s is directory on one side, but file on the another",
  2636. (File->FileName)));
  2637. }
  2638. else if (!File->IsDirectory)
  2639. {
  2640. FILETIME LocalLastWriteTime;
  2641. SYSTEMTIME SystemLastWriteTime;
  2642. FileTimeToLocalFileTime(&LocalData->LastWriteTime, &LocalLastWriteTime);
  2643. FileTimeToSystemTime(&LocalLastWriteTime, &SystemLastWriteTime);
  2644. TDateTime LocalTime = SystemTimeToDateTime(SystemLastWriteTime);
  2645. TDateTime RemoteTime = File->Modification;
  2646. ReduceDateTimePrecision(LocalTime, File->ModificationFmt);
  2647. bool LocalModified = false;
  2648. // for spTimestamp+spBySize require that the file sizes are the same
  2649. // before comparing file time
  2650. bool TimeCompare =
  2651. FLAGCLEAR(Data->Params, spNotByTime) &&
  2652. (FLAGCLEAR(Data->Params, spTimestamp) ||
  2653. FLAGCLEAR(Data->Params, spBySize) ||
  2654. (LocalData->Size != File->Size));
  2655. if (TimeCompare &&
  2656. (CompareFileTime(LocalTime, RemoteTime) < 0))
  2657. {
  2658. if (FLAGCLEAR(Data->Params, spTimestamp) ||
  2659. (Data->Mode == smBoth) || (Data->Mode == smLocal))
  2660. {
  2661. Modified = true;
  2662. }
  2663. else
  2664. {
  2665. LocalModified = true;
  2666. }
  2667. }
  2668. else if (TimeCompare &&
  2669. (CompareFileTime(LocalTime, RemoteTime) > 0))
  2670. {
  2671. if (FLAGCLEAR(Data->Params, spTimestamp) ||
  2672. (Data->Mode == smBoth) || (Data->Mode == smRemote))
  2673. {
  2674. LocalModified = true;
  2675. }
  2676. else
  2677. {
  2678. Modified = true;
  2679. }
  2680. }
  2681. else if (FLAGSET(Data->Params, spBySize) &&
  2682. (LocalData->Size != File->Size) &&
  2683. FLAGCLEAR(Data->Params, spTimestamp))
  2684. {
  2685. Modified = true;
  2686. LocalModified = true;
  2687. }
  2688. if (LocalModified)
  2689. {
  2690. LocalData->Modified = true;
  2691. LocalData->MatchingRemoteFile = const_cast<TRemoteFile *>(File)->Duplicate();
  2692. }
  2693. }
  2694. else if (FLAGCLEAR(Data->Params, spNoRecurse))
  2695. {
  2696. DoSynchronizeDirectory(
  2697. Data->LocalDirectory + File->FileName,
  2698. Data->RemoteDirectory + File->FileName,
  2699. Data->Mode, Data->CopyParam, Data->Params, Data->OnSynchronizeDirectory,
  2700. Data->Stats, Data->Options, (Data->Flags & ~sfFirstLevel));
  2701. }
  2702. }
  2703. else
  2704. {
  2705. New = !File->IsDirectory || FLAGCLEAR(Data->Params, spNoRecurse) ||
  2706. FLAGSET(Data->Params, spSubDirs);
  2707. }
  2708. if (New || Modified)
  2709. {
  2710. assert(!New || !Modified);
  2711. if (New)
  2712. {
  2713. Data->NewRemoteFileList->AddObject(FileName,
  2714. const_cast<TRemoteFile *>(File)->Duplicate());
  2715. }
  2716. else
  2717. {
  2718. Data->ModifiedRemoteFileList->AddObject(FileName,
  2719. const_cast<TRemoteFile *>(File)->Duplicate());
  2720. assert(LocalIndex >= 0);
  2721. Data->MatchingLocalFiles->AddObject(
  2722. Data->LocalFileList->Strings[LocalIndex],
  2723. Data->LocalFileList->Objects[LocalIndex]);
  2724. }
  2725. }
  2726. }
  2727. }
  2728. //---------------------------------------------------------------------------
  2729. bool __fastcall TTerminal::CopyToRemote(TStrings * FilesToCopy,
  2730. const AnsiString TargetDir, const TCopyParamType * CopyParam, int Params)
  2731. {
  2732. assert(FFileSystem);
  2733. assert(FilesToCopy);
  2734. assert(IsCapable[fcNewerOnlyUpload] || FLAGCLEAR(Params, cpNewerOnly));
  2735. bool Result = false;
  2736. bool DisconnectWhenComplete = false;
  2737. try
  2738. {
  2739. __int64 Size;
  2740. if (CopyParam->CalculateSize)
  2741. {
  2742. // dirty trick: when moving, do not pass copy param to avoid exclude mask
  2743. CalculateLocalFilesSize(FilesToCopy, Size,
  2744. (FLAGCLEAR(Params, cpDelete) ? CopyParam : NULL));
  2745. }
  2746. TFileOperationProgressType OperationProgress(FOnProgress, FOnFinished);
  2747. OperationProgress.Start((Params & cpDelete ? foMove : foCopy), osLocal,
  2748. FilesToCopy->Count, Params & cpTemporary, TargetDir);
  2749. OperationProgress.YesToNewer = FLAGSET(Params, cpNewerOnly);
  2750. FOperationProgress = &OperationProgress;
  2751. try
  2752. {
  2753. if (CopyParam->CalculateSize)
  2754. {
  2755. OperationProgress.SetTotalSize(Size);
  2756. }
  2757. AnsiString UnlockedTargetDir = TranslateLockedPath(TargetDir, false);
  2758. BeginTransaction();
  2759. try
  2760. {
  2761. if (IsLogging())
  2762. {
  2763. LogEvent(FORMAT("Copying %d files/directories to remote directory "
  2764. "\"%s\"", (FilesToCopy->Count, TargetDir)));
  2765. LogEvent(CopyParam->LogStr);
  2766. }
  2767. FFileSystem->CopyToRemote(FilesToCopy, UnlockedTargetDir,
  2768. CopyParam, Params, &OperationProgress, DisconnectWhenComplete);
  2769. }
  2770. __finally
  2771. {
  2772. if (Active)
  2773. {
  2774. ReactOnCommand(fsCopyToRemote);
  2775. EndTransaction();
  2776. }
  2777. }
  2778. if (OperationProgress.Cancel == csContinue)
  2779. {
  2780. Result = true;
  2781. }
  2782. }
  2783. __finally
  2784. {
  2785. OperationProgress.Stop();
  2786. FOperationProgress = NULL;
  2787. }
  2788. }
  2789. catch (Exception &E)
  2790. {
  2791. CommandError(&E, LoadStr(TOREMOTE_COPY_ERROR));
  2792. DisconnectWhenComplete = false;
  2793. }
  2794. if (DisconnectWhenComplete) CloseOnCompletion();
  2795. return Result;
  2796. }
  2797. //---------------------------------------------------------------------------
  2798. bool __fastcall TTerminal::CopyToLocal(TStrings * FilesToCopy,
  2799. const AnsiString TargetDir, const TCopyParamType * CopyParam, int Params)
  2800. {
  2801. assert(FFileSystem);
  2802. // see scp.c: sink(), tolocal()
  2803. bool Result = false;
  2804. bool OwnsFileList = (FilesToCopy == NULL);
  2805. bool DisconnectWhenComplete = false;
  2806. try
  2807. {
  2808. if (OwnsFileList)
  2809. {
  2810. FilesToCopy = new TStringList();
  2811. FilesToCopy->Assign(Files->SelectedFiles);
  2812. }
  2813. BeginTransaction();
  2814. try
  2815. {
  2816. __int64 TotalSize;
  2817. bool TotalSizeKnown = false;
  2818. TFileOperationProgressType OperationProgress(FOnProgress, FOnFinished);
  2819. if (CopyParam->CalculateSize)
  2820. {
  2821. ExceptionOnFail = true;
  2822. try
  2823. {
  2824. // dirty trick: when moving, do not pass copy param to avoid exclude mask
  2825. CalculateFilesSize(FilesToCopy, TotalSize, csIgnoreErrors,
  2826. (FLAGCLEAR(Params, cpDelete) ? CopyParam : NULL));
  2827. TotalSizeKnown = true;
  2828. }
  2829. __finally
  2830. {
  2831. ExceptionOnFail = false;
  2832. }
  2833. }
  2834. OperationProgress.Start((Params & cpDelete ? foMove : foCopy), osRemote,
  2835. FilesToCopy->Count, Params & cpTemporary, TargetDir);
  2836. OperationProgress.YesToNewer = FLAGSET(Params, cpNewerOnly);
  2837. FOperationProgress = &OperationProgress;
  2838. try
  2839. {
  2840. if (TotalSizeKnown)
  2841. {
  2842. OperationProgress.SetTotalSize(TotalSize);
  2843. }
  2844. try
  2845. {
  2846. try
  2847. {
  2848. FFileSystem->CopyToLocal(FilesToCopy, TargetDir, CopyParam, Params,
  2849. &OperationProgress, DisconnectWhenComplete);
  2850. }
  2851. __finally
  2852. {
  2853. if (Active)
  2854. {
  2855. ReactOnCommand(fsCopyToLocal);
  2856. }
  2857. }
  2858. }
  2859. catch (Exception &E)
  2860. {
  2861. CommandError(&E, LoadStr(TOLOCAL_COPY_ERROR));
  2862. DisconnectWhenComplete = false;
  2863. }
  2864. if (OperationProgress.Cancel == csContinue)
  2865. {
  2866. Result = true;
  2867. }
  2868. }
  2869. __finally
  2870. {
  2871. FOperationProgress = NULL;
  2872. OperationProgress.Stop();
  2873. }
  2874. }
  2875. __finally
  2876. {
  2877. // If session is still active (no fatal error) we reload directory
  2878. // by calling EndTransaction
  2879. EndTransaction();
  2880. }
  2881. }
  2882. __finally
  2883. {
  2884. if (OwnsFileList) delete FilesToCopy;
  2885. }
  2886. if (DisconnectWhenComplete) CloseOnCompletion();
  2887. return Result;
  2888. }
  2889. //---------------------------------------------------------------------------
  2890. __fastcall TSecondaryTerminal::TSecondaryTerminal(TTerminal * MainTerminal) :
  2891. TTerminal(), FMainTerminal(MainTerminal), FMasterPasswordTried(false)
  2892. {
  2893. assert(FMainTerminal != NULL);
  2894. }
  2895. //---------------------------------------------------------------------------
  2896. void __fastcall TSecondaryTerminal::DirectoryLoaded(TRemoteFileList * FileList)
  2897. {
  2898. FMainTerminal->DirectoryLoaded(FileList);
  2899. assert(FileList != NULL);
  2900. }
  2901. //---------------------------------------------------------------------------
  2902. void __fastcall TSecondaryTerminal::DirectoryModified(const AnsiString Path,
  2903. bool SubDirs)
  2904. {
  2905. // clear cache of main terminal
  2906. FMainTerminal->DirectoryModified(Path, SubDirs);
  2907. }
  2908. //---------------------------------------------------------------------------
  2909. bool __fastcall TSecondaryTerminal::DoPromptUser(AnsiString Prompt,
  2910. TPromptKind Kind, AnsiString & Response)
  2911. {
  2912. bool Result = false;
  2913. if (!FMasterPasswordTried)
  2914. {
  2915. // let's expect that the main session is already authenticated and its password
  2916. // is not written after, so no locking is necessary
  2917. Response = FMainTerminal->Password;
  2918. if (!Response.IsEmpty())
  2919. {
  2920. Result = true;
  2921. }
  2922. FMasterPasswordTried = true;
  2923. }
  2924. if (!Result)
  2925. {
  2926. Result = TTerminal::DoPromptUser(Prompt, Kind, Response);
  2927. }
  2928. return Result;
  2929. }
  2930. //---------------------------------------------------------------------------
  2931. void __fastcall TSecondaryTerminal::SetSessionData(TSessionData * value)
  2932. {
  2933. TTerminal::SetSessionData(value);
  2934. SessionData->NonPersistant();
  2935. }
  2936. //---------------------------------------------------------------------------
  2937. __fastcall TTerminalList::TTerminalList(TConfiguration * AConfiguration) :
  2938. TObjectList()
  2939. {
  2940. assert(AConfiguration);
  2941. FConfiguration = AConfiguration;
  2942. }
  2943. //---------------------------------------------------------------------------
  2944. __fastcall TTerminalList::~TTerminalList()
  2945. {
  2946. assert(Count == 0);
  2947. }
  2948. //---------------------------------------------------------------------------
  2949. TTerminal * __fastcall TTerminalList::NewTerminal(TSessionData * Data)
  2950. {
  2951. TTerminal * Terminal = new TTerminal();
  2952. try
  2953. {
  2954. Terminal->Configuration = FConfiguration;
  2955. Terminal->SessionData = Data;
  2956. Add(Terminal);
  2957. }
  2958. catch(...)
  2959. {
  2960. delete Terminal;
  2961. throw;
  2962. }
  2963. return Terminal;
  2964. }
  2965. //---------------------------------------------------------------------------
  2966. void __fastcall TTerminalList::FreeTerminal(TTerminal * Terminal)
  2967. {
  2968. assert(IndexOf(Terminal) >= 0);
  2969. Remove(Terminal);
  2970. }
  2971. //---------------------------------------------------------------------------
  2972. void __fastcall TTerminalList::FreeAndNullTerminal(TTerminal * & Terminal)
  2973. {
  2974. TTerminal * T = Terminal;
  2975. Terminal = NULL;
  2976. FreeTerminal(T);
  2977. }
  2978. //---------------------------------------------------------------------------
  2979. TTerminal * __fastcall TTerminalList::GetTerminal(int Index)
  2980. {
  2981. return dynamic_cast<TTerminal *>(Items[Index]);
  2982. }
  2983. //---------------------------------------------------------------------------
  2984. int __fastcall TTerminalList::GetActiveCount()
  2985. {
  2986. int Result = 0;
  2987. TTerminal * Terminal;
  2988. for (int i = 0; i < Count; i++)
  2989. {
  2990. Terminal = Terminals[i];
  2991. if (Terminal->Active)
  2992. {
  2993. Result++;
  2994. }
  2995. }
  2996. return Result;
  2997. }
  2998. //---------------------------------------------------------------------------
  2999. void __fastcall TTerminalList::Idle()
  3000. {
  3001. TTerminal * Terminal;
  3002. for (int i = 0; i < Count; i++)
  3003. {
  3004. Terminal = Terminals[i];
  3005. if (Terminal->Status == sshReady)
  3006. {
  3007. Terminal->Idle();
  3008. }
  3009. }
  3010. }