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.
- package/CHANGELOG.md +17 -0
- package/data/google-crawlers.json +1 -1
- package/lib/account/account-state.js +248 -0
- package/lib/account.js +45 -193
- package/lib/api-routes/account-routes.js +1023 -0
- package/lib/api-routes/message-routes.js +1377 -0
- package/lib/consts.js +12 -2
- package/lib/email-client/base-client.js +282 -771
- package/lib/email-client/gmail/gmail-api.js +243 -0
- package/lib/email-client/gmail-client.js +145 -53
- package/lib/email-client/imap/mailbox.js +24 -698
- package/lib/email-client/imap/sync-operations.js +812 -0
- package/lib/email-client/imap-client.js +1 -1
- package/lib/email-client/message-builder.js +566 -0
- package/lib/email-client/notification-handler.js +314 -0
- package/lib/email-client/outlook/graph-api.js +326 -0
- package/lib/email-client/outlook-client.js +159 -113
- package/lib/email-client/smtp-pool-manager.js +196 -0
- package/lib/imapproxy/imap-server.js +3 -12
- package/lib/oauth/gmail.js +4 -4
- package/lib/oauth/mail-ru.js +30 -5
- package/lib/oauth/outlook.js +57 -3
- package/lib/oauth/pubsub/google.js +30 -11
- package/lib/oauth/scope-checker.js +202 -0
- package/lib/oauth2-apps.js +8 -4
- package/lib/redis-operations.js +484 -0
- package/lib/routes-ui.js +283 -2582
- package/lib/tools.js +4 -196
- package/lib/ui-routes/account-routes.js +1931 -0
- package/lib/ui-routes/admin-config-routes.js +1233 -0
- package/lib/ui-routes/admin-entities-routes.js +2367 -0
- package/lib/ui-routes/oauth-routes.js +992 -0
- package/lib/utils/network.js +237 -0
- package/package.json +10 -10
- package/sbom.json +1 -1
- package/static/js/app.js +5 -5
- package/static/licenses.html +79 -19
- package/translations/de.mo +0 -0
- package/translations/de.po +97 -86
- package/translations/en.mo +0 -0
- package/translations/en.po +80 -75
- package/translations/et.mo +0 -0
- package/translations/et.po +96 -86
- package/translations/fr.mo +0 -0
- package/translations/fr.po +97 -86
- package/translations/ja.mo +0 -0
- package/translations/ja.po +96 -86
- package/translations/messages.pot +105 -91
- package/translations/nl.mo +0 -0
- package/translations/nl.po +98 -86
- package/translations/pl.mo +0 -0
- package/translations/pl.po +96 -86
- package/views/account/security.hbs +4 -4
- package/views/accounts/account.hbs +13 -13
- package/views/accounts/register/imap-server.hbs +12 -12
- package/views/config/document-store/pre-processing/index.hbs +4 -2
- package/views/config/oauth/app.hbs +6 -7
- package/views/config/oauth/index.hbs +2 -2
- package/views/config/service.hbs +3 -4
- package/views/dashboard.hbs +5 -7
- package/views/error.hbs +22 -7
- package/views/gateways/gateway.hbs +2 -2
- package/views/partials/add_account_modal.hbs +7 -10
- package/views/partials/document_store_header.hbs +1 -1
- package/views/partials/editor_scope_info.hbs +0 -1
- package/views/partials/oauth_config_header.hbs +1 -1
- package/views/partials/side_menu.hbs +3 -3
- package/views/partials/webhook_form.hbs +2 -2
- package/views/templates/index.hbs +1 -1
- package/views/templates/template.hbs +8 -8
- package/views/tokens/index.hbs +6 -6
- package/views/tokens/new.hbs +1 -1
- package/views/webhooks/index.hbs +4 -4
- package/views/webhooks/webhook.hbs +7 -7
- package/workers/api.js +148 -2436
- package/workers/smtp.js +2 -1
- package/lib/imapproxy/imap-core/test/client.js +0 -46
- package/lib/imapproxy/imap-core/test/fixtures/append.eml +0 -1196
- package/lib/imapproxy/imap-core/test/fixtures/chunks.js +0 -44
- package/lib/imapproxy/imap-core/test/fixtures/fix1.eml +0 -6
- package/lib/imapproxy/imap-core/test/fixtures/fix2.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/fix3.eml +0 -32
- package/lib/imapproxy/imap-core/test/fixtures/fix4.eml +0 -6
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.js +0 -2740
- package/lib/imapproxy/imap-core/test/fixtures/mimetorture.json +0 -1411
- package/lib/imapproxy/imap-core/test/fixtures/mimetree.js +0 -85
- package/lib/imapproxy/imap-core/test/fixtures/nodemailer.eml +0 -582
- package/lib/imapproxy/imap-core/test/fixtures/ryan_finnie_mime_torture.eml +0 -599
- package/lib/imapproxy/imap-core/test/fixtures/simple.eml +0 -42
- package/lib/imapproxy/imap-core/test/fixtures/simple.json +0 -164
- package/lib/imapproxy/imap-core/test/imap-compile-stream-test.js +0 -671
- package/lib/imapproxy/imap-core/test/imap-compiler-test.js +0 -272
- package/lib/imapproxy/imap-core/test/imap-indexer-test.js +0 -236
- package/lib/imapproxy/imap-core/test/imap-parser-test.js +0 -922
- package/lib/imapproxy/imap-core/test/memory-notifier.js +0 -129
- package/lib/imapproxy/imap-core/test/prepare.sh +0 -74
- package/lib/imapproxy/imap-core/test/protocol-test.js +0 -1756
- package/lib/imapproxy/imap-core/test/search-test.js +0 -1356
- package/lib/imapproxy/imap-core/test/test-client.js +0 -152
- package/lib/imapproxy/imap-core/test/test-server.js +0 -623
- package/lib/imapproxy/imap-core/test/tools-test.js +0 -22
- package/test/api-test.js +0 -899
- package/test/autoreply-test.js +0 -327
- package/test/bounce-test.js +0 -151
- package/test/complaint-test.js +0 -256
- package/test/fixtures/autoreply/LICENSE +0 -27
- package/test/fixtures/autoreply/rfc3834-01.eml +0 -23
- package/test/fixtures/autoreply/rfc3834-02.eml +0 -24
- package/test/fixtures/autoreply/rfc3834-03.eml +0 -26
- package/test/fixtures/autoreply/rfc3834-04.eml +0 -48
- package/test/fixtures/autoreply/rfc3834-05.eml +0 -19
- package/test/fixtures/autoreply/rfc3834-06.eml +0 -59
- package/test/fixtures/bounces/163.eml +0 -2521
- package/test/fixtures/bounces/fastmail.eml +0 -242
- package/test/fixtures/bounces/gmail.eml +0 -252
- package/test/fixtures/bounces/hotmail.eml +0 -655
- package/test/fixtures/bounces/mailru.eml +0 -121
- package/test/fixtures/bounces/outlook.eml +0 -1107
- package/test/fixtures/bounces/postfix.eml +0 -101
- package/test/fixtures/bounces/rambler.eml +0 -116
- package/test/fixtures/bounces/workmail.eml +0 -142
- package/test/fixtures/bounces/yahoo.eml +0 -139
- package/test/fixtures/bounces/zoho.eml +0 -83
- package/test/fixtures/bounces/zonemta.eml +0 -100
- package/test/fixtures/complaints/LICENSE +0 -27
- package/test/fixtures/complaints/amazonses.eml +0 -72
- package/test/fixtures/complaints/dmarc.eml +0 -59
- package/test/fixtures/complaints/hotmail.eml +0 -49
- package/test/fixtures/complaints/optout.eml +0 -40
- package/test/fixtures/complaints/standard-arf.eml +0 -68
- package/test/fixtures/complaints/yahoo.eml +0 -68
- package/test/oauth2-apps-test.js +0 -301
- package/test/sendonly-test.js +0 -160
- package/test/test-config.js +0 -34
- 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
|
-
});
|