From 8d03e37ac70d156b5eb97bb24ee34698fb88a905 Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Mon, 22 May 2017 10:03:30 +0300 Subject: [PATCH] Allow setting default retention to all mailboxes --- README.md | 1 + config/default.js | 6 +++++- imap.js | 44 ++++++++++++++++++++++++++++-------------- lib/message-handler.js | 10 +++++----- lib/user-handler.js | 24 ++++++++++++++++++++++- 5 files changed, 63 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 483a9723..7b397532 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,7 @@ Arguments - **username** is the username of the user. This is not an email address but authentication username, use only letters and numbers - **password** is the password for the user - **quota** (optional) is the maximum storage in bytes allowed for this user. If not set then the default value is used +- **retention** (optional) is the default retention time in ms for mailboxes. Messages in Trash and Junk folders have a maximum retention time of 30 days. **Example** diff --git a/config/default.js b/config/default.js index 24f7f10c..d1cf4598 100644 --- a/config/default.js +++ b/config/default.js @@ -1,5 +1,7 @@ 'use strict'; +const os = require('os'); + module.exports = { log: { level: 'silly' @@ -95,5 +97,7 @@ module.exports = { maxRecipients: 2000, // default forwarded messages for 24h (can be overriden per user) - maxForwards: 2000 + maxForwards: 2000, + + emailDomain: os.hostname() }; diff --git a/imap.js b/imap.js index 3095303a..d5e719db 100644 --- a/imap.js +++ b/imap.js @@ -195,21 +195,34 @@ server.onCreate = function (path, session, callback) { return callback(null, 'ALREADYEXISTS'); } - mailbox = { - user: session.user.id, - path, - uidValidity: Math.floor(Date.now() / 1000), - uidNext: 1, - modifyIndex: 0, - subscribed: true, - flags: [] - }; - - db.database.collection('mailboxes').insertOne(mailbox, err => { + db.database.collection('users').findOne({ + _id: session.user.id + }, { + fields: { + retention: true + } + }, (err, user) => { if (err) { return callback(err); } - return callback(null, true); + + mailbox = { + user: session.user.id, + path, + uidValidity: Math.floor(Date.now() / 1000), + uidNext: 1, + modifyIndex: 0, + subscribed: true, + flags: [], + retention: user.retention + }; + + db.database.collection('mailboxes').insertOne(mailbox, err => { + if (err) { + return callback(err); + } + return callback(null, true); + }); }); }); }; @@ -1005,8 +1018,9 @@ server.onCopy = function (path, update, session, callback) { message.mailbox = target._id; message.uid = uidNext; - message.exp = ['\\Trash', '\\Junk'].includes(target.specialUse); - message.rdate = new Date(); + // retention settings + message.exp = !!target.retention; + message.rdate = Date.now() + (target.retention || 0); if (!message.meta) { message.meta = {}; @@ -1778,7 +1792,7 @@ function clearExpiredMessages() { let cursor = db.database.collection('messages').find({ exp: true, rdate: { - $lte: new Date(Date.now() - config.imap.retention * 24 * 3600 * 1000) + $lte: Date.now() } }).project({ _id: true, diff --git a/lib/message-handler.js b/lib/message-handler.js index 7ac86b8d..a0a776f9 100644 --- a/lib/message-handler.js +++ b/lib/message-handler.js @@ -246,9 +246,9 @@ class MessageHandler { _id: id, // if true then expirest after rdate + retention - exp: ['\\Trash', '\\Junk'].includes(mailbox.specialUse), + exp: !!mailbox.retention, + rdate: Date.now() + (mailbox.retention || 0), - rdate: new Date(), idate, hdate, flags, @@ -630,9 +630,9 @@ class MessageHandler { // this will be changed later by the notification system modseq: 0, - // if exp=true, then remove message after rdate + retention ploicy - exp: ['\\Trash', '\\Junk'].includes(target.specialUse), - rdate: new Date() + // retention settings + exp: !!target.retention, + rdate: Date.now() + (target.retention || 0) } }; diff --git a/lib/user-handler.js b/lib/user-handler.js index dcedb0e5..9e13a609 100644 --- a/lib/user-handler.js +++ b/lib/user-handler.js @@ -13,6 +13,7 @@ const base32 = require('base32.js'); const MAX_STORAGE = 1 * (1024 * 1024 * 1024); const MAX_RECIPIENTS = 2000; const MAX_FORWARDS = 2000; +const JUNK_RETENTION = 30 * 24 * 3600 * 1000; const mailboxTranslations = { en: { @@ -48,6 +49,11 @@ class UserHandler { meta = {}; } + if (!password) { + // do not allow signing in without a password + return callback(null, false); + } + let checkAddress = next => { if (username.indexOf('@') < 0) { // assume regular username @@ -206,8 +212,14 @@ class UserHandler { return callback(err); } + let retention = Number(data.retention) || 0; + let junkRetention = JUNK_RETENTION; + if (retention < 0) { + retention = 0; + } + // Insert - let hash = bcrypt.hashSync(data.password, 11); + let hash = data.password ? bcrypt.hashSync(data.password, 11) : ''; this.database.collection('users').insertOne({ username: data.username, name: data.name, @@ -230,6 +242,9 @@ class UserHandler { filters: [], + // default retention for user mailboxes + retention, + created: new Date(), // until setup value is not true, this account is not usable @@ -244,6 +259,13 @@ class UserHandler { let mailboxes = this.getMailboxes(data.language).map(mailbox => { mailbox.user = user; + + if (['\\Trash', '\\Junk'].includes(mailbox.specialUse)) { + mailbox.retention = retention ? Math.min(retention, junkRetention) : junkRetention; + } else { + mailbox.retention = retention; + } + return mailbox; });