@dawnlit/outlook-bridge 1.1.1 → 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 -38
- package/dist/mac/read.js +154 -210
- 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 -167
- package/dist/types.js +8 -9
- package/dist/types.js.map +1 -1
- package/dist/windows/accounts.d.ts +7 -12
- package/dist/windows/accounts.js +19 -34
- 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 +168 -200
- package/dist/windows/bounces.js.map +1 -1
- package/dist/windows/cleanup.d.ts +19 -23
- package/dist/windows/cleanup.js +92 -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 +44 -52
- package/dist/windows/folders.js.map +1 -1
- package/dist/windows/index.d.ts +2 -27
- package/dist/windows/index.js +11 -66
- package/dist/windows/index.js.map +1 -1
- package/dist/windows/read.d.ts +24 -60
- package/dist/windows/read.js +261 -306
- 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 +65 -55
- package/dist/windows/scripts.js +109 -85
- package/dist/windows/scripts.js.map +1 -1
- package/dist/windows/send.d.ts +6 -11
- package/dist/windows/send.js +68 -88
- 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 +122 -149
- package/dist/windows/templates.js.map +1 -1
- package/package.json +4 -3
- 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/drafts.js
CHANGED
|
@@ -1,313 +1,227 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sendAllDrafts = sendAllDrafts;
|
|
4
3
|
exports.listOutlookDrafts = listOutlookDrafts;
|
|
5
|
-
exports.deleteOutlookDrafts = deleteOutlookDrafts;
|
|
6
4
|
exports.sendDrafts = sendDrafts;
|
|
7
|
-
|
|
5
|
+
exports.sendAllDrafts = sendAllDrafts;
|
|
6
|
+
exports.deleteOutlookDrafts = deleteOutlookDrafts;
|
|
7
|
+
// Drafts: which ones belong to an account, and listing, sending or deleting them.
|
|
8
8
|
const run_1 = require("./run");
|
|
9
9
|
const scripts_1 = require("./scripts");
|
|
10
10
|
const json_1 = require("../shared/json");
|
|
11
|
+
const mail_1 = require("../mail");
|
|
11
12
|
/**
|
|
12
13
|
* Resolve the Drafts folders belonging to one account into `$scan`, and define
|
|
13
|
-
* `Test-DraftMatches` over them. Emitted into every drafts script so listing,
|
|
14
|
-
*
|
|
15
|
-
* Expects `$target`, `$outlook`, `$ns` and `$account` to already be set.
|
|
14
|
+
* `Test-DraftMatches` over them. Emitted into every drafts script, so listing,
|
|
15
|
+
* sending and deleting can never disagree about which drafts are this account's.
|
|
16
16
|
*
|
|
17
|
-
* Two Drafts folders can hold
|
|
18
|
-
* -
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* account, not necessarily this one, so it's left alone.
|
|
25
|
-
* (When those two resolve to the same folder — the account IS the default — it is
|
|
26
|
-
* scanned once, under the more permissive "own store" rule.)
|
|
17
|
+
* Two Drafts folders can hold one account's drafts, so both are scanned:
|
|
18
|
+
* - the account's OWN store's Drafts, where a draft composed in that mailbox
|
|
19
|
+
* lands. A draft there with no explicit sending account still belongs to it.
|
|
20
|
+
* - the DEFAULT store's Drafts, where a program's `Save()` files a draft
|
|
21
|
+
* whatever account it sends from. Only drafts stamped with this account
|
|
22
|
+
* match there; an unstamped one belongs to the default account.
|
|
23
|
+
* When the two are the same folder it is scanned once, under the first rule.
|
|
27
24
|
*
|
|
28
|
-
*
|
|
29
|
-
* mail. Non-mail items (meeting requests, reports) are ignored.
|
|
25
|
+
* Non-mail items (meeting requests, reports) never match.
|
|
30
26
|
*/
|
|
31
27
|
const DRAFTS_SCAN_PS = `
|
|
32
|
-
# Collect the Drafts folders to scan, deduped by id. includeNull marks the account's
|
|
33
|
-
# own store, where a draft with no explicit send-account still belongs to it.
|
|
34
28
|
$scan = @()
|
|
35
|
-
$
|
|
36
|
-
|
|
29
|
+
$seenFolders = @{}
|
|
37
30
|
$homeDrafts = $null
|
|
38
|
-
try { $homeDrafts = $store.GetDefaultFolder(
|
|
31
|
+
try { $homeDrafts = $store.GetDefaultFolder(${(0, run_1.psInt)(mail_1.FolderId.Drafts)}) } catch {}
|
|
39
32
|
if ($homeDrafts -ne $null) {
|
|
40
|
-
$
|
|
41
|
-
$scan += [
|
|
33
|
+
$seenFolders["$($homeDrafts.StoreID)|$($homeDrafts.EntryID)"] = $true
|
|
34
|
+
$scan += [PSCustomObject]@{ folder = $homeDrafts; includeUnstamped = $true }
|
|
42
35
|
}
|
|
43
|
-
|
|
44
|
-
$
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
$k = "$($defDrafts.StoreID)|$($defDrafts.EntryID)"
|
|
48
|
-
if (-not $seen.ContainsKey($k)) {
|
|
49
|
-
$seen[$k] = $true
|
|
50
|
-
$scan += [pscustomobject]@{ folder = $defDrafts; includeNull = $false }
|
|
51
|
-
}
|
|
36
|
+
$defaultDrafts = $null
|
|
37
|
+
try { $defaultDrafts = $ns.GetDefaultFolder(${(0, run_1.psInt)(mail_1.FolderId.Drafts)}) } catch {}
|
|
38
|
+
if ($defaultDrafts -ne $null -and -not $seenFolders.ContainsKey("$($defaultDrafts.StoreID)|$($defaultDrafts.EntryID)")) {
|
|
39
|
+
$scan += [PSCustomObject]@{ folder = $defaultDrafts; includeUnstamped = $false }
|
|
52
40
|
}
|
|
53
|
-
|
|
54
|
-
# SendUsingAccount is null for a draft that never set one, so guard the access.
|
|
55
|
-
function Test-DraftMatches($item, $includeNull) {
|
|
41
|
+
function Test-DraftMatches($item, [bool]$includeUnstamped) {
|
|
56
42
|
if ($item.Class -ne 43) { return $false } # olMail only
|
|
57
|
-
$
|
|
58
|
-
try { $
|
|
59
|
-
if ($
|
|
60
|
-
return ($
|
|
61
|
-
}
|
|
62
|
-
`;
|
|
63
|
-
/** Session, account and the drafts scan rule — the prelude all three share. */
|
|
64
|
-
function draftsScript(emailAccount) {
|
|
65
|
-
return (0, scripts_1.accountScript)(emailAccount) + scripts_1.DELIVERY_STORE_PS + DRAFTS_SCAN_PS;
|
|
43
|
+
$sender = $null
|
|
44
|
+
try { $sender = $item.SendUsingAccount } catch {}
|
|
45
|
+
if ($sender -eq $null) { return $includeUnstamped }
|
|
46
|
+
return ([string]$sender.SmtpAddress -ieq $target)
|
|
66
47
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
# Snapshot the matching mail items first — Send() removes each from Drafts, so
|
|
78
|
-
# sending inside a live enumeration would skip every other item.
|
|
79
|
-
$items = @()
|
|
80
|
-
foreach ($entry in $scan) {
|
|
81
|
-
foreach ($it in $entry.folder.Items) {
|
|
82
|
-
if (Test-DraftMatches $it $entry.includeNull) { $items += $it }
|
|
48
|
+
# An id is acted on only once it is PROVED to be one of this account's drafts:
|
|
49
|
+
# resolved in one of the scanned folders' stores, sitting in that folder, and
|
|
50
|
+
# bound to this account (or unbound, in its own store).
|
|
51
|
+
$draftFolderRules = @{}
|
|
52
|
+
foreach ($entry in $scan) { $draftFolderRules[[string]$entry.folder.EntryID] = $entry.includeUnstamped }
|
|
53
|
+
function Resolve-OwnDraft([string]$id, [string]$verb) {
|
|
54
|
+
$draft = $null
|
|
55
|
+
foreach ($entry in $scan) {
|
|
56
|
+
try { $draft = $ns.GetItemFromID($id, $entry.folder.StoreID) } catch { $draft = $null }
|
|
57
|
+
if ($draft -ne $null) { break }
|
|
83
58
|
}
|
|
59
|
+
if ($draft -eq $null) { throw "no such item in this account's Drafts folders" }
|
|
60
|
+
$parentId = ''
|
|
61
|
+
try { $parentId = [string]$draft.Parent.EntryID } catch {}
|
|
62
|
+
if (-not $draftFolderRules.ContainsKey($parentId)) { throw "item is not in this account's Drafts folder - refusing to $verb" }
|
|
63
|
+
if (-not (Test-DraftMatches $draft $draftFolderRules[$parentId])) { throw "draft is not bound to $target - refusing to $verb" }
|
|
64
|
+
return $draft
|
|
84
65
|
}
|
|
85
|
-
|
|
86
|
-
$sent = 0
|
|
87
|
-
$failed = @()
|
|
88
|
-
foreach ($m in $items) {
|
|
89
|
-
try {
|
|
90
|
-
$m.Send()
|
|
91
|
-
$sent++
|
|
92
|
-
} catch {
|
|
93
|
-
$subj = ''
|
|
94
|
-
try { $subj = $m.Subject } catch {}
|
|
95
|
-
$failed += [pscustomobject]@{ subject = $subj; error = $_.Exception.Message }
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
[pscustomobject]@{ sent = $sent; failed = @($failed) } | ConvertTo-Json -Compress -Depth 4
|
|
100
66
|
`;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return {
|
|
104
|
-
sent: (0, json_1.num)(parsed.sent),
|
|
105
|
-
failed: (0, json_1.toArray)(parsed.failed).map(f => {
|
|
106
|
-
const e = (0, json_1.record)(f);
|
|
107
|
-
return { subject: (0, json_1.str)(e.subject), error: (0, json_1.str)(e.error) };
|
|
108
|
-
}),
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
catch (error) {
|
|
112
|
-
// A malformed report is not worth failing a send that already happened;
|
|
113
|
-
// a run that never got that far has already rejected above.
|
|
114
|
-
if (error instanceof SyntaxError)
|
|
115
|
-
return { sent: 0, failed: [] };
|
|
116
|
-
throw error;
|
|
117
|
-
}
|
|
67
|
+
function draftsScript(account) {
|
|
68
|
+
return (0, scripts_1.accountScript)(account) + scripts_1.DELIVERY_STORE_PS + DRAFTS_SCAN_PS;
|
|
118
69
|
}
|
|
119
|
-
/**
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
* against the same folders, so repeating a ~700-char store id on every row would be
|
|
125
|
-
* pure payload. Bodies are previewed, never returned whole — a templated reply body
|
|
126
|
-
* runs to tens of thousands of characters and a folder's worth would blow the cap.
|
|
127
|
-
*/
|
|
128
|
-
async function listOutlookDrafts(emailAccount, limit = 100, previewChars = 300) {
|
|
129
|
-
(0, run_1.requireWindows)();
|
|
130
|
-
const script = `${draftsScript(emailAccount)}
|
|
131
|
-
$previewChars = ${previewChars}
|
|
70
|
+
/** The account's drafts, newest first. Bodies are previewed, never returned whole. */
|
|
71
|
+
async function listOutlookDrafts(request) {
|
|
72
|
+
const output = await (0, run_1.runPowerShellJson)(`${draftsScript(request.account)}
|
|
73
|
+
$previewChars = ${(0, run_1.psInt)(request.previewChars)}
|
|
74
|
+
$limit = ${(0, run_1.psInt)(request.limit)}
|
|
132
75
|
$rows = @()
|
|
133
|
-
$
|
|
76
|
+
$folderPaths = @()
|
|
134
77
|
foreach ($entry in $scan) {
|
|
135
|
-
$
|
|
136
|
-
try { $
|
|
137
|
-
$
|
|
78
|
+
$folderPath = ''
|
|
79
|
+
try { $folderPath = [string]$entry.folder.FolderPath } catch {}
|
|
80
|
+
$folderPaths += $folderPath
|
|
138
81
|
foreach ($it in $entry.folder.Items) {
|
|
139
|
-
if (-not (Test-DraftMatches $it $entry.
|
|
140
|
-
$
|
|
141
|
-
$to = ''; try { $to = $it.To } catch {}
|
|
142
|
-
$addrs = @()
|
|
82
|
+
if (-not (Test-DraftMatches $it $entry.includeUnstamped)) { continue }
|
|
83
|
+
$addresses = @()
|
|
143
84
|
try {
|
|
144
85
|
foreach ($r in $it.Recipients) {
|
|
145
|
-
$
|
|
146
|
-
try { $
|
|
147
|
-
|
|
148
|
-
if ($
|
|
86
|
+
$address = ''
|
|
87
|
+
try { $address = [string]$r.Address } catch {}
|
|
88
|
+
# An unresolved Exchange entry has a DN for an address; its name reads better.
|
|
89
|
+
if ($address.StartsWith('/')) { try { $address = [string]$r.Name } catch {} }
|
|
90
|
+
if ($address) { $addresses += $address }
|
|
149
91
|
}
|
|
150
92
|
} catch {}
|
|
151
|
-
$
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
$
|
|
157
|
-
try { $
|
|
158
|
-
$
|
|
159
|
-
try { $
|
|
160
|
-
$rows += [
|
|
161
|
-
entryId
|
|
162
|
-
subject
|
|
163
|
-
to
|
|
164
|
-
toEmails
|
|
165
|
-
bodyPreview
|
|
166
|
-
hasAttachments = $
|
|
167
|
-
lastModified
|
|
168
|
-
folderPath
|
|
93
|
+
$preview = ''
|
|
94
|
+
if ($previewChars -gt 0) {
|
|
95
|
+
try { $preview = ([string]$it.Body -replace '\\s+', ' ').Trim() } catch {}
|
|
96
|
+
if ($preview.Length -gt $previewChars) { $preview = $preview.Substring(0, $previewChars) }
|
|
97
|
+
}
|
|
98
|
+
$modified = ''
|
|
99
|
+
try { $modified = $it.LastModificationTime.ToString('yyyy-MM-dd HH:mm') } catch {}
|
|
100
|
+
$hasAttachments = $false
|
|
101
|
+
try { $hasAttachments = ($it.Attachments.Count -gt 0) } catch {}
|
|
102
|
+
$rows += [PSCustomObject]@{
|
|
103
|
+
entryId = [string]$it.EntryID
|
|
104
|
+
subject = [string]$it.Subject
|
|
105
|
+
to = [string]$it.To
|
|
106
|
+
toEmails = @($addresses)
|
|
107
|
+
bodyPreview = $preview
|
|
108
|
+
hasAttachments = $hasAttachments
|
|
109
|
+
lastModified = $modified
|
|
110
|
+
folderPath = $folderPath
|
|
169
111
|
}
|
|
170
112
|
}
|
|
171
113
|
}
|
|
172
114
|
$sorted = @($rows | Sort-Object -Property lastModified -Descending)
|
|
173
115
|
$total = $sorted.Count
|
|
174
|
-
if ($total -gt $
|
|
175
|
-
ConvertTo-Json
|
|
176
|
-
|
|
177
|
-
|
|
116
|
+
if ($total -gt $limit) { $sorted = @($sorted[0..($limit - 1)]) }
|
|
117
|
+
ConvertTo-Json -Depth 4 -InputObject ([PSCustomObject]@{
|
|
118
|
+
account = $target
|
|
119
|
+
foldersScanned = @($folderPaths)
|
|
120
|
+
count = $total
|
|
121
|
+
truncated = ($total -gt $limit)
|
|
122
|
+
drafts = @($sorted)
|
|
123
|
+
})
|
|
124
|
+
`, 'scan');
|
|
125
|
+
const e = (0, json_1.record)(output);
|
|
178
126
|
return {
|
|
179
|
-
account: (0, json_1.str)(
|
|
180
|
-
foldersScanned: (0, json_1.toArray)(
|
|
181
|
-
count: (0, json_1.num)(
|
|
182
|
-
truncated:
|
|
183
|
-
drafts: (0, json_1.toArray)(
|
|
184
|
-
const
|
|
127
|
+
account: (0, json_1.str)(e.account) || request.account,
|
|
128
|
+
foldersScanned: (0, json_1.toArray)(e.foldersScanned).map(json_1.str),
|
|
129
|
+
count: (0, json_1.num)(e.count),
|
|
130
|
+
truncated: (0, json_1.bool)(e.truncated),
|
|
131
|
+
drafts: (0, json_1.toArray)(e.drafts).map(row => {
|
|
132
|
+
const d = (0, json_1.record)(row);
|
|
185
133
|
return {
|
|
186
|
-
entryId: (0, json_1.str)(
|
|
187
|
-
subject: (0, json_1.str)(
|
|
188
|
-
to: (0, json_1.str)(
|
|
189
|
-
toEmails: (0, json_1.strList)(
|
|
190
|
-
bodyPreview: (0, json_1.str)(
|
|
191
|
-
hasAttachments:
|
|
192
|
-
lastModified: (0, json_1.str)(
|
|
193
|
-
folderPath: (0, json_1.str)(
|
|
134
|
+
entryId: (0, json_1.str)(d.entryId),
|
|
135
|
+
subject: (0, json_1.str)(d.subject),
|
|
136
|
+
to: (0, json_1.str)(d.to),
|
|
137
|
+
toEmails: (0, json_1.strList)(d.toEmails),
|
|
138
|
+
bodyPreview: (0, json_1.str)(d.bodyPreview),
|
|
139
|
+
hasAttachments: (0, json_1.bool)(d.hasAttachments),
|
|
140
|
+
lastModified: (0, json_1.str)(d.lastModified),
|
|
141
|
+
folderPath: (0, json_1.str)(d.folderPath),
|
|
194
142
|
};
|
|
195
143
|
}),
|
|
196
144
|
};
|
|
197
145
|
}
|
|
198
|
-
/**
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
* Every id must resolve to a mail item sitting in one of THIS account's Drafts
|
|
203
|
-
* folders before anything is deleted; an id pointing at ordinary mail, or at another
|
|
204
|
-
* account's draft, is refused and reported. Without that gate this would be a
|
|
205
|
-
* general-purpose "delete any email by id" tool, which is not what it is for.
|
|
206
|
-
*/
|
|
207
|
-
async function deleteOutlookDrafts(emailAccount, entryIds) {
|
|
208
|
-
(0, run_1.requireWindows)();
|
|
209
|
-
if (entryIds.length === 0)
|
|
210
|
-
return { deleted: 0, failed: [] };
|
|
211
|
-
const script = `${draftsScript(emailAccount)}
|
|
212
|
-
# Index this account's Drafts folders by EntryID, so a resolved item can be PROVED
|
|
213
|
-
# to live in one of them before it is deleted.
|
|
214
|
-
$draftFolderIds = @{}
|
|
215
|
-
foreach ($entry in $scan) { $draftFolderIds[$entry.folder.EntryID] = $entry.includeNull }
|
|
216
|
-
|
|
217
|
-
$deleted = 0
|
|
146
|
+
/** Send the named drafts, each proved to be this account's first. */
|
|
147
|
+
async function sendDrafts(request) {
|
|
148
|
+
const output = await (0, run_1.runPowerShellJson)(`${draftsScript(request.account)}
|
|
149
|
+
$sent = 0
|
|
218
150
|
$failed = @()
|
|
219
|
-
foreach ($id in
|
|
151
|
+
foreach ($id in ${(0, run_1.psArray)(request.entryIds)}) {
|
|
152
|
+
$subject = ''
|
|
220
153
|
try {
|
|
221
|
-
$
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if ($it -ne $null) { break }
|
|
226
|
-
} catch { $it = $null }
|
|
227
|
-
}
|
|
228
|
-
if ($it -eq $null) { throw "no such item in this account's Drafts folders" }
|
|
229
|
-
$parentId = ''
|
|
230
|
-
try { $parentId = $it.Parent.EntryID } catch {}
|
|
231
|
-
if (-not $draftFolderIds.ContainsKey($parentId)) {
|
|
232
|
-
throw "item is not in this account's Drafts folder - refusing to delete"
|
|
233
|
-
}
|
|
234
|
-
if (-not (Test-DraftMatches $it $draftFolderIds[$parentId])) {
|
|
235
|
-
throw "draft is not bound to $target - refusing to delete"
|
|
236
|
-
}
|
|
237
|
-
$it.Delete()
|
|
238
|
-
$deleted++
|
|
154
|
+
$draft = Resolve-OwnDraft $id 'send'
|
|
155
|
+
try { $subject = [string]$draft.Subject } catch {}
|
|
156
|
+
$draft.Send()
|
|
157
|
+
$sent++
|
|
239
158
|
} catch {
|
|
240
|
-
$failed += [
|
|
159
|
+
$failed += [PSCustomObject]@{ entryId = $id; subject = $subject; error = $_.Exception.Message }
|
|
241
160
|
}
|
|
242
161
|
}
|
|
243
|
-
ConvertTo-Json @{
|
|
244
|
-
|
|
245
|
-
const
|
|
246
|
-
return {
|
|
247
|
-
deleted: (0, json_1.num)(parsed.deleted),
|
|
248
|
-
failed: (0, json_1.toArray)(parsed.failed).map(f => {
|
|
249
|
-
const e = (0, json_1.record)(f);
|
|
250
|
-
return { entryId: (0, json_1.str)(e.entryId), error: (0, json_1.str)(e.error) };
|
|
251
|
-
}),
|
|
252
|
-
};
|
|
162
|
+
ConvertTo-Json -Compress -Depth 3 -InputObject ([PSCustomObject]@{ sent = $sent; failed = @($failed) })
|
|
163
|
+
`, 'scan');
|
|
164
|
+
const e = (0, json_1.record)(output);
|
|
165
|
+
return { sent: (0, json_1.num)(e.sent), failed: (0, json_1.itemFailures)(e.failed) };
|
|
253
166
|
}
|
|
254
167
|
/**
|
|
255
|
-
* Send
|
|
256
|
-
* `listOutlookDrafts` and a user review pass — instead of `sendAllDrafts`'s
|
|
257
|
-
* account-wide sweep.
|
|
168
|
+
* Send every draft belonging to the account.
|
|
258
169
|
*
|
|
259
|
-
*
|
|
260
|
-
* item
|
|
261
|
-
*
|
|
262
|
-
* refused and reported rather than sent.
|
|
170
|
+
* The matching items are snapshotted before the first send: sending moves an
|
|
171
|
+
* item out of Drafts, and sending during a live enumeration would skip every
|
|
172
|
+
* other one.
|
|
263
173
|
*/
|
|
264
|
-
async function
|
|
265
|
-
(0, run_1.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
foreach ($entry in $scan) { $draftFolderIds[$entry.folder.EntryID] = $entry.includeNull }
|
|
273
|
-
|
|
174
|
+
async function sendAllDrafts(request) {
|
|
175
|
+
const output = await (0, run_1.runPowerShellJson)(`${draftsScript(request.account)}
|
|
176
|
+
$drafts = @()
|
|
177
|
+
foreach ($entry in $scan) {
|
|
178
|
+
foreach ($it in $entry.folder.Items) {
|
|
179
|
+
if (Test-DraftMatches $it $entry.includeUnstamped) { $drafts += $it }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
274
182
|
$sent = 0
|
|
275
183
|
$failed = @()
|
|
276
|
-
foreach ($
|
|
277
|
-
$
|
|
184
|
+
foreach ($draft in $drafts) {
|
|
185
|
+
$entryId = ''
|
|
186
|
+
$subject = ''
|
|
187
|
+
try { $entryId = [string]$draft.EntryID } catch {}
|
|
188
|
+
try { $subject = [string]$draft.Subject } catch {}
|
|
278
189
|
try {
|
|
279
|
-
$
|
|
280
|
-
foreach ($entry in $scan) {
|
|
281
|
-
try {
|
|
282
|
-
$it = $ns.GetItemFromID($id, $entry.folder.StoreID)
|
|
283
|
-
if ($it -ne $null) { break }
|
|
284
|
-
} catch { $it = $null }
|
|
285
|
-
}
|
|
286
|
-
if ($it -eq $null) { throw "no such item in this account's Drafts folders" }
|
|
287
|
-
try { $subj = $it.Subject } catch {}
|
|
288
|
-
$parentId = ''
|
|
289
|
-
try { $parentId = $it.Parent.EntryID } catch {}
|
|
290
|
-
if (-not $draftFolderIds.ContainsKey($parentId)) {
|
|
291
|
-
throw "item is not in this account's Drafts folder - refusing to send"
|
|
292
|
-
}
|
|
293
|
-
if (-not (Test-DraftMatches $it $draftFolderIds[$parentId])) {
|
|
294
|
-
throw "draft is not bound to $target - refusing to send"
|
|
295
|
-
}
|
|
296
|
-
$it.Send()
|
|
190
|
+
$draft.Send()
|
|
297
191
|
$sent++
|
|
298
192
|
} catch {
|
|
299
|
-
$failed += [
|
|
193
|
+
$failed += [PSCustomObject]@{ entryId = $entryId; subject = $subject; error = $_.Exception.Message }
|
|
300
194
|
}
|
|
301
195
|
}
|
|
302
|
-
[
|
|
303
|
-
|
|
304
|
-
const
|
|
305
|
-
return {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
196
|
+
ConvertTo-Json -Compress -Depth 3 -InputObject ([PSCustomObject]@{ sent = $sent; failed = @($failed) })
|
|
197
|
+
`, 'scan');
|
|
198
|
+
const e = (0, json_1.record)(output);
|
|
199
|
+
return { sent: (0, json_1.num)(e.sent), failed: (0, json_1.itemFailures)(e.failed) };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Delete the named drafts, each proved to be this account's first. Outlook's
|
|
203
|
+
* Delete() moves an item to Deleted Items, so a mistake stays recoverable.
|
|
204
|
+
* Without the proof this would be a general "delete any email by id" call,
|
|
205
|
+
* which is not what it is for.
|
|
206
|
+
*/
|
|
207
|
+
async function deleteOutlookDrafts(request) {
|
|
208
|
+
const output = await (0, run_1.runPowerShellJson)(`${draftsScript(request.account)}
|
|
209
|
+
$deleted = 0
|
|
210
|
+
$failed = @()
|
|
211
|
+
foreach ($id in ${(0, run_1.psArray)(request.entryIds)}) {
|
|
212
|
+
$subject = ''
|
|
213
|
+
try {
|
|
214
|
+
$draft = Resolve-OwnDraft $id 'delete'
|
|
215
|
+
try { $subject = [string]$draft.Subject } catch {}
|
|
216
|
+
$draft.Delete()
|
|
217
|
+
$deleted++
|
|
218
|
+
} catch {
|
|
219
|
+
$failed += [PSCustomObject]@{ entryId = $id; subject = $subject; error = $_.Exception.Message }
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
ConvertTo-Json -Compress -Depth 3 -InputObject ([PSCustomObject]@{ deleted = $deleted; failed = @($failed) })
|
|
223
|
+
`, 'scan');
|
|
224
|
+
const e = (0, json_1.record)(output);
|
|
225
|
+
return { deleted: (0, json_1.num)(e.deleted), failed: (0, json_1.itemFailures)(e.failed) };
|
|
312
226
|
}
|
|
313
227
|
//# sourceMappingURL=drafts.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drafts.js","sourceRoot":"","sources":["../../src/windows/drafts.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"drafts.js","sourceRoot":"","sources":["../../src/windows/drafts.ts"],"names":[],"mappings":"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","sourcesContent":["// Drafts: which ones belong to an account, and listing, sending or deleting them.\nimport { psArray, psInt, runPowerShellJson } from './run';\nimport { accountScript, DELIVERY_STORE_PS } from './scripts';\nimport { bool, itemFailures, num, record, str, strList, toArray } from '../shared/json';\nimport { FolderId } from '../mail';\nimport type { AccountRequest, EntryIdsRequest, ListDraftsRequest } from '../backend';\nimport type { DeleteDraftsResult, ListDraftsResult, SendDraftsResult } from '../types';\n\n/**\n * Resolve the Drafts folders belonging to one account into `$scan`, and define\n * `Test-DraftMatches` over them. Emitted into every drafts script, so listing,\n * sending and deleting can never disagree about which drafts are this account's.\n *\n * Two Drafts folders can hold one account's drafts, so both are scanned:\n * - the account's OWN store's Drafts, where a draft composed in that mailbox\n * lands. A draft there with no explicit sending account still belongs to it.\n * - the DEFAULT store's Drafts, where a program's `Save()` files a draft\n * whatever account it sends from. Only drafts stamped with this account\n * match there; an unstamped one belongs to the default account.\n * When the two are the same folder it is scanned once, under the first rule.\n *\n * Non-mail items (meeting requests, reports) never match.\n */\nconst DRAFTS_SCAN_PS = `\n$scan = @()\n$seenFolders = @{}\n$homeDrafts = $null\ntry { $homeDrafts = $store.GetDefaultFolder(${psInt(FolderId.Drafts)}) } catch {}\nif ($homeDrafts -ne $null) {\n $seenFolders[\"$($homeDrafts.StoreID)|$($homeDrafts.EntryID)\"] = $true\n $scan += [PSCustomObject]@{ folder = $homeDrafts; includeUnstamped = $true }\n}\n$defaultDrafts = $null\ntry { $defaultDrafts = $ns.GetDefaultFolder(${psInt(FolderId.Drafts)}) } catch {}\nif ($defaultDrafts -ne $null -and -not $seenFolders.ContainsKey(\"$($defaultDrafts.StoreID)|$($defaultDrafts.EntryID)\")) {\n $scan += [PSCustomObject]@{ folder = $defaultDrafts; includeUnstamped = $false }\n}\nfunction Test-DraftMatches($item, [bool]$includeUnstamped) {\n if ($item.Class -ne 43) { return $false } # olMail only\n $sender = $null\n try { $sender = $item.SendUsingAccount } catch {}\n if ($sender -eq $null) { return $includeUnstamped }\n return ([string]$sender.SmtpAddress -ieq $target)\n}\n# An id is acted on only once it is PROVED to be one of this account's drafts:\n# resolved in one of the scanned folders' stores, sitting in that folder, and\n# bound to this account (or unbound, in its own store).\n$draftFolderRules = @{}\nforeach ($entry in $scan) { $draftFolderRules[[string]$entry.folder.EntryID] = $entry.includeUnstamped }\nfunction Resolve-OwnDraft([string]$id, [string]$verb) {\n $draft = $null\n foreach ($entry in $scan) {\n try { $draft = $ns.GetItemFromID($id, $entry.folder.StoreID) } catch { $draft = $null }\n if ($draft -ne $null) { break }\n }\n if ($draft -eq $null) { throw \"no such item in this account's Drafts folders\" }\n $parentId = ''\n try { $parentId = [string]$draft.Parent.EntryID } catch {}\n if (-not $draftFolderRules.ContainsKey($parentId)) { throw \"item is not in this account's Drafts folder - refusing to $verb\" }\n if (-not (Test-DraftMatches $draft $draftFolderRules[$parentId])) { throw \"draft is not bound to $target - refusing to $verb\" }\n return $draft\n}\n`;\n\nfunction draftsScript(account: string): string {\n return accountScript(account) + DELIVERY_STORE_PS + DRAFTS_SCAN_PS;\n}\n\n/** The account's drafts, newest first. Bodies are previewed, never returned whole. */\nexport async function listOutlookDrafts(request: ListDraftsRequest): Promise<ListDraftsResult> {\n const output = await runPowerShellJson(`${draftsScript(request.account)}\n$previewChars = ${psInt(request.previewChars)}\n$limit = ${psInt(request.limit)}\n$rows = @()\n$folderPaths = @()\nforeach ($entry in $scan) {\n $folderPath = ''\n try { $folderPath = [string]$entry.folder.FolderPath } catch {}\n $folderPaths += $folderPath\n foreach ($it in $entry.folder.Items) {\n if (-not (Test-DraftMatches $it $entry.includeUnstamped)) { continue }\n $addresses = @()\n try {\n foreach ($r in $it.Recipients) {\n $address = ''\n try { $address = [string]$r.Address } catch {}\n # An unresolved Exchange entry has a DN for an address; its name reads better.\n if ($address.StartsWith('/')) { try { $address = [string]$r.Name } catch {} }\n if ($address) { $addresses += $address }\n }\n } catch {}\n $preview = ''\n if ($previewChars -gt 0) {\n try { $preview = ([string]$it.Body -replace '\\\\s+', ' ').Trim() } catch {}\n if ($preview.Length -gt $previewChars) { $preview = $preview.Substring(0, $previewChars) }\n }\n $modified = ''\n try { $modified = $it.LastModificationTime.ToString('yyyy-MM-dd HH:mm') } catch {}\n $hasAttachments = $false\n try { $hasAttachments = ($it.Attachments.Count -gt 0) } catch {}\n $rows += [PSCustomObject]@{\n entryId = [string]$it.EntryID\n subject = [string]$it.Subject\n to = [string]$it.To\n toEmails = @($addresses)\n bodyPreview = $preview\n hasAttachments = $hasAttachments\n lastModified = $modified\n folderPath = $folderPath\n }\n }\n}\n$sorted = @($rows | Sort-Object -Property lastModified -Descending)\n$total = $sorted.Count\nif ($total -gt $limit) { $sorted = @($sorted[0..($limit - 1)]) }\nConvertTo-Json -Depth 4 -InputObject ([PSCustomObject]@{\n account = $target\n foldersScanned = @($folderPaths)\n count = $total\n truncated = ($total -gt $limit)\n drafts = @($sorted)\n})\n`, 'scan');\n const e = record(output);\n return {\n account: str(e.account) || request.account,\n foldersScanned: toArray(e.foldersScanned).map(str),\n count: num(e.count),\n truncated: bool(e.truncated),\n drafts: toArray(e.drafts).map(row => {\n const d = record(row);\n return {\n entryId: str(d.entryId),\n subject: str(d.subject),\n to: str(d.to),\n toEmails: strList(d.toEmails),\n bodyPreview: str(d.bodyPreview),\n hasAttachments: bool(d.hasAttachments),\n lastModified: str(d.lastModified),\n folderPath: str(d.folderPath),\n };\n }),\n };\n}\n\n/** Send the named drafts, each proved to be this account's first. */\nexport async function sendDrafts(request: EntryIdsRequest): Promise<SendDraftsResult> {\n const output = await runPowerShellJson(`${draftsScript(request.account)}\n$sent = 0\n$failed = @()\nforeach ($id in ${psArray(request.entryIds)}) {\n $subject = ''\n try {\n $draft = Resolve-OwnDraft $id 'send'\n try { $subject = [string]$draft.Subject } catch {}\n $draft.Send()\n $sent++\n } catch {\n $failed += [PSCustomObject]@{ entryId = $id; subject = $subject; error = $_.Exception.Message }\n }\n}\nConvertTo-Json -Compress -Depth 3 -InputObject ([PSCustomObject]@{ sent = $sent; failed = @($failed) })\n`, 'scan');\n const e = record(output);\n return {sent: num(e.sent), failed: itemFailures(e.failed)};\n}\n\n/**\n * Send every draft belonging to the account.\n *\n * The matching items are snapshotted before the first send: sending moves an\n * item out of Drafts, and sending during a live enumeration would skip every\n * other one.\n */\nexport async function sendAllDrafts(request: AccountRequest): Promise<SendDraftsResult> {\n const output = await runPowerShellJson(`${draftsScript(request.account)}\n$drafts = @()\nforeach ($entry in $scan) {\n foreach ($it in $entry.folder.Items) {\n if (Test-DraftMatches $it $entry.includeUnstamped) { $drafts += $it }\n }\n}\n$sent = 0\n$failed = @()\nforeach ($draft in $drafts) {\n $entryId = ''\n $subject = ''\n try { $entryId = [string]$draft.EntryID } catch {}\n try { $subject = [string]$draft.Subject } catch {}\n try {\n $draft.Send()\n $sent++\n } catch {\n $failed += [PSCustomObject]@{ entryId = $entryId; subject = $subject; error = $_.Exception.Message }\n }\n}\nConvertTo-Json -Compress -Depth 3 -InputObject ([PSCustomObject]@{ sent = $sent; failed = @($failed) })\n`, 'scan');\n const e = record(output);\n return {sent: num(e.sent), failed: itemFailures(e.failed)};\n}\n\n/**\n * Delete the named drafts, each proved to be this account's first. Outlook's\n * Delete() moves an item to Deleted Items, so a mistake stays recoverable.\n * Without the proof this would be a general \"delete any email by id\" call,\n * which is not what it is for.\n */\nexport async function deleteOutlookDrafts(request: EntryIdsRequest): Promise<DeleteDraftsResult> {\n const output = await runPowerShellJson(`${draftsScript(request.account)}\n$deleted = 0\n$failed = @()\nforeach ($id in ${psArray(request.entryIds)}) {\n $subject = ''\n try {\n $draft = Resolve-OwnDraft $id 'delete'\n try { $subject = [string]$draft.Subject } catch {}\n $draft.Delete()\n $deleted++\n } catch {\n $failed += [PSCustomObject]@{ entryId = $id; subject = $subject; error = $_.Exception.Message }\n }\n}\nConvertTo-Json -Compress -Depth 3 -InputObject ([PSCustomObject]@{ deleted = $deleted; failed = @($failed) })\n`, 'scan');\n const e = record(output);\n return {deleted: num(e.deleted), failed: itemFailures(e.failed)};\n}\n"]}
|
|
@@ -1,16 +1,11 @@
|
|
|
1
|
+
import type { ListFoldersRequest, MoveRequest } from '../backend';
|
|
1
2
|
import type { InboxFolderInfo, MoveEmailsResult } from '../types';
|
|
2
|
-
/**
|
|
3
|
-
export declare function listInboxFolders(
|
|
3
|
+
/** The folders under an account's Inbox, depth-first. */
|
|
4
|
+
export declare function listInboxFolders(request: ListFoldersRequest): Promise<InboxFolderInfo[]>;
|
|
4
5
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* this takes well-known roots at all: it means the destructive path and the filing
|
|
10
|
-
* path are one tool, and the destructive one is undoable from the folder it lands in.
|
|
11
|
-
*
|
|
12
|
-
* `createIfMissing` builds the WHOLE missing chain, so a nested destination
|
|
13
|
-
* ("Clients\\Acme\\2026") is one call rather than a manual mkdir first.
|
|
14
|
-
* NOTE: moving changes an item's EntryID — the passed ids are dead afterwards.
|
|
6
|
+
* File emails into a folder of the account: one under the Inbox, or a
|
|
7
|
+
* well-known one by name. Moving to Deleted Items is how mail is deleted
|
|
8
|
+
* recoverably. Moving rewrites each item's entry id, so the ids passed in are
|
|
9
|
+
* dead afterwards.
|
|
15
10
|
*/
|
|
16
|
-
export declare function moveOutlookEmails(
|
|
11
|
+
export declare function moveOutlookEmails(request: MoveRequest): Promise<MoveEmailsResult>;
|