Browse Source

Merge pull request #2265 from nextcloud/e2ee

Version 4.6
Marino Faggiana 2 years ago
parent
commit
95fe45ac02
54 changed files with 1626 additions and 607 deletions
  1. 6 1
      .swiftlint.yml
  2. 1 1
      File Provider Extension/FileProviderExtension+Actions.swift
  3. 47 3
      Nextcloud.xcodeproj/project.pbxproj
  4. 2 3
      Share/NCShareExtension.swift
  5. 1 1
      Widget/Files/FilesData.swift
  6. 158 0
      iOSClient/Account Request/NCTalkAccounts.storyboard
  7. 199 0
      iOSClient/Account Request/NCTalkAccounts.swift
  8. 26 0
      iOSClient/AppDelegate.swift
  9. 3 1
      iOSClient/Brand/NCBrand.swift
  10. 1 0
      iOSClient/Brand/iOSClient.entitlements
  11. 5 0
      iOSClient/Data/NCDataSource.swift
  12. 6 6
      iOSClient/Data/NCManageDatabase+Metadata.swift
  13. 14 1
      iOSClient/Data/NCManageDatabase.swift
  14. 16 7
      iOSClient/Extensions/UIAlertController+Extension.swift
  15. 12 0
      iOSClient/Images.xcassets/talk.imageset/Contents.json
  16. BIN
      iOSClient/Images.xcassets/talk.imageset/talk.png
  17. 23 0
      iOSClient/Images.xcassets/talk_bar.imageset/Contents.json
  18. BIN
      iOSClient/Images.xcassets/talk_bar.imageset/talk.png
  19. BIN
      iOSClient/Images.xcassets/talk_bar.imageset/talk_bar 1.png
  20. BIN
      iOSClient/Images.xcassets/talk_bar.imageset/talk_bar 2.png
  21. 51 1
      iOSClient/Login/NCLogin.swift
  22. 4 0
      iOSClient/Login/NCLoginWeb.swift
  23. 18 6
      iOSClient/Main/Collection Common/NCCollectionViewCommon.swift
  24. 2 2
      iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift
  25. 11 18
      iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift
  26. 1 2
      iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift
  27. 1 2
      iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift
  28. 1 2
      iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift
  29. 2 1
      iOSClient/Main/NCFunctionCenter.swift
  30. 1 2
      iOSClient/Main/NCPickerViewController.swift
  31. 28 3
      iOSClient/Menu/AppDelegate+Menu.swift
  32. 1 1
      iOSClient/Menu/NCCollectionViewCommon+Menu.swift
  33. 1 1
      iOSClient/Menu/NCViewer+Menu.swift
  34. 2 0
      iOSClient/NCGlobal.swift
  35. 87 0
      iOSClient/Networking/E2EE/NCNetworkingE2EE.swift
  36. 150 0
      iOSClient/Networking/E2EE/NCNetworkingE2EECreateFolder.swift
  37. 85 0
      iOSClient/Networking/E2EE/NCNetworkingE2EEDelete.swift
  38. 94 0
      iOSClient/Networking/E2EE/NCNetworkingE2EERename.swift
  39. 187 0
      iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift
  40. 62 42
      iOSClient/Networking/NCNetworking.swift
  41. 0 437
      iOSClient/Networking/NCNetworkingE2EE.swift
  42. 1 1
      iOSClient/Networking/NCOperationQueue.swift
  43. 1 0
      iOSClient/Security/NCEndToEndMetadata.swift
  44. 188 0
      iOSClient/Settings/NCManageE2EE.swift
  45. 7 0
      iOSClient/Settings/NCManageEndToEndEncryption.m
  46. 11 2
      iOSClient/Settings/NCSettings.m
  47. 5 0
      iOSClient/Supporting Files/en.lproj/Localizable.strings
  48. 2 4
      iOSClient/Trash/NCTrash.swift
  49. 0 5
      iOSClient/Utility/CCUtility.h
  50. 1 44
      iOSClient/Utility/CCUtility.m
  51. 56 1
      iOSClient/Utility/NCContentPresenter.swift
  52. 30 3
      iOSClient/Utility/NCUtility.swift
  53. 1 1
      iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift
  54. 14 2
      iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift

+ 6 - 1
.swiftlint.yml

@@ -103,7 +103,11 @@ excluded:
   - iOSClient/Networking/NCNetworking.swift
   - iOSClient/Networking/NCNetworkingCheckRemoteUser.swift
   - iOSClient/Networking/NCNetworkingChunkedUpload.swift
-  - iOSClient/Networking/NCNetworkingE2EE.swift
+  - iOSClient/Networking/E2EE/NCNetworkingE2EE.swift
+  - iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift
+  - iOSClient/Networking/E2EE/NCNetworkingE2EEDelete.swift
+  - iOSClient/Networking/E2EE/NCNetworkingE2EERename.swift
+  - iOSClient/Networking/E2EE/NCNetworkingE2EECreateFolder.swift
   - iOSClient/Networking/NCNetworkingProcessUpload.swift
   - iOSClient/Networking/NCOperationQueue.swift
   - iOSClient/Networking/NCService.swift
@@ -118,6 +122,7 @@ excluded:
   - iOSClient/Select/NCSelect.swift
   - iOSClient/Settings/NCEndToEndInitialize.swift
   - iOSClient/Settings/NCManageAutoUploadFileName.swift
+  - iOSClient/Settings/NCManageE2EE.swift
   - iOSClient/Share/NCShareCommon.swift
   - iOSClient/Share/NCShareNetworking.swift
   - iOSClient/Shares/NCShares.swift

+ 1 - 1
File Provider Extension/FileProviderExtension+Actions.swift

@@ -46,7 +46,7 @@ extension FileProviderExtension {
                     if error == .success && files.count > 0 {
 
                         let file = files.first!
-                        let metadata = NCManageDatabase.shared.convertNCFileToMetadata(file, isEncrypted: false, account: fileProviderData.shared.account)
+                        let metadata = NCManageDatabase.shared.convertNCFileToMetadata(file, account: fileProviderData.shared.account)
 
                         NCManageDatabase.shared.addDirectory(encrypted: false, favorite: false, ocId: ocId!, fileId: metadata.fileId, etag: metadata.etag, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
                         NCManageDatabase.shared.addMetadata(metadata)

+ 47 - 3
Nextcloud.xcodeproj/project.pbxproj

@@ -394,8 +394,11 @@
 		F7A0D1362591FBC5008F8A13 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */; };
 		F7A0D1372591FBC5008F8A13 /* String+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */; };
 		F7A321AD1E9E6AD50069AD1B /* CCAdvanced.m in Sources */ = {isa = PBXBuildFile; fileRef = F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */; };
+		F7A60F86292D215000FCE1F2 /* NCTalkAccounts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A60F84292D215000FCE1F2 /* NCTalkAccounts.swift */; };
+		F7A60F87292D215000FCE1F2 /* NCTalkAccounts.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7A60F85292D215000FCE1F2 /* NCTalkAccounts.storyboard */; };
 		F7A76DC8256A71CD00119AB3 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; };
 		F7A76DCD256A71CE00119AB3 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B7504A2397D38E004E13EC /* UIImage+Extensions.swift */; };
+		F7A7FA6329265CF4000603EF /* NCManageE2EE.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7A7FA6229265CF4000603EF /* NCManageE2EE.swift */; };
 		F7A8D72428F1771B008BBE1C /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = F7A8D72328F1771B008BBE1C /* NextcloudKit */; };
 		F7A8D72628F17728008BBE1C /* Realm in Frameworks */ = {isa = PBXBuildFile; productRef = F7A8D72528F17728008BBE1C /* Realm */; };
 		F7A8D72828F17728008BBE1C /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F7A8D72728F17728008BBE1C /* RealmSwift */; };
@@ -439,6 +442,14 @@
 		F7BC288026663F85004D46C5 /* NCViewCertificateDetails.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7BC287F26663F85004D46C5 /* NCViewCertificateDetails.swift */; };
 		F7BD71E62636EAFC00643C34 /* NCNetworkingE2EE.swift in Sources */ = {isa = PBXBuildFile; fileRef = F785EE9C246196DF00B3F945 /* NCNetworkingE2EE.swift */; };
 		F7C1EEA525053A9C00866ACC /* NCDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C1EEA425053A9C00866ACC /* NCDataSource.swift */; };
+		F7C30DF6291BC0CA0017149B /* NCNetworkingE2EEUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DF5291BC0CA0017149B /* NCNetworkingE2EEUpload.swift */; };
+		F7C30DF7291BC0D30017149B /* NCNetworkingE2EEUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DF5291BC0CA0017149B /* NCNetworkingE2EEUpload.swift */; };
+		F7C30DFA291BCF790017149B /* NCNetworkingE2EECreateFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DF9291BCF790017149B /* NCNetworkingE2EECreateFolder.swift */; };
+		F7C30DFB291BCF790017149B /* NCNetworkingE2EECreateFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DF9291BCF790017149B /* NCNetworkingE2EECreateFolder.swift */; };
+		F7C30DFD291BD0B80017149B /* NCNetworkingE2EEDelete.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DFC291BD0B80017149B /* NCNetworkingE2EEDelete.swift */; };
+		F7C30DFE291BD0B80017149B /* NCNetworkingE2EEDelete.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DFC291BD0B80017149B /* NCNetworkingE2EEDelete.swift */; };
+		F7C30E00291BD2610017149B /* NCNetworkingE2EERename.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DFF291BD2610017149B /* NCNetworkingE2EERename.swift */; };
+		F7C30E01291BD2610017149B /* NCNetworkingE2EERename.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C30DFF291BD2610017149B /* NCNetworkingE2EERename.swift */; };
 		F7C7B25028B8AD4500E7115D /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F7E70DE91A24DE4100E1B66A /* Localizable.strings */; };
 		F7C7B25128B8B0C400E7115D /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7F67BB81A24D27800EE80DA /* Images.xcassets */; };
 		F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7C7B488245EBA4100D93E60 /* NCViewerQuickLook.swift */; };
@@ -941,6 +952,9 @@
 		F7A0D1342591FBC5008F8A13 /* String+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extensions.swift"; sourceTree = "<group>"; };
 		F7A321AB1E9E6AD50069AD1B /* CCAdvanced.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CCAdvanced.h; sourceTree = "<group>"; };
 		F7A321AC1E9E6AD50069AD1B /* CCAdvanced.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CCAdvanced.m; sourceTree = "<group>"; };
+		F7A60F84292D215000FCE1F2 /* NCTalkAccounts.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCTalkAccounts.swift; sourceTree = "<group>"; };
+		F7A60F85292D215000FCE1F2 /* NCTalkAccounts.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NCTalkAccounts.storyboard; sourceTree = "<group>"; };
+		F7A7FA6229265CF4000603EF /* NCManageE2EE.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCManageE2EE.swift; sourceTree = "<group>"; };
 		F7A8D72228F176B6008BBE1C /* WidgetDashboardIntentHandler-Brinding-header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WidgetDashboardIntentHandler-Brinding-header.h"; sourceTree = "<group>"; };
 		F7A8D73028F17C44008BBE1C /* WidgetDashboardIntentHandler.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = WidgetDashboardIntentHandler.entitlements; sourceTree = "<group>"; };
 		F7A8D73328F17C62008BBE1C /* WidgetDashboardIntentHandler.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = WidgetDashboardIntentHandler.plist; sourceTree = "<group>"; };
@@ -1056,6 +1070,10 @@
 		F7BE7C79290ADF16002ABB61 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Intent.strings"; sourceTree = "<group>"; };
 		F7BE7C7B290ADF16002ABB61 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Intent.strings"; sourceTree = "<group>"; };
 		F7C1EEA425053A9C00866ACC /* NCDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCDataSource.swift; sourceTree = "<group>"; };
+		F7C30DF5291BC0CA0017149B /* NCNetworkingE2EEUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EEUpload.swift; sourceTree = "<group>"; };
+		F7C30DF9291BCF790017149B /* NCNetworkingE2EECreateFolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EECreateFolder.swift; sourceTree = "<group>"; };
+		F7C30DFC291BD0B80017149B /* NCNetworkingE2EEDelete.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EEDelete.swift; sourceTree = "<group>"; };
+		F7C30DFF291BD2610017149B /* NCNetworkingE2EERename.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCNetworkingE2EERename.swift; sourceTree = "<group>"; };
 		F7C40BE221998C050004137E /* PDFGenerator.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PDFGenerator.framework; path = Carthage/Build/iOS/PDFGenerator.framework; sourceTree = "<group>"; };
 		F7C40BE421998D5A0004137E /* MGSwipeTableCell.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MGSwipeTableCell.framework; path = Carthage/Build/iOS/MGSwipeTableCell.framework; sourceTree = "<group>"; };
 		F7C40BE621998F410004137E /* DZNEmptyDataSet.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DZNEmptyDataSet.framework; path = Carthage/Build/iOS/DZNEmptyDataSet.framework; sourceTree = "<group>"; };
@@ -1524,11 +1542,11 @@
 		F74D3DB81BAC1941000BAE4B /* Networking */ = {
 			isa = PBXGroup;
 			children = (
+				F7C30DF8291BCBF00017149B /* E2EE */,
 				F72CD63925C19EBF00F46F9A /* NCAutoUpload.swift */,
 				F75A9EE523796C6F0044CFCE /* NCNetworking.swift */,
 				F7D96FCB246ED7E100536D73 /* NCNetworkingCheckRemoteUser.swift */,
 				F7B8CD90261AF3F7007C1359 /* NCNetworkingChunkedUpload.swift */,
-				F785EE9C246196DF00B3F945 /* NCNetworkingE2EE.swift */,
 				F70D8D8024A4A9BF000A5756 /* NCNetworkingProcessUpload.swift */,
 				F72A47EB2487B06B005AD489 /* NCOperationQueue.swift */,
 				F755BD9A20594AC7008C5FBB /* NCService.swift */,
@@ -1781,6 +1799,7 @@
 				F7ACE42F1BAC0268006C0017 /* CCManageAutoUpload.m */,
 				F7ACE4301BAC0268006C0017 /* NCSettings.h */,
 				F7ACE4311BAC0268006C0017 /* NCSettings.m */,
+				F7A7FA6229265CF4000603EF /* NCManageE2EE.swift */,
 				F726EEEB1FED1C820030B9C8 /* NCEndToEndInitialize.swift */,
 				F738E8401F90FFD100F95C8E /* NCManageEndToEndEncryption.h */,
 				F738E8411F90FFD100F95C8E /* NCManageEndToEndEncryption.m */,
@@ -1877,6 +1896,18 @@
 			path = Brand;
 			sourceTree = "<group>";
 		};
+		F7C30DF8291BCBF00017149B /* E2EE */ = {
+			isa = PBXGroup;
+			children = (
+				F785EE9C246196DF00B3F945 /* NCNetworkingE2EE.swift */,
+				F7C30DFF291BD2610017149B /* NCNetworkingE2EERename.swift */,
+				F7C30DFC291BD0B80017149B /* NCNetworkingE2EEDelete.swift */,
+				F7C30DF9291BCF790017149B /* NCNetworkingE2EECreateFolder.swift */,
+				F7C30DF5291BC0CA0017149B /* NCNetworkingE2EEUpload.swift */,
+			);
+			path = E2EE;
+			sourceTree = "<group>";
+		};
 		F7C5259A1E3B441D00FFE02C /* Notification */ = {
 			isa = PBXGroup;
 			children = (
@@ -1919,6 +1950,8 @@
 			children = (
 				F7CA212C25F1333200826ABB /* NCAccountRequest.storyboard */,
 				F7CA212B25F1333200826ABB /* NCAccountRequest.swift */,
+				F7A60F85292D215000FCE1F2 /* NCTalkAccounts.storyboard */,
+				F7A60F84292D215000FCE1F2 /* NCTalkAccounts.swift */,
 			);
 			path = "Account Request";
 			sourceTree = "<group>";
@@ -2676,6 +2709,7 @@
 				F70A58C024D0545100DED00D /* NCCapabilitiesViewController.storyboard in Resources */,
 				F7F4F10627ECDBDB008676F9 /* Inconsolata-Medium.ttf in Resources */,
 				F749C10D23C4A5340027D966 /* NCIntro.storyboard in Resources */,
+				F7A60F87292D215000FCE1F2 /* NCTalkAccounts.storyboard in Resources */,
 				F7239877253D86D300257F49 /* NCEmptyView.xib in Resources */,
 				F747BA1F22354D2000971601 /* NCCreateFormUploadVoiceNote.storyboard in Resources */,
 				F719D9E0288D37A300762E33 /* NCColorPicker.storyboard in Resources */,
@@ -2790,6 +2824,7 @@
 				F73D5E48246DE09200DF6467 /* NCElementsJSON.swift in Sources */,
 				F7EDE4E5262D7BBE00414FE6 /* NCSectionHeaderFooter.swift in Sources */,
 				F79EC78926316AC4004E59D6 /* NCPopupViewController.swift in Sources */,
+				F7C30DFB291BCF790017149B /* NCNetworkingE2EECreateFolder.swift in Sources */,
 				AF4BF61F27562B3F0081CEEF /* NCManageDatabase+Activity.swift in Sources */,
 				F7A0D1362591FBC5008F8A13 /* String+Extensions.swift in Sources */,
 				F7EDE4D6262D7B9600414FE6 /* NCListCell.swift in Sources */,
@@ -2800,12 +2835,14 @@
 				F7E98C1727E0D0FC001F9F19 /* NCManageDatabase+Video.swift in Sources */,
 				F79B646126CA661600838ACA /* UIControl+Extensions.swift in Sources */,
 				F7EDE4CC262D7B6F00414FE6 /* NCEmptyDataSet.swift in Sources */,
+				F7C30E01291BD2610017149B /* NCNetworkingE2EERename.swift in Sources */,
 				AF4BF61A27562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */,
 				AF4BF615275629E20081CEEF /* NCManageDatabase+Account.swift in Sources */,
 				F798F0E225880608000DAFFD /* UIColor+Extensions.swift in Sources */,
 				AF3FDCC32796F3FB00710F60 /* NCTrashListCell+NCTrashCellProtocol.swift in Sources */,
 				AF817EF2274BC781009ED85B /* NCUserBaseUrl.swift in Sources */,
 				F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */,
+				F7C30DFE291BD0B80017149B /* NCNetworkingE2EEDelete.swift in Sources */,
 				F74AF3A5247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
 				AF1A9B6527D0CC0500F17A9E /* UIAlertController+Extension.swift in Sources */,
 				AF22B206277B4E4C00DAB0CC /* NCCreateFormUploadConflict.swift in Sources */,
@@ -2823,6 +2860,7 @@
 				F7B8CD96261AF401007C1359 /* NCNetworkingChunkedUpload.swift in Sources */,
 				F7BAADC91ED5A87C00B7EAD4 /* NCDatabase.swift in Sources */,
 				F7D57C8B26317BDE00DE301D /* NCAccountRequest.swift in Sources */,
+				F7C30DF7291BC0D30017149B /* NCNetworkingE2EEUpload.swift in Sources */,
 				F7D68FCE28CB9051009139F3 /* NCManageDatabase+DashboardWidget.swift in Sources */,
 				AF22B217277D196700DAB0CC /* NCShareExtension+DataSource.swift in Sources */,
 				F76D364728A4F8BF00214537 /* NCActivityIndicator.swift in Sources */,
@@ -2958,6 +2996,7 @@
 				F738E8421F90FFD100F95C8E /* NCManageEndToEndEncryption.m in Sources */,
 				F70753F12542A9A200972D44 /* NCViewerMedia.swift in Sources */,
 				F78A18B823CDE2B300F681F3 /* NCViewerRichWorkspace.swift in Sources */,
+				F7A60F86292D215000FCE1F2 /* NCTalkAccounts.swift in Sources */,
 				F77910AB25DD53C700CEDB9E /* NCSettingsBundleHelper.swift in Sources */,
 				AF4BF61927562A4B0081CEEF /* NCManageDatabase+Metadata.swift in Sources */,
 				F78A18B623CDD07D00F681F3 /* NCViewerRichWorkspaceWebView.swift in Sources */,
@@ -2997,6 +3036,7 @@
 				F78F74362163781100C2ADAD /* NCTrash.swift in Sources */,
 				AF817EF1274BC781009ED85B /* NCUserBaseUrl.swift in Sources */,
 				AF2D7C7C2742556F00ADF566 /* NCShareLinkCell.swift in Sources */,
+				F7C30E00291BD2610017149B /* NCNetworkingE2EERename.swift in Sources */,
 				F7F4F0F927ECDBA4008676F9 /* NCSubtitlePlayer.swift in Sources */,
 				F7651A8B23A2A3F2001403D2 /* NCCreateFormUploadDocuments.swift in Sources */,
 				F74AF3A4247FB6AE00AC767B /* NCUtilityFileSystem.swift in Sources */,
@@ -3013,6 +3053,7 @@
 				F73D5E47246DE09200DF6467 /* NCElementsJSON.swift in Sources */,
 				F710D1F52405770F00A6033D /* NCViewerPDF.swift in Sources */,
 				F7B6B70427C4E7FA00A7F6EB /* NCScan+CollectionView.swift in Sources */,
+				F7C30DF6291BC0CA0017149B /* NCNetworkingE2EEUpload.swift in Sources */,
 				F7501C332212E57500FB1415 /* NCMedia.swift in Sources */,
 				F70BFC7420E0FA7D00C67599 /* NCUtility.swift in Sources */,
 				F79EDAA526B004980007D134 /* NCPlayer.swift in Sources */,
@@ -3034,6 +3075,7 @@
 				AF93474C27E34120002537EE /* NCUtility+Image.swift in Sources */,
 				F702F30125EE5D2C008F8E80 /* NYMnemonic.m in Sources */,
 				AF93474E27E3F212002537EE /* NCShareNewUserAddComment.swift in Sources */,
+				F7C30DFD291BD0B80017149B /* NCNetworkingE2EEDelete.swift in Sources */,
 				AF93471227E2341B002537EE /* NCShare+Menu.swift in Sources */,
 				F7EFA47825ADBA500083159A /* NCViewerProviderContextMenu.swift in Sources */,
 				F755BD9B20594AC7008C5FBB /* NCService.swift in Sources */,
@@ -3060,6 +3102,7 @@
 				F707C26521A2DC5200F6181E /* NCStoreReview.swift in Sources */,
 				F7BAADCB1ED5A87C00B7EAD4 /* NCManageDatabase.swift in Sources */,
 				F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */,
+				F7C30DFA291BCF790017149B /* NCNetworkingE2EECreateFolder.swift in Sources */,
 				F7BC288026663F85004D46C5 /* NCViewCertificateDetails.swift in Sources */,
 				F702F2E625EE5C86008F8E80 /* NCAudioRecorderViewController.swift in Sources */,
 				D5B6AA7827200C7200D49C24 /* NCActivityTableViewCell.swift in Sources */,
@@ -3074,6 +3117,7 @@
 				F70CEF5623E9C7E50007035B /* UIColor+Extensions.swift in Sources */,
 				F77BB748289985270090FC19 /* UITabBarController+Extension.swift in Sources */,
 				F75AC2431F1F62450073EC19 /* NCManageAutoUploadFileName.swift in Sources */,
+				F7A7FA6329265CF4000603EF /* NCManageE2EE.swift in Sources */,
 				F7C7B489245EBA4100D93E60 /* NCViewerQuickLook.swift in Sources */,
 				F758B45E212C569D00515F55 /* NCScanCell.swift in Sources */,
 				F7581D1A25EFDA61004DC699 /* NCLoginWeb+Menu.swift in Sources */,
@@ -3917,8 +3961,8 @@
 			isa = XCRemoteSwiftPackageReference;
 			repositoryURL = "https://github.com/nextcloud/NextcloudKit";
 			requirement = {
-				kind = exactVersion;
-				version = 1.4.0;
+				branch = develop;
+				kind = branch;
 			};
 		};
 		F788ECC5263AAAF900ADC67F /* XCRemoteSwiftPackageReference "MarkdownKit" */ = {

+ 2 - 3
Share/NCShareExtension.swift

@@ -316,7 +316,7 @@ extension NCShareExtension {
             metadata.sessionSelector = NCGlobal.shared.selectorUploadFileShareExtension
             metadata.size = NCUtilityFileSystem.shared.getFileSize(filePath: toPath)
             metadata.status = NCGlobal.shared.metadataStatusWaitUpload
-            if NCManageDatabase.shared.getMetadataConflict(account: activeAccount.account, serverUrl: serverUrl, fileName: fileName) != nil {
+            if NCManageDatabase.shared.getMetadataConflict(account: activeAccount.account, serverUrl: serverUrl, fileNameView: fileName) != nil {
                 conflicts.append(metadata)
             } else {
                 uploadMetadata.append(metadata)
@@ -329,7 +329,6 @@ extension NCShareExtension {
             conflict.serverUrl = self.serverUrl
             conflict.metadatasUploadInConflict = conflicts
             conflict.delegate = self
-            conflict.isE2EE = CCUtility.isFolderEncrypted(self.serverUrl, e2eEncrypted: false, account: activeAccount.account, urlBase: activeAccount.urlBase, userId: activeAccount.userId)
             self.present(conflict, animated: true, completion: nil)
         } else {
             upload()
@@ -345,7 +344,7 @@ extension NCShareExtension {
         metadata.iconName = results.iconName
         metadata.classFile = results.classFile
         // E2EE
-        metadata.e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: activeAccount.userId)
+        metadata.e2eEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
         // CHUNCK
         metadata.chunk = chunckSize != 0 && metadata.size > chunckSize
 

+ 1 - 1
Widget/Files/FilesData.swift

@@ -237,7 +237,7 @@ func getFilesDataEntry(configuration: AccountIntent?, isPreview: Bool, displaySi
                 }
 
                 //
-                let isEncrypted = CCUtility.isFolderEncrypted(file.serverUrl, e2eEncrypted: file.e2eEncrypted, account: account.account, urlBase: file.urlBase, userId: file.userId)
+                let isEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: file.serverUrl, e2eEncrypted: file.e2eEncrypted, account: account.account, urlBase: file.urlBase, userId: file.userId)
                 let metadata = NCManageDatabase.shared.convertNCFileToMetadata(file, isEncrypted: isEncrypted, account: account.account)
 
                 // DATA

+ 158 - 0
iOSClient/Account Request/NCTalkAccounts.storyboard

@@ -0,0 +1,158 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="V0q-CP-xMJ">
+    <device id="retina6_0" orientation="portrait" appearance="light"/>
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
+        <capability name="Image references" minToolsVersion="12.0"/>
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--Talk Accounts-->
+        <scene sceneID="L90-uG-f4z">
+            <objects>
+                <viewController id="V0q-CP-xMJ" customClass="NCTalkAccounts" customModule="Nextcloud" customModuleProvider="target" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="4vK-ua-S0e"/>
+                        <viewControllerLayoutGuide type="bottom" id="hTI-Bw-Fws"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="gzh-6E-hc4">
+                        <rect key="frame" x="0.0" y="0.0" width="300" height="310"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Accounts" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="nZr-nE-ths">
+                                <rect key="frame" x="20" y="62" width="260" height="18"/>
+                                <fontDescription key="fontDescription" type="system" weight="medium" pointSize="15"/>
+                                <nil key="textColor"/>
+                                <nil key="highlightedColor"/>
+                            </label>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QqL-qq-3JI">
+                                <rect key="frame" x="16" y="63.666666666666657" width="15" height="15"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="15" id="FJj-0n-59w"/>
+                                    <constraint firstAttribute="width" constant="15" id="FLg-jE-FHJ"/>
+                                </constraints>
+                                <state key="normal" image="xmark"/>
+                                <connections>
+                                    <action selector="actionClose:" destination="V0q-CP-xMJ" eventType="touchUpInside" id="4cX-K8-0C0"/>
+                                </connections>
+                            </button>
+                            <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="Pdo-MB-AhU">
+                                <rect key="frame" x="10" y="95" width="280" height="215"/>
+                                <color key="backgroundColor" systemColor="systemBackgroundColor"/>
+                                <prototypes>
+                                    <tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" rowHeight="60" id="Q4K-la-J3W">
+                                        <rect key="frame" x="0.0" y="50" width="280" height="60"/>
+                                        <autoresizingMask key="autoresizingMask"/>
+                                        <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Q4K-la-J3W" id="IkA-cK-iZV">
+                                            <rect key="frame" x="0.0" y="0.0" width="280" height="60"/>
+                                            <autoresizingMask key="autoresizingMask"/>
+                                            <subviews>
+                                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="10" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="4cH-oC-YBd" userLabel="Avatar">
+                                                    <rect key="frame" x="0.0" y="16" width="30" height="28.666666666666664"/>
+                                                    <color key="tintColor" systemColor="systemGray2Color"/>
+                                                    <constraints>
+                                                        <constraint firstAttribute="height" constant="30" id="Efd-BU-u61"/>
+                                                        <constraint firstAttribute="width" constant="30" id="qFy-Tu-ov6"/>
+                                                    </constraints>
+                                                    <imageReference key="image" image="person.circle" catalog="system" variableValue="0.80000000000000004"/>
+                                                </imageView>
+                                                <label opaque="NO" userInteractionEnabled="NO" tag="20" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pWI-iZ-BTy" userLabel="User">
+                                                    <rect key="frame" x="40" y="21.666666666666668" width="220" height="17.000000000000004"/>
+                                                    <fontDescription key="fontDescription" type="boldSystem" pointSize="14"/>
+                                                    <nil key="textColor"/>
+                                                    <nil key="highlightedColor"/>
+                                                </label>
+                                                <label opaque="NO" userInteractionEnabled="NO" tag="30" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sWT-NJ-ihR" userLabel="Url">
+                                                    <rect key="frame" x="40" y="39.666666666666664" width="220" height="16"/>
+                                                    <fontDescription key="fontDescription" type="system" pointSize="13"/>
+                                                    <nil key="textColor"/>
+                                                    <nil key="highlightedColor"/>
+                                                </label>
+                                                <imageView clipsSubviews="YES" userInteractionEnabled="NO" tag="40" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="chevron.right" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="x47-dQ-alI" userLabel="Active">
+                                                    <rect key="frame" x="265" y="24.666666666666671" width="15" height="11.333333333333329"/>
+                                                    <color key="tintColor" systemColor="systemGray2Color"/>
+                                                    <constraints>
+                                                        <constraint firstAttribute="height" constant="15" id="SXt-LG-c5N"/>
+                                                        <constraint firstAttribute="width" constant="15" id="vdZ-4R-gY5"/>
+                                                    </constraints>
+                                                </imageView>
+                                            </subviews>
+                                            <constraints>
+                                                <constraint firstItem="4cH-oC-YBd" firstAttribute="leading" secondItem="IkA-cK-iZV" secondAttribute="leading" id="3cz-yE-yoQ"/>
+                                                <constraint firstAttribute="trailing" secondItem="x47-dQ-alI" secondAttribute="trailing" id="Dna-rj-LLQ"/>
+                                                <constraint firstItem="x47-dQ-alI" firstAttribute="centerY" secondItem="IkA-cK-iZV" secondAttribute="centerY" id="Ghs-sZ-Bzf"/>
+                                                <constraint firstItem="sWT-NJ-ihR" firstAttribute="leading" secondItem="4cH-oC-YBd" secondAttribute="trailing" constant="10" id="MxV-Du-vUw"/>
+                                                <constraint firstItem="4cH-oC-YBd" firstAttribute="centerY" secondItem="IkA-cK-iZV" secondAttribute="centerY" id="WuT-iT-3xk"/>
+                                                <constraint firstItem="sWT-NJ-ihR" firstAttribute="top" secondItem="pWI-iZ-BTy" secondAttribute="bottom" constant="1" id="ZVm-u7-JGI"/>
+                                                <constraint firstItem="x47-dQ-alI" firstAttribute="leading" secondItem="pWI-iZ-BTy" secondAttribute="trailing" constant="5" id="e8E-uH-gvt"/>
+                                                <constraint firstItem="pWI-iZ-BTy" firstAttribute="centerY" secondItem="IkA-cK-iZV" secondAttribute="centerY" id="jUS-89-RRO"/>
+                                                <constraint firstItem="x47-dQ-alI" firstAttribute="leading" secondItem="sWT-NJ-ihR" secondAttribute="trailing" constant="5" id="lIk-Sj-EgC"/>
+                                                <constraint firstItem="pWI-iZ-BTy" firstAttribute="leading" secondItem="4cH-oC-YBd" secondAttribute="trailing" constant="10" id="mlI-8s-1Ae"/>
+                                            </constraints>
+                                        </tableViewCellContentView>
+                                        <color key="backgroundColor" systemColor="secondarySystemGroupedBackgroundColor"/>
+                                    </tableViewCell>
+                                </prototypes>
+                                <connections>
+                                    <outlet property="dataSource" destination="V0q-CP-xMJ" id="xmA-NY-Eyz"/>
+                                    <outlet property="delegate" destination="V0q-CP-xMJ" id="j64-3x-N2u"/>
+                                </connections>
+                            </tableView>
+                            <progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="e8R-j7-ObS">
+                                <rect key="frame" x="0.0" y="308" width="300" height="2"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="2" id="G3U-2x-imL"/>
+                                </constraints>
+                                <color key="progressTintColor" systemColor="systemBlueColor"/>
+                            </progressView>
+                        </subviews>
+                        <color key="backgroundColor" systemColor="secondarySystemGroupedBackgroundColor"/>
+                        <constraints>
+                            <constraint firstItem="e8R-j7-ObS" firstAttribute="leading" secondItem="gzh-6E-hc4" secondAttribute="leading" id="1gS-Ca-1Ov"/>
+                            <constraint firstItem="Pdo-MB-AhU" firstAttribute="top" secondItem="nZr-nE-ths" secondAttribute="bottom" constant="15" id="5vV-YC-uzH"/>
+                            <constraint firstAttribute="trailing" secondItem="Pdo-MB-AhU" secondAttribute="trailing" constant="10" id="819-yV-vz7"/>
+                            <constraint firstItem="hTI-Bw-Fws" firstAttribute="top" secondItem="e8R-j7-ObS" secondAttribute="bottom" id="Cko-PC-TiW"/>
+                            <constraint firstAttribute="trailing" secondItem="nZr-nE-ths" secondAttribute="trailing" constant="20" id="DPW-MV-oKc"/>
+                            <constraint firstItem="hTI-Bw-Fws" firstAttribute="top" secondItem="Pdo-MB-AhU" secondAttribute="bottom" id="Ife-Ku-hGQ"/>
+                            <constraint firstItem="QqL-qq-3JI" firstAttribute="centerY" secondItem="nZr-nE-ths" secondAttribute="centerY" id="NpU-Hq-gMP"/>
+                            <constraint firstItem="nZr-nE-ths" firstAttribute="leading" secondItem="gzh-6E-hc4" secondAttribute="leading" constant="20" id="SI9-xL-6s8"/>
+                            <constraint firstItem="Pdo-MB-AhU" firstAttribute="leading" secondItem="gzh-6E-hc4" secondAttribute="leading" constant="10" id="Y5n-ju-hts"/>
+                            <constraint firstAttribute="trailing" secondItem="e8R-j7-ObS" secondAttribute="trailing" id="chh-t9-vJN"/>
+                            <constraint firstItem="QqL-qq-3JI" firstAttribute="leading" secondItem="gzh-6E-hc4" secondAttribute="leading" constant="16" id="gCH-qr-Dyd"/>
+                            <constraint firstItem="nZr-nE-ths" firstAttribute="top" secondItem="4vK-ua-S0e" secondAttribute="bottom" constant="15" id="oyJ-sj-j5N"/>
+                        </constraints>
+                    </view>
+                    <navigationItem key="navigationItem" id="Zon-2j-rsc"/>
+                    <size key="freeformSize" width="300" height="310"/>
+                    <connections>
+                        <outlet property="closeButton" destination="QqL-qq-3JI" id="6xR-LA-F9H"/>
+                        <outlet property="progressView" destination="e8R-j7-ObS" id="2gM-MB-IhE"/>
+                        <outlet property="tableView" destination="Pdo-MB-AhU" id="AcD-SW-2ga"/>
+                        <outlet property="titleLabel" destination="nZr-nE-ths" id="UbA-Dl-0Ad"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="qdm-Cl-C5l" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="1453.125" y="133.75"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="chevron.right" catalog="system" width="97" height="128"/>
+        <image name="person.circle" catalog="system" width="128" height="123"/>
+        <image name="xmark" width="24" height="24"/>
+        <systemColor name="secondarySystemGroupedBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+        <systemColor name="systemBackgroundColor">
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
+        </systemColor>
+        <systemColor name="systemBlueColor">
+            <color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+        <systemColor name="systemGray2Color">
+            <color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+        </systemColor>
+    </resources>
+</document>

+ 199 - 0
iOSClient/Account Request/NCTalkAccounts.swift

@@ -0,0 +1,199 @@
+//
+//  NCTalkAccounts.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 22/11/22.
+//  Copyright © 2021 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+import NextcloudKit
+
+public protocol NCTalkAccountsDelegate: AnyObject {
+    func selected(url: String, user: String)
+}
+
+// optional func
+public extension NCTalkAccountsDelegate {
+    func selected(url: String, user: String) {}
+}
+
+class NCTalkAccounts: UIViewController {
+
+    @IBOutlet weak var titleLabel: UILabel!
+    @IBOutlet weak var closeButton: UIButton!
+    @IBOutlet weak var tableView: UITableView!
+    @IBOutlet weak var progressView: UIProgressView!
+
+    public var accounts: [NKDataAccountFile] = []
+    public let heightCell: CGFloat = 60
+    public var enableTimerProgress: Bool = true
+    public var dismissDidEnterBackground: Bool = true
+    public weak var delegate: NCTalkAccountsDelegate?
+
+    private var timer: Timer?
+    private var time: Float = 0
+    private let secondsAutoDismiss: Float = 3
+
+    // MARK: - View Life Cycle
+
+    override func viewDidLoad() {
+        super.viewDidLoad()
+
+        titleLabel.text = NSLocalizedString("_account_select_to_add_", comment: "")
+
+        closeButton.setImage(NCUtility.shared.loadImage(named: "xmark", color: .label), for: .normal)
+
+        tableView.tableFooterView = UIView(frame: CGRect(x: 0, y: 0, width: tableView.frame.size.width, height: 1))
+        // tableView.separatorStyle = UITableViewCell.SeparatorStyle.none
+
+        view.backgroundColor = .secondarySystemBackground
+        tableView.backgroundColor = .secondarySystemBackground
+
+        progressView.trackTintColor = .clear
+        progressView.progress = 1
+        if enableTimerProgress {
+            progressView.isHidden = false
+        } else {
+            progressView.isHidden = true
+        }
+
+        NotificationCenter.default.addObserver(self, selector: #selector(startTimer), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidBecomeActive), object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(applicationDidEnterBackground), name: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterApplicationDidEnterBackground), object: nil)
+    }
+
+    override func viewWillAppear(_ animated: Bool) {
+        super.viewWillAppear(animated)
+    }
+
+    override func viewDidAppear(_ animated: Bool) {
+        super.viewDidAppear(animated)
+
+        let visibleCells = tableView.visibleCells
+
+        if visibleCells.count == accounts.count {
+            tableView.isScrollEnabled = false
+        }
+    }
+
+    override func viewWillDisappear(_ animated: Bool) {
+        super.viewWillDisappear(animated)
+
+        timer?.invalidate()
+    }
+
+    // MARK: - Action
+
+    @IBAction func actionClose(_ sender: UIButton) {
+        dismiss(animated: true)
+    }
+
+    // MARK: - NotificationCenter
+
+    @objc func applicationDidEnterBackground() {
+
+        if dismissDidEnterBackground {
+            dismiss(animated: false)
+        }
+    }
+
+    // MARK: - Progress
+
+    @objc func startTimer() {
+
+        if enableTimerProgress {
+            time = 0
+            timer?.invalidate()
+            timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateProgress), userInfo: nil, repeats: true)
+            progressView.isHidden = false
+        } else {
+            progressView.isHidden = true
+        }
+    }
+
+    @objc func updateProgress() {
+
+        time += 0.1
+        if time >= secondsAutoDismiss {
+            dismiss(animated: true)
+        } else {
+            progressView.progress = 1 - (time / secondsAutoDismiss)
+        }
+    }
+}
+
+extension NCTalkAccounts: UITableViewDelegate {
+
+    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
+
+        timer?.invalidate()
+        progressView.progress = 0
+    }
+
+    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
+        return heightCell
+    }
+
+    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
+
+        dismiss(animated: true) {
+            let account = self.accounts[indexPath.row]
+            self.delegate?.selected(url: account.url, user: account.user)
+        }
+    }
+}
+
+extension NCTalkAccounts: UITableViewDataSource {
+
+    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
+        return accounts.count
+    }
+
+    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
+
+        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
+        cell.backgroundColor = tableView.backgroundColor
+
+        let avatarImage = cell.viewWithTag(10) as? UIImageView
+        let userLabel = cell.viewWithTag(20) as? UILabel
+        let urlLabel = cell.viewWithTag(30) as? UILabel
+
+        userLabel?.text = ""
+        urlLabel?.text = ""
+
+        let account = accounts[indexPath.row]
+
+        if let avatarPath = account.avatar, !avatarPath.isEmpty, let avatarImage = avatarImage {
+            do {
+                let data = try Data(contentsOf: URL(fileURLWithPath: avatarPath))
+                if let image = UIImage(data: data) {
+                    avatarImage.image = image
+                }
+            } catch { print("Error: \(error)") }
+        }
+
+        if let alias = account.alias, !alias.isEmpty {
+            userLabel?.text = alias.uppercased() + " (\(account.user))"
+        } else {
+            userLabel?.text = account.user.uppercased()
+        }
+        urlLabel?.text = (URL(string: account.url)?.host ?? "")
+
+        return cell
+    }
+}

+ 26 - 0
iOSClient/AppDelegate.swift

@@ -228,6 +228,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
         // Request Service Server Nextcloud
         NCService.shared.startRequestServicesServer()
+
+        // Unlock E2EE
+        NCNetworkingE2EE.shared.unlockAll(account: account)
         
         // Request TouchID, FaceID
         enableTouchFaceID()
@@ -247,6 +250,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         NCNetworkingProcessUpload.shared.invalidateObserveTableMetadata()
         NCNetworkingProcessUpload.shared.stopTimer()
 
+        // Create file account for Nextcloud Talk
+        if let error = createDataAccountFile() {
+            NKCommon.shared.writeLog("[ERROR] Create account file for Talk \(error.localizedDescription)")
+        }
+
         if CCUtility.getPrivacyScreenEnabled() {
             // Privacy
             showPrivacyProtectionWindow()
@@ -309,6 +317,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         // Registeration push notification
         NCPushNotification.shared().pushNotification()
 
+        // Unlock E2EE
+        NCNetworkingE2EE.shared.unlockAll(account: account)
+        
         // Start services
         NCService.shared.startRequestServicesServer()
 
@@ -628,6 +639,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
     }
 
+    func createDataAccountFile() -> Error? {
+        guard !account.isEmpty else { return nil }
+
+        let url =  CCUtility.getDirectoryGroup().appendingPathComponent(NCGlobal.shared.appDatabaseNextcloud + "/" + NCGlobal.shared.fileAccounts)
+
+        let tableAccount = NCManageDatabase.shared.getAllAccount()
+        var accounts = [NKDataAccountFile]()
+        for account in tableAccount {
+            let userBaseUrl = account.user + "-" + (URL(string: account.urlBase)?.host ?? "")
+            let avatar = String(CCUtility.getDirectoryUserData()) + "/" +  userBaseUrl + "-\(account.user).png"
+            accounts.append(NKDataAccountFile(withUrl: account.urlBase, user: account.user, alias: account.alias, avatar: avatar))
+        }
+        return NKCommon.shared.createDataAccountFile(at: url, accounts: accounts)
+    }
+
     // MARK: - Account Request
 
     func accountRequestChangeAccount(account: String) {

+ 3 - 1
iOSClient/Brand/NCBrand.swift

@@ -51,6 +51,7 @@ import UIKit
 
     // Capabilities Group
     @objc public var capabilitiesGroups: String = "group.it.twsweb.Crypto-Cloud"
+    @objc public var capabilitiesGroupsTalk: String = "group.com.nextcloud.Talk"
 
     // User Agent
     @objc public var userAgent: String = "Nextcloud-iOS"                                                            // Don't touch me !!
@@ -58,7 +59,8 @@ import UIKit
     // BRAND ONLY
     @objc public var use_login_web_personalized:        Bool = false                                                // Don't touch me !!
     @objc public var use_AppConfig:                     Bool = false                                                // Don't touch me !!
-    
+    @objc public var use_talkDetect:                    Bool = true                                                 // Don't touch me !!
+
     // Options
     @objc public var use_default_auto_upload:           Bool = false
     @objc public var use_themingColor:                  Bool = true

+ 1 - 0
iOSClient/Brand/iOSClient.entitlements

@@ -7,6 +7,7 @@
 	<key>com.apple.security.application-groups</key>
 	<array>
 		<string>group.it.twsweb.Crypto-Cloud</string>
+		<string>group.com.nextcloud.Talk</string>
 	</array>
 	<key>keychain-access-groups</key>
 	<array>

+ 5 - 0
iOSClient/Data/NCDataSource.swift

@@ -531,6 +531,11 @@ class NCMetadataForSection: NSObject {
                 continue
             }
 
+            // Upload [REPLACE] skip
+            if metadata.session.isEmpty && !metadataInSession.filter({ $0.fileNameView == metadata.fileNameView }).isEmpty {
+                continue
+            }
+
             // share
             if let share = self.shares.filter({ $0.serverUrl == metadata.serverUrl && $0.fileName == metadata.fileName }).first {
                 metadataShare[metadata.ocId] = share

+ 6 - 6
iOSClient/Data/NCManageDatabase+Metadata.swift

@@ -31,7 +31,7 @@ extension NCManageDatabase {
         return tableMetadata.init(value: metadata)
     }
 
-    @objc func convertNCFileToMetadata(_ file: NKFile, isEncrypted: Bool, account: String) -> tableMetadata {
+    @objc func convertNCFileToMetadata(_ file: NKFile, isEncrypted: Bool = false, account: String) -> tableMetadata {
 
         let metadata = tableMetadata()
 
@@ -146,7 +146,7 @@ extension NCManageDatabase {
             if let key = listServerUrl[file.serverUrl] {
                 isEncrypted = key
             } else {
-                isEncrypted = CCUtility.isFolderEncrypted(file.serverUrl, e2eEncrypted: file.e2eEncrypted, account: account, urlBase: file.urlBase, userId: file.userId)
+                isEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: file.serverUrl, e2eEncrypted: file.e2eEncrypted, account: account, urlBase: file.urlBase, userId: file.userId)
                 listServerUrl[file.serverUrl] = isEncrypted
             }
 
@@ -817,12 +817,12 @@ extension NCManageDatabase {
         return false
     }
 
-    func getMetadataConflict(account: String, serverUrl: String, fileName: String) -> tableMetadata? {
+    func getMetadataConflict(account: String, serverUrl: String, fileNameView: String) -> tableMetadata? {
 
         // verify exists conflict
-        let fileNameExtension = (fileName as NSString).pathExtension.lowercased()
-        let fileNameWithoutExtension = (fileName as NSString).deletingPathExtension
-        var fileNameConflict = fileName
+        let fileNameExtension = (fileNameView as NSString).pathExtension.lowercased()
+        let fileNameWithoutExtension = (fileNameView as NSString).deletingPathExtension
+        var fileNameConflict = fileNameView
 
         if fileNameExtension == "heic" && CCUtility.getFormatCompatibility() {
             fileNameConflict = fileNameWithoutExtension + ".jpg"

+ 14 - 1
iOSClient/Data/NCManageDatabase.swift

@@ -939,6 +939,19 @@ class NCManageDatabase: NSObject {
         return tableE2eEncryptionLock.init(value: result)
     }
 
+    @objc func getE2EAllTokenLock(account: String) -> [tableE2eEncryptionLock] {
+
+        let realm = try! Realm()
+
+        let results = realm.objects(tableE2eEncryptionLock.self).filter("account == %@", account)
+
+        if results.count > 0 {
+            return Array(results.map { tableE2eEncryptionLock.init(value: $0) })
+        } else {
+            return []
+        }
+    }
+
     @objc func setE2ETokenLock(account: String, serverUrl: String, fileId: String, e2eToken: String) {
 
         let realm = try! Realm()
@@ -959,7 +972,7 @@ class NCManageDatabase: NSObject {
         }
     }
 
-    @objc func deteleE2ETokenLock(account: String, serverUrl: String) {
+    @objc func deleteE2ETokenLock(account: String, serverUrl: String) {
 
         let realm = try! Realm()
 

+ 16 - 7
iOSClient/Extensions/UIAlertController+Extension.swift

@@ -31,17 +31,26 @@ extension UIAlertController {
     ///   - urlBase: UrlBase object
     ///   - completion: If not` nil` it overrides the default behavior which shows an error using `NCContentPresenter`
     /// - Returns: The presentable alert controller
-    static func createFolder(serverUrl: String, urlBase: NCUserBaseUrl, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController {
+    static func createFolder(serverUrl: String, urlBase: NCUserBaseUrl, markE2ee: Bool = false, completion: ((_ error: NKError) -> Void)? = nil) -> UIAlertController {
         let alertController = UIAlertController(title: NSLocalizedString("_create_folder_", comment: ""), message: nil, preferredStyle: .alert)
 
         let okAction = UIAlertAction(title: NSLocalizedString("_save_", comment: ""), style: .default, handler: { _ in
             guard let fileNameFolder = alertController.textFields?.first?.text else { return }
-            NCNetworking.shared.createFolder(fileName: fileNameFolder, serverUrl: serverUrl, account: urlBase.account, urlBase: urlBase.urlBase, userId: urlBase.userId, overwrite: false) { error in
-                if let completion = completion {
-                    completion(error)
-                } else if error != .success {
-                    NCContentPresenter.shared.showError(error: error)
-                } // else: successful, no action
+            if markE2ee {
+                Task {
+                    let error = await NCNetworkingE2EECreateFolder.shared.createFolderAndMarkE2EE(fileName: fileNameFolder, serverUrl: serverUrl)
+                    if error != .success {
+                        NCContentPresenter.shared.showError(error: error)
+                    }
+                }
+            } else {
+                NCNetworking.shared.createFolder(fileName: fileNameFolder, serverUrl: serverUrl, account: urlBase.account, urlBase: urlBase.urlBase, userId: urlBase.userId, overwrite: false) { error in
+                    if let completion = completion {
+                        completion(error)
+                    } else if error != .success {
+                        NCContentPresenter.shared.showError(error: error)
+                    } // else: successful, no action
+                }
             }
         })
 

+ 12 - 0
iOSClient/Images.xcassets/talk.imageset/Contents.json

@@ -0,0 +1,12 @@
+{
+  "images" : [
+    {
+      "filename" : "talk.png",
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
iOSClient/Images.xcassets/talk.imageset/talk.png


+ 23 - 0
iOSClient/Images.xcassets/talk_bar.imageset/Contents.json

@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "filename" : "talk.png",
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "talk_bar 1.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "talk_bar 2.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
iOSClient/Images.xcassets/talk_bar.imageset/talk.png


BIN
iOSClient/Images.xcassets/talk_bar.imageset/talk_bar 1.png


BIN
iOSClient/Images.xcassets/talk_bar.imageset/talk_bar 2.png


+ 51 - 1
iOSClient/Login/NCLogin.swift

@@ -23,6 +23,7 @@
 
 import UIKit
 import NextcloudKit
+import SwiftEntryKit
 
 class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
 
@@ -40,6 +41,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
     private var textColorOpponent: UIColor = .black
     private var activeTextfieldDiff: CGFloat = 0
     private var activeTextField = UITextField()
+    private var talkAccounts: [NKDataAccountFile]?
 
     // MARK: - View Life Cycle
 
@@ -116,6 +118,16 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
             navigationItem.leftBarButtonItem = navigationItemCancel
         }
 
+        if NCBrandOptions.shared.use_talkDetect, let dirGroupTalk = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: NCBrandOptions.shared.capabilitiesGroupsTalk) {
+            let url = dirGroupTalk.appendingPathComponent(NCGlobal.shared.appDatabaseTalk + "/" + NCGlobal.shared.fileAccounts)
+            if FileManager.default.fileExists(atPath: url.path), let talkAccounts = NKCommon.shared.readDataAccountFile(at: url) {
+                self.talkAccounts = talkAccounts
+                let navigationItemTalk = UIBarButtonItem(image: UIImage(named: "talk_bar"), style: .plain, target: self, action: #selector(openTalkAccountsViewController))
+                navigationItemTalk.tintColor = textColor
+                self.navigationItem.rightBarButtonItem = navigationItemTalk
+            }
+        }
+
         self.navigationController?.navigationBar.setValue(true, forKey: "hidesShadow")
         view.backgroundColor = NCBrandColor.shared.customer
 
@@ -127,6 +139,14 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         super.viewDidAppear(animated)
 
         appDelegate.timerErrorNetworking?.invalidate()
+
+        if self.talkAccounts != nil, let image = UIImage(named: "talk"), let backgroundColor =  NCBrandColor.shared.brandElement.lighter(by: 10) {
+            NCContentPresenter.shared.alertAction(image: image, backgroundColor: backgroundColor, textColor: textColor, title: "_talk_detect_", description: "_talk_add_account_", textCancelButton: "_cancel_", textOkButton: "_ok_", attributes: EKAttributes.topFloat) { identifier in
+                if identifier == "ok" {
+                    self.openTalkAccountsViewController()
+                }
+            }
+        }
     }
 
     override func viewDidDisappear(_ animated: Bool) {
@@ -201,9 +221,30 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
 
     }
 
+    // MARK: - Talk accounts View Controller
+
+    @objc func openTalkAccountsViewController() {
+
+        if let talkAccounts = self.talkAccounts, let vc = UIStoryboard(name: "NCTalkAccounts", bundle: nil).instantiateInitialViewController() as? NCTalkAccounts {
+
+            vc.accounts = talkAccounts
+            vc.enableTimerProgress = false
+            vc.dismissDidEnterBackground = false
+            vc.delegate = self
+
+            let screenHeighMax = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/5)
+            let numberCell = talkAccounts.count
+            let height = min(CGFloat(numberCell * Int(vc.heightCell) + 45), screenHeighMax)
+
+            let popup = NCPopupViewController(contentController: vc, popupWidth: 300, popupHeight: height+20)
+
+            self.present(popup, animated: true)
+        }
+    }
+
     // MARK: - Login
 
-    func isUrlValid(url: String) {
+    func isUrlValid(url: String, user: String? = nil) {
 
         loginButton.isEnabled = false
 
@@ -225,6 +266,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
                         if let loginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb {
 
                             loginWeb.urlBase = url
+                            loginWeb.user = user
                             loginWeb.loginFlowV2Available = true
                             loginWeb.loginFlowV2Token = token!
                             loginWeb.loginFlowV2Endpoint = endpoint!
@@ -239,6 +281,7 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
                         if let loginWeb = UIStoryboard(name: "NCLogin", bundle: nil).instantiateViewController(withIdentifier: "NCLoginWeb") as? NCLoginWeb {
 
                             loginWeb.urlBase = url
+                            loginWeb.user = user
 
                             self.navigationController?.pushViewController(loginWeb, animated: true)
                         }
@@ -390,3 +433,10 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
         }
     }
 }
+
+extension NCLogin: NCTalkAccountsDelegate {
+
+    func selected(url: String, user: String) {
+        isUrlValid(url: url, user: user)
+    }
+}

+ 4 - 0
iOSClient/Login/NCLoginWeb.swift

@@ -34,6 +34,7 @@ class NCLoginWeb: UIViewController {
     var titleView: String = ""
 
     var urlBase = ""
+    var user: String?
     
     var configServerUrl: String?
     var configUsername: String?
@@ -118,6 +119,9 @@ class NCLoginWeb: UIViewController {
                 urlBase = loginFlowV2Login
             } else {
                 urlBase += "/index.php/login/flow"
+                if let user = self.user {
+                    urlBase += "?user=\(user)"
+                }
             }
         }
 

+ 18 - 6
iOSClient/Main/Collection Common/NCCollectionViewCommon.swift

@@ -568,7 +568,6 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
             return
         }
         
-        if metadata.livePhoto && metadata.classFile == NKCommon.typeClassFile.video.rawValue { return }
         let (indexPath, sameSections) = dataSource.reloadMetadata(ocId: metadata.ocId, ocIdTemp: ocIdTemp)
         if let indexPath = indexPath {
             if sameSections && (indexPath.section < collectionView.numberOfSections && indexPath.row < collectionView.numberOfItems(inSection: indexPath.section)) {
@@ -1007,8 +1006,7 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
         guard !appDelegate.account.isEmpty else { return }
 
         // E2EE
-        isEncryptedFolder = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: metadataFolder?.e2eEncrypted ?? false, account: appDelegate.account, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
-
+        isEncryptedFolder = NCUtility.shared.isFolderEncrypted(serverUrl: serverUrl, e2eEncrypted: metadataFolder?.e2eEncrypted ?? false, userBase: appDelegate)
         // get auto upload folder
         autoUploadFileName = NCManageDatabase.shared.getAccountAutoUploadFileName()
         autoUploadDirectory = NCManageDatabase.shared.getAccountAutoUploadDirectory(urlBase: appDelegate.urlBase, userId: appDelegate.userId, account: appDelegate.account)
@@ -1280,6 +1278,17 @@ class NCCollectionViewCommon: UIViewController, UIGestureRecognizerDelegate, UIS
     }
 }
 
+// MARK: - E2EE
+
+extension NCCollectionViewCommon: NCEndToEndInitializeDelegate {
+
+    func endToEndInitializeSuccess() {
+        if let metadata = appDelegate.activeMetadata {
+            pushMetadata(metadata)
+        }
+    }
+}
+
 // MARK: - Collection View
 
 extension NCCollectionViewCommon: UICollectionViewDelegate {
@@ -1302,8 +1311,9 @@ extension NCCollectionViewCommon: UICollectionViewDelegate {
         }
 
         if metadata.e2eEncrypted && !CCUtility.isEnd(toEndEnabled: appDelegate.account) {
-            let error = NKError(errorCode: NCGlobal.shared.errorE2EENotEnabled, errorDescription: "_e2e_goto_settings_for_enable_")
-            NCContentPresenter.shared.showInfo(error: error)
+            let e2ee = NCEndToEndInitialize()
+            e2ee.delegate = self
+            e2ee.initEndToEndEncryption()
             return
         }
 
@@ -1703,7 +1713,9 @@ extension NCCollectionViewCommon: UICollectionViewDataSource {
 
                 header.delegate = self
                 if headerMenuButtonsCommand && !isSearchingMode {
-                    header.setButtonsCommand(heigt: NCGlobal.shared.heightButtonsCommand, imageButton1: UIImage(named: "addImage"), titleButton1: NSLocalizedString("_upload_", comment: ""), imageButton2: UIImage(named: "folder"), titleButton2: NSLocalizedString("_create_folder_", comment: ""), imageButton3: UIImage(named: "scan"), titleButton3: NSLocalizedString("_scan_", comment: ""))
+                    let imageButton2 = isEncryptedFolder ? UIImage(named: "folderEncrypted") : UIImage(named: "folder")
+                    let titleButton2 = isEncryptedFolder ? NSLocalizedString("_create_folder_e2ee_", comment: "") : NSLocalizedString("_create_folder_", comment: "")
+                    header.setButtonsCommand(heigt: NCGlobal.shared.heightButtonsCommand, imageButton1: UIImage(named: "addImage"), titleButton1: NSLocalizedString("_upload_", comment: ""), imageButton2: imageButton2, titleButton2: titleButton2, imageButton3: UIImage(named: "scan"), titleButton3: NSLocalizedString("_scan_", comment: ""))
                 } else {
                     header.setButtonsCommand(heigt: 0)
                 }

+ 2 - 2
iOSClient/Main/Create cloud/NCCreateFormUploadAssets.swift

@@ -377,7 +377,8 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
                 metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile
                 metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
 
-                if NCManageDatabase.shared.getMetadataConflict(account: self.appDelegate.account, serverUrl: serverUrl, fileName: fileName) != nil {
+                if let result = NCManageDatabase.shared.getMetadataConflict(account: self.appDelegate.account, serverUrl: serverUrl, fileNameView: fileName) {
+                    metadataForUpload.fileName = result.fileName
                     metadatasUploadInConflict.append(metadataForUpload)
                 } else {
                     metadatasNOConflict.append(metadataForUpload)
@@ -394,7 +395,6 @@ class NCCreateFormUploadAssets: XLFormViewController, NCSelectDelegate {
                         conflict.metadatasNOConflict = metadatasNOConflict
                         conflict.metadatasUploadInConflict = metadatasUploadInConflict
                         conflict.delegate = self.appDelegate
-                        conflict.isE2EE = CCUtility.isFolderEncrypted(self.serverUrl, e2eEncrypted: false, account: self.appDelegate.account, urlBase: self.appDelegate.urlBase, userId: self.appDelegate.userId)
 
                         self.appDelegate.window?.rootViewController?.present(conflict, animated: true, completion: nil)
                     }

+ 11 - 18
iOSClient/Main/Create cloud/NCCreateFormUploadConflict.swift

@@ -58,7 +58,6 @@ extension NCCreateFormUploadConflictDelegate {
     @objc weak var delegate: NCCreateFormUploadConflictDelegate?
     @objc var alwaysNewFileNameNumber: Bool = false
     @objc var textLabelDetailNewFile: String?
-    @objc var isE2EE: Bool = false
 
     var metadatasConflictNewFiles: [String] = []
     var metadatasConflictAlreadyExistingFiles: [String] = []
@@ -150,17 +149,13 @@ extension NCCreateFormUploadConflictDelegate {
         let conflictAlert = UIAlertController(title: tile, message: "", preferredStyle: .alert)
 
         // REPLACE
-        if !isE2EE {
-            conflictAlert.addAction(UIAlertAction(title: titleReplace, style: .default, handler: { action in
-
-                for metadata in self.metadatasUploadInConflict {
-                    self.metadatasNOConflict.append(metadata)
-                }
+        conflictAlert.addAction(UIAlertAction(title: titleReplace, style: .default, handler: { action in
+            for metadata in self.metadatasUploadInConflict {
+                self.metadatasNOConflict.append(metadata)
+            }
+            self.buttonContinueTouch(action)
+        }))
 
-                self.buttonContinueTouch(action)
-            }))
-        }
-        
         // KEEP BOTH
         conflictAlert.addAction(UIAlertAction(title: titleKeep, style: .default, handler: { action in
 
@@ -178,12 +173,10 @@ extension NCCreateFormUploadConflictDelegate {
             }
         }))
 
-        if !isE2EE {
-            conflictAlert.addAction(UIAlertAction(title: NSLocalizedString("_more_action_title_", comment: ""), style: .default, handler: { _ in
-                self.blurView.removeFromSuperview()
-            }))
-        }
-        
+        conflictAlert.addAction(UIAlertAction(title: NSLocalizedString("_more_action_title_", comment: ""), style: .default, handler: { _ in
+            self.blurView.removeFromSuperview()
+        }))
+
         self.present(conflictAlert, animated: true, completion: nil)
     }
 
@@ -326,7 +319,7 @@ extension NCCreateFormUploadConflict: UITableViewDataSource {
 
             // -----> Already Existing File
 
-            guard let metadataAlreadyExists = NCManageDatabase.shared.getMetadataConflict(account: metadataNewFile.account, serverUrl: metadataNewFile.serverUrl, fileName: metadataNewFile.fileNameView) else { return UITableViewCell() }
+            guard let metadataAlreadyExists = NCManageDatabase.shared.getMetadataConflict(account: metadataNewFile.account, serverUrl: metadataNewFile.serverUrl, fileNameView: metadataNewFile.fileNameView) else { return UITableViewCell() }
             if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageIconOcId(metadataAlreadyExists.ocId, etag: metadataAlreadyExists.etag)) {
                 cell.imageAlreadyExistingFile.image =  UIImage(contentsOfFile: CCUtility.getDirectoryProviderStorageIconOcId(metadataAlreadyExists.ocId, etag: metadataAlreadyExists.etag))
             } else if FileManager().fileExists(atPath: CCUtility.getDirectoryProviderStorageOcId(metadataAlreadyExists.ocId, fileNameView: metadataAlreadyExists.fileNameView)) && metadataAlreadyExists.contentType == "application/pdf" {

+ 1 - 2
iOSClient/Main/Create cloud/NCCreateFormUploadDocuments.swift

@@ -268,7 +268,7 @@ import XLForm
                 fileNameForm = (fileNameForm as! NSString).deletingPathExtension + "." + fileNameExtension
             }
 
-            if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileName: String(describing: fileNameForm)) != nil {
+            if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: String(describing: fileNameForm)) != nil {
 
                 let metadataForUpload = NCManageDatabase.shared.createMetadata(account: appDelegate.account, user: appDelegate.user, userId: appDelegate.userId, fileName: String(describing: fileNameForm), fileNameView: String(describing: fileNameForm), ocId: "", serverUrl: serverUrl, urlBase: appDelegate.urlBase, url: "", contentType: "")
 
@@ -279,7 +279,6 @@ import XLForm
                 conflict.serverUrl = serverUrl
                 conflict.metadatasUploadInConflict = [metadataForUpload]
                 conflict.delegate = self
-                conflict.isE2EE = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: false, account: appDelegate.account, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
 
                 self.present(conflict, animated: true, completion: nil)
 

+ 1 - 2
iOSClient/Main/Create cloud/NCCreateFormUploadScanDocument.swift

@@ -404,7 +404,7 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
         metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFile
         metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
 
-        if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileName: fileNameSave) != nil {
+        if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: fileNameSave) != nil {
 
             guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
 
@@ -412,7 +412,6 @@ class NCCreateFormUploadScanDocument: XLFormViewController, NCSelectDelegate, NC
             conflict.serverUrl = serverUrl
             conflict.metadatasUploadInConflict = [metadataForUpload]
             conflict.delegate = self
-            conflict.isE2EE = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: false, account: appDelegate.account, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
 
             self.present(conflict, animated: true, completion: nil)
 

+ 1 - 2
iOSClient/Main/Create cloud/NCCreateFormUploadVoiceNote.swift

@@ -231,7 +231,7 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
         metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
         metadataForUpload.size = NCUtilityFileSystem.shared.getFileSize(filePath: fileNamePath)
 
-        if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileName: fileNameSave) != nil {
+        if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: fileNameSave) != nil {
 
             guard let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict else { return }
             
@@ -239,7 +239,6 @@ class NCCreateFormUploadVoiceNote: XLFormViewController, NCSelectDelegate, AVAud
             conflict.serverUrl = serverUrl
             conflict.metadatasUploadInConflict = [metadataForUpload]
             conflict.delegate = self
-            conflict.isE2EE = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: false, account: appDelegate.account, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
 
             self.present(conflict, animated: true, completion: nil)
 

+ 2 - 1
iOSClient/Main/NCFunctionCenter.swift

@@ -605,7 +605,8 @@ import Photos
         guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocId) else {
             return UIMenu()
         }
-        let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+
+        let isFolderEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
         var titleDeleteConfirmFile = NSLocalizedString("_delete_file_", comment: "")
         if metadata.directory { titleDeleteConfirmFile = NSLocalizedString("_delete_folder_", comment: "") }
         var titleSave: String = NSLocalizedString("_save_selected_files_", comment: "")

+ 1 - 2
iOSClient/Main/NCPickerViewController.swift

@@ -162,14 +162,13 @@ class NCDocumentPickerViewController: NSObject, UIDocumentPickerDelegate {
                 metadataForUpload.size = NCUtilityFileSystem.shared.getFileSize(filePath: toPath)
                 metadataForUpload.status = NCGlobal.shared.metadataStatusWaitUpload
 
-                if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileName: fileName) != nil {
+                if NCManageDatabase.shared.getMetadataConflict(account: appDelegate.account, serverUrl: serverUrl, fileNameView: fileName) != nil {
 
                     if let conflict = UIStoryboard(name: "NCCreateFormUploadConflict", bundle: nil).instantiateInitialViewController() as? NCCreateFormUploadConflict {
 
                         conflict.delegate = appDelegate
                         conflict.serverUrl = serverUrl
                         conflict.metadatasUploadInConflict = [metadataForUpload]
-                        conflict.isE2EE = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: false, account: appDelegate.account, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
 
                         appDelegate.window?.rootViewController?.present(conflict, animated: true, completion: nil)
                     }

+ 28 - 3
iOSClient/Menu/AppDelegate+Menu.swift

@@ -35,9 +35,11 @@ extension AppDelegate {
 
         let appDelegate = UIApplication.shared.delegate as! AppDelegate
         let directEditingCreators = NCManageDatabase.shared.getDirectEditingCreators(account: appDelegate.account)
-        let isEncrypted = CCUtility.isFolderEncrypted(appDelegate.activeServerUrl, e2eEncrypted: false, account: appDelegate.account, urlBase: appDelegate.urlBase, userId: appDelegate.userId)
+        let isEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: appDelegate.activeServerUrl, userBase: appDelegate)
         let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", appDelegate.account, appDelegate.activeServerUrl))
         let serverVersionMajor = NCManageDatabase.shared.getCapabilitiesServerInt(account: appDelegate.account, elements: NCElementsJSON.shared.capabilitiesVersionMajor)
+        let serverUrlHome = NCUtilityFileSystem.shared.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
+
 
         actions.append(
             NCMenuAction(
@@ -112,9 +114,16 @@ extension AppDelegate {
             )
         )
 
+        if CCUtility.isEnd(toEndEnabled: appDelegate.account) && appDelegate.activeServerUrl == serverUrlHome {
+            actions.append(.seperator)
+        }
+
+        let titleCreateFolder = isEncrypted ? NSLocalizedString("_create_folder_e2ee_", comment: "") : NSLocalizedString("_create_folder_", comment: "")
+        let imageCreateFolder = isEncrypted ? UIImage(named: "folderEncrypted")! : UIImage(named: "folder")!
+
         actions.append(
-            NCMenuAction(title: NSLocalizedString("_create_folder_", comment: ""),
-                icon: UIImage(named: "folder")!.image(color: NCBrandColor.shared.brandElement, size: 50), action: { _ in
+            NCMenuAction(title: titleCreateFolder,
+                icon: imageCreateFolder.image(color: NCBrandColor.shared.brandElement, size: 50), action: { _ in
                     guard !appDelegate.activeServerUrl.isEmpty else { return }
                     let alertController = UIAlertController.createFolder(serverUrl: appDelegate.activeServerUrl, urlBase: appDelegate)
                     appDelegate.window?.rootViewController?.present(alertController, animated: true, completion: nil)
@@ -122,6 +131,22 @@ extension AppDelegate {
             )
         )
 
+        if CCUtility.isEnd(toEndEnabled: appDelegate.account) && appDelegate.activeServerUrl == serverUrlHome {
+            actions.append(
+                NCMenuAction(title: NSLocalizedString("_create_folder_e2ee_", comment: ""),
+                    icon: UIImage(named: "folderEncrypted")!.image(color: NCBrandColor.shared.brandElement, size: 50), action: { _ in
+                        guard !appDelegate.activeServerUrl.isEmpty else { return }
+                        let alertController = UIAlertController.createFolder(serverUrl: appDelegate.activeServerUrl, urlBase: appDelegate, markE2ee: true)
+                        appDelegate.window?.rootViewController?.present(alertController, animated: true, completion: nil)
+                    }
+                )
+            )
+        }
+
+        if CCUtility.isEnd(toEndEnabled: appDelegate.account) && appDelegate.activeServerUrl == serverUrlHome {
+            actions.append(.seperator)
+        }
+
         if serverVersionMajor >= NCGlobal.shared.nextcloudVersion18 && directory?.richWorkspace == nil && !isEncrypted && NextcloudKit.shared.isNetworkReachable() {
             actions.append(
                 NCMenuAction(

+ 1 - 1
iOSClient/Menu/NCCollectionViewCommon+Menu.swift

@@ -38,7 +38,7 @@ extension NCCollectionViewCommon {
 
         guard let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) else { return }
         let serverUrl = metadata.serverUrl + "/" + metadata.fileName
-        let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+        let isFolderEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
         let serverUrlHome = NCUtilityFileSystem.shared.getHomeServer(urlBase: appDelegate.urlBase, userId: appDelegate.userId)
         let isOffline: Bool
 

+ 1 - 1
iOSClient/Menu/NCViewer+Menu.swift

@@ -35,7 +35,7 @@ extension NCViewer {
         var titleFavorite = NSLocalizedString("_add_favorites_", comment: "")
         if metadata.favorite { titleFavorite = NSLocalizedString("_remove_favorites_", comment: "") }
         let localFile = NCManageDatabase.shared.getTableLocalFile(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-        let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+        let isFolderEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
         let isOffline = localFile?.offline == true
 
         //

+ 2 - 0
iOSClient/NCGlobal.swift

@@ -89,6 +89,7 @@ class NCGlobal: NSObject {
     @objc let appDatabaseNextcloud                  = "Library/Application Support/Nextcloud"
     @objc let appScan                               = "Library/Application Support/Scan"
     @objc let appUserData                           = "Library/Application Support/UserData"
+    @objc let appDatabaseTalk                       = "Library/Application Support/Talk"
 
     // Service
     //
@@ -122,6 +123,7 @@ class NCGlobal: NSObject {
     //
     let databaseDefault                             = "nextcloud.realm"
     let databaseSchemaVersion: UInt64               = 255
+    let fileAccounts: String                        = "accounts.json"
 
     // Intro selector
     //

+ 87 - 0
iOSClient/Networking/E2EE/NCNetworkingE2EE.swift

@@ -0,0 +1,87 @@
+//
+//  NCNetworkingE2EE.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 05/05/2020.
+//  Copyright © 2020 Marino Faggiana. All rights reserved.
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+import OpenSSL
+import NextcloudKit
+import CFNetwork
+import Alamofire
+
+class NCNetworkingE2EE: NSObject {
+    public static let shared: NCNetworkingE2EE = {
+        let instance = NCNetworkingE2EE()
+        return instance
+    }()
+
+    func generateRandomIdentifier() -> String {
+
+        var UUID = NSUUID().uuidString
+        UUID = "E2EE" + UUID.replacingOccurrences(of: "-", with: "")
+        return UUID
+    }
+
+    func lock(account: String, serverUrl: String) async -> (fileId: String?, e2eToken: String?, error: NKError) {
+
+        var e2eToken: String?
+
+        guard let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl)) else {
+            return (nil, nil, NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_e2e_error_lock_"))
+        }
+
+        if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: serverUrl) {
+            e2eToken = tableLock.e2eToken
+        }
+
+        let lockE2EEFolderResults = await NextcloudKit.shared.lockE2EEFolder(fileId: directory.fileId, e2eToken: e2eToken, method: "POST")
+        if lockE2EEFolderResults.error == .success, let e2eToken = lockE2EEFolderResults.e2eToken {
+            NCManageDatabase.shared.setE2ETokenLock(account: account, serverUrl: serverUrl, fileId: directory.fileId, e2eToken: e2eToken)
+        }
+
+        return (directory.fileId, lockE2EEFolderResults.e2eToken, lockE2EEFolderResults.error)
+    }
+
+    func unlock(account: String, serverUrl: String) async -> () {
+
+        guard let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: serverUrl) else {
+            return
+        }
+
+        let lockE2EEFolderResults = await NextcloudKit.shared.lockE2EEFolder(fileId: tableLock.fileId, e2eToken: tableLock.e2eToken, method: "DELETE")
+        if lockE2EEFolderResults.error == .success {
+            NCManageDatabase.shared.deleteE2ETokenLock(account: account, serverUrl: serverUrl)
+        }
+
+        return
+    }
+
+    func unlockAll(account: String) {
+        guard CCUtility.isEnd(toEndEnabled: account) else { return }
+
+        Task {
+            for result in NCManageDatabase.shared.getE2EAllTokenLock(account: account) {
+                let lockE2EEFolderResults = await NextcloudKit.shared.lockE2EEFolder(fileId: result.fileId, e2eToken: result.e2eToken, method: "DELETE")
+                if lockE2EEFolderResults.error == .success {
+                    NCManageDatabase.shared.deleteE2ETokenLock(account: account, serverUrl: result.serverUrl)
+                }
+            }
+        }
+    }
+}

+ 150 - 0
iOSClient/Networking/E2EE/NCNetworkingE2EECreateFolder.swift

@@ -0,0 +1,150 @@
+//
+//  NCNetworkingE2EECreateFolder.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 09/11/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+import OpenSSL
+import NextcloudKit
+import CFNetwork
+import Alamofire
+import Foundation
+
+class NCNetworkingE2EECreateFolder: NSObject {
+    public static let shared: NCNetworkingE2EECreateFolder = {
+        let instance = NCNetworkingE2EECreateFolder()
+        return instance
+    }()
+
+    func createFolderAndMarkE2EE(fileName: String, serverUrl: String) async -> NKError {
+
+        let serverUrlFileName = serverUrl + "/" + fileName
+        var error = NKError()
+
+        let createFolderResults = await NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName)
+        if createFolderResults.error != .success { return createFolderResults.error }
+
+        let readFileOrFolderResults = await NextcloudKit.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0")
+        error = readFileOrFolderResults.error
+        if error == .success, let file = readFileOrFolderResults.files.first {
+
+            let markE2EEFolderResults = await NextcloudKit.shared.markE2EEFolder(fileId: file.fileId, delete: false)
+            if markE2EEFolderResults.error != .success { return markE2EEFolderResults.error }
+
+            file.e2eEncrypted = true
+            guard let metadata = NCManageDatabase.shared.addMetadata(NCManageDatabase.shared.convertNCFileToMetadata(file, account: readFileOrFolderResults.account)) else {
+                return error
+            }
+            NCManageDatabase.shared.addDirectory(encrypted: true, favorite: metadata.favorite, ocId: metadata.ocId, fileId: metadata.fileId, etag: nil, permissions: metadata.permissions, serverUrl: serverUrlFileName, account: metadata.account)
+            NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, serverUrlFileName))
+
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterChangeStatusFolderE2EE, userInfo: ["serverUrl": serverUrl])
+        }
+
+        return error
+    }
+
+    func createFolder(fileName: String, serverUrl: String, account: String, urlBase: String, userId: String) async -> (NKError) {
+
+        var fileNameFolder = CCUtility.removeForbiddenCharactersServer(fileName)!
+        var serverUrlFileName = ""
+        var fileNameIdentifier = ""
+        var ocId: String?
+        var error = NKError()
+
+        fileNameFolder = NCUtilityFileSystem.shared.createFileName(fileNameFolder, serverUrl: serverUrl, account: account)
+        if fileNameFolder.isEmpty { return error }
+        fileNameIdentifier = NCNetworkingE2EE.shared.generateRandomIdentifier()
+        serverUrlFileName = serverUrl + "/" + fileNameIdentifier
+
+        // ** Lock **
+        let lockResults = await NCNetworkingE2EE.shared.lock(account: account, serverUrl: serverUrl)
+
+        error = lockResults.error
+        if error == .success, let e2eToken = lockResults.e2eToken, let fileIdLock = lockResults.fileId {
+
+            let createFolderResults = await NextcloudKit.shared.createFolder(serverUrlFileName: serverUrlFileName, options: NKRequestOptions(customHeader: ["e2e-token": e2eToken]))
+            error = createFolderResults.error
+            ocId = createFolderResults.ocId
+            if error == .success, let fileId = NCUtility.shared.ocIdToFileId(ocId: ocId) {
+                // Mark folder as E2EE
+                let markE2EEFolderResults = await NextcloudKit.shared.markE2EEFolder(fileId: fileId, delete: false)
+                error = markE2EEFolderResults.error
+                if error == .success {
+                    error = await createE2Ee(e2eToken: e2eToken, fileIdLock: fileIdLock, account: account, fileNameFolder: fileNameFolder, fileNameIdentifier: fileNameIdentifier, serverUrl: serverUrl, urlBase: urlBase, userId: userId)
+                }
+            }
+        }
+
+        // ** Unlock **
+        await NCNetworkingE2EE.shared.unlock(account: account, serverUrl: serverUrl)
+        
+        if error == .success, let ocId = ocId {
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterCreateFolder, userInfo: ["ocId": ocId, "serverUrl": serverUrl, "account": account, "e2ee": true])
+        }
+        return error
+    }
+
+    private func createE2Ee(e2eToken: String, fileIdLock: String, account: String, fileNameFolder: String, fileNameIdentifier: String, serverUrl: String,  urlBase: String, userId: String) async -> (NKError) {
+
+        var key: NSString?
+        var initializationVector: NSString?
+        let object = tableE2eEncryption()
+        var method = "POST"
+
+        // Get last metadata
+        let getE2EEMetadataResults = await NextcloudKit.shared.getE2EEMetadata(fileId: fileIdLock, e2eToken: e2eToken)
+        if getE2EEMetadataResults.error == .success, let e2eMetadata = getE2EEMetadataResults.e2eMetadata {
+            if !NCEndToEndMetadata.shared.decoderMetadata(e2eMetadata, privateKey: CCUtility.getEndToEndPrivateKey(account), serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId) {
+                return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: NSLocalizedString("_e2e_error_encode_metadata_", comment: ""))
+            }
+            method = "PUT"
+        }
+
+        // Add new metadata
+        NCEndToEndEncryption.sharedManager()?.encryptkey(&key, initializationVector: &initializationVector)
+        object.account = account
+        object.authenticationTag = nil
+        object.fileName = fileNameFolder
+        object.fileNameIdentifier = fileNameIdentifier
+        object.fileNamePath = ""
+        object.key = key! as String
+        object.initializationVector = initializationVector! as String
+        if let result = NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl)) {
+            object.metadataKey = result.metadataKey
+            object.metadataKeyIndex = result.metadataKeyIndex
+        } else {
+            object.metadataKey = (NCEndToEndEncryption.sharedManager()?.generateKey(16)?.base64EncodedString(options: []))! as String // AES_KEY_128_LENGTH
+            object.metadataKeyIndex = 0
+        }
+        object.mimeType = "httpd/unix-directory"
+        object.serverUrl = serverUrl
+        object.version = 1
+        NCManageDatabase.shared.addE2eEncryption(object)
+
+        // Rebuild metadata for send it
+        guard let tableE2eEncryption = NCManageDatabase.shared.getE2eEncryptions(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl)), let e2eMetadataNew = NCEndToEndMetadata.shared.encoderMetadata(tableE2eEncryption, privateKey: CCUtility.getEndToEndPrivateKey(account), serverUrl: serverUrl) else {
+            return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: NSLocalizedString("_e2e_error_encode_metadata_", comment: ""))
+        }
+
+        // send metadata
+        let putE2EEMetadataResults =  await NextcloudKit.shared.putE2EEMetadata(fileId: fileIdLock, e2eToken: e2eToken, e2eMetadata: e2eMetadataNew, method: method)
+        return putE2EEMetadataResults.error
+    }
+}

+ 85 - 0
iOSClient/Networking/E2EE/NCNetworkingE2EEDelete.swift

@@ -0,0 +1,85 @@
+//
+//  NCNetworkingE2EEDelete.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 09/11/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+import OpenSSL
+import NextcloudKit
+import CFNetwork
+import Alamofire
+import Foundation
+
+class NCNetworkingE2EEDelete: NSObject {
+    public static let shared: NCNetworkingE2EEDelete = {
+        let instance = NCNetworkingE2EEDelete()
+        return instance
+    }()
+
+    func delete(metadata: tableMetadata) async -> (NKError) {
+
+        var error = NKError()
+
+        func sendE2EMetadata(e2eToken: String, fileId: String) async -> (NKError) {
+
+            var e2eMetadataNew: String?
+            var method = "PUT"
+
+            // Get last metadata
+            let getE2EEMetadataResults = await NextcloudKit.shared.getE2EEMetadata(fileId: fileId, e2eToken: e2eToken)
+            guard getE2EEMetadataResults.error == .success, let e2eMetadata = getE2EEMetadataResults.e2eMetadata, NCEndToEndMetadata.shared.decoderMetadata(e2eMetadata, privateKey: CCUtility.getEndToEndPrivateKey(metadata.account), serverUrl: metadata.serverUrl, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId) else {
+                    return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: NSLocalizedString("_e2e_error_encode_metadata_", comment: ""))
+            }
+
+            // delete
+            NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameIdentifier == %@", metadata.account, metadata.serverUrl, metadata.fileName))
+
+            // Rebuild metadata
+            if let tableE2eEncryption = NCManageDatabase.shared.getE2eEncryptions(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
+                e2eMetadataNew = NCEndToEndMetadata.shared.encoderMetadata(tableE2eEncryption, privateKey: CCUtility.getEndToEndPrivateKey(metadata.account), serverUrl: metadata.serverUrl)
+            } else {
+                method = "DELETE"
+            }
+
+            // Send metadata
+            let putE2EEMetadataResults = await NextcloudKit.shared.putE2EEMetadata(fileId: fileId, e2eToken: e2eToken, e2eMetadata: e2eMetadataNew, method: method)
+            
+            return putE2EEMetadataResults.error
+        }
+
+        // ** Lock **
+        let lockResults = await NCNetworkingE2EE.shared.lock(account: metadata.account, serverUrl: metadata.serverUrl)
+
+        error = lockResults.error
+        if error == .success, let e2eToken = lockResults.e2eToken, let fileId = lockResults.fileId {
+
+            let deleteMetadataPlainError = await NCNetworking.shared.deleteMetadataPlain(metadata, customHeader: ["e2e-token": e2eToken])
+            error = deleteMetadataPlainError
+            if error == .success {
+                let sendE2EMetadataError = await sendE2EMetadata(e2eToken: e2eToken, fileId: fileId)
+                error = sendE2EMetadataError
+            }
+        }
+
+        // ** Unlock **
+        await NCNetworkingE2EE.shared.unlock(account: metadata.account, serverUrl: metadata.serverUrl)
+        
+        return error
+    }
+}

+ 94 - 0
iOSClient/Networking/E2EE/NCNetworkingE2EERename.swift

@@ -0,0 +1,94 @@
+//
+//  NCNetworkingE2EERename.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 09/11/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+import OpenSSL
+import NextcloudKit
+import CFNetwork
+import Alamofire
+import Foundation
+
+class NCNetworkingE2EERename: NSObject {
+    public static let shared: NCNetworkingE2EERename = {
+        let instance = NCNetworkingE2EERename()
+        return instance
+    }()
+
+    func rename(metadata: tableMetadata, fileNameNew: String) async -> (NKError) {
+
+        var error = NKError()
+
+        func sendE2EMetadata(e2eToken: String, fileId: String) async -> (NKError) {
+
+            // Get last metadata
+            let getE2EEMetadataResults = await NextcloudKit.shared.getE2EEMetadata(fileId: fileId, e2eToken: e2eToken)
+            guard getE2EEMetadataResults.error == .success, let e2eMetadata = getE2EEMetadataResults.e2eMetadata, NCEndToEndMetadata.shared.decoderMetadata(e2eMetadata, privateKey: CCUtility.getEndToEndPrivateKey(metadata.account), serverUrl: metadata.serverUrl, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId) else {
+                return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: NSLocalizedString("_e2e_error_encode_metadata_", comment: ""))
+            }
+
+            // rename
+            NCManageDatabase.shared.renameFileE2eEncryption(serverUrl: metadata.serverUrl, fileNameIdentifier: metadata.fileName, newFileName: fileNameNew, newFileNamePath: CCUtility.returnFileNamePath(fromFileName: fileNameNew, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId, account: metadata.account))
+
+            // Rebuild metadata
+            guard let tableE2eEncryption = NCManageDatabase.shared.getE2eEncryptions(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)), let e2eMetadataNew = NCEndToEndMetadata.shared.encoderMetadata(tableE2eEncryption, privateKey: CCUtility.getEndToEndPrivateKey(metadata.account), serverUrl: metadata.serverUrl) else {
+                return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: NSLocalizedString("_e2e_error_encode_metadata_", comment: ""))
+            }
+
+            // send metadata
+            let putE2EEMetadataResults = await NextcloudKit.shared.putE2EEMetadata(fileId: fileId, e2eToken: e2eToken, e2eMetadata: e2eMetadataNew, method: "PUT")
+            
+            return putE2EEMetadataResults.error
+        }
+
+        // verify if exists the new fileName
+        if NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, fileNameNew)) != nil {
+            return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_file_already_exists_")
+        }
+
+        // ** Lock **
+        let lockResults = await NCNetworkingE2EE.shared.lock(account: metadata.account, serverUrl: metadata.serverUrl)
+
+        error = lockResults.error
+        if error == .success, let e2eToken = lockResults.e2eToken, let fileId = lockResults.fileId {
+
+            let sendE2EMetadataError = await sendE2EMetadata(e2eToken: e2eToken, fileId: fileId)
+            error = sendE2EMetadataError
+            if error == .success {
+                NCManageDatabase.shared.setMetadataFileNameView(serverUrl: metadata.serverUrl, fileName: metadata.fileName, newFileNameView: fileNameNew, account: metadata.account)
+                // Move file system
+                let atPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId) + "/" + metadata.fileNameView
+                let toPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId) + "/" + fileNameNew
+                do {
+                    try FileManager.default.moveItem(atPath: atPath, toPath: toPath)
+                } catch { }
+            }
+        }
+
+        // ** Unlock **
+        await NCNetworkingE2EE.shared.unlock(account: metadata.account, serverUrl: metadata.serverUrl)
+        
+        if error == .success {
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRenameFile, userInfo: ["ocId": metadata.ocId, "account": metadata.account])
+        }
+
+        return error
+    }
+}

+ 187 - 0
iOSClient/Networking/E2EE/NCNetworkingE2EEUpload.swift

@@ -0,0 +1,187 @@
+//
+//  NCNetworkingE2EEUpload.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 09/11/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import UIKit
+import OpenSSL
+import NextcloudKit
+import CFNetwork
+import Alamofire
+import Foundation
+
+class NCNetworkingE2EEUpload: NSObject {
+    public static let shared: NCNetworkingE2EEUpload = {
+        let instance = NCNetworkingE2EEUpload()
+        return instance
+    }()
+
+    func upload(metadata: tableMetadata) async -> (NKError) {
+
+        var metadata = tableMetadata.init(value: metadata)
+        let ocIdTemp = metadata.ocId
+        let errorCreateEncrypted = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_e2e_error_create_encrypted_")
+
+        // Verify max size
+        if metadata.size > NCGlobal.shared.e2eeMaxFileSize {
+            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": metadata.ocId, "error": NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "E2E Error file too big")])
+            return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "E2E Error file too big")
+        }
+
+        // Create metadata for upload
+        if let result = NCManageDatabase.shared.getMetadata(predicate: NSPredicate(format: "serverUrl == %@ AND fileNameView == %@ AND ocId != %@", metadata.serverUrl, metadata.fileNameView, metadata.ocId)) {
+            metadata.fileName = result.fileName
+        } else {
+            metadata.fileName = NCNetworkingE2EE.shared.generateRandomIdentifier()
+        }
+        metadata.e2eEncrypted = true
+        metadata.session = NKCommon.shared.sessionIdentifierUpload
+        metadata.sessionError = ""
+        guard let result = NCManageDatabase.shared.addMetadata(metadata) else { return errorCreateEncrypted }
+        metadata = result
+
+        // ** Lock **
+        let lockResults = await NCNetworkingE2EE.shared.lock(account: metadata.account, serverUrl: metadata.serverUrl)
+
+        guard let e2eToken = lockResults.e2eToken, let fileId = lockResults.fileId, lockResults.error == .success else {
+            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp))
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_e2e_error_create_encrypted_")])
+            return errorCreateEncrypted
+        }
+
+        // Send e2e metadata
+        let createE2EeError = await createE2Ee(metadata: metadata, e2eToken: e2eToken, fileId: fileId)
+        guard createE2EeError == .success else {
+            // ** Unlock **
+            await NCNetworkingE2EE.shared.unlock(account: metadata.account, serverUrl: metadata.serverUrl)
+
+            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp))
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": createE2EeError])
+            return errorCreateEncrypted
+        }
+
+        // Send file
+        let sendFileResults = await sendFile(metadata: metadata, e2eToken: e2eToken)
+
+        // ** Unlock **
+        await NCNetworkingE2EE.shared.unlock(account: metadata.account, serverUrl: metadata.serverUrl)
+
+        if sendFileResults.afError?.isExplicitlyCancelledError ?? false {
+
+            CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
+            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": sendFileResults.error])
+
+        } else if sendFileResults.error == .success, let ocId = sendFileResults.ocId {
+
+            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
+            NCUtilityFileSystem.shared.moveFileInBackground(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId), toPath: CCUtility.getDirectoryProviderStorageOcId(ocId))
+
+            metadata.date = sendFileResults.date ?? NSDate()
+            metadata.etag = sendFileResults.etag ?? ""
+            metadata.ocId = ocId
+
+            metadata.session = ""
+            metadata.sessionError = ""
+            metadata.sessionTaskIdentifier = 0
+            metadata.status = NCGlobal.shared.metadataStatusNormal
+
+            NCManageDatabase.shared.addMetadata(metadata)
+            NCManageDatabase.shared.addLocalFile(metadata: metadata)
+            NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile)
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": sendFileResults.error])
+
+        } else {
+
+            NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: sendFileResults.error.errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError)
+            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": sendFileResults.error])
+        }
+
+        return(sendFileResults.error)
+    }
+
+    private func createE2Ee(metadata: tableMetadata, e2eToken: String, fileId: String) async -> (NKError) {
+
+        var key: NSString?, initializationVector: NSString?, authenticationTag: NSString?
+        let objectE2eEncryption = tableE2eEncryption()
+        let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!
+        var method = "POST"
+
+        if NCEndToEndEncryption.sharedManager()?.encryptFileName(metadata.fileNameView, fileNameIdentifier: metadata.fileName, directory: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId), key: &key, initializationVector: &initializationVector, authenticationTag: &authenticationTag) == false {
+            return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_e2e_error_create_encrypted_")
+        }
+
+        // Get last metadata
+        let getE2EEMetadataResults = await NextcloudKit.shared.getE2EEMetadata(fileId: fileId, e2eToken: e2eToken)
+        if getE2EEMetadataResults.error == .success, let e2eMetadata = getE2EEMetadataResults.e2eMetadata {
+            if !NCEndToEndMetadata.shared.decoderMetadata(e2eMetadata, privateKey: CCUtility.getEndToEndPrivateKey(metadata.account), serverUrl: metadata.serverUrl, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId) {
+                return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: NSLocalizedString("_e2e_error_encode_metadata_", comment: ""))
+            }
+            method = "PUT"
+        }
+
+        // [REPLACE]
+        NCManageDatabase.shared.deleteE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, metadata.fileNameView))
+
+        // Add new metadata
+        if let result = NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)) {
+            objectE2eEncryption.metadataKey = result.metadataKey
+            objectE2eEncryption.metadataKeyIndex = result.metadataKeyIndex
+        } else {
+            let key = NCEndToEndEncryption.sharedManager()?.generateKey(16) as NSData?
+            objectE2eEncryption.metadataKey = key!.base64EncodedString()
+            objectE2eEncryption.metadataKeyIndex = 0
+        }
+        objectE2eEncryption.account = metadata.account
+        objectE2eEncryption.authenticationTag = authenticationTag as String?
+        objectE2eEncryption.fileName = metadata.fileNameView
+        objectE2eEncryption.fileNameIdentifier = metadata.fileName
+        objectE2eEncryption.fileNamePath = fileNameLocalPath
+        objectE2eEncryption.key = key! as String
+        objectE2eEncryption.initializationVector = initializationVector! as String
+        objectE2eEncryption.mimeType = metadata.contentType
+        objectE2eEncryption.serverUrl = metadata.serverUrl
+        objectE2eEncryption.version = 1
+        NCManageDatabase.shared.addE2eEncryption(objectE2eEncryption)
+
+        // Rebuild metadata
+        guard let tableE2eEncryption = NCManageDatabase.shared.getE2eEncryptions(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, metadata.serverUrl)), let e2eMetadataNew = NCEndToEndMetadata.shared.encoderMetadata(tableE2eEncryption, privateKey: CCUtility.getEndToEndPrivateKey(metadata.account), serverUrl: metadata.serverUrl) else {
+            return NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: NSLocalizedString("_e2e_error_encode_metadata_", comment: ""))
+        }
+
+        // send metadata
+        let putE2EEMetadataResults = await NextcloudKit.shared.putE2EEMetadata(fileId: fileId, e2eToken: e2eToken, e2eMetadata: e2eMetadataNew, method: method)
+        
+        return putE2EEMetadataResults.error
+    }
+
+    private func sendFile(metadata: tableMetadata, e2eToken: String) async -> (ocId: String?, etag: String?, date: NSDate? ,afError: AFError?, error: NKError) {
+
+        let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!
+
+        return await withCheckedContinuation({ continuation in
+            NCNetworking.shared.uploadFile(metadata: metadata, fileNameLocalPath:fileNameLocalPath, withUploadComplete: false, addCustomHeaders: ["e2e-token": e2eToken]) {
+                NCContentPresenter.shared.noteTop(text: NSLocalizedString("_upload_e2ee_", comment: ""), image: nil, type: NCContentPresenter.messageType.info, delay: NCGlobal.shared.dismissAfterSecond, priority: .max)
+            } completion: { account, ocId, etag, date, size, allHeaderFields, afError, error in
+                continuation.resume(returning: (ocId: ocId, etag: etag, date: date ,afError: afError, error: error))
+            }
+        })
+    }
+}

+ 62 - 42
iOSClient/Networking/NCNetworking.swift

@@ -391,7 +391,9 @@ import Photos
 
         if metadata.e2eEncrypted {
             #if !EXTENSION_FILE_PROVIDER_EXTENSION && !EXTENSION_WIDGET
-            NCNetworkingE2EE.shared.upload(metadata: metadata, start: start) { error in
+            Task {
+                start()
+                let error = await NCNetworkingE2EEUpload.shared.upload(metadata: metadata)
                 completion(error)
             }
             #endif
@@ -400,7 +402,8 @@ import Photos
                 completion(error)
             }
         } else if metadata.session == NKCommon.shared.sessionIdentifierUpload {
-            uploadFile(metadata: metadata, start: start) { error in
+            let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
+            uploadFile(metadata: metadata, fileNameLocalPath:fileNameLocalPath, start: start) { account, ocId, etag, date, size, allHeaderFields, afError, error in
                 completion(error)
             }
         } else {
@@ -410,14 +413,13 @@ import Photos
         }
     }
 
-    private func uploadFile(metadata: tableMetadata, start: @escaping () -> Void, completion: @escaping (_ error: NKError) -> Void) {
+    func uploadFile(metadata: tableMetadata, fileNameLocalPath: String, withUploadComplete: Bool = true ,addCustomHeaders: [String: String]? = nil, start: @escaping () -> Void, completion: @escaping (_ account: String, _ ocId: String?, _ etag: String?, _ date: NSDate?, _ size: Int64, _ allHeaderFields: [AnyHashable : Any]?, _ afError: AFError?, _ error: NKError) -> Void) {
 
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
-        let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
         var uploadTask: URLSessionTask?
         let description = metadata.ocId
 
-        NextcloudKit.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: metadata.creationDate as Date, dateModificationFile: metadata.date as Date, customUserAgent: nil, addCustomHeaders: nil, requestHandler: { request in
+        NextcloudKit.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: metadata.creationDate as Date, dateModificationFile: metadata.date as Date, customUserAgent: nil, addCustomHeaders: addCustomHeaders, requestHandler: { request in
 
             self.uploadRequest[fileNameLocalPath] = request
 
@@ -442,13 +444,13 @@ import Photos
                     "totalBytes": NSNumber(value: progress.totalUnitCount),
                     "totalBytesExpected": NSNumber(value: progress.completedUnitCount)])
 
-        }) { _, ocId, etag, date, size, _, _, error in
+        }) { account, ocId, etag, date, size, allHeaderFields, afError, error in
 
             self.uploadRequest.removeValue(forKey: fileNameLocalPath)
-            if let uploadTask = uploadTask {
+            if withUploadComplete, let uploadTask = uploadTask {
                 self.uploadComplete(fileName: metadata.fileName, serverUrl: metadata.serverUrl, ocId: ocId, etag: etag, date: date, size: size, description: description, task: uploadTask, error: error)
             }
-            completion(error)
+            completion(account, ocId, etag, date, size, allHeaderFields, afError, error)
         }
     }
 
@@ -762,7 +764,7 @@ import Photos
                 return
             }
 
-            let isEncrypted = CCUtility.isFolderEncrypted(file.serverUrl, e2eEncrypted: file.e2eEncrypted, account: account, urlBase: file.urlBase, userId: file.userId)
+            let isEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: file.serverUrl, e2eEncrypted: file.e2eEncrypted, account: account, urlBase: file.urlBase, userId: file.userId)
             let metadata = NCManageDatabase.shared.convertNCFileToMetadata(file, isEncrypted: isEncrypted, account: account)
 
             completion(account, metadata, error)
@@ -949,12 +951,15 @@ import Photos
 
     @objc func createFolder(fileName: String, serverUrl: String, account: String, urlBase: String, userId: String, overwrite: Bool = false, completion: @escaping (_ error: NKError) -> Void) {
 
-        let isDirectoryEncrypted = CCUtility.isFolderEncrypted(serverUrl, e2eEncrypted: false, account: account, urlBase: urlBase, userId: userId)
+        let isDirectoryEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId)
         let fileName = fileName.trimmingCharacters(in: .whitespacesAndNewlines)
         
         if isDirectoryEncrypted {
             #if !EXTENSION
-            NCNetworkingE2EE.shared.createFolder(fileName: fileName, serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId, completion: completion)
+            Task {
+                let error = await NCNetworkingE2EECreateFolder.shared.createFolder(fileName: fileName, serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId)
+                completion(error)
+            }
             #endif
         } else {
             createFolderPlain(fileName: fileName, serverUrl: serverUrl, account: account, urlBase: urlBase, overwrite: overwrite, completion: completion)
@@ -1016,10 +1021,27 @@ import Photos
             return result
         }
 
+        func createNameSubFolder() -> [String] {
+
+            var datesSubFolder: [String] = []
+            let dateFormatter = DateFormatter()
+
+            for asset in assets {
+                let date = asset.creationDate ?? Date()
+                dateFormatter.dateFormat = "yyyy"
+                let year = dateFormatter.string(from: date)
+                dateFormatter.dateFormat = "MM"
+                let month = dateFormatter.string(from: date)
+                datesSubFolder.append("\(year)/\(month)")
+            }
+
+            return Array(Set(datesSubFolder))
+        }
+
         var result = createFolder(fileName: fileNameBase, serverUrl: serverUrlBase)
 
         if useSubFolder && result {
-            for dateSubFolder in createNameSubFolder(assets: assets) {
+            for dateSubFolder in createNameSubFolder() {
                 let yearMonth = dateSubFolder.split(separator: "/")
                 guard let year = yearMonth.first else { break }
                 guard let month = yearMonth.last else { break }
@@ -1036,23 +1058,6 @@ import Photos
         return result
     }
 
-    func createNameSubFolder(assets: [PHAsset]) -> [String] {
-
-        var datesSubFolder: [String] = []
-        let dateFormatter = DateFormatter()
-
-        for asset in assets {
-            let date = asset.creationDate ?? Date()
-            dateFormatter.dateFormat = "yyyy"
-            let year = dateFormatter.string(from: date)
-            dateFormatter.dateFormat = "MM"
-            let month = dateFormatter.string(from: date)
-            datesSubFolder.append("\(year)/\(month)")
-        }
-
-        return Array(Set(datesSubFolder))
-    }
-
     // MARK: - WebDav Delete
 
     @objc func deleteMetadata(_ metadata: tableMetadata, onlyLocalCache: Bool, completion: @escaping (_ error: NKError) -> Void) {
@@ -1082,20 +1087,23 @@ import Photos
             return completion(NKError())
         }
 
-        let isDirectoryEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+        let isDirectoryEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
         let metadataLive = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata)
 
         if isDirectoryEncrypted {
             #if !EXTENSION
-            if metadataLive == nil {
-                NCNetworkingE2EE.shared.deleteMetadata(metadata, completion: completion)
-            } else {
-                NCNetworkingE2EE.shared.deleteMetadata(metadataLive!) { error in
+            Task {
+                if let metadataLive = metadataLive {
+                    let error = await NCNetworkingE2EEDelete.shared.delete(metadata: metadataLive)
                     if error == .success {
-                        NCNetworkingE2EE.shared.deleteMetadata(metadata, completion: completion)
+                        let error = await NCNetworkingE2EEDelete.shared.delete(metadata: metadata)
+                        completion(error)
                     } else {
                         completion(error)
                     }
+                } else {
+                    let error = await NCNetworkingE2EEDelete.shared.delete(metadata: metadata)
+                    completion(error)
                 }
             }
             #endif
@@ -1148,6 +1156,15 @@ import Photos
         }
     }
 
+    func deleteMetadataPlain(_ metadata: tableMetadata, customHeader: [String: String]?) async -> (NKError) {
+
+        await withUnsafeContinuation({ continuation in
+            self.deleteMetadataPlain(metadata, customHeader: customHeader) { error in
+                continuation.resume(returning: error)
+            }
+        })
+    }
+
     // MARK: - WebDav Favorite
 
     @objc func favoriteMetadata(_ metadata: tableMetadata, completion: @escaping (_ error: NKError) -> Void) {
@@ -1231,22 +1248,25 @@ import Photos
 
     @objc func renameMetadata(_ metadata: tableMetadata, fileNameNew: String, viewController: UIViewController?, completion: @escaping (_ error: NKError) -> Void) {
 
-        let isDirectoryEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+        let isDirectoryEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
         let metadataLive = NCManageDatabase.shared.getMetadataLivePhoto(metadata: metadata)
         let fileNameNew = fileNameNew.trimmingCharacters(in: .whitespacesAndNewlines)
         let fileNameNewLive = (fileNameNew as NSString).deletingPathExtension + ".mov"
 
         if isDirectoryEncrypted {
             #if !EXTENSION
-            if metadataLive == nil {
-                NCNetworkingE2EE.shared.renameMetadata(metadata, fileNameNew: fileNameNew, completion: completion)
-            } else {
-                NCNetworkingE2EE.shared.renameMetadata(metadataLive!, fileNameNew: fileNameNewLive) { error in
+            Task {
+                if let metadataLive = metadataLive {
+                    let error = await NCNetworkingE2EERename.shared.rename(metadata: metadataLive, fileNameNew: fileNameNew)
                     if error == .success {
-                        NCNetworkingE2EE.shared.renameMetadata(metadata, fileNameNew: fileNameNew, completion: completion)
+                        let error = await NCNetworkingE2EERename.shared.rename(metadata: metadata, fileNameNew: fileNameNew)
+                        DispatchQueue.main.async { completion(error) }
                     } else {
-                        completion(error)
+                        DispatchQueue.main.async { completion(error) }
                     }
+                } else {
+                    let error = await NCNetworkingE2EERename.shared.rename(metadata: metadata, fileNameNew: fileNameNew)
+                    DispatchQueue.main.async { completion(error) }
                 }
             }
             #endif

+ 0 - 437
iOSClient/Networking/NCNetworkingE2EE.swift

@@ -1,437 +0,0 @@
-//
-//  NCNetworkingE2EE.swift
-//  Nextcloud
-//
-//  Created by Marino Faggiana on 05/05/2020.
-//  Copyright © 2020 Marino Faggiana. All rights reserved.
-//
-//  This program is free software: you can redistribute it and/or modify
-//  it under the terms of the GNU General Public License as published by
-//  the Free Software Foundation, either version 3 of the License, or
-//  (at your option) any later version.
-//
-//  This program is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-//  GNU General Public License for more details.
-//
-//  You should have received a copy of the GNU General Public License
-//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
-//
-
-import UIKit
-import OpenSSL
-import NextcloudKit
-import CFNetwork
-import Alamofire
-
-@objc class NCNetworkingE2EE: NSObject {
-    @objc public static let shared: NCNetworkingE2EE = {
-        let instance = NCNetworkingE2EE()
-        return instance
-    }()
-
-    // MARK: - WebDav Create Folder
-
-    func createFolder(fileName: String, serverUrl: String, account: String, urlBase: String, userId: String, completion: @escaping (_ error: NKError) -> Void) {
-
-        var fileNameFolder = CCUtility.removeForbiddenCharactersServer(fileName)!
-        var fileNameFolderUrl = ""
-        var fileNameIdentifier = ""
-        var key: NSString?
-        var initializationVector: NSString?
-
-        fileNameFolder = NCUtilityFileSystem.shared.createFileName(fileNameFolder, serverUrl: serverUrl, account: account)
-        if fileNameFolder.count == 0 {
-            return completion(NKError())
-        }
-        fileNameIdentifier = CCUtility.generateRandomIdentifier()
-        fileNameFolderUrl = serverUrl + "/" + fileNameIdentifier
-
-        self.lock(account: account, serverUrl: serverUrl) { directory, e2eToken, error in
-            if error == .success && e2eToken != nil && directory != nil {
-
-                let options = NKRequestOptions(customHeader: ["e2e-token": e2eToken!])
-                
-                NextcloudKit.shared.createFolder(serverUrlFileName: fileNameFolderUrl, options: options) { account, ocId, _, error in
-                    if error == .success {
-                        guard let fileId = NCUtility.shared.ocIdToFileId(ocId: ocId) else {
-                            // unlock
-                            if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: serverUrl) {
-                                NextcloudKit.shared.lockE2EEFolder(fileId: tableLock.fileId, e2eToken: tableLock.e2eToken, method: "DELETE") { _, _, _, _ in }
-                            }
-                            return completion(NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "Error convert ocId"))
-                        }
-                        NextcloudKit.shared.markE2EEFolder(fileId: fileId, delete: false) { account, error in
-                            if error == .success {
-
-                                let object = tableE2eEncryption()
-
-                                NCEndToEndEncryption.sharedManager()?.encryptkey(&key, initializationVector: &initializationVector)
-
-                                object.account = account
-                                object.authenticationTag = nil
-                                object.fileName = fileNameFolder
-                                object.fileNameIdentifier = fileNameIdentifier
-                                object.fileNamePath = ""
-                                object.key = key! as String
-                                object.initializationVector = initializationVector! as String
-
-                                if let result = NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl)) {
-                                    object.metadataKey = result.metadataKey
-                                    object.metadataKeyIndex = result.metadataKeyIndex
-                                } else {
-                                    object.metadataKey = (NCEndToEndEncryption.sharedManager()?.generateKey(16)?.base64EncodedString(options: []))! as String // AES_KEY_128_LENGTH
-                                    object.metadataKeyIndex = 0
-                                }
-                                object.mimeType = "httpd/unix-directory"
-                                object.serverUrl = serverUrl
-                                object.version = 1
-
-                                NCManageDatabase.shared.addE2eEncryption(object)
-
-                                self.sendE2EMetadata(account: account, serverUrl: serverUrl, fileNameRename: nil, fileNameNewRename: nil, deleteE2eEncryption: nil, urlBase: urlBase, userId: userId) { e2eToken, error in
-                                    // unlock
-                                    if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: serverUrl) {
-                                        NextcloudKit.shared.lockE2EEFolder(fileId: tableLock.fileId, e2eToken: tableLock.e2eToken, method: "DELETE") { _, _, _, _ in }
-                                    }
-                                    if error == .success, let ocId = ocId {
-                                        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterCreateFolder, userInfo: ["ocId": ocId, "serverUrl": serverUrl, "account": account, "e2ee": true])
-                                    }
-                                    completion(error)
-                                }
-
-                            } else {
-                                // unlock
-                                if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: serverUrl) {
-                                    NextcloudKit.shared.lockE2EEFolder(fileId: tableLock.fileId, e2eToken: tableLock.e2eToken, method: "DELETE") { _, _, _, _ in }
-                                }
-                                completion(error)
-                            }
-                        }
-
-                    } else {
-                        // unlock
-                        if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: serverUrl) {
-                            NextcloudKit.shared.lockE2EEFolder(fileId: tableLock.fileId, e2eToken: tableLock.e2eToken, method: "DELETE") { _, _, _, _ in }
-                        }
-                        completion(error)
-                    }
-                }
-            } else {
-                completion(error)
-            }
-        }
-    }
-
-    // MARK: - WebDav Delete
-
-    func deleteMetadata(_ metadata: tableMetadata, completion: @escaping (_ error: NKError) -> Void) {
-
-        self.lock(account: metadata.account, serverUrl: metadata.serverUrl) { directory, e2eToken, error in
-            if error == .success && e2eToken != nil && directory != nil {
-                
-                let deleteE2eEncryption = NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileNameIdentifier == %@", metadata.account, metadata.serverUrl, metadata.fileName)
-                NCNetworking.shared.deleteMetadataPlain(metadata, customHeader: ["e2e-token": e2eToken!]) { error in
-
-                    let home = NCUtilityFileSystem.shared.getHomeServer(urlBase: metadata.urlBase, userId: metadata.userId)
-                    if metadata.serverUrl != home {
-                        self.sendE2EMetadata(account: metadata.account, serverUrl: metadata.serverUrl, fileNameRename: nil, fileNameNewRename: nil, deleteE2eEncryption: deleteE2eEncryption, urlBase: metadata.urlBase, userId: metadata.userId) { e2eToken, error in
-                            // unlock
-                            if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: metadata.account, serverUrl: metadata.serverUrl) {
-                                NextcloudKit.shared.lockE2EEFolder(fileId: tableLock.fileId, e2eToken: tableLock.e2eToken, method: "DELETE") { _, _, _, _ in }
-                            }
-                            completion(error)
-                        }
-                    } else {
-                        // unlock
-                        if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: metadata.account, serverUrl: metadata.serverUrl) {
-                            NextcloudKit.shared.lockE2EEFolder(fileId: tableLock.fileId, e2eToken: tableLock.e2eToken, method: "DELETE") { _, _, _, _ in }
-                        }
-                        completion(error)
-                    }
-                }
-            } else {
-                completion(error)
-            }
-        }
-    }
-
-    // MARK: - WebDav Rename
-
-    func renameMetadata(_ metadata: tableMetadata, fileNameNew: String, completion: @escaping (_ error: NKError) -> Void) {
-
-        // verify if exists the new fileName
-        if NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@ AND fileName == %@", metadata.account, metadata.serverUrl, fileNameNew)) != nil {
-
-            return completion(NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_file_already_exists_"))
-
-        } else {
-
-            self.sendE2EMetadata(account: metadata.account, serverUrl: metadata.serverUrl, fileNameRename: metadata.fileName, fileNameNewRename: fileNameNew, deleteE2eEncryption: nil, urlBase: metadata.urlBase, userId: metadata.userId) { e2eToken, error in
-
-                if error == .success {
-                    NCManageDatabase.shared.setMetadataFileNameView(serverUrl: metadata.serverUrl, fileName: metadata.fileName, newFileNameView: fileNameNew, account: metadata.account)
-
-                    // Move file system
-                    let atPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId) + "/" + metadata.fileNameView
-                    let toPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId) + "/" + fileNameNew
-                    do {
-                        try FileManager.default.moveItem(atPath: atPath, toPath: toPath)
-                    } catch { }
-
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterRenameFile, userInfo: ["ocId": metadata.ocId, "account": metadata.account])
-                }
-
-                // unlock
-                if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: metadata.account, serverUrl: metadata.serverUrl) {
-                    NextcloudKit.shared.lockE2EEFolder(fileId: tableLock.fileId, e2eToken: tableLock.e2eToken, method: "DELETE") { _, _, _, _ in }
-                }
-
-                completion(error)
-            }
-        }
-    }
-
-    // MARK: - Upload
-
-    func upload(metadata: tableMetadata, start: @escaping () -> Void, completion: @escaping (_ error: NKError) -> Void) {
-
-        let objectE2eEncryption = tableE2eEncryption()
-        var key: NSString?, initializationVector: NSString?, authenticationTag: NSString?
-        let ocIdTemp = metadata.ocId
-        let serverUrl = metadata.serverUrl
-
-        // Verify max size
-        if metadata.size > NCGlobal.shared.e2eeMaxFileSize {
-            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp))
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "E2E Error file too big")])
-            start()
-            return completion(NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "E2E Error file too big"))
-        }
-
-        // Update metadata
-        var metadata = tableMetadata.init(value: metadata)
-        metadata.fileName = CCUtility.generateRandomIdentifier()!
-        metadata.e2eEncrypted = true
-        metadata.session = NKCommon.shared.sessionIdentifierUpload
-        metadata.sessionError = ""
-        NCManageDatabase.shared.addMetadata(metadata)
-
-        let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!
-        let fileNameLocalPathRequest = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
-        let serverUrlFileName = serverUrl + "/" + metadata.fileName
-
-        if NCEndToEndEncryption.sharedManager()?.encryptFileName(metadata.fileNameView, fileNameIdentifier: metadata.fileName, directory: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId), key: &key, initializationVector: &initializationVector, authenticationTag: &authenticationTag) == false {
-            NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp))
-            NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_e2e_error_create_encrypted_")])
-            start()
-            return completion(NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_e2e_error_create_encrypted_"))
-        }
-
-        if let result = NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", metadata.account, serverUrl)) {
-            objectE2eEncryption.metadataKey = result.metadataKey
-            objectE2eEncryption.metadataKeyIndex = result.metadataKeyIndex
-        } else {
-            let key = NCEndToEndEncryption.sharedManager()?.generateKey(16) as NSData?
-            objectE2eEncryption.metadataKey = key!.base64EncodedString()
-            objectE2eEncryption.metadataKeyIndex = 0
-        }
-
-        objectE2eEncryption.account = metadata.account
-        objectE2eEncryption.authenticationTag = authenticationTag as String?
-        objectE2eEncryption.fileName = metadata.fileNameView
-        objectE2eEncryption.fileNameIdentifier = metadata.fileName
-        objectE2eEncryption.fileNamePath = fileNameLocalPath
-        objectE2eEncryption.key = key! as String
-        objectE2eEncryption.initializationVector = initializationVector! as String
-        objectE2eEncryption.mimeType = metadata.contentType
-        objectE2eEncryption.serverUrl = serverUrl
-        objectE2eEncryption.version = 1
-
-        NCManageDatabase.shared.addE2eEncryption(objectE2eEncryption)
-
-        if let getMetadata = NCManageDatabase.shared.getMetadataFromOcId(ocIdTemp) {
-            metadata = getMetadata
-        } else {
-            start()
-            return completion(NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_e2e_error_create_encrypted_"))
-        }
-
-        NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadDataSource, userInfo: ["serverUrl": metadata.serverUrl])
-        NCContentPresenter.shared.noteTop(text: NSLocalizedString("_upload_e2ee_", comment: ""), image: nil, type: NCContentPresenter.messageType.info, delay: NCGlobal.shared.dismissAfterSecond, priority: .max)
-        NCNetworkingE2EE.shared.sendE2EMetadata(account: metadata.account, serverUrl: serverUrl, fileNameRename: nil, fileNameNewRename: nil, deleteE2eEncryption: nil, urlBase: metadata.urlBase, userId: metadata.userId, upload: true) { e2eToken, error in
-
-            start()
-
-            if error == .success && e2eToken != nil {
-
-                NextcloudKit.shared.upload(serverUrlFileName: serverUrlFileName, fileNameLocalPath: fileNameLocalPath, dateCreationFile: metadata.date as Date, dateModificationFile: metadata.date as Date, addCustomHeaders: ["e2e-token": e2eToken!], requestHandler: { request in
-
-                    NCNetworking.shared.uploadRequest[fileNameLocalPathRequest] = request
-                    NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: nil, sessionSelector: nil, sessionTaskIdentifier: nil, status: NCGlobal.shared.metadataStatusUploading)
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadStartFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "sessionSelector": metadata.sessionSelector])
-
-                }, taskHandler: { _ in
-
-                }, progressHandler: { progress in
-
-                    NotificationCenter.default.postOnMainThread(
-                        name: NCGlobal.shared.notificationCenterProgressTask,
-                        userInfo: [
-                            "account": metadata.account,
-                            "ocId": metadata.ocId,
-                            "fileName": metadata.fileName,
-                            "serverUrl": serverUrl,
-                            "status": NSNumber(value: NCGlobal.shared.metadataStatusInUpload),
-                            "progress": NSNumber(value: progress.fractionCompleted),
-                            "totalBytes": NSNumber(value: progress.totalUnitCount),
-                            "totalBytesExpected": NSNumber(value: progress.completedUnitCount)])
-
-                }) { account, ocId, etag, date, _, _, afError, error in
-
-                    NCNetworkingE2EE.shared.unlock(account: metadata.account, serverUrl: serverUrl) { _, _, errorLock in
-
-                        NCNetworking.shared.uploadRequest.removeValue(forKey: fileNameLocalPath)
-                        if let metadata = NCManageDatabase.shared.getMetadataFromOcId(metadata.ocId) {
-                            if afError?.isExplicitlyCancelledError ?? false {
-
-                                CCUtility.removeFile(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId))
-                                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", metadata.ocId))
-                                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": error])
-
-                            } else if error == .success && ocId != nil {
-
-                                NCUtilityFileSystem.shared.moveFileInBackground(atPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId), toPath: CCUtility.getDirectoryProviderStorageOcId(ocId))
-
-                                metadata.date = date ?? NSDate()
-                                metadata.etag = etag ?? ""
-                                metadata.ocId = ocId!
-
-                                metadata.session = ""
-                                metadata.sessionError = ""
-                                metadata.sessionTaskIdentifier = 0
-                                metadata.status = NCGlobal.shared.metadataStatusNormal
-
-                                NCManageDatabase.shared.addMetadata(metadata)
-                                NCManageDatabase.shared.deleteMetadata(predicate: NSPredicate(format: "ocId == %@", ocIdTemp))
-                                NCManageDatabase.shared.addLocalFile(metadata: metadata)
-
-                                NCUtility.shared.createImageFrom(fileNameView: metadata.fileNameView, ocId: metadata.ocId, etag: metadata.etag, classFile: metadata.classFile)
-                                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": error])
-
-                            } else {
-
-                                NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: error.errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError)
-
-                                NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": error])
-                            }
-                        }
-                        completion(error)
-                    }
-                }
-
-            } else {
-
-                if let metadata = NCManageDatabase.shared.getMetadataFromOcId(ocIdTemp) {
-                    NCManageDatabase.shared.setMetadataSession(ocId: metadata.ocId, session: nil, sessionError: error.errorDescription, sessionTaskIdentifier: 0, status: NCGlobal.shared.metadataStatusUploadError)
-                    NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterUploadedFile, userInfo: ["ocId": metadata.ocId, "serverUrl": metadata.serverUrl, "account": metadata.account, "fileName": metadata.fileName, "ocIdTemp": ocIdTemp, "error": error])
-                }
-                completion(error)
-            }
-        }
-    }
-
-    // MARK: - E2EE
-
-    @objc func lock(account: String, serverUrl: String, completion: @escaping (_ direcrtory: tableDirectory?, _ e2eToken: String?, _ error: NKError) -> Void) {
-
-        var e2eToken: String?
-
-        guard let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl)) else {
-            return completion(nil, nil, NKError())
-        }
-
-        if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: serverUrl) {
-            e2eToken = tableLock.e2eToken
-        }
-
-        NextcloudKit.shared.lockE2EEFolder(fileId: directory.fileId, e2eToken: e2eToken, method: "POST") { account, e2eToken, data, error in
-            if error == .success && e2eToken != nil {
-                NCManageDatabase.shared.setE2ETokenLock(account: account, serverUrl: serverUrl, fileId: directory.fileId, e2eToken: e2eToken!)
-            }
-            completion(directory, e2eToken, error)
-        }
-    }
-
-    @objc func unlock(account: String, serverUrl: String, completion: @escaping (_ direcrtory: tableDirectory?, _ e2eToken: String?, _ error: NKError) -> Void) {
-
-        var e2eToken: String?
-
-        guard let directory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl)) else {
-            return completion(nil, nil, NKError())
-        }
-
-        if let tableLock = NCManageDatabase.shared.getE2ETokenLock(account: account, serverUrl: serverUrl) {
-            e2eToken = tableLock.e2eToken
-        }
-
-        NextcloudKit.shared.lockE2EEFolder(fileId: directory.fileId, e2eToken: e2eToken, method: "DELETE") { account, e2eToken, data, error in
-            if error == .success {
-                NCManageDatabase.shared.deteleE2ETokenLock(account: account, serverUrl: serverUrl)
-            }
-            completion(directory, e2eToken, error)
-        }
-    }
-
-    @objc func sendE2EMetadata(account: String, serverUrl: String, fileNameRename: String?, fileNameNewRename: String?, deleteE2eEncryption: NSPredicate?, urlBase: String, userId: String, upload: Bool = false, completion: @escaping (_ e2eToken: String?, _ error: NKError) -> Void) {
-
-        self.lock(account: account, serverUrl: serverUrl) { directory, e2eToken, error in
-            if error == .success && e2eToken != nil && directory != nil {
-
-                NextcloudKit.shared.getE2EEMetadata(fileId: directory!.fileId, e2eToken: e2eToken) { account, e2eMetadata, data, error in
-                    var method = "POST"
-                    var e2eMetadataNew: String?
-
-                    if error == .success && e2eMetadata != nil {
-                        if !NCEndToEndMetadata.shared.decoderMetadata(e2eMetadata!, privateKey: CCUtility.getEndToEndPrivateKey(account), serverUrl: serverUrl, account: account, urlBase: urlBase, userId: userId) {
-                            return completion(e2eToken, NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: NSLocalizedString("_e2e_error_encode_metadata_", comment: "")))
-                        }
-                        method = "PUT"
-                    }
-
-                    // Rename
-                    if fileNameRename != nil && fileNameNewRename != nil {
-                        NCManageDatabase.shared.renameFileE2eEncryption(serverUrl: serverUrl, fileNameIdentifier: fileNameRename!, newFileName: fileNameNewRename!, newFileNamePath: CCUtility.returnFileNamePath(fromFileName: fileNameNewRename!, serverUrl: serverUrl, urlBase: urlBase, userId: userId, account: account))
-                    }
-
-                    // Delete
-                    if deleteE2eEncryption != nil {
-                        NCManageDatabase.shared.deleteE2eEncryption(predicate: deleteE2eEncryption!)
-                    }
-
-                    // Rebuild metadata for send it
-                    let tableE2eEncryption = NCManageDatabase.shared.getE2eEncryptions(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl))
-                    if tableE2eEncryption != nil {
-                        e2eMetadataNew = NCEndToEndMetadata.shared.encoderMetadata(tableE2eEncryption!, privateKey: CCUtility.getEndToEndPrivateKey(account), serverUrl: serverUrl)
-                    } else {
-                        method = "DELETE"
-                    }
-
-                    NextcloudKit.shared.putE2EEMetadata(fileId: directory!.fileId, e2eToken: e2eToken!, e2eMetadata: e2eMetadataNew, method: method) { account, _, _, error in
-
-                        if upload {
-                            completion(e2eToken, error)
-                        } else {
-                            self.unlock(account: account, serverUrl: serverUrl) { _, e2eToken, _ in
-                                completion(e2eToken, error)
-                            }
-                        }
-                    }
-                }
-            } else {
-                completion(e2eToken, error)
-            }
-        }
-    }
-}

+ 1 - 1
iOSClient/Networking/NCOperationQueue.swift

@@ -82,7 +82,7 @@ import NextcloudKit
 
     func delete(metadata: tableMetadata, onlyLocalCache: Bool) {
 
-        let isFolderEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+        let isFolderEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
         if isFolderEncrypted {
             for case let operation as NCOperationDelete in deleteQueueE2EE.operations where operation.metadata.ocId == metadata.ocId {
                 return

+ 1 - 0
iOSClient/Security/NCEndToEndMetadata.swift

@@ -230,6 +230,7 @@ class NCEndToEndMetadata: NSObject {
 
                 } catch let error {
                     print("Serious internal error in decoding metadata ("+error.localizedDescription+")")
+                    return false
                 }
             }
 

+ 188 - 0
iOSClient/Settings/NCManageE2EE.swift

@@ -0,0 +1,188 @@
+//
+//  NCManageE2EE.swift
+//  Nextcloud
+//
+//  Created by Marino Faggiana on 17/11/22.
+//  Copyright © 2022 Marino Faggiana. All rights reserved.
+//
+//  Author Marino Faggiana <marino.faggiana@nextcloud.com>
+//
+//  This program is free software: you can redistribute it and/or modify
+//  it under the terms of the GNU General Public License as published by
+//  the Free Software Foundation, either version 3 of the License, or
+//  (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+//
+
+import SwiftUI
+import NextcloudKit
+import TOPasscodeViewController
+import LocalAuthentication
+
+
+@objc
+class NCManageE2EEInterface: NSObject, NCEndToEndInitializeDelegate, TOPasscodeViewControllerDelegate {
+
+    let endToEndInitialize = NCEndToEndInitialize()
+
+    private let appDelegate = UIApplication.shared.delegate as! AppDelegate
+    private var passcodeType = ""
+
+    override init() {
+        super.init()
+
+        endToEndInitialize.delegate = self
+    }
+
+    @objc func makeShipDetailsUI() -> UIViewController {
+        let details = NCManageE2EE()
+        return UIHostingController(rootView: details)
+    }
+
+    func endToEndInitializeSuccess() {
+
+    }
+
+    // MARK: - Passcode
+
+    @objc func requestPasscodeType(_ passcodeType: String) {
+
+        let laContext = LAContext()
+        var error: NSError?
+
+        let passcodeViewController = TOPasscodeViewController(passcodeType: .sixDigits, allowCancel: true)
+        passcodeViewController.delegate = self
+        passcodeViewController.keypadButtonShowLettering = false
+        if CCUtility.getEnableTouchFaceID() && laContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
+            if error == nil {
+                if laContext.biometryType == .faceID  {
+                    passcodeViewController.biometryType = .faceID
+                    passcodeViewController.allowBiometricValidation = true
+                } else if laContext.biometryType == .touchID  {
+                    passcodeViewController.biometryType = .touchID
+                }
+                passcodeViewController.allowBiometricValidation = true
+                passcodeViewController.automaticallyPromptForBiometricValidation = true
+            }
+        }
+
+        self.passcodeType = passcodeType
+        appDelegate.window?.rootViewController?.present(passcodeViewController, animated: true)
+    }
+
+    @objc func correctPasscode() {
+
+        if self.passcodeType == "removeLocallyEncryption" {
+            let alertController = UIAlertController(title: NSLocalizedString("_e2e_settings_remove_", comment: ""), message: NSLocalizedString("_e2e_settings_remove_message_", comment: ""), preferredStyle: .alert)
+            alertController.addAction(UIAlertAction(title: NSLocalizedString("_remove_", comment: ""), style: .default, handler: { action in
+                CCUtility.clearAllKeysEnd(toEnd: self.appDelegate.account)
+            }))
+            alertController.addAction(UIAlertAction(title: NSLocalizedString("_cancel_", comment: ""), style: .default, handler: { action in }))
+            appDelegate.window?.rootViewController?.present(alertController, animated: true)
+        }
+    }
+
+    func passcodeViewController(_ passcodeViewController: TOPasscodeViewController, isCorrectCode code: String) -> Bool {
+
+        if code == CCUtility.getPasscode() {
+            DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+                self.correctPasscode()
+            }
+            return true
+        } else {
+            return false
+        }
+    }
+
+    func didPerformBiometricValidationRequest(in passcodeViewController: TOPasscodeViewController) {
+
+        LAContext().evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: NCBrandOptions.shared.brand) { (success, error) in
+            if success {
+                DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
+                    passcodeViewController.dismiss(animated: true)
+                    self.correctPasscode()
+                }
+            }
+        }
+    }
+
+    func didTapCancel(in passcodeViewController: TOPasscodeViewController) {
+        passcodeViewController.dismiss(animated: true)
+    }
+}
+
+struct NCManageE2EE: View {
+    let manageE2EEInterface = NCManageE2EEInterface()
+
+    var body: some View {
+        VStack {
+            VStack {
+
+                Text("Hello, world! 1 come stai spero bene ma secondo te quanto è lunga questa cosa, Hello, world! 1 come stai spero bene ma secondo te quanto è lunga questa cosa, versione 2 perchè la versione 1 e poi altro testo ")
+                    .frame(height: 100)
+                    .padding()
+
+                Button(action: {}) {
+                    HStack{
+                        Image(systemName: "person.crop.circle.fill")
+                        Text("This is a button")
+                            .padding(.horizontal)
+                    }
+                    .padding()
+                }
+                .foregroundColor(Color.white)
+                .background(Color.blue)
+                .cornerRadius(.infinity)
+                .frame(height: 100)
+
+                Button(action: {
+                    manageE2EEInterface.endToEndInitialize.initEndToEndEncryption()
+                }, label: {
+                    Text("Start")
+                })
+                Button(action: {
+                    if CCUtility.getPasscode().isEmpty {
+                        NCContentPresenter.shared.showInfo(error: NKError(errorCode: 0, errorDescription: "_e2e_settings_lock_not_active_"))
+                    } else {
+                        manageE2EEInterface.requestPasscodeType("removeLocallyEncryption")
+                    }
+                }, label: {
+                    Text(NSLocalizedString("_e2e_settings_remove_", comment: ""))
+                })
+
+#if DEBUG
+                Button(action: {
+
+                }, label: {
+                    Text("Delete Certificate")
+                })
+                Button(action: {
+                    NextcloudKit.shared.deleteE2EEPrivateKey { account, error in
+
+                    }
+                }, label: {
+                    Text("Delete PrivateKey")
+                })
+#endif
+
+            }
+            .background(Color.green)
+            Spacer()
+        }
+        .background(Color.gray)
+        .navigationTitle("Cifratura End-To-End")
+    }
+}
+
+struct NCManageE2EE_Previews: PreviewProvider {
+    static var previews: some View {
+        NCManageE2EE()
+    }
+}

+ 7 - 0
iOSClient/Settings/NCManageEndToEndEncryption.m

@@ -188,6 +188,13 @@
     [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(initialize) name:NCGlobal.shared.notificationCenterInitialize object:nil];
 
     [self initializeForm];
+
+    /*
+    NKRequestOptions *options = [[NKRequestOptions alloc] initWithEndpoint:nil customHeader:nil customUserAgent:nil contentType:nil e2eToken: nil timeout:30 queue:dispatch_get_main_queue()];
+    [[NextcloudKit shared] getE2EECertificateWithOptions:options completionHandler:^(NSString* account, NSString *certificate, NSData *data, NKError *error) {
+
+    }];
+    */
 }
 
 - (void)viewWillAppear:(BOOL)animated

+ 11 - 2
iOSClient/Settings/NCSettings.m

@@ -115,10 +115,11 @@
         row = [XLFormRowDescriptor formRowDescriptorWithTag:@"e2eEncryption" rowType:XLFormRowDescriptorTypeButton title:title];
         row.cellConfigAtConfigure[@"backgroundColor"] = UIColor.secondarySystemGroupedBackgroundColor;
         [row.cellConfig setObject:[UIFont systemFontOfSize:15.0] forKey:@"textLabel.font"];
+        [row.cellConfig setObject:@(NSTextAlignmentLeft) forKey:@"textLabel.textAlignment"];
         [row.cellConfig setObject:UIColor.labelColor forKey:@"textLabel.textColor"];
         [row.cellConfig setObject:[[UIImage imageNamed:@"lock"] imageWithColor:NCBrandColor.shared.gray size:25] forKey:@"imageView.image"];
-        row.action.viewControllerClass = [NCManageEndToEndEncryption class];
-
+        row.action.formSelector = @selector(manageE2EE:);
+        //row.action.viewControllerClass = [NCManageEndToEndEncryption class];
         [section addFormRow:row];
     }
 
@@ -296,6 +297,14 @@
 
 #pragma mark -
 
+- (void)manageE2EE:(XLFormRowDescriptor *)sender
+{
+    [self deselectFormRow:sender];
+
+    UIViewController *vc = [[NCManageE2EEInterface alloc] makeShipDetailsUI];
+    [self.navigationController pushViewController:vc animated:YES];
+}
+
 - (void)privacy:(XLFormRowDescriptor *)sender
 {
     [self deselectFormRow:sender];

+ 5 - 0
iOSClient/Supporting Files/en.lproj/Localizable.strings

@@ -77,6 +77,7 @@
 "_transfers_in_queue_"      = "Transfers in progress, please wait …";
 "_too_errors_upload_"       = "Too many errors, please verify the problem";
 "_create_folder_"           = "Create folder";
+"_create_folder_e2ee_"      = "Create encrypted folder";
 "_create_folder_on_"        = "Create folder on";
 "_close_"                   = "Close";
 "_postpone_"                = "Postpone";
@@ -196,6 +197,7 @@
 "_privacy_legal_"           = "Privacy and Legal Policy";
 "_source_code_"             = "Get source code";
 "_account_select_"          = "Select the account";
+"_account_select_to_add_"   = "Select the account to add";
 "_host_insert_"             = "Insert the host name, for example:";
 "_certificate_not_found_"   = "File %@ in documents directory not found.";
 "_copy_failed_"             = "Copy failed";
@@ -874,6 +876,7 @@
 "_privacy_screen_"          = "Splash screen when app inactive";
 "_saving_"                  = "Saving …";
 "_video_not_streamed_"      = "The server does not allow video streaming, do you want to download it?";
+"_video_not_streamed_e2ee_" = "The server does not allow video streaming because is encrypted, do you want to download it?";
 "_scan_"                    = "Scan";
 "_in_"                      = "in";
 "_enter_passphrase_"        = "Enter passphrase (12 words)";
@@ -898,6 +901,8 @@
 "_no_items_"                = "No items";
 "_check_back_later_"        = "Check back later";
 "_exporting_video_"         = "Exporting video … Tap to cancel.";
+"_talk_detect_"             = "Detected Nextcloud Talk";
+"_talk_add_account_"        = "Nextcloud Talk has been detected, do you want to add an existing account ?";
 
 // Video
 "_select_trace_"            = "Select the trace";

+ 2 - 4
iOSClient/Trash/NCTrash.swift

@@ -276,9 +276,7 @@ extension NCTrash {
 
     @objc func loadListingTrash() {
 
-        let options = NKRequestOptions(queue: NKCommon.shared.backgroundQueue)
-
-        NextcloudKit.shared.listingTrash(showHiddenFiles: false, options: options) { account, items, _, error in
+        NextcloudKit.shared.listingTrash(showHiddenFiles: false) { account, items, _, error in
 
             DispatchQueue.main.async { self.refreshControl.endRefreshing() }
 
@@ -290,7 +288,7 @@ extension NCTrash {
             NCManageDatabase.shared.deleteTrash(filePath: trashPath, account: self.appDelegate.account)
             NCManageDatabase.shared.addTrash(account: account, items: items)
 
-            DispatchQueue.main.async { self.reloadDataSource() }
+            self.reloadDataSource()
         }
     }
 

+ 0 - 5
iOSClient/Utility/CCUtility.h

@@ -239,11 +239,6 @@
 
 + (NSString *)getMimeType:(NSString *)fileNameView;
 
-// ===== E2E Encrypted =====
-
-+ (NSString *)generateRandomIdentifier;
-+ (BOOL)isFolderEncrypted:(NSString *)serverUrl e2eEncrypted:(BOOL)e2eEncrypted account:(NSString *)account urlBase:(NSString *)urlBase userId:(NSString *)userId;
-
 // ===== Share Permissions =====
 
 + (NSInteger)getPermissionsValueByCanEdit:(BOOL)canEdit andCanCreate:(BOOL)canCreate andCanChange:(BOOL)canChange andCanDelete:(BOOL)canDelete andCanShare:(BOOL)canShare andIsFolder:(BOOL) isFolder;

+ 1 - 44
iOSClient/Utility/CCUtility.m

@@ -1125,7 +1125,7 @@
 {
     NSString *fileNameViewPath = [self getDirectoryProviderStorageOcId:metadata.ocId fileNameView:metadata.fileNameView];
     NSString *fileNamePath = [self getDirectoryProviderStorageOcId:metadata.ocId fileNameView:metadata.fileName];
-    BOOL isFolderEncrypted = [self isFolderEncrypted:metadata.serverUrl e2eEncrypted:metadata.e2eEncrypted account:metadata.account urlBase:metadata.urlBase userId:metadata.userId];
+    BOOL isFolderEncrypted = [[NCUtility shared] isFolderEncryptedWithServerUrl:metadata.serverUrl e2eEncrypted:metadata.e2eEncrypted account:metadata.account urlBase:metadata.urlBase userId:metadata.userId];
 
     unsigned long long fileNameViewSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:fileNameViewPath error:nil] fileSize];
     unsigned long long fileNameSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:fileNamePath error:nil] fileSize];
@@ -1301,49 +1301,6 @@
     return path;
 }
 
-#pragma --------------------------------------------------------------------------------------------
-#pragma mark ===== E2E Encrypted =====
-#pragma --------------------------------------------------------------------------------------------
-
-+ (NSString *)generateRandomIdentifier
-{
-    NSString *UUID = [[NSUUID UUID] UUIDString];
-    
-    return [[UUID stringByReplacingOccurrencesOfString:@"-" withString:@""] lowercaseString];
-}
-
-+ (BOOL)isFolderEncrypted:(NSString *)serverUrl e2eEncrypted:(BOOL)e2eEncrypted account:(NSString *)account urlBase:(NSString *)urlBase userId:(NSString *)userId
-{
-    NSString *home = [[NCUtilityFileSystem shared] getHomeServerWithUrlBase:urlBase userId:userId];
-        
-    if (e2eEncrypted) {
-    
-        return true;
-        
-    } else if ([serverUrl isEqualToString:home] || [serverUrl isEqualToString:@".."]) {
-        
-        return false;
-
-    } else {
-       
-        tableDirectory *directory = [[NCManageDatabase shared] getTableDirectoryWithPredicate:[NSPredicate predicateWithFormat:@"account == %@ AND serverUrl == %@", account, serverUrl]];
-        
-        while (directory != nil && ![directory.serverUrl isEqualToString:home]) {
-            if (directory.e2eEncrypted == true) {
-                return true;
-            }
-            NSString* home = [[NCUtilityFileSystem shared] getHomeServerWithUrlBase:urlBase userId:userId];
-            NSString* path = [[NCUtilityFileSystem shared] deleteLastPathWithServerUrlPath:serverUrl home:home];
-            if (path != nil) {
-                serverUrl = path;
-            }
-            directory = [[NCManageDatabase shared] getTableDirectoryWithPredicate:[NSPredicate predicateWithFormat:@"account == %@ AND serverUrl == %@", account, serverUrl]];
-        }
-        
-        return false;
-    }
-}
-
 #pragma --------------------------------------------------------------------------------------------
 #pragma mark ===== Share Permissions =====
 #pragma --------------------------------------------------------------------------------------------

+ 56 - 1
iOSClient/Utility/NCContentPresenter.swift

@@ -97,7 +97,7 @@ class NCContentPresenter: NSObject {
     func messageNotification(_ title: String, error: NKError, delay: TimeInterval, type: messageType, priority: EKAttributes.Precedence.Priority = .normal, dropEnqueuedEntries: Bool = false) {
 
         // No notification message for:
-        if error.errorCode == NSURLErrorCancelled || error.errorCode == NCGlobal.shared.errorRequestExplicityCancelled || error.errorCode == 423 { return }
+        if error.errorCode == NSURLErrorCancelled || error.errorCode == NCGlobal.shared.errorRequestExplicityCancelled { return }
         else if error == .success && type == messageType.error { return }
 
         DispatchQueue.main.async {
@@ -148,6 +148,61 @@ class NCContentPresenter: NSObject {
         DispatchQueue.main.async { SwiftEntryKit.display(entry: contentView, using: attributes) }
     }
 
+    // MARK: -
+
+    func alertAction(image: UIImage, backgroundColor: UIColor, textColor: UIColor, title: String, description: String, textCancelButton: String, textOkButton: String, attributes: EKAttributes, completion: @escaping (_ identifier: String) -> Void) {
+
+        var buttonAttributes: EKAttributes {
+            var attributes = attributes
+            attributes.hapticFeedbackType = .success
+            attributes.displayDuration = .infinity
+            attributes.entryBackground = .color(color: EKColor(backgroundColor))
+            attributes.shadow = .active(with: .init(color: .black, opacity: 0.3, radius: 8))
+            attributes.screenInteraction = .dismiss
+            attributes.entryInteraction = .absorbTouches
+            attributes.scroll = .enabled(swipeable: true, pullbackAnimation: .jolt)
+            attributes.roundCorners = .all(radius: 25)
+            attributes.entranceAnimation = .init(translate: .init(duration: 0.7, spring: .init(damping: 1, initialVelocity: 0)), scale: .init(from: 1.05, to: 1, duration: 0.4, spring: .init(damping: 1, initialVelocity: 0)))
+            attributes.exitAnimation = .init(translate: .init(duration: 0.2))
+            attributes.popBehavior = .animated(animation: .init(translate: .init(duration: 0.2)))
+            attributes.positionConstraints.verticalOffset = 20
+            attributes.positionConstraints.size = .init(width: .offset(value: 20), height: .intrinsic)
+            attributes.positionConstraints.maxSize = .init(width: .constant(value: min(UIScreen.main.bounds.width, UIScreen.main.bounds.height)), height: .intrinsic)
+            attributes.statusBar = .dark
+            return attributes
+        }
+
+        let simpleMessage = EKSimpleMessage(image: EKProperty.ImageContent(image: image, size: CGSize(width: 34, height: 34)),
+                                            title: EKProperty.LabelContent(text: NSLocalizedString(title, comment: ""), style: .init(font: MainFont.medium.with(size: 15), color: EKColor(textColor)), accessibilityIdentifier: "title"),
+                                            description: EKProperty.LabelContent(text: NSLocalizedString(description, comment: ""), style: .init( font: MainFont.light.with(size: 13), color: EKColor(textColor)), accessibilityIdentifier: "description"))
+
+        let cancelButton = EKProperty.ButtonContent(
+            label: EKProperty.LabelContent(text: NSLocalizedString(textCancelButton, comment: ""), style: EKProperty.LabelStyle(font: MainFont.medium.with(size: 16), color: EKColor(textColor))),
+            backgroundColor: .clear,
+            highlightedBackgroundColor: EKColor(UIColor.lightGray),
+            accessibilityIdentifier: "close") {
+                SwiftEntryKit.dismiss()
+                completion("close")
+            }
+
+        let okButton = EKProperty.ButtonContent(
+            label: EKProperty.LabelContent(text: NSLocalizedString(textOkButton, comment: ""),style: EKProperty.LabelStyle(font: MainFont.medium.with(size: 16), color: EKColor(textColor))),
+            backgroundColor: .clear,
+            highlightedBackgroundColor: EKColor(UIColor.lightGray),
+            displayMode: EKAttributes.DisplayMode.inferred,
+            accessibilityIdentifier: "ok") {
+                SwiftEntryKit.dismiss()
+                completion("ok")
+            }
+
+        let buttonsBarContent = EKProperty.ButtonBarContent(with: cancelButton, okButton, separatorColor: EKColor(textColor), buttonHeight: 60, expandAnimatedly: true)
+        let alertMessage = EKAlertMessage(simpleMessage: simpleMessage, imagePosition: .left, buttonBarContent: buttonsBarContent)
+        let contentView = EKAlertMessageView(with: alertMessage)
+
+        SwiftEntryKit.display(entry: contentView, using: buttonAttributes)
+    }
+
+
     // MARK: - Note Message
 
     func noteTop(text: String, image: UIImage?, color: UIColor? = nil, type: messageType? = nil, delay: TimeInterval, priority: EKAttributes.Precedence.Priority = .normal, dropEnqueuedEntries: Bool = false) {

+ 30 - 3
iOSClient/Utility/NCUtility.swift

@@ -401,7 +401,7 @@ class NCUtility: NSObject {
                 metadataSource.date = date
             }
             metadataSource.chunk = chunckSize != 0 && metadata.size > chunckSize
-            metadataSource.e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+            metadataSource.e2eEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
             metadataSource.isExtractFile = true
             if let metadata = NCManageDatabase.shared.addMetadata(metadataSource) {
                 metadatas.append(metadata)
@@ -445,7 +445,7 @@ class NCUtility: NSObject {
                 var metadataReturn = metadata
                 if modifyMetadataForUpload {
                     metadata.chunk = chunckSize != 0 && metadata.size > chunckSize
-                    metadata.e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+                    metadata.e2eEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
                     metadata.isExtractFile = true
                     if let metadata = NCManageDatabase.shared.addMetadata(metadata) {
                         metadataReturn = metadata
@@ -574,7 +574,7 @@ class NCUtility: NSObject {
         options.deliveryMode = PHImageRequestOptionsDeliveryMode.fastFormat
         options.isNetworkAccessAllowed = true
         let chunckSize = CCUtility.getChunkSize() * 1000000
-        let e2eEncrypted = CCUtility.isFolderEncrypted(metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+        let e2eEncrypted = NCUtility.shared.isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
         let ocId = NSUUID().uuidString
         let fileName = (metadata.fileName as NSString).deletingPathExtension + ".mov"
         let fileNamePath = CCUtility.getDirectoryProviderStorageOcId(ocId, fileNameView: fileName)!
@@ -1049,4 +1049,31 @@ class NCUtility: NSObject {
         }
         return returnImage
     }
+
+    func isFolderEncrypted(serverUrl: String, e2eEncrypted: Bool = false, userBase: NCUserBaseUrl) -> Bool {
+        return isFolderEncrypted(serverUrl: serverUrl, e2eEncrypted: e2eEncrypted, account: userBase.account, urlBase: userBase.urlBase, userId: userBase.userId)
+    }
+    func isFolderEncrypted(metadata: tableMetadata) -> Bool {
+        return isFolderEncrypted(serverUrl: metadata.serverUrl, e2eEncrypted: metadata.e2eEncrypted, account: metadata.account, urlBase: metadata.urlBase, userId: metadata.userId)
+    }
+    @objc func isFolderEncrypted(serverUrl: String, e2eEncrypted: Bool = false, account:String, urlBase: String, userId: String) -> Bool {
+        if e2eEncrypted { return true }
+
+        let home = NCUtilityFileSystem.shared.getHomeServer(urlBase: urlBase, userId: userId)
+        if serverUrl == home || serverUrl == ".." { return false }
+        var serverUrl = serverUrl
+
+        var tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl))
+        while let directory = tableDirectory, directory.serverUrl != home {
+            if directory.e2eEncrypted { return true }
+            if let path = NCUtilityFileSystem.shared.deleteLastPath(serverUrlPath: serverUrl, home: home) {
+                serverUrl = path
+            }
+            tableDirectory = NCManageDatabase.shared.getTableDirectory(predicate: NSPredicate(format: "account == %@ AND serverUrl == %@", account, serverUrl))
+        }
+
+        return false
+    }
 }
+
+

+ 1 - 1
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCKTVHTTPCache.swift

@@ -33,7 +33,7 @@ class NCKTVHTTPCache: NSObject {
 
     func getVideoURL(metadata: tableMetadata) -> (url: URL?, isProxy: Bool) {
 
-        if CCUtility.fileProviderStorageExists(metadata) {
+        if CCUtility.fileProviderStorageExists(metadata) || NCUtility.shared.isFolderEncrypted(metadata: metadata) {
 
             return (URL(fileURLWithPath: CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)), false)
 

+ 14 - 2
iOSClient/Viewer/NCViewerMedia/NCPlayer/NCPlayer.swift

@@ -203,6 +203,13 @@ class NCPlayer: NSObject {
                         }))
                         alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", value: "No", comment: ""), style: .default, handler: { _ in }))
                         self.viewController?.present(alertController, animated: true)
+                    } else if NCUtility.shared.isFolderEncrypted(metadata: self.metadata) {
+                        let alertController = UIAlertController(title: NSLocalizedString("_info_", value: "Info", comment: ""), message: NSLocalizedString("_video_not_streamed_e2ee_", comment: ""), preferredStyle: .alert)
+                        alertController.addAction(UIAlertAction(title: NSLocalizedString("_yes_", value: "Yes", comment: ""), style: .default, handler: { _ in
+                            self.downloadVideo(isEncrypted: true)
+                        }))
+                        alertController.addAction(UIAlertAction(title: NSLocalizedString("_no_", value: "No", comment: ""), style: .default, handler: { _ in }))
+                        self.viewController?.present(alertController, animated: true)
                     } else {
 #if MFFFLIB
                         if error?.code == AVError.Code.fileFormatNotRecognized.rawValue {
@@ -416,11 +423,11 @@ class NCPlayer: NSObject {
         }
     }
 
-    internal func downloadVideo(requiredConvert: Bool = false) {
+    internal func downloadVideo(isEncrypted: Bool = false, requiredConvert: Bool = false) {
 
         guard let view = appDelegate.window?.rootViewController?.view else { return }
         let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
-        let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileNameView)!
+        let fileNameLocalPath = CCUtility.getDirectoryProviderStorageOcId(metadata.ocId, fileNameView: metadata.fileName)!
         let hud = JGProgressHUD()
         var downloadRequest: DownloadRequest?
 
@@ -444,6 +451,11 @@ class NCPlayer: NSObject {
         } completionHandler: { _, _, _, _, _, afError, error in
             if afError == nil {
                 NCManageDatabase.shared.addLocalFile(metadata: self.metadata)
+                if isEncrypted {
+                    if let result = NCManageDatabase.shared.getE2eEncryption(predicate: NSPredicate(format: "fileNameIdentifier == %@ AND serverUrl == %@", self.metadata.fileName, self.metadata.serverUrl)) {
+                        NCEndToEndEncryption.sharedManager()?.decryptFileName(self.metadata.fileName, fileNameView: self.metadata.fileNameView, ocId: self.metadata.ocId, key: result.key, initializationVector: result.initializationVector, authenticationTag: result.authenticationTag)
+                    }
+                }
                 let urlVideo = NCKTVHTTPCache.shared.getVideoURL(metadata: self.metadata)
                 if let url = urlVideo.url {
                     self.url = url