mirror of
https://github.com/nodemailer/wildduck.git
synced 2024-09-20 15:26:03 +08:00
allow name properties for addresses
This commit is contained in:
parent
3c14de845e
commit
261f28c15c
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-24T09:29:05.158Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-24T11:37:44.868Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
|
|
|
@ -1 +1 @@
|
|||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-24T09:29:05.158Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-24T11:37:44.868Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
|
|
|
@ -33,6 +33,7 @@ module.exports = (db, server) => {
|
|||
* @apiSuccess {String} nextCursor Either a cursor string or false if there are not any next results
|
||||
* @apiSuccess {Object[]} results Address listing
|
||||
* @apiSuccess {String} results.id ID of the Address
|
||||
* @apiSuccess {String} results.name Identity name
|
||||
* @apiSuccess {String} results.address E-mail address string
|
||||
* @apiSuccess {String} results.user User ID this address belongs to if this is an User address
|
||||
* @apiSuccess {Boolean} results.forwarded If true then it is a forwarded address
|
||||
|
@ -181,6 +182,7 @@ module.exports = (db, server) => {
|
|||
fields: {
|
||||
_id: true,
|
||||
address: true,
|
||||
name: true,
|
||||
user: true,
|
||||
tags: true,
|
||||
targets: true
|
||||
|
@ -216,6 +218,7 @@ module.exports = (db, server) => {
|
|||
nextCursor: result.hasNext ? result.next : false,
|
||||
results: (result.results || []).map(addressData => ({
|
||||
id: addressData._id.toString(),
|
||||
name: addressData.name || false,
|
||||
address: addressData.address,
|
||||
user: addressData.user,
|
||||
forwarded: addressData.targets && true,
|
||||
|
@ -246,6 +249,7 @@ module.exports = (db, server) => {
|
|||
*
|
||||
* @apiParam {String} user ID of the User
|
||||
* @apiParam {String} address E-mail Address
|
||||
* @apiParam {String} [name] Identity name
|
||||
* @apiParam {String[]} [tags] A list of tags associated with this address
|
||||
* @apiParam {Boolean} [main=false] Indicates if this is the default address for the User
|
||||
* @apiParam {Boolean} [allowWildcard=false] If <code>true</code> then address value can be in the form of <code>*@example.com</code>, otherwise using * is not allowed
|
||||
|
@ -290,6 +294,11 @@ module.exports = (db, server) => {
|
|||
.required(),
|
||||
Joi.string().regex(/^\w+@\*$/, 'special address')
|
||||
],
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.trim()
|
||||
.max(128)
|
||||
.required(),
|
||||
main: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 'on', 1])
|
||||
.falsy(['N', 'false', 'no', 'off', 0, '']),
|
||||
|
@ -318,6 +327,7 @@ module.exports = (db, server) => {
|
|||
|
||||
let user = new ObjectID(result.value.user);
|
||||
let main = result.value.main;
|
||||
let name = result.value.name;
|
||||
let address = tools.normalizeAddress(result.value.address);
|
||||
|
||||
if (address.indexOf('+') >= 0) {
|
||||
|
@ -417,6 +427,7 @@ module.exports = (db, server) => {
|
|||
|
||||
addressData = {
|
||||
user,
|
||||
name,
|
||||
address,
|
||||
addrview: address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@')),
|
||||
created: new Date()
|
||||
|
@ -487,6 +498,7 @@ module.exports = (db, server) => {
|
|||
* @apiSuccess {Boolean} success Indicates successful response
|
||||
* @apiSuccess {Object[]} results Address listing
|
||||
* @apiSuccess {String} results.id ID of the Address
|
||||
* @apiSuccess {String} results.name Identity name
|
||||
* @apiSuccess {String} results.address E-mail address string
|
||||
* @apiSuccess {Boolean} results.main Indicates if this is the default address for the User
|
||||
* @apiSuccess {String} results.created Datestring of the time the address was created
|
||||
|
@ -553,6 +565,7 @@ module.exports = (db, server) => {
|
|||
},
|
||||
{
|
||||
fields: {
|
||||
name: true,
|
||||
address: true
|
||||
}
|
||||
},
|
||||
|
@ -598,6 +611,7 @@ module.exports = (db, server) => {
|
|||
|
||||
results: addresses.map(address => ({
|
||||
id: address._id,
|
||||
name: address.name || false,
|
||||
address: address.address,
|
||||
main: address.address === userData.address,
|
||||
tags: address.tags || [],
|
||||
|
@ -626,6 +640,7 @@ module.exports = (db, server) => {
|
|||
*
|
||||
* @apiSuccess {Boolean} success Indicates successful response
|
||||
* @apiSuccess {String} id ID of the Address
|
||||
* @apiSuccess {String} name Identity name
|
||||
* @apiSuccess {String} address E-mail address string
|
||||
* @apiSuccess {Boolean} main Indicates if this is the default address for the User
|
||||
* @apiSuccess {String} created Datestring of the time the address was created
|
||||
|
@ -689,6 +704,7 @@ module.exports = (db, server) => {
|
|||
},
|
||||
{
|
||||
fields: {
|
||||
name: true,
|
||||
address: true
|
||||
}
|
||||
},
|
||||
|
@ -733,6 +749,7 @@ module.exports = (db, server) => {
|
|||
res.json({
|
||||
success: true,
|
||||
id: addressData._id,
|
||||
name: addressData.name || false,
|
||||
address: addressData.address,
|
||||
main: addressData.address === userData.address,
|
||||
created: addressData.created
|
||||
|
@ -756,8 +773,11 @@ module.exports = (db, server) => {
|
|||
* }
|
||||
*
|
||||
* @apiParam {String} user ID of the User
|
||||
* @apiParam {String} address ID of the Address
|
||||
* @apiParam {String} id ID of the Address
|
||||
* @apiParam {String} [name] Identity name
|
||||
* @apiParam {String} [address] New address if you want to rename existing address. Only affects normal addresses, special addresses that include \* can not be changed
|
||||
* @apiParam {Boolean} main Indicates if this is the default address for the User
|
||||
|
||||
* @apiParam {String[]} [tags] A list of tags associated with this address
|
||||
*
|
||||
* @apiSuccess {Boolean} success Indicates successful response
|
||||
|
@ -797,6 +817,10 @@ module.exports = (db, server) => {
|
|||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.trim()
|
||||
.max(128),
|
||||
address: Joi.string().email(),
|
||||
main: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 'on', 1])
|
||||
|
@ -842,6 +866,10 @@ module.exports = (db, server) => {
|
|||
updates.addrview = addrview;
|
||||
}
|
||||
|
||||
if (result.value.name) {
|
||||
updates.name = result.value.name;
|
||||
}
|
||||
|
||||
if (result.value.tags) {
|
||||
let tagSeen = new Set();
|
||||
let tags = result.value.tags
|
||||
|
@ -1171,6 +1199,7 @@ module.exports = (db, server) => {
|
|||
* }
|
||||
*
|
||||
* @apiParam {String} address E-mail Address
|
||||
* @apiParam {String} [name] Identity name
|
||||
* @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") or an URL where mail contents are POSTed to
|
||||
* @apiParam {Number} [forwards] Daily allowed forwarding count for this address
|
||||
* @apiParam {Boolean} [allowWildcard=false] If <code>true</code> then address value can be in the form of <code>*@example.com</code>, otherwise using * is not allowed
|
||||
|
@ -1224,6 +1253,10 @@ module.exports = (db, server) => {
|
|||
.required(),
|
||||
Joi.string().regex(/^\w+@\*$/, 'special address')
|
||||
],
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.trim()
|
||||
.max(128),
|
||||
targets: Joi.array().items(
|
||||
Joi.string().email(),
|
||||
Joi.string().uri({
|
||||
|
@ -1288,6 +1321,7 @@ module.exports = (db, server) => {
|
|||
|
||||
let address = tools.normalizeAddress(result.value.address);
|
||||
let addrview = address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@'));
|
||||
let name = result.value.name;
|
||||
|
||||
let targets = result.value.targets || [];
|
||||
let forwards = result.value.forwards;
|
||||
|
@ -1462,6 +1496,7 @@ module.exports = (db, server) => {
|
|||
// insert alias address to email address registry
|
||||
|
||||
let addressData = {
|
||||
name,
|
||||
address,
|
||||
addrview: address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@')),
|
||||
targets,
|
||||
|
@ -1509,6 +1544,7 @@ module.exports = (db, server) => {
|
|||
*
|
||||
* @apiParam {String} id ID of the Address
|
||||
* @apiParam {String} [address] New address. Only affects normal addresses, special addresses that include \* can not be changed
|
||||
* @apiParam {String} [name] Identity name
|
||||
* @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") or an URL where mail contents are POSTed to. If set then overwrites previous targets array
|
||||
* @apiParam {Number} [forwards] Daily allowed forwarding count for this address
|
||||
* @apiParam {String[]} [tags] A list of tags associated with this address
|
||||
|
@ -1556,6 +1592,10 @@ module.exports = (db, server) => {
|
|||
.length(24)
|
||||
.required(),
|
||||
address: Joi.string().email(),
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.trim()
|
||||
.max(128),
|
||||
targets: Joi.array().items(
|
||||
Joi.string().email(),
|
||||
Joi.string().uri({
|
||||
|
@ -1626,6 +1666,10 @@ module.exports = (db, server) => {
|
|||
updates.forwards = result.value.forwards;
|
||||
}
|
||||
|
||||
if (result.value.name) {
|
||||
updates.name = result.value.name;
|
||||
}
|
||||
|
||||
if (result.value.autoreply) {
|
||||
if (!result.value.autoreply.name && 'name' in req.params.autoreply) {
|
||||
result.value.autoreply.name = '';
|
||||
|
@ -1947,6 +1991,7 @@ module.exports = (db, server) => {
|
|||
* @apiSuccess {Boolean} success Indicates successful response
|
||||
* @apiSuccess {String} id ID of the Address
|
||||
* @apiSuccess {String} address E-mail address string
|
||||
* @apiSuccess {String} name Identity name
|
||||
* @apiSuccess {String[]} targets List of forwarding targets
|
||||
* @apiSuccess {Object} limits Account limits and usage
|
||||
* @apiSuccess {Object} limits.forwards Forwarding quota
|
||||
|
@ -2057,6 +2102,7 @@ module.exports = (db, server) => {
|
|||
res.json({
|
||||
success: true,
|
||||
id: addressData._id,
|
||||
name: addressData.name || false,
|
||||
address: addressData.address,
|
||||
targets: addressData.targets && addressData.targets.map(t => t.value),
|
||||
limits: {
|
||||
|
@ -2092,6 +2138,7 @@ module.exports = (db, server) => {
|
|||
* @apiSuccess {Boolean} success Indicates successful response
|
||||
* @apiSuccess {String} id ID of the Address
|
||||
* @apiSuccess {String} address E-mail address string
|
||||
* @apiSuccess {String} name Identity name
|
||||
* @apiSuccess {String} user ID of the user if the address belongs to an User
|
||||
* @apiSuccess {String[]} targets List of forwarding targets if this is a Forwarded address
|
||||
* @apiSuccess {Object} limits Account limits and usage for Forwarded address
|
||||
|
@ -2238,6 +2285,7 @@ module.exports = (db, server) => {
|
|||
res.json({
|
||||
success: true,
|
||||
id: addressData._id,
|
||||
name: addressData.name || '',
|
||||
address: addressData.address,
|
||||
targets: addressData.targets && addressData.targets.map(t => t.value),
|
||||
limits: {
|
||||
|
|
|
@ -67,7 +67,7 @@ module.exports = (db, server, userHandler) => {
|
|||
.try(
|
||||
Joi.string()
|
||||
.lowercase()
|
||||
.regex(/^[a-z](?:\.?[a-z0-9]+)*$/, 'username')
|
||||
.regex(/^[a-z0-9](?:\.?[a-z0-9-]+)*$/, 'username')
|
||||
.min(3)
|
||||
.max(30),
|
||||
Joi.string().email()
|
||||
|
|
|
@ -348,9 +348,9 @@ module.exports = (db, server, userHandler) => {
|
|||
const schema = Joi.object().keys({
|
||||
username: Joi.string()
|
||||
.lowercase()
|
||||
.regex(/^[a-z](?:\.?[a-z0-9]+)*$/, 'username')
|
||||
.regex(/^[a-z0-9](?:\.?[a-z0-9-]+)*$/, 'username')
|
||||
.min(1)
|
||||
.max(32)
|
||||
.max(128)
|
||||
.required(),
|
||||
password: Joi.string()
|
||||
.allow(false)
|
||||
|
@ -570,7 +570,7 @@ module.exports = (db, server, userHandler) => {
|
|||
const schema = Joi.object().keys({
|
||||
username: Joi.string()
|
||||
.lowercase()
|
||||
.regex(/^[a-z](?:\.?[a-z0-9]+)*$/, 'username')
|
||||
.regex(/^[a-z0-9](?:\.?[a-z0-9-]+)*$/, 'username')
|
||||
.min(1)
|
||||
.max(32)
|
||||
.required()
|
||||
|
|
|
@ -193,35 +193,35 @@ class UserHandler {
|
|||
let checkAddress = next => {
|
||||
if (ObjectID.isValid(username)) {
|
||||
return next(null, {
|
||||
_id: username
|
||||
query: { _id: username }
|
||||
});
|
||||
}
|
||||
|
||||
if (username.indexOf('@') < 0) {
|
||||
// assume regular username
|
||||
return next(null, {
|
||||
unameview: username.replace(/\./g, '')
|
||||
query: { unameview: username.replace(/\./g, '') }
|
||||
});
|
||||
}
|
||||
|
||||
this.resolveAddress(username, false, (err, addressData) => {
|
||||
this.resolveAddress(username, { fields: { name: true } }, (err, addressData) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (addressData.user) {
|
||||
return next(null, { _id: addressData.user });
|
||||
return next(null, { query: { _id: addressData.user }, addressData });
|
||||
}
|
||||
return callback(null, false);
|
||||
});
|
||||
};
|
||||
|
||||
checkAddress((err, query) => {
|
||||
checkAddress((err, data) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
this.users.collection('users').findOne(
|
||||
query,
|
||||
data.query,
|
||||
{
|
||||
fields
|
||||
},
|
||||
|
@ -231,6 +231,11 @@ class UserHandler {
|
|||
return callback(err);
|
||||
}
|
||||
|
||||
if (userData && fields.name && data.addressData) {
|
||||
// override name
|
||||
userData.name = data.addressData.name || userData.name;
|
||||
}
|
||||
|
||||
return callback(null, userData);
|
||||
}
|
||||
);
|
||||
|
@ -715,228 +720,269 @@ class UserHandler {
|
|||
return callback(err);
|
||||
}
|
||||
|
||||
let junkRetention = consts.JUNK_RETENTION;
|
||||
let address = data.address ? data.address : data.username + '@' + (config.emailDomain || os.hostname()).toLowerCase();
|
||||
address = tools.normalizeAddress(address).replace(/\+[^@]*@/, '@');
|
||||
let addrview = address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@'));
|
||||
|
||||
// Insert user data
|
||||
|
||||
// Users with an empty password can not log in
|
||||
let hash = data.password ? bcrypt.hashSync(data.password, consts.BCRYPT_ROUNDS) : '';
|
||||
let id = new ObjectID();
|
||||
|
||||
userData = {
|
||||
_id: id,
|
||||
|
||||
username: data.username,
|
||||
// dotless version
|
||||
unameview: data.username.replace(/\./g, ''),
|
||||
|
||||
name: data.name,
|
||||
|
||||
// security
|
||||
password: '', // set this later. having no password prevents login
|
||||
|
||||
enabled2fa: [],
|
||||
seed: '', // 2fa seed value
|
||||
|
||||
// default email address
|
||||
address: '', // set this later
|
||||
|
||||
// quota
|
||||
storageUsed: 0,
|
||||
quota: data.quota || 0,
|
||||
recipients: data.recipients || 0,
|
||||
forwards: data.forwards || 0,
|
||||
|
||||
targets: [].concat(data.targets || []),
|
||||
|
||||
// autoreply status
|
||||
// off by default, can be changed later by user through the API
|
||||
autoreply: false,
|
||||
|
||||
pubKey: data.pubKey || '',
|
||||
encryptMessages: !!data.encryptMessages,
|
||||
encryptForwarded: !!data.encryptForwarded,
|
||||
|
||||
// default retention for user mailboxes
|
||||
retention: data.retention || 0,
|
||||
|
||||
created: new Date(),
|
||||
|
||||
requirePasswordChange: false,
|
||||
|
||||
// until setup value is not true, this account is not usable
|
||||
activated: false,
|
||||
disabled: true
|
||||
};
|
||||
|
||||
if (data.tags && data.tags.length) {
|
||||
userData.tags = data.tags;
|
||||
}
|
||||
|
||||
this.users.collection('users').insertOne(userData, err => {
|
||||
if (err) {
|
||||
log.error('DB', 'CREATEFAIL username=%s error=%s', data.username, err.message);
|
||||
|
||||
let response;
|
||||
switch (err.code) {
|
||||
case 11000:
|
||||
response = 'Selected user already exists';
|
||||
err.code = 'UserExistsError';
|
||||
break;
|
||||
default:
|
||||
response = 'Database Error, failed to create user';
|
||||
err.code = 'InternalDatabaseError';
|
||||
}
|
||||
|
||||
err.message = response;
|
||||
return callback(err);
|
||||
let checkAddress = done => {
|
||||
if (data.emptyAddress) {
|
||||
return done();
|
||||
}
|
||||
|
||||
let mailboxes = this.getMailboxes(data.language).map(mailbox => {
|
||||
mailbox.user = id;
|
||||
|
||||
if (['\\Trash', '\\Junk'].includes(mailbox.specialUse)) {
|
||||
mailbox.retention = data.retention ? Math.min(data.retention, junkRetention) : junkRetention;
|
||||
} else {
|
||||
mailbox.retention = data.retention;
|
||||
}
|
||||
|
||||
return mailbox;
|
||||
});
|
||||
|
||||
this.database.collection('mailboxes').insertMany(
|
||||
mailboxes,
|
||||
this.users.collection('addresses').findOne(
|
||||
{
|
||||
w: 1,
|
||||
ordered: false
|
||||
addrview
|
||||
},
|
||||
err => {
|
||||
{
|
||||
fields: {
|
||||
_id: true
|
||||
}
|
||||
},
|
||||
(err, addressData) => {
|
||||
if (err) {
|
||||
// try to rollback
|
||||
this.users.collection('users').deleteOne({ _id: id }, () => false);
|
||||
|
||||
log.error('DB', 'CREATEFAIL username=%s error=%s', data.username, err.message);
|
||||
log.error('DB', 'CREATEFAIL username=%s address=%s error=%s', data.username, address, err.message);
|
||||
err.message = 'Database Error, failed to create user';
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let ensureAddress = done => {
|
||||
if (data.emptyAddress) {
|
||||
return done(null, '');
|
||||
}
|
||||
let address = data.address ? data.address : data.username + '@' + (config.emailDomain || os.hostname()).toLowerCase();
|
||||
if (addressData) {
|
||||
let err = new Error('This address already exists');
|
||||
err.code = 'AddressExistsError';
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let addressData = {
|
||||
user: id,
|
||||
address,
|
||||
// dotless version
|
||||
addrview: address.substr(0, address.indexOf('@')).replace(/\./g, '') + address.substr(address.indexOf('@')),
|
||||
created: new Date()
|
||||
};
|
||||
done();
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (data.tags && data.tags.length && data.addTagsToAddress) {
|
||||
addressData.tags = data.tags;
|
||||
}
|
||||
checkAddress(() => {
|
||||
let junkRetention = consts.JUNK_RETENTION;
|
||||
|
||||
// insert alias address to email address registry
|
||||
this.users.collection('addresses').insertOne(addressData, err => {
|
||||
if (err) {
|
||||
// try to rollback
|
||||
this.users.collection('users').deleteOne({ _id: id }, () => false);
|
||||
this.database.collection('mailboxes').deleteMany({ user: id }, () => false);
|
||||
// Insert user data
|
||||
|
||||
log.error('DB', 'CREATEFAIL username=%s error=%s', data.username, err.message);
|
||||
// Users with an empty password can not log in
|
||||
let hash = data.password ? bcrypt.hashSync(data.password, consts.BCRYPT_ROUNDS) : '';
|
||||
let id = new ObjectID();
|
||||
|
||||
let response;
|
||||
switch (err.code) {
|
||||
case 11000:
|
||||
response = 'Selected email address already exists';
|
||||
break;
|
||||
default:
|
||||
response = 'Database Error, failed to create user';
|
||||
}
|
||||
userData = {
|
||||
_id: id,
|
||||
|
||||
err.message = response;
|
||||
return done(err);
|
||||
}
|
||||
username: data.username,
|
||||
// dotless version
|
||||
unameview: data.username.replace(/\./g, ''),
|
||||
|
||||
done(null, address);
|
||||
});
|
||||
};
|
||||
name: data.name,
|
||||
|
||||
ensureAddress((err, address) => {
|
||||
// security
|
||||
password: '', // set this later. having no password prevents login
|
||||
|
||||
enabled2fa: [],
|
||||
seed: '', // 2fa seed value
|
||||
|
||||
// default email address
|
||||
address: '', // set this later
|
||||
|
||||
// quota
|
||||
storageUsed: 0,
|
||||
quota: data.quota || 0,
|
||||
recipients: data.recipients || 0,
|
||||
forwards: data.forwards || 0,
|
||||
|
||||
targets: [].concat(data.targets || []),
|
||||
|
||||
// autoreply status
|
||||
// off by default, can be changed later by user through the API
|
||||
autoreply: false,
|
||||
|
||||
pubKey: data.pubKey || '',
|
||||
encryptMessages: !!data.encryptMessages,
|
||||
encryptForwarded: !!data.encryptForwarded,
|
||||
|
||||
// default retention for user mailboxes
|
||||
retention: data.retention || 0,
|
||||
|
||||
created: new Date(),
|
||||
|
||||
requirePasswordChange: false,
|
||||
|
||||
// until setup value is not true, this account is not usable
|
||||
activated: false,
|
||||
disabled: true
|
||||
};
|
||||
|
||||
if (data.tags && data.tags.length) {
|
||||
userData.tags = data.tags;
|
||||
}
|
||||
|
||||
this.users.collection('users').insertOne(userData, err => {
|
||||
if (err) {
|
||||
log.error('DB', 'CREATEFAIL username=%s error=%s', data.username, err.message);
|
||||
|
||||
let response;
|
||||
switch (err.code) {
|
||||
case 11000:
|
||||
response = 'Selected user already exists';
|
||||
err.code = 'UserExistsError';
|
||||
break;
|
||||
default:
|
||||
response = 'Database Error, failed to create user';
|
||||
err.code = 'InternalDatabaseError';
|
||||
}
|
||||
|
||||
err.message = response;
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let mailboxes = this.getMailboxes(data.language).map(mailbox => {
|
||||
mailbox.user = id;
|
||||
|
||||
if (['\\Trash', '\\Junk'].includes(mailbox.specialUse)) {
|
||||
mailbox.retention = data.retention ? Math.min(data.retention, junkRetention) : junkRetention;
|
||||
} else {
|
||||
mailbox.retention = data.retention;
|
||||
}
|
||||
|
||||
return mailbox;
|
||||
});
|
||||
|
||||
this.database.collection('mailboxes').insertMany(
|
||||
mailboxes,
|
||||
{
|
||||
w: 1,
|
||||
ordered: false
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
// try to rollback
|
||||
this.users.collection('users').deleteOne({ _id: id }, () => false);
|
||||
|
||||
log.error('DB', 'CREATEFAIL username=%s error=%s', data.username, err.message);
|
||||
err.message = 'Database Error, failed to create user';
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// register this address as the default address for that user
|
||||
return this.users.collection('users').findOneAndUpdate(
|
||||
{
|
||||
_id: id,
|
||||
activated: false
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
password: hash,
|
||||
address,
|
||||
activated: true,
|
||||
disabled: false
|
||||
}
|
||||
},
|
||||
{ returnOriginal: false },
|
||||
(err, result) => {
|
||||
let ensureAddress = done => {
|
||||
if (data.emptyAddress) {
|
||||
return done(null, '');
|
||||
}
|
||||
|
||||
let addressData = {
|
||||
user: id,
|
||||
address,
|
||||
// dotless version
|
||||
addrview,
|
||||
created: new Date()
|
||||
};
|
||||
|
||||
if (data.tags && data.tags.length && data.addTagsToAddress) {
|
||||
addressData.tags = data.tags;
|
||||
}
|
||||
|
||||
// insert alias address to email address registry
|
||||
this.users.collection('addresses').insertOne(addressData, err => {
|
||||
if (err) {
|
||||
// try to rollback
|
||||
this.users.collection('users').deleteOne({ _id: id }, () => false);
|
||||
this.database.collection('mailboxes').deleteMany({ user: id }, () => false);
|
||||
|
||||
log.error('DB', 'CREATEFAIL username=%s error=%s', data.username, err.message);
|
||||
err.message = 'Database Error, failed to create user';
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
|
||||
let response;
|
||||
switch (err.code) {
|
||||
case 11000:
|
||||
response = 'Selected email address already exists';
|
||||
err.code = 'AddressExistsError';
|
||||
break;
|
||||
default:
|
||||
response = 'Database Error, failed to create user';
|
||||
err.code = 'InternalDatabaseError';
|
||||
}
|
||||
|
||||
err.message = response;
|
||||
return done(err);
|
||||
}
|
||||
|
||||
let userData = result.value;
|
||||
done(null, address);
|
||||
});
|
||||
};
|
||||
|
||||
if (!userData) {
|
||||
// should never happen
|
||||
return callback(null, id);
|
||||
}
|
||||
|
||||
let createSuccess = () =>
|
||||
this.logAuthEvent(
|
||||
id,
|
||||
{
|
||||
action: 'account created',
|
||||
result: 'success',
|
||||
sess: data.sess,
|
||||
ip: data.ip
|
||||
},
|
||||
() => callback(null, id)
|
||||
);
|
||||
|
||||
if (!this.messageHandler || data.emptyAddress) {
|
||||
return createSuccess();
|
||||
}
|
||||
|
||||
let parsedName = humanname.parse(userData.name || '');
|
||||
this.pushDefaultMessages(
|
||||
userData,
|
||||
{
|
||||
NAME: userData.name || userData.username || address,
|
||||
FNAME: parsedName.firstName,
|
||||
LNAME: parsedName.lastName,
|
||||
DOMAIN: address.substr(address.indexOf('@') + 1),
|
||||
EMAIL: address
|
||||
},
|
||||
() => createSuccess()
|
||||
);
|
||||
ensureAddress((err, address) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
// register this address as the default address for that user
|
||||
return this.users.collection('users').findOneAndUpdate(
|
||||
{
|
||||
_id: id,
|
||||
activated: false
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
password: hash,
|
||||
address,
|
||||
activated: true,
|
||||
disabled: false
|
||||
}
|
||||
},
|
||||
{ returnOriginal: false },
|
||||
(err, result) => {
|
||||
if (err) {
|
||||
// try to rollback
|
||||
this.users.collection('users').deleteOne({ _id: id }, () => false);
|
||||
this.database.collection('mailboxes').deleteMany({ user: id }, () => false);
|
||||
this.users.collection('addresses').deleteOne({ user: id }, () => false);
|
||||
|
||||
log.error('DB', 'CREATEFAIL username=%s error=%s', data.username, err.message);
|
||||
err.message = 'Database Error, failed to create user';
|
||||
err.code = 'InternalDatabaseError';
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
let userData = result.value;
|
||||
|
||||
if (!userData) {
|
||||
// should never happen
|
||||
return callback(null, id);
|
||||
}
|
||||
|
||||
let createSuccess = () =>
|
||||
this.logAuthEvent(
|
||||
id,
|
||||
{
|
||||
action: 'account created',
|
||||
result: 'success',
|
||||
sess: data.sess,
|
||||
ip: data.ip
|
||||
},
|
||||
() => callback(null, id)
|
||||
);
|
||||
|
||||
if (!this.messageHandler || data.emptyAddress) {
|
||||
return createSuccess();
|
||||
}
|
||||
|
||||
let parsedName = humanname.parse(userData.name || '');
|
||||
this.pushDefaultMessages(
|
||||
userData,
|
||||
{
|
||||
NAME: userData.name || userData.username || address,
|
||||
FNAME: parsedName.firstName,
|
||||
LNAME: parsedName.lastName,
|
||||
DOMAIN: address.substr(address.indexOf('@') + 1),
|
||||
EMAIL: address
|
||||
},
|
||||
() => createSuccess()
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
|
Loading…
Reference in a new issue