mirror of
https://github.com/nodemailer/wildduck.git
synced 2024-12-29 11:46:30 +08:00
v1.0.77
This commit is contained in:
parent
01166ed33d
commit
4cfd90f473
6 changed files with 504 additions and 151 deletions
|
@ -10,10 +10,20 @@ module.exports = (db, server) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
query: Joi.string().trim().empty('').max(255),
|
||||
limit: Joi.number().default(20).min(1).max(250),
|
||||
next: Joi.string().alphanum().max(100),
|
||||
prev: Joi.string().alphanum().max(100),
|
||||
query: Joi.string()
|
||||
.trim()
|
||||
.empty('')
|
||||
.max(255),
|
||||
limit: Joi.number()
|
||||
.default(20)
|
||||
.min(1)
|
||||
.max(250),
|
||||
next: Joi.string()
|
||||
.alphanum()
|
||||
.max(100),
|
||||
prev: Joi.string()
|
||||
.alphanum()
|
||||
.max(100),
|
||||
page: Joi.number().default(1)
|
||||
});
|
||||
|
||||
|
@ -83,18 +93,13 @@ module.exports = (db, server) => {
|
|||
page = 1;
|
||||
}
|
||||
|
||||
let prevUrl = result.hasPrevious
|
||||
? server.router.render('addresses', {}, { prev: result.previous, limit, query: query || '', page: Math.max(page - 1, 1) })
|
||||
: false;
|
||||
let nextUrl = result.hasNext ? server.router.render('addresses', {}, { next: result.next, limit, query: query || '', page: page + 1 }) : false;
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
prev: prevUrl,
|
||||
next: nextUrl,
|
||||
previousCursor: result.hasPrevious ? result.previous : false,
|
||||
nextCursor: result.hasNext ? result.next : false,
|
||||
results: (result.results || []).map(addressData => ({
|
||||
id: addressData._id.toString(),
|
||||
address: addressData.address,
|
||||
|
@ -112,8 +117,14 @@ module.exports = (db, server) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
address: Joi.string().email().required(),
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
address: Joi.string()
|
||||
.email()
|
||||
.required(),
|
||||
main: Joi.boolean().truthy(['Y', 'true', 'yes', 1])
|
||||
});
|
||||
|
||||
|
@ -233,7 +244,11 @@ module.exports = (db, server) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -310,8 +325,16 @@ module.exports = (db, server) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
address: Joi.string().hex().lowercase().length(24).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
address: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -384,9 +407,19 @@ module.exports = (db, server) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
address: Joi.string().hex().lowercase().length(24).required(),
|
||||
main: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
address: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
main: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 1])
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -487,8 +520,16 @@ module.exports = (db, server) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
address: Joi.string().hex().lowercase().length(24).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
address: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
|
|
@ -9,8 +9,19 @@ module.exports = (db, server, userHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
username: Joi.string().lowercase().regex(/^[a-z](?:\.?[a-z0-9]+)*$/, 'username').min(3).max(30).required(),
|
||||
password: Joi.string().max(256).required(),
|
||||
username: Joi.alternatives()
|
||||
.try(
|
||||
Joi.string()
|
||||
.lowercase()
|
||||
.regex(/^[a-z](?:\.?[a-z0-9]+)*$/, 'username')
|
||||
.min(3)
|
||||
.max(30),
|
||||
Joi.string().email()
|
||||
)
|
||||
.required(),
|
||||
password: Joi.string()
|
||||
.max(256)
|
||||
.required(),
|
||||
|
||||
protocol: Joi.string().default('API'),
|
||||
scope: Joi.string().default('master'),
|
||||
|
@ -70,12 +81,31 @@ module.exports = (db, server, userHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
action: Joi.string().trim().lowercase().empty('').max(100),
|
||||
limit: Joi.number().default(20).min(1).max(250),
|
||||
next: Joi.string().empty('').alphanum().max(100),
|
||||
previous: Joi.string().empty('').alphanum().max(100),
|
||||
page: Joi.number().empty('').default(1)
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
action: Joi.string()
|
||||
.trim()
|
||||
.lowercase()
|
||||
.empty('')
|
||||
.max(100),
|
||||
limit: Joi.number()
|
||||
.default(20)
|
||||
.min(1)
|
||||
.max(250),
|
||||
next: Joi.string()
|
||||
.empty('')
|
||||
.alphanum()
|
||||
.max(100),
|
||||
previous: Joi.string()
|
||||
.empty('')
|
||||
.alphanum()
|
||||
.max(100),
|
||||
page: Joi.number()
|
||||
.empty('')
|
||||
.default(1)
|
||||
});
|
||||
|
||||
req.query.user = req.params.user;
|
||||
|
@ -161,31 +191,17 @@ module.exports = (db, server, userHandler) => {
|
|||
page = 1;
|
||||
}
|
||||
|
||||
let prevUrl = result.hasPrevious
|
||||
? server.router.render(
|
||||
'authlog',
|
||||
{ user: user.toString() },
|
||||
{ prev: result.previous, action: action || '', limit, page: Math.max(page - 1, 1) }
|
||||
)
|
||||
: false;
|
||||
let nextUrl = result.hasNext
|
||||
? server.router.render('authlog', { user: user.toString() }, { next: result.next, action: action || '', limit, page: page + 1 })
|
||||
: false;
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
action,
|
||||
total,
|
||||
page,
|
||||
prev: prevUrl,
|
||||
previousCursor: result.hasPrevious ? result.previous : false,
|
||||
next: nextUrl,
|
||||
nextCursor: result.hasNext ? result.next : false,
|
||||
results: (result.results || []).map(resultData => {
|
||||
let response = {
|
||||
id: resultData._id
|
||||
};
|
||||
|
||||
Object.keys(resultData).forEach(key => {
|
||||
if (!['_id', 'user'].includes(key)) {
|
||||
response[key] = resultData[key];
|
||||
|
|
|
@ -13,13 +13,36 @@ module.exports = (db, server, messageHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
mailbox: Joi.string().hex().lowercase().length(24).required(),
|
||||
limit: Joi.number().empty('').default(20).min(1).max(250),
|
||||
order: Joi.any().empty('').allow(['asc', 'desc']).default('desc'),
|
||||
next: Joi.string().empty('').alphanum().max(100),
|
||||
previous: Joi.string().empty('').alphanum().max(100),
|
||||
page: Joi.number().empty('').default(1)
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
mailbox: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
limit: Joi.number()
|
||||
.empty('')
|
||||
.default(20)
|
||||
.min(1)
|
||||
.max(250),
|
||||
order: Joi.any()
|
||||
.empty('')
|
||||
.allow(['asc', 'desc'])
|
||||
.default('desc'),
|
||||
next: Joi.string()
|
||||
.empty('')
|
||||
.alphanum()
|
||||
.max(100),
|
||||
previous: Joi.string()
|
||||
.empty('')
|
||||
.alphanum()
|
||||
.max(100),
|
||||
page: Joi.number()
|
||||
.empty('')
|
||||
.default(1)
|
||||
});
|
||||
|
||||
req.query.user = req.params.user;
|
||||
|
@ -128,28 +151,11 @@ module.exports = (db, server, messageHandler) => {
|
|||
page = 1;
|
||||
}
|
||||
|
||||
let prevUrl = result.hasPrevious
|
||||
? server.router.render(
|
||||
'messages',
|
||||
{ user: user.toString(), mailbox: mailbox.toString() },
|
||||
{ prev: result.previous, limit, order: sortAscending ? 'asc' : 'desc', page: Math.max(page - 1, 1) }
|
||||
)
|
||||
: false;
|
||||
let nextUrl = result.hasNext
|
||||
? server.router.render(
|
||||
'messages',
|
||||
{ user: user.toString(), mailbox: mailbox.toString() },
|
||||
{ next: result.next, limit, order: sortAscending ? 'asc' : 'desc', page: page + 1 }
|
||||
)
|
||||
: false;
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
total,
|
||||
page,
|
||||
prev: prevUrl,
|
||||
previousCursor: result.hasPrevious ? result.previous : false,
|
||||
next: nextUrl,
|
||||
nextCursor: result.hasNext ? result.next : false,
|
||||
specialUse: mailboxData.specialUse,
|
||||
results: (result.results || []).map(messageData => {
|
||||
|
@ -207,11 +213,47 @@ module.exports = (db, server, messageHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
query: Joi.string().trim().max(255).required(),
|
||||
limit: Joi.number().default(20).min(1).max(250),
|
||||
next: Joi.string().alphanum().max(100),
|
||||
prev: Joi.string().alphanum().max(100),
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
mailbox: Joi.string()
|
||||
.hex()
|
||||
.length(24)
|
||||
.empty(''),
|
||||
query: Joi.string()
|
||||
.trim()
|
||||
.max(255)
|
||||
.empty(''),
|
||||
datestart: Joi.number()
|
||||
.label('Start time')
|
||||
.empty(''),
|
||||
dateend: Joi.number()
|
||||
.label('End time')
|
||||
.empty(''),
|
||||
filterFrom: Joi.string()
|
||||
.trim()
|
||||
.empty(''),
|
||||
filterTo: Joi.string()
|
||||
.trim()
|
||||
.empty(''),
|
||||
filterSubject: Joi.string()
|
||||
.trim()
|
||||
.empty(''),
|
||||
filterAttachments: Joi.boolean()
|
||||
.empty('')
|
||||
.truthy('true'),
|
||||
limit: Joi.number()
|
||||
.default(20)
|
||||
.min(1)
|
||||
.max(250),
|
||||
next: Joi.string()
|
||||
.alphanum()
|
||||
.max(100),
|
||||
previous: Joi.string()
|
||||
.alphanum()
|
||||
.max(100),
|
||||
page: Joi.number().default(1)
|
||||
});
|
||||
|
||||
|
@ -231,11 +273,19 @@ module.exports = (db, server, messageHandler) => {
|
|||
}
|
||||
|
||||
let user = new ObjectID(result.value.user);
|
||||
let mailbox = result.value.mailbox ? new ObjectID(result.value.mailbox) : false;
|
||||
let query = result.value.query;
|
||||
let datestart = result.value.datestart ? new Date(result.value.datestart) : false;
|
||||
let dateend = result.value.dateend ? new Date(result.value.dateend) : false;
|
||||
let filterFrom = result.value.filterFrom;
|
||||
let filterTo = result.value.filterTo;
|
||||
let filterSubject = result.value.filterSubject;
|
||||
let filterAttachments = result.value.filterAttachments;
|
||||
|
||||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrev = result.value.prev;
|
||||
let pagePrev = result.value.previous;
|
||||
|
||||
db.users.collection('users').findOne({
|
||||
_id: user
|
||||
|
@ -262,10 +312,104 @@ module.exports = (db, server, messageHandler) => {
|
|||
// NB! Scattered query, searches over all user mailboxes and all shards
|
||||
let filter = {
|
||||
user,
|
||||
searchable: true,
|
||||
$text: { $search: query, $language: 'none' }
|
||||
searchable: true
|
||||
};
|
||||
|
||||
if (query) {
|
||||
filter.$text = { $search: query, $language: 'none' };
|
||||
}
|
||||
|
||||
if (mailbox) {
|
||||
filter.mailbox = mailbox;
|
||||
}
|
||||
|
||||
if (datestart) {
|
||||
if (!filter.idate) {
|
||||
filter.idate = {};
|
||||
}
|
||||
filter.idate.$gte = datestart;
|
||||
}
|
||||
|
||||
if (dateend) {
|
||||
if (!filter.idate) {
|
||||
filter.idate = {};
|
||||
}
|
||||
filter.idate.$lte = dateend;
|
||||
}
|
||||
|
||||
if (filterFrom) {
|
||||
let regex = filterFrom.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
if (!filter.$and) {
|
||||
filter.$and = [];
|
||||
}
|
||||
filter.$and.push({
|
||||
headers: {
|
||||
$elemMatch: {
|
||||
key: 'from',
|
||||
value: {
|
||||
$regex: regex,
|
||||
$options: 'i'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (filterTo) {
|
||||
let regex = filterTo.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
if (!filter.$and) {
|
||||
filter.$and = [];
|
||||
}
|
||||
filter.$and.push({
|
||||
$or: [
|
||||
{
|
||||
headers: {
|
||||
$elemMatch: {
|
||||
key: 'to',
|
||||
value: {
|
||||
$regex: regex,
|
||||
$options: 'i'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
$elemMatch: {
|
||||
key: 'cc',
|
||||
value: {
|
||||
$regex: regex,
|
||||
$options: 'i'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
if (filterSubject) {
|
||||
let regex = filterSubject.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&');
|
||||
if (!filter.$and) {
|
||||
filter.$and = [];
|
||||
}
|
||||
filter.$and.push({
|
||||
headers: {
|
||||
$elemMatch: {
|
||||
key: 'subject',
|
||||
value: {
|
||||
$regex: regex,
|
||||
$options: 'i'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (filterAttachments) {
|
||||
filter.ha = true;
|
||||
}
|
||||
|
||||
getFilteredMessageCount(db, filter, (err, total) => {
|
||||
if (err) {
|
||||
res.json({
|
||||
|
@ -302,7 +446,7 @@ module.exports = (db, server, messageHandler) => {
|
|||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if (pagePrev) {
|
||||
opts.prev = pagePrev;
|
||||
opts.previous = pagePrev;
|
||||
}
|
||||
|
||||
MongoPaging.find(db.database.collection('messages'), opts, (err, result) => {
|
||||
|
@ -317,20 +461,13 @@ module.exports = (db, server, messageHandler) => {
|
|||
page = 1;
|
||||
}
|
||||
|
||||
let prevUrl = result.hasPrevious
|
||||
? server.router.render('search', { user: user.toString() }, { prev: result.previous, limit, query, page: Math.max(page - 1, 1) })
|
||||
: false;
|
||||
let nextUrl = result.hasNext
|
||||
? server.router.render('search', { user: user.toString() }, { next: result.next, limit, query, page: page + 1 })
|
||||
: false;
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
prev: prevUrl,
|
||||
next: nextUrl,
|
||||
previousCursor: result.hasPrevious ? result.previous : false,
|
||||
nextCursor: result.hasNext ? result.next : false,
|
||||
results: (result.results || []).map(messageData => {
|
||||
let parsedHeader = (messageData.mimeTree && messageData.mimeTree.parsedHeader) || {};
|
||||
let from = parsedHeader.from ||
|
||||
|
@ -387,11 +524,25 @@ module.exports = (db, server, messageHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
mailbox: Joi.string().hex().lowercase().length(24).required(),
|
||||
message: Joi.number().min(1).required(),
|
||||
replaceCidLinks: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).default(false),
|
||||
markAsSeen: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).default(false)
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
mailbox: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
message: Joi.number()
|
||||
.min(1)
|
||||
.required(),
|
||||
replaceCidLinks: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 1])
|
||||
.default(false),
|
||||
markAsSeen: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 1])
|
||||
.default(false)
|
||||
});
|
||||
|
||||
if (req.query.replaceCidLinks) {
|
||||
|
@ -582,9 +733,19 @@ module.exports = (db, server, messageHandler) => {
|
|||
|
||||
server.get({ name: 'raw', path: '/users/:user/mailboxes/:mailbox/messages/:message/message.eml' }, (req, res, next) => {
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
mailbox: Joi.string().hex().lowercase().length(24).required(),
|
||||
message: Joi.number().min(1).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
mailbox: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
message: Joi.number()
|
||||
.min(1)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -641,10 +802,23 @@ module.exports = (db, server, messageHandler) => {
|
|||
|
||||
server.get({ name: 'attachment', path: '/users/:user/mailboxes/:mailbox/messages/:message/attachments/:attachment' }, (req, res, next) => {
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
mailbox: Joi.string().hex().lowercase().length(24).required(),
|
||||
message: Joi.number().min(1).required(),
|
||||
attachment: Joi.string().regex(/^ATT\d+$/i).uppercase().required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
mailbox: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
message: Joi.number()
|
||||
.min(1)
|
||||
.required(),
|
||||
attachment: Joi.string()
|
||||
.regex(/^ATT\d+$/i)
|
||||
.uppercase()
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -728,15 +902,33 @@ module.exports = (db, server, messageHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
mailbox: Joi.string().hex().lowercase().length(24).required(),
|
||||
moveTo: Joi.string().hex().lowercase().length(24),
|
||||
message: Joi.string().regex(/^\d+(,\d+)*$|^\d+:\d+$|/i).required(),
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
mailbox: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
moveTo: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24),
|
||||
message: Joi.string()
|
||||
.regex(/^\d+(,\d+)*$|^\d+:\d+$|/i)
|
||||
.required(),
|
||||
seen: Joi.boolean().truthy(['Y', 'true', 'yes', 1]),
|
||||
deleted: Joi.boolean().truthy(['Y', 'true', 'yes', 1]),
|
||||
flagged: Joi.boolean().truthy(['Y', 'true', 'yes', 1]),
|
||||
draft: Joi.boolean().truthy(['Y', 'true', 'yes', 1]),
|
||||
expires: Joi.alternatives().try(Joi.date(), Joi.boolean().truthy(['Y', 'true', 'yes', 1]).allow(false))
|
||||
expires: Joi.alternatives().try(
|
||||
Joi.date(),
|
||||
Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 1])
|
||||
.allow(false)
|
||||
)
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -761,9 +953,17 @@ module.exports = (db, server, messageHandler) => {
|
|||
if (/^\d+$/.test(message)) {
|
||||
messageQuery = Number(message);
|
||||
} else if (/^\d+(,\d+)*$/.test(message)) {
|
||||
messageQuery = { $in: message.split(',').map(uid => Number(uid)).sort((a, b) => a - b) };
|
||||
messageQuery = {
|
||||
$in: message
|
||||
.split(',')
|
||||
.map(uid => Number(uid))
|
||||
.sort((a, b) => a - b)
|
||||
};
|
||||
} else if (/^\d+:\d+$/.test(message)) {
|
||||
let parts = message.split(':').map(uid => Number(uid)).sort((a, b) => a - b);
|
||||
let parts = message
|
||||
.split(':')
|
||||
.map(uid => Number(uid))
|
||||
.sort((a, b) => a - b);
|
||||
if (parts[0] === parts[1]) {
|
||||
messageQuery = parts[0];
|
||||
} else {
|
||||
|
@ -840,9 +1040,19 @@ module.exports = (db, server, messageHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
mailbox: Joi.string().hex().lowercase().length(24).required(),
|
||||
message: Joi.number().min(1).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
mailbox: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
message: Joi.number()
|
||||
.min(1)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
|
132
lib/api/users.js
132
lib/api/users.js
|
@ -14,10 +14,21 @@ module.exports = (db, server, userHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
query: Joi.string().alphanum().lowercase().empty('').max(100),
|
||||
limit: Joi.number().default(20).min(1).max(250),
|
||||
next: Joi.string().alphanum().max(100),
|
||||
prev: Joi.string().alphanum().max(100),
|
||||
query: Joi.string()
|
||||
.alphanum()
|
||||
.lowercase()
|
||||
.empty('')
|
||||
.max(100),
|
||||
limit: Joi.number()
|
||||
.default(20)
|
||||
.min(1)
|
||||
.max(250),
|
||||
next: Joi.string()
|
||||
.alphanum()
|
||||
.max(100),
|
||||
previous: Joi.string()
|
||||
.alphanum()
|
||||
.max(100),
|
||||
page: Joi.number().default(1)
|
||||
});
|
||||
|
||||
|
@ -38,7 +49,7 @@ module.exports = (db, server, userHandler) => {
|
|||
let limit = result.value.limit;
|
||||
let page = result.value.page;
|
||||
let pageNext = result.value.next;
|
||||
let pagePrev = result.value.prev;
|
||||
let pagePrev = result.value.previous;
|
||||
|
||||
let filter = query
|
||||
? {
|
||||
|
@ -75,7 +86,7 @@ module.exports = (db, server, userHandler) => {
|
|||
if (pageNext) {
|
||||
opts.next = pageNext;
|
||||
} else if (pagePrev) {
|
||||
opts.prev = pagePrev;
|
||||
opts.previous = pagePrev;
|
||||
}
|
||||
|
||||
MongoPaging.find(db.users.collection('users'), opts, (err, result) => {
|
||||
|
@ -90,18 +101,13 @@ module.exports = (db, server, userHandler) => {
|
|||
page = 1;
|
||||
}
|
||||
|
||||
let prevUrl = result.hasPrevious
|
||||
? server.router.render('users', {}, { prev: result.previous, limit, query: query || '', page: Math.max(page - 1, 1) })
|
||||
: false;
|
||||
let nextUrl = result.hasNext ? server.router.render('users', {}, { next: result.next, limit, query: query || '', page: page + 1 }) : false;
|
||||
|
||||
let response = {
|
||||
success: true,
|
||||
query,
|
||||
total,
|
||||
page,
|
||||
prev: prevUrl,
|
||||
next: nextUrl,
|
||||
previousCursor: result.hasPrevious ? result.previous : false,
|
||||
nextCursor: result.hasNext ? result.next : false,
|
||||
results: (result.results || []).map(userData => ({
|
||||
id: userData._id.toString(),
|
||||
username: userData.username,
|
||||
|
@ -125,24 +131,47 @@ module.exports = (db, server, userHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
username: Joi.string().lowercase().regex(/^[a-z](?:\.?[a-z0-9]+)*$/, 'username').min(3).max(30).required(),
|
||||
password: Joi.string().max(256).required(),
|
||||
username: Joi.string()
|
||||
.lowercase()
|
||||
.regex(/^[a-z](?:\.?[a-z0-9]+)*$/, 'username')
|
||||
.min(3)
|
||||
.max(30)
|
||||
.required(),
|
||||
password: Joi.string()
|
||||
.max(256)
|
||||
.required(),
|
||||
|
||||
address: Joi.string().email(),
|
||||
|
||||
language: Joi.string().min(2).max(20).lowercase(),
|
||||
retention: Joi.number().min(0).default(0),
|
||||
language: Joi.string()
|
||||
.min(2)
|
||||
.max(20)
|
||||
.lowercase(),
|
||||
retention: Joi.number()
|
||||
.min(0)
|
||||
.default(0),
|
||||
|
||||
name: Joi.string().max(256),
|
||||
forward: Joi.string().email(),
|
||||
targetUrl: Joi.string().max(256),
|
||||
|
||||
quota: Joi.number().min(0).default(0),
|
||||
recipients: Joi.number().min(0).default(0),
|
||||
forwards: Joi.number().min(0).default(0),
|
||||
quota: Joi.number()
|
||||
.min(0)
|
||||
.default(0),
|
||||
recipients: Joi.number()
|
||||
.min(0)
|
||||
.default(0),
|
||||
forwards: Joi.number()
|
||||
.min(0)
|
||||
.default(0),
|
||||
|
||||
pubKey: Joi.string().empty('').trim().regex(/^-----BEGIN PGP PUBLIC KEY BLOCK-----/, 'PGP key format'),
|
||||
encryptMessages: Joi.boolean().truthy(['Y', 'true', 'yes', 1]).default(false),
|
||||
pubKey: Joi.string()
|
||||
.empty('')
|
||||
.trim()
|
||||
.regex(/^-----BEGIN PGP PUBLIC KEY BLOCK-----/, 'PGP key format'),
|
||||
encryptMessages: Joi.boolean()
|
||||
.truthy(['Y', 'true', 'yes', 1])
|
||||
.default(false),
|
||||
|
||||
ip: Joi.string().ip({
|
||||
version: ['ipv4', 'ipv6'],
|
||||
|
@ -208,7 +237,11 @@ module.exports = (db, server, userHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -312,19 +345,42 @@ module.exports = (db, server, userHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required(),
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required(),
|
||||
|
||||
existingPassword: Joi.string().empty('').min(1).max(256),
|
||||
password: Joi.string().min(8).max(256),
|
||||
existingPassword: Joi.string()
|
||||
.empty('')
|
||||
.min(1)
|
||||
.max(256),
|
||||
password: Joi.string()
|
||||
.min(8)
|
||||
.max(256),
|
||||
|
||||
language: Joi.string().min(2).max(20).lowercase(),
|
||||
language: Joi.string()
|
||||
.min(2)
|
||||
.max(20)
|
||||
.lowercase(),
|
||||
|
||||
name: Joi.string().empty('').max(256),
|
||||
forward: Joi.string().empty('').email(),
|
||||
targetUrl: Joi.string().empty('').max(256),
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.max(256),
|
||||
forward: Joi.string()
|
||||
.empty('')
|
||||
.email(),
|
||||
targetUrl: Joi.string()
|
||||
.empty('')
|
||||
.max(256),
|
||||
|
||||
pubKey: Joi.string().empty('').trim().regex(/^-----BEGIN PGP PUBLIC KEY BLOCK-----/, 'PGP key format'),
|
||||
encryptMessages: Joi.boolean().empty('').truthy(['Y', 'true', 'yes', 1]),
|
||||
pubKey: Joi.string()
|
||||
.empty('')
|
||||
.trim()
|
||||
.regex(/^-----BEGIN PGP PUBLIC KEY BLOCK-----/, 'PGP key format'),
|
||||
encryptMessages: Joi.boolean()
|
||||
.empty('')
|
||||
.truthy(['Y', 'true', 'yes', 1]),
|
||||
|
||||
retention: Joi.number().min(0),
|
||||
quota: Joi.number().min(0),
|
||||
|
@ -402,7 +458,11 @@ module.exports = (db, server, userHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
@ -516,7 +576,11 @@ module.exports = (db, server, userHandler) => {
|
|||
res.charSet('utf-8');
|
||||
|
||||
const schema = Joi.object().keys({
|
||||
user: Joi.string().hex().lowercase().length(24).required()
|
||||
user: Joi.string()
|
||||
.hex()
|
||||
.lowercase()
|
||||
.length(24)
|
||||
.required()
|
||||
});
|
||||
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
|
|
|
@ -511,7 +511,7 @@ class MessageHandler {
|
|||
return next();
|
||||
}
|
||||
|
||||
this.attachmentStorage.deleteMany(attachmentIds, next);
|
||||
this.attachmentStorage.deleteMany(attachmentIds, message.magic, next);
|
||||
};
|
||||
|
||||
updateAttachments(() => {
|
||||
|
@ -812,14 +812,20 @@ class MessageHandler {
|
|||
.map(line => {
|
||||
line = Buffer.from(line, 'binary').toString();
|
||||
|
||||
let key = line.substr(0, line.indexOf(':')).trim().toLowerCase();
|
||||
let key = line
|
||||
.substr(0, line.indexOf(':'))
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
if (!INDEXED_HEADERS.includes(key)) {
|
||||
// do not index this header
|
||||
return false;
|
||||
}
|
||||
|
||||
let value = line.substr(line.indexOf(':') + 1).trim().replace(/\s*\r?\n\s*/g, ' ');
|
||||
let value = line
|
||||
.substr(line.indexOf(':') + 1)
|
||||
.trim()
|
||||
.replace(/\s*\r?\n\s*/g, ' ');
|
||||
|
||||
try {
|
||||
value = libmime.decodeWords(value);
|
||||
|
@ -832,13 +838,17 @@ class MessageHandler {
|
|||
|
||||
// trim long values as mongodb indexed fields can not be too long
|
||||
if (Buffer.byteLength(key, 'utf-8') >= 255) {
|
||||
key = Buffer.from(key).slice(0, 255).toString();
|
||||
key = Buffer.from(key)
|
||||
.slice(0, 255)
|
||||
.toString();
|
||||
key = key.substr(0, key.length - 4);
|
||||
}
|
||||
|
||||
if (Buffer.byteLength(value, 'utf-8') >= 880) {
|
||||
// value exceeds MongoDB max indexed value length
|
||||
value = Buffer.from(value).slice(0, 880).toString();
|
||||
value = Buffer.from(value)
|
||||
.slice(0, 880)
|
||||
.toString();
|
||||
// remove last 4 chars to be sure we do not have any incomplete unicode sequences
|
||||
value = value.substr(0, value.length - 4);
|
||||
}
|
||||
|
@ -909,7 +919,13 @@ class MessageHandler {
|
|||
.split(/\s+/)
|
||||
.map(id => id.replace(/[<>]/g, '').trim())
|
||||
.filter(id => id)
|
||||
.map(id => crypto.createHash('sha1').update(id).digest('base64').replace(/[=]+$/g, ''))
|
||||
.map(id =>
|
||||
crypto
|
||||
.createHash('sha1')
|
||||
.update(id)
|
||||
.digest('base64')
|
||||
.replace(/[=]+$/g, '')
|
||||
)
|
||||
);
|
||||
|
||||
referenceIds = Array.from(referenceIds).slice(0, 10);
|
||||
|
@ -1216,7 +1232,13 @@ class MessageHandler {
|
|||
if (/^content-type:/i.test(line)) {
|
||||
let parts = line.split(':');
|
||||
let value = parts.slice(1).join(':');
|
||||
if (value.split(';').shift().trim().toLowerCase() === 'multipart/encrypted') {
|
||||
if (
|
||||
value
|
||||
.split(';')
|
||||
.shift()
|
||||
.trim()
|
||||
.toLowerCase() === 'multipart/encrypted'
|
||||
) {
|
||||
// message is already encrypted, do nothing
|
||||
return callback(null, false);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wildduck",
|
||||
"version": "1.0.76",
|
||||
"version": "1.0.77",
|
||||
"description": "IMAP server built with Node.js and MongoDB",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
@ -11,7 +11,7 @@
|
|||
"license": "EUPL-1.1",
|
||||
"devDependencies": {
|
||||
"browserbox": "^0.9.1",
|
||||
"chai": "^4.1.1",
|
||||
"chai": "^4.1.2",
|
||||
"eslint-config-nodemailer": "^1.2.0",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-cli": "^1.2.0",
|
||||
|
@ -25,7 +25,7 @@
|
|||
"dependencies": {
|
||||
"addressparser": "^1.0.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"bugsnag": "^1.12.0",
|
||||
"bugsnag": "^1.12.1",
|
||||
"generate-password": "^1.3.0",
|
||||
"he": "^1.1.1",
|
||||
"html-to-text": "^3.3.0",
|
||||
|
|
Loading…
Reference in a new issue