diff --git a/res/S3.rc b/res/S3.rc new file mode 100644 index 0000000..f840aa1 --- /dev/null +++ b/res/S3.rc @@ -0,0 +1,4 @@ +MAINICON ICON mainicon.ico +BUCKET ICON bucket.ico +FOLDER ICON folder.ico +CONFIG ICON config.ico diff --git a/res/bucket.ico b/res/bucket.ico new file mode 100644 index 0000000..06354fb Binary files /dev/null and b/res/bucket.ico differ diff --git a/res/config.ico b/res/config.ico new file mode 100644 index 0000000..f755719 Binary files /dev/null and b/res/config.ico differ diff --git a/res/folder.ico b/res/folder.ico new file mode 100644 index 0000000..96ad171 Binary files /dev/null and b/res/folder.ico differ diff --git a/res/mainicon.ico b/res/mainicon.ico new file mode 100644 index 0000000..9b176b1 Binary files /dev/null and b/res/mainicon.ico differ diff --git a/res/pluginst.inf b/res/pluginst.inf new file mode 100644 index 0000000..305560b --- /dev/null +++ b/res/pluginst.inf @@ -0,0 +1,6 @@ +[plugininstall] +description=S3 plugin 0.9 (32bit+64bit) +type=wfx +file=WvN-S3.wfx +defaultdir=WvN-S3 +version=0.9 diff --git a/source/S3.dpr b/source/S3.dpr new file mode 100644 index 0000000..7f40ff0 --- /dev/null +++ b/source/S3.dpr @@ -0,0 +1,57 @@ +library S3; + +{$DEFINE NOFORMS} +{$R 'S3.res' '..\res\S3.rc'} + +uses + FastMM4, + System.SysUtils, + Vcl.Dialogs, + System.Classes, + WinApi.Windows, + WinApi.ShellApi, + System.AnsiStrings, + Wfx.Plugin.S3 in 'Wfx.Plugin.S3.pas', + Wfx.Plugin.S3.Path in 'Wfx.Plugin.S3.Path.pas', + Wfx.Plugin.Base in 'Wfx.Plugin.Base.pas', + Wfx.Plugin.intf in 'Wfx.Plugin.intf.pas', + Wfx.Plugin.Consts in 'Wfx.Plugin.Consts.pas', + Wfx.Plugin.ExportProcs in 'Wfx.Plugin.ExportProcs.pas'; + +{$E wfx} +{$IFDEF WIN64} +{$E w64} +{$IFNDEF VER230} +{$E wfx64} +{$ENDIF} +{$ENDIF} + + +exports + FsInitW, + FsFindFirstW, + FsFindNextW, + FsFindClose, + FsGetDefRootName, + FsExecuteFileW, + FsGetFileW, + FsStatusInfoW, + FsExtractCustomIconW, + FsDeleteFileW, + FsSetCryptCallbackW, + FsMkDirW, + FsRenMovFileW, + FsPutFileW, + FsRemoveDirW, + FsDisconnectW, + FsSetAttrW, + FsSetTimeW, + FsSetDefaultParams, + FsGetPreviewBitmapW, + FsLinksToLocalFiles, + FsGetLocalNameW; + +begin + DllProc := @DLLEntryPoint; + DLLEntryPoint(DLL_PROCESS_ATTACH); +end. diff --git a/source/S3.dproj b/source/S3.dproj new file mode 100644 index 0000000..ec16c9d --- /dev/null +++ b/source/S3.dproj @@ -0,0 +1,1090 @@ + + + {266D31E2-C5A3-4D94-B869-D7C79A053614} + S3.dpr + True + Debug + 3 + Library + None + 19.3 + Win64 + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + ..\bin + ..\dcu\$(platform)\$(config)\ + false + 00400000 + false + false + 1043 + System;Xml;Data;Datasnap;Web;Soap;Winapi;$(DCC_Namespace) + false + S3 + false + true + CompanyName=NiftySoft;FileDescription=AWS S3 Plugin for Total Commander;FileVersion=1.0.0.0;InternalName=S3;LegalCopyright=Wouter van Nifterick, 2023;LegalTrademarks=;OriginalFilename=;ProductName=S3.wfx;ProductVersion=1.0.0.0;Comments=;CFBundleName= + true + true + + + $(BDS)\bin\Artwork\iOS\iPhone\FM_ApplicationIcon_1024x1024.png + CFBundleName=$(MSBuildProjectName);CFBundleDevelopmentRegion=en;CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleInfoDictionaryVersion=7.1;CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;LSRequiresIPhoneOS=true;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);UIDeviceFamily=iPhone & iPad;NSLocationAlwaysUsageDescription=The reason for accessing the location information of the user;NSLocationWhenInUseUsageDescription=The reason for accessing the location information of the user;NSLocationAlwaysAndWhenInUseUsageDescription=The reason for accessing the location information of the user;UIBackgroundModes=;NSContactsUsageDescription=The reason for accessing the contacts;NSPhotoLibraryUsageDescription=The reason for accessing the photo library;NSPhotoLibraryAddUsageDescription=The reason for adding to the photo library;NSCameraUsageDescription=The reason for accessing the camera;NSFaceIDUsageDescription=The reason for accessing the face id;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSSiriUsageDescription=The reason for accessing Siri;ITSAppUsesNonExemptEncryption=false;NSBluetoothAlwaysUsageDescription=The reason for accessing bluetooth;NSBluetoothPeripheralUsageDescription=The reason for accessing bluetooth peripherals;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSMotionUsageDescription=The reason for accessing the accelerometer;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + iPhoneAndiPad + true + Debug + $(MSBuildProjectName) + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + Debug + + + CFBundleName=$(MSBuildProjectName);CFBundleDisplayName=$(MSBuildProjectName);CFBundleIdentifier=$(MSBuildProjectName);CFBundleVersion=1.0.0;CFBundleShortVersionString=1.0.0;CFBundlePackageType=APPL;CFBundleSignature=????;CFBundleAllowMixedLocalizations=YES;CFBundleExecutable=$(MSBuildProjectName);NSHighResolutionCapable=true;LSApplicationCategoryType=public.app-category.utilities;NSLocationUsageDescription=The reason for accessing the location information of the user;NSContactsUsageDescription=The reason for accessing the contacts;NSCalendarsUsageDescription=The reason for accessing the calendar data;NSRemindersUsageDescription=The reason for accessing the reminders;NSCameraUsageDescription=The reason for accessing the camera;NSMicrophoneUsageDescription=The reason for accessing the microphone;NSMotionUsageDescription=The reason for accessing the accelerometer;NSDesktopFolderUsageDescription=The reason for accessing the Desktop folder;NSDocumentsFolderUsageDescription=The reason for accessing the Documents folder;NSDownloadsFolderUsageDescription=The reason for accessing the Downloads folder;NSNetworkVolumesUsageDescription=The reason for accessing files on a network volume;NSRemovableVolumesUsageDescription=The reason for accessing files on a removable volume;NSSpeechRecognitionUsageDescription=The reason for requesting to send user data to Apple's speech recognition servers + Debug + + + true + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + (None) + .wfx + Z:\prog64\TotalCommander\TOTALCMD.EXE + + + true + 1033 + CompanyName=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + (None) + .wfx64 + PerMonitorV2 + Z:\prog64\TotalCommander\TOTALCMD64.EXE + + + RELEASE;$(DCC_Define) + false + 0 + 0 + + + true + 1033 + 9 + CompanyName=;FileVersion=1.0.0.9;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + true + 1033 + 22 + CompanyName=;FileVersion=1.0.0.22;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + + + DEBUG;$(DCC_Define) + true + true + false + true + true + + + true + + + true + + + true + + + true + 1033 + CompanyName=;FileVersion=1.0.0.190;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + 190 + + + 1033 + CompanyName=;FileVersion=1.0.0.448;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName);FileDescription=$(MSBuildProjectName);ProductName=$(MSBuildProjectName) + 448 + z:\prog64\TotalCommander\plugins\wfx\S3\ + Z:\prog64\TotalCommander\TOTALCMD64.EXE + true + true + true + + + + MainSource + + +
S3.res
+
+ + + + + + + + Base + + + Cfg_1 + Base + + + Cfg_2 + Base + +
+ + Delphi.Personality.12 + + + + + S3.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + False + False + False + True + True + + + + + true + + + + + true + + + + + true + + + + + true + + + + + true + + + + + S3.dll + true + + + + + S3.dll + true + + + + + true + + + + + 1 + + + 0 + + + + + classes + 64 + + + classes + 64 + + + + + classes + 1 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + + 12 + + + + +
diff --git a/source/S3.res b/source/S3.res new file mode 100644 index 0000000..fb6a353 Binary files /dev/null and b/source/S3.res differ diff --git a/source/WFX_S3_Group.groupproj b/source/WFX_S3_Group.groupproj new file mode 100644 index 0000000..ccb3c9e --- /dev/null +++ b/source/WFX_S3_Group.groupproj @@ -0,0 +1,48 @@ + + + {0BA6C6CB-5C4C-4E9D-A4EA-50362C904A6D} + + + + + + + + + + + Default.Personality.12 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/Wfx.Plugin.Base.pas b/source/Wfx.Plugin.Base.pas new file mode 100644 index 0000000..026c118 --- /dev/null +++ b/source/Wfx.Plugin.Base.pas @@ -0,0 +1,341 @@ +unit Wfx.Plugin.Base; + +interface + +uses + System.SysUtils, + System.Classes, + System.Generics.Collections, + Wfx.Plugin.Intf, + Wfx.Plugin.Consts, + WinApi.Windows; + +type + + TWFXPlugin = class abstract(TInterfacedObject, IWfxPlugin) + protected + FEnterText : TRequestProcW; + FPluginNumber : Integer; + FProgressBarProc: TProgressProcW; + FLogProc : TLogProcW; + PathExe : string; + FFileListIndex : Integer; + FFileList : TList; + AbortCopy : Boolean; + FCurrentFile : TFileInfo; + DefaultParams : TFsDefaultParamStruct; + constructor Create; virtual; + destructor Destroy; override; + + procedure LogDebug(const msg:string); + public + procedure RefreshTc(aMainWin:THandle); + function GetPluginName: string; virtual; abstract; + function ExecuteFile(aMainWin: THandle; aRemoteName, Verb: string): Integer; virtual; abstract; + function FindFirstFile(var aFindData: TWin32FindData; Path: string): THandle; virtual; abstract; + function FindNextFile(aHandle: THandle; var FindDataW: TWin32FindData): bool; virtual; + function GetFile(aRemoteName: string; aLocalName: string): Integer; virtual; abstract; + function Delete(const aRemoteName: string): Boolean; virtual; abstract; + procedure Init; virtual; + + function GetDLLPathName: string; + procedure TCShowMessage(const aTitle, aText: string); + function Input(const aTitle, Question: string; var aText: string; aInputType: Integer = RT_Other): Boolean; + procedure BuildFindData(var aFindData: TWin32FindDataW; const aFileInfo: TFileInfo); virtual; + + public + private procedure SetFileListIndex(const aValue: Integer); + private function GetFileListIndex: Integer; + public property FileListIndex: Integer read GetFileListIndex write SetFileListIndex; + + private function GetPluginNumber: Integer; + private procedure SetPluginNumber(aValue: Integer); + public property PluginNumber: Integer read GetPluginNumber write SetPluginNumber; + + private procedure SetEnterText(const aValue: TRequestProcW); + private function GetEnterText: TRequestProcW; + public property EnterText: TRequestProcW read GetEnterText write SetEnterText; + + private procedure SetProgressBarProc(const aValue: TProgressProcW); + private function GetProgressBarProc: TProgressProcW; + public property ProgressBarProc: TProgressProcW read GetProgressBarProc write SetProgressBarProc; + + private function GetLogProc: TLogProcW; + private procedure SetLogProc(const aValue: TLogProcW); + public property LogProc: TLogProcW read GetLogProc write SetLogProc; + + + private function GetFileCount: Integer; + public property FileCount: Integer read GetFileCount; + + private function GetCurrentFile: TFileInfo; + private procedure SetCurrentFile(const aValue: TFileInfo); + + property CurrentFile: TFileInfo read GetCurrentFile write SetCurrentFile; + + public function GetIcon(RemoteName: string; ExtractFlags: Integer; var TheIcon: HICON): Integer;virtual; + + procedure Connect(aRemoteName: string); virtual; + function Disconnect(aDisconnectRoot:String):Boolean; virtual; + + procedure SetCryptCallback(CryptProcW:TCryptProcW;CryptoNr,Flags:integer); virtual; + function MkDir(RemoteDir:String):Boolean; virtual; + function RenMovFile(OldName,NewName:String;Move,OverWrite:Boolean; RemoteInfo:pRemoteInfo):integer; virtual; + function PutFile(LocalName,RemoteName:String;CopyFlags:integer):integer; virtual; + function RemoveDir(RemoteName:String):Boolean; virtual; + function SetAttr(RemoteName:String;NewAttr:integer):Boolean; virtual; + function SetTime(RemoteName:String;CreationTime,LastAccessTime, LastWriteTime:PFileTime):Boolean; virtual; + procedure SetDefaultParams(dps:pFsDefaultParamStruct); virtual; + function GetPreviewBitmap(RemoteName:String;width,height:integer; var ReturnedBitmap:hbitmap):integer; virtual; + function LinksToLocalFiles:Boolean; virtual; + function GetLocalName(var RemoteName:String;maxlen:integer):Boolean; virtual; + end; + +implementation + +{ TWFXPlugin } + +uses WinApi.Messages; + +function DateTimeToFileTime(MyDateTime: TDateTime): TFileTime; +var + MyFileAge : Integer; + MyLocalFileTime: _FILETIME; +begin + MyFileAge := DateTimeToFileDate(MyDateTime); + DosDateTimeToFileTime(LongRec(MyFileAge).Hi, LongRec(MyFileAge).Lo, MyLocalFileTime); + LocalFileTimeToFileTime(MyLocalFileTime, Result); +end; + + + +constructor TWFXPlugin.Create; +begin + PathExe := ExtractFilePath(GetDLLPathName); + FFileList := TList.Create; +end; + +procedure TWFXPlugin.Connect(aRemoteName: string); +begin + LogDebug('Connect ' + aRemoteName); +end; + +destructor TWFXPlugin.Destroy; +begin + Self.FFileList.Free; + inherited; +end; + +function TWFXPlugin.Disconnect(aDisconnectRoot: String): Boolean; +begin + LogDebug('Disconnect ' + aDisconnectRoot); + Result := True; +end; + +function TWFXPlugin.FindNextFile(aHandle: THandle; var FindDataW: TWin32FindData): bool; +begin + if aHandle = 0 then + Exit(false); + + FileListIndex := FileListIndex + 1; + Result := FileCount > FileListIndex; + if Result then + BuildFindData(FindDataW, CurrentFile); +end; + +function TWFXPlugin.GetCurrentFile: TFileInfo; +begin + if FileListIndex < 0 then Exit(TFileInfo.Create); + if FileListIndex >= FFileList.Count then Exit(TFileInfo.Create); + + Result := FFileList[FileListIndex]; +end; + +function TWFXPlugin.GetDLLPathName: string; +begin + Result := GetModuleName(HInstance); +end; + +function TWFXPlugin.GetEnterText: TRequestProcW; +begin + Result := FEnterText +end; + +function TWFXPlugin.GetFileCount: Integer; +begin + if FFileList = nil then + Exit(0); + + Result := FFileList.Count; +end; + +function TWFXPlugin.GetFileListIndex: Integer; +begin + Result := FFileListIndex; +end; + +function TWFXPlugin.GetIcon(RemoteName: string; ExtractFlags: Integer; var TheIcon: HICON): Integer; +begin + Result := 0; +end; + +function TWFXPlugin.GetLocalName(var RemoteName: String; maxlen: integer): Boolean; +begin + Result := True; +end; + +function TWFXPlugin.GetLogProc: TLogProcW; +begin + Result := FLogProc; +end; + +function TWFXPlugin.GetPluginNumber: Integer; +begin + Result := FPluginNumber; +end; + +function TWFXPlugin.GetPreviewBitmap(RemoteName: String; width, height: integer; var ReturnedBitmap: hbitmap): integer; +begin + Result := 0; +end; + +function TWFXPlugin.GetProgressBarProc: TProgressProcW; +begin + Result := FProgressBarProc; +end; + +procedure TWFXPlugin.TCShowMessage(const aTitle, aText: string); +const max_msg_size = 256; +var a: array [0 .. max_msg_size] of char; +begin + EnterText(FPluginNumber, RT_MsgOK, PChar(aTitle), PChar(aText), @a, max_msg_size); +end; + + + +procedure TWFXPlugin.Init; +begin + +end; + +procedure TWFXPlugin.BuildFindData(var aFindData: TWin32FindDataW; const aFileInfo: TFileInfo); +begin + aFindData := Default (TWin32FindDataW); + StrCopy(aFindData.cFileName, PChar(aFileInfo.FileName)); + aFindData.nFileSizeLow := aFileInfo.Size; + aFindData.ftCreationTime := DateTimeToFileTime(aFileInfo.Date); + aFindData.ftLastWriteTime := DateTimeToFileTime(aFileInfo.Date); + aFindData.ftLastAccessTime := DateTimeToFileTime(aFileInfo.Date); + + if aFileInfo.ReadOnly then + aFindData.dwFileAttributes := aFindData.dwFileAttributes + FILE_ATTRIBUTE_READONLY; + + if aFileInfo.IsVirtual then + aFindData.dwFileAttributes := aFindData.dwFileAttributes + FILE_ATTRIBUTE_VIRTUAL ; + + if aFileInfo.FileType = TFileType.ftDir then + aFindData.dwFileAttributes := aFindData.dwFileAttributes + FILE_ATTRIBUTE_DIRECTORY; + +end; + +function TWFXPlugin.Input(const aTitle, Question: string; var aText: string; aInputType: Integer = RT_Other): Boolean; +var + a: array [0 .. 255] of char; +begin + StrCopy(@a, PChar(aText)); + Result := EnterText(FPluginNumber, aInputType, PChar(aTitle), PChar(Question), @a, 256); + aText := a; +end; + +function TWFXPlugin.LinksToLocalFiles: Boolean; +begin + Result := False; +end; + +procedure TWFXPlugin.LogDebug(const msg: string); +begin + if not IsDebuggerPresent then + Exit; + + OutputDebugString( PwideChar(PWideString(WideString(msg)))); +end; + +function TWFXPlugin.MkDir(RemoteDir: String): Boolean; +begin + Result := False; +end; + +function TWFXPlugin.PutFile(LocalName, RemoteName: String; CopyFlags: integer): integer; +begin + Result := FS_FILE_OK; +end; + +procedure TWFXPlugin.RefreshTc(aMainWin:THandle); +begin + const cm_RereadSource = 540; + PostMessage(aMainWin, WM_USER+51, cm_RereadSource, 0); + +end; + +function TWFXPlugin.RemoveDir(RemoteName: String): Boolean; +begin + Result := False; +end; + +function TWFXPlugin.RenMovFile(OldName, NewName: String; Move, OverWrite: Boolean; RemoteInfo: pRemoteInfo): integer; +begin + Result := FS_FILE_OK; +end; + +function TWFXPlugin.SetAttr(RemoteName: String; NewAttr: integer): Boolean; +begin + Result := False; +end; + +procedure TWFXPlugin.SetCryptCallback(CryptProcW: TCryptProcW; CryptoNr, Flags: integer); +begin + +end; + +procedure TWFXPlugin.SetCurrentFile(const aValue: TFileInfo); +begin + FCurrentFile := aValue; +end; + +procedure TWFXPlugin.SetDefaultParams(dps: pFsDefaultParamStruct); +begin + DefaultParams := dps^; +end; + +procedure TWFXPlugin.SetEnterText(const aValue: TRequestProcW); +begin + FEnterText := aValue; +end; + +procedure TWFXPlugin.SetFileListIndex(const aValue: Integer); +begin + FFileListIndex := aValue; +end; + +procedure TWFXPlugin.SetLogProc(const aValue: TLogProcW); +begin + FLogProc := aValue +end; + +procedure TWFXPlugin.SetPluginNumber(aValue: Integer); +begin + FPluginNumber := aValue; +end; + +procedure TWFXPlugin.SetProgressBarProc(const aValue: TProgressProcW); +begin + FProgressBarProc := aValue; +end; + + +function TWFXPlugin.SetTime(RemoteName: String; CreationTime, LastAccessTime, LastWriteTime: PFileTime): Boolean; +begin + Result := False; +end; + +end. diff --git a/source/Wfx.Plugin.Consts.pas b/source/Wfx.Plugin.Consts.pas new file mode 100644 index 0000000..8de4dcb --- /dev/null +++ b/source/Wfx.Plugin.Consts.pas @@ -0,0 +1,133 @@ +unit Wfx.Plugin.Consts; + +interface + +uses + Winapi.Windows; + +const + INIT_OK = 0; + +{ ids for FsGetFile } +const + FS_FILE_OK = 0; + FS_FILE_EXISTS = 1; + FS_FILE_NOTFOUND = 2; + FS_FILE_READERROR = 3; + FS_FILE_WRITEERROR = 4; + FS_FILE_USERABORT = 5; + FS_FILE_NOTSUPPORTED = 6; + FS_FILE_EXISTSRESUMEALLOWED = 7; + +const + FS_EXEC_OK = 0; + FS_EXEC_ERROR = 1; + FS_EXEC_YOURSELF = -1; + FS_EXEC_SYMLINK = -2; + +const + FS_COPYFLAGS_OVERWRITE = 1; + FS_COPYFLAGS_RESUME = 2; + FS_COPYFLAGS_MOVE = 4; + FS_COPYFLAGS_EXISTS_SAMECASE = 8; + FS_COPYFLAGS_EXISTS_DIFFERENTCASE = 16; + + { flags for tRequestProc } +const + RT_Other = 0; + RT_UserName = 1; + RT_Password = 2; + RT_Account = 3; + RT_UserNameFirewall = 4; + RT_PasswordFirewall = 5; + RT_TargetDir = 6; + RT_URL = 7; + + RT_MsgOK = 8; + RT_MsgYesNo = 9; + RT_MsgOKCancel = 10; + + { flags for tLogProc } +type + TMsgType = + record + const + connect = 1; + disconnect = 2; + details = 3; + transfercomplete = 4; + connectcomplete = 5; + importanterror = 6; + operationcomplete = 7; + end; + + { flags for FsStatusInfo } +const + FS_STATUS_START = 0; + FS_STATUS_END = 1; + +const + FS_STATUS_OP_LIST = 1; + FS_STATUS_OP_GET_SINGLE = 2; + FS_STATUS_OP_GET_MULTI = 3; + FS_STATUS_OP_PUT_SINGLE = 4; + FS_STATUS_OP_PUT_MULTI = 5; + FS_STATUS_OP_RENMOV_SINGLE = 6; + FS_STATUS_OP_RENMOV_MULTI = 7; + FS_STATUS_OP_DELETE = 8; + FS_STATUS_OP_ATTRIB = 9; + FS_STATUS_OP_MKDIR = 10; + FS_STATUS_OP_EXEC = 11; + FS_STATUS_OP_CALCSIZE = 12; + FS_STATUS_OP_SEARCH = 13; + FS_STATUS_OP_SEARCH_TEXT = 14; + + { Flags for FsExtractCustomIcon } +const + FS_ICONFLAG_SMALL = 1; + FS_ICONFLAG_BACKGROUND = 2; + + FS_ICON_USEDEFAULT = 0; + FS_ICON_EXTRACTED = 1; + FS_ICON_EXTRACTED_DESTROY = 2; + FS_ICON_DELAYED = 3; + +type + TRemoteInfo = + record + SizeLow : Integer; + SizeHigh : Integer; + LastWriteTime : TFileTime; + Attr : Integer; + end; + + pRemoteInfo = ^TRemoteInfo; + +type + TFsDefaultParamStruct = + record + Size : Integer; + PluginInterfaceVersionLow : Integer; + PluginInterfaceVersionHi : Integer; + DefaultIniName : array [0 .. MAX_PATH - 1] of AnsiChar; + end; + + pFsDefaultParamStruct = ^TFsDefaultParamStruct; + + { callback functions } +type + TProgressProc = function (PluginNr: Integer; SourceName, TargetName: PAnsiChar; PercentDone: Integer) : Integer; stdcall; + TProgressProcW = function (PluginNr: Integer; SourceName, TargetName: PWideChar; PercentDone: Integer): Integer; stdcall; + TLogProc = procedure(PluginNr: Integer; MsgType : Integer; LogString : PAnsiChar); stdcall; + TLogProcW = procedure(PluginNr: Integer; MsgType : Integer; LogString : PWideChar); stdcall; + TRequestProc = function (PluginNr: Integer; RequestType: Integer; CustomTitle, CustomText, ReturnedText: PAnsiChar; maxlen: Integer): bool; stdcall; + TRequestProcW = function (PluginNr: Integer; RequestType: Integer; CustomTitle, CustomText, ReturnedText: PWideChar; maxlen: Integer): bool; stdcall; + + PCryptProc = ^TCryptProc; + TCryptProc = function (PluginNr, CryptoNumber:integer; mode:integer; ConnectionName, Password:PAnsiChar; maxlen:integer):integer; stdcall; + PCryptProcW = ^TCryptProcW; + TCryptProcW = function (PluginNr, CryptoNumber:integer; mode:integer; ConnectionName, Password:PWideChar; maxlen:integer):integer; stdcall; + +implementation + +end. diff --git a/source/Wfx.Plugin.ExportProcs.pas b/source/Wfx.Plugin.ExportProcs.pas new file mode 100644 index 0000000..594d799 --- /dev/null +++ b/source/Wfx.Plugin.ExportProcs.pas @@ -0,0 +1,198 @@ +unit Wfx.Plugin.ExportProcs; + +interface + +uses + System.SysUtils, + Vcl.Dialogs, + System.Classes, + WinApi.Windows, + WinApi.ShellApi, + System.AnsiStrings, + Wfx.Plugin.Consts; + +procedure DLLEntryPoint(dwReason: DWORD); +function FsInitW(aPluginNr: Integer; aProgressProcW: TProgressProcW; aLogProcW: TLogProcW; aRequestProcW: TRequestProcW): Integer; stdcall; + +function FsExecuteFileW(MainWin: THandle; RemoteName, Verb: PWideChar): Integer; stdcall; +function FsFindFirstW(aPath: PWideChar; aFindData: PWin32FindDataW): Integer; stdcall; +function FsFindNextW(aHandle: THandle; var FindDataW: TWin32FindDataW): bool; stdcall; +function FsFindClose(Hdl: THandle): Integer; stdcall; +function FsDeleteFileW(aRemoteName: PWideChar): bool; stdcall; +function FsGetFileW(RemoteName, LocalName: PWideChar; CopyFlags: Integer; RemoteInfo: pRemoteInfo): Integer; stdcall; +procedure FsGetDefRootName(DefRootName: PAnsiChar; MaxLen: Integer); stdcall; +procedure FsStatusInfoW(RemoteDir: PWideChar; InfoStartEnd, InfoOperation: Integer); stdcall; +function FsExtractCustomIconW(RemoteName: PWideChar; ExtractFlags: Integer; var TheIcon: HIcon): Integer; stdcall; + +procedure FsSetCryptCallbackW(CryptProcW:TCryptProcW;CryptoNr,Flags:integer); stdcall; +function FsMkDirW(RemoteDir:pwidechar):bool; stdcall; +function FsRenMovFileW(OldName,NewName:pwidechar;Move,OverWrite:bool; RemoteInfo:pRemoteInfo):integer; stdcall; +function FsPutFileW(LocalName,RemoteName:pwidechar;CopyFlags:integer):integer; stdcall; +function FsRemoveDirW(RemoteName:pwidechar):bool; stdcall; +procedure FsConnectW(RemoteName:pwidechar); stdcall; +function FsDisconnectW(DisconnectRoot:pwidechar):bool; stdcall; +function FsSetAttrW(RemoteName:pwidechar;NewAttr:integer):bool; stdcall; +function FsSetTimeW(RemoteName:pwidechar;CreationTime,LastAccessTime, LastWriteTime:PFileTime):bool; stdcall; +procedure FsSetDefaultParams(dps:pFsDefaultParamStruct); stdcall; +function FsGetPreviewBitmapW(RemoteName:pwidechar;width,height:integer; var ReturnedBitmap:hbitmap):integer; stdcall; +function FsLinksToLocalFiles:bool; stdcall; +function FsGetLocalNameW(RemoteName:pwidechar;maxlen:integer):bool; stdcall; + +implementation + +uses + Wfx.Plugin.Intf; + +var + Plugin: IWfxPlugin; + +procedure DLLEntryPoint(dwReason: DWORD); +begin + case dwReason of + DLL_PROCESS_ATTACH: + Plugin := globalPluginFactory; + DLL_PROCESS_DETACH: + Plugin := nil; + end; +end; + +function FsInitW(aPluginNr: Integer; aProgressProcW: TProgressProcW; aLogProcW: TLogProcW; aRequestProcW: TRequestProcW): Integer; stdcall; +begin + Plugin.PluginNumber := aPluginNr; + Plugin.EnterText := aRequestProcW; + Plugin.ProgressBarProc := aProgressProcW; + Plugin.LogProc := aLogProcW; + Plugin.Init; + Result := INIT_OK +end; + +function FsExecuteFileW(MainWin: THandle; RemoteName, Verb: PWideChar): Integer; stdcall; +begin + Result := Plugin.ExecuteFile(MainWin, RemoteName, Verb); +end; + +function FsFindFirstW(aPath: PWideChar; aFindData: PWin32FindDataW): Integer; stdcall; +begin + Result := Plugin.FindFirstFile(aFindData^, aPath); + if Plugin.FileCount = 0 then + begin + SetLastError(ERROR_NO_MORE_FILES); + aFindData.dwFileAttributes := 0; + Result := -1; + end; +end; + +function FsFindNextW(aHandle: THandle; var FindDataW: TWin32FindDataW): bool; stdcall; +begin + if Plugin.FileCount = 0 then + Exit(False); + + Result := Plugin.FindNextFile(aHandle, FindDataW); +end; + +function FsFindClose(Hdl: THandle): Integer; stdcall; +begin + Result := 0; +end; + +function FsDeleteFileW(aRemoteName: PWideChar): bool; stdcall; +begin + Result := Plugin.Delete(aRemoteName); +end; + +function FsGetFileW(RemoteName, LocalName: PWideChar; CopyFlags: Integer; RemoteInfo: pRemoteInfo): Integer; stdcall; +begin + Result := Plugin.GetFile(RemoteName, LocalName) +end; + +procedure FsGetDefRootName(DefRootName: PAnsiChar; MaxLen: Integer); stdcall; +begin + System.AnsiStrings.StrLCopy(DefRootName, PAnsiChar(AnsiString(Plugin.GetPluginName)), MaxLen - 1); +end; + +procedure FsStatusInfoW(RemoteDir: PWideChar; InfoStartEnd, InfoOperation: Integer); stdcall; +begin + if (InfoStartEnd <> FS_STATUS_END) and + (InfoOperation = FS_STATUS_OP_GET_MULTI) then + begin + + end; +end; + +function FsExtractCustomIconW(RemoteName: PWideChar; ExtractFlags: Integer; var TheIcon: HIcon): Integer; stdcall; +begin + Result := Plugin.GetIcon(RemoteName, ExtractFlags, TheIcon); +end; + + +procedure FsSetCryptCallbackW(CryptProcW:TCryptProcW;CryptoNr,Flags:integer); stdcall; +begin + Plugin.SetCryptCallback(CryptProcW, CryptoNr, Flags) +end; + +function FsMkDirW(RemoteDir:pwidechar):bool; stdcall; +begin + Result := Plugin.MkDir(RemoteDir); +end; + +function FsRenMovFileW(OldName,NewName:pwidechar;Move,OverWrite:bool; RemoteInfo:pRemoteInfo):integer; stdcall; +begin + Result := Plugin.RenMovFile(OldName, newName, Move, Overwrite, remoteInfo); +end; + +function FsPutFileW(LocalName,RemoteName:pwidechar;CopyFlags:integer):integer; stdcall; +begin + Result := Plugin.PutFile(LocalName, RemoteName, CopyFlags); +end; + +function FsRemoveDirW(RemoteName:pwidechar):bool; stdcall; +begin + Result := Plugin.RemoveDir(RemoteName) +end; + +procedure FsConnectW(RemoteName:pwidechar); stdcall; +begin + Plugin.Connect(RemoteName); +end; + +function FsDisconnectW(DisconnectRoot:pwidechar):bool; stdcall; +begin + Result := Plugin.Disconnect(DisconnectRoot) +end; + +function FsSetAttrW(RemoteName:pwidechar;NewAttr:integer):bool; stdcall; +begin + Result := Plugin.SetAttr(RemoteName, NewAttr) +end; + +function FsSetTimeW(RemoteName:pwidechar;CreationTime,LastAccessTime, LastWriteTime:PFileTime):bool; stdcall; +begin + Result := Plugin.SetTime(RemoteName, CreationTime, LastAccessTime, LastWriteTime) +end; + +procedure FsSetDefaultParams(dps:pFsDefaultParamStruct); stdcall; +begin + Plugin.SetDefaultParams(dps) +end; + +function FsGetPreviewBitmapW(RemoteName:pwidechar;width,height:integer; var ReturnedBitmap:hbitmap):integer; stdcall; +begin + Result := Plugin.GetPreviewBitmap(RemoteName, width, height, ReturnedBitmap) +end; + +function FsLinksToLocalFiles:bool; stdcall; +begin + Result := Plugin.LinksToLocalFiles +end; + +function FsGetLocalNameW(RemoteName:pwidechar;maxlen:integer):bool; stdcall; +var + rem, loc:string; +begin + loc := RemoteName; + Result := Plugin.GetLocalName(loc, maxlen) +end; + + + +end. diff --git a/source/Wfx.Plugin.S3.Path.pas b/source/Wfx.Plugin.S3.Path.pas new file mode 100644 index 0000000..e19d885 --- /dev/null +++ b/source/Wfx.Plugin.S3.Path.pas @@ -0,0 +1,105 @@ +unit Wfx.Plugin.S3.Path; + +interface + +type + TS3TcPath = record + BucketName: string; + TcPathWithoutBucket :string; + S3Path :string; + function IsRoot(const aRemoteName:string): Boolean; + function IsBucket(const aRemoteName:string): Boolean; + function IsS3Object(const aRemoteName:string): Boolean; + function GetBucketName(const aRemoteName:string):string; + function StripAnyBucket(const aRemoteName:string):string; + function StripKnownBucket(const aRemoteName:string):string; + function RemoteNameToS3(const aRemoteName:string):string; + function GetS3FileName(const aRemoteName:string):string; + function GetPrefix(const aRemoteName:string):string; + constructor Create(aBucketName:string); + end; + +implementation + +uses + System.SysUtils, + System.Classes, + System.AnsiStrings, + System.IOUtils +; + + +{ TS3TcPath } + +function TS3TcPath.RemoteNameToS3(const aRemoteName: string): string; +begin + Result := aRemoteName.Replace('\' + BucketName + '\', ''); +end; + +function TS3TcPath.StripAnyBucket(const aRemoteName: string): string; +begin + const LBucketName = GetBucketName(aRemoteName); + + Result := + aRemoteName + .TrimLeft(['\']) + .Replace(LBucketName,'') + .TrimLeft(['\']) + .Replace('\','/'); + +end; + +function TS3TcPath.StripKnownBucket(const aRemoteName: string): string; +begin + Result := + aRemoteName + .TrimLeft(['\']) + .Replace(BucketName,'') + .TrimLeft(['\']) + .Replace('\','/'); + +end; + +function TS3TcPath.IsBucket(const aRemoteName: string): Boolean; +begin + Result := aRemoteName.StartsWith('\') and aRemoteName.EndsWith('\') and + (aRemoteName.TrimLeft(['\']).TrimRight(['\']) = aRemoteName.Replace('\','')); +end; + +function TS3TcPath.IsRoot(const aRemoteName: string): Boolean; +begin + Result := aRemoteName = '\'; +end; + +function TS3TcPath.IsS3Object(const aRemoteName: string): Boolean; +begin + Result := not IsBucket(aRemoteName) +end; + +function TS3TcPath.GetS3FileName(const aRemoteName: string): string; +begin + Result := TPath.GetFileName(StripKnownBucket(aRemoteName)) +end; + +constructor TS3TcPath.Create(aBucketName: string); +begin + BucketName := aBucketName; +end; + +function TS3TcPath.GetBucketName(const aRemoteName: string): string; +begin + var Slugs := aRemoteName.TrimLeft(['\']).Split(['\']); + if length(Slugs) > 0 then + Result :=Slugs[0] + else + Result := ''; +end; + +function TS3TcPath.GetPrefix(const aRemoteName: string): string; +begin + Result := StripAnyBucket(aRemoteName); + if Result <> '' then + Result := Result + '/' +end; + +end. diff --git a/source/Wfx.Plugin.S3.pas b/source/Wfx.Plugin.S3.pas new file mode 100644 index 0000000..e909a9e --- /dev/null +++ b/source/Wfx.Plugin.S3.pas @@ -0,0 +1,554 @@ +unit Wfx.Plugin.S3; + +interface + +uses + System.SysUtils, + System.Classes, + System.AnsiStrings, + System.IOUtils, + System.Generics.Collections, + System.IniFiles, + System.TypInfo, + WinApi.ShFolder, + + WinApi.Windows, + WinApi.Messages, + + Data.Cloud.CloudAPI, + Data.Cloud.AmazonAPI, + + Wfx.Plugin.intf, + Wfx.Plugin.Base, + Wfx.Plugin.Consts, + Wfx.Plugin.S3.Path, Vcl.Dialogs; + +type + TPluginMode = ( pmInit, pmPickProfile, pmPickBucket, pmShowFolderContents ); + TS3Plugin = class(TWFXPlugin, IWfxPlugin) + const + PLUGIN_NAME = 'S3'; + private + procedure SetBucketName(const Value: string); + procedure ConnectToS3; + protected + FConnectionInfo: TAmazonConnectionInfo; + S3 : TAmazonStorageService; + FBuckets : TStrings; + FRegion : string; + FBucketName : string; + FCurrentPath : string; + Path : TS3TcPath; + FProfile : string; + FPickProfile : TFileInfo; + PluginMode : TPluginMode; + FProfiles : TStringList; + public + constructor Create; override; + destructor Destroy; override; + procedure Init; override; + function GetPluginName: string;override; + function GetUserPath:string; + function FindFileInfo(aRemoteName: string; var fi:TFileInfo):boolean; + function ExecuteFile(aMainWin: THandle; aRemoteName, aVerb: string): Integer; override; + function FindFirstFile(var aFindData: TWin32FindData; aPath: string): THandle; override; + function GetFile(aRemoteName: string; aLocalName: string): Integer;override; + function GetIcon(aRemoteName: string; ExtractFlags: Integer; var TheIcon: HICON): Integer;override; + function Delete(const aRemoteName: string): Boolean; override; + function MkDir(aRemoteDir:String):Boolean;override; + function RenMovFile(aOldName,aNewName:String;aMove,aOverWrite:Boolean; aRemoteInfo:pRemoteInfo):integer;override; + function PutFile(aLocalName,aRemoteName:String;aCopyFlags:integer):integer;override; + function RemoveDir(aRemoteName:String):Boolean;override; + function Disconnect(aDisconnectRoot:String):Boolean;override; + function GetLocalName(var aRemoteName:String;maxlen:integer):Boolean;override; + + property BucketName:string read FBucketName write SetBucketName; + + + end; + +implementation + +function ISO8601ToDateTime(Value: String):TDateTime; +var + fs: TFormatSettings; +begin + fs := TFormatSettings.Create(GetThreadLocale); + fs.DateSeparator := '-'; + fs.ShortDateFormat := 'yyyy-MM-dd'; + Value := Value.Replace('T', ' ').Replace('Z','').Substring(0,19); + Result := StrToDateTime(Value, fs); +end; + +procedure TS3Plugin.ConnectToS3; +begin + const awsPath = GetUserPath; + const credentials = TPath.Combine(TPath.Combine(awsPath,'.aws'),'credentials'); + + FProfiles.Free; + FProfiles := TStringList.Create; + var AccountName := ''; + var AccountKey := ''; + + if TFile.Exists(credentials) then + begin + const ini = TIniFile.Create(credentials); + try + AccountName := ini.ReadString(FProfile,'aws_access_key_id',AccountName); + AccountKey := ini.ReadString(FProfile,'aws_secret_access_key',AccountKey); + FRegion := ini.ReadString(FProfile,'region',FRegion); + ini.ReadSections(FProfiles); + finally + ini.Free; + end; + end; + + FConnectionInfo.Free; + FConnectionInfo := TAmazonConnectionInfo.Create(nil); + FConnectionInfo.AccountName := AccountName; + FConnectionInfo.AccountKey := AccountKey; + FConnectionInfo.Region := FREgion; + S3.Free; + S3 := TAmazonStorageService.Create(FConnectionInfo); + + if (AccountName = '') or + (AccountKey = '') or + (FRegion = '') then + PluginMode := TPluginMode.pmPickProfile + else + PluginMode := TPluginMode.pmPickBucket; + + +end; + +constructor TS3Plugin.Create; +begin + inherited; + PluginMode := TPluginMode.pmInit; + Path := TS3TcPath.Create(''); + FProfiles := TStringList.Create; + FBuckets := TStringList.Create; + +end; + +function TS3Plugin.Delete(const aRemoteName: string): Boolean; + var res:TCloudResponseInfo; + var s3ObjectName: string; + +begin + res := TCloudResponseInfo.Create; + try + if Path.IsBucket(aRemoteName) then + begin + Result := S3.DeleteBucket(Path.GetBucketName(aRemoteName), res, FRegion ); + end + else + begin + s3ObjectName := Path.StripKnownBucket(aRemoteName); + Result := S3.DeleteObject( BucketName, s3ObjectName, res, FRegion ); + end; + finally + LogDebug(res.StatusMessage); + res.Free; + end; +end; + +destructor TS3Plugin.Destroy; +begin + FBuckets.Free; + S3.Free; + FConnectionInfo.Free; + inherited; +end; + +function TS3Plugin.Disconnect(aDisconnectRoot: String): Boolean; +begin + inherited; + Result := false; +end; + +function TS3Plugin.ExecuteFile(aMainWin: THandle; aRemoteName, aVerb: string): Integer; +begin + try + var f:TFileinfo; + if not self.FindFileInfo(aRemoteName, f) then + Exit(FS_EXEC_ERROR); + + if f.FileType = TFileType.ftAction then + begin + if Assigned(f.OnExecute) then + f.OnExecute(aMainWin, aRemoteName, aVerb, @f); + end; + Result := FS_EXEC_OK; + except + on E: Exception do + begin + Result := FS_EXEC_ERROR; + TCShowMessage('', E.Message); + end; + end; +end; + +function TS3Plugin.FindFileInfo(aRemoteName: string; var fi:TFileInfo):boolean; +begin + const a = aRemoteName.Split(['\']); + + var l := ''; + if length(a)>0 then + l := a[high(a)] + else + l := aRemoteName; + + for var f in FFileList do + begin + if f.FileName = l then + begin + fi := f; + Exit(True); + end; + end; + Result := False; +end; + +function TS3Plugin.FindFirstFile(var aFindData: TWin32FindData; aPath: string): THandle; +begin + FCurrentPath := aPath; + + FFileList.Clear; + + if pluginMode=TPluginMode.pmShowFolderContents then + if Path.IsRoot(FCurrentPath) then + PluginMode := TPluginMode.pmPickBucket; + + case pluginMode of + pmInit: + FFileList.Add(FPickProfile); + + pmPickProfile: + begin + for var p in FProfiles do + begin + var f := TFileInfo.Create; + f.FileName := p; + f.IsVirtual := True; + f.OnExecute := + procedure(aMainWin: THandle; aRemoteName, aVerb: string; sender:PFileInfo) + begin + const a = aRemoteName.Split(['\']); + + var l := ''; + if length(a)>0 then + l := a[high(a)] + else + l := aRemoteName; + + PluginMode := TPluginMode.pmPickBucket; + + FProfile := l; + ConnectToS3; + RefreshTc(aMainWin); + end; + f.FileType := TFileType.ftAction; + + FFileList.Add(f); + end; + end; + pmPickBucket: + begin + FFileList.Add(FPickProfile); + FBuckets := S3.ListBuckets; + LogDebug('Retrieved bucket list'); + for var bucketName in FBuckets do + begin + var FileInfo:= TFileInfo.Create; + var item := bucketName.Split(['=']); + if length(item) = 2 then + begin + FileInfo.FileName := item[0]; + FileInfo.Date := ISO8601ToDateTime(item[1]); + end + else + begin + FileInfo.FileName := bucketName; + FileInfo.Date := 0; + end; + + FileInfo.Directory := ''; + FileInfo.ReadOnly := True; + FileInfo.Size := 0; + FileInfo.FileType := TFileType.ftDir; + FileInfo.OnExecute := + procedure(aMainWin: THandle; aRemoteName, aVerb: string; sender:PFileInfo) + begin + PluginMode := TPluginMode.pmShowFolderContents; + end; + FFileList.Add(FileInfo); + end; + FFileListIndex := 0; + PluginMode := TPluginMode.pmShowFolderContents; + end; + pmShowFolderContents: + begin + const LBucketName = Path.GetBucketName(FCurrentPath); + const LDirectory = Path.GetPrefix(FCurrentPath); + var res := TCloudResponseInfo.Create; + var params := TStringList.Create; + params.AddPair('prefix', LDirectory ); + + var LBucketResult := S3.GetBucket(LBucketName, params, res, FRegion); + + if LBucketResult <> nil then + begin + BucketName := LBucketResult.Name; + for var o in LBucketResult.Objects do + begin + if o.Name = LDirectory then + Continue; + + var FileInfo:= TFileInfo.Create; + var Name := o.Name.TrimRight(['/']).Replace('/','\'); + + FileInfo.Directory := LBucketResult.RequestPrefix; + FileInfo.Date := ISO8601ToDateTime(o.LastModified); + FileInfo.Size := o.Size; + if o.Name.EndsWith('/') then + FileInfo.FileType := TFileType.ftDir + else + FileInfo.FileType := TFileType.ftFile; + + FileInfo.ReadOnly := false; + const prefix = ExtractFilePath(Name).Replace('\','/'); + if prefix <> LBucketResult.RequestPrefix then + Continue; + + FileInfo.FileName := Name.Substring(length(Prefix),MaxInt); + + FFileList.Add(fileInfo); + end; + end + else + TCShowMessage('Error', res.StatusMessage ); + + res.Free; + end; + + + end; + + + if FFileList.Count > 0 then + begin + FFileListIndex := 0; + BuildFindData(aFindData, FFileList[0]); + end + else + begin + FFileListIndex := -1; + end; + Result := 1; + +end; + +procedure TS3Plugin.Init; +begin + inherited; + + FProfile := 'default'; + FRegion := 'eu-west-1'; + FPickProfile.FileName := '[PICK AWS PROFILE]'; + + FPickProfile.FileType := TFileType.ftAction; + FPickProfile.ReadOnly := True; + FPickProfile.Size := 0; + FPickProfile.OnExecute := + procedure(aMainWin: THandle; aRemoteName, aVerb: string; sender:PFileInfo) + begin + PluginMode := TPluginMode.pmPickProfile; + RefreshTc(aMainWin); + end; + + PluginMode := TPluginMode.pmPickProfile; + + ConnectToS3; + + FBuckets.Free; + FBuckets := TStringList.Create; +end; + +function TS3Plugin.MkDir(aRemoteDir: String): Boolean; +var res:TCloudResponseInfo; +begin + inherited; + aRemoteDir := aRemoteDir.TrimLeft(['\']); + var Slugs := aRemoteDir.Split(['\']); + if length(Slugs) = 1 then + begin + // we're at the root, so let's create a bucket + res := TCloudResponseInfo.Create; + Result := S3.CreateBucket(aRemoteDir, TAmazonACLType.amzbaPrivate, FRegion, res); + end + else + begin + // we're in a bucket, so let's create a folder + res := TCloudResponseInfo.Create; + var s3Name := Path.StripKnownBucket(aRemoteDir) + '/'; + S3.UploadObject(BucketName, s3Name, [], false, nil, nil, TAmazonACLType.amzbaNotSpecified, res, FRegion ); + + end; + Exit(False); +end; + +function TS3Plugin.PutFile(aLocalName, aRemoteName: String; aCopyFlags: integer): integer; +var res:TCloudResponseInfo; +begin + if BucketName = '' then + begin + TCShowMessage('Cannot upload file', 'Select a bucket first'); + Exit(FS_FILE_OK); + end; + + try + res := TCloudResponseInfo.Create; + var s3Name := Path.StripKnownBucket(aRemoteName); + S3.UploadObject(BucketName, s3Name, TFile.ReadAllBytes(aLocalName), false, nil, nil, TAmazonACLType.amzbaNotSpecified, res, FRegion ); + Result := FS_FILE_OK; + except + Result := FS_FILE_WRITEERROR; + end; +end; + + + +function TS3Plugin.GetPluginName: string; +begin + Result := PLUGIN_NAME; +end; + +function TS3Plugin.RemoveDir(aRemoteName: String): Boolean; +begin + Result := Delete(aRemoteName); +end; + +function TS3Plugin.RenMovFile(aOldName, aNewName: String; aMove, aOverWrite: Boolean; aRemoteInfo: pRemoteInfo): integer; +begin + try + // Shitty, S3 does not support renaming of objects directly. + // Amazon suggests creating a new object, and deleting the old + var params := TAmazonGetObjectOptionals.Create; + + var oldName := Path.StripAnyBucket(aOldName); + var oldBucket := Path.GetBucketName(aOldName); + var newName := Path.StripAnyBucket(aNewName); + var newBucket := Path.GetBucketName(aNewName); + + S3.CopyObject(newBucket, newName, oldBucket, oldName, nil, nil, FRegion); + if oldBucket = newBucket then + S3.DeleteObject(oldBucket, oldName, nil, FRegion) ; + Result := FS_FILE_OK; + except + Result := 1; + end; +end; + +procedure TS3Plugin.SetBucketName(const Value: string); +begin + FBucketName := Value; + Path.BucketName := Value; +end; + +function TS3Plugin.GetUserPath: string; +var + LStr: array[0 .. MAX_PATH] of Char; +begin + const CSIDL_PROFILE = $28; + SetLastError(ERROR_SUCCESS); + if SHGetFolderPath(0, CSIDL_PROFILE, 0, 0, @LStr) = S_OK then + Result := LStr; +end; + +function TS3Plugin.GetFile(aRemoteName, aLocalName: string): Integer; +begin + try + if (FCurrentPath = '\') then + begin + Result := FS_FILE_NOTSUPPORTED; + exit; + end + else + begin + if not AbortCopy then + begin + var s := TFileStream.Create(aLocalName, fmCreate); + var s3Item := Path.StripKnownBucket(aRemoteName); + try + var params := TAmazonGetObjectOptionals.Create; + + if S3.GetObject( BucketName, s3Item, params, s, nil, FRegion ) then + begin + Result := FS_FILE_OK + end + else + Result := FS_FILE_READERROR; + finally + s.Free; + end; + end + end; + Result := FS_FILE_OK; + except + on E: Exception do + begin + Result := FS_FILE_READERROR; + TCShowMessage('', E.Message); + end; + end; +end; + +function TS3Plugin.GetIcon(aRemoteName: string; ExtractFlags: Integer; var TheIcon: HICON): Integer; +begin + Result := FS_ICON_USEDEFAULT; + if aRemoteName.EndsWith('\..\') then + Exit; + + if Path.IsRoot(aRemoteName) then + begin + TheIcon := LoadIcon(HInstance, 'BUCKET'); + Result := FS_ICON_EXTRACTED; + Exit; + end; + + var fi:= TFileInfo.Create; + if FindFileInfo(aRemoteName, fi) then + if fi.FileType = TFileType.ftAction then + + begin + TheIcon := LoadIcon(HInstance, 'CONFIG'); + Result := FS_ICON_EXTRACTED; + Exit; + end; + + if Path.IsBucket(aRemoteName) then + begin + TheIcon := LoadIcon(HInstance, 'BUCKET'); + Result := FS_ICON_EXTRACTED; + Exit; + end; + +end; + +function TS3Plugin.GetLocalName(var aRemoteName: String; maxlen: integer): Boolean; +begin + Result := True; +end; + + + + +initialization + globalPluginFactory := + function:IWfxPlugin + begin + Result := TS3Plugin.Create; + end; + +end. diff --git a/source/Wfx.Plugin.intf.pas b/source/Wfx.Plugin.intf.pas new file mode 100644 index 0000000..23b9b73 --- /dev/null +++ b/source/Wfx.Plugin.intf.pas @@ -0,0 +1,111 @@ +unit Wfx.Plugin.intf; + +interface + +uses + Wfx.Plugin.Consts, + WinApi.Windows; + +type + PFileInfo = ^TFileInfo; + TFileType = (ftFile,ftDir,ftAction); + TFileInfoProc = reference to procedure(aMainWin: THandle; aRemoteName, aVerb: string; sender:PFileInfo); + TFileInfo = record + FileName: string; + Directory: string; + Date: TDateTime; + Size: Int64; + ReadOnly: Boolean; + IsVirtual: Boolean; + OnExecute: TFileInfoProc; + FileType:TFileType; + class function Create:TFileInfo;static; + end; + + IWfxPlugin = interface + ['{659F13FB-D7DF-4416-A1A4-526A78208865}'] + function GetDLLPathName: string; + function GetPluginName: string; + function ExecuteFile(MainWin: THandle; RemoteName, Verb: string): Integer; + function FindFirstFile(var FindData: TWin32FindDataW; Path: string): THandle; + function FindNextFile(aHandle: THandle; var FindDataW: TWin32FindDataW): bool; + function Delete(const RemoteName: string): Boolean; + procedure TCShowMessage(const Title, Text: string); + function Input(const Title, Question: string; var Text: string; InputType: Integer = RT_Other): Boolean; + + function GetFileListIndex: Integer; + procedure SetFileListIndex(const Value: Integer); + property FileListIndex: Integer read GetFileListIndex write SetFileListIndex; + + procedure SetPluginNumber(AValue: Integer); + function GetPluginNumber: Integer; + property PluginNumber: Integer read GetPluginNumber write SetPluginNumber; + + function GetProgressBarProc: TProgressProcW; + procedure SetProgressBarProc(const Value: TProgressProcW); + property ProgressBarProc : TProgressProcW read GetProgressBarProc write SetProgressBarProc; + + function GetLogProc: TLogProcW; + procedure SetLogProc(const Value: TLogProcW); + property LogProc : TLogProcW read GetLogProc write SetLogProc ; + + function GetEnterText: TRequestProcW; + procedure SetEnterText(const Value: TRequestProcW); + property EnterText: TRequestProcW read GetEnterText write SetEnterText; + + function GetFileCount: integer; + property FileCount:integer read GetFileCount; + + function GetCurrentFile:TFileInfo; + property CurrentFile:TFileInfo read GetCurrentFile; + + procedure Init; + + function GetFile(aRemoteName,aLocalName:string):integer; + + function GetIcon(RemoteName: string; ExtractFlags: Integer; var TheIcon: HIcon):Integer; + + + procedure SetCryptCallback(CryptProcW:TCryptProcW;CryptoNr,Flags:integer); + function MkDir(RemoteDir:String):Boolean; + function RenMovFile(OldName,NewName:String;Move,OverWrite:Boolean; RemoteInfo:pRemoteInfo):integer; + function PutFile(LocalName,RemoteName:String;CopyFlags:integer):integer; + function RemoveDir(RemoteName:String):Boolean; + procedure Connect(aRemoteName: string); + function Disconnect(DisconnectRoot:String):Boolean; + function SetAttr(RemoteName:String;NewAttr:integer):Boolean; + function SetTime(RemoteName:String;CreationTime,LastAccessTime, LastWriteTime:PFileTime):Boolean; + procedure SetDefaultParams(dps:pFsDefaultParamStruct); + function GetPreviewBitmap(RemoteName:String;width,height:integer; var ReturnedBitmap:hbitmap):integer; + function LinksToLocalFiles:Boolean; + function GetLocalName(var RemoteName:String;maxlen:integer):Boolean; + end; + +type + TPluginFactory = reference to function:IWfxPlugin; + +var globalPluginFactory :TPluginFactory ; + +implementation + +uses System.SysUtils; + +{ TFileInfo } + +class function TFileInfo.Create: TFileInfo; +begin + Result.FileName := ''; + Result.Date := now; + Result.Size := 0; + Result.FileType := TFileType.ftFile; + Result.ReadOnly := False; + Result.IsVirtual := True; + Result.OnExecute := + procedure(aMainWin: THandle; aRemoteName, aVerb: string; sender:PFileInfo) + begin + + end; + +end; + +end. diff --git a/test/S3.Tests.dpr b/test/S3.Tests.dpr new file mode 100644 index 0000000..1a6be6a --- /dev/null +++ b/test/S3.Tests.dpr @@ -0,0 +1,71 @@ +program S3.Tests; + +{$IFNDEF TESTINSIGHT} +{$APPTYPE CONSOLE} +{$ENDIF} +{$STRONGLINKTYPES ON} +uses + FastMM4, + DUnitX.MemoryLeakMonitor.FastMM4, + System.SysUtils, + {$IFDEF TESTINSIGHT} + TestInsight.DUnitX, + {$ELSE} + DUnitX.Loggers.Console, + DUnitX.Loggers.Xml.NUnit, + {$ENDIF } + DUnitX.TestFramework, + Wfx.Plugin.S3.tests in 'Wfx.Plugin.S3.tests.pas', + Wfx.Plugin.S3.Path in '..\source\Wfx.Plugin.S3.Path.pas', + Wfx.Plugin.ExportProcs in '..\source\Wfx.Plugin.ExportProcs.pas', + Wfx.Plugin.Consts in '..\source\Wfx.Plugin.Consts.pas', + Wfx.Plugin.intf in '..\source\Wfx.Plugin.intf.pas', + Wfx.Plugin.Base in '..\source\Wfx.Plugin.Base.pas', + Wfx.Plugin.S3 in '..\source\Wfx.Plugin.S3.pas'; + +var + LRunner : ITestRunner; + LResults : IRunResults; + LLogger : ITestLogger; + LNUnitLogger : ITestLogger; +begin +{$IFDEF TESTINSIGHT} + TestInsight.DUnitX.RunRegisteredTests; + Exit; +{$ENDIF} + try + //Check command line optTDUnitX.RegisterTestFixture(TestFixtureClassName);ions, will exit if invalid + TDUnitX.CheckCommandLine; + //Create the test runner + LRunner := TDUnitX.CreateRunner; + //Tell the runner to use RTTI to find Fixtures + LRunner.UseRTTI := True; + //tell the runner how we will log things + //Log to the console window + LLogger := TDUnitXConsoleLogger.Create(true); + LRunner.AddLogger(LLogger); + //Generate an NUnit compatible XML File + TDUnitX.Options.ExitBehavior := TDUnitXExitBehavior.Pause; + LNUnitLogger := TDUnitXXMLNUnitFileLogger.Create(TDUnitX.Options.XMLOutputFile); + LRunner.AddLogger(LNUnitLogger); + LRunner.FailsOnNoAsserts := False; //When true, Assertions must be made during tests; + + //Run tests + LResults := LRunner.Execute; + if not LResults.AllPassed then + System.ExitCode := EXIT_ERRORS; + + {$IFNDEF CI} + //We don't want this happening when running under CI. + if TDUnitX.Options.ExitBehavior = TDUnitXExitBehavior.Pause then + begin + System.Write('Done.. press key to quit.'); + System.Readln; + end; + {$ENDIF} + except + on E: Exception do + System.Writeln(E.ClassName, ': ', E.Message); + end; +end. + diff --git a/test/S3.Tests.dproj b/test/S3.Tests.dproj new file mode 100644 index 0000000..44193d2 --- /dev/null +++ b/test/S3.Tests.dproj @@ -0,0 +1,958 @@ + + + {7CFA85E4-E75B-4B2C-B5ED-2CE6031B6CE6} + 19.3 + VCL + True + Debug + Win32 + 1 + Console + S3.Tests.dpr + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + true + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + $(DUnitX);$(DCC_UnitSearchPath) + S3_Tests + SKIA;$(DCC_Define) + + + soapserver;IndySystem;fmx;DbxCommonDriver;bindengine;IndyIPCommon;FireDACCommonDriver;IndyProtocols;IndyIPClient;dbxcds;bindcomp;FmxTeeUI;FireDACCommon;Skia.Package.RTL;IndyCore;RESTBackendComponents;bindcompfmx;bindcompdbx;rtl;FireDACSqliteDriver;DbxClientDriver;RESTComponents;DBXSqliteDriver;IndyIPServer;fmxFireDAC;dbexpress;dsnapxml;soapmidas;inet;FireDAC;fmxase;xmlrtl;tethering;dbrtl;dsnap;Skia.Package.FMX;CloudService;CustomIPTransport;FMXTee;soaprtl;DBXInterBaseDriver;FireDACIBDriver;$(DCC_UsePackage) + + + soapserver;IndySystem;fmx;DbxCommonDriver;bindengine;IndyIPCommon;FireDACCommonODBC;FireDACCommonDriver;IndyProtocols;IndyIPClient;dbxcds;bindcomp;FmxTeeUI;FireDACCommon;Skia.Package.RTL;IndyCore;RESTBackendComponents;bindcompfmx;bindcompdbx;inetdb;rtl;FireDACMySQLDriver;FireDACSqliteDriver;DbxClientDriver;RESTComponents;DBXSqliteDriver;IndyIPServer;fmxFireDAC;dbexpress;dsnapxml;soapmidas;DBXMySQLDriver;inet;FireDACPgDriver;FireDAC;fmxase;inetdbxpress;xmlrtl;tethering;dbrtl;dsnap;fmxdae;Skia.Package.FMX;CloudService;CustomIPTransport;fmxobj;FMXTee;soaprtl;DBXInterBaseDriver;FireDACIBDriver;$(DCC_UsePackage) + + + soapserver;IndySystem;fmx;DbxCommonDriver;bindengine;IndyIPCommon;FireDACCommonODBC;FireDACCommonDriver;IndyProtocols;IndyIPClient;dbxcds;bindcomp;FmxTeeUI;FireDACCommon;IndyCore;RESTBackendComponents;bindcompfmx;bindcompdbx;inetdb;rtl;FireDACMySQLDriver;FireDACSqliteDriver;DbxClientDriver;RESTComponents;DBXSqliteDriver;IndyIPServer;fmxFireDAC;dbexpress;dsnapxml;soapmidas;DBXMySQLDriver;inet;FireDACPgDriver;FireDAC;fmxase;inetdbxpress;xmlrtl;tethering;dbrtl;dsnap;fmxdae;CloudService;CustomIPTransport;fmxobj;FMXTee;soaprtl;DBXInterBaseDriver;FireDACIBDriver;$(DCC_UsePackage) + + + soapserver;DAV_ASIOHost_D19;IndySystem;DAV_VSTHost_D19;vclwinx;DAV_DSP_D19;fmx;Skia.Package.VCL;vclie;DbxCommonDriver;bindengine;vcldb;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;DAV_GUI_D19;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;IconFontsImageList;SynEditDR;IndyIPClient;dbxcds;vcledge;vclFireDAC;LSPComponents;bindcompvclwinx;bindcomp;FmxTeeUI;FireDACCommon;Skia.Package.RTL;MidiControls;MidiComponents2010;IndyCore;RESTBackendComponents;bindcompfmx;bindcompdbx;inetdb;rtl;FireDACMySQLDriver;DAV_VSTPlugin_D19;FireDACSqliteDriver;DbxClientDriver;FireDACADSDriver;GR32_R;Tee;RESTComponents;DBXSqliteDriver;vcl;vclactnband;TeeUI;IndyIPServer;fmxFireDAC;dbexpress;dsnapxml;dsnapcon;soapmidas;adortl;SVGIconImageListFMX;DBXMySQLDriver;VirtualTreesDR;DAV_Common_D19;VclSmp;inet;DAV_SEHost_D19;vclimg;vcltouch;FireDACPgDriver;FireDAC;fmxase;inetdbxpress;xmlrtl;tethering;GR32_D;dbrtl;ARH;bindcompvcl;dsnap;fmxdae;TeeDB;Skia.Package.FMX;CloudService;FireDACMSAccDriver;CustomIPTransport;SVGIconPackage;IconFontsImageListFMX;fmxobj;bindcompvclsmp;FMXTee;SVGIconImageList;soaprtl;vcldsnap;DBXInterBaseDriver;FireDACIBDriver;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + + + soapserver;IndySystem;vclwinx;fmx;Skia.Package.VCL;vclie;DbxCommonDriver;bindengine;vcldb;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;appanalytics;IndyProtocols;vclx;SynEditDR;IndyIPClient;dbxcds;vcledge;vclFireDAC;LSPComponents;bindcompvclwinx;bindcomp;FmxTeeUI;FireDACCommon;Skia.Package.RTL;IndyCore;RESTBackendComponents;bindcompfmx;bindcompdbx;inetdb;rtl;FireDACMySQLDriver;FireDACSqliteDriver;DbxClientDriver;FireDACADSDriver;Tee;RESTComponents;DBXSqliteDriver;vcl;vclactnband;TeeUI;IndyIPServer;fmxFireDAC;dbexpress;dsnapxml;dsnapcon;soapmidas;adortl;SVGIconImageListFMX;DBXMySQLDriver;VirtualTreesDR;VclSmp;inet;vclimg;vcltouch;FireDACPgDriver;FireDAC;fmxase;inetdbxpress;xmlrtl;tethering;dbrtl;bindcompvcl;dsnap;fmxdae;TeeDB;Skia.Package.FMX;CloudService;FireDACMSAccDriver;CustomIPTransport;SVGIconPackage;fmxobj;bindcompvclsmp;FMXTee;SVGIconImageList;soaprtl;vcldsnap;DBXInterBaseDriver;FireDACIBDriver;$(DCC_UsePackage) + + + DEBUG;$(DCC_Define) + true + false + true + true + true + true + true + + + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + + + + + + + + + Base + + + Cfg_1 + Base + + + Cfg_2 + Base + + + + Delphi.Personality.12 + Console + + + + S3.Tests.dpr + + + + + + .\ + 0 + sk4d.dll + true + + + + + S3_Tests.exe + true + + + + + true + + + + + .\ + 0 + sk4d.dll + true + + + + + true + + + + + true + + + + + Contents\MacOS\ + 1 + sk4d.dylib + true + + + + + Contents\MacOS\ + 1 + sk4d.dylib + true + + + + + .\ + 0 + sk4d.dll + true + + + + + .\ + 0 + sk4d.dll + true + + + + + Contents\MacOS\ + 1 + sk4d.dylib + true + + + + + Contents\MacOS\ + 1 + sk4d.dylib + true + + + + + 1 + + + 0 + + + + + classes + 64 + + + classes + 64 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + 1 + + + 1 + + + 0 + + + + + 1 + .framework + + + 1 + .framework + + + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + + + + 1 + + + 1 + + + 1 + + + + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + + + + + + + + + + + + False + False + False + True + False + + + 12 + + + + + diff --git a/test/S3.Tests.res b/test/S3.Tests.res new file mode 100644 index 0000000..73d4f5a Binary files /dev/null and b/test/S3.Tests.res differ diff --git a/test/Wfx.Plugin.S3.tests.pas b/test/Wfx.Plugin.S3.tests.pas new file mode 100644 index 0000000..351bb0c --- /dev/null +++ b/test/Wfx.Plugin.S3.tests.pas @@ -0,0 +1,53 @@ +unit Wfx.Plugin.S3.tests; + +interface + +uses + + Wfx.Plugin.Intf, + Wfx.Plugin.S3, + DUnitX.TestFramework; + +type + [TestFixture] + S3PluginFixture = class + SUT : TS3Plugin; + public + [Setup] + procedure Setup; + [TearDown] + procedure TearDown; + + [Test] + procedure PluginNameNotEmpty; + + [Test] + procedure InitDoesNotRaise; + end; + +implementation + +procedure S3PluginFixture.Setup; +begin + SUT := TS3Plugin.Create; +end; + +procedure S3PluginFixture.TearDown; +begin + SUT.Free; +end; + +procedure S3PluginFixture.PluginNameNotEmpty; +begin + Assert.IsNotEmpty(SUT.GetPluginName); +end; + +procedure S3PluginFixture.InitDoesNotRaise(); +begin + Assert.WillNotRaiseAny( SUT.Init ); +end; + +initialization + TDUnitX.RegisterTestFixture(S3PluginFixture); + +end.