diff --git a/CHANGES b/CHANGES index d4741ef2a..111c06eb7 100644 --- a/CHANGES +++ b/CHANGES @@ -2050,4 +2050,5 @@ * postgresql: add btree_gist,postgres_fdw extensions for gitlab * SFTP/Filebrowser: fix access of external data directories * Fix contrast issues in dark mode +* Add option to delete mailbox data when mailbox is delete diff --git a/setup/start/sudoers b/setup/start/sudoers index a5d85136a..f71d9d035 100644 --- a/setup/start/sudoers +++ b/setup/start/sudoers @@ -50,3 +50,5 @@ yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/restartdocker.s Defaults!/home/yellowtent/box/src/scripts/restartunbound.sh env_keep="HOME BOX_ENV" yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/restartunbound.sh +Defaults!/home/yellowtent/box/src/scripts/rmmailbox.sh env_keep="HOME BOX_ENV" +yellowtent ALL=(root) NOPASSWD: /home/yellowtent/box/src/scripts/rmmailbox.sh diff --git a/src/mail.js b/src/mail.js index c75c61ab6..08c3c9fde 100644 --- a/src/mail.js +++ b/src/mail.js @@ -82,6 +82,7 @@ var assert = require('assert'), const DNS_OPTIONS = { timeout: 5000 }; var NOOP_CALLBACK = function (error) { if (error) debug(error); }; +const REMOVE_MAILBOX = path.join(__dirname, 'scripts/rmmailbox.sh'); function validateName(name) { assert.strictEqual(typeof name, 'string'); @@ -1124,18 +1125,25 @@ function updateMailboxOwner(name, domain, userId, auditSource, callback) { }); } -function removeMailbox(name, domain, auditSource, callback) { +function removeMailbox(name, domain, options, auditSource, callback) { assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof name, 'string'); + assert.strictEqual(typeof options, 'object'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); - mailboxdb.del(name, domain, function (error) { - if (error) return callback(error); + const deleteMailFunc = options.deleteMails ? shell.sudo.bind(null, 'removeMailbox', [ REMOVE_MAILBOX, `${name}@${domain}` ], {}) : (next) => next(); - eventlog.add(eventlog.ACTION_MAIL_MAILBOX_REMOVE, auditSource, { name, domain }); + deleteMailFunc(function (error) { + if (error) return callback(new BoxError(BoxError.FS_ERROR, `Error removing mailbox: ${error.message}`)); - callback(null); + mailboxdb.del(name, domain, function (error) { + if (error) return callback(error); + + eventlog.add(eventlog.ACTION_MAIL_MAILBOX_REMOVE, auditSource, { name, domain }); + + callback(); + }); }); } diff --git a/src/routes/mail.js b/src/routes/mail.js index a84eb30ef..cff0707f8 100644 --- a/src/routes/mail.js +++ b/src/routes/mail.js @@ -221,7 +221,9 @@ function removeMailbox(req, res, next) { assert.strictEqual(typeof req.params.domain, 'string'); assert.strictEqual(typeof req.params.name, 'string'); - mail.removeMailbox(req.params.name, req.params.domain, auditSource.fromRequest(req), function (error) { + if (typeof req.body.deleteMails !== 'boolean') return next(new HttpError(400, 'deleteMails must be a boolean')); + + mail.removeMailbox(req.params.name, req.params.domain, req.body, auditSource.fromRequest(req), function (error) { if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(201, {})); diff --git a/src/routes/test/mail-test.js b/src/routes/test/mail-test.js index 52789a8eb..c127e7d55 100644 --- a/src/routes/test/mail-test.js +++ b/src/routes/test/mail-test.js @@ -625,6 +625,7 @@ describe('Mail API', function () { it('disable fails even if not exist', function (done) { superagent.del(SERVER_URL + '/api/v1/mail/' + DOMAIN_0.domain + '/mailboxes/' + 'someuserdoesnotexist') + .send({ deleteMails: false }) .query({ access_token: token }) .end(function (err, res) { expect(res.statusCode).to.equal(404); @@ -634,6 +635,7 @@ describe('Mail API', function () { it('disable succeeds', function (done) { superagent.del(SERVER_URL + '/api/v1/mail/' + DOMAIN_0.domain + '/mailboxes/' + MAILBOX_NAME) + .send({ deleteMails: false }) .query({ access_token: token }) .end(function (err, res) { expect(res.statusCode).to.equal(201); diff --git a/src/scripts/rmmailbox.sh b/src/scripts/rmmailbox.sh new file mode 100755 index 000000000..457d8495c --- /dev/null +++ b/src/scripts/rmmailbox.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -eu -o pipefail + +if [[ ${EUID} -ne 0 ]]; then + echo "This script should be run as root." > /dev/stderr + exit 1 +fi + +if [[ $# -eq 0 ]]; then + echo "No arguments supplied" + exit 1 +fi + +if [[ "$1" == "--check" ]]; then + echo "OK" + exit 0 +fi + +mailbox="$1" + +if [[ "${BOX_ENV}" == "cloudron" ]]; then + readonly mailbox_dir="${HOME}/boxdata/mail/vmail/$1" +else + readonly mailbox_dir="${HOME}/.cloudron_test/boxdata/mail/vmail/$1" +fi + +rm -rf "${mailbox_dir}" + diff --git a/src/server.js b/src/server.js index 4e99e6a6b..b674c9ccd 100644 --- a/src/server.js +++ b/src/server.js @@ -266,7 +266,7 @@ function initializeExpressSync() { router.get ('/api/v1/mail/:domain/mailboxes/:name', token, authorizeAdmin, routes.mail.getMailbox); router.post('/api/v1/mail/:domain/mailboxes', json, token, authorizeAdmin, routes.mail.addMailbox); router.post('/api/v1/mail/:domain/mailboxes/:name', json, token, authorizeAdmin, routes.mail.updateMailbox); - router.del ('/api/v1/mail/:domain/mailboxes/:name', token, authorizeAdmin, routes.mail.removeMailbox); + router.del ('/api/v1/mail/:domain/mailboxes/:name', json, token, authorizeAdmin, routes.mail.removeMailbox); router.get ('/api/v1/mail/:domain/mailboxes/:name/aliases', token, authorizeAdmin, routes.mail.getAliases); router.put ('/api/v1/mail/:domain/mailboxes/:name/aliases', json, token, authorizeAdmin, routes.mail.setAliases); router.get ('/api/v1/mail/:domain/lists', token, authorizeAdmin, routes.mail.getLists);