diff --git a/background.js b/background.js index 03e822f..1b1a176 100644 --- a/background.js +++ b/background.js @@ -628,6 +628,7 @@ function normalizeMailProvider(value = '') { case CLOUDFLARE_TEMP_EMAIL_PROVIDER: case '163': case '163-vip': + case '126': case 'qq': case 'inbucket': case '2925': @@ -6131,6 +6132,9 @@ function getMailConfig(state) { if (provider === '163-vip') { return { source: 'mail-163', url: 'https://webmail.vip.163.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '163 VIP 邮箱' }; } + if (provider === '126') { + return { source: 'mail-163', url: 'https://mail.126.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', label: '126 邮箱' }; + } if (provider === 'inbucket') { const host = normalizeInbucketOrigin(state.inbucketHost); const mailbox = (state.inbucketMailbox || '').trim(); diff --git a/content/utils.js b/content/utils.js index 1960e26..3b8f0f5 100644 --- a/content/utils.js +++ b/content/utils.js @@ -2,13 +2,20 @@ const getActivationStrategy = self.MultiPageActivationUtils?.getActivationStrategy; -const SCRIPT_SOURCE = (() => { - if (window.__MULTIPAGE_SOURCE) return window.__MULTIPAGE_SOURCE; - const url = location.href; - const hostname = location.hostname; +function detectScriptSource({ + injectedSource, + url = '', + hostname = '', +} = {}) { + if (injectedSource) return injectedSource; if (url.includes('auth0.openai.com') || url.includes('auth.openai.com') || url.includes('accounts.openai.com')) return 'signup-page'; if (hostname === 'mail.qq.com' || hostname === 'wx.mail.qq.com') return 'qq-mail'; - if (hostname === 'mail.163.com' || hostname.endsWith('.mail.163.com') || hostname === 'webmail.vip.163.com') return 'mail-163'; + if ( + hostname === 'mail.163.com' + || hostname.endsWith('.mail.163.com') + || hostname === 'webmail.vip.163.com' + || hostname === 'mail.126.com' + ) return 'mail-163'; if (hostname === 'mail.google.com') return 'gmail-mail'; if (hostname === 'www.icloud.com' || hostname === 'www.icloud.com.cn') return 'icloud-mail'; if (url.includes('duckduckgo.com/email/settings/autofill')) return 'duck-mail'; @@ -16,6 +23,14 @@ const SCRIPT_SOURCE = (() => { if (url.includes("2925.com")) return "mail-2925"; // VPS panel — detected dynamically since URL is configurable return 'vps-panel'; +} + +const SCRIPT_SOURCE = (() => { + return detectScriptSource({ + injectedSource: window.__MULTIPAGE_SOURCE, + url: location.href, + hostname: location.hostname, + }); })(); const LOG_PREFIX = `[MultiPage:${SCRIPT_SOURCE}]`; diff --git a/mail-provider-utils.js b/mail-provider-utils.js new file mode 100644 index 0000000..cccb000 --- /dev/null +++ b/mail-provider-utils.js @@ -0,0 +1,80 @@ +const HOTMAIL_PROVIDER = 'hotmail-api'; +const NETEASE_LIST_PATH = '/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D'; + +function normalizeMailProvider(value = '') { + const normalized = String(value || '').trim().toLowerCase(); + switch (normalized) { + case HOTMAIL_PROVIDER: + case '163': + case '163-vip': + case '126': + case 'qq': + case 'inbucket': + return normalized; + default: + return '163'; + } +} + +function getMailProviderConfig(state = {}, options = {}) { + const provider = normalizeMailProvider(state.mailProvider); + const normalizeInbucketOrigin = options.normalizeInbucketOrigin || (() => ''); + + if (provider === HOTMAIL_PROVIDER) { + return { provider: HOTMAIL_PROVIDER, label: 'Hotmail(微软 Graph)' }; + } + if (provider === '163') { + return { + source: 'mail-163', + url: `https://mail.163.com${NETEASE_LIST_PATH}`, + label: '163 邮箱', + }; + } + if (provider === '163-vip') { + return { + source: 'mail-163', + url: `https://webmail.vip.163.com${NETEASE_LIST_PATH}`, + label: '163 VIP 邮箱', + }; + } + if (provider === '126') { + return { + source: 'mail-163', + url: `https://mail.126.com${NETEASE_LIST_PATH}`, + label: '126 邮箱', + }; + } + if (provider === 'inbucket') { + const host = normalizeInbucketOrigin(state.inbucketHost); + const mailbox = String(state.inbucketMailbox || '').trim(); + if (!host) { + return { error: 'Inbucket 主机地址为空或无效。' }; + } + if (!mailbox) { + return { error: 'Inbucket 邮箱名称为空。' }; + } + return { + source: 'inbucket-mail', + url: `${host}/m/${encodeURIComponent(mailbox)}/`, + label: `Inbucket 邮箱(${mailbox})`, + navigateOnReuse: true, + inject: ['content/activation-utils.js', 'content/utils.js', 'content/inbucket-mail.js'], + injectSource: 'inbucket-mail', + }; + } + return { source: 'qq-mail', url: 'https://wx.mail.qq.com/', label: 'QQ 邮箱' }; +} + +const api = { + HOTMAIL_PROVIDER, + getMailProviderConfig, + normalizeMailProvider, +}; + +if (typeof module !== 'undefined' && module.exports) { + module.exports = api; +} + +if (typeof self !== 'undefined') { + self.MailProviderUtils = api; +} diff --git a/manifest.json b/manifest.json index dd2a526..aca5f9b 100644 --- a/manifest.json +++ b/manifest.json @@ -69,7 +69,8 @@ "matches": [ "https://mail.163.com/*", "https://*.mail.163.com/*", - "https://webmail.vip.163.com/*" + "https://webmail.vip.163.com/*", + "https://mail.126.com/*" ], "js": [ "content/activation-utils.js", diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index 3dc8cec..f6f7c49 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -169,6 +169,7 @@ + diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js index f8bf5df..2a70689 100644 --- a/sidepanel/sidepanel.js +++ b/sidepanel/sidepanel.js @@ -444,6 +444,11 @@ const MAIL_PROVIDER_LOGIN_CONFIGS = { url: 'https://webmail.vip.163.com/', buttonLabel: '登录', }, + '126': { + label: '126 邮箱', + url: 'https://mail.126.com/', + buttonLabel: '登录', + }, qq: { label: 'QQ 邮箱', url: 'https://wx.mail.qq.com/', @@ -1693,7 +1698,7 @@ function applySettingsState(state) { inputSub2ApiGroup.value = state?.sub2apiGroupName || ''; inputSub2ApiDefaultProxy.value = state?.sub2apiDefaultProxyName || ''; const restoredMailProvider = isCustomMailProvider(state?.mailProvider) - || [ICLOUD_PROVIDER, 'hotmail-api', GMAIL_PROVIDER, 'luckmail-api', '163', '163-vip', 'qq', 'inbucket', '2925', 'cloudflare-temp-email'].includes(String(state?.mailProvider || '').trim()) + || [ICLOUD_PROVIDER, 'hotmail-api', GMAIL_PROVIDER, 'luckmail-api', '163', '163-vip', '126', 'qq', 'inbucket', '2925', 'cloudflare-temp-email'].includes(String(state?.mailProvider || '').trim()) ? String(state?.mailProvider || '163').trim() : (String(state?.emailGenerator || '').trim().toLowerCase() === 'custom' || String(state?.emailGenerator || '').trim().toLowerCase() === 'manual' diff --git a/tests/content-utils.test.js b/tests/content-utils.test.js new file mode 100644 index 0000000..0ea69ca --- /dev/null +++ b/tests/content-utils.test.js @@ -0,0 +1,66 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('node:fs'); + +const source = fs.readFileSync('content/utils.js', 'utf8'); + +function extractFunction(name) { + const start = source.indexOf(`function ${name}(`); + if (start < 0) { + throw new Error(`missing function ${name}`); + } + + let parenDepth = 0; + let signatureEnded = false; + let braceStart = -1; + for (let index = start; index < source.length; index += 1) { + const ch = source[index]; + if (ch === '(') { + parenDepth += 1; + } else if (ch === ')') { + parenDepth -= 1; + if (parenDepth === 0) { + signatureEnded = true; + } + } else if (ch === '{' && signatureEnded) { + braceStart = index; + break; + } + } + + if (braceStart < 0) { + throw new Error(`missing body for function ${name}`); + } + + let depth = 0; + let end = braceStart; + for (; end < source.length; end += 1) { + const ch = source[end]; + if (ch === '{') depth += 1; + if (ch === '}') { + depth -= 1; + if (depth === 0) { + end += 1; + break; + } + } + } + + return source.slice(start, end); +} + +test('detectScriptSource maps 126 mail hosts to the shared 163 mail source', () => { + const bundle = [extractFunction('detectScriptSource')].join('\n'); + const api = new Function(` +${bundle} +return { detectScriptSource }; +`)(); + + assert.equal( + api.detectScriptSource({ + url: 'https://mail.126.com/js6/main.jsp', + hostname: 'mail.126.com', + }), + 'mail-163' + ); +}); diff --git a/tests/mail-provider-utils.test.js b/tests/mail-provider-utils.test.js new file mode 100644 index 0000000..a52ce10 --- /dev/null +++ b/tests/mail-provider-utils.test.js @@ -0,0 +1,35 @@ +const test = require('node:test'); +const assert = require('node:assert/strict'); + +const { + HOTMAIL_PROVIDER, + getMailProviderConfig, + normalizeMailProvider, +} = require('../mail-provider-utils.js'); + +test('normalizeMailProvider accepts 126 and falls back to 163', () => { + assert.equal(normalizeMailProvider('126'), '126'); + assert.equal(normalizeMailProvider('163-vip'), '163-vip'); + assert.equal(normalizeMailProvider('unknown-provider'), '163'); +}); + +test('getMailProviderConfig returns the shared NetEase source for 126 mail', () => { + assert.deepEqual( + getMailProviderConfig({ mailProvider: '126' }), + { + source: 'mail-163', + url: 'https://mail.126.com/js6/main.jsp?df=mail163_letter#module=mbox.ListModule%7C%7B%22fid%22%3A1%2C%22order%22%3A%22date%22%2C%22desc%22%3Atrue%7D', + label: '126 邮箱', + } + ); +}); + +test('getMailProviderConfig preserves the hotmail provider sentinel', () => { + assert.deepEqual( + getMailProviderConfig({ mailProvider: HOTMAIL_PROVIDER }), + { + provider: HOTMAIL_PROVIDER, + label: 'Hotmail(微软 Graph)', + } + ); +});