mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-28 07:54:31 +08:00
use asynchronous search matching
This commit is contained in:
parent
9314708e1f
commit
cae2ad9e95
5 changed files with 2427 additions and 227 deletions
|
@ -6,36 +6,32 @@ let indexer = new Indexer();
|
|||
module.exports.matchSearchQuery = matchSearchQuery;
|
||||
|
||||
let queryHandlers = {
|
||||
|
||||
// atom beautify uses invalid indentation that messes up shorthand methods
|
||||
/*eslint-disable object-shorthand */
|
||||
|
||||
// always matches
|
||||
all: function () {
|
||||
return true;
|
||||
all(message, query, callback) {
|
||||
return callback(null, true);
|
||||
},
|
||||
|
||||
// matches if the message object includes (exists:true) or does not include (exists:false) specifiec flag
|
||||
flag: function (message, query) {
|
||||
flag(message, query, callback) {
|
||||
let pos = [].concat(message.flags || []).indexOf(query.value);
|
||||
return query.exists ? pos >= 0 : pos < 0;
|
||||
return callback(null, query.exists ? pos >= 0 : pos < 0);
|
||||
},
|
||||
|
||||
// matches message receive date
|
||||
internaldate: function (message, query) {
|
||||
internaldate(message, query, callback) {
|
||||
switch (query.operator) {
|
||||
case '<':
|
||||
return getShortDate(message.internaldate) < getShortDate(query.value);
|
||||
return callback(null, getShortDate(message.internaldate) < getShortDate(query.value));
|
||||
case '=':
|
||||
return getShortDate(message.internaldate) === getShortDate(query.value);
|
||||
return callback(null, getShortDate(message.internaldate) === getShortDate(query.value));
|
||||
case '>=':
|
||||
return getShortDate(message.internaldate) >= getShortDate(query.value);
|
||||
return callback(null, getShortDate(message.internaldate) >= getShortDate(query.value));
|
||||
}
|
||||
return false;
|
||||
return callback(null, false);
|
||||
},
|
||||
|
||||
// matches message header date
|
||||
date: function (message, query) {
|
||||
date(message, query, callback) {
|
||||
let date;
|
||||
if (message.headerdate) {
|
||||
date = message.headerdate;
|
||||
|
@ -49,39 +45,97 @@ let queryHandlers = {
|
|||
|
||||
switch (query.operator) {
|
||||
case '<':
|
||||
return getShortDate(date) < getShortDate(query.value);
|
||||
return callback(null, getShortDate(date) < getShortDate(query.value));
|
||||
case '=':
|
||||
return getShortDate(date) === getShortDate(query.value);
|
||||
return callback(null, getShortDate(date) === getShortDate(query.value));
|
||||
case '>=':
|
||||
return getShortDate(date) >= getShortDate(query.value);
|
||||
return callback(null, getShortDate(date) >= getShortDate(query.value));
|
||||
}
|
||||
|
||||
return false;
|
||||
return callback(null, false);
|
||||
},
|
||||
|
||||
// matches message body
|
||||
body: function (message, query) {
|
||||
let body = indexer.getContents(message.mimeTree).toString();
|
||||
let bodyStart = body.match(/\r?\r?\n/);
|
||||
if (!bodyStart) {
|
||||
return false;
|
||||
}
|
||||
return body.substr(bodyStart.index + bodyStart[0].length).toLowerCase().indexOf((query.value || '').toString().toLowerCase()) >= 0;
|
||||
body(message, query, callback) {
|
||||
let data = indexer.getContents(message.mimeTree, {
|
||||
type: 'text'
|
||||
});
|
||||
|
||||
let resolveData = next => {
|
||||
if (data.type !== 'stream') {
|
||||
return next(null, data.value);
|
||||
}
|
||||
|
||||
let chunks = [];
|
||||
let chunklen = 0;
|
||||
|
||||
data.value.once('error', err => next(err));
|
||||
|
||||
data.value.on('readable', () => {
|
||||
let chunk;
|
||||
while ((chunk = data.value.read()) !== null) {
|
||||
chunks.push(chunk);
|
||||
chunklen += chunk.length;
|
||||
}
|
||||
});
|
||||
|
||||
data.value.on('end', () => {
|
||||
next(null, Buffer.concat(chunks, chunklen));
|
||||
});
|
||||
};
|
||||
|
||||
resolveData((err, body) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, body.toString().toLowerCase().indexOf((query.value || '').toString().toLowerCase()) >= 0);
|
||||
});
|
||||
},
|
||||
|
||||
// matches message source
|
||||
text: function (message, query) {
|
||||
let body = indexer.getContents(message.mimeTree).toString();
|
||||
return body.toLowerCase().indexOf((query.value || '').toString().toLowerCase()) >= 0;
|
||||
text(message, query, callback) {
|
||||
let data = indexer.getContents(message.mimeTree, {
|
||||
type: 'content'
|
||||
});
|
||||
|
||||
let resolveData = next => {
|
||||
if (data.type !== 'stream') {
|
||||
return next(null, data.value);
|
||||
}
|
||||
|
||||
let chunks = [];
|
||||
let chunklen = 0;
|
||||
|
||||
data.value.once('error', err => next(err));
|
||||
|
||||
data.value.on('readable', () => {
|
||||
let chunk;
|
||||
while ((chunk = data.value.read()) !== null) {
|
||||
chunks.push(chunk);
|
||||
chunklen += chunk.length;
|
||||
}
|
||||
});
|
||||
|
||||
data.value.on('end', () => {
|
||||
next(null, Buffer.concat(chunks, chunklen));
|
||||
});
|
||||
};
|
||||
|
||||
resolveData((err, text) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, text.toString().toLowerCase().indexOf((query.value || '').toString().toLowerCase()) >= 0);
|
||||
});
|
||||
},
|
||||
|
||||
// matches message UID number. Sequence queries are also converted to UID queries
|
||||
uid: function (message, query) {
|
||||
return query.value.indexOf(message.uid) >= 0;
|
||||
uid(message, query, callback) {
|
||||
return callback(null, query.value.indexOf(message.uid) >= 0);
|
||||
},
|
||||
|
||||
// matches message source size
|
||||
size: function (message, query) {
|
||||
size(message, query, callback) {
|
||||
let size = message.size;
|
||||
if (!size) {
|
||||
size = (message.raw || '').length;
|
||||
|
@ -89,18 +143,18 @@ let queryHandlers = {
|
|||
|
||||
switch (query.operator) {
|
||||
case '<':
|
||||
return size < query.value;
|
||||
return callback(null, size < query.value);
|
||||
case '=':
|
||||
return size === query.value;
|
||||
return callback(null, size === query.value);
|
||||
case '>':
|
||||
return size > query.value;
|
||||
return callback(null, size > query.value);
|
||||
}
|
||||
|
||||
return false;
|
||||
return callback(null, false);
|
||||
},
|
||||
|
||||
// matches message headers
|
||||
header: function (message, query) {
|
||||
header(message, query, callback) {
|
||||
let mimeTree = message.mimeTree;
|
||||
if (!mimeTree) {
|
||||
mimeTree = indexer.parseMimeTree(message.raw || '');
|
||||
|
@ -123,24 +177,22 @@ let queryHandlers = {
|
|||
value = (parts.join(':') || '');
|
||||
|
||||
if (key === header && (!term || value.toLowerCase().indexOf(term) >= 0)) {
|
||||
return true;
|
||||
return callback(null, true);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return callback(null, false);
|
||||
},
|
||||
|
||||
// matches messages with modifyIndex exual or greater than criteria
|
||||
modseq: function (message, query) {
|
||||
return message.modseq >= query.value;
|
||||
modseq(message, query, callback) {
|
||||
return callback(null, message.modseq >= query.value);
|
||||
},
|
||||
|
||||
// charset argument is ignored
|
||||
charset: function () {
|
||||
return true;
|
||||
charset(message, query, callback) {
|
||||
return callback(null, true);
|
||||
}
|
||||
|
||||
/*eslint-enable object-shorthand */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -164,20 +216,41 @@ function getShortDate(date) {
|
|||
* @param {Object} query Query term object
|
||||
* @returns {Boolean} Term matched (true) or not (false)
|
||||
*/
|
||||
function matchSearchTerm(message, query) {
|
||||
function matchSearchTerm(message, query, callback) {
|
||||
|
||||
if (Array.isArray(query)) {
|
||||
// AND, all terms need to match
|
||||
return matchSearchQuery(message, query);
|
||||
return matchSearchQuery(message, query, callback);
|
||||
}
|
||||
|
||||
if (!query || typeof query !== 'object') {
|
||||
// unknown query term
|
||||
return false;
|
||||
return setImmediate(() => callback(null, false));
|
||||
}
|
||||
|
||||
switch (query.key) {
|
||||
case 'or':
|
||||
{
|
||||
// OR, only single match needed
|
||||
let checked = 0;
|
||||
let checkNext = () => {
|
||||
if (checked >= query.value.length) {
|
||||
return callback(null, false);
|
||||
}
|
||||
let term = query.value[checked++];
|
||||
matchSearchTerm(message, term, (err, match) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (match) {
|
||||
return callback(null, true);
|
||||
}
|
||||
setImmediate(checkNext);
|
||||
});
|
||||
};
|
||||
return setImmediate(checkNext);
|
||||
}
|
||||
/*
|
||||
// OR, only single match needed
|
||||
for (let i = query.value.length - 1; i >= 0; i--) {
|
||||
if (matchSearchTerm(message, query.value[i])) {
|
||||
|
@ -185,15 +258,21 @@ function matchSearchTerm(message, query) {
|
|||
}
|
||||
}
|
||||
return false;
|
||||
*/
|
||||
case 'not':
|
||||
// return reverse match
|
||||
return !matchSearchTerm(message, query.value);
|
||||
return matchSearchTerm(message, query.value, (err, match) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
callback(null, !match);
|
||||
});
|
||||
default:
|
||||
// check if there is a handler for the term and use it
|
||||
if (queryHandlers.hasOwnProperty(query.key)) {
|
||||
return queryHandlers[query.key](message, query);
|
||||
return setImmediate(() => queryHandlers[query.key](message, query, callback));
|
||||
}
|
||||
return false;
|
||||
return setImmediate(() => callback(null, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,16 +283,35 @@ function matchSearchTerm(message, query) {
|
|||
* @param {Object} query Query term object
|
||||
* @returns {Boolean} Term matched (true) or not (false)
|
||||
*/
|
||||
function matchSearchQuery(message, query) {
|
||||
function matchSearchQuery(message, query, callback) {
|
||||
if (!Array.isArray(query)) {
|
||||
query = [].concat(query || []);
|
||||
}
|
||||
|
||||
for (let i = 0, len = query.length; i < len; i++) {
|
||||
if (!matchSearchTerm(message, query[i])) {
|
||||
return false;
|
||||
let checked = 0;
|
||||
let checkNext = () => {
|
||||
if (checked >= query.length) {
|
||||
return callback(null, true);
|
||||
}
|
||||
let term = query[checked++];
|
||||
matchSearchTerm(message, term, (err, match) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (!match) {
|
||||
return callback(null, false);
|
||||
}
|
||||
setImmediate(checkNext);
|
||||
});
|
||||
};
|
||||
return setImmediate(checkNext);
|
||||
/*
|
||||
for (let i = 0, len = query.length; i < len; i++) {
|
||||
if (!matchSearchTerm(message, query[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
|
||||
let parseQueryTerms = require('../lib/commands/search').parseQueryTerms;
|
||||
let matchSearchQuery = require('../lib/search').matchSearchQuery;
|
||||
let Indexer = require('../lib/indexer/indexer');
|
||||
let indexer = new Indexer();
|
||||
|
||||
let chai = require('chai');
|
||||
let expect = chai.expect;
|
||||
chai.config.includeStack = true;
|
||||
|
@ -400,8 +403,8 @@ describe('#parseQueryTerms', function () {
|
|||
|
||||
describe('Search term match tests', function () {
|
||||
describe('AND', function () {
|
||||
it('should find all matches', function () {
|
||||
expect(matchSearchQuery({}, [{
|
||||
it('should find all matches', function (done) {
|
||||
matchSearchQuery({}, [{
|
||||
key: 'all',
|
||||
value: true
|
||||
}, {
|
||||
|
@ -413,11 +416,15 @@ describe('Search term match tests', function () {
|
|||
}, {
|
||||
key: 'all',
|
||||
value: true
|
||||
}])).to.be.true;
|
||||
}], (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail on single error', function () {
|
||||
expect(matchSearchQuery({}, [{
|
||||
it('should fail on single error', function (done) {
|
||||
matchSearchQuery({}, [{
|
||||
key: 'all',
|
||||
value: true
|
||||
}, {
|
||||
|
@ -430,13 +437,17 @@ describe('Search term match tests', function () {
|
|||
key: 'flag',
|
||||
value: 'zzzzzzz',
|
||||
exists: true
|
||||
}])).to.be.false;
|
||||
}], (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('OR', function () {
|
||||
it('should succeed with at least one match', function () {
|
||||
expect(matchSearchQuery({}, {
|
||||
it('should succeed with at least one match', function (done) {
|
||||
matchSearchQuery({}, {
|
||||
key: 'or',
|
||||
value: [{
|
||||
key: 'flag',
|
||||
|
@ -450,11 +461,15 @@ describe('Search term match tests', function () {
|
|||
value: 'zzzzzzz',
|
||||
exists: true
|
||||
}]
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail with no matches', function () {
|
||||
expect(matchSearchQuery({}, {
|
||||
it('should fail with no matches', function (done) {
|
||||
matchSearchQuery({}, {
|
||||
key: 'or',
|
||||
value: [{
|
||||
key: 'flag',
|
||||
|
@ -465,419 +480,581 @@ describe('Search term match tests', function () {
|
|||
value: 'zzzzzzz',
|
||||
exists: true
|
||||
}]
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('NOT', function () {
|
||||
it('should succeed with false value', function () {
|
||||
expect(matchSearchQuery({}, {
|
||||
it('should succeed with false value', function (done) {
|
||||
matchSearchQuery({}, {
|
||||
key: 'not',
|
||||
value: {
|
||||
key: 'flag',
|
||||
value: 'zzzzzzz',
|
||||
exists: true
|
||||
}
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail with thruthy value', function () {
|
||||
expect(matchSearchQuery({}, {
|
||||
it('should fail with thruthy value', function (done) {
|
||||
matchSearchQuery({}, {
|
||||
key: 'not',
|
||||
value: {
|
||||
key: 'all',
|
||||
value: true
|
||||
}
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('ALL', function () {
|
||||
it('should match ALL', function () {
|
||||
expect(matchSearchQuery({}, {
|
||||
it('should match ALL', function (done) {
|
||||
matchSearchQuery({}, {
|
||||
key: 'all',
|
||||
value: true
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('FLAG', function () {
|
||||
it('should match existing flag', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should match existing flag', function (done) {
|
||||
matchSearchQuery({
|
||||
flags: ['abc', 'def', 'ghi']
|
||||
}, {
|
||||
key: 'flag',
|
||||
value: 'def',
|
||||
exists: true
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match non-existing flag', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should match non-existing flag', function (done) {
|
||||
matchSearchQuery({
|
||||
flags: ['abc', 'def', 'ghi']
|
||||
}, {
|
||||
key: 'flag',
|
||||
value: 'zzzzz',
|
||||
exists: false
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail non-existing flag', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should fail non-existing flag', function (done) {
|
||||
matchSearchQuery({
|
||||
flags: ['abc', 'def', 'ghi']
|
||||
}, {
|
||||
key: 'flag',
|
||||
value: 'zzzzz',
|
||||
exists: true
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail existing flag', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should fail existing flag', function (done) {
|
||||
matchSearchQuery({
|
||||
flags: ['abc', 'def', 'ghi']
|
||||
}, {
|
||||
key: 'flag',
|
||||
value: 'abc',
|
||||
exists: false
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('INTERNALDATE', function () {
|
||||
it('should match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should match <', function (done) {
|
||||
matchSearchQuery({
|
||||
internaldate: new Date('1999-01-01')
|
||||
}, {
|
||||
key: 'internaldate',
|
||||
value: '2001-01-01',
|
||||
operator: '<'
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should not match <', function (done) {
|
||||
matchSearchQuery({
|
||||
internaldate: new Date('1999-01-01')
|
||||
}, {
|
||||
key: 'internaldate',
|
||||
value: '1998-01-01',
|
||||
operator: '<'
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match =', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should match =', function (done) {
|
||||
matchSearchQuery({
|
||||
internaldate: new Date('1999-01-01')
|
||||
}, {
|
||||
key: 'internaldate',
|
||||
value: '1999-01-01',
|
||||
operator: '='
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should not match <', function (done) {
|
||||
matchSearchQuery({
|
||||
internaldate: new Date('1999-01-01')
|
||||
}, {
|
||||
key: 'internaldate',
|
||||
value: '1999-01-02',
|
||||
operator: '='
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match >=', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should match >=', function (done) {
|
||||
matchSearchQuery({
|
||||
internaldate: new Date('1999-01-01')
|
||||
}, {
|
||||
key: 'internaldate',
|
||||
value: '1999-01-01',
|
||||
operator: '>='
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
|
||||
expect(matchSearchQuery({
|
||||
internaldate: new Date('1999-01-02')
|
||||
}, {
|
||||
key: 'internaldate',
|
||||
value: '1999-01-01',
|
||||
operator: '>='
|
||||
})).to.be.true;
|
||||
matchSearchQuery({
|
||||
internaldate: new Date('1999-01-02')
|
||||
}, {
|
||||
key: 'internaldate',
|
||||
value: '1999-01-01',
|
||||
operator: '>='
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match >=', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should not match >=', function (done) {
|
||||
matchSearchQuery({
|
||||
internaldate: new Date('1999-01-01')
|
||||
}, {
|
||||
key: 'internaldate',
|
||||
value: '1999-01-02',
|
||||
operator: '>='
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DATE', function () {
|
||||
let raw = 'Subject: test\r\nDate: 1999-01-01\r\n\r\nHello world!';
|
||||
|
||||
it('should match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should match <', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'date',
|
||||
value: '2001-01-01',
|
||||
operator: '<'
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should not match <', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'date',
|
||||
value: '1998-01-01',
|
||||
operator: '<'
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match =', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should match =', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'date',
|
||||
value: '1999-01-01',
|
||||
operator: '='
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should not match <', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'date',
|
||||
value: '1999-01-02',
|
||||
operator: '='
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match >=', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should match >=', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'date',
|
||||
value: '1999-01-01',
|
||||
operator: '>='
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
}, {
|
||||
key: 'date',
|
||||
value: '1998-01-01',
|
||||
operator: '>='
|
||||
})).to.be.true;
|
||||
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'date',
|
||||
value: '1998-01-01',
|
||||
operator: '>='
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match >=', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should not match >=', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'date',
|
||||
value: '1999-01-02',
|
||||
operator: '>='
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('BODY', function () {
|
||||
let raw = 'Subject: test\r\n\r\nHello world!';
|
||||
|
||||
it('should match a string', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should match a string', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'body',
|
||||
value: 'hello'
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match a string', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should not match a string', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'body',
|
||||
value: 'test'
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('TEXT', function () {
|
||||
let raw = 'Subject: test\r\n\r\nHello world!';
|
||||
|
||||
it('should match a string', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should match a string', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'text',
|
||||
value: 'hello'
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
}, {
|
||||
key: 'text',
|
||||
value: 'test'
|
||||
})).to.be.true;
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'text',
|
||||
value: 'test'
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match a string', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should not match a string', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'text',
|
||||
value: 'zzzzz'
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('UID', function () {
|
||||
it('should match message uid', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should match message uid', function (done) {
|
||||
matchSearchQuery({
|
||||
uid: 123
|
||||
}, {
|
||||
key: 'uid',
|
||||
value: [11, 123, 134]
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match message uid', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should not match message uid', function (done) {
|
||||
matchSearchQuery({
|
||||
uid: 124
|
||||
}, {
|
||||
key: 'uid',
|
||||
value: [11, 123, 134]
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('SIZE', function () {
|
||||
it('should match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw: new Buffer(10)
|
||||
it('should match <', function (done) {
|
||||
matchSearchQuery({
|
||||
size: 10
|
||||
}, {
|
||||
key: 'size',
|
||||
value: 11,
|
||||
operator: '<'
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw: new Buffer(10)
|
||||
it('should not match <', function (done) {
|
||||
matchSearchQuery({
|
||||
size: 10
|
||||
}, {
|
||||
key: 'size',
|
||||
value: 9,
|
||||
operator: '<'
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match =', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw: new Buffer(10)
|
||||
it('should match =', function (done) {
|
||||
matchSearchQuery({
|
||||
size: 10
|
||||
}, {
|
||||
key: 'size',
|
||||
value: 10,
|
||||
operator: '='
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match =', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw: new Buffer(10)
|
||||
it('should not match =', function (done) {
|
||||
matchSearchQuery({
|
||||
size: 10
|
||||
}, {
|
||||
key: 'size',
|
||||
value: 11,
|
||||
operator: '='
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match >', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw: new Buffer(10)
|
||||
it('should match >', function (done) {
|
||||
matchSearchQuery({
|
||||
size: 10
|
||||
}, {
|
||||
key: 'size',
|
||||
value: 9,
|
||||
operator: '>'
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match <', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw: new Buffer(10)
|
||||
it('should not match <', function (done) {
|
||||
matchSearchQuery({
|
||||
size: 10
|
||||
}, {
|
||||
key: 'size',
|
||||
value: 11,
|
||||
operator: '>'
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('header', function () {
|
||||
let raw = 'Subject: test\r\n\r\nHello world!';
|
||||
|
||||
it('should match header value', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should match header value', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'header',
|
||||
value: 'test',
|
||||
header: 'subject'
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match empty header value', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should match empty header value', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'header',
|
||||
value: '',
|
||||
header: 'subject'
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match header value', function () {
|
||||
expect(matchSearchQuery({
|
||||
raw
|
||||
it('should not match header value', function (done) {
|
||||
matchSearchQuery({
|
||||
mimeTree: indexer.parseMimeTree(raw)
|
||||
}, {
|
||||
key: 'header',
|
||||
value: 'tests',
|
||||
header: 'subject'
|
||||
})).to.be.false;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.false;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('MODSEQ', function () {
|
||||
it('should match equal modseq', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should match equal modseq', function (done) {
|
||||
matchSearchQuery({
|
||||
modseq: 500
|
||||
}, {
|
||||
key: 'modseq',
|
||||
value: [500]
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should match greater modseq', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should match greater modseq', function (done) {
|
||||
matchSearchQuery({
|
||||
modseq: 1000
|
||||
}, {
|
||||
key: 'modseq',
|
||||
value: [500]
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not match lesser modseq', function () {
|
||||
expect(matchSearchQuery({
|
||||
it('should not match lesser modseq', function (done) {
|
||||
matchSearchQuery({
|
||||
modseq: 500
|
||||
}, {
|
||||
key: 'modseq',
|
||||
value: [100]
|
||||
})).to.be.true;
|
||||
}, (err, match) => {
|
||||
expect(err).to.not.exist;
|
||||
expect(match).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
22
imap.js
22
imap.js
|
@ -1029,15 +1029,21 @@ server.onSearch = function (path, options, session, callback) {
|
|||
message.raw = message.raw.toString();
|
||||
}
|
||||
|
||||
let match = session.matchSearchQuery(message, options.query);
|
||||
if (match && highestModseq < message.modseq) {
|
||||
highestModseq = message.modseq;
|
||||
}
|
||||
if (match) {
|
||||
uidList.push(message.uid);
|
||||
}
|
||||
session.matchSearchQuery(message, options.query, (err, match) => {
|
||||
if (err) {
|
||||
return cursor.close(() => callback(err));
|
||||
}
|
||||
|
||||
processNext();
|
||||
if (match && highestModseq < message.modseq) {
|
||||
highestModseq = message.modseq;
|
||||
}
|
||||
|
||||
if (match) {
|
||||
uidList.push(message.uid);
|
||||
}
|
||||
|
||||
processNext();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
"dependencies": {
|
||||
"addressparser": "^1.0.1",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"clone": "^2.1.0",
|
||||
"clone": "^2.1.1",
|
||||
"config": "^1.25.1",
|
||||
"joi": "^10.2.2",
|
||||
"libbase64": "^0.1.0",
|
||||
|
|
Loading…
Add table
Reference in a new issue