From 0c4cc5530b350f279027a6de139e11b6ed4ce2d9 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 29 Oct 2025 16:40:41 +0100 Subject: [PATCH 01/27] fix(macOS): Enforce valid app group identifier. Signed-off-by: Iva Horn --- admin/osx/macosx.entitlements.cmake | 2 +- cmake/modules/MacOSXBundleInfo.plist.in | 2 +- .../FileProviderExt/FileProviderExt.entitlements | 2 +- .../MacOSX/NextcloudIntegration/FileProviderExt/Info.plist | 2 +- .../FileProviderUIExt/FileProviderUIExt.entitlements | 2 +- .../FinderSyncExt/FinderSyncExt.entitlements | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/admin/osx/macosx.entitlements.cmake b/admin/osx/macosx.entitlements.cmake index b32ff2749c36e..fd47ba4df7d60 100644 --- a/admin/osx/macosx.entitlements.cmake +++ b/admin/osx/macosx.entitlements.cmake @@ -4,7 +4,7 @@ com.apple.security.application-groups - @SOCKETAPI_TEAM_IDENTIFIER_PREFIX@@APPLICATION_REV_DOMAIN@ + group.@APPLICATION_REV_DOMAIN@ @DEBUG_ENTITLEMENTS@ diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in index 962240069c9a8..134cfe55cf38a 100644 --- a/cmake/modules/MacOSXBundleInfo.plist.in +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -91,6 +91,6 @@ NCFPKAppGroupIdentifier - @SOCKETAPI_TEAM_IDENTIFIER_PREFIX@@APPLICATION_REV_DOMAIN@ + group.@APPLICATION_REV_DOMAIN@ diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExt.entitlements b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExt.entitlements index eab912dc49600..ff6803420534f 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExt.entitlements +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExt.entitlements @@ -6,7 +6,7 @@ com.apple.security.application-groups - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) + group.$(OC_APPLICATION_REV_DOMAIN) com.apple.security.network.client diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist index efb5462421ae5..7f90434745212 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist @@ -49,6 +49,6 @@ SocketApiPrefix $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) NCFPKAppGroupIdentifier - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) + group.$(OC_APPLICATION_REV_DOMAIN) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderUIExt.entitlements b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderUIExt.entitlements index eab912dc49600..ff6803420534f 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderUIExt.entitlements +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderUIExt.entitlements @@ -6,7 +6,7 @@ com.apple.security.application-groups - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) + group.$(OC_APPLICATION_REV_DOMAIN) com.apple.security.network.client diff --git a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncExt.entitlements b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncExt.entitlements index 5d2a36d31b72d..a665d757b86ec 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncExt.entitlements +++ b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncExt.entitlements @@ -6,7 +6,7 @@ com.apple.security.application-groups - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) + group.$(OC_APPLICATION_REV_DOMAIN) From 78d905558a02afab16a853dec99e925d6e0484d6 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 29 Oct 2025 16:46:01 +0100 Subject: [PATCH 02/27] chore(macOS): Updated Xcode build settings. - Removed redundant build settings about signing. - Updated default base identifier from ownCloud to Nextcloud. Signed-off-by: Iva Horn --- .../project.pbxproj | 38 ++----------------- 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index 670ba1a0347a8..49a1159a34880 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -970,10 +970,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderExt/FileProviderExt.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; - CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -1000,7 +997,6 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; @@ -1026,10 +1022,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderExt/FileProviderExt.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; - CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -1050,7 +1043,6 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_COMPILATION_MODE = wholemodule; @@ -1076,8 +1068,6 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1111,9 +1101,8 @@ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.owncloud.NCDesktopClientSocketKit; + PRODUCT_BUNDLE_IDENTIFIER = com.nextcloud.NCDesktopClientSocketKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; @@ -1143,8 +1132,6 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -1172,9 +1159,8 @@ MARKETING_VERSION = 1.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.owncloud.NCDesktopClientSocketKit; + PRODUCT_BUNDLE_IDENTIFIER = com.nextcloud.NCDesktopClientSocketKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; @@ -1205,7 +1191,6 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderUIExt/FileProviderUIExt.entitlements; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; ENABLE_USER_SCRIPT_SANDBOXING = YES; @@ -1269,7 +1254,6 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderUIExt/FileProviderUIExt.entitlements; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; - CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -1327,7 +1311,6 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUGGING_SYMBOLS = YES; DEBUG_INFORMATION_FORMAT = dwarf; @@ -1368,7 +1351,6 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -1475,8 +1457,6 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1504,7 +1484,6 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; SDKROOT = macosx; SWIFT_OBJC_BRIDGING_HEADER = "desktopclient/desktopclient-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1531,8 +1510,6 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; ENABLE_NS_ASSERTIONS = NO; @@ -1553,7 +1530,6 @@ MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; SDKROOT = macosx; SWIFT_OBJC_BRIDGING_HEADER = "desktopclient/desktopclient-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -1578,8 +1554,6 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements; - CODE_SIGN_IDENTITY = "-"; - CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -1607,12 +1581,11 @@ MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; OC_APPLICATION_NAME = ownCloud; - OC_APPLICATION_REV_DOMAIN = com.owncloud.desktopclient; + OC_APPLICATION_REV_DOMAIN = com.nextcloud.desktopclient; OC_OEM_SHARE_ICNS = ""; OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; SDKROOT = macosx; SKIP_INSTALL = YES; }; @@ -1636,9 +1609,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements; - CODE_SIGN_IDENTITY = "-"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; - CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; ENABLE_NS_ASSERTIONS = NO; @@ -1660,11 +1631,10 @@ MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; OC_APPLICATION_NAME = ownCloud; - OC_APPLICATION_REV_DOMAIN = com.owncloud.desktopclient; + OC_APPLICATION_REV_DOMAIN = com.nextcloud.desktopclient; OC_OEM_SHARE_ICNS = ""; OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE = ""; SDKROOT = macosx; SKIP_INSTALL = YES; }; From 072aa4e2b58254e9f094f7602be2e5769950cb9b Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 29 Oct 2025 17:01:35 +0100 Subject: [PATCH 03/27] fix(macOS): Updated signing settings for all targets. - Use automatically managed code signing. - Defined Nextcloud GmbH (NKUJUXUJ3B) as the default development team. - Set default code sign identity to "Apple Development". - This is necessary because the new and correct group container identifier "group.com.nextcloud.desktopclient" requires a provisioning profile which requires development signing. Signed-off-by: Iva Horn --- .../project.pbxproj | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index 49a1159a34880..e5e99cf365c4b 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -631,7 +631,6 @@ }; 53903D0B2956164F00D0B308 = { CreatedOnToolsVersion = 14.2; - ProvisioningStyle = Manual; }; 53B9797D2B84C81F002DA742 = { CreatedOnToolsVersion = 15.2; @@ -641,13 +640,9 @@ }; C2B573B01B1CD91E00303B36 = { CreatedOnToolsVersion = 6.3.1; - DevelopmentTeam = 9B5WD74GWJ; - ProvisioningStyle = Manual; }; C2B573D61B1CD9CE00303B36 = { CreatedOnToolsVersion = 6.3.1; - DevelopmentTeam = 9B5WD74GWJ; - ProvisioningStyle = Manual; SystemCapabilities = { com.apple.ApplicationGroups.Mac = { enabled = 1; @@ -970,10 +965,12 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderExt/FileProviderExt.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; + CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_TESTING_SEARCH_PATHS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; @@ -997,6 +994,7 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; @@ -1022,10 +1020,12 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderExt/FileProviderExt.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_NS_ASSERTIONS = NO; ENABLE_TESTING_SEARCH_PATHS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1043,6 +1043,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SWIFT_COMPILATION_MODE = wholemodule; @@ -1068,11 +1069,13 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = NKUJUXUJ3B; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1103,6 +1106,7 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.nextcloud.NCDesktopClientSocketKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; @@ -1132,11 +1136,13 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = NKUJUXUJ3B; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1161,6 +1167,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.nextcloud.NCDesktopClientSocketKit; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = auto; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; @@ -1191,8 +1198,11 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderUIExt/FileProviderUIExt.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; @@ -1223,6 +1233,7 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; @@ -1253,10 +1264,12 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderUIExt/FileProviderUIExt.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_NS_ASSERTIONS = NO; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -1281,6 +1294,7 @@ MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = "$(OC_APPLICATION_REV_DOMAIN).$(PRODUCT_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = macosx; @@ -1457,8 +1471,11 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -1484,6 +1501,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SWIFT_OBJC_BRIDGING_HEADER = "desktopclient/desktopclient-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -1510,8 +1528,11 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1530,6 +1551,7 @@ MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SWIFT_OBJC_BRIDGING_HEADER = "desktopclient/desktopclient-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -1554,8 +1576,11 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -1586,6 +1611,7 @@ OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; }; @@ -1609,9 +1635,12 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1635,6 +1664,7 @@ OC_OEM_SHARE_ICNS = ""; OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; }; From 577ff5696ba90302bddc31b387631590fc78ee9b Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 4 Nov 2025 12:58:47 +0100 Subject: [PATCH 04/27] feat(macOS): Enabled app sandbox and network client entitlements for main app. Signed-off-by: Iva Horn --- admin/osx/macosx.entitlements.cmake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/admin/osx/macosx.entitlements.cmake b/admin/osx/macosx.entitlements.cmake index fd47ba4df7d60..1641bb394af6c 100644 --- a/admin/osx/macosx.entitlements.cmake +++ b/admin/osx/macosx.entitlements.cmake @@ -2,6 +2,10 @@ + com.apple.security.app-sandbox + + com.apple.security.network.client + com.apple.security.application-groups group.@APPLICATION_REV_DOMAIN@ From 25c66b3dd5ed24d4b210a68cfc7df3d79d1f08db Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 29 Oct 2025 17:09:07 +0100 Subject: [PATCH 05/27] fix(macOS): Relocated socket files into sandboxed containers. Content of sandboxed apps must not be created on the root level of a container but lower in the hierarchy of it. Signed-off-by: Iva Horn --- .../NextcloudIntegration/FinderSyncExt/FinderSync.m | 4 +++- src/gui/macOS/fileprovidersocketserver_mac.mm | 13 ++++++------- src/gui/macOS/fileproviderutils_mac.mm | 2 ++ src/gui/socketapi/socketapi_mac.mm | 13 ++++++------- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m index 97f32c8a3a47f..d4621c0ea04ec 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m +++ b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m @@ -57,7 +57,9 @@ - (instancetype)init // https://developer.apple.com/library/mac/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24 NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:socketApiPrefix]; - NSURL *socketPath = [container URLByAppendingPathComponent:@".socket" isDirectory:NO]; + NSURL *library = [container URLByAppendingPathComponent:@"Library" isDirectory:true]; + NSURL *applicationSupport = [library URLByAppendingPathComponent:@"Application Support" isDirectory:true]; + NSURL *socketPath = [applicationSupport URLByAppendingPathComponent:@".socket" isDirectory:NO]; NSLog(@"Socket path: %@", socketPath.path); diff --git a/src/gui/macOS/fileprovidersocketserver_mac.mm b/src/gui/macOS/fileprovidersocketserver_mac.mm index 3c0b4a8e12bc4..3d9a658d41f9a 100644 --- a/src/gui/macOS/fileprovidersocketserver_mac.mm +++ b/src/gui/macOS/fileprovidersocketserver_mac.mm @@ -16,14 +16,13 @@ QString fileProviderSocketPath() { - // This must match the code signing Team setting of the extension - // Example for developer builds (with ad-hoc signing identity): "" "com.owncloud.desktopclient" ".fileprovidersocket" - // Example for official signed packages: "9B5WD74GWJ." "com.owncloud.desktopclient" ".fileprovidersocket" - NSString *appGroupId = @SOCKETAPI_TEAM_IDENTIFIER_PREFIX APPLICATION_REV_DOMAIN; - + NSString *appGroupId = [NSString stringWithFormat:@"group.%@", @APPLICATION_REV_DOMAIN]; NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId]; - NSURL *socketPath = [container URLByAppendingPathComponent:@".fileprovidersocket" isDirectory:false]; - return QString::fromNSString(socketPath.path); + NSURL *library = [container URLByAppendingPathComponent:@"Library" isDirectory:true]; + NSURL *applicationSupport = [library URLByAppendingPathComponent:@"Application Support" isDirectory:true]; + NSURL *socket = [applicationSupport URLByAppendingPathComponent:@".fileprovidersocket" isDirectory:false]; + + return QString::fromNSString(socket.path); } } // namespace Mac diff --git a/src/gui/macOS/fileproviderutils_mac.mm b/src/gui/macOS/fileproviderutils_mac.mm index 9ca8238a90b80..0e3d7cdcb25cc 100644 --- a/src/gui/macOS/fileproviderutils_mac.mm +++ b/src/gui/macOS/fileproviderutils_mac.mm @@ -209,10 +209,12 @@ QDir fileProviderDomainSupportDirectory(const QString domainIdentifier) QString groupContainerPath() { NSString *const groupId = (NSString *)[NSBundle.mainBundle objectForInfoDictionaryKey:@"NCFPKAppGroupIdentifier"]; + if (groupId == nil) { qCWarning(lcMacFileProviderUtils) << "No app group identifier found in Info.plist, cannot determine group container path."; return QString(); } + return QString::fromNSString([NSFileManager.defaultManager containerURLForSecurityApplicationGroupIdentifier:groupId].path); } diff --git a/src/gui/socketapi/socketapi_mac.mm b/src/gui/socketapi/socketapi_mac.mm index ac0fabf15da74..dbfb86d9d8a4a 100644 --- a/src/gui/socketapi/socketapi_mac.mm +++ b/src/gui/socketapi/socketapi_mac.mm @@ -13,14 +13,13 @@ QString socketApiSocketPath() { - // This must match the code signing Team setting of the extension - // Example for developer builds (with ad-hoc signing identity): "" "com.owncloud.desktopclient" ".socket" - // Example for official signed packages: "9B5WD74GWJ." "com.owncloud.desktopclient" ".socket" - NSString *appGroupId = @SOCKETAPI_TEAM_IDENTIFIER_PREFIX APPLICATION_REV_DOMAIN; - + NSString *appGroupId = [NSString stringWithFormat:@"group.%@", @APPLICATION_REV_DOMAIN]; NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId]; - NSURL *socketPath = [container URLByAppendingPathComponent:@".socket" isDirectory:false]; - return QString::fromNSString(socketPath.path); + NSURL *library = [container URLByAppendingPathComponent:@"Library" isDirectory:true]; + NSURL *applicationSupport = [library URLByAppendingPathComponent:@"Application Support" isDirectory:true]; + NSURL *socket = [applicationSupport URLByAppendingPathComponent:@".socket" isDirectory:false]; + + return QString::fromNSString(socket.path); } } From b889b247beddcde42c3c287b66fcaca72b6fe9e6 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 5 Nov 2025 13:16:38 +0100 Subject: [PATCH 06/27] fix(macOS): Added container migration manifest. This moves the preferences of the app into its sandbox container as documented here: https://developer.apple.com/documentation/security/migrating-your-app-s-files-to-its-app-sandbox-container Signed-off-by: Iva Horn --- REUSE.toml | 1 + admin/osx/CMakeLists.txt | 5 +++++ admin/osx/container-migration.plist.cmake | 10 ++++++++++ 3 files changed, 16 insertions(+) create mode 100644 admin/osx/container-migration.plist.cmake diff --git a/REUSE.toml b/REUSE.toml index a071c20e148e8..01cee8866b8af 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -31,6 +31,7 @@ SPDX-License-Identifier = "GPL-2.0-or-later" [[annotations]] path = [ + "admin/osx/container-migration.plist.cmake", "admin/osx/TransifexStringCatalogSanitizer/Package.swift", "admin/osx/TransifexStringCatalogSanitizer/Package.resolved", "admin/osx/TransifexStringCatalogSanitizer/README.md", diff --git a/admin/osx/CMakeLists.txt b/admin/osx/CMakeLists.txt index 9f695b74338e5..d7952d27c6a50 100644 --- a/admin/osx/CMakeLists.txt +++ b/admin/osx/CMakeLists.txt @@ -21,6 +21,11 @@ else() set(DEBUG_ENTITLEMENTS "") endif() +# Sandbox migration description +configure_file(container-migration.plist.cmake ${CMAKE_CURRENT_BINARY_DIR}/container-migration.plist @ONLY) +install(FILES ${CMAKE_BINARY_DIR}/admin/osx/container-migration.plist + DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources) + configure_file(create_mac.sh.cmake ${CMAKE_CURRENT_BINARY_DIR}/create_mac.sh) configure_file(macosx.entitlements.cmake ${CMAKE_CURRENT_BINARY_DIR}/macosx.entitlements) configure_file(macosx.pkgproj.cmake ${CMAKE_CURRENT_BINARY_DIR}/macosx.pkgproj) diff --git a/admin/osx/container-migration.plist.cmake b/admin/osx/container-migration.plist.cmake new file mode 100644 index 0000000000000..0fceee516b70a --- /dev/null +++ b/admin/osx/container-migration.plist.cmake @@ -0,0 +1,10 @@ + + + + + Move + + ${Library}/Preferences/@APPLICATION_NAME@ + + + From b5e986d236ca92b47082bf11fe0b608bbd6587d4 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 5 Nov 2025 13:39:09 +0100 Subject: [PATCH 07/27] chore(macOS): Removed obsolete values from Info.plist Signed-off-by: Iva Horn --- .../FileProviderExt/FileProviderExtension.swift | 1 - .../MacOSX/NextcloudIntegration/FileProviderExt/Info.plist | 4 ---- 2 files changed, 5 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift index 04a7a03ca4dbf..82b1cf213661d 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift @@ -21,7 +21,6 @@ import OSLog /// let ncKit: NextcloudKit - let appGroupIdentifier = Bundle.main.object(forInfoDictionaryKey: "SocketApiPrefix") as? String var ncAccount: Account? var dbManager: FilesDatabaseManager? var changeObserver: RemoteChangeObserver? diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist index 7f90434745212..605ed5804c1e6 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist @@ -46,9 +46,5 @@ NSExtensionPrincipalClass $(PRODUCT_MODULE_NAME).FileProviderExtension - SocketApiPrefix - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) - NCFPKAppGroupIdentifier - group.$(OC_APPLICATION_REV_DOMAIN) From 032dd67ff08c0442225a6a3ef56c267c793ed67c Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 5 Nov 2025 13:48:04 +0100 Subject: [PATCH 08/27] chore(macOS): Outsourced some build settings from Xcode project into file. - CODE_SIGN_IDENTITY - CODE_SIGN_STYLE - DEVELOPMENT_TEAM Signed-off-by: Iva Horn --- .../NextcloudDev/Build.xcconfig.template | 12 ++++++- .../project.pbxproj | 34 ++----------------- 2 files changed, 13 insertions(+), 33 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig.template b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig.template index 51fc661d961e7..44078461fce74 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig.template +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig.template @@ -5,4 +5,14 @@ // The common name of the signing certificate to use for code signing. // It must be present in your keychain and a development certificate. // -CODE_SIGN_IDENTITY= +CODE_SIGN_IDENTITY=Apple Development + +// +// Xcode should automatically manage signing assets. +// +CODE_SIGN_STYLE = Automatic + +// +// The development team identifier registered with Apple. +// +DEVELOPMENT_TEAM=NKUJUXUJ3B \ No newline at end of file diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index e5e99cf365c4b..222467cf05b00 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -965,12 +965,9 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderExt/FileProviderExt.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = YES; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_TESTING_SEARCH_PATHS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; @@ -1020,12 +1017,9 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderExt/FileProviderExt.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_NS_ASSERTIONS = NO; ENABLE_TESTING_SEARCH_PATHS = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1069,13 +1063,10 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = NKUJUXUJ3B; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1136,13 +1127,10 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = NKUJUXUJ3B; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1198,11 +1186,8 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderUIExt/FileProviderUIExt.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GCC_DYNAMIC_NO_PIC = NO; @@ -1264,12 +1249,9 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_ENTITLEMENTS = FileProviderUIExt/FileProviderUIExt.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; - CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_NS_ASSERTIONS = NO; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; @@ -1311,7 +1293,6 @@ }; AA0A62F32E8EA929007F4A7A /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1351,7 +1332,6 @@ }; AA0A62F42E8EA929007F4A7A /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1383,6 +1363,7 @@ }; C2B573991B1CD88000303B36 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */; buildSettings = { CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; @@ -1419,6 +1400,7 @@ }; C2B5739A1B1CD88000303B36 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */; buildSettings = { CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; @@ -1471,11 +1453,8 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -1528,11 +1507,8 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -1576,11 +1552,8 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; @@ -1635,12 +1608,9 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_ENTITLEMENTS = FinderSyncExt/FinderSyncExt.entitlements; - CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO; - CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; - DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; From 402b4290d57ff25c75c776f625400125f8b887a9 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 5 Nov 2025 13:50:22 +0100 Subject: [PATCH 09/27] chore(macOS): Updated bundle identifier in Xcode project to Nextcloud. Signed-off-by: Iva Horn --- .../MacOSX/NextcloudIntegration/desktopclient/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/desktopclient/Info.plist b/shell_integration/MacOSX/NextcloudIntegration/desktopclient/Info.plist index bbaa296f4908a..e5613ce78b883 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/desktopclient/Info.plist +++ b/shell_integration/MacOSX/NextcloudIntegration/desktopclient/Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - com.owncloud.$(PRODUCT_NAME:rfc1034identifier) + com.nextcloud.$(PRODUCT_NAME:rfc1034identifier) CFBundleInfoDictionaryVersion 6.0 CFBundleName From e1344e05b641e0627ba19e4c8218c2ff735b8927 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 5 Nov 2025 14:46:45 +0100 Subject: [PATCH 10/27] chore(macOS): Creating build settings file on demand from template. Signed-off-by: Iva Horn --- .../MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh index a890a0ba8370e..dc081b98559a9 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh @@ -20,6 +20,11 @@ if [ -z "${CODE_SIGN_IDENTITY}" ]; then exit 1 fi +if [ ! -f "$SOURCE_ROOT/NextcloudDev/Build.xcconfig" ]; then + cp "$SOURCE_ROOT/NextcloudDev/Build.xcconfig.example" "$SOURCE_ROOT/NextcloudDev/Build.xcconfig" + echo "Automatically created Build.xcconfig from template because it was not present yet." +fi + DESKTOP_CLIENT_PROJECT_ROOT="$SOURCE_ROOT/../../.." if [ -d "$DESKTOP_CLIENT_PROJECT_ROOT/admin/osx/mac-crafter" ]; then From b898958c25450662edc988a2bcd9a79c433ca589 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 5 Nov 2025 14:47:07 +0100 Subject: [PATCH 11/27] chore(macOS): Added default values to build settings file. Also replaced the template file with a default file for build settings which can be overridden by environment variables. Signed-off-by: Iva Horn --- .../MacOSX/NextcloudIntegration/.gitignore | 3 --- .../NextcloudDev/Build.xcconfig.template | 18 ------------------ .../NextcloudIntegration/NextcloudDev/Craft.sh | 5 ----- .../project.pbxproj | 2 -- 4 files changed, 28 deletions(-) delete mode 100644 shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig.template diff --git a/shell_integration/MacOSX/NextcloudIntegration/.gitignore b/shell_integration/MacOSX/NextcloudIntegration/.gitignore index bd4d3e80df37b..712978b8aeb8c 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/.gitignore +++ b/shell_integration/MacOSX/NextcloudIntegration/.gitignore @@ -2,6 +2,3 @@ # SPDX-License-Identifier: GPL-2.0-or-later DerivedData - -# exception -!NextcloudDev/Build.xcconfig.template diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig.template b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig.template deleted file mode 100644 index 44078461fce74..0000000000000 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig.template +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors -// SPDX-License-Identifier: GPL-2.0-or-later - -// -// The common name of the signing certificate to use for code signing. -// It must be present in your keychain and a development certificate. -// -CODE_SIGN_IDENTITY=Apple Development - -// -// Xcode should automatically manage signing assets. -// -CODE_SIGN_STYLE = Automatic - -// -// The development team identifier registered with Apple. -// -DEVELOPMENT_TEAM=NKUJUXUJ3B \ No newline at end of file diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh index dc081b98559a9..a890a0ba8370e 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Craft.sh @@ -20,11 +20,6 @@ if [ -z "${CODE_SIGN_IDENTITY}" ]; then exit 1 fi -if [ ! -f "$SOURCE_ROOT/NextcloudDev/Build.xcconfig" ]; then - cp "$SOURCE_ROOT/NextcloudDev/Build.xcconfig.example" "$SOURCE_ROOT/NextcloudDev/Build.xcconfig" - echo "Automatically created Build.xcconfig from template because it was not present yet." -fi - DESKTOP_CLIENT_PROJECT_ROOT="$SOURCE_ROOT/../../.." if [ -d "$DESKTOP_CLIENT_PROJECT_ROOT/admin/osx/mac-crafter" ]; then diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index 222467cf05b00..95a486c7121fd 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -198,7 +198,6 @@ 53FE14662B8F78B6006C4193 /* ShareOptionsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareOptionsView.swift; sourceTree = ""; }; AA02B2AA2E7048C600C72B34 /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = ""; }; AA0A6B1D2E8EA94F007F4A7A /* Craft.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = Craft.sh; sourceTree = ""; }; - AA1191B52E8EAFF900E21C7B /* Build.xcconfig.template */ = {isa = PBXFileReference; lastKnownFileType = text; path = Build.xcconfig.template; sourceTree = ""; }; AA27A4E32E93C0D700665051 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; AA7F17E02E7017230000E928 /* Authentication.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Authentication.storyboard; sourceTree = ""; }; AA7F17E22E70173E0000E928 /* AuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = ""; }; @@ -389,7 +388,6 @@ isa = PBXGroup; children = ( AA826BEE2E8EAA7500CE49C4 /* Build.xcconfig */, - AA1191B52E8EAFF900E21C7B /* Build.xcconfig.template */, AA0A6B1D2E8EA94F007F4A7A /* Craft.sh */, ); path = NextcloudDev; From 06ad59fa0c78706eb74c4e4123a22c3f8c87e89c Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Fri, 7 Nov 2025 14:08:36 +0100 Subject: [PATCH 12/27] fix(macOS): NextcloudDesktopClientSocketKit signing. Signed-off-by: Iva Horn --- .../NextcloudDev/Build.xcconfig | 28 +++++++++++++++++++ .../project.pbxproj | 10 ++++--- 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig new file mode 100644 index 0000000000000..884b9b6063039 --- /dev/null +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudDev/Build.xcconfig @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors +// SPDX-License-Identifier: GPL-2.0-or-later + +// +// The common name of the signing certificate to use for code signing. +// It must be present in your keychain and a development certificate. +// +CODE_SIGN_IDENTITY=Apple Development//: Iva Horn (U949586L5B) + +// +// Xcode should automatically manage signing assets. +// +CODE_SIGN_STYLE = Automatic + +// +// The development team identifier registered with Apple. +// +DEVELOPMENT_TEAM=NKUJUXUJ3B + +// +// The app name. +// +OC_APPLICATION_NAME=Nextcloud + +// +// Reverse base identifier. +// +OC_APPLICATION_REV_DOMAIN=com.nextcloud.desktopclient diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index 95a486c7121fd..4cbbde43a44e8 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -1061,10 +1061,12 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1125,10 +1127,12 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_STYLE = Manual; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; @@ -1382,6 +1386,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; ENABLE_TESTING_SEARCH_PATHS = YES; @@ -1419,6 +1424,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = NKUJUXUJ3B; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTING_SEARCH_PATHS = YES; GCC_NO_COMMON_BLOCKS = YES; @@ -1576,8 +1582,6 @@ ); MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; - OC_APPLICATION_NAME = ownCloud; - OC_APPLICATION_REV_DOMAIN = com.nextcloud.desktopclient; OC_OEM_SHARE_ICNS = ""; OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; ONLY_ACTIVE_ARCH = YES; @@ -1627,8 +1631,6 @@ ); MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; - OC_APPLICATION_NAME = ownCloud; - OC_APPLICATION_REV_DOMAIN = com.nextcloud.desktopclient; OC_OEM_SHARE_ICNS = ""; OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; From 3b3c31d594bb51a102936a44a404d915936a2f65 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Fri, 7 Nov 2025 14:44:33 +0100 Subject: [PATCH 13/27] fix(macOS): Added missing user selected read-write entitlement to main app sandbox. Signed-off-by: Iva Horn --- admin/osx/macosx.entitlements.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/osx/macosx.entitlements.cmake b/admin/osx/macosx.entitlements.cmake index 1641bb394af6c..2be5449c1a9b9 100644 --- a/admin/osx/macosx.entitlements.cmake +++ b/admin/osx/macosx.entitlements.cmake @@ -6,6 +6,8 @@ com.apple.security.network.client + com.apple.security.files.user-selected.read-write + com.apple.security.application-groups group.@APPLICATION_REV_DOMAIN@ From 19c9d1cf542c45a29685f2daa6dd60a7f82d3bc5 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Fri, 7 Nov 2025 14:44:46 +0100 Subject: [PATCH 14/27] fix(macOS): Writable check on debug archive destination directory. Signed-off-by: Iva Horn --- src/gui/generalsettings.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index d0cfcc4f745e8..95ce335ddf8e3 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -119,23 +119,21 @@ QVector createDebugArchiveFileList() bool createDebugArchive(const QString &filename) { - const auto fileInfo = QFileInfo(filename); - const auto dirInfo = QFileInfo(fileInfo.dir().absolutePath()); - if (!dirInfo.isWritable()) { + const auto entries = createDebugArchiveFileList(); + + KZip zip(filename); + + if (!zip.open(QIODevice::WriteOnly)) { QMessageBox::critical( nullptr, QObject::tr("Failed to create debug archive"), QObject::tr("Could not create debug archive in selected location!"), QMessageBox::Ok ); + return false; } - const auto entries = createDebugArchiveFileList(); - - KZip zip(filename); - zip.open(QIODevice::WriteOnly); - for (const auto &entry : entries) { zip.addLocalFile(entry.localFilename, entry.zipFilename); } From 2c986fed8ddbb8a20d8b89a92f5c4c249e679b47 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Mon, 10 Nov 2025 13:41:49 +0100 Subject: [PATCH 15/27] fix(macOS): Accessing target of debug archive as a security scoped URL. Signed-off-by: Iva Horn --- doc/macOS-Sandbox-Qt.md | 231 ++++++++++++++++++++++++++++++ src/common/common.cmake | 2 + src/common/utility_mac_sandbox.h | 70 +++++++++ src/common/utility_mac_sandbox.mm | 106 ++++++++++++++ src/gui/addcertificatedialog.cpp | 24 +++- src/gui/generalsettings.cpp | 33 ++++- src/gui/socketapi/socketapi.cpp | 20 ++- 7 files changed, 477 insertions(+), 9 deletions(-) create mode 100644 doc/macOS-Sandbox-Qt.md create mode 100644 src/common/utility_mac_sandbox.h create mode 100644 src/common/utility_mac_sandbox.mm diff --git a/doc/macOS-Sandbox-Qt.md b/doc/macOS-Sandbox-Qt.md new file mode 100644 index 0000000000000..ffe4ebb632c10 --- /dev/null +++ b/doc/macOS-Sandbox-Qt.md @@ -0,0 +1,231 @@ + + +# macOS App Sandbox Support for Qt Applications + +## Overview + +This document explains how to make the Nextcloud Desktop Client work properly with macOS App Sandbox when using Qt. The key issue is that Qt's `QFileDialog` returns security-scoped URLs that require explicit access management in sandboxed applications. + +## The Problem + +When running a sandboxed macOS application with the `com.apple.security.files.user-selected.read-write` entitlement, file operations on user-selected files (via `QFileDialog`) will fail unless you explicitly: + +1. Call `startAccessingSecurityScopedResource()` on the URL before accessing the file +2. Call `stopAccessingSecurityScopedResource()` when done + +This is **required by macOS sandbox security**, but Qt does not handle this automatically. The underlying issue is that: + +- `QFileDialog::getSaveFileUrl()` returns a `QUrl` that represents a security-scoped bookmark +- Without calling `startAccessingSecurityScopedResource()`, the sandboxed app has no permission to access the file +- Even though you have the entitlement, you must explicitly claim access for each user-selected file + +## The Solution + +### 1. Security-Scoped Access Wrapper + +We created a RAII wrapper class `MacSandboxSecurityScopedAccess` (in `utility_mac_sandbox.h/mm`) that: + +- Automatically calls `startAccessingSecurityScopedResource()` in the constructor +- Automatically calls `stopAccessingSecurityScopedResource()` in the destructor +- Uses unique_ptr for exception safety +- Provides `isValid()` to check if access was successfully obtained + +### 2. Usage Pattern + +```cpp +#ifdef Q_OS_MACOS +#include "utility_mac_sandbox.h" +#endif + +void MyClass::saveFile() +{ + const auto fileUrl = QFileDialog::getSaveFileUrl( + this, + tr("Save File"), + QUrl::fromLocalFile(QDir::homePath()), + tr("Text Files (*.txt)") + ); + + if (fileUrl.isEmpty()) { + return; + } + +#ifdef Q_OS_MACOS + // Acquire security-scoped access for the user-selected file + auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(fileUrl); + + if (!scopedAccess->isValid()) { + // Handle error - access could not be obtained + QMessageBox::critical(this, tr("Error"), tr("Could not access file")); + return; + } + // scopedAccess will automatically release when it goes out of scope +#endif + + // Now you can safely access the file + QFile file(fileUrl.toLocalFile()); + if (file.open(QIODevice::WriteOnly)) { + // Write to file... + } +} +``` + +### 3. Required Entitlements + +In `admin/osx/macosx.entitlements.cmake`, ensure you have: + +```xml +com.apple.security.app-sandbox + +com.apple.security.files.user-selected.read-write + +``` + +## Key Requirements for Qt + macOS Sandbox + +### 1. Use QFileDialog URL-based Methods + +Always use the URL-based variants of QFileDialog methods: +- ✅ `QFileDialog::getSaveFileUrl()` +- ✅ `QFileDialog::getOpenFileUrl()` +- ✅ `QFileDialog::getOpenFileUrls()` +- ❌ `QFileDialog::getSaveFileName()` - returns QString, not security-scoped +- ❌ `QFileDialog::getOpenFileName()` - returns QString, not security-scoped + +### 2. Wrap File Access with Security Scoping + +```cpp +#ifdef Q_OS_MACOS +auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(fileUrl); +if (!scopedAccess->isValid()) { + // Handle error + return; +} +#endif +// Access file here +// scopedAccess releases automatically when going out of scope +``` + +### 3. Handle Scope Lifetime Correctly + +The security-scoped access must remain valid for the entire duration of file access: + +```cpp +// ✅ CORRECT - scopedAccess lives until after file operations +#ifdef Q_OS_MACOS +auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(fileUrl); +if (!scopedAccess->isValid()) { + return; +} +#endif + +QFile file(fileUrl.toLocalFile()); +file.open(QIODevice::WriteOnly); +file.write(data); +file.close(); +// scopedAccess destructor called here + +// ❌ WRONG - scopedAccess destroyed before file operations +#ifdef Q_OS_MACOS +{ + auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(fileUrl); + if (!scopedAccess->isValid()) { + return; + } +} // scopedAccess destroyed here! +#endif + +QFile file(fileUrl.toLocalFile()); // This will fail! +file.open(QIODevice::WriteOnly); // No longer have access +``` + +### 4. Consider All File Operations + +This applies to ANY file operation on user-selected files: +- Reading files +- Writing files +- Creating archives/zip files +- Copying files +- Moving files +- Checking file existence/permissions + +## Common Pitfalls + +### 1. Using QString-based paths instead of QUrl + +```cpp +// ❌ WRONG - loses security-scoped bookmark +QString path = QFileDialog::getSaveFileName(...); + +// ✅ CORRECT - preserves security-scoped bookmark +QUrl url = QFileDialog::getSaveFileUrl(...); +``` + +### 2. Converting QUrl too early + +```cpp +// ❌ WRONG - converts to string before starting access +QUrl url = QFileDialog::getSaveFileUrl(...); +QString path = url.toLocalFile(); // Loses security scope! +#ifdef Q_OS_MACOS +auto access = Utility::MacSandboxSecurityScopedAccess::create(QUrl::fromLocalFile(path)); // Won't work +#endif + +// ✅ CORRECT - start access before conversion +QUrl url = QFileDialog::getSaveFileUrl(...); +#ifdef Q_OS_MACOS +auto access = Utility::MacSandboxSecurityScopedAccess::create(url); // Works! +#endif +QString path = url.toLocalFile(); +``` + +### 3. Forgetting to check isValid() + +```cpp +// ❌ RISKY - doesn't check if access was obtained +auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(fileUrl); +QFile file(fileUrl.toLocalFile()); // Might fail silently + +// ✅ CORRECT - always check validity +auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(fileUrl); +if (!scopedAccess->isValid()) { + // Show error to user + return; +} +QFile file(fileUrl.toLocalFile()); // Now safe to use +``` + +## Testing Sandbox Behavior + +To test if your app properly handles sandbox restrictions: + +1. **Build with proper entitlements**: Ensure the app is codesigned with the entitlements file +2. **Test file operations**: Try to save/open files in various locations +3. **Check Console.app**: Look for sandbox violation messages like: + ``` + Sandbox: MyApp(12345) deny(1) file-write-create /Users/... + ``` +4. **Test without access calls**: Temporarily remove the security-scoped access calls to verify they're needed + +## References + +- [Apple Documentation: App Sandbox](https://developer.apple.com/documentation/security/app_sandbox) +- [Apple Documentation: Security-Scoped Bookmarks](https://developer.apple.com/documentation/foundation/nsurl/1417051-startaccessingsecurityscopedreso) +- [Qt Documentation: QFileDialog](https://doc.qt.io/qt-6/qfiledialog.html) + +## Files Modified + +- `src/common/utility_mac_sandbox.h` - Header for security-scoped access wrapper +- `src/common/utility_mac_sandbox.mm` - Implementation using Objective-C++ +- `src/common/common.cmake` - Added new files to build system +- `src/gui/generalsettings.cpp` - Fixed debug archive creation to use security-scoped access + +## Future Work + +Consider auditing all uses of `QFileDialog` in the codebase to ensure they: +1. Use URL-based methods (`getSaveFileUrl`, `getOpenFileUrl`, etc.) +2. Properly acquire security-scoped access on macOS +3. Handle access errors gracefully diff --git a/src/common/common.cmake b/src/common/common.cmake index 5a19933dc95c1..f0ba1e2726c0c 100644 --- a/src/common/common.cmake +++ b/src/common/common.cmake @@ -31,6 +31,8 @@ if(WIN32) elseif(APPLE) list(APPEND common_SOURCES ${CMAKE_CURRENT_LIST_DIR}/utility_mac.mm + ${CMAKE_CURRENT_LIST_DIR}/utility_mac_sandbox.h + ${CMAKE_CURRENT_LIST_DIR}/utility_mac_sandbox.mm ) elseif(UNIX AND NOT APPLE) list(APPEND common_SOURCES diff --git a/src/common/utility_mac_sandbox.h b/src/common/utility_mac_sandbox.h new file mode 100644 index 0000000000000..38aff1c7f725f --- /dev/null +++ b/src/common/utility_mac_sandbox.h @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#pragma once + +#include +#include +#include + +namespace OCC { +namespace Utility { + +/** + * @brief RAII wrapper for macOS security-scoped resource access + * + * When working with files selected by the user via QFileDialog in a sandboxed + * macOS app, the returned URLs are security-scoped bookmarks that require + * explicit access management via startAccessingSecurityScopedResource() and + * stopAccessingSecurityScopedResource(). + * + * This class provides RAII semantics to ensure proper cleanup. + * + * Usage: + * @code + * QUrl fileUrl = QFileDialog::getSaveFileUrl(...); + * if (!fileUrl.isEmpty()) { + * auto scopedAccess = MacSandboxSecurityScopedAccess::create(fileUrl); + * if (scopedAccess->isValid()) { + * // Now you can access the file + * QFile file(fileUrl.toLocalFile()); + * file.open(QIODevice::WriteOnly); + * } + * } + * @endcode + */ +class MacSandboxSecurityScopedAccess +{ +public: + ~MacSandboxSecurityScopedAccess(); + + // Non-copyable, movable + MacSandboxSecurityScopedAccess(const MacSandboxSecurityScopedAccess&) = delete; + MacSandboxSecurityScopedAccess& operator=(const MacSandboxSecurityScopedAccess&) = delete; + MacSandboxSecurityScopedAccess(MacSandboxSecurityScopedAccess&&) noexcept; + MacSandboxSecurityScopedAccess& operator=(MacSandboxSecurityScopedAccess&&) noexcept; + + /** + * @brief Create a security-scoped access wrapper for the given URL + * @param url The URL to access (typically from QFileDialog) + * @return A unique pointer to the access wrapper + */ + [[nodiscard]] static std::unique_ptr create(const QUrl &url); + + /** + * @brief Check if the security-scoped access was successfully acquired + * @return true if access is valid and the file can be accessed + */ + [[nodiscard]] bool isValid() const; + +private: + explicit MacSandboxSecurityScopedAccess(const QUrl &url); + + class Impl; + std::unique_ptr _impl; +}; + +} // namespace Utility +} // namespace OCC diff --git a/src/common/utility_mac_sandbox.mm b/src/common/utility_mac_sandbox.mm new file mode 100644 index 0000000000000..e620e8cd5c5e9 --- /dev/null +++ b/src/common/utility_mac_sandbox.mm @@ -0,0 +1,106 @@ +/* + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "utility_mac_sandbox.h" + +#include + +#import + +Q_LOGGING_CATEGORY(lcMacSandbox, "nextcloud.common.mac.sandbox", QtInfoMsg) + +namespace OCC { +namespace Utility { + +class MacSandboxSecurityScopedAccess::Impl +{ +public: + explicit Impl(const QUrl &url) + : _url(url) + , _nsUrl(nullptr) + , _hasAccess(false) + { + if (!url.isValid() || url.isEmpty()) { + qCWarning(lcMacSandbox) << "Invalid or empty URL provided"; + return; + } + + // Convert QUrl to NSURL + const QString localPath = url.toLocalFile(); + if (localPath.isEmpty()) { + qCWarning(lcMacSandbox) << "URL does not represent a local file:" << url; + return; + } + + @autoreleasepool { + _nsUrl = [[NSURL fileURLWithPath:localPath.toNSString()] retain]; + + if (!_nsUrl) { + qCWarning(lcMacSandbox) << "Failed to create NSURL from path:" << localPath; + return; + } + + // Start accessing the security-scoped resource + _hasAccess = [_nsUrl startAccessingSecurityScopedResource]; + + if (_hasAccess) { + qCDebug(lcMacSandbox) << "Successfully started accessing security-scoped resource:" << localPath; + } else { + qCWarning(lcMacSandbox) << "Failed to start accessing security-scoped resource:" << localPath; + } + } + } + + ~Impl() + { + @autoreleasepool { + if (_hasAccess && _nsUrl) { + [_nsUrl stopAccessingSecurityScopedResource]; + qCDebug(lcMacSandbox) << "Stopped accessing security-scoped resource"; + _hasAccess = false; + } + + if (_nsUrl) { + [_nsUrl release]; + _nsUrl = nullptr; + } + } + } + + // Non-copyable + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + + [[nodiscard]] bool hasAccess() const { return _hasAccess; } + +private: + QUrl _url; + NSURL *_nsUrl; + bool _hasAccess; +}; + +MacSandboxSecurityScopedAccess::MacSandboxSecurityScopedAccess(const QUrl &url) + : _impl(std::make_unique(url)) +{ +} + +MacSandboxSecurityScopedAccess::~MacSandboxSecurityScopedAccess() = default; + +MacSandboxSecurityScopedAccess::MacSandboxSecurityScopedAccess(MacSandboxSecurityScopedAccess&&) noexcept = default; + +MacSandboxSecurityScopedAccess& MacSandboxSecurityScopedAccess::operator=(MacSandboxSecurityScopedAccess&&) noexcept = default; + +std::unique_ptr MacSandboxSecurityScopedAccess::create(const QUrl &url) +{ + return std::unique_ptr(new MacSandboxSecurityScopedAccess(url)); +} + +bool MacSandboxSecurityScopedAccess::isValid() const +{ + return _impl && _impl->hasAccess(); +} + +} // namespace Utility +} // namespace OCC diff --git a/src/gui/addcertificatedialog.cpp b/src/gui/addcertificatedialog.cpp index 4721e2cccebe8..36bcd5364ba0b 100644 --- a/src/gui/addcertificatedialog.cpp +++ b/src/gui/addcertificatedialog.cpp @@ -8,6 +8,10 @@ #include #include +#ifdef Q_OS_MACOS +#include "common/utility_mac_sandbox.h" +#endif + namespace OCC { AddCertificateDialog::AddCertificateDialog(QWidget *parent) @@ -25,8 +29,24 @@ AddCertificateDialog::~AddCertificateDialog() void AddCertificateDialog::on_pushButtonBrowseCertificate_clicked() { - QString fileName = QFileDialog::getOpenFileName(this, tr("Select a certificate"), "", tr("Certificate files (*.p12 *.pfx)")); - ui->lineEditCertificatePath->setText(fileName); + // Use getSaveFileUrl for sandbox compatibility + const auto fileUrl = QFileDialog::getOpenFileUrl(this, tr("Select a certificate"), QUrl(), tr("Certificate files (*.p12 *.pfx)")); + + if (fileUrl.isEmpty()) { + return; + } + +#ifdef Q_OS_MACOS + // On macOS with app sandbox, we need to verify we can access the security-scoped resource + auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(fileUrl); + + if (!scopedAccess->isValid()) { + ui->labelErrorCertif->setText(tr("Could not access the selected certificate file.")); + return; + } +#endif + + ui->lineEditCertificatePath->setText(fileUrl.toLocalFile()); } QString AddCertificateDialog::getCertificatePath() diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 95ce335ddf8e3..2c7bfbf97854b 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -47,6 +47,10 @@ #include #include +#ifdef Q_OS_MACOS +#include "common/utility_mac_sandbox.h" +#endif + namespace { struct ZipEntry { QString localFilename; @@ -124,6 +128,11 @@ bool createDebugArchive(const QString &filename) KZip zip(filename); if (!zip.open(QIODevice::WriteOnly)) { + qWarning() << "Failed to open debug archive for writing:" + << filename + << "because of error:" + << zip.errorString(); + QMessageBox::critical( nullptr, QObject::tr("Failed to create debug archive"), @@ -653,22 +662,38 @@ void GeneralSettings::slotIgnoreFilesEditor() void GeneralSettings::slotCreateDebugArchive() { - const auto filename = QFileDialog::getSaveFileName( + const auto destination = QFileDialog::getSaveFileUrl( this, tr("Create Debug Archive"), QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation), tr("Zip Archives") + " (*.zip)" ); - if (filename.isEmpty()) { + if (destination.path().isEmpty()) { + return; + } + +#ifdef Q_OS_MACOS + // On macOS with app sandbox, we need to explicitly access the security-scoped resource + // that was selected by the user via the file dialog. This is required even though we have + // the com.apple.security.files.user-selected.read-write entitlement. + auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(destination); + + if (!scopedAccess->isValid()) { + QMessageBox::critical( + this, + tr("Failed to Access File"), + tr("Could not access the selected location. Please try again or choose a different location.") + ); return; } +#endif - if (createDebugArchive(filename)) { + if (createDebugArchive(destination.path())) { QMessageBox::information( this, tr("Debug Archive Created"), - tr("Redact information deemed sensitive before sharing! Debug archive created at %1").arg(filename) + tr("Redact information deemed sensitive before sharing! Debug archive created at %1").arg(destination.toString()) ); } } diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp index 78cbf4706e3c3..709af7f909a74 100644 --- a/src/gui/socketapi/socketapi.cpp +++ b/src/gui/socketapi/socketapi.cpp @@ -64,6 +64,7 @@ #ifdef Q_OS_MACOS #include +#include "common/utility_mac_sandbox.h" #endif #ifdef HAVE_KGUIADDONS @@ -1048,14 +1049,27 @@ void SocketApi::command_MOVE_ITEM(const QString &localFile, SocketListener *) // Add back the folder path defaultDirAndName = QDir(fileData.folder->path()).filePath(defaultDirAndName); - const auto target = QFileDialog::getSaveFileName( + // Use getSaveFileUrl for sandbox compatibility + const auto targetUrl = QFileDialog::getSaveFileUrl( nullptr, tr("Select new location …"), - defaultDirAndName, + QUrl::fromLocalFile(defaultDirAndName), QString(), nullptr, QFileDialog::HideNameFilterDetails); - if (target.isEmpty()) + if (targetUrl.isEmpty()) return; +#ifdef Q_OS_MACOS + // On macOS with app sandbox, we need to explicitly access the security-scoped resource + auto scopedAccess = Utility::MacSandboxSecurityScopedAccess::create(targetUrl); + + if (!scopedAccess->isValid()) { + qCWarning(lcSocketApi) << "Could not access security-scoped resource for conflict resolution:" << targetUrl; + return; + } +#endif + + const auto target = targetUrl.toLocalFile(); + ConflictSolver solver; solver.setLocalVersionFilename(localFile); solver.setRemoteVersionFilename(target); From 856454c3b555df20ed114001937e3c230d44ee20 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 09:01:02 +0100 Subject: [PATCH 16/27] fix(file-provider): App group container lookup API change in NextcloudFileProviderKit. Signed-off-by: Iva Horn --- .../FileProviderExt/FileProviderExtension.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift index 82b1cf213661d..52bfc760797ff 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift @@ -26,15 +26,16 @@ import OSLog var changeObserver: RemoteChangeObserver? var ignoredFiles: IgnoredFilesMatcher? lazy var ncKitBackground = NKBackground(nkCommonInstance: ncKit.nkCommonInstance) + lazy var socketClient: LocalSocketClient? = { - guard let containerUrl = pathForAppGroupContainer() else { + guard let containerUrl = FileManager.default.applicationGroupContainer() else { logger.fault("Won't start socket client, no container URL available!") return nil; } - let socketPath = containerUrl.appendingPathComponent( - ".fileprovidersocket", conformingTo: .archive) + let socketPath = containerUrl.appendingPathComponent(".fileprovidersocket", conformingTo: .archive) let lineProcessor = FileProviderSocketLineProcessor(delegate: self, log: log) + return LocalSocketClient(socketPath: socketPath.path, lineProcessor: lineProcessor) }() From 012a9bc140077b11c531f7d056dade335e8cd44f Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 12:11:40 +0100 Subject: [PATCH 17/27] fix(macOS): File provider path assembly for debug archive. Signed-off-by: Iva Horn --- src/gui/macOS/fileproviderutils_mac.mm | 48 ++++++++++++-------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/src/gui/macOS/fileproviderutils_mac.mm b/src/gui/macOS/fileproviderutils_mac.mm index 0e3d7cdcb25cc..1badebedfb40d 100644 --- a/src/gui/macOS/fileproviderutils_mac.mm +++ b/src/gui/macOS/fileproviderutils_mac.mm @@ -136,54 +136,52 @@ QString domainIdentifierForAccount(const OCC::AccountPtr account) return domainIdentifierForAccount(account.get()); } -QDir fileProviderExtensionContainer() +QString applicationGroupContainer() { - const auto baseBundleId = QCoreApplication::organizationDomain(); - const auto extensionBundleId = baseBundleId + QStringLiteral(".FileProviderExt"); + NSString *const groupId = (NSString *)[NSBundle.mainBundle objectForInfoDictionaryKey:@"NCFPKAppGroupIdentifier"]; - auto dir = QDir::home(); - dir.cd("Library"); - dir.cd("Containers"); - dir.cd(extensionBundleId); + if (groupId == nil) { + qCWarning(lcMacFileProviderUtils) << "No app group identifier found in Info.plist, cannot determine application group container."; + return QString::fromNSString([NSFileManager.defaultManager temporaryDirectory].path); + } - return dir; + return QString::fromNSString([NSFileManager.defaultManager containerURLForSecurityApplicationGroupIdentifier:groupId].path); } QDir fileProviderExtensionLogDirectory() { - auto dir = fileProviderExtensionContainer(); - dir.cd("Data"); - dir.cd("Library"); - dir.cd("Logs"); + auto applicationGroupContainerPath = applicationGroupContainer(); + auto logsPath = applicationGroupContainerPath + "/Library/Logs/File Provider Domains"; + auto directory = QDir(logsPath); - return dir; + return directory; } QDir fileProviderDomainLogDirectory(const QString domainIdentifier) { - auto dir = fileProviderExtensionLogDirectory(); - dir.cd(domainIdentifier); + auto logsDirectory = fileProviderExtensionLogDirectory(); + auto domainLogsPath = logsDirectory.filePath(domainIdentifier); + auto directory = QDir(domainLogsPath); - return dir; + return directory; } QDir fileProviderDomainsSupportDirectory() { - auto dir = fileProviderExtensionContainer(); - dir.cd("Data"); - dir.cd("Library"); - dir.cd("Application Support"); - dir.cd("File Provider Domains"); + auto applicationGroupContainerPath = applicationGroupContainer(); + auto supportPath = applicationGroupContainerPath + "/Library/Application Support/File Provider Domains"; + auto directory = QDir(supportPath); - return dir; + return directory; } QDir fileProviderDomainSupportDirectory(const QString domainIdentifier) { - auto dir = fileProviderDomainsSupportDirectory(); - dir.cd(domainIdentifier); + auto supportDirectory = fileProviderDomainsSupportDirectory(); + auto domainSupportPath = supportDirectory.filePath(domainIdentifier); + auto directory = QDir(domainSupportPath); - return dir; + return directory; } NSFileProviderManager *managerForDomainIdentifier(const QString &domainIdentifier) From 376a40cfc01cd31eb9b724da76f8f0eaf67c318e Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 12:11:52 +0100 Subject: [PATCH 18/27] fix(macOS): Debug archive creation in a sandbox. Signed-off-by: Iva Horn --- src/gui/generalsettings.cpp | 52 +++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 2c7bfbf97854b..04f22ad0641c1 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -125,11 +125,15 @@ bool createDebugArchive(const QString &filename) { const auto entries = createDebugArchiveFileList(); - KZip zip(filename); + // Create the ZIP archive in a temporary directory first + const auto tempDir = QDir::temp(); + const auto tempFilePath = tempDir.filePath(QStringLiteral("nextcloud-debug-archive-temp.zip")); + + KZip zip(tempFilePath); if (!zip.open(QIODevice::WriteOnly)) { qWarning() << "Failed to open debug archive for writing:" - << filename + << tempFilePath << "because of error:" << zip.errorString(); @@ -200,6 +204,50 @@ bool createDebugArchive(const QString &filename) zip.prepareWriting("_client_buildinfo.txt", {}, {}, buildInfo.size()); zip.writeData(buildInfo, buildInfo.size()); zip.finishWriting(buildInfo.size()); + + zip.close(); + + // Now move the temporary ZIP file to the desired destination + QFile tempFile(tempFilePath); + if (!tempFile.exists()) { + qWarning() << "Temporary debug archive file does not exist:" << tempFilePath; + QMessageBox::critical( + nullptr, + QObject::tr("Failed to create debug archive"), + QObject::tr("Could not create debug archive in temporary location!"), + QMessageBox::Ok + ); + return false; + } + + // Remove destination file if it already exists + if (QFile::exists(filename)) { + if (!QFile::remove(filename)) { + qWarning() << "Failed to remove existing file at destination:" << filename; + tempFile.remove(); + QMessageBox::critical( + nullptr, + QObject::tr("Failed to create debug archive"), + QObject::tr("Could not remove existing file at destination!"), + QMessageBox::Ok + ); + return false; + } + } + + // Move the temporary file to the final destination + if (!tempFile.rename(filename)) { + qWarning() << "Failed to move debug archive from" << tempFilePath << "to" << filename; + tempFile.remove(); + QMessageBox::critical( + nullptr, + QObject::tr("Failed to create debug archive"), + QObject::tr("Could not move debug archive to selected location!"), + QMessageBox::Ok + ); + return false; + } + return true; } From b3b71ea0df8340d935a8e72a0844a1093f5f766f Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 12:35:47 +0100 Subject: [PATCH 19/27] fix(macOS): Development team group identifier prefix. - Replaced "group." with DEVELOPMENT_TEAM. - Removed every occurrence of SOCKETAPI_TEAM_IDENTIFIER_PREFIX. - Removed NCFPKAppGroupIdentifier from MacOSXBundleInfo.plist.in. Signed-off-by: Iva Horn --- CMakeLists.txt | 4 ---- admin/osx/macosx.entitlements.cmake | 2 +- cmake/modules/MacOSXBundleInfo.plist.in | 2 -- config.h.in | 2 +- shell_integration/MacOSX/CMakeLists.txt | 3 --- .../FileProviderExt/FileProviderExt.entitlements | 2 +- .../MacOSX/NextcloudIntegration/FileProviderExt/Info.plist | 2 +- .../FileProviderUIExt/FileProviderUIExt.entitlements | 2 +- .../FinderSyncExt/FinderSyncExt.entitlements | 2 +- .../MacOSX/NextcloudIntegration/FinderSyncExt/Info.plist | 2 -- .../NextcloudIntegration.xcodeproj/project.pbxproj | 2 -- src/gui/macOS/fileprovidersocketserver_mac.mm | 2 +- src/gui/socketapi/socketapi_mac.mm | 2 +- 13 files changed, 8 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a9c91deae16b..c4ad9e37d4293 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -237,10 +237,6 @@ if(OWNCLOUD_5XX_NO_BLACKLIST) add_definitions(-DOWNCLOUD_5XX_NO_BLACKLIST=1) endif() -if(APPLE) - set( SOCKETAPI_TEAM_IDENTIFIER_PREFIX "" CACHE STRING "SocketApi prefix (including a following dot) that must match the codesign key's TeamIdentifier/Organizational Unit" ) -endif() - if(BUILD_CLIENT) OPTION(GUI_TESTING "Build with gui introspection features of socket api" OFF) diff --git a/admin/osx/macosx.entitlements.cmake b/admin/osx/macosx.entitlements.cmake index 2be5449c1a9b9..2480d4cb3109b 100644 --- a/admin/osx/macosx.entitlements.cmake +++ b/admin/osx/macosx.entitlements.cmake @@ -10,7 +10,7 @@ com.apple.security.application-groups - group.@APPLICATION_REV_DOMAIN@ + @DEVELOPMENT_TEAM@.@APPLICATION_REV_DOMAIN@ @DEBUG_ENTITLEMENTS@ diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in index 134cfe55cf38a..eb9f3de70e18b 100644 --- a/cmake/modules/MacOSXBundleInfo.plist.in +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -90,7 +90,5 @@ - NCFPKAppGroupIdentifier - group.@APPLICATION_REV_DOMAIN@ diff --git a/config.h.in b/config.h.in index 192790319534f..1166b8accd736 100644 --- a/config.h.in +++ b/config.h.in @@ -5,7 +5,7 @@ #cmakedefine WITH_QTKEYCHAIN 1 #cmakedefine BUILD_FILE_PROVIDER_MODULE "@BUILD_FILE_PROVIDER_MODULE@" #cmakedefine WITH_PROVIDERS "@WITH_PROVIDERS@" -#define SOCKETAPI_TEAM_IDENTIFIER_PREFIX "@SOCKETAPI_TEAM_IDENTIFIER_PREFIX@" +#define DEVELOPMENT_TEAM "@DEVELOPMENT_TEAM@" #cmakedefine THEME_CLASS @THEME_CLASS@ #cmakedefine THEME_INCLUDE @THEME_INCLUDE@ diff --git a/shell_integration/MacOSX/CMakeLists.txt b/shell_integration/MacOSX/CMakeLists.txt index 694fb3fd51c71..5311006b667bf 100644 --- a/shell_integration/MacOSX/CMakeLists.txt +++ b/shell_integration/MacOSX/CMakeLists.txt @@ -21,7 +21,6 @@ if(APPLE) "OC_OEM_SHARE_ICNS=${OC_OEM_SHARE_ICNS}" "OC_APPLICATION_NAME=${APPLICATION_NAME}" "OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}" - "OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}" COMMENT building Mac Overlay icons VERBATIM) @@ -34,7 +33,6 @@ if(APPLE) "OC_APPLICATION_VENDOR=${APPLICATION_VENDOR}" "OC_APPLICATION_NAME=${APPLICATION_NAME}" "OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}" - "OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}" COMMENT building macOS File Provider extension VERBATIM) @@ -46,7 +44,6 @@ if(APPLE) "OC_APPLICATION_VENDOR=${APPLICATION_VENDOR}" "OC_APPLICATION_NAME=${APPLICATION_NAME}" "OC_APPLICATION_REV_DOMAIN=${APPLICATION_REV_DOMAIN}" - "OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX=${SOCKETAPI_TEAM_IDENTIFIER_PREFIX}" DEPENDS mac_fileproviderplugin COMMENT building macOS File Provider UI extension VERBATIM) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExt.entitlements b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExt.entitlements index ff6803420534f..2981bf99d426e 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExt.entitlements +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExt.entitlements @@ -6,7 +6,7 @@ com.apple.security.application-groups - group.$(OC_APPLICATION_REV_DOMAIN) + $(DEVELOPMENT_TEAM).$(OC_APPLICATION_REV_DOMAIN) com.apple.security.network.client diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist index 605ed5804c1e6..faf9b44fdc224 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/Info.plist @@ -38,7 +38,7 @@ NSExtensionFileProviderDocumentGroup - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) + $(DEVELOPMENT_TEAM).$(OC_APPLICATION_REV_DOMAIN) NSExtensionFileProviderSupportsEnumeration NSExtensionPointIdentifier diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderUIExt.entitlements b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderUIExt.entitlements index ff6803420534f..2981bf99d426e 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderUIExt.entitlements +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderUIExt/FileProviderUIExt.entitlements @@ -6,7 +6,7 @@ com.apple.security.application-groups - group.$(OC_APPLICATION_REV_DOMAIN) + $(DEVELOPMENT_TEAM).$(OC_APPLICATION_REV_DOMAIN) com.apple.security.network.client diff --git a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncExt.entitlements b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncExt.entitlements index a665d757b86ec..7160407cf0217 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncExt.entitlements +++ b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncExt.entitlements @@ -6,7 +6,7 @@ com.apple.security.application-groups - group.$(OC_APPLICATION_REV_DOMAIN) + $(DEVELOPMENT_TEAM).$(OC_APPLICATION_REV_DOMAIN) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/Info.plist b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/Info.plist index 2a669ce7374d9..a2fb6bbaea129 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/Info.plist +++ b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/Info.plist @@ -37,7 +37,5 @@ NSPrincipalClass NSApplication - SocketApiPrefix - $(OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX)$(OC_APPLICATION_REV_DOMAIN) diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index 4cbbde43a44e8..0c60db4846f0b 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -1583,7 +1583,6 @@ MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; OC_OEM_SHARE_ICNS = ""; - OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; ONLY_ACTIVE_ARCH = YES; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1632,7 +1631,6 @@ MACOSX_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; OC_OEM_SHARE_ICNS = ""; - OC_SOCKETAPI_TEAM_IDENTIFIER_PREFIX = ""; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; diff --git a/src/gui/macOS/fileprovidersocketserver_mac.mm b/src/gui/macOS/fileprovidersocketserver_mac.mm index 3d9a658d41f9a..324b83558f0af 100644 --- a/src/gui/macOS/fileprovidersocketserver_mac.mm +++ b/src/gui/macOS/fileprovidersocketserver_mac.mm @@ -16,7 +16,7 @@ QString fileProviderSocketPath() { - NSString *appGroupId = [NSString stringWithFormat:@"group.%@", @APPLICATION_REV_DOMAIN]; + NSString *appGroupId = [NSString stringWithFormat:@"%@.%@", @DEVELOPMENT_TEAM, @APPLICATION_REV_DOMAIN]; NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId]; NSURL *library = [container URLByAppendingPathComponent:@"Library" isDirectory:true]; NSURL *applicationSupport = [library URLByAppendingPathComponent:@"Application Support" isDirectory:true]; diff --git a/src/gui/socketapi/socketapi_mac.mm b/src/gui/socketapi/socketapi_mac.mm index dbfb86d9d8a4a..bb89692d67a49 100644 --- a/src/gui/socketapi/socketapi_mac.mm +++ b/src/gui/socketapi/socketapi_mac.mm @@ -13,7 +13,7 @@ QString socketApiSocketPath() { - NSString *appGroupId = [NSString stringWithFormat:@"group.%@", @APPLICATION_REV_DOMAIN]; + NSString *appGroupId = [NSString stringWithFormat:@"%@.%@", @DEVELOPMENT_TEAM, @APPLICATION_REV_DOMAIN]; NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId]; NSURL *library = [container URLByAppendingPathComponent:@"Library" isDirectory:true]; NSURL *applicationSupport = [library URLByAppendingPathComponent:@"Application Support" isDirectory:true]; From c37b3866915b041eab30288ddd95ab62bf2788be Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 12:36:03 +0100 Subject: [PATCH 20/27] Work in progress: Direct and local NextcloudFileProviderKit reference. This is for working with a development branch of NextcloudFileProviderKit only while the required changes there have not been integrated yet. This reference must be removed in a follow up commit. Signed-off-by: Iva Horn --- .../project.pbxproj | 2 + .../xcshareddata/swiftpm/Package.resolved | 56 +++++++++++++++---- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index 0c60db4846f0b..e2fa63dd608a7 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -199,6 +199,7 @@ AA02B2AA2E7048C600C72B34 /* Keychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keychain.swift; sourceTree = ""; }; AA0A6B1D2E8EA94F007F4A7A /* Craft.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = Craft.sh; sourceTree = ""; }; AA27A4E32E93C0D700665051 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + AA3CA0332EC356D400AFCA0C /* nextcloudfileproviderkit */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = nextcloudfileproviderkit; path = /Users/iva/Projekte/nextcloud/nextcloudfileproviderkit; sourceTree = ""; }; AA7F17E02E7017230000E928 /* Authentication.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Authentication.storyboard; sourceTree = ""; }; AA7F17E22E70173E0000E928 /* AuthenticationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewController.swift; sourceTree = ""; }; AA7F17E62E7038340000E928 /* NSError+FileProviderErrorCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSError+FileProviderErrorCode.swift"; sourceTree = ""; }; @@ -405,6 +406,7 @@ C2B573941B1CD88000303B36 = { isa = PBXGroup; children = ( + AA3CA0332EC356D400AFCA0C /* nextcloudfileproviderkit */, AAD7F6032EAA11670071D385 /* gui */, AA27A4E32E93C0D700665051 /* README.md */, C2B573B31B1CD91E00303B36 /* desktopclient */, diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 20b8cd3f35782..3a35792e65771 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "74a3274bff88648671702b7d1dfa0c8ea5acb44280987019f6c5bb7055b848b2", + "originHash" : "d82a73daf5dd90074877cf6ec77b0c894bff74b290079c18e98ef17e1f837e25", "pins" : [ { "identity" : "alamofire", @@ -19,15 +19,6 @@ "version" : "2.4.5" } }, - { - "identity" : "nextcloudfileproviderkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/nextcloud/NextcloudFileProviderKit.git", - "state" : { - "revision" : "3aed8ec1bef3d67d2d29e50123c5d7182f49afb9", - "version" : "3.2.9" - } - }, { "identity" : "nextcloudkit", "kind" : "remoteSourceControl", @@ -64,6 +55,51 @@ "version" : "1.0.1" } }, + { + "identity" : "swift-atomics", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-atomics.git", + "state" : { + "revision" : "b601256eab081c0f92f059e12818ac1d4f178ff7", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections.git", + "state" : { + "revision" : "7b847a3b7008b2dc2f47ca3110d8c782fb2e5c7e", + "version" : "1.3.0" + } + }, + { + "identity" : "swift-nio", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-nio.git", + "state" : { + "revision" : "a24771a4c228ff116df343c85fcf3dcfae31a06c", + "version" : "2.88.0" + } + }, + { + "identity" : "swift-system", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-system.git", + "state" : { + "revision" : "395a77f0aa927f0ff73941d7ac35f2b46d47c9db", + "version" : "1.6.3" + } + }, + { + "identity" : "swiftformat", + "kind" : "remoteSourceControl", + "location" : "https://github.com/nicklockwood/SwiftFormat", + "state" : { + "revision" : "9c82dcb15307ba06795d31788d3467d9a375e3e7", + "version" : "0.58.5" + } + }, { "identity" : "swiftyjson", "kind" : "remoteSourceControl", From b5ba15ad87a2fb2a8c3ca2b27f51dce1af0ac76a Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 12:49:47 +0100 Subject: [PATCH 21/27] fix(macOS): Defined "FileProviderUIExt" as target dependency to "desktopclient". This was still missing in contrast to FileProviderExt. "desktopclient" is no the actual app for macOS but only a shallow wrapper and meta target to enable convenient build of everything in the Xcode project without building the main Qt app. Signed-off-by: Iva Horn --- .../NextcloudIntegration.xcodeproj/project.pbxproj | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index e2fa63dd608a7..b0dfbef5a186f 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -97,6 +97,13 @@ remoteGlobalIDString = 53903D0B2956164F00D0B308; remoteInfo = NCDesktopClientSocketKit; }; + AA3CA0342EC35A3B00AFCA0C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C2B573951B1CD88000303B36 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 53B9797D2B84C81F002DA742; + remoteInfo = FileProviderUIExt; + }; C2B573DF1B1CD9CE00303B36 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = C2B573951B1CD88000303B36 /* Project object */; @@ -585,6 +592,7 @@ buildRules = ( ); dependencies = ( + AA3CA0352EC35A3B00AFCA0C /* PBXTargetDependency */, C2B573E01B1CD9CE00303B36 /* PBXTargetDependency */, 538E397527F4765000FA63D5 /* PBXTargetDependency */, 53903D202956164F00D0B308 /* PBXTargetDependency */, @@ -942,6 +950,11 @@ target = 53903D0B2956164F00D0B308 /* NCDesktopClientSocketKit */; targetProxy = 53903D322956173F00D0B308 /* PBXContainerItemProxy */; }; + AA3CA0352EC35A3B00AFCA0C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 53B9797D2B84C81F002DA742 /* FileProviderUIExt */; + targetProxy = AA3CA0342EC35A3B00AFCA0C /* PBXContainerItemProxy */; + }; C2B573E01B1CD9CE00303B36 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = C2B573D61B1CD9CE00303B36 /* FinderSyncExt */; From 83af3005728833fda7e8201b794d76945406bcbc Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 13:56:26 +0100 Subject: [PATCH 22/27] fix: Define default DEVELOPMENT_TEAM for main app. Signed-off-by: Iva Horn --- NEXTCLOUD.cmake | 1 + config.h.in | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/NEXTCLOUD.cmake b/NEXTCLOUD.cmake index b0a90f8de43bc..f6733e4f4b7ca 100644 --- a/NEXTCLOUD.cmake +++ b/NEXTCLOUD.cmake @@ -31,6 +31,7 @@ set( APPLICATION_ICON_SET "SVG" ) set( APPLICATION_SERVER_URL "" CACHE STRING "URL for the server to use. If entered, the UI field will be pre-filled with it" ) set( APPLICATION_SERVER_URL_ENFORCE ON ) # If set and APPLICATION_SERVER_URL is defined, the server can only connect to the pre-defined URL set( APPLICATION_REV_DOMAIN "com.nextcloud.desktopclient" ) +set( DEVELOPMENT_TEAM "NKUJUXUJ3B" CACHE STRING "Apple Development Team ID for code signing" ) set( APPLICATION_VIRTUALFILE_SUFFIX "nextcloud" CACHE STRING "Virtual file suffix (not including the .)") set( APPLICATION_OCSP_STAPLING_ENABLED OFF ) set( APPLICATION_FORBID_BAD_SSL OFF ) diff --git a/config.h.in b/config.h.in index 1166b8accd736..3159cdb9233b5 100644 --- a/config.h.in +++ b/config.h.in @@ -5,7 +5,7 @@ #cmakedefine WITH_QTKEYCHAIN 1 #cmakedefine BUILD_FILE_PROVIDER_MODULE "@BUILD_FILE_PROVIDER_MODULE@" #cmakedefine WITH_PROVIDERS "@WITH_PROVIDERS@" -#define DEVELOPMENT_TEAM "@DEVELOPMENT_TEAM@" +#cmakedefine DEVELOPMENT_TEAM "@DEVELOPMENT_TEAM@" #cmakedefine THEME_CLASS @THEME_CLASS@ #cmakedefine THEME_INCLUDE @THEME_INCLUDE@ From fb21dc8d2e72c61c99edef4689e544d935e21989 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 14:37:34 +0100 Subject: [PATCH 23/27] fix: Restored NCFPKAppGroupIdentifier in main app Info.plist Signed-off-by: Iva Horn --- cmake/modules/MacOSXBundleInfo.plist.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmake/modules/MacOSXBundleInfo.plist.in b/cmake/modules/MacOSXBundleInfo.plist.in index eb9f3de70e18b..c8d175074efb2 100644 --- a/cmake/modules/MacOSXBundleInfo.plist.in +++ b/cmake/modules/MacOSXBundleInfo.plist.in @@ -90,5 +90,7 @@ + NCFPKAppGroupIdentifier + @DEVELOPMENT_TEAM@.@APPLICATION_REV_DOMAIN@ From c8906c68caf8fdb4f346722bb812a769576a6e67 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Tue, 11 Nov 2025 15:41:05 +0100 Subject: [PATCH 24/27] fix: Fixed too long socket paths (max 104 characters). Signed-off-by: Iva Horn --- .../FileProviderExt/FileProviderExtension.swift | 2 +- .../NextcloudIntegration/FinderSyncExt/FinderSync.m | 13 +------------ src/gui/macOS/fileprovidersocketserver.cpp | 7 ++++++- src/gui/macOS/fileprovidersocketserver_mac.mm | 4 +--- src/gui/socketapi/socketapi.cpp | 7 ++++++- src/gui/socketapi/socketapi_mac.mm | 4 +--- 6 files changed, 16 insertions(+), 21 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift index 52bfc760797ff..c0d6351a25ee0 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift +++ b/shell_integration/MacOSX/NextcloudIntegration/FileProviderExt/FileProviderExtension.swift @@ -33,7 +33,7 @@ import OSLog return nil; } - let socketPath = containerUrl.appendingPathComponent(".fileprovidersocket", conformingTo: .archive) + let socketPath = containerUrl.appendingPathComponent("fps", conformingTo: .archive) let lineProcessor = FileProviderSocketLineProcessor(delegate: self, log: log) return LocalSocketClient(socketPath: socketPath.path, lineProcessor: lineProcessor) diff --git a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m index d4621c0ea04ec..7c389f8a47263 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m +++ b/shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m @@ -45,21 +45,10 @@ - (instancetype)init [syncController setBadgeImage:warning label:@"Ignored" forBadgeIdentifier:@"IGNORE+SWM"]; [syncController setBadgeImage:error label:@"Error" forBadgeIdentifier:@"ERROR+SWM"]; - // The Mach port name needs to: - // - Be prefixed with the code signing Team ID - // - Then infixed with the sandbox App Group - // - The App Group itself must be a prefix of (or equal to) the application bundle identifier - // We end up in the official signed client with: 9B5WD74GWJ.com.owncloud.desktopclient.socket - // With ad-hoc signing (the '-' signing identity) we must drop the Team ID. - // When the code isn't sandboxed (e.g. the OC client or the legacy overlay icon extension) - // the OS doesn't seem to put any restriction on the port name, so we just follow what - // the sandboxed App Extension needs. - // https://developer.apple.com/library/mac/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24 - NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:socketApiPrefix]; NSURL *library = [container URLByAppendingPathComponent:@"Library" isDirectory:true]; NSURL *applicationSupport = [library URLByAppendingPathComponent:@"Application Support" isDirectory:true]; - NSURL *socketPath = [applicationSupport URLByAppendingPathComponent:@".socket" isDirectory:NO]; + NSURL *socketPath = [applicationSupport URLByAppendingPathComponent:@"s" isDirectory:NO]; NSLog(@"Socket path: %@", socketPath.path); diff --git a/src/gui/macOS/fileprovidersocketserver.cpp b/src/gui/macOS/fileprovidersocketserver.cpp index 7774c4a4d9b4f..d703e6f6d7b5d 100644 --- a/src/gui/macOS/fileprovidersocketserver.cpp +++ b/src/gui/macOS/fileprovidersocketserver.cpp @@ -30,9 +30,14 @@ void FileProviderSocketServer::startListening() QLocalServer::removeServer(_socketPath); const auto serverStarted = _socketServer.listen(_socketPath); + if (!serverStarted) { qCWarning(lcFileProviderSocketServer) << "Could not start file provider socket server" - << _socketPath; + << _socketPath + << "Error:" + << _socketServer.errorString() + << "Error code:" + << _socketServer.serverError(); } else { qCInfo(lcFileProviderSocketServer) << "File provider socket server started, listening" << _socketPath; diff --git a/src/gui/macOS/fileprovidersocketserver_mac.mm b/src/gui/macOS/fileprovidersocketserver_mac.mm index 324b83558f0af..c051cfead1c63 100644 --- a/src/gui/macOS/fileprovidersocketserver_mac.mm +++ b/src/gui/macOS/fileprovidersocketserver_mac.mm @@ -18,9 +18,7 @@ QString fileProviderSocketPath() { NSString *appGroupId = [NSString stringWithFormat:@"%@.%@", @DEVELOPMENT_TEAM, @APPLICATION_REV_DOMAIN]; NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId]; - NSURL *library = [container URLByAppendingPathComponent:@"Library" isDirectory:true]; - NSURL *applicationSupport = [library URLByAppendingPathComponent:@"Application Support" isDirectory:true]; - NSURL *socket = [applicationSupport URLByAppendingPathComponent:@".fileprovidersocket" isDirectory:false]; + NSURL *socket = [container URLByAppendingPathComponent:@"fps" isDirectory:false]; return QString::fromNSString(socket.path); } diff --git a/src/gui/socketapi/socketapi.cpp b/src/gui/socketapi/socketapi.cpp index 709af7f909a74..be25e03bc8e1d 100644 --- a/src/gui/socketapi/socketapi.cpp +++ b/src/gui/socketapi/socketapi.cpp @@ -298,7 +298,12 @@ SocketApi::SocketApi(QObject *parent) } } if (!_localServer.listen(socketPath)) { - qCWarning(lcSocketApi) << "can't start server" << socketPath; + qCWarning(lcSocketApi) << "can't start server" + << socketPath + << "Error:" + << _localServer.errorString() + << "Error code:" + << _localServer.serverError(); } else { qCInfo(lcSocketApi) << "server started, listening at " << socketPath; } diff --git a/src/gui/socketapi/socketapi_mac.mm b/src/gui/socketapi/socketapi_mac.mm index bb89692d67a49..87f3d718f8b03 100644 --- a/src/gui/socketapi/socketapi_mac.mm +++ b/src/gui/socketapi/socketapi_mac.mm @@ -15,9 +15,7 @@ QString socketApiSocketPath() { NSString *appGroupId = [NSString stringWithFormat:@"%@.%@", @DEVELOPMENT_TEAM, @APPLICATION_REV_DOMAIN]; NSURL *container = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId]; - NSURL *library = [container URLByAppendingPathComponent:@"Library" isDirectory:true]; - NSURL *applicationSupport = [library URLByAppendingPathComponent:@"Application Support" isDirectory:true]; - NSURL *socket = [applicationSupport URLByAppendingPathComponent:@".socket" isDirectory:false]; + NSURL *socket = [container URLByAppendingPathComponent:@"s" isDirectory:false]; return QString::fromNSString(socket.path); } From d72db428718bcecdd4e52a88d23a6e14235f6c4a Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 12 Nov 2025 08:35:39 +0100 Subject: [PATCH 25/27] chore(macOS): Made whole "src" instead of just "gui" folder accessible conveniently from Xcode. Signed-off-by: Iva Horn --- .../NextcloudIntegration.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj index b0dfbef5a186f..9f89280ac4ab7 100644 --- a/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj +++ b/shell_integration/MacOSX/NextcloudIntegration/NextcloudIntegration.xcodeproj/project.pbxproj @@ -232,7 +232,7 @@ /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ - AAD7F6032EAA11670071D385 /* gui */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); name = gui; path = /Users/iva/Projekte/nextcloud/desktop/src/gui; sourceTree = ""; }; + AA3CB33F2EC383C300AFCA0C /* src */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); name = src; path = /Users/iva/Projekte/nextcloud/desktop/src; sourceTree = ""; }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -414,8 +414,8 @@ isa = PBXGroup; children = ( AA3CA0332EC356D400AFCA0C /* nextcloudfileproviderkit */, - AAD7F6032EAA11670071D385 /* gui */, AA27A4E32E93C0D700665051 /* README.md */, + AA3CB33F2EC383C300AFCA0C /* src */, C2B573B31B1CD91E00303B36 /* desktopclient */, AA0A6B1C2E8EA948007F4A7A /* NextcloudDev */, C2B573D81B1CD9CE00303B36 /* FinderSyncExt */, From b2ef402fce03a0fce15710caf14649c1c125b6f1 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 12 Nov 2025 10:15:41 +0100 Subject: [PATCH 26/27] fix(macOS): Synchronization folder selection. - Force an initial folder selection on folder wizard appearance. - Turn the path text field into read only to force a change through the open folder dialog as required by the sandbox. - On macOS, look up the actual user's home directory instead of the sandboxed container. - Removed initial value for path text field in folder wizard because it was pointing into the sandbox container and also obsolete due to the forced folder selection on appearance. Signed-off-by: Iva Horn --- src/common/utility_mac_sandbox.h | 11 ++++++++ src/common/utility_mac_sandbox.mm | 8 ++++++ src/gui/folderwizard.cpp | 45 ++++++++++++++++++++++--------- src/gui/folderwizard.h | 4 +++ src/gui/folderwizardsourcepage.ui | 6 ++++- 5 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/common/utility_mac_sandbox.h b/src/common/utility_mac_sandbox.h index 38aff1c7f725f..7efe65f4040be 100644 --- a/src/common/utility_mac_sandbox.h +++ b/src/common/utility_mac_sandbox.h @@ -66,5 +66,16 @@ class MacSandboxSecurityScopedAccess std::unique_ptr _impl; }; +/** + * @brief Get the real user home directory path + * + * In sandboxed macOS apps, QStandardPaths::HomeLocation returns the sandbox + * container directory, not the actual user home directory. This function uses + * NSHomeDirectory() to retrieve the real home directory path. + * + * @return The real user home directory path + */ +[[nodiscard]] QString getRealHomeDirectory(); + } // namespace Utility } // namespace OCC diff --git a/src/common/utility_mac_sandbox.mm b/src/common/utility_mac_sandbox.mm index e620e8cd5c5e9..67629fc9e9213 100644 --- a/src/common/utility_mac_sandbox.mm +++ b/src/common/utility_mac_sandbox.mm @@ -102,5 +102,13 @@ explicit Impl(const QUrl &url) return _impl && _impl->hasAccess(); } +QString getRealHomeDirectory() +{ + @autoreleasepool { + NSString *homeDir = NSHomeDirectory(); + return QString::fromNSString(homeDir); + } +} + } // namespace Utility } // namespace OCC diff --git a/src/gui/folderwizard.cpp b/src/gui/folderwizard.cpp index 3dc10ad198578..9aed5dfbdf952 100644 --- a/src/gui/folderwizard.cpp +++ b/src/gui/folderwizard.cpp @@ -16,6 +16,10 @@ #include "wizard/owncloudwizard.h" #include "common/asserts.h" +#ifdef Q_OS_MACOS +#include "common/utility_mac_sandbox.h" +#endif + #include #include #include @@ -78,9 +82,6 @@ FolderWizardLocalPath::FolderWizardLocalPath(const AccountPtr &account) QUrl serverUrl = _account->url(); serverUrl.setUserName(_account->credentials()->user()); - QString defaultPath = QDir::homePath() + QLatin1Char('/') + Theme::instance()->appName(); - defaultPath = FolderMan::instance()->findGoodPathForNewSyncFolder(defaultPath, serverUrl, FolderMan::GoodPathStrategy::AllowOnlyNewPath); - _ui.localFolderLineEdit->setText(QDir::toNativeSeparators(defaultPath)); _ui.localFolderLineEdit->setToolTip(tr("Enter the path to the local folder.")); _ui.warnLabel->setTextFormat(Qt::RichText); @@ -94,6 +95,12 @@ FolderWizardLocalPath::~FolderWizardLocalPath() = default; void FolderWizardLocalPath::initializePage() { _ui.warnLabel->hide(); + + // Automatically trigger folder selection dialog on first appearance + if (_initialFolderSelection) { + // Use QTimer to defer the dialog until the page is fully shown + QTimer::singleShot(0, this, &FolderWizardLocalPath::slotChooseLocalFolder); + } } void FolderWizardLocalPath::cleanupPage() @@ -130,16 +137,16 @@ bool FolderWizardLocalPath::isComplete() const void FolderWizardLocalPath::slotChooseLocalFolder() { - QString sf = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); - QDir d(sf); - - // open the first entry of the home dir. Otherwise the dir picker comes - // up with the closed home dir icon, stupid Qt default... - QStringList dirs = d.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::NoSymLinks, - QDir::DirsFirst | QDir::Name); + const bool isInitialSelection = _initialFolderSelection; + QString sf; - if (dirs.count() > 0) - sf += "/" + dirs.at(0); // Take the first dir in home dir. + #ifdef Q_OS_MACOS + // On macOS with app sandbox, QStandardPaths returns the sandbox container directory, + // not the actual user home directory. Use NSHomeDirectory() to get the real path. + sf = Utility::getRealHomeDirectory(); + #else + sf = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); + #endif QString dir = QFileDialog::getExistingDirectory(this, tr("Select the source folder"), @@ -147,7 +154,15 @@ void FolderWizardLocalPath::slotChooseLocalFolder() QFileDialog::ShowDirsOnly); if (!dir.isEmpty()) { // set the last directory component name as alias - _ui.localFolderLineEdit->setText(QDir::toNativeSeparators(dir)); + const QString pathWithAppName = dir + QLatin1Char('/') + Theme::instance()->appName(); + _ui.localFolderLineEdit->setText(QDir::toNativeSeparators(pathWithAppName)); + _initialFolderSelection = false; + } else { + // If this was the initial folder selection and the user canceled, + // emit signal to close the wizard + if (isInitialSelection) { + emit initialFolderSelectionCanceled(); + } } emit completeChanged(); } @@ -689,6 +704,10 @@ FolderWizard::FolderWizard(AccountPtr account, QWidget *parent) setWindowTitle(tr("Add Folder Sync Connection")); setOptions(QWizard::CancelButtonOnLeft); setButtonText(QWizard::FinishButton, tr("Add Sync Connection")); + + // Close the wizard if initial folder selection is canceled + connect(_folderWizardSourcePage, &FolderWizardLocalPath::initialFolderSelectionCanceled, + this, &FolderWizard::reject); } FolderWizard::~FolderWizard() = default; diff --git a/src/gui/folderwizard.h b/src/gui/folderwizard.h index 43d03e00250cc..239c052ef61c1 100644 --- a/src/gui/folderwizard.h +++ b/src/gui/folderwizard.h @@ -53,6 +53,9 @@ class FolderWizardLocalPath : public FormatWarningsWizardPage void setFolderMap(const Folder::Map &fm) { _folderMap = fm; } +signals: + void initialFolderSelectionCanceled(); + protected: void changeEvent(QEvent *) override; @@ -65,6 +68,7 @@ protected slots: Ui_FolderWizardSourcePage _ui{}; Folder::Map _folderMap; AccountPtr _account; + bool _initialFolderSelection = true; }; diff --git a/src/gui/folderwizardsourcepage.ui b/src/gui/folderwizardsourcepage.ui index 94af3645a594f..af73c80a131e5 100644 --- a/src/gui/folderwizardsourcepage.ui +++ b/src/gui/folderwizardsourcepage.ui @@ -36,7 +36,11 @@ - + + + true + + From 1534ff2f68e9887adb7d35f9a771578af410b3e3 Mon Sep 17 00:00:00 2001 From: Iva Horn Date: Wed, 12 Nov 2025 10:52:13 +0100 Subject: [PATCH 27/27] fix(file-provider): Consolidated support and log directories. Signed-off-by: Iva Horn --- src/gui/generalsettings.cpp | 39 ++++++-------------------- src/gui/macOS/fileproviderutils.h | 5 ---- src/gui/macOS/fileproviderutils_mac.mm | 15 ++-------- 3 files changed, 12 insertions(+), 47 deletions(-) diff --git a/src/gui/generalsettings.cpp b/src/gui/generalsettings.cpp index 04f22ad0641c1..23c0a95a3ebce 100644 --- a/src/gui/generalsettings.cpp +++ b/src/gui/generalsettings.cpp @@ -152,46 +152,25 @@ bool createDebugArchive(const QString &filename) } #ifdef BUILD_FILE_PROVIDER_MODULE - qDebug() << "Trying to add file provider domain log files..."; - const auto fileProviderExtensionLogDirectory = OCC::Mac::FileProviderUtils::fileProviderExtensionLogDirectory(); - - if (fileProviderExtensionLogDirectory.exists()) { - // Recursively add all files from the container log directory - QDirIterator it(fileProviderExtensionLogDirectory.path(), QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); - - while (it.hasNext()) { - const auto logFilePath = it.next(); - - // Calculate relative path from the base container log directory - const auto relativePath = fileProviderExtensionLogDirectory.relativeFilePath(logFilePath); - const auto zipPath = QStringLiteral("File Provider Domains/%1").arg(relativePath); - - zip.addLocalFile(logFilePath, zipPath); - } - - qDebug() << "Added file provider domain log files from" << fileProviderExtensionLogDirectory.path(); - } else { - qWarning() << "file provider domain container log directory not found at" << fileProviderExtensionLogDirectory.path(); - } - - qDebug() << "Trying to add file provider database files..."; + qDebug() << "Trying to add file provider domain database and log files..."; const auto fileProviderDomainsSupportDirectory = OCC::Mac::FileProviderUtils::fileProviderDomainsSupportDirectory(); if (fileProviderDomainsSupportDirectory.exists()) { - QDirIterator it(fileProviderDomainsSupportDirectory.path(), QStringList() << "*.realm", QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); + // Recursively add all files from the container log directory + QDirIterator it(fileProviderDomainsSupportDirectory.path(), QStringList() << "*.jsonl" << "*.realm", QDir::Files | QDir::NoDotAndDotDot, QDirIterator::Subdirectories); while (it.hasNext()) { - const auto dbFilePath = it.next(); + const auto filePath = it.next(); - // Calculate relative path from the base domains support directory - const auto relativePath = fileProviderDomainsSupportDirectory.relativeFilePath(dbFilePath); + // Calculate relative path from the base container log directory + const auto relativePath = fileProviderDomainsSupportDirectory.relativeFilePath(filePath); const auto zipPath = QStringLiteral("File Provider Domains/%1").arg(relativePath); - zip.addLocalFile(dbFilePath, zipPath); - qDebug() << "Added file provider domain database file from" << dbFilePath; + zip.addLocalFile(filePath, zipPath); + qDebug() << "Added file from" << filePath; } } else { - qWarning() << "file provider domains support directory not found at" << fileProviderDomainsSupportDirectory.path(); + qWarning() << "file provider domain container log directory not found at" << fileProviderDomainsSupportDirectory.path(); } #endif diff --git a/src/gui/macOS/fileproviderutils.h b/src/gui/macOS/fileproviderutils.h index 90eb547949913..132f5cbc468ab 100644 --- a/src/gui/macOS/fileproviderutils.h +++ b/src/gui/macOS/fileproviderutils.h @@ -103,11 +103,6 @@ QString domainIdentifierForAccountIdentifier(const NSString *accountId); */ bool illegalDomainIdentifier(const QString &domainId); -/** - * @brief Find the logs directory of the file provider extension for all the domains. - */ -QDir fileProviderExtensionLogDirectory(); - /** * @brief Find the logs directory of the file provider domain with the given identifier. */ diff --git a/src/gui/macOS/fileproviderutils_mac.mm b/src/gui/macOS/fileproviderutils_mac.mm index 1badebedfb40d..e7f026c6445c6 100644 --- a/src/gui/macOS/fileproviderutils_mac.mm +++ b/src/gui/macOS/fileproviderutils_mac.mm @@ -148,19 +148,10 @@ QString applicationGroupContainer() return QString::fromNSString([NSFileManager.defaultManager containerURLForSecurityApplicationGroupIdentifier:groupId].path); } -QDir fileProviderExtensionLogDirectory() -{ - auto applicationGroupContainerPath = applicationGroupContainer(); - auto logsPath = applicationGroupContainerPath + "/Library/Logs/File Provider Domains"; - auto directory = QDir(logsPath); - - return directory; -} - QDir fileProviderDomainLogDirectory(const QString domainIdentifier) { - auto logsDirectory = fileProviderExtensionLogDirectory(); - auto domainLogsPath = logsDirectory.filePath(domainIdentifier); + auto logsDirectory = fileProviderDomainSupportDirectory(domainIdentifier); + auto domainLogsPath = logsDirectory.filePath("Logs"); auto directory = QDir(domainLogsPath); return directory; @@ -169,7 +160,7 @@ QDir fileProviderDomainLogDirectory(const QString domainIdentifier) QDir fileProviderDomainsSupportDirectory() { auto applicationGroupContainerPath = applicationGroupContainer(); - auto supportPath = applicationGroupContainerPath + "/Library/Application Support/File Provider Domains"; + auto supportPath = applicationGroupContainerPath + "/File Provider Domains"; auto directory = QDir(supportPath); return directory;