This commit is contained in:
Andris Reinman 2017-09-01 14:50:53 +03:00
parent 01166ed33d
commit 4cfd90f473
6 changed files with 504 additions and 151 deletions

View file

@ -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, {

View file

@ -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];

View file

@ -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, {

View file

@ -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, {

View file

@ -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);
}

View file

@ -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",