diff --git a/.eslintrc b/.eslintrc index 06e364c0..7ca59e93 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,6 @@ { + "rules": { + "indent": 0 + }, "extends": "nodemailer" } diff --git a/config/sender.toml b/config/sender.toml index fe8c35b4..47a8a582 100644 --- a/config/sender.toml +++ b/config/sender.toml @@ -1,7 +1,3 @@ -# Push messages to ZoneMTA queue for delivery -# if `false` then no messages are sent -enabled=true - # which ZoneMTA queue to use by default zone="default" diff --git a/lib/api/addresses.js b/lib/api/addresses.js index 5e501bdb..d3b4f7fc 100644 --- a/lib/api/addresses.js +++ b/lib/api/addresses.js @@ -1002,7 +1002,7 @@ module.exports = (db, server) => { * @apiParam {Number} [forwards] Daily allowed forwarding count for this address * @apiParam {Boolean} [allowWildcard=false] If true then address value can be in the form of *@example.com, otherwise using * is not allowed * @apiParam {Object} [autoreply] Autoreply information - * @apiParam {Boolean} [autoreply.enabled] If true, then autoreply is enabled for this address + * @apiParam {Boolean} [autoreply.status] If true, then autoreply is enabled for this address * @apiParam {String} [autoreply.start] Either a date string or boolean false to disable start time checks * @apiParam {String} [autoreply.end] Either a date string or boolean false to disable end time checks * @apiParam {String} [autoreply.subject] Autoreply subject line @@ -1062,7 +1062,7 @@ module.exports = (db, server) => { .default(0), allowWildcard: Joi.boolean().truthy(['Y', 'true', 'yes', 1]), autoreply: Joi.object().keys({ - enabled: Joi.boolean() + status: Joi.boolean() .truthy(['Y', 'true', 'yes', 1]) .default(true), start: Joi.date() @@ -1127,7 +1127,7 @@ module.exports = (db, server) => { } } else { result.value.autoreply = { - enabled: false + status: false }; } @@ -1299,7 +1299,7 @@ module.exports = (db, server) => { * @apiParam {String[]} [targets] An array of forwarding targets. The value could either be an email address or a relay url to next MX server ("smtp://mx2.zone.eu:25"). If set then overwrites previous targets array * @apiParam {Number} [forwards] Daily allowed forwarding count for this address * @apiParam {Object} [autoreply] Autoreply information - * @apiParam {Boolean} [autoreply.enabled] If true, then autoreply is enabled for this address + * @apiParam {Boolean} [autoreply.status] If true, then autoreply is enabled for this address * @apiParam {String} [autoreply.start] Either a date string or boolean false to disable start time checks * @apiParam {String} [autoreply.end] Either a date string or boolean false to disable end time checks * @apiParam {String} [autoreply.subject] Autoreply subject line @@ -1351,7 +1351,7 @@ module.exports = (db, server) => { .min(1), forwards: Joi.number().min(0), autoreply: Joi.object().keys({ - enabled: Joi.boolean().truthy(['Y', 'true', 'yes', 1]), + status: Joi.boolean().truthy(['Y', 'true', 'yes', 1]), start: Joi.date() .empty('') .allow(false), @@ -1708,7 +1708,7 @@ module.exports = (db, server) => { * @apiSuccess {Number} limits.forwards.used How many messages are forwarded during current 24 hour period * @apiSuccess {Number} limits.forwards.ttl Time until the end of current 24 hour period * @apiSuccess {Object} autoreply Autoreply information - * @apiSuccess {Boolean} autoreply.enabled If true, then autoreply is enabled for this address + * @apiSuccess {Boolean} autoreply.status If true, then autoreply is enabled for this address * @apiSuccess {String} autoreply.subject Autoreply subject line * @apiSuccess {String} autoreply.text Autoreply plaintext content * @apiSuccess {String} autoreply.html Autoreply HTML content @@ -1818,7 +1818,7 @@ module.exports = (db, server) => { ttl: forwardsTtl >= 0 ? forwardsTtl : false } }, - autoreply: addressData.autoreply || { enabled: false }, + autoreply: addressData.autoreply || { status: false }, created: addressData.created }); @@ -1851,7 +1851,7 @@ module.exports = (db, server) => { * @apiSuccess {Number} limits.forwards.used How many messages are forwarded during current 24 hour period * @apiSuccess {Number} limits.forwards.ttl Time until the end of current 24 hour period * @apiSuccess {Object} autoreply Autoreply information - * @apiSuccess {Boolean} autoreply.enabled If true, then autoreply is enabled for this address + * @apiSuccess {Boolean} autoreply.status If true, then autoreply is enabled for this address * @apiSuccess {String} autoreply.subject Autoreply subject line * @apiSuccess {String} autoreply.text Autoreply plaintext content * @apiSuccess {String} autoreply.html Autoreply HTML content @@ -1889,7 +1889,7 @@ module.exports = (db, server) => { * } * }, * "autoreply": { - * "enabled": false + * "status": false * }, * "created": "2017-10-24T11:19:10.911Z" * } @@ -1995,7 +1995,7 @@ module.exports = (db, server) => { ttl: forwardsTtl >= 0 ? forwardsTtl : false } }, - autoreply: addressData.autoreply || { enabled: false }, + autoreply: addressData.autoreply || { status: false }, created: addressData.created }); diff --git a/lib/autoreply.js b/lib/autoreply.js index 28f53f4a..f2b338ba 100644 --- a/lib/autoreply.js +++ b/lib/autoreply.js @@ -80,7 +80,7 @@ module.exports = (options, autoreplyData, callback) => { }, headers: { 'Auto-Submitted': 'auto-replied', - 'X-WD-Autoreply-For': (options.parentId || '').toString() + 'X-WD-Autoreply-For': (options.parentId || options.queueId).toString() }, inReplyTo: headers.getFirst('Message-ID'), references: (headers.getFirst('Message-ID') + ' ' + headers.getFirst('References')).trim(), @@ -104,18 +104,24 @@ module.exports = (options, autoreplyData, callback) => { } return callback(err, ...args); } - options.db.database.collection('messagelog').insertOne( - { - id: args[0].id, - messageId: args[0].messageId, - parentId: options.parentId, - action: 'AUTOREPLY', - from: '', - to: options.sender, - created: new Date() - }, - () => callback(err, args && args[0].id) - ); + let logentry = { + id: args[0].id, + messageId: args[0].messageId, + action: 'AUTOREPLY', + from: '', + to: options.sender, + sender: options.recipient, + created: new Date() + }; + + if (options.parentId) { + logentry.parentId = options.parentId; + } + if (options.queueId) { + logentry.queueId = options.queueId; + } + + options.db.database.collection('messagelog').insertOne(logentry, () => callback(err, args && args[0].id)); } ); diff --git a/lib/filter-handler.js b/lib/filter-handler.js index 075c8438..0d845fcf 100644 --- a/lib/filter-handler.js +++ b/lib/filter-handler.js @@ -42,7 +42,6 @@ class FilterHandler { this.spamChecks = options.spamChecks || tools.prepareSpamChecks(defaultSpamHeaderKeys); this.spamHeaderKeys = options.spamHeaderKeys || this.spamChecks.map(check => check.key); - this.senderEnabled = options.sender.enabled; this.maildrop = new Maildropper({ db: this.db, zone: options.sender.zone, @@ -329,10 +328,6 @@ class FilterHandler { }; let forwardMessage = done => { - if (!this.senderEnabled) { - return setImmediate(done); - } - if (userData.forward && !filterActions.get('delete')) { // forward to default recipient only if the message is not deleted (Array.isArray(userData.forward) ? userData.forward : [].concat(userData.forward || [])).forEach(forward => { @@ -397,10 +392,6 @@ class FilterHandler { }; let sendAutoreply = done => { - if (!this.senderEnabled) { - return setImmediate(done); - } - // never reply to messages marked as spam if (!sender || !userData.autoreply || filterActions.get('spam')) { return setImmediate(done); diff --git a/lib/maildrop.js b/lib/maildrop.js index 1173cf28..b6c1c5d1 100644 --- a/lib/maildrop.js +++ b/lib/maildrop.js @@ -7,16 +7,10 @@ const Maildropper = require('./maildropper'); let maildropper; module.exports = (options, callback) => { - if (!config.sender.enabled) { - setImmediate(() => callback(null, false)); - return false; - } - maildropper = maildropper || new Maildropper({ db, - enabled: config.sender.enabled, zone: config.sender.zone, collection: config.sender.collection, gfs: config.sender.gfs diff --git a/lib/user-handler.js b/lib/user-handler.js index bc1bcb96..2f0b4411 100644 --- a/lib/user-handler.js +++ b/lib/user-handler.js @@ -48,16 +48,22 @@ class UserHandler { let username = address.substr(0, address.indexOf('@')).replace(/\./g, ''); let domain = address.substr(address.indexOf('@') + 1); + let fields = { + user: true, + targets: true + }; + + Object.keys(options.fields || {}).forEach(key => { + fields[key] = true; + }); + // try exact match this.users.collection('addresses').findOne( { addrview: username + '@' + domain }, { - fields: { - user: true, - targets: true - } + fields }, (err, addressData) => { if (err) { @@ -87,10 +93,7 @@ class UserHandler { addrview: username + '@' + aliasDomain }, { - fields: { - user: true, - targets: true - } + fields }, done ); @@ -124,10 +127,7 @@ class UserHandler { this.users.collection('addresses').findOne( query, { - fields: { - user: true, - targets: true - } + fields }, (err, addressData) => { if (err) { @@ -145,10 +145,7 @@ class UserHandler { addrview: address.substr(0, address.indexOf('@')).replace(/\./g, '') + '@*' }, { - fields: { - user: true, - targets: true - } + fields }, (err, addressData) => { if (err) { @@ -1246,15 +1243,15 @@ class UserHandler { let update = !userData.enabled2fa || typeof userData.enabled2fa === 'boolean' ? { - $set: { - enabled2fa: ['totp'] - } - } + $set: { + enabled2fa: ['totp'] + } + } : { - $addToSet: { - enabled2fa: 'totp' - } - }; + $addToSet: { + enabled2fa: 'totp' + } + }; // token was valid, update user settings return this.users.collection('users').findOneAndUpdate( @@ -1328,19 +1325,19 @@ class UserHandler { let update = !userData.enabled2fa || typeof userData.enabled2fa === 'boolean' ? { - $set: { - enabled2fa: [], - seed: '' - } - } + $set: { + enabled2fa: [], + seed: '' + } + } : { - $pull: { - enabled2fa: 'totp' - }, - $set: { - seed: '' - } - }; + $pull: { + enabled2fa: 'totp' + }, + $set: { + seed: '' + } + }; return this.users.collection('users').findOneAndUpdate( { @@ -1627,25 +1624,25 @@ class UserHandler { let update = !userData.enabled2fa || typeof userData.enabled2fa === 'boolean' ? { - $set: { - enabled2fa: ['u2f'], - u2fKeyHandle: result.keyHandle, - u2fPubKey: result.publicKey, - u2fCert: result.certificate, - u2fDate: curDate - } - } + $set: { + enabled2fa: ['u2f'], + u2fKeyHandle: result.keyHandle, + u2fPubKey: result.publicKey, + u2fCert: result.certificate, + u2fDate: curDate + } + } : { - $addToSet: { - enabled2fa: 'u2f' - }, - $set: { - u2fKeyHandle: result.keyHandle, - u2fPubKey: result.publicKey, - u2fCert: result.certificate, - u2fDate: curDate - } - }; + $addToSet: { + enabled2fa: 'u2f' + }, + $set: { + u2fKeyHandle: result.keyHandle, + u2fPubKey: result.publicKey, + u2fCert: result.certificate, + u2fDate: curDate + } + }; return this.users.collection('users').findOneAndUpdate( { @@ -1719,23 +1716,23 @@ class UserHandler { let update = !userData.enabled2fa || typeof userData.enabled2fa === 'boolean' ? { - $set: { - enabled2fa: [], - u2fKeyHandle: '', - u2fPubKey: '', - u2fCert: '' - } - } + $set: { + enabled2fa: [], + u2fKeyHandle: '', + u2fPubKey: '', + u2fCert: '' + } + } : { - $pull: { - enabled2fa: 'u2f' - }, - $set: { - u2fKeyHandle: '', - u2fPubKey: '', - u2fCert: '' - } - }; + $pull: { + enabled2fa: 'u2f' + }, + $set: { + u2fKeyHandle: '', + u2fPubKey: '', + u2fCert: '' + } + }; return this.users.collection('users').findOneAndUpdate( { diff --git a/package.json b/package.json index f329bbb2..5fe47d23 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wildduck", - "version": "1.0.106", + "version": "1.0.107", "description": "IMAP server built with Node.js and MongoDB", "main": "server.js", "scripts": { @@ -31,7 +31,7 @@ "dependencies": { "addressparser": "1.0.1", "bcryptjs": "2.4.3", - "bugsnag": "2.0.1", + "bugsnag": "2.1.1", "generate-password": "1.3.0", "he": "1.1.1", "html-to-text": "3.3.0", @@ -40,7 +40,7 @@ "iconv-lite": "0.4.19", "ioredfour": "1.0.2-ioredis", "ioredis": "3.2.2", - "joi": "13.0.2", + "joi": "13.1.0", "js-yaml": "3.10.0", "key-fingerprint": "1.1.0", "libbase64": "1.0.2",