wildduck/lib/prepare-search-filter.js
2022-05-16 12:08:06 +03:00

271 lines
6.7 KiB
JavaScript

'use strict';
const ObjectId = require('mongodb').ObjectId;
const { escapeRegexStr } = require('./tools');
const prepareSearchFilter = async (db, user, payload) => {
let mailbox = payload.mailbox ? new ObjectId(payload.mailbox) : false;
let thread = payload.thread ? new ObjectId(payload.thread) : false;
let orTerms = payload.or || {};
let orQuery = [];
let query = payload.query;
let datestart = payload.datestart || false;
let dateend = payload.dateend || false;
let filterFrom = payload.from;
let filterTo = payload.to;
let filterSubject = payload.subject;
let filterAttachments = payload.attachments;
let filterFlagged = payload.flagged;
let filterUnseen = payload.unseen;
let filterSearchable = payload.searchable;
let filterMinSize = payload.minSize;
let filterMaxSize = payload.maxSize;
let userData;
try {
userData = await db.users.collection('users').findOne(
{
_id: user
},
{
projection: {
username: true,
address: true,
specialUse: true
}
}
);
} catch (err) {
err.responseCode = 500;
err.code = 'InternalDatabaseError';
err.formattedMessage = 'Database Error';
throw err;
}
if (!userData) {
let err = new Error('This user does not exist');
err.responseCode = 404;
err.code = 'UserNotFound';
err.formattedMessage = 'This user does not exist';
throw err;
}
// NB! Scattered query, searches over all user mailboxes and all shards
let filter = {
user
};
if (query) {
filter.searchable = true;
filter.$text = { $search: query };
} else if (orTerms.query) {
filter.searchable = true;
orQuery.push({ $text: { $search: query } });
}
if (mailbox) {
filter.mailbox = mailbox;
} else if (filterSearchable) {
// filter out Trash and Junk
let mailboxes;
try {
mailboxes = await db.database
.collection('mailboxes')
.find({ user, specialUse: { $in: ['\\Junk', '\\Trash'] } })
.project({
_id: true
})
.toArray();
} catch (err) {
err.responseCode = 500;
err.code = 'InternalDatabaseError';
err.formattedMessage = 'Database Error';
throw err;
}
filter.mailbox = { $nin: mailboxes.map(m => m._id) };
}
if (thread) {
filter.thread = thread;
}
if (filterFlagged) {
// mailbox is not needed as there's a special index for flagged messages
filter.flagged = true;
}
if (filterUnseen) {
filter.unseen = true;
filter.searchable = true;
}
if (filterSearchable) {
filter.searchable = true;
}
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 = escapeRegexStr(filterFrom);
if (!filter.$and) {
filter.$and = [];
}
filter.$and.push({
headers: {
$elemMatch: {
key: 'from',
value: {
$regex: regex,
$options: 'i'
}
}
}
});
}
if (orTerms.from) {
let regex = escapeRegexStr(orTerms.from);
orQuery.push({
headers: {
$elemMatch: {
key: 'from',
value: {
$regex: regex,
$options: 'i'
}
}
}
});
}
if (filterTo) {
let regex = escapeRegexStr(filterTo);
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 (orTerms.to) {
let regex = escapeRegexStr(orTerms.to);
orQuery.push({
headers: {
$elemMatch: {
key: 'to',
value: {
$regex: regex,
$options: 'i'
}
}
}
});
orQuery.push({
headers: {
$elemMatch: {
key: 'cc',
value: {
$regex: regex,
$options: 'i'
}
}
}
});
}
if (filterSubject) {
let regex = escapeRegexStr(filterSubject);
if (!filter.$and) {
filter.$and = [];
}
filter.$and.push({
headers: {
$elemMatch: {
key: 'subject',
value: {
$regex: regex,
$options: 'i'
}
}
}
});
}
if (orTerms.subject) {
let regex = escapeRegexStr(orTerms.subject);
orQuery.push({
headers: {
$elemMatch: {
key: 'subject',
value: {
$regex: regex,
$options: 'i'
}
}
}
});
}
if (filterAttachments) {
filter.ha = true;
}
if (filterMinSize) {
filter.size = filter.size || {};
filter.size.$gte = filterMinSize;
}
if (filterMaxSize) {
filter.size = filter.size || {};
filter.size.$lte = filterMaxSize;
}
if (orQuery.length) {
filter.$or = orQuery;
}
return { filter, query };
};
module.exports = prepareSearchFilter;