Fix Anisette sync logic
Dadoum

Dadoum commited on 2026-09-28 01:52:24
Showing 3 changed files, with 42 additions and 12 deletions.

... ...
@@ -131,6 +131,7 @@ pub enum AuthOutcome {
131 131
     SecondaryActionRequired(Option<ServerProvidedData>, String),
132 132
     AnisetteResyncRequired(Vec<u8>),
133 133
     AnisetteReprovisionRequired,
134
+    /// Opaque `idata` to pass to `url_switch` with the original bag lookup URL.
134 135
     UrlSwitchingRequired(String),
135 136
 }
136 137
 
... ...
@@ -271,7 +271,7 @@ pub async fn sync_machine(
271 271
         .ok_or(SyncError::URLNotFound)?;
272 272
 
273 273
     let sync_machine_request_builder =
274
-        http_session.simple_request_builder(Method::GET, mid_sync_machine_url);
274
+        http_session.simple_request_builder(Method::POST, mid_sync_machine_url);
275 275
 
276 276
     let mut sync_machine_request_body = Vec::<u8>::new();
277 277
     plist::to_writer_xml(&mut sync_machine_request_body, &sync_machine_request_plist)
... ...
@@ -283,6 +283,8 @@ pub async fn sync_machine(
283 283
         .send()
284 284
         .await
285 285
         .map_err(SyncError::Network)?
286
+        .error_for_status()
287
+        .map_err(SyncError::Network)?
286 288
         .bytes()
287 289
         .await
288 290
         .map_err(SyncError::Network)?;
... ...
@@ -290,13 +292,38 @@ pub async fn sync_machine(
290 292
     let sync_machine_response_plist: Dictionary =
291 293
         plist::from_bytes(&sync_machine_response).map_err(SyncError::Parsing)?;
292 294
 
293
-    sync_machine_response_plist
295
+    let response = sync_machine_response_plist
294 296
         .get("Response")
295
-        .and_then(|response| response.as_dictionary())
296
-        .and_then(|response| response.get("Status"))
297
-        .and_then(|response| response.as_dictionary())
298
-        .map(|status| parse_status(status).map_err(SyncError::Apple))
299
-        .ok_or(SyncError::ResponseStructure(
300
-            sync_machine_response_plist.clone(),
301
-        ))?
297
+        .and_then(Value::as_dictionary)
298
+        .ok_or_else(|| SyncError::ResponseStructure(sync_machine_response_plist.clone()))?;
299
+    let status = response
300
+        .get("Status")
301
+        .and_then(Value::as_dictionary)
302
+        .ok_or_else(|| SyncError::ResponseStructure(sync_machine_response_plist.clone()))?;
303
+    let code = status
304
+        .get("ec")
305
+        .and_then(Value::as_signed_integer)
306
+        .ok_or_else(|| SyncError::ResponseStructure(sync_machine_response_plist.clone()))?;
307
+    if code != 0 {
308
+        return Err(SyncError::Apple(AppleError {
309
+            code,
310
+            message: status
311
+                .get("em")
312
+                .and_then(Value::as_string)
313
+                .unwrap_or("Unknown synchronization error")
314
+                .to_owned(),
315
+        }));
316
+    }
317
+
318
+    if let Some(routing_info) = response.get("X-Apple-I-MD-RINFO") {
319
+        let routing_info = routing_info
320
+            .as_unsigned_integer()
321
+            .or_else(|| routing_info.as_string()?.parse().ok())
322
+            .ok_or_else(|| SyncError::ResponseStructure(sync_machine_response_plist.clone()))?;
323
+        adi_proxy
324
+            .set_idms_routing(GRANDSLAM_DSID, routing_info)
325
+            .map_err(SyncError::ADI)?;
326
+    }
327
+
328
+    Ok(())
302 329
 }
... ...
@@ -29,14 +29,16 @@ impl Display for URLSwitchingError {
29 29
 
30 30
 impl Error for URLSwitchingError {}
31 31
 
32
+/// Fetches a replacement URL bag from the original bag lookup URL.
33
+/// `idata` is the opaque data from `AuthOutcome::UrlSwitchingRequired`, not a URL.
32 34
 pub async fn url_switch(
33 35
     http_session: &mut HTTPSession<'_>,
34
-    url: &str,
36
+    bag_lookup_url: &str,
35 37
     idata: &str,
36 38
 ) -> Result<(), URLSwitchingError> {
37 39
     let url_bag_response = http_session
38
-        .request_builder(Method::GET, url)
39
-        .query(&[("idata", idata.to_string())])
40
+        .request_builder(Method::GET, bag_lookup_url)
41
+        .query(&[("idata", idata)])
40 42
         .send()
41 43
         .await
42 44
         .map_err(URLSwitchingError::Reqwest)?
43 45