mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-15 01:24:56 +08:00
v1.0.55
This commit is contained in:
parent
bb105ef3d3
commit
13469f5b74
3 changed files with 293 additions and 71 deletions
232
api.js
232
api.js
|
@ -1547,7 +1547,9 @@ server.get({ name: 'messages', path: '/users/:user/mailboxes/:mailbox/messages'
|
||||||
ha: true,
|
ha: true,
|
||||||
intro: true,
|
intro: true,
|
||||||
unseen: true,
|
unseen: true,
|
||||||
|
undeleted: true,
|
||||||
flagged: true,
|
flagged: true,
|
||||||
|
draft: true,
|
||||||
thread: true
|
thread: true
|
||||||
},
|
},
|
||||||
paginatedField: 'uid',
|
paginatedField: 'uid',
|
||||||
|
@ -1616,8 +1618,10 @@ server.get({ name: 'messages', path: '/users/:user/mailboxes/:mailbox/messages'
|
||||||
date: messageData.hdate.toISOString(),
|
date: messageData.hdate.toISOString(),
|
||||||
intro: messageData.intro,
|
intro: messageData.intro,
|
||||||
attachments: !!messageData.ha,
|
attachments: !!messageData.ha,
|
||||||
unseen: messageData.unseen,
|
seen: !messageData.unseen,
|
||||||
flagged: messageData.flagged
|
deleted: !messageData.undeleted,
|
||||||
|
flagged: messageData.flagged,
|
||||||
|
draft: messageData.draft
|
||||||
};
|
};
|
||||||
return response;
|
return response;
|
||||||
})
|
})
|
||||||
|
@ -1715,7 +1719,9 @@ server.get({ name: 'search', path: '/users/:user/search' }, (req, res, next) =>
|
||||||
ha: true,
|
ha: true,
|
||||||
intro: true,
|
intro: true,
|
||||||
unseen: true,
|
unseen: true,
|
||||||
|
undeleted: true,
|
||||||
flagged: true,
|
flagged: true,
|
||||||
|
draft: true,
|
||||||
thread: true
|
thread: true
|
||||||
},
|
},
|
||||||
paginatedField: '_id',
|
paginatedField: '_id',
|
||||||
|
@ -1775,8 +1781,10 @@ server.get({ name: 'search', path: '/users/:user/search' }, (req, res, next) =>
|
||||||
date: messageData.hdate.toISOString(),
|
date: messageData.hdate.toISOString(),
|
||||||
intro: messageData.intro,
|
intro: messageData.intro,
|
||||||
attachments: !!messageData.ha,
|
attachments: !!messageData.ha,
|
||||||
unseen: messageData.unseen,
|
seen: !messageData.unseen,
|
||||||
flagged: messageData.flagged
|
deleted: !messageData.undeleted,
|
||||||
|
flagged: messageData.flagged,
|
||||||
|
draft: messageData.draft
|
||||||
};
|
};
|
||||||
return response;
|
return response;
|
||||||
})
|
})
|
||||||
|
@ -1831,9 +1839,13 @@ server.get('/users/:user/mailboxes/:mailbox/messages/:message', (req, res, next)
|
||||||
'mimeTree.parsedHeader': true,
|
'mimeTree.parsedHeader': true,
|
||||||
subject: true,
|
subject: true,
|
||||||
msgid: true,
|
msgid: true,
|
||||||
|
exp: true,
|
||||||
|
rdate: true,
|
||||||
ha: true,
|
ha: true,
|
||||||
unseen: true,
|
unseen: true,
|
||||||
|
undeleted: true,
|
||||||
flagged: true,
|
flagged: true,
|
||||||
|
draft: true,
|
||||||
attachments: true,
|
attachments: true,
|
||||||
map: true,
|
map: true,
|
||||||
html: true
|
html: true
|
||||||
|
@ -1899,6 +1911,11 @@ server.get('/users/:user/mailboxes/:mailbox/messages/:message', (req, res, next)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let expires;
|
||||||
|
if (messageData.exp) {
|
||||||
|
expires = new Date(messageData.rdate).toISOString();
|
||||||
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
id: message.toString() + ':' + uid,
|
id: message.toString() + ':' + uid,
|
||||||
|
@ -1910,6 +1927,11 @@ server.get('/users/:user/mailboxes/:mailbox/messages/:message', (req, res, next)
|
||||||
messageId: messageData.msgid,
|
messageId: messageData.msgid,
|
||||||
date: messageData.hdate.toISOString(),
|
date: messageData.hdate.toISOString(),
|
||||||
list,
|
list,
|
||||||
|
expires,
|
||||||
|
seen: !messageData.unseen,
|
||||||
|
deleted: !messageData.undeleted,
|
||||||
|
flagged: messageData.flagged,
|
||||||
|
draft: messageData.draft,
|
||||||
html: messageData.html,
|
html: messageData.html,
|
||||||
attachments: (messageData.attachments || [])
|
attachments: (messageData.attachments || [])
|
||||||
.map(attachment => {
|
.map(attachment => {
|
||||||
|
@ -1931,6 +1953,202 @@ server.get('/users/:user/mailboxes/:mailbox/messages/:message', (req, res, next)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.put('/users/:user/mailboxes/:mailbox/messages/:message', (req, res, next) => {
|
||||||
|
res.charSet('utf-8');
|
||||||
|
|
||||||
|
const schema = Joi.object().keys({
|
||||||
|
user: Joi.string().hex().lowercase().length(24).required(),
|
||||||
|
mailbox: Joi.string().hex().lowercase().length(24).required(),
|
||||||
|
message: Joi.string().regex(/^[0-9a-f]{24}:\d{1,10}/).lowercase().required(),
|
||||||
|
seen: Joi.boolean().truthy(['Y', 'true', 'yes', 1]),
|
||||||
|
deleted: Joi.boolean().truthy(['Y', 'true', 'yes', 1]),
|
||||||
|
flagged: Joi.boolean().truthy(['Y', 'true', 'yes', 1]),
|
||||||
|
draft: Joi.boolean().truthy(['Y', 'true', 'yes', 1]),
|
||||||
|
expires: Joi.alternatives().try(Joi.date(), Joi.boolean().truthy(['Y', 'true', 'yes', 1]).allow(false))
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = Joi.validate(req.params, schema, {
|
||||||
|
abortEarly: false,
|
||||||
|
convert: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (result.error) {
|
||||||
|
res.json({
|
||||||
|
error: result.error.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let messageparts = result.value.message.split(':');
|
||||||
|
let user = new ObjectID(result.value.user);
|
||||||
|
let mailbox = new ObjectID(result.value.mailbox);
|
||||||
|
let message = new ObjectID(messageparts[0]);
|
||||||
|
let uid = Number(messageparts[1]);
|
||||||
|
|
||||||
|
let updates = { $set: {} };
|
||||||
|
let update = false;
|
||||||
|
let addFlags = [];
|
||||||
|
let removeFlags = [];
|
||||||
|
|
||||||
|
Object.keys(result.value || {}).forEach(key => {
|
||||||
|
switch (key) {
|
||||||
|
case 'seen':
|
||||||
|
updates.$set.unseen = !result.value.seen;
|
||||||
|
if (result.value.seen) {
|
||||||
|
addFlags.push('\\Seen');
|
||||||
|
} else {
|
||||||
|
removeFlags.push('\\Seen');
|
||||||
|
}
|
||||||
|
update = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'deleted':
|
||||||
|
updates.$set.undeleted = !result.value.deleted;
|
||||||
|
if (result.value.deleted) {
|
||||||
|
addFlags.push('\\Deleted');
|
||||||
|
} else {
|
||||||
|
removeFlags.push('\\Deleted');
|
||||||
|
}
|
||||||
|
update = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'flagged':
|
||||||
|
updates.$set.flagged = result.value.flagged;
|
||||||
|
if (result.value.flagged) {
|
||||||
|
addFlags.push('\\Flagged');
|
||||||
|
} else {
|
||||||
|
removeFlags.push('\\Flagged');
|
||||||
|
}
|
||||||
|
update = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'draft':
|
||||||
|
updates.$set.flagged = result.value.draft;
|
||||||
|
if (result.value.draft) {
|
||||||
|
addFlags.push('\\Draft');
|
||||||
|
} else {
|
||||||
|
removeFlags.push('\\Draft');
|
||||||
|
}
|
||||||
|
update = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'expires':
|
||||||
|
if (result.value.expires) {
|
||||||
|
updates.$set.exp = true;
|
||||||
|
updates.$set.rdate = result.value.expires.getTime();
|
||||||
|
} else {
|
||||||
|
updates.$set.exp = false;
|
||||||
|
}
|
||||||
|
update = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!update) {
|
||||||
|
res.json({
|
||||||
|
error: 'Nothing was changed'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addFlags.length) {
|
||||||
|
if (!updates.$addToSet) {
|
||||||
|
updates.$addToSet = {};
|
||||||
|
}
|
||||||
|
updates.$addToSet.flags = { $each: addFlags };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (removeFlags.length) {
|
||||||
|
if (!updates.$pull) {
|
||||||
|
updates.$pull = {};
|
||||||
|
}
|
||||||
|
updates.$pull.flags = { $in: removeFlags };
|
||||||
|
}
|
||||||
|
|
||||||
|
// acquire new MODSEQ
|
||||||
|
db.database.collection('mailboxes').findOneAndUpdate({
|
||||||
|
_id: mailbox,
|
||||||
|
user
|
||||||
|
}, {
|
||||||
|
$inc: {
|
||||||
|
// allocate new MODSEQ value
|
||||||
|
modifyIndex: 1
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
returnOriginal: false
|
||||||
|
}, (err, item) => {
|
||||||
|
if (err) {
|
||||||
|
res.json({
|
||||||
|
error: err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item || !item.value) {
|
||||||
|
// was not able to acquire a lock
|
||||||
|
res.json({
|
||||||
|
error: 'Mailbox is missing'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mailboxData = item.value;
|
||||||
|
|
||||||
|
updates.$set.modseq = mailboxData.modifyIndex;
|
||||||
|
|
||||||
|
db.database.collection('messages').findOneAndUpdate({
|
||||||
|
_id: message,
|
||||||
|
// hash key
|
||||||
|
mailbox,
|
||||||
|
uid
|
||||||
|
}, updates, {
|
||||||
|
projection: {
|
||||||
|
flags: true,
|
||||||
|
exp: true,
|
||||||
|
rdate: true
|
||||||
|
},
|
||||||
|
returnOriginal: false
|
||||||
|
}, (err, item) => {
|
||||||
|
if (err) {
|
||||||
|
res.json({
|
||||||
|
error: err.message
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!item || !item.value) {
|
||||||
|
// message was not found for whatever reason
|
||||||
|
res.json({
|
||||||
|
error: 'Message was not found'
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
let messageData = item.value;
|
||||||
|
|
||||||
|
notifier.addEntries(
|
||||||
|
mailboxData,
|
||||||
|
false,
|
||||||
|
{
|
||||||
|
command: 'FETCH',
|
||||||
|
uid,
|
||||||
|
flags: messageData.flags,
|
||||||
|
message: message._id,
|
||||||
|
unseenChange: !!result.value.unseen
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
notifier.fire(mailboxData.user, mailboxData.path);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true
|
||||||
|
});
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
server.get('/users/:user/updates', (req, res, next) => {
|
server.get('/users/:user/updates', (req, res, next) => {
|
||||||
res.charSet('utf-8');
|
res.charSet('utf-8');
|
||||||
|
|
||||||
|
@ -2158,13 +2376,17 @@ function loadJournalStream(req, res, user, lastEventId, done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (e.command) {
|
switch (e.command) {
|
||||||
case 'FETCH':
|
|
||||||
case 'EXISTS':
|
case 'EXISTS':
|
||||||
case 'EXPUNGE':
|
case 'EXPUNGE':
|
||||||
if (e.mailbox) {
|
if (e.mailbox) {
|
||||||
mailboxes.add(e.mailbox.toString());
|
mailboxes.add(e.mailbox.toString());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'FETCH':
|
||||||
|
if (e.mailbox && (e.unseen || e.unseenChange)) {
|
||||||
|
mailboxes.add(e.mailbox.toString());
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.write(formatJournalData(e));
|
res.write(formatJournalData(e));
|
||||||
|
|
|
@ -369,14 +369,14 @@ class MessageHandler {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mailbox = item.value;
|
let mailboxData = item.value;
|
||||||
let uid = mailbox.uidNext;
|
let uid = mailboxData.uidNext;
|
||||||
let modseq = mailbox.modifyIndex + 1;
|
let modseq = mailboxData.modifyIndex + 1;
|
||||||
|
|
||||||
this.database.collection('messages').findOneAndUpdate({
|
this.database.collection('messages').findOneAndUpdate({
|
||||||
_id: existing._id,
|
_id: existing._id,
|
||||||
// hash key
|
// hash key
|
||||||
mailbox: mailbox._id,
|
mailbox: mailboxData._id,
|
||||||
uid: existing.uid
|
uid: existing.uid
|
||||||
}, {
|
}, {
|
||||||
$set: {
|
$set: {
|
||||||
|
@ -398,15 +398,15 @@ class MessageHandler {
|
||||||
|
|
||||||
let updated = item.value;
|
let updated = item.value;
|
||||||
|
|
||||||
if (options.session && options.session.selected && options.session.selected.mailbox === mailbox.path) {
|
if (options.session && options.session.selected && options.session.selected.mailbox === mailboxData.path) {
|
||||||
options.session.writeStream.write(options.session.formatResponse('EXPUNGE', existing.uid));
|
options.session.writeStream.write(options.session.formatResponse('EXPUNGE', existing.uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.session && options.session.selected && options.session.selected.mailbox === mailbox.path) {
|
if (options.session && options.session.selected && options.session.selected.mailbox === mailboxData.path) {
|
||||||
options.session.writeStream.write(options.session.formatResponse('EXISTS', updated.uid));
|
options.session.writeStream.write(options.session.formatResponse('EXISTS', updated.uid));
|
||||||
}
|
}
|
||||||
this.notifier.addEntries(
|
this.notifier.addEntries(
|
||||||
mailbox,
|
mailboxData,
|
||||||
false,
|
false,
|
||||||
{
|
{
|
||||||
command: 'EXPUNGE',
|
command: 'EXPUNGE',
|
||||||
|
@ -417,7 +417,7 @@ class MessageHandler {
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.notifier.addEntries(
|
this.notifier.addEntries(
|
||||||
mailbox,
|
mailboxData,
|
||||||
false,
|
false,
|
||||||
{
|
{
|
||||||
command: 'EXISTS',
|
command: 'EXISTS',
|
||||||
|
@ -428,9 +428,9 @@ class MessageHandler {
|
||||||
unseen: updated.unseen
|
unseen: updated.unseen
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
this.notifier.fire(mailbox.user, mailbox.path);
|
this.notifier.fire(mailboxData.user, mailboxData.path);
|
||||||
return callback(null, true, {
|
return callback(null, true, {
|
||||||
uidValidity: mailbox.uidValidity,
|
uidValidity: mailboxData.uidValidity,
|
||||||
uid,
|
uid,
|
||||||
id: existing._id,
|
id: existing._id,
|
||||||
status: 'update'
|
status: 'update'
|
||||||
|
|
112
package.json
112
package.json
|
@ -1,58 +1,58 @@
|
||||||
{
|
{
|
||||||
"name": "wildduck",
|
"name": "wildduck",
|
||||||
"version": "1.0.54",
|
"version": "1.0.55",
|
||||||
"description": "IMAP server built with Node.js and MongoDB",
|
"description": "IMAP server built with Node.js and MongoDB",
|
||||||
"main": "server.js",
|
"main": "server.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "grunt"
|
"test": "grunt"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "Andris Reinman",
|
"author": "Andris Reinman",
|
||||||
"license": "EUPL-1.1",
|
"license": "EUPL-1.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browserbox": "^0.9.1",
|
"browserbox": "^0.9.1",
|
||||||
"chai": "^4.1.0",
|
"chai": "^4.1.0",
|
||||||
"eslint-config-nodemailer": "^1.2.0",
|
"eslint-config-nodemailer": "^1.2.0",
|
||||||
"grunt": "^1.0.1",
|
"grunt": "^1.0.1",
|
||||||
"grunt-cli": "^1.2.0",
|
"grunt-cli": "^1.2.0",
|
||||||
"grunt-eslint": "^20.0.0",
|
"grunt-eslint": "^20.0.0",
|
||||||
"grunt-mocha-test": "^0.13.2",
|
"grunt-mocha-test": "^0.13.2",
|
||||||
"mocha": "^3.4.2"
|
"mocha": "^3.4.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"addressparser": "^1.0.1",
|
"addressparser": "^1.0.1",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"generate-password": "^1.3.0",
|
"generate-password": "^1.3.0",
|
||||||
"html-to-text": "^3.3.0",
|
"html-to-text": "^3.3.0",
|
||||||
"iconv-lite": "^0.4.18",
|
"iconv-lite": "^0.4.18",
|
||||||
"joi": "^10.6.0",
|
"joi": "^10.6.0",
|
||||||
"js-yaml": "^3.9.0",
|
"js-yaml": "^3.9.0",
|
||||||
"libbase64": "^0.2.0",
|
"libbase64": "^0.2.0",
|
||||||
"libmime": "^3.1.0",
|
"libmime": "^3.1.0",
|
||||||
"libqp": "^1.1.0",
|
"libqp": "^1.1.0",
|
||||||
"mailsplit": "^4.0.2",
|
"mailsplit": "^4.0.2",
|
||||||
"mongo-cursor-pagination": "^5.0.0",
|
"mongo-cursor-pagination": "^5.0.0",
|
||||||
"mongodb": "^2.2.30",
|
"mongodb": "^2.2.30",
|
||||||
"node-redis-scripty": "0.0.5",
|
"node-redis-scripty": "0.0.5",
|
||||||
"nodemailer": "^4.0.1",
|
"nodemailer": "^4.0.1",
|
||||||
"npmlog": "^4.1.2",
|
"npmlog": "^4.1.2",
|
||||||
"qrcode": "^0.8.2",
|
"qrcode": "^0.8.2",
|
||||||
"redfour": "^1.0.2",
|
"redfour": "^1.0.2",
|
||||||
"redis": "^2.7.1",
|
"redis": "^2.7.1",
|
||||||
"restify": "^5.0.1",
|
"restify": "^5.0.1",
|
||||||
"seq-index": "^1.1.0",
|
"seq-index": "^1.1.0",
|
||||||
"smtp-server": "^3.0.1",
|
"smtp-server": "^3.0.1",
|
||||||
"speakeasy": "^2.0.0",
|
"speakeasy": "^2.0.0",
|
||||||
"utf7": "^1.0.2",
|
"utf7": "^1.0.2",
|
||||||
"uuid": "^3.1.0",
|
"uuid": "^3.1.0",
|
||||||
"wild-config": "^1.0.0"
|
"wild-config": "^1.0.0"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git://github.com/wildduck-email/wildduck.git"
|
"url": "git://github.com/wildduck-email/wildduck.git"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@ronomon/crypto-async": "^2.0.1",
|
"@ronomon/crypto-async": "^2.0.1",
|
||||||
"modern-syslog": "^1.1.4"
|
"modern-syslog": "^1.1.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue