@dawnlit/outlook-bridge 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -183
- package/dist/backend.d.ts +151 -0
- package/dist/backend.js +3 -0
- package/dist/backend.js.map +1 -0
- package/dist/bridge.d.ts +56 -0
- package/dist/bridge.js +507 -0
- package/dist/bridge.js.map +1 -0
- package/dist/errors.d.ts +81 -43
- package/dist/errors.js +98 -52
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.js +25 -52
- package/dist/index.js.map +1 -1
- package/dist/mac/accounts.d.ts +7 -2
- package/dist/mac/accounts.js +23 -6
- package/dist/mac/accounts.js.map +1 -1
- package/dist/mac/attachments.d.ts +5 -25
- package/dist/mac/attachments.js +40 -99
- package/dist/mac/attachments.js.map +1 -1
- package/dist/mac/bounces.d.ts +12 -27
- package/dist/mac/bounces.js +95 -112
- package/dist/mac/bounces.js.map +1 -1
- package/dist/mac/cleanup.d.ts +11 -25
- package/dist/mac/cleanup.js +50 -76
- package/dist/mac/cleanup.js.map +1 -1
- package/dist/mac/drafts.d.ts +13 -34
- package/dist/mac/drafts.js +95 -169
- package/dist/mac/drafts.js.map +1 -1
- package/dist/mac/folders.d.ts +10 -21
- package/dist/mac/folders.js +33 -47
- package/dist/mac/folders.js.map +1 -1
- package/dist/mac/index.d.ts +2 -22
- package/dist/mac/index.js +19 -81
- package/dist/mac/index.js.map +1 -1
- package/dist/mac/profile.d.ts +13 -0
- package/dist/mac/profile.js +149 -0
- package/dist/mac/profile.js.map +1 -0
- package/dist/mac/read.d.ts +18 -36
- package/dist/mac/read.js +154 -208
- package/dist/mac/read.js.map +1 -1
- package/dist/mac/run.d.ts +35 -39
- package/dist/mac/run.js +97 -102
- package/dist/mac/run.js.map +1 -1
- package/dist/mac/scripts.d.ts +78 -86
- package/dist/mac/scripts.js +125 -115
- package/dist/mac/scripts.js.map +1 -1
- package/dist/mac/send.d.ts +10 -20
- package/dist/mac/send.js +64 -86
- package/dist/mac/send.js.map +1 -1
- package/dist/mac/signatures.d.ts +2 -2
- package/dist/mac/signatures.js +9 -11
- package/dist/mac/signatures.js.map +1 -1
- package/dist/mac/templates.d.ts +14 -26
- package/dist/mac/templates.js +75 -113
- package/dist/mac/templates.js.map +1 -1
- package/dist/mail.d.ts +77 -45
- package/dist/mail.js +166 -73
- package/dist/mail.js.map +1 -1
- package/dist/runtime.d.ts +76 -43
- package/dist/runtime.js +103 -48
- package/dist/runtime.js.map +1 -1
- package/dist/shared/args.d.ts +48 -0
- package/dist/shared/args.js +216 -0
- package/dist/shared/args.js.map +1 -0
- package/dist/shared/attachmentMatch.d.ts +28 -16
- package/dist/shared/attachmentMatch.js +60 -22
- package/dist/shared/attachmentMatch.js.map +1 -1
- package/dist/shared/bounceReport.d.ts +11 -0
- package/dist/shared/bounceReport.js +45 -0
- package/dist/shared/bounceReport.js.map +1 -0
- package/dist/shared/bounceRules.d.ts +38 -10
- package/dist/shared/bounceRules.js +50 -32
- package/dist/shared/bounceRules.js.map +1 -1
- package/dist/shared/exec.d.ts +21 -0
- package/dist/shared/exec.js +47 -0
- package/dist/shared/exec.js.map +1 -0
- package/dist/shared/json.d.ts +12 -6
- package/dist/shared/json.js +29 -23
- package/dist/shared/json.js.map +1 -1
- package/dist/shared/locate.d.ts +39 -0
- package/dist/shared/locate.js +129 -0
- package/dist/shared/locate.js.map +1 -0
- package/dist/shared/replyBody.d.ts +42 -32
- package/dist/shared/replyBody.js +84 -71
- package/dist/shared/replyBody.js.map +1 -1
- package/dist/templateBody.d.ts +63 -0
- package/dist/{outlookTemplateSections.js → templateBody.js} +72 -72
- package/dist/templateBody.js.map +1 -0
- package/dist/types.d.ts +360 -165
- package/dist/types.js +8 -9
- package/dist/types.js.map +1 -1
- package/dist/windows/accounts.d.ts +10 -1
- package/dist/windows/accounts.js +25 -13
- package/dist/windows/accounts.js.map +1 -1
- package/dist/windows/attachments.d.ts +9 -24
- package/dist/windows/attachments.js +94 -119
- package/dist/windows/attachments.js.map +1 -1
- package/dist/windows/bounces.d.ts +13 -26
- package/dist/windows/bounces.js +169 -200
- package/dist/windows/bounces.js.map +1 -1
- package/dist/windows/cleanup.d.ts +19 -23
- package/dist/windows/cleanup.js +93 -111
- package/dist/windows/cleanup.js.map +1 -1
- package/dist/windows/drafts.d.ts +16 -35
- package/dist/windows/drafts.js +167 -253
- package/dist/windows/drafts.js.map +1 -1
- package/dist/windows/folders.d.ts +8 -13
- package/dist/windows/folders.js +45 -52
- package/dist/windows/folders.js.map +1 -1
- package/dist/windows/index.d.ts +2 -27
- package/dist/windows/index.js +11 -65
- package/dist/windows/index.js.map +1 -1
- package/dist/windows/read.d.ts +24 -45
- package/dist/windows/read.js +266 -279
- package/dist/windows/read.js.map +1 -1
- package/dist/windows/run.d.ts +39 -39
- package/dist/windows/run.js +151 -111
- package/dist/windows/run.js.map +1 -1
- package/dist/windows/scripts.d.ts +71 -38
- package/dist/windows/scripts.js +123 -71
- package/dist/windows/scripts.js.map +1 -1
- package/dist/windows/send.d.ts +6 -11
- package/dist/windows/send.js +70 -79
- package/dist/windows/send.js.map +1 -1
- package/dist/windows/signatures.d.ts +4 -10
- package/dist/windows/signatures.js +30 -37
- package/dist/windows/signatures.js.map +1 -1
- package/dist/windows/templates.d.ts +12 -18
- package/dist/windows/templates.js +126 -151
- package/dist/windows/templates.js.map +1 -1
- package/package.json +5 -4
- package/dist/OutlookService.d.ts +0 -43
- package/dist/OutlookService.js +0 -137
- package/dist/OutlookService.js.map +0 -1
- package/dist/outlookTemplateSections.d.ts +0 -62
- package/dist/outlookTemplateSections.js.map +0 -1
- package/dist/shared/capabilities.d.ts +0 -3
- package/dist/shared/capabilities.js +0 -9
- package/dist/shared/capabilities.js.map +0 -1
package/dist/windows/send.js
CHANGED
|
@@ -6,109 +6,100 @@ exports.replyOutlookEmail = replyOutlookEmail;
|
|
|
6
6
|
const run_1 = require("./run");
|
|
7
7
|
const scripts_1 = require("./scripts");
|
|
8
8
|
const replyBody_1 = require("../shared/replyBody");
|
|
9
|
+
const errors_1 = require("../errors");
|
|
9
10
|
const json_1 = require("../shared/json");
|
|
10
|
-
const signatures_1 = require("./signatures");
|
|
11
|
-
const templates_1 = require("./templates");
|
|
12
11
|
/**
|
|
13
12
|
* Set the sending account through IDispatch reflection.
|
|
14
13
|
*
|
|
15
|
-
*
|
|
14
|
+
* Plain assignment (`$mail.SendUsingAccount = $account`) is a silent no-op under
|
|
16
15
|
* PowerShell's COM binding — the mail then goes out from whichever account
|
|
17
|
-
* Outlook considers default, which is
|
|
16
|
+
* Outlook considers default, which is exactly what this package must never do.
|
|
17
|
+
*
|
|
18
|
+
* Refuses a mailbox that resolved to a store with no Account: SendUsingAccount
|
|
19
|
+
* takes an Account, and sending with none falls through to the default account.
|
|
20
|
+
* The usual cause is a stale session — Outlook builds its Accounts list at
|
|
21
|
+
* startup, so a mailbox added since has its store mounted and readable but no
|
|
22
|
+
* Account until Outlook restarts — and the message says so.
|
|
18
23
|
*/
|
|
19
24
|
function sendUsingAccount(variable) {
|
|
20
|
-
return `
|
|
25
|
+
return `if ($account -eq $null) {
|
|
26
|
+
throw "${(0, errors_1.failureTag)('INVALID_REQUEST')}Mailbox '$target' has no sending account in this Outlook session. If it was added to the profile after Outlook started, restart Outlook and try again."
|
|
27
|
+
}
|
|
28
|
+
[void]$${variable}.GetType().InvokeMember('SendUsingAccount', [Reflection.BindingFlags]::SetProperty, $null, $${variable}, @($account))`;
|
|
21
29
|
}
|
|
22
30
|
/**
|
|
23
|
-
* What to do with a composed item
|
|
24
|
-
*
|
|
25
|
-
* `Display()` only shows the compose window — it never files the item, so a
|
|
26
|
-
* windowless draft has to `Save()` into Drafts explicitly.
|
|
31
|
+
* What to do with a composed item. `Display()` only shows a compose window and
|
|
32
|
+
* never files the item, so a windowless draft has to `Save()` into Drafts.
|
|
27
33
|
*/
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
function dispose(variable, disposition) {
|
|
35
|
+
switch (disposition) {
|
|
36
|
+
case 'send':
|
|
37
|
+
return `$${variable}.Send()`;
|
|
38
|
+
case 'display':
|
|
39
|
+
return `$${variable}.Display()`;
|
|
40
|
+
case 'save':
|
|
41
|
+
return `$${variable}.Save()`;
|
|
42
|
+
}
|
|
32
43
|
}
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
(0, run_1.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// The
|
|
40
|
-
//
|
|
41
|
-
const body = (0, run_1.scriptInput)('email-body',
|
|
42
|
-
const
|
|
44
|
+
/** Outlook takes recipients as one semicolon-separated line. */
|
|
45
|
+
function recipientLine(entries) {
|
|
46
|
+
return (0, run_1.psString)(entries.join('; '));
|
|
47
|
+
}
|
|
48
|
+
/** Send, display or file a new email. */
|
|
49
|
+
async function sendOutlookEmail(request) {
|
|
50
|
+
// The body goes through a file: inline, a large email bloats the script and
|
|
51
|
+
// every error that carries it.
|
|
52
|
+
const body = (0, run_1.scriptInput)('email-body', request.htmlBody);
|
|
53
|
+
const attach = request.attachments
|
|
54
|
+
.map(file => `[void]$mail.Attachments.Add(${(0, run_1.psString)(file)})`)
|
|
55
|
+
.join('\n');
|
|
56
|
+
try {
|
|
57
|
+
await (0, run_1.runPowerShell)(`${(0, scripts_1.accountScript)(request.account)}
|
|
43
58
|
$mail = $outlook.CreateItem(0)
|
|
44
59
|
${sendUsingAccount('mail')}
|
|
45
|
-
$mail.To =
|
|
46
|
-
$mail.CC =
|
|
47
|
-
$mail.
|
|
48
|
-
$mail.
|
|
49
|
-
${
|
|
50
|
-
${
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
await (0, run_1.runPowerShellFile)(script, 120000);
|
|
60
|
+
$mail.To = ${recipientLine(request.to)}
|
|
61
|
+
$mail.CC = ${recipientLine(request.cc)}
|
|
62
|
+
$mail.BCC = ${recipientLine(request.bcc)}
|
|
63
|
+
$mail.Subject = ${(0, run_1.psString)(request.subject)}
|
|
64
|
+
$mail.HTMLBody = [IO.File]::ReadAllText(${(0, run_1.psString)(body.path)}, [Text.Encoding]::UTF8)
|
|
65
|
+
${attach}
|
|
66
|
+
${dispose('mail', request.disposition)}
|
|
67
|
+
`, 'standard');
|
|
54
68
|
}
|
|
55
69
|
finally {
|
|
56
70
|
body.cleanup();
|
|
57
71
|
}
|
|
58
72
|
}
|
|
59
|
-
/**
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
async function replyOutlookEmail(params) {
|
|
67
|
-
(0, run_1.requireWindows)();
|
|
68
|
-
const insertHtml = await (0, replyBody_1.composeReplyHtml)(params, {
|
|
69
|
-
readTemplateEmails: templates_1.readTemplateEmails,
|
|
70
|
-
readOutlookSignatureHtml: signatures_1.readOutlookSignatureHtml,
|
|
71
|
-
listOutlookSignatures: signatures_1.listOutlookSignatures,
|
|
72
|
-
});
|
|
73
|
-
const body = (0, run_1.scriptInput)('reply-insert', insertHtml);
|
|
74
|
-
const replyMethod = params.replyAll ? 'ReplyAll' : 'Reply';
|
|
75
|
-
// Outlook builds the reply as a Word document whose own empty paragraph sits
|
|
76
|
-
// inside WordSection1; inserting straight after that div's opening tag puts
|
|
77
|
-
// the new text where the user's cursor would have been. A reply Outlook did
|
|
78
|
-
// not build that way falls back to just inside <body>.
|
|
79
|
-
const script = `${(0, scripts_1.accountScript)(params.emailAccount)}
|
|
80
|
-
${(0, scripts_1.getItemScript)(params.entryId, params.storeId)}
|
|
81
|
-
$reply = $item.${replyMethod}()
|
|
73
|
+
/** Reply to an email, with the composed HTML above the quoted original. */
|
|
74
|
+
async function replyOutlookEmail(request) {
|
|
75
|
+
const insert = (0, run_1.scriptInput)('reply-insert', request.html);
|
|
76
|
+
try {
|
|
77
|
+
const output = await (0, run_1.runPowerShellJson)(`${(0, scripts_1.accountScript)(request.account)}
|
|
78
|
+
${(0, scripts_1.itemLookupScript)(request.email)}
|
|
79
|
+
$reply = $item.${request.replyAll ? 'ReplyAll' : 'Reply'}()
|
|
82
80
|
${sendUsingAccount('reply')}
|
|
83
|
-
|
|
84
|
-
$
|
|
85
|
-
|
|
86
|
-
#
|
|
87
|
-
|
|
88
|
-
$
|
|
89
|
-
$
|
|
90
|
-
|
|
91
|
-
$
|
|
92
|
-
$idx = if ($m.Success) { $m.Index + $m.Length } else { 0 }
|
|
81
|
+
$insertHtml = [IO.File]::ReadAllText(${(0, run_1.psString)(insert.path)}, [Text.Encoding]::UTF8)
|
|
82
|
+
$anchor = ${(0, run_1.psString)(replyBody_1.WORD_SECTION_ANCHOR)}
|
|
83
|
+
# Read once: every HTMLBody read marshals the whole generated reply across COM,
|
|
84
|
+
# and a reply on a long thread runs to hundreds of KB.
|
|
85
|
+
$html = [string]$reply.HTMLBody
|
|
86
|
+
$at = $html.IndexOf($anchor)
|
|
87
|
+
if ($at -ge 0) { $at += $anchor.Length } else {
|
|
88
|
+
$bodyTag = [regex]::Match($html, '<body[^>]*>')
|
|
89
|
+
$at = if ($bodyTag.Success) { $bodyTag.Index + $bodyTag.Length } else { 0 }
|
|
93
90
|
}
|
|
94
|
-
$reply.HTMLBody = $html.Insert($
|
|
91
|
+
$reply.HTMLBody = $html.Insert($at, $insertHtml)
|
|
95
92
|
$to = [string]$reply.To
|
|
96
93
|
$subject = [string]$reply.Subject
|
|
97
|
-
$
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const parsed = (0, json_1.parseObject)(await (0, run_1.runPowerShell)(script, 60000));
|
|
104
|
-
return {
|
|
105
|
-
to: (0, json_1.str)(parsed.to),
|
|
106
|
-
subject: (0, json_1.str)(parsed.subject),
|
|
107
|
-
repliedToSender: (0, json_1.str)(parsed.repliedToSender),
|
|
108
|
-
};
|
|
94
|
+
${scripts_1.SENDER_SMTP_PS}
|
|
95
|
+
${dispose('reply', request.disposition)}
|
|
96
|
+
ConvertTo-Json -Compress @{ to = $to; subject = $subject; repliedToSender = $senderSmtp }
|
|
97
|
+
`, 'standard');
|
|
98
|
+
const result = (0, json_1.record)(output);
|
|
99
|
+
return { to: (0, json_1.str)(result.to), subject: (0, json_1.str)(result.subject), repliedToSender: (0, json_1.str)(result.repliedToSender) };
|
|
109
100
|
}
|
|
110
101
|
finally {
|
|
111
|
-
|
|
102
|
+
insert.cleanup();
|
|
112
103
|
}
|
|
113
104
|
}
|
|
114
105
|
//# sourceMappingURL=send.js.map
|
package/dist/windows/send.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/windows/send.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"send.js","sourceRoot":"","sources":["../../src/windows/send.ts"],"names":[],"mappings":"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","sourcesContent":["// Composing outgoing mail: a new email, and a reply to an existing one.\nimport { psString, runPowerShell, runPowerShellJson, scriptInput } from './run';\nimport { accountScript, itemLookupScript, SENDER_SMTP_PS } from './scripts';\nimport { WORD_SECTION_ANCHOR } from '../shared/replyBody';\nimport { failureTag } from '../errors';\nimport { record, str } from '../shared/json';\nimport type { Disposition, ReplyRequest, SendRequest } from '../backend';\nimport type { ReplyEmailResult } from '../types';\n\n/**\n * Set the sending account through IDispatch reflection.\n *\n * Plain assignment (`$mail.SendUsingAccount = $account`) is a silent no-op under\n * PowerShell's COM binding — the mail then goes out from whichever account\n * Outlook considers default, which is exactly what this package must never do.\n *\n * Refuses a mailbox that resolved to a store with no Account: SendUsingAccount\n * takes an Account, and sending with none falls through to the default account.\n * The usual cause is a stale session — Outlook builds its Accounts list at\n * startup, so a mailbox added since has its store mounted and readable but no\n * Account until Outlook restarts — and the message says so.\n */\nfunction sendUsingAccount(variable: string): string {\n return `if ($account -eq $null) {\n throw \"${failureTag('INVALID_REQUEST')}Mailbox '$target' has no sending account in this Outlook session. If it was added to the profile after Outlook started, restart Outlook and try again.\"\n}\n[void]$${variable}.GetType().InvokeMember('SendUsingAccount', [Reflection.BindingFlags]::SetProperty, $null, $${variable}, @($account))`;\n}\n\n/**\n * What to do with a composed item. `Display()` only shows a compose window and\n * never files the item, so a windowless draft has to `Save()` into Drafts.\n */\nfunction dispose(variable: string, disposition: Disposition): string {\n switch (disposition) {\n case 'send':\n return `$${variable}.Send()`;\n case 'display':\n return `$${variable}.Display()`;\n case 'save':\n return `$${variable}.Save()`;\n }\n}\n\n/** Outlook takes recipients as one semicolon-separated line. */\nfunction recipientLine(entries: readonly string[]): string {\n return psString(entries.join('; '));\n}\n\n/** Send, display or file a new email. */\nexport async function sendOutlookEmail(request: SendRequest): Promise<void> {\n // The body goes through a file: inline, a large email bloats the script and\n // every error that carries it.\n const body = scriptInput('email-body', request.htmlBody);\n const attach = request.attachments\n .map(file => `[void]$mail.Attachments.Add(${psString(file)})`)\n .join('\\n');\n try {\n await runPowerShell(`${accountScript(request.account)}\n$mail = $outlook.CreateItem(0)\n${sendUsingAccount('mail')}\n$mail.To = ${recipientLine(request.to)}\n$mail.CC = ${recipientLine(request.cc)}\n$mail.BCC = ${recipientLine(request.bcc)}\n$mail.Subject = ${psString(request.subject)}\n$mail.HTMLBody = [IO.File]::ReadAllText(${psString(body.path)}, [Text.Encoding]::UTF8)\n${attach}\n${dispose('mail', request.disposition)}\n`, 'standard');\n } finally {\n body.cleanup();\n }\n}\n\n/** Reply to an email, with the composed HTML above the quoted original. */\nexport async function replyOutlookEmail(request: ReplyRequest): Promise<ReplyEmailResult> {\n const insert = scriptInput('reply-insert', request.html);\n try {\n const output = await runPowerShellJson(`${accountScript(request.account)}\n${itemLookupScript(request.email)}\n$reply = $item.${request.replyAll ? 'ReplyAll' : 'Reply'}()\n${sendUsingAccount('reply')}\n$insertHtml = [IO.File]::ReadAllText(${psString(insert.path)}, [Text.Encoding]::UTF8)\n$anchor = ${psString(WORD_SECTION_ANCHOR)}\n# Read once: every HTMLBody read marshals the whole generated reply across COM,\n# and a reply on a long thread runs to hundreds of KB.\n$html = [string]$reply.HTMLBody\n$at = $html.IndexOf($anchor)\nif ($at -ge 0) { $at += $anchor.Length } else {\n $bodyTag = [regex]::Match($html, '<body[^>]*>')\n $at = if ($bodyTag.Success) { $bodyTag.Index + $bodyTag.Length } else { 0 }\n}\n$reply.HTMLBody = $html.Insert($at, $insertHtml)\n$to = [string]$reply.To\n$subject = [string]$reply.Subject\n${SENDER_SMTP_PS}\n${dispose('reply', request.disposition)}\nConvertTo-Json -Compress @{ to = $to; subject = $subject; repliedToSender = $senderSmtp }\n`, 'standard');\n const result = record(output);\n return {to: str(result.to), subject: str(result.subject), repliedToSender: str(result.repliedToSender)};\n } finally {\n insert.cleanup();\n }\n}\n"]}
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Names of the user's Outlook signatures (the ".htm" files), sorted. Empty off
|
|
3
|
-
* Windows. Reads from disk synchronously but is declared async to match macOS,
|
|
4
|
-
* which has to ask Outlook itself — one signature for both platforms.
|
|
5
|
-
*/
|
|
1
|
+
/** The signature names — the ".htm" files — sorted. */
|
|
6
2
|
export declare function listOutlookSignatures(): Promise<string[]>;
|
|
7
3
|
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* Outlook resolve and embed the images on Display/Send. Returns '' if the
|
|
12
|
-
* signature can't be found.
|
|
4
|
+
* A signature's HTML with its image references made absolute file: URIs, so an
|
|
5
|
+
* email body built from it resolves and embeds the images; '' when there is no
|
|
6
|
+
* signature by that name.
|
|
13
7
|
*/
|
|
14
8
|
export declare function readOutlookSignatureHtml(name: string): Promise<string>;
|
|
@@ -5,27 +5,24 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.listOutlookSignatures = listOutlookSignatures;
|
|
7
7
|
exports.readOutlookSignatureHtml = readOutlookSignatureHtml;
|
|
8
|
-
// Outlook
|
|
9
|
-
// "<name>_files" folder
|
|
10
|
-
//
|
|
8
|
+
// Outlook for Windows keeps each signature as "<name>.htm" (beside .rtf/.txt
|
|
9
|
+
// copies and a "<name>_files" folder of images) under
|
|
10
|
+
// %APPDATA%\Microsoft\Signatures, so these read the disk rather than COM.
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
13
|
const url_1 = require("url");
|
|
14
|
-
|
|
15
|
-
? path_1.default.join(process.env.APPDATA, 'Microsoft', 'Signatures')
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Names of the user's Outlook signatures (the ".htm" files), sorted. Empty off
|
|
19
|
-
* Windows. Reads from disk synchronously but is declared async to match macOS,
|
|
20
|
-
* which has to ask Outlook itself — one signature for both platforms.
|
|
21
|
-
*/
|
|
14
|
+
function signaturesDir() {
|
|
15
|
+
return process.env.APPDATA ? path_1.default.join(process.env.APPDATA, 'Microsoft', 'Signatures') : '';
|
|
16
|
+
}
|
|
17
|
+
/** The signature names — the ".htm" files — sorted. */
|
|
22
18
|
async function listOutlookSignatures() {
|
|
23
|
-
|
|
19
|
+
const dir = signaturesDir();
|
|
20
|
+
if (!dir)
|
|
24
21
|
return [];
|
|
25
22
|
try {
|
|
26
|
-
return fs_1.default.readdirSync(
|
|
27
|
-
.filter(
|
|
28
|
-
.map(
|
|
23
|
+
return fs_1.default.readdirSync(dir)
|
|
24
|
+
.filter(file => file.toLowerCase().endsWith('.htm'))
|
|
25
|
+
.map(file => file.slice(0, -'.htm'.length))
|
|
29
26
|
.sort((a, b) => a.localeCompare(b));
|
|
30
27
|
}
|
|
31
28
|
catch {
|
|
@@ -33,37 +30,33 @@ async function listOutlookSignatures() {
|
|
|
33
30
|
}
|
|
34
31
|
}
|
|
35
32
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* Outlook resolve and embed the images on Display/Send. Returns '' if the
|
|
40
|
-
* signature can't be found.
|
|
33
|
+
* A signature's HTML with its image references made absolute file: URIs, so an
|
|
34
|
+
* email body built from it resolves and embeds the images; '' when there is no
|
|
35
|
+
* signature by that name.
|
|
41
36
|
*/
|
|
42
37
|
async function readOutlookSignatureHtml(name) {
|
|
43
|
-
|
|
38
|
+
const dir = signaturesDir();
|
|
39
|
+
if (!dir)
|
|
44
40
|
return '';
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
const
|
|
48
|
-
const file = path_1.default.join(SIGNATURES_DIR, `${safe}.htm`);
|
|
41
|
+
// A bare name only — never a path — so a crafted name can't reach outside
|
|
42
|
+
// the Signatures folder.
|
|
43
|
+
const file = path_1.default.join(dir, `${path_1.default.basename(name)}.htm`);
|
|
49
44
|
if (!fs_1.default.existsSync(file))
|
|
50
45
|
return '';
|
|
51
|
-
//
|
|
52
|
-
// the
|
|
53
|
-
const
|
|
54
|
-
const
|
|
55
|
-
const charset = head.match(/charset=["']?([\w-]+)/i)?.[1] || 'utf-8';
|
|
46
|
+
// Signatures are saved in whatever charset Outlook chose (often
|
|
47
|
+
// windows-1252), so decode by the one the file declares.
|
|
48
|
+
const bytes = fs_1.default.readFileSync(file);
|
|
49
|
+
const charset = bytes.toString('latin1', 0, 2048).match(/charset=["']?([\w-]+)/i)?.[1] || 'utf-8';
|
|
56
50
|
let html;
|
|
57
51
|
try {
|
|
58
|
-
html = new TextDecoder(charset).decode(
|
|
52
|
+
html = new TextDecoder(charset).decode(bytes);
|
|
59
53
|
}
|
|
60
54
|
catch {
|
|
61
|
-
html =
|
|
55
|
+
html = bytes.toString('utf8');
|
|
62
56
|
}
|
|
63
|
-
// pathToFileURL encodes
|
|
64
|
-
//
|
|
65
|
-
const dirUri = (0, url_1.pathToFileURL)(
|
|
66
|
-
|
|
67
|
-
return html;
|
|
57
|
+
// pathToFileURL encodes the directory the way Outlook's relative refs are
|
|
58
|
+
// already encoded, so prefixing it keeps each reference a valid URI.
|
|
59
|
+
const dirUri = (0, url_1.pathToFileURL)(dir + path_1.default.sep).href;
|
|
60
|
+
return html.replace(/(src|background)=(["'])(?!https?:|cid:|data:|file:|mailto:|#)/gi, `$1=$2${dirUri}`);
|
|
68
61
|
}
|
|
69
62
|
//# sourceMappingURL=signatures.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signatures.js","sourceRoot":"","sources":["../../src/windows/signatures.ts"],"names":[],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"signatures.js","sourceRoot":"","sources":["../../src/windows/signatures.ts"],"names":[],"mappings":";;;;;AAYA,sDAWC;AAOD,4DAqBC;AAnDD,6EAA6E;AAC7E,sDAAsD;AACtD,0EAA0E;AAC1E,4CAAoB;AACpB,gDAAwB;AACxB,6BAAoC;AAEpC,SAAS,aAAa;IAClB,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAChG,CAAC;AAED,uDAAuD;AAChD,KAAK,UAAU,qBAAqB;IACvC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,IAAI,CAAC;QACD,OAAO,YAAE,CAAC,WAAW,CAAC,GAAG,CAAC;aACrB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;aACnD,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;aAC1C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,EAAE,CAAC;IACd,CAAC;AACL,CAAC;AAED;;;;GAIG;AACI,KAAK,UAAU,wBAAwB,CAAC,IAAY;IACvD,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,0EAA0E;IAC1E,yBAAyB;IACzB,MAAM,IAAI,GAAG,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,cAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1D,IAAI,CAAC,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,gEAAgE;IAChE,yDAAyD;IACzD,MAAM,KAAK,GAAG,YAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,wBAAwB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC;IAClG,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACD,IAAI,GAAG,IAAI,WAAW,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACL,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IACD,0EAA0E;IAC1E,qEAAqE;IACrE,MAAM,MAAM,GAAG,IAAA,mBAAa,EAAC,GAAG,GAAG,cAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;IAClD,OAAO,IAAI,CAAC,OAAO,CAAC,iEAAiE,EAAE,QAAQ,MAAM,EAAE,CAAC,CAAC;AAC7G,CAAC","sourcesContent":["// Outlook for Windows keeps each signature as \"<name>.htm\" (beside .rtf/.txt\n// copies and a \"<name>_files\" folder of images) under\n// %APPDATA%\\Microsoft\\Signatures, so these read the disk rather than COM.\nimport fs from 'fs';\nimport path from 'path';\nimport { pathToFileURL } from 'url';\n\nfunction signaturesDir(): string {\n return process.env.APPDATA ? path.join(process.env.APPDATA, 'Microsoft', 'Signatures') : '';\n}\n\n/** The signature names — the \".htm\" files — sorted. */\nexport async function listOutlookSignatures(): Promise<string[]> {\n const dir = signaturesDir();\n if (!dir) return [];\n try {\n return fs.readdirSync(dir)\n .filter(file => file.toLowerCase().endsWith('.htm'))\n .map(file => file.slice(0, -'.htm'.length))\n .sort((a, b) => a.localeCompare(b));\n } catch {\n return [];\n }\n}\n\n/**\n * A signature's HTML with its image references made absolute file: URIs, so an\n * email body built from it resolves and embeds the images; '' when there is no\n * signature by that name.\n */\nexport async function readOutlookSignatureHtml(name: string): Promise<string> {\n const dir = signaturesDir();\n if (!dir) return '';\n // A bare name only — never a path — so a crafted name can't reach outside\n // the Signatures folder.\n const file = path.join(dir, `${path.basename(name)}.htm`);\n if (!fs.existsSync(file)) return '';\n // Signatures are saved in whatever charset Outlook chose (often\n // windows-1252), so decode by the one the file declares.\n const bytes = fs.readFileSync(file);\n const charset = bytes.toString('latin1', 0, 2048).match(/charset=[\"']?([\\w-]+)/i)?.[1] || 'utf-8';\n let html: string;\n try {\n html = new TextDecoder(charset).decode(bytes);\n } catch {\n html = bytes.toString('utf8');\n }\n // pathToFileURL encodes the directory the way Outlook's relative refs are\n // already encoded, so prefixing it keeps each reference a valid URI.\n const dirUri = pathToFileURL(dir + path.sep).href;\n return html.replace(/(src|background)=([\"'])(?!https?:|cid:|data:|file:|mailto:|#)/gi, `$1=$2${dirUri}`);\n}\n"]}
|
|
@@ -1,25 +1,19 @@
|
|
|
1
|
+
import type { EditTemplateRequest, ReadTemplatesRequest, SaveTemplateRequest } from '../backend';
|
|
1
2
|
import type { SaveTemplateResult, TemplateFolderResult } from '../types';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* caller can ask the user whether to create it rather than fail.
|
|
4
|
+
* The template emails in a mailbox folder, newest first. A missing folder is
|
|
5
|
+
* reported as folderFound:false with the mailbox's top-level folder names
|
|
6
|
+
* rather than thrown, so a caller can offer to create it.
|
|
7
7
|
*/
|
|
8
|
-
export declare function readTemplateEmails(
|
|
8
|
+
export declare function readTemplateEmails(request: ReadTemplatesRequest): Promise<TemplateFolderResult>;
|
|
9
9
|
/**
|
|
10
|
-
* Save a new template email into a
|
|
11
|
-
*
|
|
12
|
-
* plain unsent mail (subject + HTML body) that can be edited in Outlook.
|
|
13
|
-
* Never overwrites an existing item — it always adds a new one, so check with
|
|
14
|
-
* readTemplateEmails first and only call after the user has agreed to create it.
|
|
10
|
+
* Save a new template email into a folder, creating the folder at the mailbox
|
|
11
|
+
* root when absent. Always adds a new item — never overwrites one.
|
|
15
12
|
*/
|
|
16
|
-
export declare function saveTemplateEmail(
|
|
13
|
+
export declare function saveTemplateEmail(request: SaveTemplateRequest): Promise<SaveTemplateResult>;
|
|
17
14
|
/**
|
|
18
|
-
* Open
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* Deliberately unbounded: the run lasts as long as the person is editing. Give
|
|
22
|
-
* it an AbortSignal (`bridge.withOptions({ signal })`) if the caller needs a way
|
|
23
|
-
* to give up on them.
|
|
15
|
+
* Open a compose window pre-filled with the HTML, wait for it to close, and
|
|
16
|
+
* return what was saved (Ctrl+S) — or, failing a save, the body as it last
|
|
17
|
+
* stood while the window was open. Leftover editor drafts are removed.
|
|
24
18
|
*/
|
|
25
|
-
export declare function editEmailTemplate(
|
|
19
|
+
export declare function editEmailTemplate(request: EditTemplateRequest): Promise<string>;
|