Skip to content
Merged
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
56 changes: 0 additions & 56 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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"]
Expand Down
2 changes: 0 additions & 2 deletions core/src/raw/http_util/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand Down
77 changes: 58 additions & 19 deletions core/src/services/icloud/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -68,7 +69,7 @@ pub struct SessionData {

scnt: Option<String>,
account_country: Option<String>,

cookies: BTreeMap<String, String>,
drivews_url: String,
docws_url: String,
}
Expand All @@ -81,6 +82,8 @@ impl SessionData {
session_token: None,
scnt: None,
account_country: None,

cookies: BTreeMap::default(),
drivews_url: String::new(),
docws_url: String::new(),
}
Expand Down Expand Up @@ -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<&str> {
self.init().await?;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check self.initiated first. Or we can move the check into init() directly.

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> {
self.init().await?;
Ok(&self.data.docws_url)
}

/// iCloud will use our oauth state as client id.
Expand All @@ -121,6 +133,10 @@ impl IcloudSigner {
}

async fn init(&mut self) -> Result<()> {
if self.initiated {
return Ok(());
}

// Sign the auth endpoint first.
let uri = format!("{}/signin?isRememberMeEnable=true", AUTH_ENDPOINT);
let body = serde_json::to_vec(&json!({
Expand All @@ -141,6 +157,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 {
Expand All @@ -154,8 +171,7 @@ impl IcloudSigner {
"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(),
}))
"trustToken": self.trust_token.clone().unwrap_or_default(),}))
.map_err(new_json_serialize_error)?;

let mut req = Request::post(uri)
Expand All @@ -169,6 +185,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)?;
Expand All @@ -185,6 +204,7 @@ impl IcloudSigner {
self.data.docws_url = v.to_string();
}

self.initiated = true;
Ok(())
}

Expand Down Expand Up @@ -224,6 +244,19 @@ impl IcloudSigner {
);
}

if !self.data.cookies.is_empty() {
let cookies: Vec<String> = self
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this because reqwest automatically loads and sets cookies.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apple server will check 'X-APPLE-WEBAUTH-USER cookie' in cookies.If we don't update in authenticate request(setup.icloud.com/setup/ws/1) to keep value in seesionData,the next sign cookies are useless.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apple server will check 'X-APPLE-WEBAUTH-USER cookie' in cookies.If we don't update in authenticate request(setup.icloud.com/setup/ws/1) to keep value in seesionData,the next sign cookies are useless.

We don't need to. reqwest will handle COOKIES and SET-COOKIES headers exactly the same as you do here.

.data
.cookies
.iter()
.map(|(k, v)| format!("{}={}", k, v))
.collect();
headers.insert(
header::COOKIE,
build_header_value(&cookies.as_slice().join("; "))?,
);
}

for (key, value) in AUTH_HEADERS {
headers.insert(key, build_header_value(value)?);
}
Expand All @@ -237,7 +270,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());
}
Expand All @@ -249,6 +281,19 @@ impl IcloudSigner {
self.data.scnt = Some(scnt.to_string());
}

let cookies: Vec<String> = 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.into());
}
}

Ok(())
}

Expand All @@ -261,15 +306,8 @@ impl IcloudSigner {
&mut self,
mut req: Request<AsyncBody>,
) -> Result<Response<IncomingAsyncBody>> {
// Init the signer first.
if self.initiated {
self.init().await?;
self.initiated = true;
}

self.sign(&mut req)?;
let resp = self.client.send(req).await?;
self.update(&resp)?;

Ok(resp)
}
Expand Down Expand Up @@ -297,7 +335,7 @@ impl IcloudCore {

let uri = format!(
"{}/retrieveItemDetailsInFolders",
signer.session_data().drivews_url
signer.drivews_url().await?
);

let body = serde_json::to_vec(&json!([
Expand Down Expand Up @@ -333,7 +371,7 @@ impl IcloudCore {

let uri = format!(
"{}/ws/{}/download/by_id?document_id={}",
signer.session_data().drivews_url,
signer.docws_url().await?,
zone,
id
);
Expand Down Expand Up @@ -454,7 +492,7 @@ impl PathQuery for IcloudPathQuery {

let uri = format!(
"{}/retrieveItemDetailsInFolders",
signer.session_data().drivews_url
signer.drivews_url().await?
);

let body = serde_json::to_vec(&json!([
Expand Down Expand Up @@ -489,9 +527,10 @@ impl PathQuery for IcloudPathQuery {

async fn create_dir(&self, parent_id: &str, name: &str) -> Result<String> {
let mut signer = self.signer.lock().await;
let client_id = signer.client_id();

let uri = format!("{}/createFolders", signer.session_data().drivews_url);
let client_id = signer.client_id().to_string();

let uri = format!("{}/createFolders", signer.drivews_url().await?);
let body = serde_json::to_vec(&json!(
{
"destinationDrivewsId": parent_id,
Expand Down