Skip to content

FTP! #3610

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed

FTP! #3610

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Files.Package/Package.appxmanifest
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<rescap:Capability Name="broadFileSystemAccess" />
<rescap:Capability Name="allowElevation"/>
<uap:Capability Name="removableStorage" />
<Capability Name="internetClient"/>
<Capability Name="privateNetworkClientServer"/>
<Capability Name="internetClient"/>
</Capabilities>
</Package>
8 changes: 7 additions & 1 deletion Files/Files.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<UseVSHostingProcess>false</UseVSHostingProcess>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
<LangVersion>8.0</LangVersion>
<LangVersion>latest</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<UseDotNetNativeToolchain>false</UseDotNetNativeToolchain>
</PropertyGroup>
Expand Down Expand Up @@ -183,6 +183,8 @@
<Compile Include="Enums\DynamicDialogResult.cs" />
<Compile Include="Enums\FolderLayout.cs" />
<Compile Include="Enums\FolderLayoutModes.cs" />
<Compile Include="Filesystem\FilesystemOperations\FtpFilesystemOperaions.cs" />
<Compile Include="Filesystem\StorageEnumerators\FtpStorageEnumerator.cs" />
<Compile Include="EventArguments\LayoutPreferenceEventArgs.cs" />
<Compile Include="Extensions\TaskExtensions.cs" />
<Compile Include="Filesystem\FolderHelpers.cs" />
Expand All @@ -196,6 +198,7 @@
<Compile Include="Helpers\AdaptiveLayoutHelpers.cs" />
<Compile Include="Filesystem\Cloud\Providers\OneDriveSharePointCloudProvider.cs" />
<Compile Include="Filesystem\NetworkDrivesManager.cs" />
<Compile Include="Filesystem\StorageFileHelpers\FtpStorageItem.cs" />
<Compile Include="Helpers\AppServiceConnectionHelper.cs" />
<Compile Include="Filesystem\Cloud\Providers\AmazonDriveProvider.cs" />
<Compile Include="Helpers\AppUpdater.cs" />
Expand Down Expand Up @@ -959,6 +962,9 @@
<PackageReference Include="ByteSize">
<Version>2.0.0</Version>
</PackageReference>
<PackageReference Include="FluentFTP">
<Version>33.0.3</Version>
</PackageReference>
<PackageReference Include="ini-parser-netstandard">
<Version>2.5.2</Version>
</PackageReference>
Expand Down
105 changes: 59 additions & 46 deletions Files/Filesystem/FilesystemOperations/FilesystemOperations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public FilesystemOperations(IShellPage associatedInstance)

#region IFilesystemOperations

public async Task<IStorageHistory> CreateAsync(IStorageItemWithPath source, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
public async Task<IStorageHistory> CreateAsync(IStorageItem source, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
{
try
{
Expand Down Expand Up @@ -112,7 +112,7 @@ public async Task<IStorageHistory> CopyAsync(IStorageItem source,
cancellationToken);
}

public async Task<IStorageHistory> CopyAsync(IStorageItemWithPath source,
public async Task<IStorageHistory> CopyAsync(IStorageItem source,
string destination,
IProgress<float> progress,
IProgress<FileSystemStatusCode> errorCode,
Expand Down Expand Up @@ -224,57 +224,66 @@ await DialogDisplayHelper.ShowDialogAsync(
}
else if (source.ItemType == FilesystemItemType.File)
{
var fsResult = (FilesystemResult)await Task.Run(() => NativeFileOperationsHelper.CopyFileFromApp(source.Path, destination, true));

if (!fsResult)
FilesystemResult fsResult;
if (IsFtp(source.Path))
{
Debug.WriteLine(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

FilesystemResult<StorageFolder> destinationResult = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(Path.GetDirectoryName(destination));
var sourceResult = await source.ToStorageItemResult(associatedInstance);
fsResult = sourceResult.ErrorCode | destinationResult.ErrorCode;
var ftpOperation = new FtpFilesystemOperaions();
return await ftpOperation.CopyAsync(source, destination, progress, errorCode, cancellationToken);
}
else
{
fsResult = (FilesystemResult)await Task.Run(() => NativeFileOperationsHelper.CopyFileFromApp(source.Path, destination, true));

if (fsResult)
if (!fsResult)
{
var file = (StorageFile)sourceResult;
var fsResultCopy = await FilesystemTasks.Wrap(() => file.CopyAsync(destinationResult.Result, Path.GetFileName(file.Name), NameCollisionOption.FailIfExists).AsTask());
if (fsResultCopy == FileSystemStatusCode.AlreadyExists)
Debug.WriteLine(System.Runtime.InteropServices.Marshal.GetLastWin32Error());

FilesystemResult<StorageFolder> destinationResult = await associatedInstance.FilesystemViewModel.GetFolderFromPathAsync(Path.GetDirectoryName(destination));
var sourceResult = await source.ToStorageItemResult(associatedInstance);
fsResult = sourceResult.ErrorCode | destinationResult.ErrorCode;

if (fsResult)
{
var ItemAlreadyExistsDialog = new ContentDialog()
var file = (StorageFile)sourceResult;
var fsResultCopy = await FilesystemTasks.Wrap(() => file.CopyAsync(destinationResult.Result, Path.GetFileName(file.Name), NameCollisionOption.FailIfExists).AsTask());
if (fsResultCopy == FileSystemStatusCode.AlreadyExists)
{
Title = "ItemAlreadyExistsDialogTitle".GetLocalized(),
Content = "ItemAlreadyExistsDialogContent".GetLocalized(),
PrimaryButtonText = "ItemAlreadyExistsDialogPrimaryButtonText".GetLocalized(),
SecondaryButtonText = "ItemAlreadyExistsDialogSecondaryButtonText".GetLocalized(),
CloseButtonText = "ItemAlreadyExistsDialogCloseButtonText".GetLocalized()
};
var ItemAlreadyExistsDialog = new ContentDialog()
{
Title = "ItemAlreadyExistsDialogTitle".GetLocalized(),
Content = "ItemAlreadyExistsDialogContent".GetLocalized(),
PrimaryButtonText = "ItemAlreadyExistsDialogPrimaryButtonText".GetLocalized(),
SecondaryButtonText = "ItemAlreadyExistsDialogSecondaryButtonText".GetLocalized(),
CloseButtonText = "ItemAlreadyExistsDialogCloseButtonText".GetLocalized()
};

if (Interacts.Interaction.IsAnyContentDialogOpen())
{
// Only a single ContentDialog can be open at any time.
return null;
}
ContentDialogResult result = await ItemAlreadyExistsDialog.ShowAsync();
if (Interacts.Interaction.IsAnyContentDialogOpen())
{
// Only a single ContentDialog can be open at any time.
return null;
}
ContentDialogResult result = await ItemAlreadyExistsDialog.ShowAsync();

if (result == ContentDialogResult.Primary)
{
fsResultCopy = await FilesystemTasks.Wrap(() => file.CopyAsync(destinationResult.Result, Path.GetFileName(file.Name), NameCollisionOption.GenerateUniqueName).AsTask());
}
else if (result == ContentDialogResult.Secondary)
{
fsResultCopy = await FilesystemTasks.Wrap(() => file.CopyAsync(destinationResult.Result, Path.GetFileName(file.Name), NameCollisionOption.ReplaceExisting).AsTask());
return null; // Cannot undo overwrite operation
if (result == ContentDialogResult.Primary)
{
fsResultCopy = await FilesystemTasks.Wrap(() => file.CopyAsync(destinationResult.Result, Path.GetFileName(file.Name), NameCollisionOption.GenerateUniqueName).AsTask());
}
else if (result == ContentDialogResult.Secondary)
{
fsResultCopy = await FilesystemTasks.Wrap(() => file.CopyAsync(destinationResult.Result, Path.GetFileName(file.Name), NameCollisionOption.ReplaceExisting).AsTask());
return null; // Cannot undo overwrite operation
}
else
{
return null;
}
}
else
if (fsResultCopy)
{
return null;
copiedItem = fsResultCopy.Result;
}
fsResult = fsResultCopy;
}
if (fsResultCopy)
{
copiedItem = fsResultCopy.Result;
}
fsResult = fsResultCopy;
}
}
errorCode?.Report(fsResult.ErrorCode);
Expand Down Expand Up @@ -320,7 +329,7 @@ public async Task<IStorageHistory> MoveAsync(IStorageItem source,
cancellationToken);
}

public async Task<IStorageHistory> MoveAsync(IStorageItemWithPath source,
public async Task<IStorageHistory> MoveAsync(IStorageItem source,
string destination,
IProgress<float> progress,
IProgress<FileSystemStatusCode> errorCode,
Expand Down Expand Up @@ -541,7 +550,7 @@ public async Task<IStorageHistory> DeleteAsync(IStorageItem source,
cancellationToken);
}

public async Task<IStorageHistory> DeleteAsync(IStorageItemWithPath source,
public async Task<IStorageHistory> DeleteAsync(IStorageItem source,
IProgress<float> progress,
IProgress<FileSystemStatusCode> errorCode,
bool permanently,
Expand Down Expand Up @@ -661,7 +670,7 @@ public async Task<IStorageHistory> RenameAsync(IStorageItem source,
return await RenameAsync(StorageItemHelpers.FromStorageItem(source), newName, collision, errorCode, cancellationToken);
}

public async Task<IStorageHistory> RenameAsync(IStorageItemWithPath source,
public async Task<IStorageHistory> RenameAsync(IStorageItem source,
string newName,
NameCollisionOption collision,
IProgress<FileSystemStatusCode> errorCode,
Expand Down Expand Up @@ -760,7 +769,7 @@ public async Task<IStorageHistory> RenameAsync(IStorageItemWithPath source,
return null;
}

public async Task<IStorageHistory> RestoreFromTrashAsync(IStorageItemWithPath source,
public async Task<IStorageHistory> RestoreFromTrashAsync(IStorageItem source,
string destination,
IProgress<float> progress,
IProgress<FileSystemStatusCode> errorCode,
Expand Down Expand Up @@ -846,6 +855,10 @@ await associatedInstance.FilesystemViewModel.GetFileFromPathAsync(iFilePath)
#endregion IFilesystemOperations

#region Helpers
private static bool IsFtp(string path)
{
return path.StartsWith("ftp://", StringComparison.OrdinalIgnoreCase) || path.StartsWith("ftps://", StringComparison.OrdinalIgnoreCase);
}

private async static Task<StorageFolder> CloneDirectoryAsync(IStorageFolder sourceFolder, IStorageFolder destinationFolder, string sourceRootName, CreationCollisionOption collision = CreationCollisionOption.FailIfExists)
{
Expand Down
134 changes: 134 additions & 0 deletions Files/Filesystem/FilesystemOperations/FtpFilesystemOperaions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
using Files.Enums;
using Files.Filesystem.FilesystemHistory;
using Files.Filesystem.StorageFileHelpers;
using FluentFTP;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Windows.Storage;

namespace Files.Filesystem
{
public class FtpFilesystemOperaions : IFilesystemOperations
{
public async Task<IStorageHistory> CopyAsync(IStorageItem source, string destination, IProgress<float> progress, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
{
await Task.Run(async () =>
{
if (!(source is StorageFile item))
{
return;
}

var ftpProgress = new Progress<FtpProgress>();

errorCode.Report(FileSystemStatusCode.InProgress);

ftpProgress.ProgressChanged += (_, args) =>
{
progress.Report(Convert.ToSingle(args.Progress));
};

try
{

if (item.IsOfType(StorageItemTypes.File))
{
using var fileStream = new FileStream(destination, FileMode.Create);
await item.CopyAsync(await StorageFolder.GetFolderFromPathAsync(destination));
errorCode.Report(FileSystemStatusCode.Success);
return;
}
else
{
throw new NotImplementedException();
}
}
catch (UnauthorizedAccessException)
{
errorCode.Report(FileSystemStatusCode.Unauthorized);
}
catch
{
errorCode.Report(FileSystemStatusCode.Generic);
}
});

return null;
}

public Task<IStorageHistory> CopyAsync(IStorageItemWithPath source, string destination, IProgress<float> progress, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
{
return CopyAsync(source.Item, destination, progress, errorCode, cancellationToken);
}

public Task<IStorageHistory> CreateAsync(IStorageItemWithPath source, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken) => throw new NotImplementedException();
public async Task<IStorageHistory> DeleteAsync(IStorageItem source, IProgress<float> progress, IProgress<FileSystemStatusCode> errorCode, bool permanently, CancellationToken cancellationToken)
{
await Task.Run(async () =>
{
errorCode.Report(FileSystemStatusCode.InProgress);
progress.Report(0);
try
{
await source.DeleteAsync();
progress.Report(100);
errorCode.Report(FileSystemStatusCode.Success);
}
catch
{
errorCode.Report(FileSystemStatusCode.Generic);
}
});

return null;
}
public Task<IStorageHistory> DeleteAsync(IStorageItemWithPath source, IProgress<float> progress, IProgress<FileSystemStatusCode> errorCode, bool permanently, CancellationToken cancellationToken)
{
return DeleteAsync(source.Item, progress, errorCode, permanently, cancellationToken);
}

public void Dispose() { }

public async Task<IStorageHistory> MoveAsync(IStorageItem source, string destination, IProgress<float> progress, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
{
await CopyAsync(source, destination, progress, errorCode, cancellationToken);
await DeleteAsync(source, progress, errorCode, true, cancellationToken);

return null;
}
public Task<IStorageHistory> MoveAsync(IStorageItemWithPath source, string destination, IProgress<float> progress, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
{
return MoveAsync(source.Item, destination, progress, errorCode, cancellationToken);
}
public async Task<IStorageHistory> RenameAsync(IStorageItem source, string newName, NameCollisionOption collision, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
{
await Task.Run(async () =>
{
errorCode.Report(FileSystemStatusCode.InProgress); ;
try
{
await source.RenameAsync(newName, collision);
errorCode.Report(FileSystemStatusCode.Success);
}
catch
{
errorCode.Report(FileSystemStatusCode.Generic);
}
});

return null;
}

public Task<IStorageHistory> RenameAsync(IStorageItemWithPath source, string newName, NameCollisionOption collision, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
{
return RenameAsync(source.Item, newName, collision, errorCode, cancellationToken);
}

public Task<IStorageHistory> RestoreFromTrashAsync(IStorageItemWithPath source, string destination, IProgress<float> progress, IProgress<FileSystemStatusCode> errorCode, CancellationToken cancellationToken)
{
throw new NotSupportedException();
}
}
}
Loading