@rndmjoker/proton-mcp 0.4.11 → 0.4.13

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.
Files changed (97) hide show
  1. package/README.md +61 -16
  2. package/dist/bridge/certificate.d.ts +33 -1
  3. package/dist/bridge/certificate.js +82 -9
  4. package/dist/bridge/certificate.js.map +1 -1
  5. package/dist/bridge/connection.js +31 -6
  6. package/dist/bridge/connection.js.map +1 -1
  7. package/dist/bridge/errors.d.ts +8 -0
  8. package/dist/bridge/errors.js +28 -3
  9. package/dist/bridge/errors.js.map +1 -1
  10. package/dist/config.d.ts +6 -0
  11. package/dist/config.js +1 -0
  12. package/dist/config.js.map +1 -1
  13. package/dist/credentials/availability.d.ts +5 -0
  14. package/dist/credentials/availability.js +7 -0
  15. package/dist/credentials/availability.js.map +1 -1
  16. package/dist/credentials/encrypted-file.js +7 -2
  17. package/dist/credentials/encrypted-file.js.map +1 -1
  18. package/dist/credentials/keyring.d.ts +12 -2
  19. package/dist/credentials/keyring.js +44 -6
  20. package/dist/credentials/keyring.js.map +1 -1
  21. package/dist/credentials/plain-file.js +8 -2
  22. package/dist/credentials/plain-file.js.map +1 -1
  23. package/dist/credentials/session-bus.d.ts +38 -0
  24. package/dist/credentials/session-bus.js +68 -0
  25. package/dist/credentials/session-bus.js.map +1 -0
  26. package/dist/credentials/store.d.ts +5 -1
  27. package/dist/credentials/store.js.map +1 -1
  28. package/dist/mail/attachments.d.ts +12 -2
  29. package/dist/mail/attachments.js +19 -11
  30. package/dist/mail/attachments.js.map +1 -1
  31. package/dist/mail/compose.d.ts +65 -9
  32. package/dist/mail/compose.js +147 -11
  33. package/dist/mail/compose.js.map +1 -1
  34. package/dist/mail/drafts.d.ts +13 -2
  35. package/dist/mail/drafts.js +69 -18
  36. package/dist/mail/drafts.js.map +1 -1
  37. package/dist/mail/ids.js +6 -4
  38. package/dist/mail/ids.js.map +1 -1
  39. package/dist/mail/messages.d.ts +25 -2
  40. package/dist/mail/messages.js +37 -4
  41. package/dist/mail/messages.js.map +1 -1
  42. package/dist/mail/saved-attachments.d.ts +128 -0
  43. package/dist/mail/saved-attachments.js +448 -0
  44. package/dist/mail/saved-attachments.js.map +1 -0
  45. package/dist/mail/search.d.ts +67 -3
  46. package/dist/mail/search.js +134 -23
  47. package/dist/mail/search.js.map +1 -1
  48. package/dist/mail/send.js +24 -9
  49. package/dist/mail/send.js.map +1 -1
  50. package/dist/mail/uid-search.d.ts +17 -0
  51. package/dist/mail/uid-search.js +25 -0
  52. package/dist/mail/uid-search.js.map +1 -0
  53. package/dist/mime/filenames.d.ts +24 -0
  54. package/dist/mime/filenames.js +58 -0
  55. package/dist/mime/filenames.js.map +1 -0
  56. package/dist/mime/parts.d.ts +34 -0
  57. package/dist/mime/parts.js +92 -0
  58. package/dist/mime/parts.js.map +1 -0
  59. package/dist/server.js +39 -6
  60. package/dist/server.js.map +1 -1
  61. package/dist/session.d.ts +15 -3
  62. package/dist/session.js +35 -8
  63. package/dist/session.js.map +1 -1
  64. package/dist/tools/confirm.js +39 -11
  65. package/dist/tools/confirm.js.map +1 -1
  66. package/dist/tools/drafts.d.ts +3 -0
  67. package/dist/tools/drafts.js +18 -6
  68. package/dist/tools/drafts.js.map +1 -1
  69. package/dist/tools/filters.d.ts +40 -0
  70. package/dist/tools/filters.js +89 -0
  71. package/dist/tools/filters.js.map +1 -0
  72. package/dist/tools/format.d.ts +8 -0
  73. package/dist/tools/format.js +29 -6
  74. package/dist/tools/format.js.map +1 -1
  75. package/dist/tools/guidance.js +27 -3
  76. package/dist/tools/guidance.js.map +1 -1
  77. package/dist/tools/messages.js +7 -2
  78. package/dist/tools/messages.js.map +1 -1
  79. package/dist/tools/pending-view.d.ts +7 -0
  80. package/dist/tools/pending-view.js +4 -1
  81. package/dist/tools/pending-view.js.map +1 -1
  82. package/dist/tools/saved.d.ts +15 -0
  83. package/dist/tools/saved.js +107 -0
  84. package/dist/tools/saved.js.map +1 -0
  85. package/dist/tools/send.js +4 -1
  86. package/dist/tools/send.js.map +1 -1
  87. package/dist/web/layout.js +2 -0
  88. package/dist/web/layout.js.map +1 -1
  89. package/dist/web/pages.js +1 -0
  90. package/dist/web/pages.js.map +1 -1
  91. package/dist/web/pending.d.ts +4 -0
  92. package/dist/web/pending.js +32 -4
  93. package/dist/web/pending.js.map +1 -1
  94. package/dist/web/server.d.ts +16 -5
  95. package/dist/web/server.js +12 -8
  96. package/dist/web/server.js.map +1 -1
  97. package/package.json +16 -8
@@ -0,0 +1,448 @@
1
+ /**
2
+ * Attachments saved to this machine, and taking them away again.
3
+ *
4
+ * Saving means writing what Proton keeps encrypted, decrypted, to a disk. That
5
+ * is worth doing so an assistant can open a PDF or a spreadsheet rather than
6
+ * only say it exists, and it is bounded on every side:
7
+ *
8
+ * - **Only into a directory the user named**, in PROTON_MCP_ATTACHMENT_DIR, and
9
+ * only through the environment: a setting in the web interface would be
10
+ * within reach of an assistant, which gets that page's address. Without the
11
+ * variable nothing is written and the tool says how to set it up.
12
+ * - **Nothing outside it.** A folder is checked after it is resolved, `..` is
13
+ * refused, and so is a symbolic link anywhere on the way.
14
+ * - **The name comes from the sender** and is reduced to a plain one, with the
15
+ * rule in mime/filenames.ts. An existing file is never replaced: the file is
16
+ * created with O_EXCL, which also refuses to follow a link in its place.
17
+ * - **Readable by the user only**, 0600, and folders created here 0700.
18
+ * - **Gone again within the hour.** Decided by Tobi on 2026-11-02: a file lies
19
+ * unattended for at most one hour, and is deleted when the server stops.
20
+ * Better the assistant fetches it again than that it lies around for days.
21
+ * A server that was killed leaves its files to the next one to start.
22
+ *
23
+ * What may be deleted is recorded per process, in this server's own
24
+ * configuration directory rather than in the directory the files go to. Each
25
+ * running server writes only its own record, so two of them never overwrite
26
+ * each other's. Before anything is deleted it is checked to be the very file
27
+ * that was saved: the same inode, size and modification time, a plain file and
28
+ * not a link. A file replaced or changed since is left where it is, because in
29
+ * the directory there may be files that are not ours.
30
+ *
31
+ * A folder created for a file goes again once it is empty, decided by Tobi on
32
+ * 2026-11-02: its name can say what was in it. Recorded like the files, with
33
+ * its inode, and removed with rmdir, which takes an empty folder and nothing
34
+ * else. A folder that was there before is never recorded and never removed.
35
+ */
36
+ import { createHash } from 'node:crypto';
37
+ import { closeSync, constants, fstatSync, lstatSync, mkdirSync, openSync, readdirSync, readFileSync, realpathSync, renameSync, rmdirSync, rmSync, statSync, unlinkSync, writeFileSync, writeSync, } from 'node:fs';
38
+ import { homedir } from 'node:os';
39
+ import { basename, dirname, isAbsolute, join, parse as parsePath, relative, resolve, sep } from 'node:path';
40
+ import { BridgeError } from '../bridge/errors.js';
41
+ import { plainFileName } from '../mime/filenames.js';
42
+ /** How long a saved file may lie, fixed. */
43
+ export const KEEP_MS = 60 * 60 * 1000;
44
+ /** Proton accepts up to 25 MB per message, so no real attachment is larger. */
45
+ export const MAX_SAVE_BYTES = 25 * 1024 * 1024;
46
+ /** How long past its hour a file of a server that still runs is left to it. */
47
+ const GRACE_MS = 10 * 60 * 1000;
48
+ /** The variable that names the directory. */
49
+ export const ATTACHMENT_DIR_VARIABLE = 'PROTON_MCP_ATTACHMENT_DIR';
50
+ /** A refusal to save or delete, explained. Reported like any explained failure. */
51
+ export class SaveRefused extends BridgeError {
52
+ name = 'SaveRefused';
53
+ }
54
+ /** Where the records live unless told otherwise. */
55
+ export function defaultRecordDir() {
56
+ return join(homedir(), '.config', 'proton-mcp', 'saved-attachments');
57
+ }
58
+ function processAlive(pid) {
59
+ try {
60
+ process.kill(pid, 0);
61
+ return true;
62
+ }
63
+ catch (error) {
64
+ // EPERM: it exists, it is merely not ours to signal.
65
+ return error.code === 'EPERM';
66
+ }
67
+ }
68
+ /** A file whose identity no longer matches the record is not ours to delete. */
69
+ function deleteIfSame(entry) {
70
+ let info;
71
+ try {
72
+ info = lstatSync(entry.path);
73
+ }
74
+ catch {
75
+ return 'missing';
76
+ }
77
+ const same = info.isFile() &&
78
+ info.dev === entry.dev &&
79
+ info.ino === entry.ino &&
80
+ info.size === entry.size &&
81
+ info.mtimeMs === entry.mtimeMs;
82
+ if (!same)
83
+ return 'left';
84
+ try {
85
+ unlinkSync(entry.path);
86
+ }
87
+ catch {
88
+ // Not deletable, permissions for instance. Left and reported rather than
89
+ // letting one file stop the clearing up of all the others.
90
+ return 'left';
91
+ }
92
+ return 'deleted';
93
+ }
94
+ /**
95
+ * Removes the folders this server created that are empty now, deepest first,
96
+ * so that a parent can follow its child.
97
+ *
98
+ * rmdir and nothing stronger: whatever someone put into a folder keeps it
99
+ * standing. And only while it is still the folder that was created, the same
100
+ * inode, never a link in its place. Returns what went and what is left to
101
+ * watch; a folder that is gone or no longer ours is neither.
102
+ *
103
+ * The limit of that: a folder deleted and made again under the same name can
104
+ * get the same inode back, as it did on the CI's file system on 2026-11-02,
105
+ * and is then taken for the one created here. rmdir removes it only if it is
106
+ * empty. The time a folder was made would tell them apart, but where the file
107
+ * system does not keep it, Node reports the ctime instead, which changes as
108
+ * soon as a file inside goes, and no folder would ever be removed.
109
+ */
110
+ function pruneFolders(folders) {
111
+ const removed = [];
112
+ const kept = [];
113
+ const deepestFirst = [...folders].sort((a, b) => b.path.split(sep).length - a.path.split(sep).length);
114
+ for (const folder of deepestFirst) {
115
+ let info;
116
+ try {
117
+ info = lstatSync(folder.path);
118
+ }
119
+ catch {
120
+ continue;
121
+ }
122
+ if (!info.isDirectory() || info.dev !== folder.dev || info.ino !== folder.ino)
123
+ continue;
124
+ try {
125
+ rmdirSync(folder.path);
126
+ removed.push(folder.path);
127
+ }
128
+ catch {
129
+ // Not empty, or not removable: watched until this server stops.
130
+ kept.push(folder);
131
+ }
132
+ }
133
+ return { removed, kept };
134
+ }
135
+ export class SavedAttachments {
136
+ #root;
137
+ #recordDir;
138
+ #now;
139
+ #pid;
140
+ #isAlive;
141
+ #startedAt;
142
+ #timers = new Map();
143
+ #entries = [];
144
+ #folders = [];
145
+ constructor(options) {
146
+ this.#root = options.root?.trim() || undefined;
147
+ this.#recordDir = options.recordDir ?? defaultRecordDir();
148
+ this.#now = options.now ?? Date.now;
149
+ this.#pid = options.pid ?? process.pid;
150
+ this.#isAlive = options.isAlive ?? processAlive;
151
+ this.#startedAt = new Date(this.#now()).toISOString();
152
+ }
153
+ /** Whether the user set a directory at all. */
154
+ get configured() {
155
+ return this.#root !== undefined;
156
+ }
157
+ /**
158
+ * Throws the explanation when nothing can be saved: no directory set, or one
159
+ * that is not there. Called before a message is fetched, so that nothing is
160
+ * decrypted only to be thrown away.
161
+ */
162
+ assertUsable() {
163
+ this.#realRoot();
164
+ }
165
+ /** This process's own record, named so that no two processes share one. */
166
+ get #recordPath() {
167
+ return join(this.#recordDir, `${this.#pid}-${Date.parse(this.#startedAt)}.json`);
168
+ }
169
+ /** The directory, resolved, or the reason it cannot be used. */
170
+ #realRoot() {
171
+ const root = this.#root;
172
+ if (root === undefined) {
173
+ throw new SaveRefused(`Saving attachments is not set up. The user has to set ${ATTACHMENT_DIR_VARIABLE} to a ` +
174
+ 'directory, in the configuration this MCP server is started with, and restart the client. ' +
175
+ 'Files are saved there and nowhere else. Until then an attachment can only be read with ' +
176
+ 'get_attachment, and only when it is text.');
177
+ }
178
+ if (!isAbsolute(root)) {
179
+ throw new SaveRefused(`${ATTACHMENT_DIR_VARIABLE} has to be an absolute path, and "${root}" is not.`);
180
+ }
181
+ let real;
182
+ try {
183
+ real = realpathSync(root);
184
+ }
185
+ catch {
186
+ throw new SaveRefused(`${ATTACHMENT_DIR_VARIABLE} names "${root}", which does not exist. It is not created ` +
187
+ 'here, so that a mistyped path does not quietly become a new place for mail to land.');
188
+ }
189
+ if (!statSync(real).isDirectory()) {
190
+ throw new SaveRefused(`${ATTACHMENT_DIR_VARIABLE} names "${root}", which is not a directory.`);
191
+ }
192
+ return real;
193
+ }
194
+ /**
195
+ * The folder to write into, inside the directory and nowhere else.
196
+ *
197
+ * Missing folders are created one level at a time, each checked before the
198
+ * next, so that no folder is ever created through a link pointing out.
199
+ */
200
+ #targetDir(folder) {
201
+ const real = this.#realRoot();
202
+ if (!folder)
203
+ return real;
204
+ const requested = resolve(isAbsolute(folder) ? folder : join(real, folder));
205
+ // Compared with the directory as given and as resolved: a directory that
206
+ // is itself reached through a link reads differently from either side.
207
+ const inside = [this.#root, real]
208
+ .map((base) => relative(base, requested))
209
+ .find((rel) => rel === '' || (!rel.startsWith('..') && !isAbsolute(rel)));
210
+ if (inside === undefined) {
211
+ throw new SaveRefused(`"${folder}" is outside the directory set for attachments, ${this.#root}. Files are saved ` +
212
+ 'there or in a folder inside it, never anywhere else.');
213
+ }
214
+ let current = real;
215
+ for (const part of inside.split(sep).filter(Boolean)) {
216
+ current = join(current, part);
217
+ let info;
218
+ try {
219
+ info = lstatSync(current);
220
+ }
221
+ catch {
222
+ mkdirSync(current, { mode: 0o700 });
223
+ const made = lstatSync(current);
224
+ this.#folders.push({ path: current, dev: made.dev, ino: made.ino });
225
+ continue;
226
+ }
227
+ if (info.isSymbolicLink()) {
228
+ throw new SaveRefused(`"${current}" is a symbolic link, and a folder reached through one is refused.`);
229
+ }
230
+ if (!info.isDirectory())
231
+ throw new SaveRefused(`"${current}" is not a folder.`);
232
+ }
233
+ return current;
234
+ }
235
+ /** Writes the record of this process, atomically, or removes it once empty. */
236
+ #writeRecord() {
237
+ if (this.#entries.length === 0 && this.#folders.length === 0) {
238
+ rmSync(this.#recordPath, { force: true });
239
+ return;
240
+ }
241
+ mkdirSync(this.#recordDir, { recursive: true, mode: 0o700 });
242
+ const record = {
243
+ pid: this.#pid,
244
+ startedAt: this.#startedAt,
245
+ entries: this.#entries,
246
+ folders: this.#folders,
247
+ };
248
+ const temporary = `${this.#recordPath}.tmp`;
249
+ writeFileSync(temporary, `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600 });
250
+ renameSync(temporary, this.#recordPath);
251
+ }
252
+ /**
253
+ * Saves one attachment. Synchronous on purpose: the file and its record are
254
+ * written in one step, with nothing in between that could stop halfway.
255
+ */
256
+ save(name, content, contentType, folder) {
257
+ if (content.length > MAX_SAVE_BYTES) {
258
+ throw new SaveRefused(`The attachment is ${content.length} bytes, more than the ${MAX_SAVE_BYTES} this server saves.`);
259
+ }
260
+ try {
261
+ return this.#write(name, content, contentType, folder);
262
+ }
263
+ catch (error) {
264
+ // A folder made for a file that never arrived is not left behind.
265
+ this.#settleFolders();
266
+ throw error;
267
+ }
268
+ }
269
+ #write(name, content, contentType, folder) {
270
+ const directory = this.#targetDir(folder);
271
+ const plain = plainFileName(name, 'attachment');
272
+ const { name: stem, ext } = parsePath(plain);
273
+ for (let n = 1; n <= 100; n++) {
274
+ const path = join(directory, n === 1 ? plain : `${stem} (${n})${ext}`);
275
+ let fd;
276
+ try {
277
+ // O_EXCL: never an existing file, and never through a link in its place.
278
+ fd = openSync(path, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0), 0o600);
279
+ }
280
+ catch (error) {
281
+ if (error.code === 'EEXIST')
282
+ continue;
283
+ throw error;
284
+ }
285
+ let ino;
286
+ try {
287
+ writeSync(fd, content);
288
+ ino = fstatSync(fd).ino;
289
+ }
290
+ finally {
291
+ closeSync(fd);
292
+ }
293
+ const info = lstatSync(path);
294
+ if (info.ino !== ino)
295
+ throw new SaveRefused(`"${path}" was replaced while it was being written.`);
296
+ const savedAt = this.#now();
297
+ const expiresAt = savedAt + KEEP_MS;
298
+ this.#entries.push({
299
+ path,
300
+ dev: info.dev,
301
+ ino: info.ino,
302
+ size: info.size,
303
+ mtimeMs: info.mtimeMs,
304
+ savedAt: new Date(savedAt).toISOString(),
305
+ expiresAt: new Date(expiresAt).toISOString(),
306
+ });
307
+ this.#writeRecord();
308
+ // Exactly on the hour rather than on the next sweep. Unref'd, so a
309
+ // waiting deletion never keeps the process alive.
310
+ this.#timers.set(path, setTimeout(() => this.#expire(path), KEEP_MS).unref());
311
+ return {
312
+ path,
313
+ size: info.size,
314
+ contentType,
315
+ sha256: createHash('sha256').update(content).digest('hex'),
316
+ expiresAt: new Date(expiresAt),
317
+ };
318
+ }
319
+ throw new SaveRefused(`There are already a hundred files named like "${plain}" in "${directory}".`);
320
+ }
321
+ /** Deletes one file whose hour is up, if it is still the one that was saved. */
322
+ #expire(path) {
323
+ this.#timers.delete(path);
324
+ const entry = this.#entries.find((e) => e.path === path);
325
+ if (!entry)
326
+ return;
327
+ deleteIfSame(entry);
328
+ this.#entries = this.#entries.filter((e) => e !== entry);
329
+ this.#settleFolders();
330
+ }
331
+ /** Removes the empty ones among the folders created here, and writes the record. */
332
+ #settleFolders() {
333
+ const { removed, kept } = pruneFolders(this.#folders);
334
+ this.#folders = kept;
335
+ this.#writeRecord();
336
+ return removed;
337
+ }
338
+ /** The record a path stands for, read the way save wrote it. */
339
+ #entryFor(path) {
340
+ const absolute = resolve(path);
341
+ const exact = this.#entries.find((e) => e.path === absolute);
342
+ if (exact)
343
+ return exact;
344
+ // The same file reached through a link in its folder's path.
345
+ try {
346
+ const real = join(realpathSync(dirname(absolute)), basename(absolute));
347
+ return this.#entries.find((e) => e.path === real);
348
+ }
349
+ catch {
350
+ return undefined;
351
+ }
352
+ }
353
+ /**
354
+ * Deletes what this server saved: everything, or the files named.
355
+ *
356
+ * A path it did not save is not touched, whatever it is.
357
+ */
358
+ delete(paths) {
359
+ const report = { deleted: [], left: [], missing: [], unknown: [], folders: [] };
360
+ const chosen = [];
361
+ if (paths === undefined) {
362
+ chosen.push(...this.#entries);
363
+ }
364
+ else {
365
+ for (const path of paths) {
366
+ const entry = this.#entryFor(path);
367
+ if (entry)
368
+ chosen.push(entry);
369
+ else
370
+ report.unknown.push(path);
371
+ }
372
+ }
373
+ for (const entry of chosen) {
374
+ const outcome = deleteIfSame(entry);
375
+ report[outcome === 'deleted' ? 'deleted' : outcome === 'left' ? 'left' : 'missing'].push(entry.path);
376
+ clearTimeout(this.#timers.get(entry.path));
377
+ this.#timers.delete(entry.path);
378
+ }
379
+ this.#entries = this.#entries.filter((e) => !chosen.includes(e));
380
+ report.folders = this.#settleFolders();
381
+ return report;
382
+ }
383
+ /**
384
+ * Clears up after other servers: everything of one that no longer runs, and
385
+ * what one that still runs has left lying well past its hour.
386
+ */
387
+ sweepOthers() {
388
+ let names;
389
+ try {
390
+ names = readdirSync(this.#recordDir);
391
+ }
392
+ catch {
393
+ return;
394
+ }
395
+ const own = basename(this.#recordPath);
396
+ for (const name of names) {
397
+ if (!name.endsWith('.json') || name === own)
398
+ continue;
399
+ const path = join(this.#recordDir, name);
400
+ let record;
401
+ try {
402
+ record = JSON.parse(readFileSync(path, 'utf8'));
403
+ }
404
+ catch {
405
+ continue;
406
+ }
407
+ const dead = !this.#isAlive(record.pid);
408
+ const keep = [];
409
+ for (const entry of record.entries ?? []) {
410
+ const stale = this.#now() > Date.parse(entry.expiresAt) + GRACE_MS;
411
+ if (dead || stale)
412
+ deleteIfSame(entry);
413
+ else
414
+ keep.push(entry);
415
+ }
416
+ // The folders of a server that still runs stay its own business: it may
417
+ // be about to save into one of them.
418
+ if (dead)
419
+ pruneFolders(record.folders ?? []);
420
+ if (keep.length === 0) {
421
+ rmSync(path, { force: true });
422
+ }
423
+ else if (keep.length !== (record.entries ?? []).length) {
424
+ const temporary = `${path}.tmp`;
425
+ writeFileSync(temporary, `${JSON.stringify({ ...record, entries: keep }, null, 2)}\n`, { mode: 0o600 });
426
+ renameSync(temporary, path);
427
+ }
428
+ }
429
+ }
430
+ /** Called once at start: clears up what earlier servers left. */
431
+ start() {
432
+ this.sweepOthers();
433
+ }
434
+ /** Called on the way out: deletes everything this server saved. */
435
+ stop() {
436
+ for (const timer of this.#timers.values())
437
+ clearTimeout(timer);
438
+ this.#timers.clear();
439
+ if (this.#entries.length || this.#folders.length)
440
+ this.delete();
441
+ rmSync(this.#recordPath, { force: true });
442
+ }
443
+ /** What this server has saved and not yet deleted. */
444
+ get entries() {
445
+ return this.#entries;
446
+ }
447
+ }
448
+ //# sourceMappingURL=saved-attachments.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saved-attachments.js","sourceRoot":"","sources":["../../src/mail/saved-attachments.ts"],"names":[],"mappings":"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"}
@@ -12,10 +12,32 @@
12
12
  *
13
13
  * Metadata queries are cheap by comparison: unread came back in 3 ms, a size
14
14
  * filter in 437 ms.
15
+ *
16
+ * Text is the exception to "the Bridge searches". Measured on 2026-11-02 and
17
+ * read in the source of gluon, its IMAP library: SUBJECT, FROM and TO compare
18
+ * against the header as it is stored, and a header with one letter outside
19
+ * ASCII is stored encoded, "Mit Anhang" as "Mit_Anhang", or in base64, where no
20
+ * word is legible at all. Up to 0.4.11 a subject search for "Mit Anhang" found
21
+ * nothing, and the answer read like a search without matches. So the Bridge
22
+ * now filters by everything but those three, and this server compares them
23
+ * against the envelope, which imapflow decodes. Full text stays with the
24
+ * Bridge, which compares the raw bytes; a word outside ASCII is also asked for
25
+ * in the form a quoted-printable part carries it, and a part sent as base64
26
+ * stays out of reach, which every answer says.
15
27
  */
16
28
  import type { SearchObject } from 'imapflow';
17
29
  import type { Connection } from '../bridge/connection.js';
18
- import { type MessageHeader, type OrderingCost } from './messages.js';
30
+ import { type EnvelopeEntry, type MessageHeader, type OrderingCost } from './messages.js';
31
+ /**
32
+ * How many envelopes a search compares itself, at most.
33
+ *
34
+ * Without other criteria a subject search reads the envelope of every message
35
+ * in the mailbox. Reading one per hit is what the ordering has always cost, and
36
+ * a common word matched 6803 messages in a mailbox of 26816, but there has to
37
+ * be an end. Past it, the most recently added messages are compared and the
38
+ * answer says so; there is no silent cut.
39
+ */
40
+ export declare const MAX_COMPARED = 20000;
19
41
  export interface SearchCriteria {
20
42
  /** Free text, searched in the body and in headers. The expensive part. */
21
43
  text?: string;
@@ -58,9 +80,51 @@ export interface SearchResult {
58
80
  ordering: OrderingCost;
59
81
  /** True when the search included a full-text term, which is the slow case. */
60
82
  fullText: boolean;
83
+ /** True when a full-text word was also asked for in its quoted-printable form. */
84
+ encodedForms: boolean;
85
+ /** Set when not every candidate was compared, see MAX_COMPARED. */
86
+ compared?: {
87
+ compared: number;
88
+ of: number;
89
+ };
90
+ }
91
+ /** What this server compares itself, against the decoded envelope. */
92
+ export interface LocalMatch {
93
+ subject?: string;
94
+ from?: string;
95
+ to?: string;
61
96
  }
62
- /** Translates the criteria into what imapflow expects. */
63
- export declare function buildQuery(criteria: SearchCriteria): SearchObject;
97
+ /** What the Bridge is asked, and what is left to compare here. */
98
+ export interface PlannedSearch {
99
+ query: SearchObject;
100
+ local: LocalMatch;
101
+ }
102
+ /**
103
+ * A word as a quoted-printable part carries it: every byte of its UTF-8 form
104
+ * outside printable ASCII, and the equals sign, as =XX.
105
+ */
106
+ export declare function quotedPrintable(text: string): string;
107
+ /**
108
+ * The forms a full-text word is asked for in.
109
+ *
110
+ * The Bridge compares the raw bytes of a message, lower cased, so a word with a
111
+ * letter outside ASCII is found only where a part carries it unencoded. Most
112
+ * text with umlauts travels as quoted-printable, where a u with umlaut is the
113
+ * six characters "=C3=BC" and an a with umlaut "=C3=A4". Lower casing does not reach into
114
+ * that encoding, so the usual spellings of a word are each asked for: as given,
115
+ * in lower case, and capitalised. Forms that differ only in the case of ASCII
116
+ * letters are asked once, since the Bridge lower cases both sides. A word
117
+ * broken across a line by the encoding is still missed, and so is anything in
118
+ * base64.
119
+ */
120
+ export declare function textForms(term: string): string[];
121
+ /** Whether a message passes what this server compares itself. */
122
+ export declare function matchesLocally(entry: EnvelopeEntry, local: LocalMatch): boolean;
123
+ /**
124
+ * Splits the criteria into what the Bridge is asked and what this server
125
+ * compares itself.
126
+ */
127
+ export declare function planSearch(criteria: SearchCriteria): PlannedSearch;
64
128
  /** Whether a query contains the expensive full-text part. */
65
129
  export declare function isFullText(criteria: SearchCriteria): boolean;
66
130
  export interface SearchOptions extends SearchCriteria {