mirror of
https://github.com/nodemailer/wildduck.git
synced 2024-09-20 15:26:03 +08:00
Use header parser from libmime, removed local implementation
This commit is contained in:
parent
83b7353f3f
commit
e983eae623
|
@ -1,6 +1,7 @@
|
|||
'use strict';
|
||||
|
||||
const addressparser = require('nodemailer/lib/addressparser');
|
||||
const libmime = require('libmime');
|
||||
|
||||
/**
|
||||
* Parses a RFC822 message into a structured object (JSON compatible)
|
||||
|
@ -254,61 +255,19 @@ class MIMEParser {
|
|||
* @return {Object} Parsed value
|
||||
*/
|
||||
parseValueParams(headerValue) {
|
||||
let data = {
|
||||
value: '',
|
||||
type: '',
|
||||
subtype: '',
|
||||
params: {}
|
||||
let parsed = libmime.parseHeaderValue(headerValue) || {};
|
||||
|
||||
let subtype = (parsed.value || '').split('/');
|
||||
let type = (subtype.shift() || '').toLowerCase();
|
||||
subtype = subtype.join('/');
|
||||
|
||||
return {
|
||||
value: parsed.value || '',
|
||||
type,
|
||||
subtype,
|
||||
params: parsed.params || {},
|
||||
hasParams: !!Object.keys(parsed.params || {}).length
|
||||
};
|
||||
let match;
|
||||
let processEncodedWords = {};
|
||||
|
||||
(headerValue || '').split(';').forEach((part, i) => {
|
||||
let key, value;
|
||||
if (!i) {
|
||||
data.value = part.trim();
|
||||
data.subtype = data.value.split('/');
|
||||
data.type = (data.subtype.shift() || '').toLowerCase();
|
||||
data.subtype = data.subtype.join('/');
|
||||
return;
|
||||
}
|
||||
value = part.split('=');
|
||||
key = (value.shift() || '').trim().toLowerCase();
|
||||
value = value.join('=').replace(/^['"\s]*|['"\s]*$/g, '');
|
||||
|
||||
// Do not touch headers that have strange looking keys, keep these
|
||||
// only in the unparsed array
|
||||
if (/[^a-zA-Z0-9\-*]/.test(key) || key.length >= 100) {
|
||||
return;
|
||||
}
|
||||
|
||||
// This regex allows for an optional trailing asterisk, for headers
|
||||
// which are encoded with lang/charset info as well as a continuation.
|
||||
// See https://tools.ietf.org/html/rfc2231 section 4.1.
|
||||
if ((match = key.match(/^([^*]+)\*(\d)?\*?$/))) {
|
||||
if (!processEncodedWords[match[1]]) {
|
||||
processEncodedWords[match[1]] = [];
|
||||
}
|
||||
processEncodedWords[match[1]][Number(match[2]) || 0] = value;
|
||||
} else {
|
||||
data.params[key] = value;
|
||||
}
|
||||
data.hasParams = true;
|
||||
});
|
||||
|
||||
// convert extended mime word into a regular one
|
||||
Object.keys(processEncodedWords).forEach(key => {
|
||||
let charset = '';
|
||||
let value = '';
|
||||
processEncodedWords[key].forEach(val => {
|
||||
let parts = val.split("'"); // eslint-disable-line quotes
|
||||
charset = charset || parts.shift();
|
||||
value += (parts.pop() || '').replace(/%/g, '=');
|
||||
});
|
||||
data.params[key] = '=?' + (charset || 'ISO-8859-1').toUpperCase() + '?Q?' + value + '?=';
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -337,4 +296,6 @@ function parse(rfc822) {
|
|||
return response;
|
||||
}
|
||||
|
||||
parse.MIMEParser = MIMEParser;
|
||||
|
||||
module.exports = parse;
|
||||
|
|
33
imap-core/test/parse-mime-tree-test.js
Normal file
33
imap-core/test/parse-mime-tree-test.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*eslint no-unused-expressions: 0, prefer-arrow-callback: 0 */
|
||||
|
||||
'use strict';
|
||||
|
||||
const MIMEParser = require('../lib/indexer/parse-mime-tree').MIMEParser;
|
||||
|
||||
const chai = require('chai');
|
||||
const expect = chai.expect;
|
||||
chai.config.includeStack = true;
|
||||
|
||||
describe('#parseValueParams', function () {
|
||||
it.only('should return as is', function () {
|
||||
let parser = new MIMEParser();
|
||||
const parsed = parser.parseValueParams(
|
||||
'text/plain;\n' +
|
||||
'\tname*0=emailengine_uuendamise_kasud_ja_muud_asjad_ja_veelgi_pikem_pealk;\n' +
|
||||
'\tname*1=iri.txt;\n' +
|
||||
'\tx-apple-part-url=99AFDE83-8953-43B4-BE59-F59D6160AFAB'
|
||||
);
|
||||
|
||||
console.log('PARSED', parsed);
|
||||
expect(parsed).to.equal({
|
||||
value: 'text/plain',
|
||||
type: 'text',
|
||||
subtype: 'plain',
|
||||
params: {
|
||||
'x-apple-part-url': '99AFDE83-8953-43B4-BE59-F59D6160AFAB',
|
||||
name: 'emailengine_uuendamise_kasud_ja_muud_asjad_ja_veelgi_pikem_pealkiri.txt'
|
||||
},
|
||||
hasParams: true
|
||||
});
|
||||
});
|
||||
});
|
|
@ -314,8 +314,213 @@ const getMongoDBQuery = async (db, user, queryStr) => {
|
|||
|
||||
return { user: false };
|
||||
};
|
||||
/*
|
||||
const getElasticSearchQuery = async (db, user, queryStr) => {
|
||||
const parsed = parseSearchQuery(queryStr);
|
||||
|
||||
module.exports = { parseSearchQuery, getMongoDBQuery };
|
||||
let searchQuery = {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
term: {
|
||||
user: (user || '').toString().trim()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
let curNode = searchQuery;
|
||||
|
||||
let walkTree = async (node, curNode) => {
|
||||
if (Array.isArray(node)) {
|
||||
let branches = [];
|
||||
for (let entry of node) {
|
||||
branches.push(await walkTree(entry));
|
||||
}
|
||||
return branches;
|
||||
}
|
||||
|
||||
if (node.$and && node.$and.length) {
|
||||
let branch = {
|
||||
bool: { must: [] }
|
||||
};
|
||||
|
||||
for (let entry of node.$and) {
|
||||
let subBranch = await walkTree(entry);
|
||||
branch.bool.must = branch.bool.must.concat(subBranch || []);
|
||||
}
|
||||
|
||||
return branch;
|
||||
} else if (node.$or && node.$or.length) {
|
||||
let branch = {
|
||||
bool: { should: [], minimum_should_match: 1 }
|
||||
};
|
||||
|
||||
for (let entry of node.$or) {
|
||||
let subBranch = await walkTree(entry);
|
||||
|
||||
branch.bool.should = branch.bool.should.concat(subBranch || []);
|
||||
}
|
||||
|
||||
return branch;
|
||||
} else if (node.text) {
|
||||
let branch = {
|
||||
bool: {
|
||||
should: [
|
||||
{
|
||||
match: {
|
||||
'text.plain': {
|
||||
query: node.text.value,
|
||||
operator: 'and'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
match: {
|
||||
'text.html': {
|
||||
query: node.text.value,
|
||||
operator: 'and'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
minimum_should_match: 1
|
||||
}
|
||||
};
|
||||
|
||||
if (node.text.negated) {
|
||||
// FIXME: negation support!
|
||||
}
|
||||
|
||||
return branch;
|
||||
} else if (node.keywords) {
|
||||
let branches = [];
|
||||
|
||||
let keyword = Object.keys(node.keywords || {}).find(key => key && key !== 'negated');
|
||||
if (keyword) {
|
||||
let { value, negated } = node.keywords[keyword];
|
||||
switch (keyword) {
|
||||
case 'from':
|
||||
case 'subject':
|
||||
{
|
||||
let regex = escapeRegexStr(value);
|
||||
let branch = {
|
||||
headers: {
|
||||
$elemMatch: {
|
||||
key: keyword,
|
||||
value: {
|
||||
$regex: regex,
|
||||
$options: 'i'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (negated) {
|
||||
branch = { $not: branch };
|
||||
}
|
||||
branches.push(branch);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'to':
|
||||
{
|
||||
let regex = escapeRegexStr(value);
|
||||
for (let toKey of ['to', 'cc', 'bcc']) {
|
||||
let branch = {
|
||||
headers: {
|
||||
$elemMatch: {
|
||||
key: toKey,
|
||||
value: {
|
||||
$regex: regex,
|
||||
$options: 'i'
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if (negated) {
|
||||
branch = { $not: branch };
|
||||
}
|
||||
branches.push(branch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'in': {
|
||||
value = (value || '').toString().trim();
|
||||
let resolveQuery = { user, $or: [] };
|
||||
if (/^[0-9a-f]{24}$/i.test(value)) {
|
||||
resolveQuery.$or.push({ _id: new ObjectId(value) });
|
||||
} else if (/^Inbox$/i.test(value)) {
|
||||
resolveQuery.$or.push({ path: 'INBOX' });
|
||||
} else {
|
||||
resolveQuery.$or.push({ path: value });
|
||||
if (/^\/?(spam|junk)/i.test(value)) {
|
||||
resolveQuery.$or.push({ specialUse: '\\Junk' });
|
||||
} else if (/^\/?(sent)/i.test(value)) {
|
||||
resolveQuery.$or.push({ specialUse: '\\Sent' });
|
||||
} else if (/^\/?(trash|deleted)/i.test(value)) {
|
||||
resolveQuery.$or.push({ specialUse: '\\Trash' });
|
||||
} else if (/^\/?(drafts)/i.test(value)) {
|
||||
resolveQuery.$or.push({ specialUse: '\\Drafts' });
|
||||
}
|
||||
}
|
||||
|
||||
let mailboxEntry = await db.database.collection('mailboxes').findOne(resolveQuery, { project: { _id: -1 } });
|
||||
|
||||
let branch = { mailbox: mailboxEntry ? mailboxEntry._id : new ObjectId('0'.repeat(24)) };
|
||||
if (negated) {
|
||||
branch = { $not: branch };
|
||||
}
|
||||
branches.push(branch);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'thread':
|
||||
{
|
||||
value = (value || '').toString().trim();
|
||||
if (/^[0-9a-f]{24}$/i.test(value)) {
|
||||
let branch = { thread: new ObjectId(value) };
|
||||
if (negated) {
|
||||
branch = { $not: branch };
|
||||
}
|
||||
branches.push(branch);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'has': {
|
||||
switch (value) {
|
||||
case 'attachment': {
|
||||
branches.push({ ha: true });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return branches;
|
||||
}
|
||||
};
|
||||
|
||||
if (parsed && parsed.length) {
|
||||
let filter = await walkTree(Array.isArray(parsed) ? { $and: parsed } : parsed);
|
||||
|
||||
let extras = { user };
|
||||
if (hasTextFilter) {
|
||||
extras.searchable = true;
|
||||
}
|
||||
|
||||
return Object.assign({ user: null }, filter, extras);
|
||||
}
|
||||
|
||||
return { user: false };
|
||||
};
|
||||
*/
|
||||
|
||||
module.exports = { parseSearchQuery, getMongoDBQuery /*, getElasticSearchQuery*/ };
|
||||
|
||||
/*
|
||||
const util = require('util');
|
||||
|
|
Loading…
Reference in a new issue