Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZMS-93 #542

Merged
merged 6 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 1 addition & 9 deletions lib/api/mailboxes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,7 @@ module.exports = (db, server, mailboxHandler) => {
const getMailboxCounter = util.promisify(tools.getMailboxCounter);
const updateMailbox = util.promisify(mailboxHandler.update.bind(mailboxHandler));
const deleteMailbox = util.promisify(mailboxHandler.del.bind(mailboxHandler));
const createMailbox = util.promisify((...args) => {
let callback = args.pop();
mailboxHandler.create(...args, (err, status, id) => {
if (err) {
return callback(err);
}
return callback(null, { status, id });
});
});
const createMailbox = mailboxHandler.createAsync.bind(mailboxHandler);

server.get(
'/users/:user/mailboxes',
Expand Down
5 changes: 4 additions & 1 deletion lib/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,5 +134,8 @@ module.exports = {
MAX_IMAP_UPLOAD: 10 * 1024 * 1024 * 1024,

// maximum number of filters per account
MAX_FILTERS: 400
MAX_FILTERS: 400,

// maximum amount of mailboxes per user
MAX_MAILBOXES: 1500
};
156 changes: 75 additions & 81 deletions lib/mailbox-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const ObjectId = require('mongodb').ObjectId;
const ImapNotifier = require('./imap-notifier');
const { publish, MAILBOX_CREATED, MAILBOX_RENAMED, MAILBOX_DELETED } = require('./events');
const { SettingsHandler } = require('./settings-handler');

class MailboxHandler {
constructor(options) {
Expand All @@ -19,99 +20,92 @@ class MailboxHandler {
redis: this.redis,
pushOnly: true
});

this.settingsHandler = new SettingsHandler({ db: this.database });
}

create(user, path, opts, callback) {
this.database.collection('mailboxes').findOne(
{
user,
path
},
(err, mailboxData) => {
if (err) {
return callback(err);
}
if (mailboxData) {
const err = new Error('Mailbox creation failed with code MailboxAlreadyExists');
err.code = 'ALREADYEXISTS';
err.responseCode = 400;
return callback(err, 'ALREADYEXISTS');
}
this.createAsync(user, path, opts)
.then(mailboxData => callback(null, ...[mailboxData.status, mailboxData.id]))
NickOvt marked this conversation as resolved.
Show resolved Hide resolved
.catch(err => callback(err));
}

this.users.collection('users').findOne(
{
_id: user
},
{
projection: {
retention: true
}
},
(err, userData) => {
if (err) {
return callback(err);
}
async createAsync(user, path, opts) {
const userData = await this.database.collection('users').findOne({ _id: user }, { projection: { retention: true } });

if (!userData) {
const err = new Error('This user does not exist');
err.code = 'UserNotFound';
err.responseCode = 404;
return callback(err, 'UserNotFound');
}
if (!userData) {
const err = new Error('This user does not exist');
err.code = 'UserNotFound';
err.responseCode = 404;
throw err;
}

mailboxData = {
_id: new ObjectId(),
user,
path,
uidValidity: Math.floor(Date.now() / 1000),
uidNext: 1,
modifyIndex: 0,
subscribed: true,
flags: [],
retention: userData.retention
};
let mailboxData = await this.database.collection('mailboxes').findOne({ user, path });

Object.keys(opts || {}).forEach(key => {
if (!['_id', 'user', 'path'].includes(key)) {
mailboxData[key] = opts[key];
}
});
if (mailboxData) {
const err = new Error('Mailbox creation failed with code MailboxAlreadyExists');
err.code = 'ALREADYEXISTS';
err.responseCode = 400;
throw err;
}

this.database.collection('mailboxes').insertOne(mailboxData, { writeConcern: 'majority' }, (err, r) => {
if (err) {
if (err.code === 11000) {
const err = new Error('Mailbox creation failed with code MailboxAlreadyExists');
err.code = 'ALREADYEXISTS';
err.responseCode = 400;
return callback(err, 'ALREADYEXISTS');
}
return callback(err);
}
const mailboxCountForUser = await this.database.collection('mailboxes').countDocuments({ user });

publish(this.redis, {
ev: MAILBOX_CREATED,
user,
mailbox: r.insertedId,
path: mailboxData.path
}).catch(() => false);
if (mailboxCountForUser > (await this.settingsHandler.get('const:max:mailboxes'))) {
const err = new Error('Mailbox creation failed with code ReachedMailboxCountLimit. Max mailboxes count reached.');
err.code = 'CANNOT';
err.responseCode = 400;
throw err;
}

return this.notifier.addEntries(
mailboxData,
{
command: 'CREATE',
mailbox: r.insertedId,
path
},
() => {
this.notifier.fire(user);
return callback(null, true, mailboxData._id);
}
);
});
}
);
mailboxData = {
_id: new ObjectId(),
user,
path,
uidValidity: Math.floor(Date.now() / 1000),
uidNext: 1,
modifyIndex: 0,
subscribed: true,
flags: [],
retention: userData.retention
};

Object.keys(opts || {}).forEach(key => {
if (!['_id', 'user', 'path'].includes(key)) {
mailboxData[key] = opts[key];
}
});

const r = this.database.collection('mailboxes').insertOne(mailboxData, { writeConcern: 'majority' });

try {
await publish(this.redis, {
ev: MAILBOX_CREATED,
user,
mailbox: r.insertedId,
path: mailboxData.path
});
} catch {
// ignore
}

await this.notifier.addEntries(
mailboxData,
{
command: 'CREATE',
mailbox: r.insertedId,
path
},
() => {
this.notifier.fire(user);
return;
}
);

return {
status: true,
id: mailboxData._id
};
}

rename(user, mailbox, newname, opts, callback) {
Expand Down
9 changes: 9 additions & 0 deletions lib/settings-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,15 @@ const SETTING_KEYS = [
schema: Joi.number()
},

{
key: 'const:max:mailboxes',
name: 'Max mailboxes',
description: 'Maximum amount of mailboxes for a user',
type: 'number',
constKey: 'MAX_MAILBOXES',
schema: Joi.number()
},

{
key: 'const:max:rcpt_to',
name: 'Max message recipients',
Expand Down