diff --git a/Common/Common.csproj b/Common/Common.csproj
index 9e57fd0..cb8f71e 100644
--- a/Common/Common.csproj
+++ b/Common/Common.csproj
@@ -10,7 +10,7 @@
AnyCPU;x64
-
-
+
+
\ No newline at end of file
diff --git a/Windows/Common/Core/Common.Windows.Core.csproj b/Windows/Common/Core/Common.Windows.Core.csproj
index 2ddd7f1..d805f48 100644
--- a/Windows/Common/Core/Common.Windows.Core.csproj
+++ b/Windows/Common/Core/Common.Windows.Core.csproj
@@ -1,6 +1,6 @@
- net6.0-windows10.0.19041.0;net48
+ net7.0-windows10.0.19041.0;net48
Contains functionality common for all Windows Virtual Drive samples.
IT Hit LTD.
IT Hit User File System
@@ -20,8 +20,8 @@
-
-
+
+
\ No newline at end of file
diff --git a/Windows/Common/VirtualDrive/Common.Windows.VirtualDrive.csproj b/Windows/Common/VirtualDrive/Common.Windows.VirtualDrive.csproj
index 432caab..5241897 100644
--- a/Windows/Common/VirtualDrive/Common.Windows.VirtualDrive.csproj
+++ b/Windows/Common/VirtualDrive/Common.Windows.VirtualDrive.csproj
@@ -1,6 +1,6 @@
- net6.0-windows10.0.19041.0;net48
+ net7.0-windows10.0.19041.0;net48
Contains functionality common for all Windows Virtual Drive samples.
IT Hit LTD.
IT Hit User File System
@@ -13,7 +13,7 @@
-
+
diff --git a/Windows/VirtualDrive/VirtualDrive.ShellExtension/VirtualDrive.ShellExtension.csproj b/Windows/VirtualDrive/VirtualDrive.ShellExtension/VirtualDrive.ShellExtension.csproj
index efe9ac1..3418223 100644
--- a/Windows/VirtualDrive/VirtualDrive.ShellExtension/VirtualDrive.ShellExtension.csproj
+++ b/Windows/VirtualDrive/VirtualDrive.ShellExtension/VirtualDrive.ShellExtension.csproj
@@ -1,6 +1,6 @@
- net6.0-windows10.0.19041.0
+ net7.0-windows10.0.19041.0
True
x64
@@ -19,7 +19,7 @@
-
+
diff --git a/Windows/VirtualDrive/VirtualDrive/VirtualDrive.csproj b/Windows/VirtualDrive/VirtualDrive/VirtualDrive.csproj
index 3218022..15b79aa 100644
--- a/Windows/VirtualDrive/VirtualDrive/VirtualDrive.csproj
+++ b/Windows/VirtualDrive/VirtualDrive/VirtualDrive.csproj
@@ -1,7 +1,7 @@
Exe
- net6.0-windows10.0.19041.0
+ net7.0-windows10.0.19041.0
IT Hit LTD.
IT Hit LTD.
Virtual Drive
@@ -40,7 +40,7 @@ This is an advanced project with ETags support, Microsoft Office documents editi
-
+
diff --git a/Windows/VirtualDrive/VirtualDrive/VirtualFile.cs b/Windows/VirtualDrive/VirtualDrive/VirtualFile.cs
index 47a4ffa..471ce09 100644
--- a/Windows/VirtualDrive/VirtualDrive/VirtualFile.cs
+++ b/Windows/VirtualDrive/VirtualDrive/VirtualFile.cs
@@ -90,11 +90,11 @@ public async Task ValidateDataAsync(long offset, long length, IValidateDataOpera
}
///
- public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFile)}.{nameof(WriteAsync)}()", UserFileSystemPath, default, operationContext);
- if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return;
+ if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return null;
// Send the ETag to the server as part of the update to ensure
// the file in the remote storge is not modified since last read.
@@ -129,15 +129,36 @@ public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null,
}
// Update remote storage file metadata.
- remoteStorageItem.Attributes = fileMetadata.Attributes & ~FileAttributes.ReadOnly;
- remoteStorageItem.CreationTimeUtc = fileMetadata.CreationTime.UtcDateTime;
- remoteStorageItem.LastWriteTimeUtc = fileMetadata.LastWriteTime.UtcDateTime;
- remoteStorageItem.LastAccessTimeUtc = fileMetadata.LastAccessTime.UtcDateTime;
- remoteStorageItem.Attributes = fileMetadata.Attributes;
+ if (fileBasicInfo.Attributes.HasValue)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value & ~FileAttributes.ReadOnly;
+ }
+
+ if (fileBasicInfo.CreationTime.HasValue)
+ {
+ remoteStorageItem.CreationTimeUtc = fileBasicInfo.CreationTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastWriteTime.HasValue)
+ {
+ remoteStorageItem.LastWriteTimeUtc = fileBasicInfo.LastWriteTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastAccessTime.HasValue)
+ {
+ remoteStorageItem.LastAccessTimeUtc = fileBasicInfo.LastAccessTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.Attributes.HasValue)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value;
+ }
// Save ETag received from your remote storage in persistent placeholder properties.
// string newEtag = ...
// placeholder.SetETag(newEtag);
+
+ return null;
}
}
}
diff --git a/Windows/VirtualDrive/VirtualDrive/VirtualFolder.cs b/Windows/VirtualDrive/VirtualDrive/VirtualFolder.cs
index fdbcd4a..ea28b09 100644
--- a/Windows/VirtualDrive/VirtualDrive/VirtualFolder.cs
+++ b/Windows/VirtualDrive/VirtualDrive/VirtualFolder.cs
@@ -168,19 +168,40 @@ public async Task> EnumerateChildrenAsync
}
///
- public async Task WriteAsync(IFolderMetadata folderMetadata, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFolder)}.{nameof(WriteAsync)}()", UserFileSystemPath, default, operationContext);
- if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return;
+ if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return null;
DirectoryInfo remoteStorageItem = new DirectoryInfo(remoteStoragePath);
// Update remote storage folder metadata.
- remoteStorageItem.Attributes = folderMetadata.Attributes & ~FileAttributes.ReadOnly;
- remoteStorageItem.CreationTimeUtc = folderMetadata.CreationTime.UtcDateTime;
- remoteStorageItem.LastWriteTimeUtc = folderMetadata.LastWriteTime.UtcDateTime;
- remoteStorageItem.LastAccessTimeUtc = folderMetadata.LastAccessTime.UtcDateTime;
- remoteStorageItem.Attributes = folderMetadata.Attributes;
+ if (fileBasicInfo.Attributes.HasValue)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value & ~FileAttributes.ReadOnly;
+ }
+
+ if (fileBasicInfo.CreationTime.HasValue)
+ {
+ remoteStorageItem.CreationTimeUtc = fileBasicInfo.CreationTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastWriteTime.HasValue)
+ {
+ remoteStorageItem.LastWriteTimeUtc = fileBasicInfo.LastWriteTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastAccessTime.HasValue)
+ {
+ remoteStorageItem.LastAccessTimeUtc = fileBasicInfo.LastAccessTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.Attributes.HasValue)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value;
+ }
+
+ return null;
}
diff --git a/Windows/VirtualDrive/VirtualDrive/appsettings.json b/Windows/VirtualDrive/VirtualDrive/appsettings.json
index 15853cb..b9041b0 100644
--- a/Windows/VirtualDrive/VirtualDrive/appsettings.json
+++ b/Windows/VirtualDrive/VirtualDrive/appsettings.json
@@ -23,7 +23,7 @@
// Your virtual file system will be mounted under this path.
// Make sure to delete the all plceholders created by previous version of the software under the sync root.
- "UserFileSystemRootPath": "%USERPROFILE%\\VirtualDriveV7\\",
+ "UserFileSystemRootPath": "%USERPROFILE%\\VirtualDriveV73\\",
// Full synchronization interval in milliseconds.
diff --git a/Windows/VirtualFileSystem/RemoteStorageMonitor.cs b/Windows/VirtualFileSystem/RemoteStorageMonitor.cs
index 7725593..31a17f4 100644
--- a/Windows/VirtualFileSystem/RemoteStorageMonitor.cs
+++ b/Windows/VirtualFileSystem/RemoteStorageMonitor.cs
@@ -317,7 +317,7 @@ internal static bool IsModified(string userFileSystemPath, string remoteStorageP
FileInfo fiUserFileSystem = new FileInfo(userFileSystemPath);
FileInfo fiRemoteStorage = new FileInfo(remoteStoragePath);
- // This check is to prevent circular calls. In you real app you wouuld not send notifications to the client that generated the event.
+ // This check is to prevent circular calls. In your real app you would not send notifications to the client that generated the event.
if (fiUserFileSystem.LastWriteTimeUtc >= fiRemoteStorage.LastWriteTimeUtc)
{
return false;
@@ -328,7 +328,7 @@ internal static bool IsModified(string userFileSystemPath, string remoteStorageP
if (fiUserFileSystem.Length == fiRemoteStorage.Length)
{
// Verify that the file is not offline,
- // therwise the file will be hydrated when the file stream is opened.
+ // otherwise the file will be hydrated when the file stream is opened.
if (fiUserFileSystem.Attributes.HasFlag(System.IO.FileAttributes.Offline)
|| fiUserFileSystem.Attributes.HasFlag(System.IO.FileAttributes.Offline))
{
diff --git a/Windows/VirtualFileSystem/VirtualFile.cs b/Windows/VirtualFileSystem/VirtualFile.cs
index 30b0dc9..997cc49 100644
--- a/Windows/VirtualFileSystem/VirtualFile.cs
+++ b/Windows/VirtualFileSystem/VirtualFile.cs
@@ -83,11 +83,11 @@ public async Task ValidateDataAsync(long offset, long length, IValidateDataOpera
}
///
- public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFile)}.{nameof(WriteAsync)}()", UserFileSystemPath, default, operationContext);
- if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return;
+ if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return null;
FileInfo remoteStorageItem = new FileInfo(remoteStoragePath);
@@ -102,11 +102,32 @@ public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null,
}
// Update remote storage file metadata.
- remoteStorageItem.Attributes = fileMetadata.Attributes & ~FileAttributes.ReadOnly;
- remoteStorageItem.CreationTimeUtc = fileMetadata.CreationTime.UtcDateTime;
- remoteStorageItem.LastWriteTimeUtc = fileMetadata.LastWriteTime.UtcDateTime;
- remoteStorageItem.LastAccessTimeUtc = fileMetadata.LastAccessTime.UtcDateTime;
- remoteStorageItem.Attributes = fileMetadata.Attributes;
+ if (fileBasicInfo.Attributes.HasValue)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value & ~FileAttributes.ReadOnly;
+ }
+
+ if (fileBasicInfo.CreationTime.HasValue)
+ {
+ remoteStorageItem.CreationTimeUtc = fileBasicInfo.CreationTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastWriteTime.HasValue)
+ {
+ remoteStorageItem.LastWriteTimeUtc = fileBasicInfo.LastWriteTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastAccessTime.HasValue)
+ {
+ remoteStorageItem.LastAccessTimeUtc = fileBasicInfo.LastAccessTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.Attributes.HasValue)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value;
+ }
+
+ return null;
}
}
}
diff --git a/Windows/VirtualFileSystem/VirtualFileSystem.csproj b/Windows/VirtualFileSystem/VirtualFileSystem.csproj
index f565ca6..68cdefd 100644
--- a/Windows/VirtualFileSystem/VirtualFileSystem.csproj
+++ b/Windows/VirtualFileSystem/VirtualFileSystem.csproj
@@ -1,7 +1,7 @@
Exe
- net6.0-windows10.0.19041.0;net48
+ net7.0-windows10.0.19041.0;net48
IT Hit LTD.
IT Hit LTD.
Virtual File System
diff --git a/Windows/VirtualFileSystem/VirtualFolder.cs b/Windows/VirtualFileSystem/VirtualFolder.cs
index 9c06b43..dbd4a0b 100644
--- a/Windows/VirtualFileSystem/VirtualFolder.cs
+++ b/Windows/VirtualFileSystem/VirtualFolder.cs
@@ -141,19 +141,40 @@ public async Task GetChildrenAsync(string pattern, IOperationContext operationCo
}
///
- public async Task WriteAsync(IFolderMetadata folderMetadata, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFolder)}.{nameof(WriteAsync)}()", UserFileSystemPath, default, operationContext);
- if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return;
+ if (!Mapping.TryGetRemoteStoragePathById(RemoteStorageItemId, out string remoteStoragePath)) return null;
DirectoryInfo remoteStorageItem = new DirectoryInfo(remoteStoragePath);
// Update remote storage folder metadata.
- remoteStorageItem.Attributes = folderMetadata.Attributes & ~FileAttributes.ReadOnly;
- remoteStorageItem.CreationTimeUtc = folderMetadata.CreationTime.UtcDateTime;
- remoteStorageItem.LastWriteTimeUtc = folderMetadata.LastWriteTime.UtcDateTime;
- remoteStorageItem.LastAccessTimeUtc = folderMetadata.LastAccessTime.UtcDateTime;
- remoteStorageItem.Attributes = folderMetadata.Attributes;
+ if (fileBasicInfo.Attributes.HasValue)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value & ~FileAttributes.ReadOnly;
+ }
+
+ if (fileBasicInfo.CreationTime.HasValue)
+ {
+ remoteStorageItem.CreationTimeUtc = fileBasicInfo.CreationTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastWriteTime.HasValue)
+ {
+ remoteStorageItem.LastWriteTimeUtc = fileBasicInfo.LastWriteTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastAccessTime.HasValue)
+ {
+ remoteStorageItem.LastAccessTimeUtc = fileBasicInfo.LastAccessTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.Attributes.HasValue)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value;
+ }
+
+ return null;
}
}
diff --git a/Windows/VirtualFileSystem/appsettings.json b/Windows/VirtualFileSystem/appsettings.json
index 5e6b7ee..5149ffe 100644
--- a/Windows/VirtualFileSystem/appsettings.json
+++ b/Windows/VirtualFileSystem/appsettings.json
@@ -23,7 +23,7 @@
// Your virtual file system will be mounted under this path.
// Make sure to delete the all plceholders created by previous version of the software under the sync root.
- "UserFileSystemRootPath": "%USERPROFILE%\\VFSv7\\"
+ "UserFileSystemRootPath": "%USERPROFILE%\\VFSv73\\"
// To test performance:
diff --git a/Windows/WebDAVDrive/WebDAVDrive.ShellExtension/WebDAVDrive.ShellExtension.csproj b/Windows/WebDAVDrive/WebDAVDrive.ShellExtension/WebDAVDrive.ShellExtension.csproj
index fa211cc..1b70623 100644
--- a/Windows/WebDAVDrive/WebDAVDrive.ShellExtension/WebDAVDrive.ShellExtension.csproj
+++ b/Windows/WebDAVDrive/WebDAVDrive.ShellExtension/WebDAVDrive.ShellExtension.csproj
@@ -1,6 +1,6 @@
- net6.0-windows10.0.19041.0
+ net7.0-windows10.0.19041.0
True
x64
@@ -12,7 +12,7 @@
IT HIT LTD.
-
+
diff --git a/Windows/WebDAVDrive/WebDAVDrive.UI/WebDAVDrive.UI.csproj b/Windows/WebDAVDrive/WebDAVDrive.UI/WebDAVDrive.UI.csproj
index 4153509..478a4a1 100644
--- a/Windows/WebDAVDrive/WebDAVDrive.UI/WebDAVDrive.UI.csproj
+++ b/Windows/WebDAVDrive/WebDAVDrive.UI/WebDAVDrive.UI.csproj
@@ -1,7 +1,7 @@
- net6.0-windows10.0.19041.0
+ net7.0-windows10.0.19041.0
true
True
IT Hit LTD.
diff --git a/Windows/WebDAVDrive/WebDAVDrive/VirtualFile.cs b/Windows/WebDAVDrive/WebDAVDrive/VirtualFile.cs
index aaba42e..0d1fbc6 100644
--- a/Windows/WebDAVDrive/WebDAVDrive/VirtualFile.cs
+++ b/Windows/WebDAVDrive/WebDAVDrive/VirtualFile.cs
@@ -97,7 +97,7 @@ public async Task ValidateDataAsync(long offset, long length, IValidateDataOpera
}
///
- public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFile)}.{nameof(WriteAsync)}()", UserFileSystemPath, default, operationContext);
@@ -144,6 +144,8 @@ public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null,
inSyncResultContext.SetInSync = false;
}
}
+
+ return null;
}
}
}
diff --git a/Windows/WebDAVDrive/WebDAVDrive/VirtualFolder.cs b/Windows/WebDAVDrive/WebDAVDrive/VirtualFolder.cs
index 844cf1e..a8028b8 100644
--- a/Windows/WebDAVDrive/WebDAVDrive/VirtualFolder.cs
+++ b/Windows/WebDAVDrive/WebDAVDrive/VirtualFolder.cs
@@ -144,10 +144,11 @@ public async Task> EnumerateChildrenAsync
}
///
- public async Task WriteAsync(IFolderMetadata folderMetadata, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
// Typically we can not change any folder metadata on a WebDAV server, just logging the call.
Logger.LogMessage($"{nameof(IFolder)}.{nameof(WriteAsync)}()", UserFileSystemPath, default, operationContext);
+ return null;
}
///
diff --git a/Windows/WebDAVDrive/WebDAVDrive/WebDAVDrive.csproj b/Windows/WebDAVDrive/WebDAVDrive/WebDAVDrive.csproj
index 91c9e18..2093b13 100644
--- a/Windows/WebDAVDrive/WebDAVDrive/WebDAVDrive.csproj
+++ b/Windows/WebDAVDrive/WebDAVDrive/WebDAVDrive.csproj
@@ -1,7 +1,7 @@
Exe
- net6.0-windows10.0.19041.0
+ net7.0-windows10.0.19041.0
IT Hit LTD.
IT Hit LTD.
WebDAV Drive
@@ -39,7 +39,7 @@
-
+
diff --git a/Windows/WebDAVDrive/WebDAVDrive/appsettings.json b/Windows/WebDAVDrive/WebDAVDrive/appsettings.json
index 14a3664..5535720 100644
--- a/Windows/WebDAVDrive/WebDAVDrive/appsettings.json
+++ b/Windows/WebDAVDrive/WebDAVDrive/appsettings.json
@@ -40,7 +40,7 @@
//Your virtual file system will be mounted under this path.
- "UserFileSystemRootPath": "%USERPROFILE%\\DAVv7\\",
+ "UserFileSystemRootPath": "%USERPROFILE%\\DAVv73\\",
// Automatic lock timout in milliseconds. Automatic lock will be extended (refreshed) when this period is about to expire.
diff --git a/macOS/Common/Core/Common.Core.csproj b/macOS/Common/Core/Common.Core.csproj
index 7ee3cfe..850c48e 100644
--- a/macOS/Common/Core/Common.Core.csproj
+++ b/macOS/Common/Core/Common.Core.csproj
@@ -19,6 +19,6 @@
None
-
+
diff --git a/macOS/Common/Core/ConsoleLogger.cs b/macOS/Common/Core/ConsoleLogger.cs
index f0ed7f4..62124f2 100644
--- a/macOS/Common/Core/ConsoleLogger.cs
+++ b/macOS/Common/Core/ConsoleLogger.cs
@@ -39,7 +39,6 @@ public void LogError(IEngine sender, EngineErrorEventArgs e)
{
LogError(e.Message, sourcePath: e.SourcePath, targetPath: e.TargetPath, ex: e.Exception, operationContext: e.OperationContext, callerLineNumber: e.CallerLineNumber,
callerMemberName: e.CallerMemberName, callerFilePath: e.CallerFilePath);
- //WriteLog(sender, e, log4net.Core.Level.Error);
}
public void LogMessage(IEngine sender, EngineMessageEventArgs e)
@@ -61,7 +60,7 @@ public void LogError(string message, string sourcePath = null, string targetPath
public void LogMessage(string message, string sourcePath = null, string targetPath = null, IOperationContext operationContext = null, [CallerLineNumber] int callerLineNumber = 0, [CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null)
{
- LogWithLevel(string.Empty, $"\n{DateTimeOffset.Now} [{Thread.CurrentThread.ManagedThreadId,2}] {ComponentName,-26}{message,-45} {sourcePath,-80} {targetPath}");
+ LogWithLevel(string.Empty, $"\n{ComponentName,-26}{message,-45} {sourcePath,-80} {targetPath}");
}
private void LogError(string str, Exception ex)
@@ -86,7 +85,7 @@ public ILogger CreateLogger(string componentName)
public void LogDebug(string message, string sourcePath = null, string targetPath = null, IOperationContext operationContext = null, [CallerLineNumber] int callerLineNumber = 0, [CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null)
{
- LogDebug($"\n{DateTimeOffset.Now} [{Thread.CurrentThread.ManagedThreadId,2}] {ComponentName,-26}{message,-45} {sourcePath,-80} {targetPath}");
+ LogDebug($"\n{ComponentName,-26}{message,-45} {sourcePath,-80} {targetPath}");
}
diff --git a/macOS/UserFileSystemSamplesMac.sln b/macOS/UserFileSystemSamplesMac.sln
index 797b007..aa722c5 100644
--- a/macOS/UserFileSystemSamplesMac.sln
+++ b/macOS/UserFileSystemSamplesMac.sln
@@ -21,12 +21,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDAVMacApp", "WebDAVDrive
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDAVFileProviderExtension", "WebDAVDrive\WebDAVFileProviderExtension\WebDAVFileProviderExtension.csproj", "{2EAE2CFB-2220-4599-A462-20973AA1E4E3}"
EndProject
-Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "FileProviderExtension", "VirtualFileSystem\FileProviderExtension\FileProviderExtension.csproj", "{B8E37561-0C38-432E-ACC2-34D5530F350E}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FileProviderExtension", "VirtualFileSystem\FileProviderExtension\FileProviderExtension.csproj", "{B8E37561-0C38-432E-ACC2-34D5530F350E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDAVFileProviderUIExtension", "WebDAVDrive\WebDAVFileProviderUIExtension\WebDAVFileProviderUIExtension.csproj", "{5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
+ Debug|x64 = Debug|x64
+ Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E16FE844-A1C6-4C2E-A920-D0C0B8A1A47D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -37,6 +41,10 @@ Global
{07FF7DBA-27A0-417C-89F6-27E04E51F8DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{07FF7DBA-27A0-417C-89F6-27E04E51F8DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{07FF7DBA-27A0-417C-89F6-27E04E51F8DD}.Release|Any CPU.Build.0 = Release|Any CPU
+ {07FF7DBA-27A0-417C-89F6-27E04E51F8DD}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {07FF7DBA-27A0-417C-89F6-27E04E51F8DD}.Debug|x64.Build.0 = Debug|Any CPU
+ {07FF7DBA-27A0-417C-89F6-27E04E51F8DD}.Release|x64.ActiveCfg = Release|Any CPU
+ {07FF7DBA-27A0-417C-89F6-27E04E51F8DD}.Release|x64.Build.0 = Release|Any CPU
{FC3C471C-1B0E-4CAD-A084-408525EA86A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC3C471C-1B0E-4CAD-A084-408525EA86A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC3C471C-1B0E-4CAD-A084-408525EA86A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -45,6 +53,10 @@ Global
{867899D4-8931-44FE-A554-122C2D65616C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{867899D4-8931-44FE-A554-122C2D65616C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{867899D4-8931-44FE-A554-122C2D65616C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {867899D4-8931-44FE-A554-122C2D65616C}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {867899D4-8931-44FE-A554-122C2D65616C}.Debug|x64.Build.0 = Debug|Any CPU
+ {867899D4-8931-44FE-A554-122C2D65616C}.Release|x64.ActiveCfg = Release|Any CPU
+ {867899D4-8931-44FE-A554-122C2D65616C}.Release|x64.Build.0 = Release|Any CPU
{427510AC-F886-4521-AC23-E74E5665B309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{427510AC-F886-4521-AC23-E74E5665B309}.Debug|Any CPU.Build.0 = Debug|Any CPU
{427510AC-F886-4521-AC23-E74E5665B309}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -53,6 +65,10 @@ Global
{B1EBA44C-F695-440C-A7B7-8D05CA72EF3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B1EBA44C-F695-440C-A7B7-8D05CA72EF3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B1EBA44C-F695-440C-A7B7-8D05CA72EF3A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B1EBA44C-F695-440C-A7B7-8D05CA72EF3A}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B1EBA44C-F695-440C-A7B7-8D05CA72EF3A}.Debug|x64.Build.0 = Debug|Any CPU
+ {B1EBA44C-F695-440C-A7B7-8D05CA72EF3A}.Release|x64.ActiveCfg = Release|Any CPU
+ {B1EBA44C-F695-440C-A7B7-8D05CA72EF3A}.Release|x64.Build.0 = Release|Any CPU
{B23C34D8-3EF8-4627-9425-30243E1A4223}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B23C34D8-3EF8-4627-9425-30243E1A4223}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B23C34D8-3EF8-4627-9425-30243E1A4223}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -65,18 +81,42 @@ Global
{DF0B99D0-BA4D-4F24-8759-0BA9905F377E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DF0B99D0-BA4D-4F24-8759-0BA9905F377E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DF0B99D0-BA4D-4F24-8759-0BA9905F377E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {DF0B99D0-BA4D-4F24-8759-0BA9905F377E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {DF0B99D0-BA4D-4F24-8759-0BA9905F377E}.Debug|x64.Build.0 = Debug|Any CPU
+ {DF0B99D0-BA4D-4F24-8759-0BA9905F377E}.Release|x64.ActiveCfg = Release|Any CPU
+ {DF0B99D0-BA4D-4F24-8759-0BA9905F377E}.Release|x64.Build.0 = Release|Any CPU
{E9CFD15E-6885-41BA-AAC5-1AC95247940B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E9CFD15E-6885-41BA-AAC5-1AC95247940B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E9CFD15E-6885-41BA-AAC5-1AC95247940B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E9CFD15E-6885-41BA-AAC5-1AC95247940B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E9CFD15E-6885-41BA-AAC5-1AC95247940B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E9CFD15E-6885-41BA-AAC5-1AC95247940B}.Debug|x64.Build.0 = Debug|Any CPU
+ {E9CFD15E-6885-41BA-AAC5-1AC95247940B}.Release|x64.ActiveCfg = Release|Any CPU
+ {E9CFD15E-6885-41BA-AAC5-1AC95247940B}.Release|x64.Build.0 = Release|Any CPU
{2EAE2CFB-2220-4599-A462-20973AA1E4E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2EAE2CFB-2220-4599-A462-20973AA1E4E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2EAE2CFB-2220-4599-A462-20973AA1E4E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2EAE2CFB-2220-4599-A462-20973AA1E4E3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2EAE2CFB-2220-4599-A462-20973AA1E4E3}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {2EAE2CFB-2220-4599-A462-20973AA1E4E3}.Debug|x64.Build.0 = Debug|Any CPU
+ {2EAE2CFB-2220-4599-A462-20973AA1E4E3}.Release|x64.ActiveCfg = Release|Any CPU
+ {2EAE2CFB-2220-4599-A462-20973AA1E4E3}.Release|x64.Build.0 = Release|Any CPU
{B8E37561-0C38-432E-ACC2-34D5530F350E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B8E37561-0C38-432E-ACC2-34D5530F350E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B8E37561-0C38-432E-ACC2-34D5530F350E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B8E37561-0C38-432E-ACC2-34D5530F350E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B8E37561-0C38-432E-ACC2-34D5530F350E}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {B8E37561-0C38-432E-ACC2-34D5530F350E}.Debug|x64.Build.0 = Debug|Any CPU
+ {B8E37561-0C38-432E-ACC2-34D5530F350E}.Release|x64.ActiveCfg = Release|Any CPU
+ {B8E37561-0C38-432E-ACC2-34D5530F350E}.Release|x64.Build.0 = Release|Any CPU
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}.Debug|x64.Build.0 = Debug|Any CPU
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}.Release|x64.ActiveCfg = Release|Any CPU
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -98,5 +138,6 @@ Global
{E9CFD15E-6885-41BA-AAC5-1AC95247940B} = {34CB985B-1D15-4E17-8FA5-3FAFB4D26DA9}
{2EAE2CFB-2220-4599-A462-20973AA1E4E3} = {34CB985B-1D15-4E17-8FA5-3FAFB4D26DA9}
{B8E37561-0C38-432E-ACC2-34D5530F350E} = {036FC7F3-0FEC-497D-867F-3F67A465B6B8}
+ {5839C7B6-0F26-4E6A-8B26-FC9C1B8B4957} = {34CB985B-1D15-4E17-8FA5-3FAFB4D26DA9}
EndGlobalSection
EndGlobal
diff --git a/macOS/VirtualFileSystem/FileProviderExtension/FileProviderExtension.csproj b/macOS/VirtualFileSystem/FileProviderExtension/FileProviderExtension.csproj
index 5176b6f..b7e0e0a 100644
--- a/macOS/VirtualFileSystem/FileProviderExtension/FileProviderExtension.csproj
+++ b/macOS/VirtualFileSystem/FileProviderExtension/FileProviderExtension.csproj
@@ -74,9 +74,9 @@
-
-
-
+
+
+
diff --git a/macOS/VirtualFileSystem/FileProviderExtension/VirtualFile.cs b/macOS/VirtualFileSystem/FileProviderExtension/VirtualFile.cs
index 6cf887d..4756cd7 100644
--- a/macOS/VirtualFileSystem/FileProviderExtension/VirtualFile.cs
+++ b/macOS/VirtualFileSystem/FileProviderExtension/VirtualFile.cs
@@ -34,7 +34,7 @@ public async Task ReadAsync(Stream output, long offset, long length, ITransferDa
}
///
- public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFile)}.{nameof(WriteAsync)}()", RemoteStoragePath);
@@ -49,11 +49,29 @@ public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null,
remoteStorageStream.SetLength(content.Length);
}
- remoteStorageItem.LastWriteTimeUtc = fileMetadata.LastWriteTime.UtcDateTime;
+ if (fileBasicInfo.LastWriteTime != null)
+ {
+ remoteStorageItem.LastWriteTimeUtc = fileBasicInfo.LastWriteTime.Value.UtcDateTime;
+ }
+ }
+
+ // Update remote storage file metadata.
+ if (fileBasicInfo.Attributes != null)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value;
+ }
+
+ if (fileBasicInfo.CreationTime != null)
+ {
+ remoteStorageItem.CreationTimeUtc = fileBasicInfo.CreationTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastAccessTime != null)
+ {
+ remoteStorageItem.LastAccessTimeUtc = fileBasicInfo.LastAccessTime.Value.UtcDateTime;
}
- // Update remote storage file metadata.
- remoteStorageItem.LastAccessTimeUtc = fileMetadata.LastAccessTime.UtcDateTime;
+ return await GetMetadataAsync() as IFileMetadata;
}
}
}
diff --git a/macOS/VirtualFileSystem/FileProviderExtension/VirtualFileSystemItem.cs b/macOS/VirtualFileSystem/FileProviderExtension/VirtualFileSystemItem.cs
index f3db8e2..bb87cdc 100644
--- a/macOS/VirtualFileSystem/FileProviderExtension/VirtualFileSystemItem.cs
+++ b/macOS/VirtualFileSystem/FileProviderExtension/VirtualFileSystemItem.cs
@@ -74,7 +74,7 @@ public async Task DeleteAsync(IOperationContext operationContext = null, IConfir
}
///
- public async Task GetMetadataAsync()
+ public async Task GetMetadataAsync(IResultContext resultContext = null)
{
// Return IFileMetadata for a file, IFolderMetadata for a folder.
diff --git a/macOS/VirtualFileSystem/FileProviderExtension/VirtualFolder.cs b/macOS/VirtualFileSystem/FileProviderExtension/VirtualFolder.cs
index 74f6cf8..a25a78c 100644
--- a/macOS/VirtualFileSystem/FileProviderExtension/VirtualFolder.cs
+++ b/macOS/VirtualFileSystem/FileProviderExtension/VirtualFolder.cs
@@ -90,18 +90,34 @@ public async Task GetChildrenAsync(string pattern, IOperationContext operationCo
///
- public async Task WriteAsync(IFolderMetadata folderMetadata, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFolder)}.{nameof(WriteAsync)}()", RemoteStoragePath);
DirectoryInfo remoteStorageItem = new DirectoryInfo(RemoteStoragePath);
// Update remote storage folder metadata.
- remoteStorageItem.Attributes = folderMetadata.Attributes;
- remoteStorageItem.CreationTimeUtc = folderMetadata.CreationTime.UtcDateTime;
- remoteStorageItem.LastWriteTimeUtc = folderMetadata.LastWriteTime.UtcDateTime;
- remoteStorageItem.LastAccessTimeUtc = folderMetadata.LastAccessTime.UtcDateTime;
- remoteStorageItem.LastWriteTimeUtc = folderMetadata.LastWriteTime.UtcDateTime;
+ if (fileBasicInfo.Attributes != null)
+ {
+ remoteStorageItem.Attributes = fileBasicInfo.Attributes.Value;
+ }
+
+ if (fileBasicInfo.CreationTime != null)
+ {
+ remoteStorageItem.CreationTimeUtc = fileBasicInfo.CreationTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastWriteTime != null)
+ {
+ remoteStorageItem.LastWriteTimeUtc = fileBasicInfo.LastWriteTime.Value.UtcDateTime;
+ }
+
+ if (fileBasicInfo.LastAccessTime != null)
+ {
+ remoteStorageItem.LastAccessTimeUtc = fileBasicInfo.LastAccessTime.Value.UtcDateTime;
+ }
+
+ return await GetMetadataAsync() as IFolderMetadata;
}
}
diff --git a/macOS/VirtualFileSystem/VirtualFileSystemMacApp/VirtualFileSystemMacApp.csproj b/macOS/VirtualFileSystem/VirtualFileSystemMacApp/VirtualFileSystemMacApp.csproj
index d710a02..ad042b2 100644
--- a/macOS/VirtualFileSystem/VirtualFileSystemMacApp/VirtualFileSystemMacApp.csproj
+++ b/macOS/VirtualFileSystem/VirtualFileSystemMacApp/VirtualFileSystemMacApp.csproj
@@ -135,7 +135,7 @@
-
-
+
+
diff --git a/macOS/WebDAVDrive/WebDAVCommon/SecureStorage.cs b/macOS/WebDAVDrive/WebDAVCommon/SecureStorage.cs
new file mode 100644
index 0000000..5ac6ea6
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVCommon/SecureStorage.cs
@@ -0,0 +1,93 @@
+using System;
+using System.IO;
+using System.Text.Json;
+using CoreWlan;
+using Security;
+
+namespace WebDAVCommon
+{
+ public class SecureStorage
+ {
+ public const string ExtensionIdentifier = "com.webdav.vfs.app";
+ public const string ExtensionDisplayName = "IT Hit WebDAV Drive";
+
+ private const string AppGroupId = "65S3A9JQ35.group.com.webdav.vfs";
+ private const string InternalSettingFile = "data.out";
+
+ ///
+ /// Writes value to storage.
+ ///
+ /// Key.
+ /// Value.
+ ///
+ ///
+ public async Task SetAsync(string key, string value)
+ {
+ if (string.IsNullOrWhiteSpace(key))
+ {
+ throw new ArgumentNullException(nameof(key));
+ }
+ if (value == null)
+ {
+ throw new ArgumentNullException(nameof(value));
+ }
+
+ // Because secure storage requires provisioning profile, in case of the development mode, we store credentials in external file.
+ string userDataPath = Path.Combine(GetSharedContainerPath(), InternalSettingFile);
+ Dictionary userData = File.Exists(userDataPath) ? JsonSerializer.Deserialize>(await File.ReadAllTextAsync(userDataPath)) : new Dictionary();
+
+ if (userData.ContainsKey(key))
+ {
+ userData[key] = value;
+ }
+ else
+ {
+ userData.Add(key, value);
+ }
+
+ await File.WriteAllTextAsync(userDataPath, JsonSerializer.Serialize(userData));
+ }
+
+ ///
+ /// Returns value by key.
+ ///
+ /// Key.
+ ///
+ public async Task GetAsync(string key)
+ {
+ string userDataPath = Path.Combine(GetSharedContainerPath(), InternalSettingFile);
+ if (File.Exists(userDataPath))
+ {
+ Dictionary userData = JsonSerializer.Deserialize>(await File.ReadAllTextAsync(userDataPath));
+
+ if (userData.ContainsKey(key))
+ {
+ return userData[key];
+ }
+ else
+ {
+ return null;
+ }
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+
+ public string GetSharedContainerPath()
+ {
+ return NSFileManager.DefaultManager.GetContainerUrl(AppGroupId)?.Path;
+ }
+
+ ///
+ /// Triggers log-in button in file manager.
+ ///
+ public async Task RequireAuthenticationAsync()
+ {
+ await SetAsync("LoginType", "RequireAuthentication");
+ }
+ }
+}
+
diff --git a/macOS/WebDAVDrive/WebDAVCommon/WebDAVCommon.csproj b/macOS/WebDAVDrive/WebDAVCommon/WebDAVCommon.csproj
index b42dc88..41da35e 100644
--- a/macOS/WebDAVDrive/WebDAVCommon/WebDAVCommon.csproj
+++ b/macOS/WebDAVDrive/WebDAVCommon/WebDAVCommon.csproj
@@ -45,4 +45,7 @@
+
+
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualEngine.cs b/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualEngine.cs
index ab8d9be..33684cc 100644
--- a/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualEngine.cs
+++ b/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualEngine.cs
@@ -1,13 +1,17 @@
using System;
using System.IO;
+using System.Net;
+using System.Net.Mail;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
+using AuthenticationServices;
using Common.Core;
using FileProvider;
using Foundation;
using ITHit.FileSystem;
using ITHit.FileSystem.Mac;
+using ITHit.FileSystem.Mac.Contexts;
using ITHit.WebDAV.Client;
using WebDAVCommon;
@@ -21,6 +25,11 @@ public class VirtualEngine : EngineMac
///
public WebDavSession WebDavSession;
+ ///
+ /// Secure Storage.
+ ///
+ public SecureStorage SecureStorage;
+
///
/// Automatic lock timout in milliseconds.
///
@@ -51,9 +60,10 @@ public VirtualEngine(NSFileProviderDomain domain)
Error += consolelogger.LogError;
Message += consolelogger.LogMessage;
Debug += consolelogger.LogDebug;
-
- WebDavSession = new WebDavSession(AppGroupSettings.Settings.Value.WebDAVClientLicense);
- WebDavSession.CustomHeaders.Add("InstanceId", Environment.MachineName);
+
+ SecureStorage = new SecureStorage();
+
+ InitWebDavSession();
AutoLock = AppGroupSettings.Settings.Value.AutoLock;
AutoLockTimoutMs = AppGroupSettings.Settings.Value.AutoLockTimoutMs;
@@ -65,6 +75,25 @@ public VirtualEngine(NSFileProviderDomain domain)
Logger.LogMessage("Engine started.");
}
+ ///
+ /// Initializes WebDAV session.
+ ///
+ internal void InitWebDavSession()
+ {
+ if(WebDavSession != null)
+ {
+ WebDavSession.Dispose();
+ }
+ WebDavSession = new WebDavSession(AppGroupSettings.Settings.Value.WebDAVClientLicense);
+ WebDavSession.CustomHeaders.Add("InstanceId", Environment.MachineName);
+
+ string loginType = SecureStorage.GetAsync("LoginType").Result;
+ if (!string.IsNullOrEmpty(loginType) && loginType.Equals("UserNamePassword"))
+ {
+ WebDavSession.Credentials = new NetworkCredential(SecureStorage.GetAsync("UserName").Result, SecureStorage.GetAsync("Password").Result);
+ }
+ }
+
///
public override async Task GetFileSystemItemAsync(byte[] remoteStorageItemId, FileSystemItemType itemType, IContext context, ILogger logger = null)
{
@@ -105,7 +134,29 @@ public override async Task GetMenuCommandAsync(Guid menuGuid, IOpe
///
public async Task GetRootStorageItemIdAsync()
{
- return (await new VirtualFolder(Encoding.UTF8.GetBytes(AppGroupSettings.Settings.Value.WebDAVServerUrl), this, Logger).GetMetadataAsync()).RemoteStorageItemId;
+ Logger.LogMessage($"{nameof(VirtualEngine)}.{nameof(GetRootStorageItemIdAsync)}()");
+ try
+ {
+ return (await new VirtualFolder(Encoding.UTF8.GetBytes(AppGroupSettings.Settings.Value.WebDAVServerUrl), this, Logger).GetMetadataAsync())?.RemoteStorageItemId;
+ }
+ catch (ITHit.WebDAV.Client.Exceptions.WebDavHttpException httpException)
+ {
+ Logger.LogError($"{nameof(VirtualEngine)}.{nameof(GetRootStorageItemIdAsync)}()", ex: httpException);
+ switch (httpException.Status.Code)
+ {
+ // Challenge-responce auth: Basic, Digest, NTLM or Kerberos
+ case 401:
+ // Set login type to display sing in button in Finder.
+ await SecureStorage.RequireAuthenticationAsync();
+ return null;
+ }
+ return null;
+ }
+ catch (Exception ex)
+ {
+ Logger.LogError($"{nameof(VirtualEngine)}.{nameof(GetRootStorageItemIdAsync)}()", ex: ex);
+ return null;
+ }
}
@@ -120,5 +171,12 @@ protected override void Stop()
{
Logger.LogMessage($"{nameof(IEngine)}.{nameof(Stop)}()");
}
+
+ public override async Task IsAuthenticatedAsync()
+ {
+ Logger.LogMessage($"{nameof(IEngine)}.{nameof(IsAuthenticatedAsync)}()");
+ string loginType = await SecureStorage.GetAsync("LoginType");
+ return string.IsNullOrEmpty(loginType) || loginType != "RequireAuthentication";
+ }
}
}
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFile.cs b/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFile.cs
index 1cb630d..f9e4cb4 100644
--- a/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFile.cs
+++ b/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFile.cs
@@ -1,6 +1,7 @@
using FileProviderExtension.Extensions;
using ITHit.FileSystem;
using ITHit.FileSystem.Mac;
+using WebDAVCommon;
using Client = ITHit.WebDAV.Client;
namespace WebDAVFileProviderExtension
@@ -40,13 +41,17 @@ public async Task ReadAsync(Stream output, long offset, long length, ITransferDa
// Operation was canceled.
Logger.LogMessage($"{nameof(ReadAsync)}({offset}, {length}) canceled", RemoteStorageUriById.AbsoluteUri, default);
}
+ catch (Client.Exceptions.WebDavHttpException httpException)
+ {
+ HandleWebExceptions(httpException, resultContext);
+ }
}
}
}
///
- public async Task WriteAsync(IFileMetadata fileMetadata, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, Stream content = null, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(IFile)}.{nameof(WriteAsync)}()", RemoteStorageUriById.AbsoluteUri, default, operationContext);
@@ -75,20 +80,26 @@ await Engine.WebDavSession.UploadAsync(RemoteStorageUriById, async (outputStream
content.Position = 0; // Setting position to 0 is required in case of retry.
await content.CopyToAsync(outputStream);
}, null, content.Length, 0, -1, lockTokens, eTag, null, cancellationToken);
+
+
+ // macOS requires last modification date on the server to match the client. If date does not match, the file will be redownloaded.
+ // Here we use property name indetical to Microsoft Windows Explorer for max interability.
+ if (fileBasicInfo.LastWriteTime.HasValue)
+ {
+ Client.IFile file = (await Engine.WebDavSession.GetFileAsync(RemoteStorageUriById.AbsoluteUri, null, cancellationToken)).WebDavResponse;
+ Client.Property[] propsToAddAndUpdate = new Client.Property[1];
+ propsToAddAndUpdate[0] = new Client.Property(new Client.PropertyName("Win32LastModifiedTime", "urn:schemas-microsoft-com:"), fileBasicInfo.LastWriteTime.ToString());
+
+ await file.UpdatePropertiesAsync(propsToAddAndUpdate, null, lockTokens?.FirstOrDefault()?.LockToken);
+ }
}
catch (Client.Exceptions.PreconditionFailedException)
{
Logger.LogMessage($"Conflict. The item is modified.", RemoteStorageUriById.AbsoluteUri, default, operationContext);
- }
-
- // macOS requires last modification date on the server to match the client. If date does not match, the file will be redownloaded.
- // Here we use property name indetical to Microsoft Windows Explorer for max interability.
- Client.IFile file = (await Engine.WebDavSession.GetFileAsync(RemoteStorageUriById.AbsoluteUri, null, cancellationToken)).WebDavResponse;
- Client.Property[] propsToAddAndUpdate = new Client.Property[1];
- propsToAddAndUpdate[0] = new Client.Property(new Client.PropertyName("Win32LastModifiedTime", "urn:schemas-microsoft-com:"), fileMetadata.LastWriteTime.ToString());
-
- await file.UpdatePropertiesAsync(propsToAddAndUpdate, null, lockTokens?.FirstOrDefault()?.LockToken);
+ }
}
+
+ return await GetMetadataAsync() as IFileMetadata;
}
}
}
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFileSystemItem.cs b/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFileSystemItem.cs
index d3ac1ea..8f882a1 100644
--- a/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFileSystemItem.cs
+++ b/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFileSystemItem.cs
@@ -1,3 +1,5 @@
+using System.Net;
+using System.Net.Mail;
using System.Text;
using System.Threading;
using ITHit.FileSystem;
@@ -73,7 +75,7 @@ public async Task DeleteAsync(IOperationContext operationContext = null, IConfir
}
///
- public async Task GetMetadataAsync()
+ public async Task GetMetadataAsync(IResultContext resultContext = null)
{
Logger.LogMessage($"{nameof(IFileSystemItem)}.{nameof(GetMetadataAsync)}()", RemoteStorageUriById.AbsoluteUri);
@@ -87,12 +89,16 @@ public async Task DeleteAsync(IOperationContext operationContext = null, IConfir
// Return IFileMetadata for a file, IFolderMetadata for a folder.
item = (await Engine.WebDavSession.GetItemAsync(RemoteStorageUriById, propNames)).WebDavResponse;
}
- catch (ITHit.WebDAV.Client.Exceptions.NotFoundException e)
+ catch (Client.Exceptions.NotFoundException e)
{
Logger.LogError($"{nameof(IFileSystemItem)}.{nameof(GetMetadataAsync)}()", RemoteStorageUriById.AbsoluteUri, ex: e);
item = null;
}
+ catch (Client.Exceptions.WebDavHttpException httpException)
+ {
+ HandleWebExceptions(httpException, resultContext);
+ }
return item != null ? Mapping.GetUserFileSystemItemMetadata(item) : null;
}
@@ -163,12 +169,37 @@ public Task> GetPropertiesAsync(IOperati
throw new NotImplementedException();
}
+ protected async void HandleWebExceptions(Client.Exceptions.WebDavHttpException webDavHttpException, IResultContext resultContext)
+ {
+ switch (webDavHttpException.Status.Code)
+ {
+ // Challenge-responce auth: Basic, Digest, NTLM or Kerberos
+ case 401:
+
+ if (Engine.WebDavSession.Credentials == null || !(Engine.WebDavSession.Credentials is NetworkCredential) ||
+ (Engine.WebDavSession.Credentials as NetworkCredential).UserName != await Engine.SecureStorage.GetAsync("UserName"))
+ {
+ // Set login type to display sing in button in Finder.
+ await Engine.SecureStorage.RequireAuthenticationAsync();
+ if (resultContext != null)
+ {
+ resultContext.ReportStatus(CloudFileStatus.STATUS_CLOUD_FILE_AUTHENTICATION_FAILED);
+ }
+ }
+ else
+ {
+ // Reset WebDavSession.
+ Engine.InitWebDavSession();
+ }
+ break;
+ }
+ }
+
public async Task LockAsync(LockMode lockMode, IOperationContext operationContext = null, CancellationToken cancellationToken = default)
{
Logger.LogMessage($"{nameof(ILock)}.{nameof(LockAsync)}()", RemoteStorageUriById.AbsoluteUri, default, operationContext);
// Call your remote storage here to lock the item.
- // Save the lock token and other lock info received from the remote storage on the client.
// Supply the lock-token as part of each remote storage update in IFile.WriteAsync() method.
// Note that the actual lock timout applied by the server may be different from the one requested.
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFolder.cs b/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFolder.cs
index 4590184..20ef6b5 100644
--- a/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFolder.cs
+++ b/macOS/WebDAVDrive/WebDAVFileProviderExtension/VirtualFolder.cs
@@ -9,6 +9,8 @@
using ITHit.FileSystem.Synchronization;
using System.Text;
+using ITHit.WebDAV.Client.Exceptions;
+using WebDAVCommon;
namespace WebDAVFileProviderExtension
{
@@ -37,7 +39,8 @@ public async Task CreateFileAsync(IFileMetadata fileMetadata, Stream? co
// Create a new file in the remote storage.
long contentLength = content != null ? content.Length : 0;
- Client.IWebDavResponse response = await Engine.WebDavSession.UploadAsync(newFileUri, async (outputStream) => {
+ Client.IWebDavResponse response = await Engine.WebDavSession.UploadAsync(newFileUri, async (outputStream) =>
+ {
if (content != null)
{
// Setting position to 0 is required in case of retry.
@@ -67,31 +70,40 @@ public async Task CreateFolderAsync(IFolderMetadata folderMetadata, IInS
///
public async Task GetChildrenAsync(string pattern, IOperationContext operationContext, IFolderListingResultContext resultContext, CancellationToken cancellationToken)
{
- Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", RemoteStorageUriById.AbsoluteUri);
+ try
+ {
+ Logger.LogMessage($"{nameof(IFolder)}.{nameof(GetChildrenAsync)}({pattern})", RemoteStorageUriById.AbsoluteUri);
- Client.PropertyName[] propNames = new Client.PropertyName[2];
- propNames[0] = new Client.PropertyName("resource-id", "DAV:");
- propNames[1] = new Client.PropertyName("parent-resource-id", "DAV:");
+ Client.PropertyName[] propNames = new Client.PropertyName[2];
+ propNames[0] = new Client.PropertyName("resource-id", "DAV:");
+ propNames[1] = new Client.PropertyName("parent-resource-id", "DAV:");
+
+ IList remoteStorageChildren = (await Engine.WebDavSession.GetChildrenAsync(RemoteStorageUriById, false, propNames, null, cancellationToken)).WebDavResponse;
- IList remoteStorageChildren = (await Engine.WebDavSession.GetChildrenAsync(RemoteStorageUriById, false, propNames, null, cancellationToken)).WebDavResponse;
+ List userFileSystemChildren = new List();
+ foreach (Client.IHierarchyItem remoteStorageItem in remoteStorageChildren)
+ {
+ IFileSystemItemMetadata itemInfo = Mapping.GetUserFileSystemItemMetadata(remoteStorageItem);
+ userFileSystemChildren.Add(itemInfo);
+ }
- List userFileSystemChildren = new List();
- foreach (Client.IHierarchyItem remoteStorageItem in remoteStorageChildren)
+ // To signal that the children enumeration is completed
+ // always call ReturnChildren(), even if the folder is empty.
+ await resultContext.ReturnChildrenAsync(userFileSystemChildren.ToArray(), userFileSystemChildren.Count);
+ }
+ catch (WebDavHttpException httpException)
{
- IFileSystemItemMetadata itemInfo = Mapping.GetUserFileSystemItemMetadata(remoteStorageItem);
- userFileSystemChildren.Add(itemInfo);
+ HandleWebExceptions(httpException, resultContext);
}
-
- // To signal that the children enumeration is completed
- // always call ReturnChildren(), even if the folder is empty.
- await resultContext.ReturnChildrenAsync(userFileSystemChildren.ToArray(), userFileSystemChildren.Count);
}
///
- public async Task WriteAsync(IFolderMetadata folderMetadata, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
+ public async Task WriteAsync(IFileSystemBasicInfo fileBasicInfo, IOperationContext operationContext = null, IInSyncResultContext inSyncResultContext = null, CancellationToken cancellationToken = default)
{
// Typically we can not change any folder metadata on a WebDAV server, just logging the call.
Logger.LogMessage($"{nameof(IFolder)}.{nameof(WriteAsync)}()", RemoteStorageUriById.AbsoluteUri, default, operationContext);
+
+ return await GetMetadataAsync() as IFolderMetadata;
}
///
@@ -128,5 +140,5 @@ public async Task GetChangesAsync(string syncToken, bool deep, long? l
return changes;
}
}
-
+
}
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderExtension/WebDAVFileProviderExtension.csproj b/macOS/WebDAVDrive/WebDAVFileProviderExtension/WebDAVFileProviderExtension.csproj
index c77e3d1..fea13a9 100644
--- a/macOS/WebDAVDrive/WebDAVFileProviderExtension/WebDAVFileProviderExtension.csproj
+++ b/macOS/WebDAVDrive/WebDAVFileProviderExtension/WebDAVFileProviderExtension.csproj
@@ -76,11 +76,11 @@
-
+
-
-
+
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Entitlements.plist b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Entitlements.plist
new file mode 100644
index 0000000..a62467a
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Entitlements.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.application-groups
+
+ 65S3A9JQ35.group.com.webdav.vfs
+
+
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Extension/NSViewExtensions.cs b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Extension/NSViewExtensions.cs
new file mode 100644
index 0000000..963237c
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Extension/NSViewExtensions.cs
@@ -0,0 +1,43 @@
+namespace WebDAVFileProviderUIExtension.Extension
+{
+ public static class NSViewExtensions
+ {
+ public static TView SetBackgroundColor(this TView view, NSColor color)
+ where TView : NSView
+ {
+ return view.SetBackgroundColor(color.CGColor);
+ }
+
+ public static TView SetBackgroundColor(this TView view, CGColor color)
+ where TView : NSView
+ {
+ view.EnsureWantsLayer();
+ if (view.Layer != null)
+ {
+ view.Layer.BackgroundColor = color;
+ }
+
+ return view;
+ }
+
+ public static TView EnsureWantsLayer(this TView view)
+ where TView : NSView
+ {
+ view.WantsLayer = true;
+ return view;
+ }
+
+ public static NSButton SetColoredTitle(
+ this NSButton button,
+ string text,
+ NSColor color)
+ {
+ button.Title = text;
+
+ button.AttributedTitle = text.CreateAttributedString(color);
+
+ return button;
+ }
+ }
+}
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Extension/StringExtensions.cs b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Extension/StringExtensions.cs
new file mode 100644
index 0000000..f600e73
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Extension/StringExtensions.cs
@@ -0,0 +1,16 @@
+namespace WebDAVFileProviderUIExtension.Extension
+{
+ public static class StringExtensions
+ {
+ public static NSMutableAttributedString CreateAttributedString(
+ this string text,
+ NSColor color)
+ {
+ var attributedString = new NSMutableAttributedString(text);
+ var range = new NSRange(0, text.Length);
+ attributedString.AddAttribute(NSStringAttributeKey.ForegroundColor, color, range);
+ return attributedString;
+ }
+ }
+}
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/FPUIActionExtension.cs b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/FPUIActionExtension.cs
new file mode 100644
index 0000000..87200a1
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/FPUIActionExtension.cs
@@ -0,0 +1,31 @@
+using Common.Core;
+using FileProvider;
+using ITHit.FileSystem.Mac;
+using WebDAVCommon;
+using WebDAVFileProviderUIExtension.ViewControllers;
+
+namespace WebDAVFileProviderUIExtension;
+
+[Register("FPUIActionExtension")]
+public class FPUIActionExtension : FPUIActionExtensionViewControllerMac
+{
+ public FPUIActionExtension() : base(NSFileProviderManager.FromDomain(new NSFileProviderDomain(SecureStorage.ExtensionIdentifier, SecureStorage.ExtensionDisplayName)),
+ new ConsoleLogger(typeof(FPUIActionExtension).Name))
+ { }
+
+ ///
+ public override async Task GetMenuCommandAsync(Guid menuGuid, IEnumerable remoteStorageItemIds, IMacFPUIActionExtensionContext context)
+ {
+ Logger.LogMessage($"{nameof(FPUIActionExtension)}.{nameof(GetMenuCommandAsync)}()");
+
+ return new ContectMenuUIViewController(context);
+ }
+
+ ///
+ public override async Task RequireAuthenticationAsync(IMacFPUIActionExtensionContext context)
+ {
+ Logger.LogMessage($"{nameof(FPUIActionExtension)}.{nameof(RequireAuthenticationAsync)}()");
+
+ return new AuthViewController(context);
+ }
+}
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Info.plist b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Info.plist
new file mode 100644
index 0000000..ce2548b
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Info.plist
@@ -0,0 +1,57 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleDisplayName
+ WebDAVFileProviderUIExtension
+ CFBundleIdentifier
+ com.webdav.vfs.app.extension.ui
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ WebDAVFileProviderUIExtension
+ CFBundlePackageType
+ XPC!
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSMinimumSystemVersion
+ 10.14
+ LSUIElement
+
+ NSAppTransportSecurity
+
+ NSExceptionDomains
+
+ localhost
+
+ NSExceptionAllowsInsecureHTTPLoads
+
+
+
+
+ NSExtension
+
+ NSExtensionFileProviderActions
+
+
+ NSExtensionFileProviderActionActivationRule
+ TRUEPREDICATE
+ NSExtensionFileProviderActionIdentifier
+ 18d0e0cb-5df7-432c-a06f-297c06b5ec6d
+ NSExtensionFileProviderActionName
+ Action UI
+
+
+ NSExtensionPointIdentifier
+ com.apple.fileprovider-actionsui
+ NSExtensionPrincipalClass
+ FPUIActionExtension
+ NSExtensionFileProviderDocumentGroup
+ 65S3A9JQ35.group.com.webdav.vfs
+
+
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Resources/LaunchScreen.xib b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Resources/LaunchScreen.xib
new file mode 100644
index 0000000..772defd
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/Resources/LaunchScreen.xib
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/ViewControllers/AuthViewController.cs b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/ViewControllers/AuthViewController.cs
new file mode 100644
index 0000000..445a7a2
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/ViewControllers/AuthViewController.cs
@@ -0,0 +1,135 @@
+using Common.Core;
+using ITHit.FileSystem.Mac;
+using ObjCRuntime;
+using WebDAVCommon;
+using WebDAVFileProviderUIExtension.Extension;
+
+namespace WebDAVFileProviderUIExtension.ViewControllers
+{
+ public class AuthViewController : NSViewController
+ {
+ private ConsoleLogger logger = new ConsoleLogger(typeof(AuthViewController).Name);
+ private SecureStorage secureStorage = new SecureStorage();
+
+ private NSTextField loginTextField = new()
+ {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ private NSSecureTextField passwordTextField = new()
+ {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ private NSButton authenticationButton = new()
+ {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ private IMacFPUIActionExtensionContext extensionContext;
+
+ public AuthViewController(IMacFPUIActionExtensionContext extensionContext) : base(nameof(AuthViewController), null)
+ {
+ this.extensionContext = extensionContext;
+ }
+
+ protected AuthViewController(NativeHandle handle) : base(handle)
+ {
+ // This constructor is required if the view controller is loaded from a xib or a storyboard.
+ // Do not put any initialization here, use ViewDidLoad instead.
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (loginTextField != null)
+ {
+ loginTextField.Dispose();
+ loginTextField = null;
+ }
+
+ if (passwordTextField != null)
+ {
+ passwordTextField.Dispose();
+ passwordTextField = null;
+ }
+
+ if (authenticationButton != null)
+ {
+ authenticationButton.Dispose();
+ authenticationButton = null;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ public override void LoadView()
+ {
+ NSView view = new NSView()
+ {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ authenticationButton.Activated += OnAuthenticationButtonActivated;
+
+ loginTextField.SetBackgroundColor(NSColor.White);
+ passwordTextField.SetBackgroundColor(NSColor.White);
+ authenticationButton.SetBackgroundColor(NSColor.White);
+
+ loginTextField.Cell.PlaceholderAttributedString = "Login".CreateAttributedString(NSColor.Gray);
+ passwordTextField.Cell.PlaceholderAttributedString = "Password".CreateAttributedString(NSColor.Gray);
+ authenticationButton.SetColoredTitle("Authenticate", NSColor.Black);
+
+ loginTextField.TextColor = NSColor.Black;
+ passwordTextField.TextColor = NSColor.Black;
+
+
+ view.AddSubview(loginTextField);
+ view.AddSubview(passwordTextField);
+ view.AddSubview(authenticationButton);
+
+ View = view;
+
+ const int padding = 20;
+
+ NSLayoutConstraint.ActivateConstraints(new[] {
+ loginTextField.HeightAnchor.ConstraintEqualTo(25f),
+ loginTextField.LeadingAnchor.ConstraintEqualTo(view.LeadingAnchor, padding),
+ loginTextField.TopAnchor.ConstraintEqualTo(view.TopAnchor, padding),
+ loginTextField.TrailingAnchor.ConstraintEqualTo(view.TrailingAnchor, -padding),
+
+ passwordTextField.HeightAnchor.ConstraintEqualTo(25f),
+ passwordTextField.LeadingAnchor.ConstraintEqualTo(view.LeadingAnchor, padding),
+ passwordTextField.TopAnchor.ConstraintEqualTo(loginTextField.BottomAnchor, padding),
+ passwordTextField.TrailingAnchor.ConstraintEqualTo(view.TrailingAnchor, -padding),
+
+ authenticationButton.HeightAnchor.ConstraintEqualTo(25f),
+ authenticationButton.CenterXAnchor.ConstraintEqualTo(view.CenterXAnchor),
+ authenticationButton.LeadingAnchor.ConstraintGreaterThanOrEqualTo(view.LeadingAnchor, padding * 3),
+ authenticationButton.TrailingAnchor.ConstraintGreaterThanOrEqualTo(view.TrailingAnchor, -padding),
+ authenticationButton.TopAnchor.ConstraintEqualTo(passwordTextField.BottomAnchor, padding * 3),
+ authenticationButton.BottomAnchor.ConstraintEqualTo(view.BottomAnchor, -padding)
+ });
+ }
+
+ private void OnAuthenticationButtonActivated(object? sender, EventArgs e)
+ {
+ try
+ {
+ logger.LogDebug($"OnAuthenticationButtonActivated: send signal to file provider.");
+
+ secureStorage.SetAsync("LoginType", "UserNamePassword").Wait();
+ secureStorage.SetAsync("UserName", loginTextField.StringValue).Wait();
+ secureStorage.SetAsync("Password", passwordTextField.StringValue).Wait();
+ extensionContext.CompleteRequest();
+ }
+ catch(Exception ex)
+ {
+ logger.LogError($"OnAuthenticationButtonActivated: {ex.Message}", ex: ex);
+ }
+ }
+ }
+}
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/ViewControllers/ContectMenuUIViewController.cs b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/ViewControllers/ContectMenuUIViewController.cs
new file mode 100644
index 0000000..3765408
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/ViewControllers/ContectMenuUIViewController.cs
@@ -0,0 +1,109 @@
+using Common.Core;
+using ITHit.FileSystem.Mac;
+using ObjCRuntime;
+using WebDAVCommon;
+using WebDAVFileProviderUIExtension.Extension;
+
+namespace WebDAVFileProviderUIExtension.ViewControllers
+{
+ public class ContectMenuUIViewController : NSViewController
+ {
+ private ConsoleLogger logger = new ConsoleLogger(typeof(AuthViewController).Name);
+
+ private NSTextField loginTextField = new()
+ {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ private NSButton closeButton = new()
+ {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ private IMacFPUIActionExtensionContext extensionContext;
+
+ public ContectMenuUIViewController(IMacFPUIActionExtensionContext extensionContext) : base(nameof(AuthViewController), null)
+ {
+ this.extensionContext = extensionContext;
+ }
+
+ protected ContectMenuUIViewController(NativeHandle handle) : base(handle)
+ {
+ // This constructor is required if the view controller is loaded from a xib or a storyboard.
+ // Do not put any initialization here, use ViewDidLoad instead.
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (loginTextField != null)
+ {
+ loginTextField.Dispose();
+ loginTextField = null;
+ }
+
+
+ if (closeButton != null)
+ {
+ closeButton.Dispose();
+ closeButton = null;
+ }
+ }
+
+ base.Dispose(disposing);
+ }
+
+ public override void LoadView()
+ {
+ NSView view = new NSView()
+ {
+ TranslatesAutoresizingMaskIntoConstraints = false
+ };
+
+ closeButton.Activated += OnCloseButtonActivated;
+
+ loginTextField.SetBackgroundColor(NSColor.White);
+ closeButton.SetBackgroundColor(NSColor.White);
+
+ loginTextField.Cell.PlaceholderAttributedString = "Custom Textbox".CreateAttributedString(NSColor.Gray);
+ closeButton.SetColoredTitle("Close", NSColor.Black);
+
+ loginTextField.TextColor = NSColor.Black;
+
+ view.AddSubview(loginTextField);
+ view.AddSubview(closeButton);
+
+ View = view;
+
+ const int padding = 20;
+
+ NSLayoutConstraint.ActivateConstraints(new[] {
+ loginTextField.HeightAnchor.ConstraintEqualTo(25f),
+ loginTextField.LeadingAnchor.ConstraintEqualTo(view.LeadingAnchor, padding),
+ loginTextField.TopAnchor.ConstraintEqualTo(view.TopAnchor, padding),
+ loginTextField.TrailingAnchor.ConstraintEqualTo(view.TrailingAnchor, -padding),
+
+ closeButton.HeightAnchor.ConstraintEqualTo(25f),
+ closeButton.CenterXAnchor.ConstraintEqualTo(view.CenterXAnchor),
+ closeButton.LeadingAnchor.ConstraintGreaterThanOrEqualTo(view.LeadingAnchor, padding * 3),
+ closeButton.TrailingAnchor.ConstraintGreaterThanOrEqualTo(view.TrailingAnchor, -padding),
+ closeButton.BottomAnchor.ConstraintEqualTo(view.BottomAnchor, -padding)
+ });
+ }
+
+ private void OnCloseButtonActivated(object? sender, EventArgs e)
+ {
+ try
+ {
+ logger.LogDebug($"OnCloseButtonActivated: send signal to file provider.");
+ extensionContext.CompleteRequest();
+ }
+ catch(Exception ex)
+ {
+ logger.LogError($"OnCloseButtonActivated: {ex.Message}", ex: ex);
+ }
+ }
+ }
+}
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/WebDAVFileProviderUIExtension.csproj b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/WebDAVFileProviderUIExtension.csproj
new file mode 100644
index 0000000..0b89efe
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/WebDAVFileProviderUIExtension.csproj
@@ -0,0 +1,95 @@
+
+
+ net7.0-macos
+ Library
+ enable
+ true
+ 10.14
+ True
+ false
+ false
+
+
+ true
+ full
+ false
+ bin\Debug
+ DEBUG;
+ prompt
+ 4
+ false
+ false
+ false
+ true
+ true
+
+ Full
+ None
+
+
+
+ AfterBuild
+ bash ${ProjectDir}/sign.sh ${TargetDir}
+
+
+ AfterBuild
+ codesign --force --sign - -o runtime --entitlements ${ProjectDir}/Entitlements.plist --timestamp --generate-entitlement-der ${TargetDir}/WebDAVFileProviderUIExtension.appex
+
+
+
+
+
+ pdbonly
+ true
+ bin\Release
+
+ prompt
+ 4
+ False
+ true
+ false
+ true
+ true
+ true
+ Entitlements.plist
+ None
+
+ None
+ 10.0
+ true
+
+
+
+ AfterBuild
+ bash ${ProjectDir}/sign_release.sh ${TargetDir}
+
+
+ AfterBuild
+ codesign --force --sign "Mac Developer" -o runtime --entitlements ${ProjectDir}/Entitlements.plist --timestamp --generate-entitlement-der ${TargetDir}/WebDAVFileProviderUIExtension.appex
+
+
+
+
+
+
+
+
+
+
+
+ $([MSBuild]::MakeRelative ('$(MSBuildProjectDirectory)', '%(Identity)'))
+
+
+ $([MSBuild]::MakeRelative ('$(MSBuildProjectDirectory)', '%(Identity)'))
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/sign.sh b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/sign.sh
new file mode 100644
index 0000000..80f4b45
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/sign.sh
@@ -0,0 +1 @@
+find $1/WebDAVFileProviderUIExtension.appex -iname '*.dylib' | while read libfile ; do codesign --force --sign - -o runtime --timestamp "${libfile}" ; done ;
\ No newline at end of file
diff --git a/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/sign_release.sh b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/sign_release.sh
new file mode 100644
index 0000000..21b22bc
--- /dev/null
+++ b/macOS/WebDAVDrive/WebDAVFileProviderUIExtension/sign_release.sh
@@ -0,0 +1 @@
+find $1/WebDAVFileProviderUIExtension.appex -iname '*.dylib' | while read libfile ; do codesign --force --sign "Mac Developer" -o runtime --timestamp "${libfile}" ; done ;
\ No newline at end of file
diff --git a/macOS/WebDAVDrive/WebDAVMacApp/AppDelegate.cs b/macOS/WebDAVDrive/WebDAVMacApp/AppDelegate.cs
index aea7a25..c8278a2 100644
--- a/macOS/WebDAVDrive/WebDAVMacApp/AppDelegate.cs
+++ b/macOS/WebDAVDrive/WebDAVMacApp/AppDelegate.cs
@@ -13,8 +13,6 @@ public class AppDelegate : NSApplicationDelegate
{
private ILogger Logger = new ConsoleLogger("WebDavFileProviderHostApp");
private RemoteStorageMonitor RemoteStorageMonitor = new RemoteStorageMonitor(AppGroupSettings.Settings.Value.WebSocketServerUrl, new ConsoleLogger(typeof(RemoteStorageMonitor).Name));
- private string ExtensionIdentifier = "com.webdav.vfs.app";
- private string ExtensionDisplayName = "IT Hit WebDAV Drive";
private NSMenuItem InstallMenuItem = new NSMenuItem("Install WebDAV FS Extension");
private NSMenuItem UninstallMenuItem = new NSMenuItem("Uninstall WebDAV FS Extension");
private NSStatusItem StatusItem;
@@ -27,7 +25,7 @@ public override void DidFinishLaunching(NSNotification notification)
{
NSMenu menu = new NSMenu();
- Task taskIsExtensionRegistered = Task.Run(async () => await Common.Core.Registrar.IsRegisteredAsync(ExtensionIdentifier));
+ Task taskIsExtensionRegistered = Task.Run(async () => await Common.Core.Registrar.IsRegisteredAsync(SecureStorage.ExtensionIdentifier));
bool isExtensionRegistered = taskIsExtensionRegistered.Result;
if (isExtensionRegistered)
{
@@ -35,7 +33,7 @@ public override void DidFinishLaunching(NSNotification notification)
Task.Run(async () =>
{
RemoteStorageMonitor.ServerNotifications = new ServerNotifications(
- NSFileProviderManager.FromDomain(new NSFileProviderDomain(ExtensionIdentifier, ExtensionDisplayName)),
+ NSFileProviderManager.FromDomain(new NSFileProviderDomain(SecureStorage.ExtensionIdentifier, SecureStorage.ExtensionDisplayName)),
RemoteStorageMonitor.Logger);
await RemoteStorageMonitor.StartAsync();
}).Wait();
@@ -45,6 +43,7 @@ public override void DidFinishLaunching(NSNotification notification)
InstallMenuItem.Activated += Install;
}
+ NSMenuItem logoutMenuItem = new NSMenuItem("Log out", (_, _) => { });
NSMenuItem exitMenuItem = new NSMenuItem("Quit", (a, b) => { NSApplication.SharedApplication.Terminate(this); });
menu.AddItem(InstallMenuItem);
@@ -71,7 +70,7 @@ private void Install(object? sender, EventArgs e)
Process.Start("open", AppGroupSettings.Settings.Value.WebDAVServerUrl);
Task.Run(async () =>
{
- NSFileProviderDomain domain = await Common.Core.Registrar.RegisterAsync(ExtensionIdentifier, ExtensionDisplayName, Logger);
+ NSFileProviderDomain domain = await Common.Core.Registrar.RegisterAsync(SecureStorage.ExtensionIdentifier, SecureStorage.ExtensionDisplayName, Logger);
RemoteStorageMonitor.ServerNotifications = new ServerNotifications(NSFileProviderManager.FromDomain(domain), RemoteStorageMonitor.Logger);
await RemoteStorageMonitor.StartAsync();
}).Wait();
@@ -86,7 +85,7 @@ private void Uninstall(object? sender, EventArgs e)
Task.Run(async () =>
{
await RemoteStorageMonitor.StopAsync();
- await Common.Core.Registrar.UnregisterAsync(ExtensionIdentifier, Logger);
+ await Common.Core.Registrar.UnregisterAsync(SecureStorage.ExtensionIdentifier, Logger);
}).Wait();
InstallMenuItem.Activated += Install;
diff --git a/macOS/WebDAVDrive/WebDAVMacApp/WebDAVMacApp.csproj b/macOS/WebDAVDrive/WebDAVMacApp/WebDAVMacApp.csproj
index 0910ba9..3694a56 100644
--- a/macOS/WebDAVDrive/WebDAVMacApp/WebDAVMacApp.csproj
+++ b/macOS/WebDAVDrive/WebDAVMacApp/WebDAVMacApp.csproj
@@ -28,6 +28,10 @@
AfterBuild
cp -rf "${ProjectDir}/../WebDAVFileProviderExtension/bin/Debug/WebDAVFileProviderExtension.appex" "${TargetDir}/WebDAV Drive.app/Contents/PlugIns"
+
+ AfterBuild
+ cp -rf "${ProjectDir}/../WebDAVFileProviderUIExtension/bin/Debug/WebDAVFileProviderUIExtension.appex" "${TargetDir}/WebDAV Drive.app/Contents/PlugIns"
+
AfterBuild
codesign --force --sign - --entitlements ${ProjectDir}/Entitlements.plist --timestamp --generate-entitlement-der "${TargetDir}/WebDAV Drive.app"
@@ -137,15 +141,16 @@
-
+
-
+
+
-
-
+
+