Commit Graph
19 Commits
Author SHA1 Message Date
Nick Craig-Wood a64c0a0fde build: modernize with "go fix -rangeint": use range over int 2026-08-21 12:23:31 +01:00
Nick Craig-Wood 53d9f7f956 build: modernize with "go fix -newexpr": use go1.26 new(expr)
Also inline and remove the now unneeded pointer helper functions.
2026-08-21 12:23:31 +01:00
Nick Craig-Wood bfd0e3f3c2 build: modernize with "go fix -mapsloop": use maps.Copy 2026-08-21 12:23:31 +01:00
Christian De SantisandGitHub aba403fa79 iclouddrive: fix uploads into an app container failing with 412 - fixes #9729
Writing any file into a third-party app container - the Obsidian, Pages or
Shortcuts folders that iCloud Drive shows alongside your own - failed with

    HTTP error 412 (412 Precondition Failed) returned body:
    "{ ... \"error_code\" : \"VALIDATING_REFERENCE_ERROR\", \"reason\" :
    \"Request has out of order children to be chained but the parents were
    missing\" }"

Reading from those paths worked, and so did creating directories in them, so
the failure looked like a missing parent when the parent was plainly there.

Items in an app container live in a different zone from ordinary iCloud Drive
folders: a folder under Documents has a drive ID like
FOLDER::com.apple.CloudDocs::<uuid>, while the Obsidian container has
FOLDER::iCloud.md.obsidian::documents#o2v. DownloadFile already accounts for
this - it deconstructs the item's own ID and addresses the zone it finds - but
CreateUpload and UpdateFile hardcoded defaultZone, and UpdateFile built the
resulting Drivewsid with a hardcoded com.apple.CloudDocs as well.

So rclone asked Apple to chain the new document to a parent in
com.apple.CloudDocs while the parent lived in iCloud.md.obsidian. The parent
really was missing from the zone being addressed, which is what the error said.

Take the zone from the parent's drive ID instead, the same way the download
path does, and build the new item's ID with ConstructDriveID. Uploads outside
an app container are unaffected: their parents are in com.apple.CloudDocs, so
the derived zone is the value that was previously hardcoded.

Verified against a real remote: files now upload into an Obsidian vault inside
the container and read back correctly with an unpatched binary afterwards.
2026-08-11 21:17:13 +01:00
731f2a6c29 iclouddrive: fix 2FA failing with 409 even when the code is valid
Since around mid-2026 Apple's idmsa endpoints `POST
/verify/trusteddevice/securitycode` and `POST /verify/phone/securitycode`
return HTTP 409 (instead of 2xx) even when the submitted code is accepted:
the response body carries `"securityCode": {..., "valid": true}` and the
response headers include a fresh X-Apple-Session-Token, scnt and
X-Apple-Auth-Attributes, which are only issued on successful validation.

rclone treated any 409 as failure and aborted before TrustSession, so
configuring an iclouddrive remote always failed after the 2FA step with:

    validate2FACode failed: HTTP error 409 (409 ) returned body:
    "{... \"securityCode\": {\"code\": \"...\", \"valid\": true} ...}"

Treat a 409 response that carries X-Apple-Session-Token as success: absorb
the session headers and continue to TrustSession. Applies to both the
trusted-device and SMS validation paths.

Fixes #9488
Closes #9534

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 20:25:57 +01:00
Nick Craig-Wood 5e9b809a82 iclouddrive: fix "cannot unmarshal number" error when listing photo albums
CloudKit is inconsistent about how it encodes the isDeleted field on
album records, returning a JSON boolean (true/false) for some accounts
and a number (0/1) for others. The numeric form caused listing of a
photo library to fail with:

    json: cannot unmarshal number into Go struct field
    ckBoolField.records.fields.isDeleted.value of type bool

The encoding also varies over time, not just per account: a full HTTP
dump from the reporting user showed the server sending

    "isDeleted" : { "value" : 0, "type" : "INT64" }

but the same account later reverted to the boolean encoding with no
client change. Asset records already deliver isDeleted as a number, so
both encodings are in active use server side and either may appear.

Accept both encodings when parsing CloudKit boolean fields.

See: https://forum.rclone.org/t/error-when-trying-to-list-contents-of-primarysync-directory-in-icloud-photos/54028
2026-07-21 15:18:23 +01:00
Nick Craig-Wood ffd36fc2d7 iclouddrive: fix "Index has invalid data" error listing iCloud Photos
Accounts with shared-album zones (named CMM-*) returned these zones in
the photos changes/database listing alongside the real photo
libraries. rclone treated them as libraries and queried their albums,
but these zones have no CPLAlbumByPositionLive index so the query
failed with BAD_REQUEST / "Index has invalid data", aborting the whole
listing.

Only PrimarySync and SharedSync-* zones are photo libraries, so skip
any other zone during discovery.

See: https://forum.rclone.org/t/sync-to-icloud-gives-index-has-invalid-data-errors/53852
2026-06-01 17:11:23 +01:00
Yakov TillandNick Craig-Wood 2dbad62a11 iclouddrive: fix ADP/PCS cookie acquisition for iCloud Drive 2026-05-31 10:14:07 +01:00
caltechustcandNick Craig-Wood be3bfe2d19 refactor: use slices.Contains to simplify code
Signed-off-by: caltechustc <caltechustc@outlook.com>
2026-05-24 19:57:31 +01:00
Yakov TillandGitHub d0c469c3c0 iclouddrive: add read only iCloud Photos support and SRP authentication
Add read-only iCloud Photos support to the existing iclouddrive
backend via `service = photos` config option.

Also includes auth improvements on top of #9209's SRP authentication.

**Photos features:**
- 3-level hierarchy: libraries (Personal + Shared Photo Library) →
  albums → photos/videos
- server-side smart albums (All Photos, Videos, Favorites,
  Screenshots, Live, Bursts, Panoramas, Slo-mo, Time-lapse, Portrait,
  Long Exposure, Animated, Hidden, Recently Deleted)
- User-created albums and nested album folders
- Live Photo `.MOV` companions as first-class entries
- Edited photo versions (`-edited` suffix) and RAW alternatives
- Duplicate filename dedup for camera counter wrap collisions
- Parallel cold listing for large albums
- Delta sync via CloudKit `changes/zone` - warm listings near-instant from disk cache
- Disk cache (libraries, albums, photos) with atomic writes for crash safety
- `ChangeNotify` support for FUSE mounts via `changes/zone` polling
- `ListR` support for `--fast-list` and recursive operations
- `--metadata` support - width, height, added-time, favorite, hidden
- Fresh download URLs per file - no stale URL failures on long copies
- FUSE mount documentation with recommended flags

**Auth improvements over #9209:**
- SMS 2FA fallback for users without trusted Apple devices
- Explicit push notification request - fixes iOS/macOS 26.4+ where 409
  no longer auto-pushes
- Thread safety for concurrent FUSE callers (mutexes on session and client state)
- Session endpoint caching - skips ~5s `/validate` round-trip on warm start
- `Disconnect` support - clears auth state + disk cache
- PCS cookie support for Advanced Data Protection accounts, including
  trusted-device approval for PCS cookies

Built on @coughlanio's Photos PoC (Closes #8734) and @mikegillan's SRP auth (#9209).

Fixes #7982
Co-authored-by: Chris Coughlan <chris@coughlan.io>
2026-04-27 16:55:31 +01:00
Mike GIllanandNick Craig-Wood 4a00a4dc4b iclouddrive: lowercase Apple ID for SRP authentication
Apple IDs are case-insensitive, but the SRP proof computation (M1)
hashes the username client-side. The old plaintext signin let the
server normalize the case, but with SRP the client must match.
Lowercase the Apple ID before use so mixed-case IDs authenticate
correctly.

Reported-by: ArturKlauser
2026-04-02 17:52:56 +01:00
XiangzheandNick Craig-Wood 2610beb18d iclouddrive: use dynamic origin for SRP auth headers
This fixes China mainland iCloud authentication by deriving the Origin
and Referer headers from authEndpoint instead of hardcoding idmsa.apple.com.

Fixes compatibility with PR #8818 (China region support) and PR #9209
(SRP authentication).

Signed-off-by: Xiangzhe <xiangzhedev@gmail.com>
2026-04-02 17:52:56 +01:00
Mike GIllanandNick Craig-Wood 35e4f60548 iclouddrive: replace plaintext signin with SRP authentication
Apple has deprecated the legacy /appleauth/auth/signin endpoint and
now blocks it, causing "Invalid Session Token" errors for all users
when their trust token expires. The browser login flow now requires
SRP (Secure Remote Password), a cryptographic handshake that never
transmits the password.

Replace Session.SignIn() with a multi-step SRP-6a flow:
1. authStart - initialize session at /authorize/signin
2. authFederate - submit account name to /federate
3. authSRPInit - exchange client public value for salt/B at /signin/init
4. authSRPComplete - send M1/M2 proofs to /signin/complete

The SRP implementation uses the RFC 5054 2048-bit group with SHA-256
and Apple's NoUserNameInX variant. Password derivation supports both
s2k and s2k_fo protocols via SHA-256 + PBKDF2.

The 2FA and trust token flow is unchanged. Auth headers for all
idmsa.apple.com requests now include X-Apple-Auth-Attributes,
X-Apple-Frame-Id, and use Origin/Referer of https://idmsa.apple.com.

Fixes #8587
2026-04-02 17:52:56 +01:00
Nick Craig-Wood b2866f0291 build: modernize Go code with go fix for go1.25 2026-02-18 12:11:52 +00:00
Clément WehrungandGitHub 9d55b2411f iclouddrive: fix panic and files potentially downloaded twice
- Fixing SIGSEGV Fixes #8211
- Removed files potentially downloaded twice
2025-05-07 18:00:33 +01:00
Ben AlexandNick Craig-Wood 4f1fc1a84e iclouddrive: fix so created files are writable
At present any created file (eg through the touch command, copy, mount
etc) is read-only in iCloud.

This has been reported by users at
https://forum.rclone.org/t/icloud-and-file-editing-permissions/50659.
2025-04-10 11:38:38 +01:00
Nick Craig-Wood 401cf81034 build: modernize Go usage
This commit modernizes Go usage. This was done with:

go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

Then files needed to be `go fmt`ed and a few comments needed to be
restored.

The modernizations include replacing

- if/else conditional assignment by a call to the built-in min or max functions added in go1.21
- sort.Slice(x, func(i, j int) bool) { return s[i] < s[j] } by a call to slices.Sort(s), added in go1.21
- interface{} by the 'any' type added in go1.18
- append([]T(nil), s...) by slices.Clone(s) or slices.Concat(s), added in go1.21
- loop around an m[k]=v map update by a call to one of the Collect, Copy, Clone, or Insert functions from the maps package, added in go1.21
- []byte(fmt.Sprintf...) by fmt.Appendf(nil, ...), added in go1.19
- append(s[:i], s[i+1]...) by slices.Delete(s, i, i+1), added in go1.21
- a 3-clause for i := 0; i < n; i++ {} loop by for i := range n {}, added in go1.22
2025-02-28 11:31:14 +00:00
luzpazandNick Craig-Wood e0d477804b docs: fix various typos
Found via `codespell -q 3 -S "./docs/static,./fs/rc/params_test.go" -L aadd,afile,alledges,bbefore,bu,buda,copys,couldn,crashers,crypted,ddelete,deriver,failre,goup,hashin,hel,inbraces,keep-alives,ket,medias,ment,mis,nd,nin,notin,ois,ot,parth,re-use,re-using,responser,rin,sav,splited,streamin,synching,te,twoo,ue,unknwon,wasn`
2025-02-19 20:30:44 +00:00
lostb1tandNick Craig-Wood 51db76fd47 Add ICloud Drive backend 2024-10-02 10:19:11 +01:00