mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-06 05:04:47 +08:00
allow configure autoexpunge
This commit is contained in:
parent
6c406c11f5
commit
97d004bd53
10 changed files with 566 additions and 536 deletions
|
@ -49,6 +49,9 @@ ignoredHosts=[]
|
||||||
#secure=false
|
#secure=false
|
||||||
#ignoreSTARTTLS=true
|
#ignoreSTARTTLS=true
|
||||||
|
|
||||||
|
# If true then EXPUNGE is called after a message gets a \Deleted flag set
|
||||||
|
autoExpunge=true
|
||||||
|
|
||||||
[setup]
|
[setup]
|
||||||
# Public configuration for IMAP
|
# Public configuration for IMAP
|
||||||
hostname="localhost"
|
hostname="localhost"
|
||||||
|
|
|
@ -9,3 +9,6 @@
|
||||||
redis="redis://127.0.0.1:6379/13"
|
redis="redis://127.0.0.1:6379/13"
|
||||||
|
|
||||||
dbname="wildduck-test"
|
dbname="wildduck-test"
|
||||||
|
|
||||||
|
[imap]
|
||||||
|
autoExpunge=false
|
||||||
|
|
|
@ -477,11 +477,11 @@ class IMAPConnection extends EventEmitter {
|
||||||
|
|
||||||
conn._listenerData.lock = true;
|
conn._listenerData.lock = true;
|
||||||
conn._server.notifier.getUpdates(selectedMailbox, conn.selected.modifyIndex, (err, updates) => {
|
conn._server.notifier.getUpdates(selectedMailbox, conn.selected.modifyIndex, (err, updates) => {
|
||||||
conn._listenerData.lock = false;
|
if (!conn._listenerData || conn._listenerData.cleared) {
|
||||||
if (conn._listenerData.cleared) {
|
|
||||||
// already logged out
|
// already logged out
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
conn._listenerData.lock = false;
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
conn.logger.info(
|
conn.logger.info(
|
||||||
|
|
|
@ -1089,8 +1089,8 @@ describe('IMAP Protocol integration tests', function() {
|
||||||
|
|
||||||
describe('EXPUNGE', function() {
|
describe('EXPUNGE', function() {
|
||||||
// EXPUNGE is a NO OP with autoexpunge
|
// EXPUNGE is a NO OP with autoexpunge
|
||||||
it.skip('should automatically expunge messages', function(done) {
|
it('should expunge all deleted messages', function(done) {
|
||||||
let cmds = ['T1 LOGIN testuser pass', 'T2 SELECT INBOX', 'T3 STORE 2:* +FLAGS (\\Deleted)', 'SLEEP', 'T4 EXPUNGE', 'T6 LOGOUT'];
|
let cmds = ['T1 LOGIN testuser pass', 'T2 SELECT INBOX', 'T3 STORE 2:* +FLAGS (\\Deleted)', 'T4 EXPUNGE', 'T6 LOGOUT'];
|
||||||
|
|
||||||
testClient(
|
testClient(
|
||||||
{
|
{
|
||||||
|
@ -1100,7 +1100,6 @@ describe('IMAP Protocol integration tests', function() {
|
||||||
},
|
},
|
||||||
function(resp) {
|
function(resp) {
|
||||||
resp = resp.toString();
|
resp = resp.toString();
|
||||||
console.log(resp);
|
|
||||||
expect(resp.match(/^\* \d+ EXPUNGE/gm).length).to.equal(5);
|
expect(resp.match(/^\* \d+ EXPUNGE/gm).length).to.equal(5);
|
||||||
expect(/^T4 OK/m.test(resp)).to.be.true;
|
expect(/^T4 OK/m.test(resp)).to.be.true;
|
||||||
done();
|
done();
|
||||||
|
@ -1111,8 +1110,8 @@ describe('IMAP Protocol integration tests', function() {
|
||||||
|
|
||||||
describe('UID EXPUNGE', function() {
|
describe('UID EXPUNGE', function() {
|
||||||
// UID EXPUNGE is a NO OP with autoexpunge
|
// UID EXPUNGE is a NO OP with autoexpunge
|
||||||
it.skip('should automatically expunge messages', function(done) {
|
it('should expunge specific messages', function(done) {
|
||||||
let cmds = ['T1 LOGIN testuser pass', 'T2 SELECT INBOX', 'T3 STORE 1:* +FLAGS (\\Deleted)', 'SLEEP', 'T4 UID EXPUNGE 50', 'T5 LOGOUT'];
|
let cmds = ['T1 LOGIN testuser pass', 'T2 SELECT INBOX', 'T3 STORE 1:* +FLAGS (\\Deleted)', 'T4 UID EXPUNGE 103,105', 'T5 LOGOUT'];
|
||||||
|
|
||||||
testClient(
|
testClient(
|
||||||
{
|
{
|
||||||
|
@ -1122,8 +1121,9 @@ describe('IMAP Protocol integration tests', function() {
|
||||||
},
|
},
|
||||||
function(resp) {
|
function(resp) {
|
||||||
resp = resp.toString();
|
resp = resp.toString();
|
||||||
expect(resp.match(/^\* \d+ EXPUNGE/gm).length).to.equal(1);
|
expect(resp.match(/^\* \d+ EXPUNGE/gm).length).to.equal(2);
|
||||||
expect(resp.match(/^\* 3 EXPUNGE/gm).length).to.equal(1);
|
expect(resp.match(/^\* 3 EXPUNGE/gm).length).to.equal(1);
|
||||||
|
expect(resp.match(/^\* 4 EXPUNGE/gm).length).to.equal(1);
|
||||||
expect(/^T4 OK/m.test(resp)).to.be.true;
|
expect(/^T4 OK/m.test(resp)).to.be.true;
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,8 @@ module.exports = (db, server, messageHandler) => {
|
||||||
function sendMessage(options, callback) {
|
function sendMessage(options, callback) {
|
||||||
let user = options.user;
|
let user = options.user;
|
||||||
|
|
||||||
db.users.collection('users').findOne({ _id: user },
|
db.users.collection('users').findOne(
|
||||||
|
{ _id: user },
|
||||||
{
|
{
|
||||||
fields: {
|
fields: {
|
||||||
username: true,
|
username: true,
|
||||||
|
@ -84,7 +85,8 @@ module.exports = (db, server, messageHandler) => {
|
||||||
return done(null, false);
|
return done(null, false);
|
||||||
}
|
}
|
||||||
query.user = user;
|
query.user = user;
|
||||||
db.users.collection('messages').findOne(query,
|
db.users.collection('messages').findOne(
|
||||||
|
query,
|
||||||
{
|
{
|
||||||
fields: {
|
fields: {
|
||||||
'mimeTree.parsedHeader': true,
|
'mimeTree.parsedHeader': true,
|
||||||
|
@ -178,7 +180,8 @@ module.exports = (db, server, messageHandler) => {
|
||||||
subject,
|
subject,
|
||||||
thread: messageData.thread
|
thread: messageData.thread
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
getReferencedMessage((err, referenceData) => {
|
getReferencedMessage((err, referenceData) => {
|
||||||
|
@ -291,7 +294,9 @@ module.exports = (db, server, messageHandler) => {
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
log.info('API', 'RCPTDENY denied sent=%s allowed=%s expires=%ss.', sent, userData.recipients, ttl);
|
log.info('API', 'RCPTDENY denied sent=%s allowed=%s expires=%ss.', sent, userData.recipients, ttl);
|
||||||
let err = new Error('You reached a daily sending limit for your account' + (ttl ? '. Limit expires in ' + ttlHuman : ''));
|
let err = new Error(
|
||||||
|
'You reached a daily sending limit for your account' + (ttl ? '. Limit expires in ' + ttlHuman : '')
|
||||||
|
);
|
||||||
err.code = 'ERRSENDINGLIMIT';
|
err.code = 'ERRSENDINGLIMIT';
|
||||||
return setImmediate(() => callback(err));
|
return setImmediate(() => callback(err));
|
||||||
}
|
}
|
||||||
|
@ -397,7 +402,8 @@ module.exports = (db, server, messageHandler) => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -13,7 +13,8 @@ module.exports = (options, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
let curtime = new Date();
|
let curtime = new Date();
|
||||||
db.database.collection('autoreplies').findOne({
|
db.database.collection('autoreplies').findOne(
|
||||||
|
{
|
||||||
user: options.userData._id,
|
user: options.userData._id,
|
||||||
start: {
|
start: {
|
||||||
$lte: curtime
|
$lte: curtime
|
||||||
|
@ -127,7 +128,8 @@ module.exports = (options, callback) => {
|
||||||
}
|
}
|
||||||
return callback(err, ...args);
|
return callback(err, ...args);
|
||||||
}
|
}
|
||||||
db.database.collection('messagelog').insertOne({
|
db.database.collection('messagelog').insertOne(
|
||||||
|
{
|
||||||
id: args[0].id,
|
id: args[0].id,
|
||||||
messageId: args[0].messageId,
|
messageId: args[0].messageId,
|
||||||
parentId: options.parentId,
|
parentId: options.parentId,
|
||||||
|
@ -136,7 +138,8 @@ module.exports = (options, callback) => {
|
||||||
to: options.sender,
|
to: options.sender,
|
||||||
created: new Date()
|
created: new Date()
|
||||||
},
|
},
|
||||||
() => callback(err, args && args[0].id));
|
() => callback(err, args && args[0].id)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -169,5 +172,6 @@ module.exports = (options, callback) => {
|
||||||
};
|
};
|
||||||
setImmediate(writeNextChunk);
|
setImmediate(writeNextChunk);
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,7 +28,8 @@ module.exports = (options, callback) => {
|
||||||
}
|
}
|
||||||
return callback(err, ...args);
|
return callback(err, ...args);
|
||||||
}
|
}
|
||||||
db.database.collection('messagelog').insertOne({
|
db.database.collection('messagelog').insertOne(
|
||||||
|
{
|
||||||
id: args[0].id,
|
id: args[0].id,
|
||||||
messageId: args[0].messageId,
|
messageId: args[0].messageId,
|
||||||
action: 'FORWARD',
|
action: 'FORWARD',
|
||||||
|
@ -38,7 +39,8 @@ module.exports = (options, callback) => {
|
||||||
targets: options.targets,
|
targets: options.targets,
|
||||||
created: new Date()
|
created: new Date()
|
||||||
},
|
},
|
||||||
() => callback(err, args && args[0] && args[0].id));
|
() => callback(err, args && args[0] && args[0].id)
|
||||||
|
);
|
||||||
});
|
});
|
||||||
if (message) {
|
if (message) {
|
||||||
if (options.stream) {
|
if (options.stream) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
|
const tools = require('../tools');
|
||||||
|
|
||||||
// EXPUNGE deletes all messages in selected mailbox marked with \Delete
|
// EXPUNGE deletes all messages in selected mailbox marked with \Delete
|
||||||
module.exports = (server, messageHandler) => (mailbox, update, session, callback) => {
|
module.exports = (server, messageHandler) => (mailbox, update, session, callback) => {
|
||||||
|
@ -25,18 +26,24 @@ module.exports = (server, messageHandler) => (mailbox, update, session, callback
|
||||||
return callback(null, 'NONEXISTENT');
|
return callback(null, 'NONEXISTENT');
|
||||||
}
|
}
|
||||||
|
|
||||||
let cursor = db.database
|
let query = {
|
||||||
.collection('messages')
|
|
||||||
.find({
|
|
||||||
user: session.user.id,
|
user: session.user.id,
|
||||||
mailbox: mailboxData._id,
|
mailbox: mailboxData._id,
|
||||||
undeleted: false,
|
undeleted: false,
|
||||||
// uid is part of the sharding key so we need it somehow represented in the query
|
// uid is part of the sharding key so we need it somehow represented in the query
|
||||||
uid: {
|
uid: {}
|
||||||
$gt: 0,
|
};
|
||||||
$lt: mailboxData.uidNext
|
|
||||||
|
if (update.isUid) {
|
||||||
|
query.uid = tools.checkRangeQuery(update.messages);
|
||||||
|
} else {
|
||||||
|
query.uid.$gt = 0;
|
||||||
|
query.uid.$lt = mailboxData.uidNext;
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
let cursor = db.database
|
||||||
|
.collection('messages')
|
||||||
|
.find(query)
|
||||||
.sort([['uid', 1]]);
|
.sort([['uid', 1]]);
|
||||||
|
|
||||||
let deletedMessages = 0;
|
let deletedMessages = 0;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const config = require('wild-config');
|
||||||
const imapTools = require('../../imap-core/lib/imap-tools');
|
const imapTools = require('../../imap-core/lib/imap-tools');
|
||||||
const db = require('../db');
|
const db = require('../db');
|
||||||
const tools = require('../tools');
|
const tools = require('../tools');
|
||||||
|
@ -114,7 +115,7 @@ module.exports = server => (mailbox, update, session, callback) => {
|
||||||
// first argument is an error
|
// first argument is an error
|
||||||
return callback(...args);
|
return callback(...args);
|
||||||
} else {
|
} else {
|
||||||
if (shouldExpunge) {
|
if (config.imap.autoExpunge && shouldExpunge) {
|
||||||
// shcedule EXPUNGE command for current folder
|
// shcedule EXPUNGE command for current folder
|
||||||
let expungeOptions = {
|
let expungeOptions = {
|
||||||
// create new temporary session so it would not mix with the active one
|
// create new temporary session so it would not mix with the active one
|
||||||
|
|
|
@ -279,7 +279,8 @@ module.exports = (options, callback) => {
|
||||||
documents.push(delivery);
|
documents.push(delivery);
|
||||||
}
|
}
|
||||||
|
|
||||||
db.senderDb.collection(config.sender.collection).insertMany(documents,
|
db.senderDb.collection(config.sender.collection).insertMany(
|
||||||
|
documents,
|
||||||
{
|
{
|
||||||
w: 1,
|
w: 1,
|
||||||
ordered: false
|
ordered: false
|
||||||
|
@ -290,7 +291,8 @@ module.exports = (options, callback) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(null, envelope);
|
callback(null, envelope);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -352,7 +354,8 @@ function removeMessage(id, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setMeta(id, data, callback) {
|
function setMeta(id, data, callback) {
|
||||||
db.senderDb.collection(config.sender.gfs + '.files').findOneAndUpdate({
|
db.senderDb.collection(config.sender.gfs + '.files').findOneAndUpdate(
|
||||||
|
{
|
||||||
filename: 'message ' + id
|
filename: 'message ' + id
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -366,5 +369,6 @@ function setMeta(id, data, callback) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
return callback();
|
return callback();
|
||||||
});
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue