Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ cmake_minimum_required(VERSION 3.18)
include(VERSION.cmake)
project(OpenCloudDesktop LANGUAGES CXX VERSION ${MIRALL_VERSION_MAJOR}.${MIRALL_VERSION_MINOR}.${MIRALL_VERSION_PATCH})

if(APPLE)
enable_language(OBJCXX)
endif()

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

Expand Down Expand Up @@ -104,8 +108,14 @@ option(WITH_EXTERNAL_BRANDING "A URL to an external branding repo" "")
# specify additional vfs plugins
set(VIRTUAL_FILE_SYSTEM_PLUGINS off cfapi openvfs CACHE STRING "Name of internal plugin in src/libsync/vfs or the locations of virtual file plugins")

# On macOS 12+ (Darwin 21+), add the NSFileProvider-based VFS plugin
if(APPLE AND CMAKE_SYSTEM_VERSION VERSION_GREATER_EQUAL "21.0")
list(APPEND VIRTUAL_FILE_SYSTEM_PLUGINS nsfp)
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" )
set( APPLE_DEVELOPMENT_TEAM "" CACHE STRING "Apple Development Team ID used for code signing and App Group identifiers" )
endif()


Expand Down
5 changes: 5 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ endif()

add_subdirectory(plugins)

# On macOS 12+ (Darwin 21+), build the File Provider App Extension for Files On Demand
if(APPLE AND CMAKE_SYSTEM_VERSION VERSION_GREATER_EQUAL "21.0")
add_subdirectory(extensions/fileprovider)
endif()

install(EXPORT ${APPLICATION_SHORTNAME}Config DESTINATION "${KDE_INSTALL_CMAKEPACKAGEDIR}/${APPLICATION_SHORTNAME}" NAMESPACE OpenCloud::)

ecm_setup_version(PROJECT
Expand Down
6 changes: 6 additions & 0 deletions src/cmd/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ apply_common_target_settings(cmd)


if(APPLE)
# NSFileProvider diagnostic command (VOD-027)
enable_language(OBJCXX)
target_sources(cmd PRIVATE nsfpdiagnostic.mm nsfpdiagnostic.h)
target_compile_options(cmd PRIVATE -fobjc-arc)
target_link_libraries(cmd "-framework Foundation" "-framework FileProvider")

set_target_properties(cmd PROPERTIES RUNTIME_OUTPUT_DIRECTORY "$<TARGET_FILE_DIR:opencloud>")
else()
install(TARGETS cmd ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
Expand Down
15 changes: 15 additions & 0 deletions src/cmd/cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@

#include <zlib.h>

#if defined(Q_OS_MACOS)
#include "cmd/nsfpdiagnostic.h"
#endif


using namespace OCC;

Expand Down Expand Up @@ -311,6 +315,11 @@ CmdOptions parseOptions(const QStringList &app_args)
const auto testCrashReporter =
addOption({{QStringLiteral("crash")}, QStringLiteral("Crash the client to test the crash reporter")}, QCommandLineOption::HiddenFromHelp);

#if defined(Q_OS_MACOS)
auto dumpNsfpDomainsOption =
addOption({{QStringLiteral("dump-nsfp-domains")}, QStringLiteral("Dump all registered NSFileProvider domains and exit (macOS only)")});
#endif

auto verbosityOption = addOption({{QStringLiteral("verbose")},
QStringLiteral("Specify the [verbosity]\n0: no logging (default)\n"
"1: general logging\n"
Expand All @@ -331,6 +340,12 @@ CmdOptions parseOptions(const QStringList &app_args)

parser.process(app_args);

#if defined(Q_OS_MACOS)
// Handle --dump-nsfp-domains early: no server URL or credentials needed.
if (parser.isSet(dumpNsfpDomainsOption)) {
exit(OCC::dumpNSFileProviderDomains());
}
#endif

const int verbosity = parser.value(verbosityOption).toInt();
if (verbosity >= 0 && verbosity <= 3) {
Expand Down
127 changes: 127 additions & 0 deletions src/extensions/fileprovider/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# CMake build configuration for the macOS File Provider App Extension.
# Builds an .appex bundle implementing NSFileProviderReplicatedExtension
# for Files On Demand support on macOS 12+.

if(APPLE)
enable_language(OBJCXX)

# Generate entitlements with the configured App Group identifier.
set(APP_GROUP_IDENTIFIER "${APPLE_DEVELOPMENT_TEAM}.${APPLICATION_REV_DOMAIN}")
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/OpenCloudFileProvider.entitlements"
"${CMAKE_CURRENT_BINARY_DIR}/OpenCloudFileProvider.entitlements"
@ONLY
)

set(FILEPROVIDER_EXTENSION_SOURCES
OpenCloudFileProviderExtension.mm
FileProviderXPCService.mm
FileProviderItem.mm
FileProviderEnumerator.mm
FileProviderThumbnails.mm
)

set(FILEPROVIDER_EXTENSION_HEADERS
OpenCloudFileProviderExtension.h
FileProviderXPCService.h
FileProviderItem.h
FileProviderEnumerator.h
FileProviderThumbnails.h
)

# Must be add_executable (not add_library MODULE) so the Mach-O filetype
# is MH_EXECUTE (2) rather than MH_BUNDLE (8). macOS requires app
# extensions to be executables for sandbox entitlements to be embedded.
add_executable(OpenCloudFileProviderExtension
${FILEPROVIDER_EXTENSION_SOURCES}
${FILEPROVIDER_EXTENSION_HEADERS}
)

# Mark as an App Extension bundle (.appex)
set_target_properties(OpenCloudFileProviderExtension PROPERTIES
BUNDLE_EXTENSION "appex"
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in"
MACOSX_BUNDLE_BUNDLE_NAME "OpenCloudFileProvider"
MACOSX_BUNDLE_BUNDLE_VERSION "${MIRALL_VERSION_FULL}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${MIRALL_VERSION}"
MACOSX_BUNDLE_GUI_IDENTIFIER "${APPLICATION_REV_DOMAIN}.fileprovider"
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_BINARY_DIR}/OpenCloudFileProvider.entitlements"
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "$(CODE_SIGN_IDENTITY)"
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${APPLE_DEVELOPMENT_TEAM}"
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${APPLICATION_REV_DOMAIN}.fileprovider"
XCODE_ATTRIBUTE_ENABLE_HARDENED_RUNTIME YES
)

# Pass the App Group identifier to source code.
target_compile_definitions(OpenCloudFileProviderExtension PRIVATE
APP_GROUP_IDENTIFIER="${APP_GROUP_IDENTIFIER}"
)

# App Extension compile flag: restricts API surface to extension-safe APIs
target_compile_options(OpenCloudFileProviderExtension PRIVATE
-fapplication-extension
-fobjc-arc
)

# Link against required Apple frameworks
target_link_libraries(OpenCloudFileProviderExtension PRIVATE
"-framework CoreGraphics"
"-framework Foundation"
"-framework FileProvider"
"-framework UniformTypeIdentifiers"
)

# Linker flag for extension-safe linking
target_link_options(OpenCloudFileProviderExtension PRIVATE
-fapplication-extension
-e _NSExtensionMain
)

# Set deployment target to macOS 12+ (minimum for NSFileProviderReplicatedExtension)
set_target_properties(OpenCloudFileProviderExtension PROPERTIES
XCODE_ATTRIBUTE_MACOSX_DEPLOYMENT_TARGET "12.0"
)

# Embed the extension in the main application bundle under PlugIns/
# XCODE_EMBED_APP_EXTENSIONS only works with Xcode generator.
# For Ninja/Makefile generators, use a post-build copy step.
if(TARGET ${APPLICATION_EXECUTABLE})
if(CMAKE_GENERATOR STREQUAL "Xcode")
set_target_properties(${APPLICATION_EXECUTABLE} PROPERTIES
XCODE_EMBED_APP_EXTENSIONS OpenCloudFileProviderExtension
)
else()
add_custom_command(TARGET OpenCloudFileProviderExtension POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"$<TARGET_BUNDLE_DIR:OpenCloudFileProviderExtension>"
"$<TARGET_FILE_DIR:${APPLICATION_EXECUTABLE}>/../PlugIns/OpenCloudFileProviderExtension.appex"
COMMENT "Embedding FileProviderExtension.appex into app bundle"
)
# Sign the extension with entitlements (XCODE_ATTRIBUTE_* only works
# with the Xcode generator, so Ninja/Make need an explicit codesign).
if(DEFINED CODESIGN_IDENTITY AND NOT CODESIGN_IDENTITY STREQUAL "")
add_custom_command(TARGET OpenCloudFileProviderExtension POST_BUILD
COMMAND codesign --force --sign "${CODESIGN_IDENTITY}"
--entitlements "${CMAKE_CURRENT_SOURCE_DIR}/OpenCloudFileProvider.entitlements"
"$<TARGET_BUNDLE_DIR:OpenCloudFileProviderExtension>"
COMMAND codesign --force --sign "${CODESIGN_IDENTITY}"
--entitlements "${CMAKE_CURRENT_SOURCE_DIR}/OpenCloudFileProvider.entitlements"
"$<TARGET_FILE_DIR:${APPLICATION_EXECUTABLE}>/../PlugIns/OpenCloudFileProviderExtension.appex"
COMMENT "Code-signing FileProviderExtension with sandbox entitlements"
)
endif()
endif()
endif()

# Notarisation: After archiving, run `xcrun notarytool submit <archive> --apple-id ...`
# to notarise the signed application bundle. This is handled by the release pipeline,
# not as part of the CMake build. Hardened Runtime (set above) is required for
# successful notarisation.

# Install extension into the app bundle PlugIns directory
install(TARGETS OpenCloudFileProviderExtension
RUNTIME DESTINATION "${KDE_INSTALL_BUNDLEDIR}/${APPLICATION_NAME}.app/Contents/PlugIns"
BUNDLE DESTINATION "${KDE_INSTALL_BUNDLEDIR}/${APPLICATION_NAME}.app/Contents/PlugIns"
)
endif()
26 changes: 26 additions & 0 deletions src/extensions/fileprovider/FileProviderEnumerator.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// FileProviderEnumerator -- NSFileProviderEnumerator implementation that serves
// directory listings to the macOS File Provider system via XPC.
#pragma once

#import <FileProvider/FileProvider.h>
#import <Foundation/Foundation.h>

@class FileProviderXPCService;

NS_ASSUME_NONNULL_BEGIN

/// Enumerates items within a given container (directory) for the File Provider
/// framework. Obtains item listings from the main application via XPC since
/// the extension runs in a separate process without direct sync journal access.
API_AVAILABLE(macos(12.0))
@interface FileProviderEnumerator : NSObject <NSFileProviderEnumerator>

/// Designated initializer.
/// @param containerId The identifier of the container to enumerate
/// (e.g. root container or a folder's file ID).
/// @param service The XPC service used to communicate with the main app.
- (instancetype)initWithContainerIdentifier:(NSFileProviderItemIdentifier)containerId xpcService:(FileProviderXPCService *)service;

@end

NS_ASSUME_NONNULL_END
Loading