S3FileSystem.cpp 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #define NE_LFS
  5. #define WINSCP
  6. #define NEED_LIBS3
  7. #include "S3FileSystem.h"
  8. #include "SessionData.h"
  9. #include "Interface.h"
  10. #include "Common.h"
  11. #include "Exceptions.h"
  12. #include "Terminal.h"
  13. #include "TextsCore.h"
  14. #include "HelpCore.h"
  15. #include "NeonIntf.h"
  16. #include <ne_request.h>
  17. //---------------------------------------------------------------------------
  18. #pragma package(smart_init)
  19. //---------------------------------------------------------------------------
  20. #define StrFromS3(S) StrFromNeon(S)
  21. #define StrToS3(S) StrToNeon(S)
  22. //---------------------------------------------------------------------------
  23. #define FILE_OPERATION_LOOP_TERMINAL FTerminal
  24. //---------------------------------------------------------------------------
  25. const int tfFirstLevel = 0x01;
  26. //---------------------------------------------------------------------------
  27. static std::unique_ptr<TCriticalSection> LibS3Section(TraceInitPtr(new TCriticalSection()));
  28. //---------------------------------------------------------------------------
  29. UTF8String LibS3Delimiter(L"/");
  30. //---------------------------------------------------------------------------
  31. UnicodeString __fastcall S3LibVersion()
  32. {
  33. return FORMAT(L"%s.%s", (LIBS3_VER_MAJOR, LIBS3_VER_MINOR));
  34. }
  35. //---------------------------------------------------------------------------
  36. UnicodeString __fastcall S3LibDefaultHostName()
  37. {
  38. return UnicodeString(S3_DEFAULT_HOSTNAME);
  39. }
  40. //---------------------------------------------------------------------------
  41. //---------------------------------------------------------------------------
  42. TS3FileSystem::TS3FileSystem(TTerminal * ATerminal) :
  43. TCustomFileSystem(ATerminal),
  44. FActive(false)
  45. {
  46. FFileSystemInfo.ProtocolBaseName = L"S3";
  47. FFileSystemInfo.ProtocolName = FFileSystemInfo.ProtocolBaseName;
  48. S3_create_request_context(&FRequestContext);
  49. S3_set_request_context_session_callback(FRequestContext, LibS3SessionCallback, this);
  50. S3_set_request_context_ssl_callback(FRequestContext, LibS3SslCallback, this);
  51. S3_set_request_context_response_data_callback(FRequestContext, LibS3ResponseDataCallback, this);
  52. }
  53. //---------------------------------------------------------------------------
  54. __fastcall TS3FileSystem::~TS3FileSystem()
  55. {
  56. S3_destroy_request_context(FRequestContext);
  57. FRequestContext = NULL;
  58. UnregisterFromNeonDebug(FTerminal);
  59. }
  60. //---------------------------------------------------------------------------
  61. void __fastcall TS3FileSystem::Open()
  62. {
  63. FTlsVersionStr = L"";
  64. FNeonSession = NULL;
  65. FCurrentDirectory = L"";
  66. RequireNeon(FTerminal);
  67. FTerminal->Information(LoadStr(STATUS_CONNECT), true);
  68. TSessionData * Data = FTerminal->SessionData;
  69. FSessionInfo.LoginTime = Now();
  70. FLibS3Protocol = (Data->Ftps != ftpsNone) ? S3ProtocolHTTPS : S3ProtocolHTTP;
  71. UnicodeString AccessKeyId = Data->UserNameExpanded;
  72. if (AccessKeyId.IsEmpty())
  73. {
  74. if (!FTerminal->PromptUser(Data, pkUserName, LoadStr(S3_ACCESS_KEY_ID_TITLE), L"",
  75. LoadStr(S3_ACCESS_KEY_ID_PROMPT), true, 0, AccessKeyId))
  76. {
  77. // note that we never get here actually
  78. throw Exception(L"");
  79. }
  80. }
  81. FAccessKeyId = UTF8String(AccessKeyId);
  82. UnicodeString SecretAccessKey = UTF8String(Data->Password);
  83. if (SecretAccessKey.IsEmpty())
  84. {
  85. if (!FTerminal->PromptUser(Data, pkPassword, LoadStr(S3_SECRET_ACCESS_KEY_TITLE), L"",
  86. LoadStr(S3_SECRET_ACCESS_KEY_PROMPT), false, 0, SecretAccessKey))
  87. {
  88. // note that we never get here actually
  89. throw Exception(L"");
  90. }
  91. }
  92. FSecretAccessKey = UTF8String(SecretAccessKey);
  93. FHostName = UTF8String(Data->HostNameExpanded);
  94. FTimeout = Data->Timeout;
  95. RegisterForNeonDebug(FTerminal);
  96. UpdateNeonDebugMask();
  97. {
  98. TGuard Guard(LibS3Section.get());
  99. S3_initialize(NULL, S3_INIT_ALL, NULL);
  100. }
  101. FActive = false;
  102. try
  103. {
  104. TryOpenDirectory(ROOTDIRECTORY);
  105. }
  106. catch (Exception & E)
  107. {
  108. LibS3Deinitialize();
  109. FTerminal->Closed();
  110. FTerminal->FatalError(&E, LoadStr(CONNECTION_FAILED));
  111. }
  112. FActive = true;
  113. }
  114. //---------------------------------------------------------------------------
  115. struct TLibS3CallbackData
  116. {
  117. TLibS3CallbackData()
  118. {
  119. Status = (S3Status)-1;
  120. }
  121. TS3FileSystem * FileSystem;
  122. S3Status Status;
  123. UnicodeString RegionDetail;
  124. UnicodeString EndpointDetail;
  125. UnicodeString ErrorMessage;
  126. UnicodeString ErrorDetails;
  127. };
  128. //---------------------------------------------------------------------------
  129. TS3FileSystem * TS3FileSystem::GetFileSystem(void * CallbackData)
  130. {
  131. return static_cast<TLibS3CallbackData *>(CallbackData)->FileSystem;
  132. }
  133. //---------------------------------------------------------------------------
  134. void TS3FileSystem::LibS3SessionCallback(ne_session_s * Session, void * CallbackData)
  135. {
  136. TS3FileSystem * FileSystem = static_cast<TS3FileSystem *>(CallbackData);
  137. TSessionData * Data = FileSystem->FTerminal->SessionData;
  138. InitNeonSession(
  139. Session, Data->ProxyMethod, Data->ProxyHost, Data->ProxyPort,
  140. Data->ProxyUsername, Data->ProxyPassword, FileSystem->FTerminal);
  141. SetNeonTlsInit(Session, FileSystem->InitSslSession);
  142. // Data->Timeout is propagated via timeoutMs parameter of functions like S3_list_service
  143. FileSystem->FNeonSession = Session;
  144. }
  145. //------------------------------------------------------------------------------
  146. void TS3FileSystem::InitSslSession(ssl_st * Ssl, ne_session * /*Session*/)
  147. {
  148. // See also CAsyncSslSocketLayer::InitSSLConnection
  149. SetupSsl(Ssl, FTerminal->SessionData->MinTlsVersion, FTerminal->SessionData->MaxTlsVersion);
  150. }
  151. //---------------------------------------------------------------------------
  152. int TS3FileSystem::LibS3SslCallback(int Failures, const ne_ssl_certificate_s * Certificate, void * CallbackData)
  153. {
  154. TNeonCertificateData Data;
  155. RetrieveNeonCertificateData(Failures, Certificate, Data);
  156. TS3FileSystem * FileSystem = static_cast<TS3FileSystem *>(CallbackData);
  157. return FileSystem->VerifyCertificate(Data) ? NE_OK : NE_ERROR;
  158. }
  159. //---------------------------------------------------------------------------
  160. // Similar to TWebDAVFileSystem::VerifyCertificate
  161. bool TS3FileSystem::VerifyCertificate(TNeonCertificateData Data)
  162. {
  163. FSessionInfo.CertificateFingerprint = Data.Fingerprint;
  164. bool Result;
  165. if (FTerminal->SessionData->FingerprintScan)
  166. {
  167. Result = false;
  168. }
  169. else
  170. {
  171. FTerminal->LogEvent(CertificateVerificationMessage(Data));
  172. UnicodeString SiteKey = TSessionData::FormatSiteKey(FTerminal->SessionData->HostNameExpanded, FTerminal->SessionData->PortNumber);
  173. Result =
  174. FTerminal->VerifyCertificate(HttpsCertificateStorageKey, SiteKey, Data.Fingerprint, Data.Subject, Data.Failures);
  175. if (!Result)
  176. {
  177. UnicodeString Message;
  178. Result = NeonWindowsValidateCertificateWithMessage(Data, Message);
  179. FTerminal->LogEvent(Message);
  180. }
  181. FSessionInfo.Certificate = CertificateSummary(Data, FTerminal->SessionData->HostNameExpanded);
  182. if (!Result)
  183. {
  184. Result = FTerminal->ConfirmCertificate(FSessionInfo, Data.Failures, HttpsCertificateStorageKey, true);
  185. }
  186. if (Result)
  187. {
  188. CollectTLSSessionInfo();
  189. }
  190. }
  191. return Result;
  192. }
  193. //------------------------------------------------------------------------------
  194. void TS3FileSystem::CollectTLSSessionInfo()
  195. {
  196. // See also TFTPFileSystem::Open().
  197. // Have to cache the value as the connection (the neon HTTP session, not "our" session)
  198. // can be closed at the time we need it in CollectUsage().
  199. UnicodeString Message = NeonTlsSessionInfo(FNeonSession, FSessionInfo, FTlsVersionStr);
  200. FTerminal->LogEvent(Message);
  201. }
  202. //---------------------------------------------------------------------------
  203. S3Status TS3FileSystem::LibS3ResponsePropertiesCallback(const S3ResponseProperties * /*Properties*/, void * /*CallbackData*/)
  204. {
  205. // TODO
  206. return S3StatusOK;
  207. }
  208. //---------------------------------------------------------------------------
  209. void TS3FileSystem::LibS3ResponseDataCallback(const char * Data, size_t Size, void * CallbackData)
  210. {
  211. TS3FileSystem * FileSystem = static_cast<TS3FileSystem *>(CallbackData);
  212. if (FileSystem->FTerminal->Log->Logging)
  213. {
  214. UnicodeString Content = UnicodeString(UTF8String(Data, Size)).Trim();
  215. FileSystem->FResponse += Content;
  216. }
  217. }
  218. //---------------------------------------------------------------------------
  219. void TS3FileSystem::LibS3ResponseCompleteCallback(S3Status Status, const S3ErrorDetails * Error, void * CallbackData)
  220. {
  221. TLibS3CallbackData & Data = *static_cast<TLibS3CallbackData *>(CallbackData);
  222. TS3FileSystem * FileSystem = Data.FileSystem;
  223. Data.Status = Status;
  224. Data.RegionDetail = L"";
  225. Data.EndpointDetail = L"";
  226. Data.ErrorMessage = L"";
  227. Data.ErrorDetails = L"";
  228. if (Error != NULL)
  229. {
  230. if (Error->message != NULL)
  231. {
  232. Data.ErrorMessage = StrFromS3(Error->message);
  233. FileSystem->FTerminal->LogEvent(Data.ErrorMessage);
  234. }
  235. UnicodeString ErrorDetails;
  236. if (Error->resource != NULL)
  237. {
  238. AddToList(ErrorDetails, FMTLOAD(S3_ERROR_RESOURCE, (StrFromS3(Error->resource))), L"\n");
  239. }
  240. if (Error->furtherDetails != NULL)
  241. {
  242. AddToList(ErrorDetails, FMTLOAD(S3_ERROR_FURTHER_DETAILS, (StrFromS3(Error->furtherDetails))), L"\n");
  243. }
  244. if (Error->extraDetailsCount)
  245. {
  246. UnicodeString ExtraDetails;
  247. for (int I = 0; I < Error->extraDetailsCount; I++)
  248. {
  249. UnicodeString DetailName = StrFromS3(Error->extraDetails[I].name);
  250. UnicodeString DetailValue = StrFromS3(Error->extraDetails[I].value);
  251. if (SameText(DetailName, L"Region"))
  252. {
  253. Data.RegionDetail = DetailValue;
  254. }
  255. else if (SameText(DetailName, L"Endpoint"))
  256. {
  257. Data.EndpointDetail = DetailValue;
  258. }
  259. AddToList(ExtraDetails, FORMAT(L"%s: %s", (DetailName, DetailValue)), L", ");
  260. }
  261. AddToList(ErrorDetails, LoadStr(S3_ERROR_EXTRA_DETAILS) + ExtraDetails, L"\n");
  262. }
  263. if (!ErrorDetails.IsEmpty())
  264. {
  265. FileSystem->FTerminal->LogEvent(ErrorDetails);
  266. Data.ErrorDetails = ErrorDetails;
  267. }
  268. }
  269. if (!FileSystem->FResponse.IsEmpty())
  270. {
  271. FileSystem->FTerminal->Log->Add(llOutput, FileSystem->FResponse);
  272. }
  273. }
  274. //---------------------------------------------------------------------------
  275. void TS3FileSystem::RequestInit()
  276. {
  277. FResponse = L"";
  278. }
  279. //---------------------------------------------------------------------------
  280. bool TS3FileSystem::RetryBucket(const TLibS3CallbackData & Data, const UnicodeString & BucketName)
  281. {
  282. bool Result = false;
  283. UnicodeString EndpointDetail = Data.EndpointDetail;
  284. if ((Data.Status == S3StatusErrorAuthorizationHeaderMalformed) &&
  285. (GetBucketRegion(BucketName) != Data.RegionDetail))
  286. {
  287. FTerminal->LogEvent(FORMAT("Will use region \"%s\" for bucket \"%s\" from now on.", (Data.RegionDetail, BucketName)));
  288. FRegions.insert(std::make_pair(BucketName, Data.RegionDetail));
  289. Result = true;
  290. }
  291. // happens with newly created buckets
  292. else if ((Data.Status == S3StatusErrorTemporaryRedirect) && !Data.EndpointDetail.IsEmpty())
  293. {
  294. UnicodeString HostName = Data.EndpointDetail;
  295. if (SameText(HostName.SubString(1, BucketName.Length() + 1), BucketName + L"."))
  296. {
  297. HostName.Delete(1, BucketName.Length() + 1);
  298. }
  299. if (GetBucketHostName(BucketName) != HostName)
  300. {
  301. FTerminal->LogEvent(FORMAT("Will use endpoint \"%s\" for bucket \"%s\" from now on.", (HostName, BucketName)));
  302. FHostNames.insert(std::make_pair(BucketName, HostName));
  303. Result = true;
  304. }
  305. }
  306. return Result;
  307. }
  308. //---------------------------------------------------------------------------
  309. void TS3FileSystem::CheckLibS3Error(const TLibS3CallbackData & Data)
  310. {
  311. if (Data.Status != S3StatusOK)
  312. {
  313. UnicodeString Error, Details;
  314. switch (Data.Status)
  315. {
  316. case S3StatusErrorAccessDenied:
  317. Error = LoadStr(S3_STATUS_ACCESS_DENIED);
  318. break;
  319. // While it can mean an implementation fault, it will typically mean a wrong secure key.
  320. case S3StatusErrorSignatureDoesNotMatch:
  321. Error = LoadStr(AUTHENTICATION_FAILED);
  322. break;
  323. }
  324. if (!Error.IsEmpty())
  325. {
  326. Details = Data.ErrorMessage;
  327. AddToList(Details, Data.ErrorDetails, L"\n");
  328. }
  329. else
  330. {
  331. if (!Data.ErrorMessage.IsEmpty())
  332. {
  333. Error = Data.ErrorMessage;
  334. }
  335. else
  336. {
  337. // only returns name of the S3 status code symbol, like S3StatusErrorAccountProblem,
  338. // not something we should really display to an user, but still better than an internal error code
  339. Error = S3_get_status_name(Data.Status);
  340. }
  341. Details = Data.ErrorDetails;
  342. }
  343. throw ExtException(MainInstructions(Error), Details);
  344. }
  345. }
  346. //---------------------------------------------------------------------------
  347. void TS3FileSystem::LibS3Deinitialize()
  348. {
  349. TGuard Guard(LibS3Section.get());
  350. S3_deinitialize();
  351. }
  352. //---------------------------------------------------------------------------
  353. void TS3FileSystem::ParsePath(UnicodeString Path, UnicodeString & BucketName, UnicodeString & Prefix)
  354. {
  355. if (DebugAlwaysTrue(Path.SubString(1, 1) == L"/"))
  356. {
  357. Path.Delete(1, 1);
  358. }
  359. int P = Path.Pos(L"/");
  360. UnicodeString Result;
  361. if (P == 0)
  362. {
  363. BucketName = Path;
  364. Prefix = L"";
  365. }
  366. else
  367. {
  368. BucketName = Path.SubString(0, P - 1);
  369. Prefix = Path.SubString(P + 1, Path.Length() - P) + L"/";
  370. }
  371. }
  372. //---------------------------------------------------------------------------
  373. UnicodeString TS3FileSystem::GetBucketRegion(const UnicodeString & BucketName)
  374. {
  375. TRegions::const_iterator I = FRegions.find(BucketName);
  376. UnicodeString Result;
  377. if (I != FRegions.end())
  378. {
  379. Result = I->second;
  380. }
  381. else
  382. {
  383. Result = StrFromS3(S3_DEFAULT_REGION);
  384. }
  385. return Result;
  386. }
  387. //---------------------------------------------------------------------------
  388. UnicodeString TS3FileSystem::GetBucketHostName(const UnicodeString & BucketName)
  389. {
  390. TRegions::const_iterator I = FHostNames.find(BucketName);
  391. UnicodeString Result;
  392. if (I != FHostNames.end())
  393. {
  394. Result = I->second;
  395. }
  396. else
  397. {
  398. Result = UnicodeString(FHostName);
  399. }
  400. return Result;
  401. }
  402. //---------------------------------------------------------------------------
  403. void __fastcall TS3FileSystem::Close()
  404. {
  405. DebugAssert(FActive);
  406. LibS3Deinitialize();
  407. FTerminal->Closed();
  408. FActive = false;
  409. UnregisterFromNeonDebug(FTerminal);
  410. }
  411. //---------------------------------------------------------------------------
  412. bool __fastcall TS3FileSystem::GetActive()
  413. {
  414. return FActive;
  415. }
  416. //---------------------------------------------------------------------------
  417. void __fastcall TS3FileSystem::CollectUsage()
  418. {
  419. // noop
  420. }
  421. //---------------------------------------------------------------------------
  422. const TSessionInfo & __fastcall TS3FileSystem::GetSessionInfo()
  423. {
  424. return FSessionInfo;
  425. }
  426. //---------------------------------------------------------------------------
  427. const TFileSystemInfo & __fastcall TS3FileSystem::GetFileSystemInfo(bool /*Retrieve*/)
  428. {
  429. return FFileSystemInfo;
  430. }
  431. //---------------------------------------------------------------------------
  432. bool __fastcall TS3FileSystem::TemporaryTransferFile(const UnicodeString & /*FileName*/)
  433. {
  434. return false;
  435. }
  436. //---------------------------------------------------------------------------
  437. bool __fastcall TS3FileSystem::GetStoredCredentialsTried()
  438. {
  439. // if we have one, we always try it
  440. return !FTerminal->SessionData->Password.IsEmpty();
  441. }
  442. //---------------------------------------------------------------------------
  443. UnicodeString __fastcall TS3FileSystem::GetUserName()
  444. {
  445. return UnicodeString(FAccessKeyId);
  446. }
  447. //---------------------------------------------------------------------------
  448. void __fastcall TS3FileSystem::Idle()
  449. {
  450. // noop
  451. }
  452. //---------------------------------------------------------------------------
  453. UnicodeString __fastcall TS3FileSystem::AbsolutePath(const UnicodeString Path, bool /*Local*/)
  454. {
  455. if (UnixIsAbsolutePath(Path))
  456. {
  457. return Path;
  458. }
  459. else
  460. {
  461. return ::AbsolutePath(FCurrentDirectory, Path);
  462. }
  463. }
  464. //---------------------------------------------------------------------------
  465. bool __fastcall TS3FileSystem::IsCapable(int Capability) const
  466. {
  467. DebugAssert(FTerminal);
  468. switch (Capability)
  469. {
  470. // Only to make double-click on file edit/open the file,
  471. // instead of trying to open it as directory
  472. case fcResolveSymlink:
  473. return true;
  474. case fcRename:
  475. case fcRemoteMove:
  476. case fcMoveToQueue:
  477. case fcPreservingTimestampUpload:
  478. case fcCheckingSpaceAvailable:
  479. case fsSkipTransfer:
  480. case fsParallelTransfers:
  481. case fcRemoteCopy:
  482. case fcUserGroupListing:
  483. case fcModeChanging:
  484. case fcModeChangingUpload:
  485. case fcGroupChanging:
  486. case fcOwnerChanging:
  487. case fcAnyCommand:
  488. case fcShellAnyCommand:
  489. case fcHardLink:
  490. case fcSymbolicLink:
  491. case fcTextMode:
  492. case fcNativeTextMode:
  493. case fcNewerOnlyUpload:
  494. case fcTimestampChanging:
  495. case fcLoadingAdditionalProperties:
  496. case fcIgnorePermErrors:
  497. case fcCalculatingChecksum:
  498. case fcSecondaryShell:
  499. case fcGroupOwnerChangingByID:
  500. case fcRemoveCtrlZUpload:
  501. case fcRemoveBOMUpload:
  502. case fcPreservingTimestampDirs:
  503. case fcResumeSupport:
  504. case fcChangePassword:
  505. case fcLocking:
  506. return false;
  507. default:
  508. DebugFail();
  509. return false;
  510. }
  511. }
  512. //---------------------------------------------------------------------------
  513. UnicodeString __fastcall TS3FileSystem::GetCurrentDirectory()
  514. {
  515. return FCurrentDirectory;
  516. }
  517. //---------------------------------------------------------------------------
  518. void __fastcall TS3FileSystem::DoStartup()
  519. {
  520. FTerminal->SetExceptionOnFail(true);
  521. // retrieve initialize working directory to save it as home directory
  522. ReadCurrentDirectory();
  523. FTerminal->SetExceptionOnFail(false);
  524. }
  525. //---------------------------------------------------------------------------
  526. void __fastcall TS3FileSystem::LookupUsersGroups()
  527. {
  528. DebugFail();
  529. }
  530. //---------------------------------------------------------------------------
  531. void __fastcall TS3FileSystem::ReadCurrentDirectory()
  532. {
  533. if (FCachedDirectoryChange.IsEmpty())
  534. {
  535. FCurrentDirectory = FCurrentDirectory.IsEmpty() ? UnicodeString(L"/") : FCurrentDirectory;
  536. }
  537. else
  538. {
  539. FCurrentDirectory = FCachedDirectoryChange;
  540. FCachedDirectoryChange = L"";
  541. }
  542. }
  543. //---------------------------------------------------------------------------
  544. void __fastcall TS3FileSystem::HomeDirectory()
  545. {
  546. ChangeDirectory(L"/");
  547. }
  548. //---------------------------------------------------------------------------
  549. void __fastcall TS3FileSystem::AnnounceFileListOperation()
  550. {
  551. // noop
  552. }
  553. //---------------------------------------------------------------------------
  554. void TS3FileSystem::TryOpenDirectory(const UnicodeString & Directory)
  555. {
  556. FTerminal->LogEvent(FORMAT(L"Trying to open directory \"%s\".", (Directory)));
  557. std::unique_ptr<TRemoteFileList> FileList(new TRemoteFileList());
  558. ReadDirectoryInternal(Directory, FileList.get(), 1, UnicodeString());
  559. }
  560. //---------------------------------------------------------------------------
  561. void __fastcall TS3FileSystem::ChangeDirectory(const UnicodeString ADirectory)
  562. {
  563. UnicodeString Path = AbsolutePath(ADirectory, false);
  564. // to verify existence of directory try to open it
  565. TryOpenDirectory(Path);
  566. // if open dir did not fail, directory exists -> success.
  567. FCachedDirectoryChange = Path;
  568. }
  569. //---------------------------------------------------------------------------
  570. void __fastcall TS3FileSystem::CachedChangeDirectory(const UnicodeString Directory)
  571. {
  572. FCachedDirectoryChange = UnixExcludeTrailingBackslash(Directory);
  573. }
  574. //---------------------------------------------------------------------------
  575. TRemoteToken TS3FileSystem::MakeRemoteToken(const char * OwnerId, const char * OwnerDisplayName)
  576. {
  577. TRemoteToken Result;
  578. Result.Name = StrFromS3(OwnerDisplayName);
  579. if (Result.Name.IsEmpty())
  580. {
  581. Result.Name = StrFromS3(OwnerId);
  582. }
  583. return Result;
  584. }
  585. //---------------------------------------------------------------------------
  586. struct TLibS3ListServiceCallbackData : TLibS3CallbackData
  587. {
  588. TRemoteFileList * FileList;
  589. };
  590. //---------------------------------------------------------------------------
  591. S3Status TS3FileSystem::LibS3ListServiceCallback(
  592. const char * OwnerId, const char * OwnerDisplayName, const char * BucketName,
  593. int64_t /*CreationDate*/, void * CallbackData)
  594. {
  595. TLibS3ListServiceCallbackData & Data = *static_cast<TLibS3ListServiceCallbackData *>(CallbackData);
  596. std::unique_ptr<TRemoteFile> File(new TRemoteFile(NULL));
  597. File->Terminal = Data.FileSystem->FTerminal;
  598. File->FileName = StrFromS3(BucketName);
  599. File->Type = FILETYPE_DIRECTORY;
  600. File->Owner = Data.FileSystem->MakeRemoteToken(OwnerId, OwnerDisplayName);
  601. File->ModificationFmt = mfNone;
  602. Data.FileList->AddFile(File.release());
  603. return S3StatusOK;
  604. }
  605. //---------------------------------------------------------------------------
  606. struct TLibS3ListBucketCallbackData : TLibS3CallbackData
  607. {
  608. TRemoteFileList * FileList;
  609. int KeyCount;
  610. UTF8String NextMarker;
  611. bool IsTruncated;
  612. };
  613. //---------------------------------------------------------------------------
  614. S3Status TS3FileSystem::LibS3ListBucketCallback(
  615. int IsTruncated, const char * NextMarker, int ContentsCount, const S3ListBucketContent * Contents,
  616. int CommonPrefixesCount, const char ** CommonPrefixes, void * CallbackData)
  617. {
  618. TLibS3ListBucketCallbackData & Data = *static_cast<TLibS3ListBucketCallbackData *>(CallbackData);
  619. Data.IsTruncated = IsTruncated;
  620. // This is being called in chunks, not once for all data in a response.
  621. Data.KeyCount += ContentsCount;
  622. Data.NextMarker = StrFromS3(NextMarker);
  623. for (int Index = 0; Index < ContentsCount; Index++)
  624. {
  625. const S3ListBucketContent * Content = &Contents[Index];
  626. UnicodeString FileName = UnixExtractFileName(StrFromS3(Content->key));
  627. if (!FileName.IsEmpty())
  628. {
  629. std::unique_ptr<TRemoteFile> File(new TRemoteFile(NULL));
  630. File->Terminal = Data.FileSystem->FTerminal;
  631. File->FileName = FileName;
  632. File->Type = FILETYPE_DEFAULT;
  633. File->Modification = UnixToDateTime(Content->lastModified, dstmWin);
  634. File->Size = Content->size;
  635. File->Owner = Data.FileSystem->MakeRemoteToken(Content->ownerId, Content->ownerDisplayName);
  636. Data.FileList->AddFile(File.release());
  637. }
  638. }
  639. for (int Index = 0; Index < CommonPrefixesCount; Index++)
  640. {
  641. std::unique_ptr<TRemoteFile> File(new TRemoteFile(NULL));
  642. File->Terminal = Data.FileSystem->FTerminal;
  643. File->FileName = UnixExtractFileName(UnixExcludeTrailingBackslash(StrFromS3(CommonPrefixes[Index])));
  644. File->Type = FILETYPE_DIRECTORY;
  645. File->ModificationFmt = mfNone;
  646. Data.FileList->AddFile(File.release());
  647. }
  648. return S3StatusOK;
  649. }
  650. //---------------------------------------------------------------------------
  651. void TS3FileSystem::ReadDirectoryInternal(
  652. const UnicodeString & APath, TRemoteFileList * FileList, int MaxKeys, const UnicodeString & FileName)
  653. {
  654. UnicodeString Path = AbsolutePath(APath, false);
  655. if (IsUnixRootPath(Path))
  656. {
  657. DebugAssert(FileList != NULL);
  658. DebugAssert(FileName.IsEmpty());
  659. S3ListServiceHandler ListServiceHandler =
  660. {
  661. { &LibS3ResponsePropertiesCallback, &LibS3ResponseCompleteCallback },
  662. &LibS3ListServiceCallback
  663. };
  664. RequestInit();
  665. TLibS3ListServiceCallbackData Data;
  666. Data.FileSystem = this;
  667. Data.FileList = FileList;
  668. S3_list_service(
  669. FLibS3Protocol, FAccessKeyId.c_str(), FSecretAccessKey.c_str(), 0, FHostName.c_str(),
  670. NULL, MaxKeys, FRequestContext, FTimeout, &ListServiceHandler, &Data);
  671. CheckLibS3Error(Data);
  672. }
  673. else
  674. {
  675. bool Retry;
  676. do
  677. {
  678. FileList->Clear();
  679. UnicodeString BucketName, Prefix;
  680. ParsePath(Path, BucketName, Prefix);
  681. Prefix += FileName;
  682. UTF8String BucketNameUtf = UTF8String(BucketName);
  683. UTF8String Region = UTF8String(GetBucketRegion(BucketName));
  684. UTF8String HostName = UTF8String(GetBucketHostName(BucketName));
  685. S3BucketContext BucketContext =
  686. {
  687. HostName.c_str(),
  688. BucketNameUtf.c_str(),
  689. FLibS3Protocol,
  690. S3UriStyleVirtualHost,
  691. FAccessKeyId.c_str(),
  692. FSecretAccessKey.c_str(),
  693. 0,
  694. Region.c_str()
  695. };
  696. S3ListBucketHandler ListBucketHandler =
  697. {
  698. { &LibS3ResponsePropertiesCallback, &LibS3ResponseCompleteCallback },
  699. &LibS3ListBucketCallback
  700. };
  701. TLibS3ListBucketCallbackData Data;
  702. bool Continue;
  703. do
  704. {
  705. RequestInit();
  706. Data.FileSystem = this;
  707. Data.KeyCount = 0;
  708. Data.FileList = FileList;
  709. Data.IsTruncated = false;
  710. S3_list_bucket(
  711. &BucketContext, StrToS3(Prefix), StrToS3(Data.NextMarker),
  712. LibS3Delimiter.c_str(), MaxKeys, FRequestContext, FTimeout, &ListBucketHandler, &Data);
  713. Retry = RetryBucket(Data, BucketName);
  714. Continue = false;
  715. if (!Retry)
  716. {
  717. CheckLibS3Error(Data);
  718. if (Data.IsTruncated && ((MaxKeys == 0) || (Data.KeyCount < MaxKeys)))
  719. {
  720. bool Cancel = false;
  721. FTerminal->DoReadDirectoryProgress(FileList->Count, false, Cancel);
  722. if (!Cancel)
  723. {
  724. Continue = true;
  725. }
  726. }
  727. }
  728. } while (Continue);
  729. }
  730. while (Retry);
  731. if (MaxKeys != 1)
  732. {
  733. FileList->AddFile(new TRemoteParentDirectory(FTerminal));
  734. }
  735. }
  736. }
  737. //---------------------------------------------------------------------------
  738. void __fastcall TS3FileSystem::ReadDirectory(TRemoteFileList * FileList)
  739. {
  740. TOperationVisualizer Visualizer(FTerminal->UseBusyCursor);
  741. ReadDirectoryInternal(FileList->Directory, FileList, 0, UnicodeString());
  742. }
  743. //---------------------------------------------------------------------------
  744. void __fastcall TS3FileSystem::ReadSymlink(TRemoteFile * /*SymlinkFile*/,
  745. TRemoteFile *& /*File*/)
  746. {
  747. // we never set SymLink flag, so we should never get here
  748. DebugFail();
  749. }
  750. //---------------------------------------------------------------------------
  751. void __fastcall TS3FileSystem::ReadFile(const UnicodeString FileName,
  752. TRemoteFile *& File)
  753. {
  754. TOperationVisualizer Visualizer(FTerminal->UseBusyCursor);
  755. UnicodeString FileNameOnly = UnixExtractFileName(FileName);
  756. std::unique_ptr<TRemoteFileList> FileList(new TRemoteFileList());
  757. ReadDirectoryInternal(UnixExtractFileDir(FileName), FileList.get(), 1, FileNameOnly);
  758. TRemoteFile * AFile = FileList->FindFile(FileNameOnly);
  759. if (AFile == NULL)
  760. {
  761. throw Exception(FMTLOAD(FILE_NOT_EXISTS, (FileName)));
  762. }
  763. File = AFile->Duplicate();
  764. }
  765. //---------------------------------------------------------------------------
  766. void __fastcall TS3FileSystem::DeleteFile(const UnicodeString FileName,
  767. const TRemoteFile * /*File*/, int /*Params*/, TRmSessionAction & /*Action*/)
  768. {
  769. throw Exception(L"Not implemented");
  770. }
  771. //---------------------------------------------------------------------------
  772. void __fastcall TS3FileSystem::RenameFile(const UnicodeString FileName,
  773. const UnicodeString NewName)
  774. {
  775. throw Exception(L"Not implemented");
  776. }
  777. //---------------------------------------------------------------------------
  778. void __fastcall TS3FileSystem::CopyFile(const UnicodeString FileName,
  779. const UnicodeString NewName)
  780. {
  781. throw Exception(L"Not implemented");
  782. }
  783. //---------------------------------------------------------------------------
  784. void __fastcall TS3FileSystem::CreateDirectory(const UnicodeString DirName)
  785. {
  786. throw Exception(L"Not implemented");
  787. }
  788. //---------------------------------------------------------------------------
  789. void __fastcall TS3FileSystem::CreateLink(const UnicodeString FileName,
  790. const UnicodeString PointTo, bool /*Symbolic*/)
  791. {
  792. DebugFail();
  793. }
  794. //---------------------------------------------------------------------------
  795. void __fastcall TS3FileSystem::ChangeFileProperties(const UnicodeString FileName,
  796. const TRemoteFile * /*File*/, const TRemoteProperties * /*Properties*/,
  797. TChmodSessionAction & /*Action*/)
  798. {
  799. DebugFail();
  800. }
  801. //---------------------------------------------------------------------------
  802. bool __fastcall TS3FileSystem::LoadFilesProperties(TStrings * /*FileList*/)
  803. {
  804. DebugFail();
  805. return false;
  806. }
  807. //---------------------------------------------------------------------------
  808. void __fastcall TS3FileSystem::CalculateFilesChecksum(const UnicodeString & /*Alg*/,
  809. TStrings * /*FileList*/, TStrings * /*Checksums*/,
  810. TCalculatedChecksumEvent /*OnCalculatedChecksum*/)
  811. {
  812. DebugFail();
  813. }
  814. //---------------------------------------------------------------------------
  815. void __fastcall TS3FileSystem::CustomCommandOnFile(const UnicodeString FileName,
  816. const TRemoteFile * /*File*/, UnicodeString Command, int /*Params*/, TCaptureOutputEvent /*OutputEvent*/)
  817. {
  818. DebugFail();
  819. }
  820. //---------------------------------------------------------------------------
  821. void __fastcall TS3FileSystem::AnyCommand(const UnicodeString Command,
  822. TCaptureOutputEvent /*OutputEvent*/)
  823. {
  824. DebugFail();
  825. }
  826. //---------------------------------------------------------------------------
  827. TStrings * __fastcall TS3FileSystem::GetFixedPaths()
  828. {
  829. return NULL;
  830. }
  831. //---------------------------------------------------------------------------
  832. void __fastcall TS3FileSystem::SpaceAvailable(const UnicodeString Path,
  833. TSpaceAvailable & /*ASpaceAvailable*/)
  834. {
  835. DebugFail();
  836. }
  837. //---------------------------------------------------------------------------
  838. void __fastcall TS3FileSystem::CopyToRemote(TStrings * /*FilesToCopy*/,
  839. const UnicodeString ATargetDir, const TCopyParamType * /*CopyParam*/,
  840. int /*Params*/, TFileOperationProgressType * /*OperationProgress*/,
  841. TOnceDoneOperation & /*OnceDoneOperation*/)
  842. {
  843. throw Exception(L"Not implemented");
  844. }
  845. //---------------------------------------------------------------------------
  846. void __fastcall TS3FileSystem::CopyToLocal(TStrings * /*FilesToCopy*/,
  847. const UnicodeString TargetDir, const TCopyParamType * /*CopyParam*/,
  848. int /*Params*/, TFileOperationProgressType * /*OperationProgress*/,
  849. TOnceDoneOperation & /*OnceDoneOperation*/)
  850. {
  851. throw Exception(L"Not implemented");
  852. }
  853. //---------------------------------------------------------------------------
  854. void __fastcall TS3FileSystem::GetSupportedChecksumAlgs(TStrings * /*Algs*/)
  855. {
  856. // NOOP
  857. }
  858. //---------------------------------------------------------------------------
  859. void __fastcall TS3FileSystem::LockFile(const UnicodeString & /*FileName*/, const TRemoteFile * /*File*/)
  860. {
  861. DebugFail();
  862. }
  863. //---------------------------------------------------------------------------
  864. void __fastcall TS3FileSystem::UnlockFile(const UnicodeString & /*FileName*/, const TRemoteFile * /*File*/)
  865. {
  866. DebugFail();
  867. }
  868. //---------------------------------------------------------------------------
  869. void __fastcall TS3FileSystem::UpdateFromMain(TCustomFileSystem * /*AMainFileSystem*/)
  870. {
  871. // noop
  872. }
  873. //------------------------------------------------------------------------------
  874. void __fastcall TS3FileSystem::ClearCaches()
  875. {
  876. FRegions.clear();
  877. FHostNames.clear();
  878. }
  879. //------------------------------------------------------------------------------