Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support iCloud in China #47

Closed
wants to merge 4 commits into from
Closed
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
16 changes: 13 additions & 3 deletions src/iCloudClient.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,25 @@
import { getBrowserStorageValue, COUNTRY_KEYS } from './storage';

export class UnsuccessfulRequestError extends Error {}

type ServiceName = 'premiummailsettings';

type Country = 'default' | 'CN'

class ICloudClient {
public webservices?: Record<ServiceName, { url: string; status: string }>;
private country: Country
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
private country: Country
private setupUrl: string


constructor(webservices?: ICloudClient['webservices']) {
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
constructor(webservices?: ICloudClient['webservices']) {
constructor(setupUrl: string, webservices?: ICloudClient['webservices']) {

this.webservices = webservices;
this.country = 'default'
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
this.country = 'default'
this.setupUrl = setupUrl

}

static get setupUrl() {
return 'https://setup.icloud.com/setup/ws/1';
return {
default: 'https://setup.icloud.com/setup/ws/1',
CN: 'https://setup.icloud.com.cn/setup/ws/1'
}
Comment on lines +19 to +22
Copy link
Owner

Choose a reason for hiding this comment

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

let's remove this static getter all together and rely on the setupUrl variable passed to the constructor.

}

public async request(
Expand Down Expand Up @@ -47,6 +56,7 @@ class ICloudClient {

public async isAuthenticated(): Promise<boolean> {
try {
this.country = await getBrowserStorageValue<Country>(COUNTRY_KEYS) || 'default'
Copy link
Owner

Choose a reason for hiding this comment

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

I'd like to keep the ICloudClient class agnostic of the browser context it runs on. Let's not have it access browser specific APIs (such us the storage one).

await this.validateToken();
return true;
} catch {
Expand All @@ -57,7 +67,7 @@ class ICloudClient {
public async validateToken(): Promise<void> {
const { webservices } = (await this.request(
'POST',
`${ICloudClient.setupUrl}/validate`
`${ICloudClient.setupUrl[this.country]}/validate`
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
`${ICloudClient.setupUrl[this.country]}/validate`
`${ICloudClient.setupUrl}/validate`

)) as {
webservices: ICloudClient['webservices'];
};
Expand All @@ -71,7 +81,7 @@ class ICloudClient {
options: { trust: boolean } = { trust: false }
): Promise<void> {
const { trust } = options;
await this.request('POST', `${ICloudClient.setupUrl}/logout`, {
await this.request('POST', `${ICloudClient.setupUrl[this.country]}/logout`, {
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
await this.request('POST', `${ICloudClient.setupUrl[this.country]}/logout`, {
await this.request('POST', `${ICloudClient.setupUrl}/logout`, {

data: {
trustBrowsers: trust,
allBrowsers: trust,
Expand Down
5 changes: 4 additions & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@
"webRequest",
"notifications"
],
"host_permissions": ["https://*.icloud.com/*"],
"host_permissions": [
"https://*.icloud.com/*",
"https://*.icloud.com.cn/*"
],
"icons": {
"16": "icon-16.png",
"32": "icon-32.png",
Expand Down
17 changes: 14 additions & 3 deletions src/pages/Background/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
getBrowserStorageValue,
POPUP_STATE_STORAGE_KEYS,
OPTIONS_STORAGE_KEYS,
COUNTRY_KEYS,
setBrowserStorageValue,
} from '../../storage';
import ICloudClient, { PremiumMailSettings } from '../../iCloudClient';
Expand Down Expand Up @@ -259,20 +260,27 @@ browser.contextMenus.onClicked.addListener(async (info, tab) => {
// as enabled.
browser.webRequest.onResponseStarted.addListener(
async (details: browser.WebRequest.OnResponseStartedDetailsType) => {
const { statusCode } = details;
const { statusCode, url } = details;
if (statusCode < 200 && statusCode > 299) {
console.debug('Request failed', details);
return;
}

const country = Object.entries(ICloudClient.setupUrl).find((entries) => url.startsWith(`${entries[1]}/accountLogin`))?.[0];
await setBrowserStorageValue(COUNTRY_KEYS, country);
Comment on lines +269 to +270
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
const country = Object.entries(ICloudClient.setupUrl).find((entries) => url.startsWith(`${entries[1]}/accountLogin`))?.[0];
await setBrowserStorageValue(COUNTRY_KEYS, country);
const setupUrl = new URL(url).hostname.split("/accountLogin")[0]
const client = new ICloudClient(setupUrl);

Copy link
Owner

Choose a reason for hiding this comment

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

Also, instead of storing the setupUrl value here, we can do it as part of performAuthSideEffects.


const client = new ICloudClient();

const isAuthenticated = await client.isAuthenticated();
if (isAuthenticated) {
performAuthSideEffects();
}
},
{
urls: [`${ICloudClient.setupUrl}/accountLogin*`],
urls: [
`${ICloudClient.setupUrl.default}/accountLogin*`,
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
`${ICloudClient.setupUrl.default}/accountLogin*`,
`https://setup.icloud.com/setup/ws/1/accountLogin*`,

`${ICloudClient.setupUrl.CN}/accountLogin*`
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
`${ICloudClient.setupUrl.CN}/accountLogin*`
`https://setup.icloud.com.cn/setup/ws/1/accountLogin*`

],
},
[]
);
Expand All @@ -290,7 +298,10 @@ browser.webRequest.onResponseStarted.addListener(
performDeauthSideEffects();
},
{
urls: [`${ICloudClient.setupUrl}/logout*`],
urls: [
`${ICloudClient.setupUrl.default}/logout*`,
`${ICloudClient.setupUrl.CN}/logout*`,
],
},
[]
);
Expand Down
24 changes: 24 additions & 0 deletions src/rules.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,29 @@
"resourceTypes": ["xmlhttprequest"],
"excludedInitiatorDomains": ["apple.com", "icloud.com"]
}
},
{
"id": 2,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{
"header": "Origin",
"operation": "set",
"value": "https://www.icloud.com.cn"
},
{
"header": "Referer",
"operation": "set",
"value": "https://www.icloud.com.cn/"
}
]
},
"condition": {
"urlFilter": "|https://*.icloud.com.cn/*",
"resourceTypes": ["xmlhttprequest"],
"excludedInitiatorDomains": ["apple.com", "icloud.com.cn"]
}
}
]
1 change: 1 addition & 0 deletions src/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import browser from 'webextension-polyfill';

export const POPUP_STATE_STORAGE_KEYS = ['iCloudHmePopupState'];
export const OPTIONS_STORAGE_KEYS = ['iCloudHmeOptions'];
export const COUNTRY_KEYS = ['country'];
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
export const COUNTRY_KEYS = ['country'];
export const ICLOUD_CLIENT_STATE = ['iCloudClientState'];

storing an object of type:

{setupUrl: string}


export async function getBrowserStorageValue<T>(
keys: string[]
Expand Down
15 changes: 11 additions & 4 deletions src/webRequestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import ICloudClient from './iCloudClient';

export const setupBlockingWebRequestListeners = () => {
browser.webRequest.onBeforeSendHeaders.addListener(
({ requestHeaders, documentUrl, originUrl, initiator }) => {
({ requestHeaders, documentUrl, originUrl, initiator, url, }) => {
const initiatedByTheExtension = [documentUrl, originUrl, initiator].some(
(url) =>
url?.includes(browser.runtime.id) ||
Expand All @@ -20,19 +20,26 @@ export const setupBlockingWebRequestListeners = () => {
(header) => !['referer', 'origin'].includes(header.name.toLowerCase())
) || [];

const suffix = url.match(/icloud.com(\.\w+)/)?.[1] || '';
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
const suffix = url.match(/icloud.com(\.\w+)/)?.[1] || '';
const originComponents = new URL(url).origin.split(".");
const pivot = originComponents.indexOf("icloud");
const tld = originComponents.slice(pivot + 1).join(".")


modifiedHeaders.push({
name: 'Referer',
value: 'https://www.icloud.com/',
value: `https://www.icloud.com${suffix}/`,
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
value: `https://www.icloud.com${suffix}/`,
value: `https://www.icloud.${tld}/`,

});
modifiedHeaders.push({
name: 'Origin',
value: 'https://www.icloud.com',
value: `https://www.icloud.com${suffix}`,
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
value: `https://www.icloud.com${suffix}`,
value: `https://www.icloud.${tld}`,

});

return { requestHeaders: modifiedHeaders };
},
{
urls: [`${ICloudClient.setupUrl}/*`, 'https://*.icloud.com/v*/hme/*'],
urls: [
`${ICloudClient.setupUrl.default}/*`,
`${ICloudClient.setupUrl.CN}/*`,
'https://*.icloud.com/v*/hme/*',
'https://*.icloud.com.cn/v*/hme/*',
],
},
['requestHeaders', 'blocking']
);
Expand Down
Loading