From d27b8d4adc91f5f3747776e7d09bad862ce1f94c Mon Sep 17 00:00:00 2001 From: bokket <3100563328@qq.com> Date: Fri, 19 Jan 2024 20:44:28 +0800 Subject: [PATCH 1/8] fix(service/icloud):Missing 'X-APPLE-WEBAUTH-USER cookie' and URL not securely initialized Signed-off-by: bokket <3100563328@qq.com> --- core/src/services/icloud/core.rs | 77 +++++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/core/src/services/icloud/core.rs b/core/src/services/icloud/core.rs index dcf6525f73b7..e234028681dd 100644 --- a/core/src/services/icloud/core.rs +++ b/core/src/services/icloud/core.rs @@ -17,6 +17,7 @@ use async_trait::async_trait; use bytes::{Buf, Bytes}; +use std::collections::BTreeMap; use std::fmt::{Debug, Formatter}; use std::sync::Arc; @@ -68,6 +69,7 @@ pub struct SessionData { scnt: Option, account_country: Option, + cookies: BTreeMap, drivews_url: String, docws_url: String, @@ -81,6 +83,7 @@ impl SessionData { session_token: None, scnt: None, account_country: None, + cookies: Default::default(), drivews_url: String::new(), docws_url: String::new(), } @@ -110,9 +113,18 @@ impl Debug for IcloudSigner { } impl IcloudSigner { - /// Get the session data from signer. - pub fn session_data(&self) -> &SessionData { - &self.data + /// Get the drivews_url from signer session data. + /// Async await init finish. + pub async fn drivews_url(&mut self) -> Result { + self.init().await?; + Ok(self.data.drivews_url.clone()) + } + + /// Get the docws_url from signer session data. + /// Async await init finish. + pub async fn docws_url(&mut self) -> Result { + self.init().await?; + Ok(self.data.docws_url.clone()) } /// iCloud will use our oauth state as client id. @@ -141,6 +153,7 @@ impl IcloudSigner { if resp.status() != StatusCode::OK { return Err(parse_error(resp).await?); } + if let Some(rscd) = resp.headers().get(APPLE_RESPONSE_HEADER) { let status_code = StatusCode::from_bytes(rscd.as_bytes()).unwrap(); if status_code != StatusCode::CONFLICT { @@ -169,6 +182,9 @@ impl IcloudSigner { return Err(parse_error(resp).await?); } + // Updata SessionData cookies.We need obtain `X-APPLE-WEBAUTH-USER` cookie to get file. + self.update(&resp)?; + let bs = resp.into_body().bytes().await?; let auth_info: IcloudWebservicesResponse = serde_json::from_slice(&bs).map_err(new_json_deserialize_error)?; @@ -224,6 +240,21 @@ impl IcloudSigner { ); } + if !self.data.cookies.is_empty() { + let cookies: Vec = self + .data + .cookies + .iter() + .map(|(k, v)| format!("{}={}", k, v)) + .collect(); + headers.insert( + header::COOKIE, + cookies.as_slice().join("; ").parse().unwrap(), + ); + + println!("cookies {:?}", cookies); + } + for (key, value) in AUTH_HEADERS { headers.insert(key, build_header_value(value)?); } @@ -249,6 +280,18 @@ impl IcloudSigner { self.data.scnt = Some(scnt.to_string()); } + for (key, value) in resp.headers() { + if key == header::SET_COOKIE { + if let Some(cookie) = value.to_str().unwrap().split(';').next() { + if let Some((key, value)) = cookie.split_once('=') { + self.data + .cookies + .insert(String::from(key), String::from(value)); + } + } + } + } + Ok(()) } @@ -269,7 +312,6 @@ impl IcloudSigner { self.sign(&mut req)?; let resp = self.client.send(req).await?; - self.update(&resp)?; Ok(resp) } @@ -295,10 +337,9 @@ impl IcloudCore { pub async fn get_root(&self, id: &str) -> Result { let mut signer = self.signer.lock().await; - let uri = format!( - "{}/retrieveItemDetailsInFolders", - signer.session_data().drivews_url - ); + let drivews_url = signer.drivews_url().await?; + + let uri = format!("{}/retrieveItemDetailsInFolders", drivews_url); let body = serde_json::to_vec(&json!([ { @@ -331,11 +372,11 @@ impl IcloudCore { ) -> Result> { let mut signer = self.signer.lock().await; + let docws_url = signer.docws_url().await?; + let uri = format!( "{}/ws/{}/download/by_id?document_id={}", - signer.session_data().drivews_url, - zone, - id + docws_url, zone, id ); let req = Request::get(uri) @@ -452,10 +493,9 @@ impl PathQuery for IcloudPathQuery { async fn query(&self, parent_id: &str, name: &str) -> Result> { let mut signer = self.signer.lock().await; - let uri = format!( - "{}/retrieveItemDetailsInFolders", - signer.session_data().drivews_url - ); + let drivews_url = signer.drivews_url().await?; + + let uri = format!("{}/retrieveItemDetailsInFolders", drivews_url); let body = serde_json::to_vec(&json!([ { @@ -489,9 +529,12 @@ impl PathQuery for IcloudPathQuery { async fn create_dir(&self, parent_id: &str, name: &str) -> Result { let mut signer = self.signer.lock().await; - let client_id = signer.client_id(); + let clone = signer.clone(); + let client_id = clone.client_id(); + + let drivews_url = signer.drivews_url().await?; - let uri = format!("{}/createFolders", signer.session_data().drivews_url); + let uri = format!("{}/createFolders", drivews_url); let body = serde_json::to_vec(&json!( { "destinationDrivewsId": parent_id, From aabad8582b8845f3a6650475a0ef47c0ded1ba0f Mon Sep 17 00:00:00 2001 From: bokket <3100563328@qq.com> Date: Fri, 19 Jan 2024 20:47:57 +0800 Subject: [PATCH 2/8] remove Signed-off-by: bokket <3100563328@qq.com> --- core/src/services/icloud/core.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/services/icloud/core.rs b/core/src/services/icloud/core.rs index e234028681dd..52481842ae17 100644 --- a/core/src/services/icloud/core.rs +++ b/core/src/services/icloud/core.rs @@ -251,8 +251,6 @@ impl IcloudSigner { header::COOKIE, cookies.as_slice().join("; ").parse().unwrap(), ); - - println!("cookies {:?}", cookies); } for (key, value) in AUTH_HEADERS { From f631dcf976dae297b2f0deba7a9b5e90ac5324a5 Mon Sep 17 00:00:00 2001 From: bokket <3100563328@qq.com> Date: Sat, 20 Jan 2024 00:26:21 +0800 Subject: [PATCH 3/8] change init logic Signed-off-by: bokket <3100563328@qq.com> --- core/src/services/icloud/core.rs | 152 ++++++++++++------------------- 1 file changed, 59 insertions(+), 93 deletions(-) diff --git a/core/src/services/icloud/core.rs b/core/src/services/icloud/core.rs index 52481842ae17..e3b7b0c9d56e 100644 --- a/core/src/services/icloud/core.rs +++ b/core/src/services/icloud/core.rs @@ -17,7 +17,6 @@ use async_trait::async_trait; use bytes::{Buf, Bytes}; -use std::collections::BTreeMap; use std::fmt::{Debug, Formatter}; use std::sync::Arc; @@ -69,8 +68,6 @@ pub struct SessionData { scnt: Option, account_country: Option, - cookies: BTreeMap, - drivews_url: String, docws_url: String, } @@ -83,7 +80,6 @@ impl SessionData { session_token: None, scnt: None, account_country: None, - cookies: Default::default(), drivews_url: String::new(), docws_url: String::new(), } @@ -128,79 +124,81 @@ impl IcloudSigner { } /// iCloud will use our oauth state as client id. - pub fn client_id(&self) -> &str { + pub fn client_id(&mut self) -> &str { &self.data.oauth_state } async fn init(&mut self) -> Result<()> { // Sign the auth endpoint first. - let uri = format!("{}/signin?isRememberMeEnable=true", AUTH_ENDPOINT); - let body = serde_json::to_vec(&json!({ - "accountName" : self.apple_id, - "password" : self.password, - "rememberMe": true, - "trustTokens": [self.trust_token.clone().unwrap()], - })) - .map_err(new_json_serialize_error)?; - - let mut req = Request::post(uri) - .header(header::CONTENT_TYPE, "application/json") - .body(AsyncBody::Bytes(Bytes::from(body))) - .map_err(new_request_build_error)?; - self.sign(&mut req)?; - - let resp = self.client.send(req).await?; - if resp.status() != StatusCode::OK { - return Err(parse_error(resp).await?); - } - - if let Some(rscd) = resp.headers().get(APPLE_RESPONSE_HEADER) { - let status_code = StatusCode::from_bytes(rscd.as_bytes()).unwrap(); - if status_code != StatusCode::CONFLICT { + if !self.initiated { + let uri = format!("{}/signin?isRememberMeEnable=true", AUTH_ENDPOINT); + let body = serde_json::to_vec(&json!({ + "accountName" : self.apple_id, + "password" : self.password, + "rememberMe": true, + "trustTokens": [self.trust_token.clone().unwrap()], + })) + .map_err(new_json_serialize_error)?; + + let mut req = Request::post(uri) + .header(header::CONTENT_TYPE, "application/json") + .body(AsyncBody::Bytes(Bytes::from(body))) + .map_err(new_request_build_error)?; + self.sign(&mut req)?; + + let resp = self.client.send(req).await?; + if resp.status() != StatusCode::OK { return Err(parse_error(resp).await?); } - } - // Setup to get the session id. - let uri = format!("{}/accountLogin", SETUP_ENDPOINT); - let body = serde_json::to_vec(&json!({ + if let Some(rscd) = resp.headers().get(APPLE_RESPONSE_HEADER) { + let status_code = StatusCode::from_bytes(rscd.as_bytes()).unwrap(); + if status_code != StatusCode::CONFLICT { + return Err(parse_error(resp).await?); + } + } + + // Setup to get the session id. + let uri = format!("{}/accountLogin", SETUP_ENDPOINT); + let body = serde_json::to_vec(&json!({ "accountCountryCode": self.data.account_country.clone().unwrap_or_default(), "dsWebAuthToken":self.ds_web_auth_token.clone().unwrap_or_default(), "extended_login": true, - "trustToken": self.trust_token.clone().unwrap_or_default(), - })) - .map_err(new_json_serialize_error)?; + "trustToken": self.trust_token.clone().unwrap_or_default(),})) + .map_err(new_json_serialize_error)?; - let mut req = Request::post(uri) - .header(header::CONTENT_TYPE, "application/json") - .body(AsyncBody::Bytes(Bytes::from(body))) - .map_err(new_request_build_error)?; - self.sign(&mut req)?; + let mut req = Request::post(uri) + .header(header::CONTENT_TYPE, "application/json") + .body(AsyncBody::Bytes(Bytes::from(body))) + .map_err(new_request_build_error)?; + self.sign(&mut req)?; - let resp = self.client.send(req).await?; - if resp.status() != StatusCode::OK { - return Err(parse_error(resp).await?); - } + let resp = self.client.send(req).await?; + if resp.status() != StatusCode::OK { + return Err(parse_error(resp).await?); + } - // Updata SessionData cookies.We need obtain `X-APPLE-WEBAUTH-USER` cookie to get file. - self.update(&resp)?; + // Updata SessionData cookies.We need obtain `X-APPLE-WEBAUTH-USER` cookie to get file. + self.update(&resp)?; - let bs = resp.into_body().bytes().await?; - let auth_info: IcloudWebservicesResponse = - serde_json::from_slice(&bs).map_err(new_json_deserialize_error)?; + let bs = resp.into_body().bytes().await?; + let auth_info: IcloudWebservicesResponse = + serde_json::from_slice(&bs).map_err(new_json_deserialize_error)?; - // Check if we have extra challenge to take. - if auth_info.hsa_challenge_required && !auth_info.hsa_trusted_browser { - return Err(Error::new(ErrorKind::Unexpected, "Apple icloud AuthenticationFailed:Unauthorized request:Needs two-factor authentication")); - } + // Check if we have extra challenge to take. + if auth_info.hsa_challenge_required && !auth_info.hsa_trusted_browser { + return Err(Error::new(ErrorKind::Unexpected, "Apple icloud AuthenticationFailed:Unauthorized request:Needs two-factor authentication")); + } - if let Some(v) = &auth_info.webservices.drivews.url { - self.data.drivews_url = v.to_string(); - } - if let Some(v) = &auth_info.webservices.docws.url { - self.data.docws_url = v.to_string(); - } + if let Some(v) = &auth_info.webservices.drivews.url { + self.data.drivews_url = v.to_string(); + } + if let Some(v) = &auth_info.webservices.docws.url { + self.data.docws_url = v.to_string(); + } + self.initiated = true; + } Ok(()) } @@ -240,19 +238,6 @@ impl IcloudSigner { ); } - if !self.data.cookies.is_empty() { - let cookies: Vec = self - .data - .cookies - .iter() - .map(|(k, v)| format!("{}={}", k, v)) - .collect(); - headers.insert( - header::COOKIE, - cookies.as_slice().join("; ").parse().unwrap(), - ); - } - for (key, value) in AUTH_HEADERS { headers.insert(key, build_header_value(value)?); } @@ -266,7 +251,6 @@ impl IcloudSigner { { self.data.account_country = Some(account_country.to_string()); } - if let Some(session_id) = parse_header_to_str(resp.headers(), SESSION_ID_HEADER)? { self.data.session_id = Some(session_id.to_string()); } @@ -278,18 +262,6 @@ impl IcloudSigner { self.data.scnt = Some(scnt.to_string()); } - for (key, value) in resp.headers() { - if key == header::SET_COOKIE { - if let Some(cookie) = value.to_str().unwrap().split(';').next() { - if let Some((key, value)) = cookie.split_once('=') { - self.data - .cookies - .insert(String::from(key), String::from(value)); - } - } - } - } - Ok(()) } @@ -302,12 +274,6 @@ impl IcloudSigner { &mut self, mut req: Request, ) -> Result> { - // Init the signer first. - if self.initiated { - self.init().await?; - self.initiated = true; - } - self.sign(&mut req)?; let resp = self.client.send(req).await?; @@ -527,8 +493,8 @@ impl PathQuery for IcloudPathQuery { async fn create_dir(&self, parent_id: &str, name: &str) -> Result { let mut signer = self.signer.lock().await; - let clone = signer.clone(); - let client_id = clone.client_id(); + + let client_id = signer.client_id().to_string(); let drivews_url = signer.drivews_url().await?; From 82c817b720cefc0d52422f8346f4ef781969fff2 Mon Sep 17 00:00:00 2001 From: bokket <3100563328@qq.com> Date: Mon, 22 Jan 2024 15:33:57 +0800 Subject: [PATCH 4/8] add cookies Signed-off-by: bokket <3100563328@qq.com> --- Cargo.lock | 56 ---------- core/Cargo.toml | 3 +- core/src/raw/http_util/client.rs | 2 - core/src/services/icloud/core.rs | 183 ++++++++++++++++++------------- 4 files changed, 110 insertions(+), 134 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0381b429dd07..03cb8b4e56ab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1532,34 +1532,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - -[[package]] -name = "cookie_store" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" -dependencies = [ - "cookie", - "idna 0.2.3", - "log", - "publicsuffix", - "serde", - "serde_derive", - "serde_json", - "time", - "url", -] - [[package]] name = "core-foundation" version = "0.9.3" @@ -3306,16 +3278,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.4.0" @@ -5624,12 +5586,6 @@ version = "2.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "106dd99e98437432fed6519dedecfade6a06a73bb7b2a1e019fdd2bee5778d94" -[[package]] -name = "psl-types" -version = "2.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" - [[package]] name = "ptr_meta" version = "0.1.4" @@ -5650,16 +5606,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "publicsuffix" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457" -dependencies = [ - "idna 0.3.0", - "psl-types", -] - [[package]] name = "pulldown-cmark" version = "0.9.3" @@ -6096,8 +6042,6 @@ checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64 0.21.5", "bytes", - "cookie", - "cookie_store", "encoding_rs", "futures-core", "futures-util", diff --git a/core/Cargo.toml b/core/Cargo.toml index ec73bb493956..276aa7f2f179 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -75,7 +75,6 @@ native-tls-vendored = ["reqwest/native-tls-vendored"] # Enable path cache. # This is an internal feature, and should not be used by users. internal-path-cache = ["dep:moka"] -internal-http-cookies = ["reqwest/cookies"] # Enable all layers. layers-all = [ @@ -155,7 +154,7 @@ services-http = [] services-huggingface = [] services-ipfs = ["dep:prost"] services-ipmfs = [] -services-icloud = ["internal-path-cache", "internal-http-cookies"] +services-icloud = ["internal-path-cache"] services-koofr = [] services-libsql = ["dep:hrana-client-proto"] services-memcached = ["dep:bb8"] diff --git a/core/src/raw/http_util/client.rs b/core/src/raw/http_util/client.rs index e4d47d88e864..2c5e4bc5c78b 100644 --- a/core/src/raw/http_util/client.rs +++ b/core/src/raw/http_util/client.rs @@ -69,8 +69,6 @@ impl HttpClient { #[cfg(feature = "trust-dns")] let builder = builder.trust_dns(true); - #[cfg(feature = "internal-http-cookies")] - let builder = builder.cookie_store(true); Ok(Self { client: builder.build().map_err(|err| { diff --git a/core/src/services/icloud/core.rs b/core/src/services/icloud/core.rs index e3b7b0c9d56e..f76cc459156a 100644 --- a/core/src/services/icloud/core.rs +++ b/core/src/services/icloud/core.rs @@ -17,6 +17,7 @@ use async_trait::async_trait; use bytes::{Buf, Bytes}; +use std::collections::BTreeMap; use std::fmt::{Debug, Formatter}; use std::sync::Arc; @@ -68,6 +69,7 @@ pub struct SessionData { scnt: Option, account_country: Option, + cookies: BTreeMap, drivews_url: String, docws_url: String, } @@ -80,6 +82,8 @@ impl SessionData { session_token: None, scnt: None, account_country: None, + + cookies: Default::default(), drivews_url: String::new(), docws_url: String::new(), } @@ -111,94 +115,100 @@ impl Debug for IcloudSigner { impl IcloudSigner { /// Get the drivews_url from signer session data. /// Async await init finish. - pub async fn drivews_url(&mut self) -> Result { - self.init().await?; - Ok(self.data.drivews_url.clone()) + pub async fn drivews_url(&mut self) -> Result<&str> { + if !self.initiated { + self.init().await?; + } + Ok(&self.data.drivews_url) } /// Get the docws_url from signer session data. /// Async await init finish. - pub async fn docws_url(&mut self) -> Result { - self.init().await?; - Ok(self.data.docws_url.clone()) + pub async fn docws_url(&mut self) -> Result<&str> { + if !self.initiated { + self.init().await?; + } + Ok(&self.data.docws_url) } /// iCloud will use our oauth state as client id. - pub fn client_id(&mut self) -> &str { + pub fn client_id(&self) -> &str { &self.data.oauth_state } async fn init(&mut self) -> Result<()> { + if self.initiated { + return Ok(()); + } + // Sign the auth endpoint first. - if !self.initiated { - let uri = format!("{}/signin?isRememberMeEnable=true", AUTH_ENDPOINT); - let body = serde_json::to_vec(&json!({ - "accountName" : self.apple_id, - "password" : self.password, - "rememberMe": true, - "trustTokens": [self.trust_token.clone().unwrap()], - })) - .map_err(new_json_serialize_error)?; - - let mut req = Request::post(uri) - .header(header::CONTENT_TYPE, "application/json") - .body(AsyncBody::Bytes(Bytes::from(body))) - .map_err(new_request_build_error)?; - self.sign(&mut req)?; - - let resp = self.client.send(req).await?; - if resp.status() != StatusCode::OK { - return Err(parse_error(resp).await?); - } + let uri = format!("{}/signin?isRememberMeEnable=true", AUTH_ENDPOINT); + let body = serde_json::to_vec(&json!({ + "accountName" : self.apple_id, + "password" : self.password, + "rememberMe": true, + "trustTokens": [self.trust_token.clone().unwrap()], + })) + .map_err(new_json_serialize_error)?; - if let Some(rscd) = resp.headers().get(APPLE_RESPONSE_HEADER) { - let status_code = StatusCode::from_bytes(rscd.as_bytes()).unwrap(); - if status_code != StatusCode::CONFLICT { - return Err(parse_error(resp).await?); - } + let mut req = Request::post(uri) + .header(header::CONTENT_TYPE, "application/json") + .body(AsyncBody::Bytes(Bytes::from(body))) + .map_err(new_request_build_error)?; + self.sign(&mut req)?; + + let resp = self.client.send(req).await?; + if resp.status() != StatusCode::OK { + return Err(parse_error(resp).await?); + } + + if let Some(rscd) = resp.headers().get(APPLE_RESPONSE_HEADER) { + let status_code = StatusCode::from_bytes(rscd.as_bytes()).unwrap(); + if status_code != StatusCode::CONFLICT { + return Err(parse_error(resp).await?); } + } - // Setup to get the session id. - let uri = format!("{}/accountLogin", SETUP_ENDPOINT); - let body = serde_json::to_vec(&json!({ + // Setup to get the session id. + let uri = format!("{}/accountLogin", SETUP_ENDPOINT); + let body = serde_json::to_vec(&json!({ "accountCountryCode": self.data.account_country.clone().unwrap_or_default(), "dsWebAuthToken":self.ds_web_auth_token.clone().unwrap_or_default(), "extended_login": true, "trustToken": self.trust_token.clone().unwrap_or_default(),})) - .map_err(new_json_serialize_error)?; - - let mut req = Request::post(uri) - .header(header::CONTENT_TYPE, "application/json") - .body(AsyncBody::Bytes(Bytes::from(body))) - .map_err(new_request_build_error)?; - self.sign(&mut req)?; + .map_err(new_json_serialize_error)?; - let resp = self.client.send(req).await?; - if resp.status() != StatusCode::OK { - return Err(parse_error(resp).await?); - } + let mut req = Request::post(uri) + .header(header::CONTENT_TYPE, "application/json") + .body(AsyncBody::Bytes(Bytes::from(body))) + .map_err(new_request_build_error)?; + self.sign(&mut req)?; - // Updata SessionData cookies.We need obtain `X-APPLE-WEBAUTH-USER` cookie to get file. - self.update(&resp)?; + let resp = self.client.send(req).await?; + if resp.status() != StatusCode::OK { + return Err(parse_error(resp).await?); + } - let bs = resp.into_body().bytes().await?; - let auth_info: IcloudWebservicesResponse = - serde_json::from_slice(&bs).map_err(new_json_deserialize_error)?; + // Updata SessionData cookies.We need obtain `X-APPLE-WEBAUTH-USER` cookie to get file. + self.update(&resp)?; - // Check if we have extra challenge to take. - if auth_info.hsa_challenge_required && !auth_info.hsa_trusted_browser { - return Err(Error::new(ErrorKind::Unexpected, "Apple icloud AuthenticationFailed:Unauthorized request:Needs two-factor authentication")); - } + let bs = resp.into_body().bytes().await?; + let auth_info: IcloudWebservicesResponse = + serde_json::from_slice(&bs).map_err(new_json_deserialize_error)?; - if let Some(v) = &auth_info.webservices.drivews.url { - self.data.drivews_url = v.to_string(); - } - if let Some(v) = &auth_info.webservices.docws.url { - self.data.docws_url = v.to_string(); - } + // Check if we have extra challenge to take. + if auth_info.hsa_challenge_required && !auth_info.hsa_trusted_browser { + return Err(Error::new(ErrorKind::Unexpected, "Apple icloud AuthenticationFailed:Unauthorized request:Needs two-factor authentication")); + } - self.initiated = true; + if let Some(v) = &auth_info.webservices.drivews.url { + self.data.drivews_url = v.to_string(); + } + if let Some(v) = &auth_info.webservices.docws.url { + self.data.docws_url = v.to_string(); } + + self.initiated = true; Ok(()) } @@ -238,6 +248,19 @@ impl IcloudSigner { ); } + if !self.data.cookies.is_empty() { + let cookies: Vec = self + .data + .cookies + .iter() + .map(|(k, v)| format!("{}={}", k, v)) + .collect(); + headers.insert( + header::COOKIE, + cookies.as_slice().join("; ").parse().unwrap(), + ); + } + for (key, value) in AUTH_HEADERS { headers.insert(key, build_header_value(value)?); } @@ -262,6 +285,18 @@ impl IcloudSigner { self.data.scnt = Some(scnt.to_string()); } + for (key, value) in resp.headers() { + if key == header::SET_COOKIE { + if let Some(cookie) = value.to_str().unwrap().split(';').next() { + if let Some((key, value)) = cookie.split_once('=') { + self.data + .cookies + .insert(String::from(key), String::from(value)); + } + } + } + } + Ok(()) } @@ -301,9 +336,10 @@ impl IcloudCore { pub async fn get_root(&self, id: &str) -> Result { let mut signer = self.signer.lock().await; - let drivews_url = signer.drivews_url().await?; - - let uri = format!("{}/retrieveItemDetailsInFolders", drivews_url); + let uri = format!( + "{}/retrieveItemDetailsInFolders", + signer.drivews_url().await? + ); let body = serde_json::to_vec(&json!([ { @@ -336,11 +372,11 @@ impl IcloudCore { ) -> Result> { let mut signer = self.signer.lock().await; - let docws_url = signer.docws_url().await?; - let uri = format!( "{}/ws/{}/download/by_id?document_id={}", - docws_url, zone, id + signer.docws_url().await?, + zone, + id ); let req = Request::get(uri) @@ -457,9 +493,10 @@ impl PathQuery for IcloudPathQuery { async fn query(&self, parent_id: &str, name: &str) -> Result> { let mut signer = self.signer.lock().await; - let drivews_url = signer.drivews_url().await?; - - let uri = format!("{}/retrieveItemDetailsInFolders", drivews_url); + let uri = format!( + "{}/retrieveItemDetailsInFolders", + signer.drivews_url().await? + ); let body = serde_json::to_vec(&json!([ { @@ -496,9 +533,7 @@ impl PathQuery for IcloudPathQuery { let client_id = signer.client_id().to_string(); - let drivews_url = signer.drivews_url().await?; - - let uri = format!("{}/createFolders", drivews_url); + let uri = format!("{}/createFolders", signer.drivews_url().await?); let body = serde_json::to_vec(&json!( { "destinationDrivewsId": parent_id, From 126b552e0dae43fbafc54ff3e9cfd99946b8707b Mon Sep 17 00:00:00 2001 From: bokket <3100563328@qq.com> Date: Mon, 22 Jan 2024 16:17:06 +0800 Subject: [PATCH 5/8] refactor:use get_all headers instead loop Signed-off-by: bokket <3100563328@qq.com> --- core/src/services/icloud/core.rs | 33 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/core/src/services/icloud/core.rs b/core/src/services/icloud/core.rs index f76cc459156a..998c84a53aab 100644 --- a/core/src/services/icloud/core.rs +++ b/core/src/services/icloud/core.rs @@ -83,7 +83,7 @@ impl SessionData { scnt: None, account_country: None, - cookies: Default::default(), + cookies: BTreeMap::default(), drivews_url: String::new(), docws_url: String::new(), } @@ -116,18 +116,14 @@ impl IcloudSigner { /// Get the drivews_url from signer session data. /// Async await init finish. pub async fn drivews_url(&mut self) -> Result<&str> { - if !self.initiated { - self.init().await?; - } + self.init().await?; Ok(&self.data.drivews_url) } /// Get the docws_url from signer session data. /// Async await init finish. pub async fn docws_url(&mut self) -> Result<&str> { - if !self.initiated { - self.init().await?; - } + self.init().await?; Ok(&self.data.docws_url) } @@ -257,7 +253,7 @@ impl IcloudSigner { .collect(); headers.insert( header::COOKIE, - cookies.as_slice().join("; ").parse().unwrap(), + build_header_value(&cookies.as_slice().join("; "))?, ); } @@ -285,15 +281,18 @@ impl IcloudSigner { self.data.scnt = Some(scnt.to_string()); } - for (key, value) in resp.headers() { - if key == header::SET_COOKIE { - if let Some(cookie) = value.to_str().unwrap().split(';').next() { - if let Some((key, value)) = cookie.split_once('=') { - self.data - .cookies - .insert(String::from(key), String::from(value)); - } - } + let cookies: Vec = resp + .headers() + .get_all(header::SET_COOKIE) + .iter() + .map(|v| v.to_str().unwrap().to_string()) + .collect(); + + for cookie in cookies { + if let Some((key, value)) = cookie.split_once('=') { + self.data + .cookies + .insert(key.into(), value.split(';').next().unwrap().into()); } } From 58059cca3bf16b63f7f2ebf0af89eea1d146afaf Mon Sep 17 00:00:00 2001 From: bokket <3100563328@qq.com> Date: Mon, 22 Jan 2024 16:29:08 +0800 Subject: [PATCH 6/8] remove Signed-off-by: bokket <3100563328@qq.com> --- core/src/services/icloud/core.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/services/icloud/core.rs b/core/src/services/icloud/core.rs index 998c84a53aab..518e7470a893 100644 --- a/core/src/services/icloud/core.rs +++ b/core/src/services/icloud/core.rs @@ -290,9 +290,11 @@ impl IcloudSigner { for cookie in cookies { if let Some((key, value)) = cookie.split_once('=') { + + println!("{}={}",key,value); self.data .cookies - .insert(key.into(), value.split(';').next().unwrap().into()); + .insert(key.into(), value.into()); } } From c930f6ff0b29da9a54acc5d932ea4c453849fe77 Mon Sep 17 00:00:00 2001 From: bokket <3100563328@qq.com> Date: Mon, 22 Jan 2024 16:29:57 +0800 Subject: [PATCH 7/8] fmt Signed-off-by: bokket <3100563328@qq.com> --- core/src/services/icloud/core.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/core/src/services/icloud/core.rs b/core/src/services/icloud/core.rs index 518e7470a893..c1568991f054 100644 --- a/core/src/services/icloud/core.rs +++ b/core/src/services/icloud/core.rs @@ -290,11 +290,8 @@ impl IcloudSigner { for cookie in cookies { if let Some((key, value)) = cookie.split_once('=') { - - println!("{}={}",key,value); - self.data - .cookies - .insert(key.into(), value.into()); + println!("{}={}", key, value); + self.data.cookies.insert(key.into(), value.into()); } } From e3694d13d17d8fe7c7eda82e8c3c3903a53d8670 Mon Sep 17 00:00:00 2001 From: bokket <3100563328@qq.com> Date: Mon, 22 Jan 2024 16:31:50 +0800 Subject: [PATCH 8/8] delete Signed-off-by: bokket <3100563328@qq.com> --- core/src/services/icloud/core.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/services/icloud/core.rs b/core/src/services/icloud/core.rs index c1568991f054..0430e954fb39 100644 --- a/core/src/services/icloud/core.rs +++ b/core/src/services/icloud/core.rs @@ -290,7 +290,6 @@ impl IcloudSigner { for cookie in cookies { if let Some((key, value)) = cookie.split_once('=') { - println!("{}={}", key, value); self.data.cookies.insert(key.into(), value.into()); } }