emailengine-app 2.61.1 → 2.61.3

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 (136) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/data/google-crawlers.json +1 -1
  3. package/lib/account/account-state.js +248 -0
  4. package/lib/account.js +45 -193
  5. package/lib/api-routes/account-routes.js +1023 -0
  6. package/lib/api-routes/message-routes.js +1377 -0
  7. package/lib/consts.js +12 -2
  8. package/lib/email-client/base-client.js +282 -771
  9. package/lib/email-client/gmail/gmail-api.js +243 -0
  10. package/lib/email-client/gmail-client.js +145 -53
  11. package/lib/email-client/imap/mailbox.js +24 -698
  12. package/lib/email-client/imap/sync-operations.js +812 -0
  13. package/lib/email-client/imap-client.js +1 -1
  14. package/lib/email-client/message-builder.js +566 -0
  15. package/lib/email-client/notification-handler.js +314 -0
  16. package/lib/email-client/outlook/graph-api.js +326 -0
  17. package/lib/email-client/outlook-client.js +159 -113
  18. package/lib/email-client/smtp-pool-manager.js +196 -0
  19. package/lib/imapproxy/imap-server.js +3 -12
  20. package/lib/oauth/gmail.js +4 -4
  21. package/lib/oauth/mail-ru.js +30 -5
  22. package/lib/oauth/outlook.js +57 -3
  23. package/lib/oauth/pubsub/google.js +30 -11
  24. package/lib/oauth/scope-checker.js +202 -0
  25. package/lib/oauth2-apps.js +8 -4
  26. package/lib/redis-operations.js +484 -0
  27. package/lib/routes-ui.js +283 -2582
  28. package/lib/tools.js +4 -196
  29. package/lib/ui-routes/account-routes.js +1931 -0
  30. package/lib/ui-routes/admin-config-routes.js +1233 -0
  31. package/lib/ui-routes/admin-entities-routes.js +2367 -0
  32. package/lib/ui-routes/oauth-routes.js +992 -0
  33. package/lib/utils/network.js +237 -0
  34. package/package.json +10 -10
  35. package/sbom.json +1 -1
  36. package/static/js/app.js +5 -5
  37. package/static/licenses.html +79 -19
  38. package/translations/de.mo +0 -0
  39. package/translations/de.po +97 -86
  40. package/translations/en.mo +0 -0
  41. package/translations/en.po +80 -75
  42. package/translations/et.mo +0 -0
  43. package/translations/et.po +96 -86
  44. package/translations/fr.mo +0 -0
  45. package/translations/fr.po +97 -86
  46. package/translations/ja.mo +0 -0
  47. package/translations/ja.po +96 -86
  48. package/translations/messages.pot +105 -91
  49. package/translations/nl.mo +0 -0
  50. package/translations/nl.po +98 -86
  51. package/translations/pl.mo +0 -0
  52. package/translations/pl.po +96 -86
  53. package/views/account/security.hbs +4 -4
  54. package/views/accounts/account.hbs +13 -13
  55. package/views/accounts/register/imap-server.hbs +12 -12
  56. package/views/config/document-store/pre-processing/index.hbs +4 -2
  57. package/views/config/oauth/app.hbs +6 -7
  58. package/views/config/oauth/index.hbs +2 -2
  59. package/views/config/service.hbs +3 -4
  60. package/views/dashboard.hbs +5 -7
  61. package/views/error.hbs +22 -7
  62. package/views/gateways/gateway.hbs +2 -2
  63. package/views/partials/add_account_modal.hbs +7 -10
  64. package/views/partials/document_store_header.hbs +1 -1
  65. package/views/partials/editor_scope_info.hbs +0 -1
  66. package/views/partials/oauth_config_header.hbs +1 -1
  67. package/views/partials/side_menu.hbs +3 -3
  68. package/views/partials/webhook_form.hbs +2 -2
  69. package/views/templates/index.hbs +1 -1
  70. package/views/templates/template.hbs +8 -8
  71. package/views/tokens/index.hbs +6 -6
  72. package/views/tokens/new.hbs +1 -1
  73. package/views/webhooks/index.hbs +4 -4
  74. package/views/webhooks/webhook.hbs +7 -7
  75. package/workers/api.js +148 -2436
  76. package/workers/smtp.js +2 -1
  77. package/lib/imapproxy/imap-core/test/client.js +0 -46
  78. package/lib/imapproxy/imap-core/test/fixtures/append.eml +0 -1196
  79. package/lib/imapproxy/imap-core/test/fixtures/chunks.js +0 -44
  80. package/lib/imapproxy/imap-core/test/fixtures/fix1.eml +0 -6
  81. package/lib/imapproxy/imap-core/test/fixtures/fix2.eml +0 -599
  82. package/lib/imapproxy/imap-core/test/fixtures/fix3.eml +0 -32
  83. package/lib/imapproxy/imap-core/test/fixtures/fix4.eml +0 -6
  84. package/lib/imapproxy/imap-core/test/fixtures/mimetorture.eml +0 -599
  85. package/lib/imapproxy/imap-core/test/fixtures/mimetorture.js +0 -2740
  86. package/lib/imapproxy/imap-core/test/fixtures/mimetorture.json +0 -1411
  87. package/lib/imapproxy/imap-core/test/fixtures/mimetree.js +0 -85
  88. package/lib/imapproxy/imap-core/test/fixtures/nodemailer.eml +0 -582
  89. package/lib/imapproxy/imap-core/test/fixtures/ryan_finnie_mime_torture.eml +0 -599
  90. package/lib/imapproxy/imap-core/test/fixtures/simple.eml +0 -42
  91. package/lib/imapproxy/imap-core/test/fixtures/simple.json +0 -164
  92. package/lib/imapproxy/imap-core/test/imap-compile-stream-test.js +0 -671
  93. package/lib/imapproxy/imap-core/test/imap-compiler-test.js +0 -272
  94. package/lib/imapproxy/imap-core/test/imap-indexer-test.js +0 -236
  95. package/lib/imapproxy/imap-core/test/imap-parser-test.js +0 -922
  96. package/lib/imapproxy/imap-core/test/memory-notifier.js +0 -129
  97. package/lib/imapproxy/imap-core/test/prepare.sh +0 -74
  98. package/lib/imapproxy/imap-core/test/protocol-test.js +0 -1756
  99. package/lib/imapproxy/imap-core/test/search-test.js +0 -1356
  100. package/lib/imapproxy/imap-core/test/test-client.js +0 -152
  101. package/lib/imapproxy/imap-core/test/test-server.js +0 -623
  102. package/lib/imapproxy/imap-core/test/tools-test.js +0 -22
  103. package/test/api-test.js +0 -899
  104. package/test/autoreply-test.js +0 -327
  105. package/test/bounce-test.js +0 -151
  106. package/test/complaint-test.js +0 -256
  107. package/test/fixtures/autoreply/LICENSE +0 -27
  108. package/test/fixtures/autoreply/rfc3834-01.eml +0 -23
  109. package/test/fixtures/autoreply/rfc3834-02.eml +0 -24
  110. package/test/fixtures/autoreply/rfc3834-03.eml +0 -26
  111. package/test/fixtures/autoreply/rfc3834-04.eml +0 -48
  112. package/test/fixtures/autoreply/rfc3834-05.eml +0 -19
  113. package/test/fixtures/autoreply/rfc3834-06.eml +0 -59
  114. package/test/fixtures/bounces/163.eml +0 -2521
  115. package/test/fixtures/bounces/fastmail.eml +0 -242
  116. package/test/fixtures/bounces/gmail.eml +0 -252
  117. package/test/fixtures/bounces/hotmail.eml +0 -655
  118. package/test/fixtures/bounces/mailru.eml +0 -121
  119. package/test/fixtures/bounces/outlook.eml +0 -1107
  120. package/test/fixtures/bounces/postfix.eml +0 -101
  121. package/test/fixtures/bounces/rambler.eml +0 -116
  122. package/test/fixtures/bounces/workmail.eml +0 -142
  123. package/test/fixtures/bounces/yahoo.eml +0 -139
  124. package/test/fixtures/bounces/zoho.eml +0 -83
  125. package/test/fixtures/bounces/zonemta.eml +0 -100
  126. package/test/fixtures/complaints/LICENSE +0 -27
  127. package/test/fixtures/complaints/amazonses.eml +0 -72
  128. package/test/fixtures/complaints/dmarc.eml +0 -59
  129. package/test/fixtures/complaints/hotmail.eml +0 -49
  130. package/test/fixtures/complaints/optout.eml +0 -40
  131. package/test/fixtures/complaints/standard-arf.eml +0 -68
  132. package/test/fixtures/complaints/yahoo.eml +0 -68
  133. package/test/oauth2-apps-test.js +0 -301
  134. package/test/sendonly-test.js +0 -160
  135. package/test/test-config.js +0 -34
  136. package/test/webhooks-server.js +0 -39
@@ -1,623 +0,0 @@
1
- 'use strict';
2
-
3
- const IMAPServerModule = require('../index.js');
4
- const IMAPServer = IMAPServerModule.IMAPServer;
5
- const MemoryNotifier = require('./memory-notifier.js');
6
- const fs = require('fs');
7
- const parseMimeTree = require('../lib/indexer/parse-mime-tree');
8
- const imapHandler = require('../lib/handler/imap-handler');
9
-
10
- module.exports = function (options) {
11
- // This example uses global folders and subscriptions
12
- let folders = new Map();
13
- let subscriptions = new WeakSet();
14
-
15
- [
16
- {
17
- mailbox: Symbol('INBOX'),
18
- path: 'INBOX',
19
- uidValidity: 123,
20
- uidNext: 70,
21
- modifyIndex: 5000,
22
- messages: [
23
- {
24
- uid: 45,
25
- flags: [],
26
- modseq: 100,
27
- idate: new Date('14-Sep-2013 21:22:28 -0300'),
28
- mimeTree: parseMimeTree(
29
- Buffer.from('from: sender@example.com\r\nto: to@example.com\r\ncc: cc@example.com\r\nsubject: test\r\n\r\nzzzz\r\n')
30
- )
31
- },
32
- {
33
- uid: 49,
34
- flags: ['\\Seen'],
35
- idate: new Date(),
36
- modseq: 5000,
37
- mimeTree: parseMimeTree(fs.readFileSync(__dirname + '/fixtures/ryan_finnie_mime_torture.eml'))
38
- },
39
- {
40
- uid: 50,
41
- flags: ['\\Seen'],
42
- modseq: 45,
43
- idate: new Date(),
44
- mimeTree: parseMimeTree(
45
- 'MIME-Version: 1.0\r\n' +
46
- 'From: andris@kreata.ee\r\n' +
47
- 'To: andris@tr.ee\r\n' +
48
- 'Content-Type: multipart/mixed;\r\n' +
49
- " boundary='----mailcomposer-?=_1-1328088797399'\r\n" +
50
- 'Message-Id: <testmessage-for-bug>;\r\n' +
51
- '\r\n' +
52
- '------mailcomposer-?=_1-1328088797399\r\n' +
53
- 'Content-Type: message/rfc822\r\n' +
54
- 'Content-Transfer-Encoding: 7bit\r\n' +
55
- '\r\n' +
56
- 'MIME-Version: 1.0\r\n' +
57
- 'From: andris@kreata.ee\r\n' +
58
- 'To: andris@pangalink.net\r\n' +
59
- 'In-Reply-To: <test1>\r\n' +
60
- '\r\n' +
61
- 'Hello world 1!\r\n' +
62
- '------mailcomposer-?=_1-1328088797399\r\n' +
63
- 'Content-Type: message/rfc822\r\n' +
64
- 'Content-Transfer-Encoding: 7bit\r\n' +
65
- '\r\n' +
66
- 'MIME-Version: 1.0\r\n' +
67
- 'From: andris@kreata.ee\r\n' +
68
- 'To: andris@pangalink.net\r\n' +
69
- '\r\n' +
70
- 'Hello world 2!\r\n' +
71
- '------mailcomposer-?=_1-1328088797399\r\n' +
72
- 'Content-Type: text/html; charset=utf-8\r\n' +
73
- 'Content-Transfer-Encoding: quoted-printable\r\n' +
74
- '\r\n' +
75
- '<b>Hello world 3!</b>\r\n' +
76
- '------mailcomposer-?=_1-1328088797399--\r\n'
77
- )
78
- },
79
- {
80
- uid: 52,
81
- flags: [],
82
- modseq: 4,
83
- idate: new Date(),
84
- mimeTree: parseMimeTree('from: sender@example.com\r\nto: to@example.com\r\ncc: cc@example.com\r\nsubject: test\r\n\r\nHello World!\r\n')
85
- },
86
- {
87
- uid: 53,
88
- flags: [],
89
- modseq: 5,
90
- idate: new Date()
91
- },
92
- {
93
- uid: 60,
94
- flags: [],
95
- modseq: 6,
96
- idate: new Date()
97
- }
98
- ],
99
- journal: []
100
- },
101
- {
102
- mailbox: Symbol('[Gmail]/Sent Mail'),
103
- path: '[Gmail]/Sent Mail',
104
- specialUse: '\\Sent',
105
- uidValidity: 123,
106
- uidNext: 90,
107
- modifyIndex: 1,
108
- messages: [],
109
- journal: []
110
- }
111
- ].forEach(folder => {
112
- folders.set(folder.path, folder);
113
- subscriptions.add(folder);
114
- });
115
-
116
- // Setup server
117
- let server = new IMAPServer(options);
118
- server.notifier = new MemoryNotifier({
119
- logger: {
120
- info: () => false,
121
- debug: () => false,
122
- error: () => false
123
- },
124
- folders
125
- });
126
-
127
- server.on('error', err => {
128
- console.log('SERVER ERR\n%s', err.stack);
129
- });
130
-
131
- server.onAuth = function (login, session, callback) {
132
- if (login.username !== 'testuser' || login.password !== 'pass') {
133
- return callback();
134
- }
135
-
136
- callback(null, {
137
- user: {
138
- id: 'id.' + login.username,
139
- username: login.username
140
- }
141
- });
142
- };
143
-
144
- // LIST "" "*"
145
- // Returns all folders, query is informational
146
- // folders is either an Array or a Map
147
- server.onList = function (query, session, callback) {
148
- this.logger.debug('[%s] LIST for "%s"', session.id, query);
149
-
150
- callback(null, folders);
151
- };
152
-
153
- // LSUB "" "*"
154
- // Returns all subscribed folders, query is informational
155
- // folders is either an Array or a Map
156
- server.onLsub = function (query, session, callback) {
157
- this.logger.debug('[%s] LSUB for "%s"', session.id, query);
158
-
159
- let subscribed = [];
160
- folders.forEach(folder => {
161
- if (subscriptions.has(folder)) {
162
- subscribed.push(folder);
163
- }
164
- });
165
-
166
- callback(null, subscribed);
167
- };
168
-
169
- // SUBSCRIBE "path/to/mailbox"
170
- server.onSubscribe = function (mailbox, session, callback) {
171
- this.logger.debug('[%s] SUBSCRIBE to "%s"', session.id, mailbox);
172
-
173
- if (!folders.has(mailbox)) {
174
- return callback(null, 'NONEXISTENT');
175
- }
176
-
177
- subscriptions.add(folders.get(mailbox));
178
- callback(null, true);
179
- };
180
-
181
- // UNSUBSCRIBE "path/to/mailbox"
182
- server.onUnsubscribe = function (mailbox, session, callback) {
183
- this.logger.debug('[%s] UNSUBSCRIBE from "%s"', session.id, mailbox);
184
-
185
- if (!folders.has(mailbox)) {
186
- return callback(null, 'NONEXISTENT');
187
- }
188
-
189
- subscriptions.delete(folders.get(mailbox));
190
- callback(null, true);
191
- };
192
-
193
- // CREATE "path/to/mailbox"
194
- server.onCreate = function (mailbox, session, callback) {
195
- this.logger.debug('[%s] CREATE "%s"', session.id, mailbox);
196
-
197
- if (folders.has(mailbox)) {
198
- return callback(null, 'ALREADYEXISTS');
199
- }
200
-
201
- folders.set(mailbox, {
202
- path: mailbox,
203
- uidValidity: Date.now(),
204
- uidNext: 1,
205
- modifyIndex: 0,
206
- messages: [],
207
- journal: []
208
- });
209
-
210
- subscriptions.add(folders.get(mailbox));
211
- callback(null, true);
212
- };
213
-
214
- // RENAME "path/to/mailbox" "new/path"
215
- // NB! RENAME affects child and hierarchy mailboxes as well, this example does not do this
216
- server.onRename = function (mailbox, newname, session, callback) {
217
- this.logger.debug('[%s] RENAME "%s" to "%s"', session.id, mailbox, newname);
218
-
219
- if (!folders.has(mailbox)) {
220
- return callback(null, 'NONEXISTENT');
221
- }
222
-
223
- if (folders.has(newname)) {
224
- return callback(null, 'ALREADYEXISTS');
225
- }
226
-
227
- let oldMailbox = folders.get(mailbox);
228
- folders.delete(mailbox);
229
-
230
- oldMailbox.path = newname;
231
- folders.set(newname, oldMailbox);
232
-
233
- callback(null, true);
234
- };
235
-
236
- // DELETE "path/to/mailbox"
237
- server.onDelete = function (mailbox, session, callback) {
238
- this.logger.debug('[%s] DELETE "%s"', session.id, mailbox);
239
-
240
- if (!folders.has(mailbox)) {
241
- return callback(null, 'NONEXISTENT');
242
- }
243
-
244
- // keep SPECIAL-USE folders
245
- if (folders.get(mailbox).specialUse) {
246
- return callback(null, 'CANNOT');
247
- }
248
-
249
- folders.delete(mailbox);
250
- callback(null, true);
251
- };
252
-
253
- // SELECT/EXAMINE
254
- server.onOpen = function (mailbox, session, callback) {
255
- this.logger.debug('[%s] Opening "%s"', session.id, mailbox);
256
-
257
- if (!folders.has(mailbox)) {
258
- return callback(null, 'NONEXISTENT');
259
- }
260
-
261
- let folder = folders.get(mailbox);
262
-
263
- return callback(null, {
264
- specialUse: folder.specialUse,
265
- uidValidity: folder.uidValidity,
266
- uidNext: folder.uidNext,
267
- modifyIndex: folder.modifyIndex,
268
- uidList: folder.messages.map(message => message.uid)
269
- });
270
- };
271
-
272
- // STATUS (X Y X)
273
- server.onStatus = function (mailbox, session, callback) {
274
- this.logger.debug('[%s] Requested status for "%s"', session.id, mailbox);
275
-
276
- if (!folders.has(mailbox)) {
277
- return callback(null, 'NONEXISTENT');
278
- }
279
-
280
- let folder = folders.get(mailbox);
281
-
282
- return callback(null, {
283
- messages: folder.messages.length,
284
- uidNext: folder.uidNext,
285
- uidValidity: folder.uidValidity,
286
- highestModseq: folder.modifyIndex,
287
- unseen: folder.messages.filter(message => !message.flags.includes('\\Seen')).length
288
- });
289
- };
290
-
291
- // APPEND mailbox (flags) date message
292
- server.onAppend = function (mailbox, flags, date, raw, session, callback) {
293
- this.logger.debug('[%s] Appending message to "%s"', session.id, mailbox);
294
-
295
- if (!folders.has(mailbox)) {
296
- return callback(null, 'TRYCREATE');
297
- }
298
-
299
- date = (date && new Date(date)) || new Date();
300
-
301
- let folder = folders.get(mailbox);
302
- let message = {
303
- uid: folder.uidNext++,
304
- modseq: ++folder.modifyIndex,
305
- date: (date && new Date(date)) || new Date(),
306
- mimeTree: parseMimeTree(raw),
307
- flags
308
- };
309
-
310
- folder.messages.push(message);
311
-
312
- // do not write directly to stream, use notifications as the currently selected mailbox might not be the one that receives the message
313
- this.notifier.addEntries(
314
- session.user.id,
315
- mailbox,
316
- {
317
- command: 'EXISTS',
318
- uid: message.uid
319
- },
320
- () => {
321
- this.notifier.fire(session.user.id, mailbox);
322
-
323
- return callback(null, true, {
324
- uidValidity: folder.uidValidity,
325
- uid: message.uid
326
- });
327
- }
328
- );
329
- };
330
-
331
- // STORE / UID STORE, updates flags for selected UIDs
332
- server.onStore = function (mailbox, update, session, callback) {
333
- this.logger.debug('[%s] Updating messages in "%s"', session.id, mailbox);
334
-
335
- if (!folders.has(mailbox)) {
336
- return callback(null, 'NONEXISTENT');
337
- }
338
-
339
- let condstoreEnabled = !!session.selected.condstoreEnabled;
340
-
341
- let modified = [];
342
- let folder = folders.get(mailbox);
343
- let i = 0;
344
-
345
- let processMessages = () => {
346
- if (i >= folder.messages.length) {
347
- this.notifier.fire(session.user.id, mailbox);
348
- return callback(null, true, modified);
349
- }
350
-
351
- let message = folder.messages[i++];
352
- let updated = false;
353
-
354
- if (update.messages.indexOf(message.uid) < 0) {
355
- return processMessages();
356
- }
357
-
358
- if (update.unchangedSince && message.modseq > update.unchangedSince) {
359
- modified.push(message.uid);
360
- return processMessages();
361
- }
362
-
363
- switch (update.action) {
364
- case 'set':
365
- // check if update set matches current or is different
366
- if (message.flags.length !== update.value.length || update.value.filter(flag => message.flags.indexOf(flag) < 0).length) {
367
- updated = true;
368
- }
369
- // set flags
370
- message.flags = [].concat(update.value);
371
- break;
372
-
373
- case 'add':
374
- message.flags = message.flags.concat(
375
- update.value.filter(flag => {
376
- if (message.flags.indexOf(flag) < 0) {
377
- updated = true;
378
- return true;
379
- }
380
- return false;
381
- })
382
- );
383
- break;
384
-
385
- case 'remove':
386
- message.flags = message.flags.filter(flag => {
387
- if (update.value.indexOf(flag) < 0) {
388
- return true;
389
- }
390
- updated = true;
391
- return false;
392
- });
393
- break;
394
- }
395
-
396
- // notify only if something changed
397
- if (updated) {
398
- message.modseq = ++folder.modifyIndex;
399
-
400
- // Only show response if not silent or modseq is required
401
- if (!update.silent || condstoreEnabled) {
402
- session.writeStream.write(
403
- session.formatResponse('FETCH', message.uid, {
404
- uid: update.isUid ? message.uid : false,
405
- flags: update.silent ? false : message.flags,
406
- modseq: condstoreEnabled ? message.modseq : false
407
- })
408
- );
409
- }
410
-
411
- this.notifier.addEntries(
412
- session.user.id,
413
- mailbox,
414
- {
415
- command: 'FETCH',
416
- ignore: session.id,
417
- uid: message.uid,
418
- flags: message.flags
419
- },
420
- processMessages
421
- );
422
- } else {
423
- processMessages();
424
- }
425
- };
426
-
427
- processMessages();
428
- };
429
-
430
- // EXPUNGE deletes all messages in selected mailbox marked with \Delete
431
- server.onExpunge = function (mailbox, update, session, callback) {
432
- this.logger.debug('[%s] Deleting messages from "%s"', session.id, mailbox);
433
-
434
- if (!folders.has(mailbox)) {
435
- return callback(null, 'NONEXISTENT');
436
- }
437
-
438
- let folder = folders.get(mailbox);
439
- let deleted = [];
440
- let i, len;
441
-
442
- for (i = folder.messages.length - 1; i >= 0; i--) {
443
- if (
444
- ((update.isUid && update.messages.indexOf(folder.messages[i].uid) >= 0) || !update.isUid) &&
445
- folder.messages[i].flags.indexOf('\\Deleted') >= 0
446
- ) {
447
- deleted.unshift(folder.messages[i].uid);
448
- folder.messages.splice(i, 1);
449
- }
450
- }
451
-
452
- let entries = [];
453
- for (i = 0, len = deleted.length; i < len; i++) {
454
- entries.push({
455
- command: 'EXPUNGE',
456
- ignore: session.id,
457
- uid: deleted[i]
458
- });
459
- if (!update.silent) {
460
- session.writeStream.write(session.formatResponse('EXPUNGE', deleted[i]));
461
- }
462
- }
463
-
464
- this.notifier.addEntries(session.user.id, mailbox, entries, () => {
465
- this.notifier.fire(session.user.id, mailbox);
466
- return callback(null, true);
467
- });
468
- };
469
-
470
- // COPY / UID COPY sequence mailbox
471
- server.onCopy = function (connection, mailbox, update, session, callback) {
472
- this.logger.debug('[%s] Copying messages from "%s" to "%s"', session.id, mailbox, update.destination);
473
-
474
- if (!folders.has(mailbox)) {
475
- return callback(null, 'NONEXISTENT');
476
- }
477
-
478
- if (!folders.has(update.destination)) {
479
- return callback(null, 'TRYCREATE');
480
- }
481
-
482
- let sourceFolder = folders.get(mailbox);
483
- let destinationFolder = folders.get(update.destination);
484
-
485
- let messages = [];
486
- let sourceUid = [];
487
- let destinationUid = [];
488
- let i, len;
489
- let entries = [];
490
-
491
- for (i = sourceFolder.messages.length - 1; i >= 0; i--) {
492
- if (update.messages.indexOf(sourceFolder.messages[i].uid) >= 0) {
493
- messages.unshift(JSON.parse(JSON.stringify(sourceFolder.messages[i])));
494
- sourceUid.unshift(sourceFolder.messages[i].uid);
495
- }
496
- }
497
-
498
- for (i = 0, len = messages.length; i < len; i++) {
499
- messages[i].uid = destinationFolder.uidNext++;
500
- destinationUid.push(messages[i].uid);
501
- destinationFolder.messages.push(messages[i]);
502
-
503
- // do not write directly to stream, use notifications as the currently selected mailbox might not be the one that receives the message
504
- entries.push({
505
- command: 'EXISTS',
506
- uid: messages[i].uid
507
- });
508
- }
509
-
510
- this.notifier.addEntries(update.destination, session.user.id, entries, () => {
511
- this.notifier.fire(session.user.id, update.destination);
512
-
513
- return callback(null, true, {
514
- uidValidity: destinationFolder.uidValidity,
515
- sourceUid,
516
- destinationUid
517
- });
518
- });
519
- };
520
-
521
- // sends results to socket
522
- server.onFetch = function (mailbox, options, session, callback) {
523
- this.logger.debug('[%s] Requested FETCH for "%s"', session.id, mailbox);
524
- this.logger.debug('[%s] FETCH: %s', session.id, JSON.stringify(options.query));
525
- if (!folders.has(mailbox)) {
526
- return callback(null, 'NONEXISTENT');
527
- }
528
-
529
- let folder = folders.get(mailbox);
530
- let entries = [];
531
-
532
- if (options.markAsSeen) {
533
- // mark all matching messages as seen
534
- folder.messages.forEach(message => {
535
- if (options.messages.indexOf(message.uid) < 0) {
536
- return;
537
- }
538
-
539
- // if BODY[] is touched, then add \Seen flag and notify other clients
540
- if (!message.flags.includes('\\Seen')) {
541
- message.flags.unshift('\\Seen');
542
- entries.push({
543
- command: 'FETCH',
544
- ignore: session.id,
545
- uid: message.uid,
546
- flags: message.flags
547
- });
548
- }
549
- });
550
- }
551
-
552
- this.notifier.addEntries(session.user.id, mailbox, entries, () => {
553
- let pos = 0;
554
- let processMessage = () => {
555
- if (pos >= folder.messages.length) {
556
- // once messages are processed show relevant updates
557
- this.notifier.fire(session.user.id, mailbox);
558
- return callback(null, true);
559
- }
560
- let message = folder.messages[pos++];
561
-
562
- if (options.messages.indexOf(message.uid) < 0) {
563
- return setImmediate(processMessage);
564
- }
565
-
566
- if (options.changedSince && message.modseq <= options.changedSince) {
567
- return setImmediate(processMessage);
568
- }
569
-
570
- let stream = imapHandler.compileStream(
571
- session.formatResponse('FETCH', message.uid, {
572
- query: options.query,
573
- values: session.getQueryResponse(options.query, message)
574
- })
575
- );
576
-
577
- // send formatted response to socket
578
- session.writeStream.write(stream, () => {
579
- setImmediate(processMessage);
580
- });
581
- };
582
-
583
- setImmediate(processMessage);
584
- });
585
- };
586
-
587
- // returns an array of matching UID values and the highest modseq of matching messages
588
- server.onSearch = function (mailbox, options, session, callback) {
589
- if (!folders.has(mailbox)) {
590
- return callback(null, 'NONEXISTENT');
591
- }
592
-
593
- let folder = folders.get(mailbox);
594
- let highestModseq = 0;
595
-
596
- let uidList = [];
597
- let checked = 0;
598
- let checkNext = () => {
599
- if (checked >= folder.messages.length) {
600
- return callback(null, {
601
- uidList,
602
- highestModseq
603
- });
604
- }
605
- let message = folder.messages[checked++];
606
- session.matchSearchQuery(message, options.query, (err, match) => {
607
- if (err) {
608
- // ignore
609
- }
610
- if (match && highestModseq < message.modseq) {
611
- highestModseq = message.modseq;
612
- }
613
- if (match) {
614
- uidList.push(message.uid);
615
- }
616
- checkNext();
617
- });
618
- };
619
- checkNext();
620
- };
621
-
622
- return server;
623
- };
@@ -1,22 +0,0 @@
1
- /*eslint no-unused-expressions: 0, prefer-arrow-callback: 0 */
2
-
3
- 'use strict';
4
-
5
- const imapTools = require('../lib/imap-tools');
6
- const chai = require('chai');
7
- const expect = chai.expect;
8
- chai.config.includeStack = true;
9
-
10
- describe('#packMessageRange', function() {
11
- it('should return as is', function() {
12
- expect(imapTools.packMessageRange([1, 3, 5, 9])).to.equal('1,3,5,9');
13
- });
14
-
15
- it('should return a range', function() {
16
- expect(imapTools.packMessageRange([1, 2, 3, 4])).to.equal('1:4');
17
- });
18
-
19
- it('should return mixed ranges', function() {
20
- expect(imapTools.packMessageRange([1, 3, 4, 6, 8, 9, 10, 11, 13])).to.equal('1,3:4,6,8:11,13');
21
- });
22
- });