Skip to content

Commit 8a5551e

Browse files
author
restot
committed
copy .md files
1 parent a290bcc commit 8a5551e

2 files changed

Lines changed: 297 additions & 0 deletions

File tree

PROGRESS.md

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
# OpenCloud macOS Extensions – Progress
2+
3+
## Current Status
4+
- FileProviderExt: **Phase 3 In Progress** – WebDAV client + database added, XPC auth debugging ⚙️
5+
- FinderSyncExt: **Phase 1 Complete** – Unix socket IPC working, extension enabled ✅
6+
- App version: 3.1.6
7+
8+
## Implementation Progress
9+
10+
### Phase 1: Fix FinderSyncExt with Unix Domain Socket ✅ COMPLETE
11+
**Goal**: Restore badge icons and context menus
12+
13+
| Task | Status | Notes |
14+
|------|--------|-------|
15+
| 1.1 Create LocalSocketClient (Obj-C) | ✅ Done | Async Unix socket client with auto-reconnect |
16+
| 1.2 Create FinderSyncSocketLineProcessor | ✅ Done | Line processor for command parsing |
17+
| 1.3 Modify Main App Socket Server | ✅ Done | QLocalServer at App Group container path |
18+
| 1.4 Update FinderSyncExt | ✅ Done | Integrated LocalSocketClient, removed XPC |
19+
| 1.5 Add App Group Entitlements | ✅ Done | Main app + extension entitlements |
20+
| 1.6 Finder Integration UI | ✅ Done | Settings button + first-launch prompt |
21+
| 1.7 Sandbox FinderSyncExt | ✅ Done | Required for pluginkit registration |
22+
23+
### Phase 2: FileProvider Account Integration ✅ COMPLETE
24+
**Goal**: Account-aware domains with XPC communication
25+
26+
| Task | Status | Notes |
27+
|------|-----------|-------|
28+
| 2.1 Account-Aware DomainManager | ✅ Done | Domains per account with UUID identifiers |
29+
| 2.2 ClientCommunicationProtocol | ✅ Done | Obj-C protocol for XPC interface |
30+
| 2.3 ClientCommunicationService | ✅ Done | NSFileProviderServiceSource in extension |
31+
| 2.4 FileProviderXPC Client | ✅ Done | Main app connects via NSFileProviderManager.getService() |
32+
| 2.5 FileProvider Coordinator | ✅ Done | FileProvider singleton manages domain manager + XPC |
33+
| 2.6 Account Lifecycle | ✅ Done | Domains created/removed on account add/remove |
34+
35+
### Phase 3: Real File Operations ⚙️ IN PROGRESS
36+
**Goal**: On-demand file download like iCloud
37+
38+
| Task | Status | Notes |
39+
|------|-----------|-------|
40+
| 3.1 WebDAV Client | ✅ Done | WebDAVClient.swift with PROPFIND, GET, PUT, DELETE, MKCOL |
41+
| 3.2 Item Database | ✅ Done | SQLite-based ItemDatabase.swift + ItemMetadata.swift |
42+
| 3.3 WebDAV XML Parser | ✅ Done | WebDAVXMLParser.swift parses PROPFIND responses |
43+
| 3.4 XPC Auth Flow | ✅ Done | Main app sends OAuth token (1283 chars), extension XPC connected |
44+
| 3.5 Bundle ID Fix | ✅ Done | Standardized to eu.opencloud.desktop everywhere |
45+
| 3.6 NSFileProviderServicing | ✅ Done | Added protocol for XPC service discovery |
46+
| 3.7 Sandbox Entitlement | ✅ Done | Required for extension to launch |
47+
| 3.8 Real File Enumeration | ⚙️ In Progress | Wire WebDAV to enumerator (credentials received) |
48+
| 3.9 On-Demand Download | ⬜ Not Started | fetchContents with WebDAV GET |
49+
| 3.10 Upload Handling | ⬜ Not Started | createItem, modifyItem with WebDAV PUT |
50+
51+
### Phase 4: Full VFS Features
52+
**Goal**: Complete iCloud-like experience
53+
54+
| Task | Status | Notes |
55+
|------|-----------|-------|
56+
| 4.1 Download States | ⬜ Not Started | Cloud-only, downloading, downloaded |
57+
| 4.2 Eviction (Offloading) | ⬜ Not Started | Like iCloud "Optimize Mac Storage" |
58+
| 4.3 Progress Reporting | ⬜ Not Started | NSProgress integration |
59+
60+
## What Works
61+
62+
### FileProviderExt
63+
- Built and bundled via CMake alongside FinderSyncExt
64+
- Domain auto-registers on startup (`FileProviderDomainManager`) and appears at `~/Library/CloudStorage/desktopclient-OpenCloud/`
65+
- XPC service discovery working via NSFileProviderServicing protocol
66+
- Main app sends OAuth access token (1283 chars) to extension via XPC
67+
- Extension is sandboxed with correct App Group (<APPLE_TEAM_ID>.eu.opencloud.desktop)
68+
- Enumeration returns demo items (needs WebDAV wiring)
69+
- `fetchContents` ready for WebDAV integration
70+
- macOS 26 compatibility: required `NSFileProviderReplicatedExtension` methods implemented; `NSExtensionFileProviderSupportsEnumeration` set
71+
72+
### FinderSyncExt
73+
- Extension loads and can be enabled in System Settings → Extensions
74+
- IPC migration complete (NSConnection → NSXPCConnection) - but XPC approach failed
75+
76+
### Domain Registration & Cleanup (Nov 30, 2025)
77+
- Problem: Finder showed orphaned OpenCloud locations from previous builds (stale FileProvider domains).
78+
- Fix: Implemented `FileProviderDomainManager::removeAllDomains()` to remove all UUID-based domains owned by the app (skips system domains like iCloud).
79+
- Added CLI flag to host app: `--clear-fileprovider-domains` to perform cleanup without starting full UI.
80+
- Result: Orphaned OpenCloud domain removed; `~/Library/CloudStorage/` no longer contains stale OpenCloud folders.
81+
82+
## Critical Discovery (Nov 30, 2025)
83+
84+
### NSXPCListenerEndpoint Cannot Be Serialized to File
85+
**Problem:** `NSXPCListenerEndpoint` throws exception when archived with `NSKeyedArchiver`:
86+
```
87+
Caught exception during archival: *** -[NSXPCListenerEndpoint encodeWithCoder:]:
88+
This class may only be encoded by an NSXPCCoder.
89+
```
90+
91+
**Root cause:** Apple restricts `NSXPCListenerEndpoint` to only be serialized over XPC connections themselves. You cannot write it to a file for another process to read.
92+
93+
**Implication:** Our approach of writing endpoint to `~/Library/Application Support/OpenCloud/<service>.endpoint` is fundamentally flawed.
94+
95+
### Solution: Follow Nextcloud's Architecture
96+
97+
Analyzed `nextcloud-desktop` repo which has working macOS FileProvider + FinderSync:
98+
99+
#### 1. FinderSyncExt → Main App: Unix Domain Socket
100+
Nextcloud uses **Unix domain sockets** (not XPC) for FinderSync communication:
101+
- Socket file in App Group container: `~/Library/Group Containers/<TEAM>.<id>/.socket`
102+
- `LocalSocketClient.m` - async socket client using `dispatch_source`
103+
- Main app creates socket server, extension connects as client
104+
- Line-based protocol (same as our existing socket API)
105+
106+
#### 2. Main App → FileProviderExt: NSFileProviderServiceSource
107+
Nextcloud uses Apple's built-in **FileProvider Service** mechanism:
108+
- Extension exposes `NSFileProviderServiceSource` (e.g., `ClientCommunicationService.swift`)
109+
- Main app connects via `NSFileProviderManager.getServiceWithName()`
110+
- XPC connection is managed by the system – no manual endpoint passing!
111+
112+
## Architecture
113+
114+
```
115+
Main App Extensions
116+
┌─────────────────────┐ ┌─────────────────────┐
117+
│ SocketApi │←─Unix Socket──│ FinderSyncExt │
118+
│ (badges/menus) │ │ LocalSocketClient │
119+
├─────────────────────┤ ├─────────────────────┤
120+
│ FileProviderXPC │←─System XPC───│ FileProviderExt │
121+
│ (via NSFileProvider │ │ NSFileProvider │
122+
│ Manager) │ │ ServiceSource │
123+
└─────────────────────┘ └─────────────────────┘
124+
│ │
125+
└──────────── App Group Container ─────┘
126+
~/Library/Group Containers/
127+
<TEAM>.eu.opencloud.desktop/
128+
```
129+
130+
### Bundle Structure
131+
```
132+
OpenCloud.app
133+
├── Contents/MacOS/OpenCloud # Host app (registers FileProvider domain)
134+
└── Contents/PlugIns
135+
├── FileProviderExt.appex # FileProvider (VFS)
136+
└── FinderSyncExt.appex # Badges + context menus
137+
```
138+
139+
### Key Files
140+
- `src/gui/macOS/fileprovider*.mm` – FileProvider coordinator, domain manager, XPC client
141+
- `shell_integration/.../FileProviderExt/*.swift` – extension implementation
142+
- `shell_integration/.../FileProviderExt/Services/*` – ClientCommunicationService XPC
143+
- `shell_integration/.../FinderSyncExt/*.m` – FinderSync socket client
144+
- `src/gui/socketapi/socketapisocket_mac.mm` – Unix socket server
145+
146+
## Files to Create/Modify
147+
148+
### New Files (Phase 1)
149+
| Path | Purpose |
150+
|------|---------|
151+
| `shell_integration/.../FinderSyncExt/LocalSocketClient.h` | Socket client header |
152+
| `shell_integration/.../FinderSyncExt/LocalSocketClient.m` | Async Unix socket client |
153+
| `shell_integration/.../FinderSyncExt/LineProcessor.h` | Protocol for line processing |
154+
| `shell_integration/.../FinderSyncExt/FinderSyncSocketLineProcessor.h` | Line processor header |
155+
| `shell_integration/.../FinderSyncExt/FinderSyncSocketLineProcessor.m` | FinderSync message handler |
156+
| `src/gui/socketapi/socketapi_mac.mm` | Unix socket path utility |
157+
| `src/gui/OpenCloud.entitlements` | Main app entitlements |
158+
159+
### Modified Files (Phase 1)
160+
| Path | Changes |
161+
|------|---------|
162+
| `shell_integration/.../FinderSyncExt/FinderSync.m` | Replace XPC with LocalSocketClient |
163+
| `shell_integration/.../FinderSyncExt/FinderSyncExt.entitlements` | Add App Group |
164+
| `src/gui/socketapi/socketapisocket_mac.mm` | Change to Unix socket server |
165+
| `shell_integration/MacOSX/CMakeLists.txt` | Add LocalSocketClient to build |
166+
167+
## Useful Commands
168+
```bash
169+
# Verify extensions
170+
pluginkit -m -v | rg -i "eu.opencloud.desktop"
171+
172+
# Check FileProvider domain
173+
fileproviderctl dump | rg -A5 "OpenCloud|eu.opencloud.desktop"
174+
175+
# Clean all app FileProvider domains (useful after identifier/schema changes)
176+
~/Documents/craft/macos-clang-arm64/Applications/KDE/OpenCloud.app/Contents/MacOS/OpenCloud --clear-fileprovider-domains
177+
178+
# Finder logs
179+
log stream --predicate 'process CONTAINS "FinderSyncExt"' --level debug
180+
181+
# Main app socket logs
182+
log stream --predicate 'process CONTAINS "OpenCloud" AND subsystem CONTAINS "socket"' --level debug
183+
```
184+
185+
## Recent Commits
186+
- a0b776612 – docs: Update WARP.md to use rg for build filtering and add git clang-format commands
187+
- 22dd53930 – macOS: Add --clear-fileprovider-domains CLI and domain cleanup API
188+
- 346db296 – FinderSyncExt: remove app sandbox for local dev
189+
- ee8b53f64 – Integrate FileProviderExt + FinderSyncExt into core app (CMake), register domain
190+
- 75c3c44d6 – XPC server: anonymous listener + endpoint file
191+
- 22f0470ff – XPC client: read endpoint from file
192+
- a8be59ab7 – XPC endpoint serialization fix (non-secure coding)
193+
194+
## Reference Implementation
195+
Based on [Nextcloud Desktop Client](https://github.com/nextcloud/desktop).
196+
197+
**Local copy:** `../nextcloud-desktop/`
198+
199+
Key files:
200+
- `shell_integration/MacOSX/NextcloudIntegration/NCDesktopClientSocketKit/LocalSocketClient.m`
201+
- `shell_integration/MacOSX/NextcloudIntegration/NCDesktopClientSocketKit/LineProcessor.h`
202+
- `shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSyncSocketLineProcessor.m`
203+
- `shell_integration/MacOSX/NextcloudIntegration/FinderSyncExt/FinderSync.m`
204+
- `src/gui/socketapi/socketapi_mac.mm`
205+
206+
## Dependencies
207+
- **macOS 26+ (Tahoe)** - No backwards compatibility needed
208+
- App Group capability in developer account
209+
- Code signing with team identifier (or ad-hoc for dev)

plan.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
Phase 3: Real File Operations for FileProvider
2+
Problem Statement
3+
The FileProviderExt currently returns hardcoded demo items. We need to implement real file operations that:
4+
Enumerate files from the OpenCloud server using WebDAV PROPFIND
5+
Download files on-demand when accessed (like iCloud)
6+
Upload new/modified files back to the server
7+
Current State
8+
Phase 2 complete: FileProvider domains per account, XPC for credentials
9+
Extension receives serverUrl, username, password via setupDomainAccount()
10+
Extension has demo FileProviderItem and FileProviderEnumerator implementations
11+
Main app has working WebDAV code in libsync (PropfindJob, GETFileJob, etc.)
12+
Proposed Changes
13+
3.1 WebDAV Client for Extension (Swift)
14+
Create a Swift WebDAV client in the extension since we can't use the C++ libsync code directly.
15+
Files:
16+
FileProviderExt/WebDAV/WebDAVClient.swift - HTTP client for WebDAV operations
17+
FileProviderExt/WebDAV/WebDAVItem.swift - Parsed PROPFIND response item
18+
FileProviderExt/WebDAV/WebDAVXMLParser.swift - XML parser for multistatus responses
19+
Key operations:
20+
listDirectory(path:) → PROPFIND Depth 1
21+
downloadFile(path:, to:) → GET with progress
22+
uploadFile(from:, to:) → PUT
23+
createDirectory(path:) → MKCOL
24+
deleteItem(path:) → DELETE
25+
moveItem(from:, to:) → MOVE
26+
3.2 Item Database/Cache
27+
Store enumerated items locally for fast access and change tracking.
28+
Files:
29+
FileProviderExt/Database/ItemDatabase.swift - SQLite wrapper for item metadata
30+
FileProviderExt/Database/ItemMetadata.swift - Database model
31+
Schema:
32+
CREATE TABLE items (
33+
identifier TEXT PRIMARY KEY,
34+
parent_identifier TEXT,
35+
filename TEXT,
36+
remote_path TEXT,
37+
etag TEXT,
38+
content_type TEXT,
39+
size INTEGER,
40+
creation_date REAL,
41+
modification_date REAL,
42+
is_downloaded INTEGER DEFAULT 0
43+
);
44+
3.3 Real FileProviderItem
45+
Update FileProviderItem.swift to:
46+
Initialize from ItemMetadata (database) or WebDAVItem (server response)
47+
Map server ETag to itemVersion
48+
Report correct isDownloaded state (false = cloud icon, true = local copy exists)
49+
Use server remote path hash as itemIdentifier
50+
3.4 Real FileProviderEnumerator
51+
Update FileProviderEnumerator.swift to:
52+
On enumerateItems(): fetch from WebDAV if stale, return cached items
53+
On enumerateChanges(): compare server ETags with cached, report changes
54+
Signal enumerator on authentication changes
55+
3.5 Real fetchContents (Download)
56+
Update FileProviderExtension.swift fetchContents() to:
57+
Look up item's remote path from database
58+
Create temp file in extension's container
59+
Download via WebDAV GET with progress reporting
60+
Mark item as downloaded in database
61+
Return downloaded file URL
62+
3.6 Real createItem/modifyItem (Upload)
63+
createItem:
64+
If folder: MKCOL to create directory
65+
If file: PUT content to server
66+
Do PROPFIND to get server-assigned ETag/metadata
67+
Store in database, return updated item
68+
modifyItem:
69+
If content changed: PUT new content
70+
If renamed/moved: MOVE on server
71+
Update database with new metadata
72+
3.7 Real deleteItem
73+
Look up remote path
74+
DELETE via WebDAV
75+
Remove from database
76+
Implementation Order
77+
WebDAV client + XML parser (foundation for all operations)
78+
Item database (needed for identifier↔path mapping)
79+
FileProviderItem from database/WebDAV
80+
Enumerator with real listing
81+
fetchContents (download)
82+
createItem/modifyItem/deleteItem (upload/modify)
83+
Testing
84+
Manual: Add account, check Finder sidebar shows server files
85+
Manual: Double-click file → downloads and opens
86+
Manual: Drag file into folder → uploads
87+
Manual: Rename/delete in Finder → reflected on server
88+

0 commit comments

Comments
 (0)