mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-09-08 06:04:42 +08:00
v1.0.113
This commit is contained in:
parent
cc3cc4922a
commit
44d9fffda7
11 changed files with 147 additions and 11 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-26T11:25:03.019Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-30T14:14:01.100Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
|
|
|
@ -1 +1 @@
|
|||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-26T11:25:03.019Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-01-30T14:14:01.100Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
|
|
|
@ -36,8 +36,9 @@ function send() {
|
|||
},
|
||||
|
||||
headers: {
|
||||
// set to Yes to send this message to Junk folder
|
||||
'x-rspamd-spam': 'No'
|
||||
'X-Rspamd-Bar': '/',
|
||||
'X-Rspamd-Report': 'R_PARTS_DIFFER(0.5) MIME_GOOD(-0.1) R_DKIM_ALLOW(-0.2) R_SPF_ALLOW(-0.2)',
|
||||
'X-Rspamd-Score': '22.6'
|
||||
},
|
||||
|
||||
from: 'Kärbes 🐧 <andris@kreata.ee>',
|
||||
|
|
|
@ -301,6 +301,7 @@ module.exports = (db, server, userHandler) => {
|
|||
* @apiParam {String} [pubKey] Public PGP key for the User that is used for encryption. Use empty string to remove the key
|
||||
* @apiParam {String} [language] Language code for the User
|
||||
* @apiParam {String[]} [targets] An array of forwarding targets. The value could either be an email address or a relay url to next MX server ("smtp://mx2.zone.eu:25") or an URL where mail contents are POSTed to
|
||||
* @apiParam {Number} [spamLevel=50] Relative scale for detecting spam. 0 means that everything is spam, 100 means that nothing is spam
|
||||
* @apiParam {Number} [quota] Allowed quota of the user in bytes
|
||||
* @apiParam {Number} [recipients] How many messages per 24 hour can be sent
|
||||
* @apiParam {Number} [forwards] How many messages per 24 hour can be forwarded
|
||||
|
@ -381,6 +382,11 @@ module.exports = (db, server, userHandler) => {
|
|||
})
|
||||
),
|
||||
|
||||
spamLevel: Joi.number()
|
||||
.min(0)
|
||||
.max(100)
|
||||
.default(50),
|
||||
|
||||
quota: Joi.number()
|
||||
.min(0)
|
||||
.default(0),
|
||||
|
@ -652,6 +658,7 @@ module.exports = (db, server, userHandler) => {
|
|||
* @apiSuccess {String} keyInfo.address E-mail address listed in public key
|
||||
* @apiSuccess {String} keyInfo.fingerprint Fingerprint of the public key
|
||||
* @apiSuccess {String[]} targets List of forwarding targets
|
||||
* @apiSuccess {Number} spamLevel Relative scale for detecting spam. 0 means that everything is spam, 100 means that nothing is spam
|
||||
* @apiSuccess {Object} limits Account limits and usage
|
||||
* @apiSuccess {Object} limits.quota Quota usage limits
|
||||
* @apiSuccess {Number} limits.quota.allowed Allowed quota of the user in bytes
|
||||
|
@ -805,6 +812,7 @@ module.exports = (db, server, userHandler) => {
|
|||
encryptMessages: userData.encryptMessages,
|
||||
encryptForwarded: userData.encryptForwarded,
|
||||
pubKey: userData.pubKey,
|
||||
spamLevel: userData.spamLevel,
|
||||
keyInfo: getKeyInfo(userData.pubKey),
|
||||
|
||||
targets: [].concat(userData.targets || []),
|
||||
|
@ -859,6 +867,7 @@ module.exports = (db, server, userHandler) => {
|
|||
* @apiParam {String} [pubKey] Public PGP key for the User that is used for encryption. Use empty string to remove the key
|
||||
* @apiParam {String} [language] Language code for the User
|
||||
* @apiParam {String[]} [targets] An array of forwarding targets. The value could either be an email address or a relay url to next MX server ("smtp://mx2.zone.eu:25") or an URL where mail contents are POSTed to
|
||||
* @apiParam {Number} [spamLevel] Relative scale for detecting spam. 0 means that everything is spam, 100 means that nothing is spam
|
||||
* @apiParam {Number} [quota] Allowed quota of the user in bytes
|
||||
* @apiParam {Number} [recipients] How many messages per 24 hour can be sent
|
||||
* @apiParam {Number} [forwards] How many messages per 24 hour can be forwarded
|
||||
|
@ -928,6 +937,10 @@ module.exports = (db, server, userHandler) => {
|
|||
})
|
||||
),
|
||||
|
||||
spamLevel: Joi.number()
|
||||
.min(0)
|
||||
.max(100),
|
||||
|
||||
pubKey: Joi.string()
|
||||
.empty('')
|
||||
.trim()
|
||||
|
|
77
lib/draft-handler.js
Normal file
77
lib/draft-handler.js
Normal file
|
@ -0,0 +1,77 @@
|
|||
'use strict';
|
||||
|
||||
// TODO: handle drafts
|
||||
|
||||
/*
|
||||
|
||||
{
|
||||
_id: "aaaaa",
|
||||
user: "bbbbb",
|
||||
|
||||
reference: 'message_id',
|
||||
action: 'reply',
|
||||
references: ['from-ref-message'],
|
||||
inReplyTo: ['from-ref-message'],
|
||||
|
||||
messageId: '<draft.id@local>',
|
||||
date: date_obj,
|
||||
|
||||
from: identity_ref,
|
||||
to: [{name, address}],
|
||||
cc: [{name, address}],
|
||||
bcc: [{name, address}],
|
||||
|
||||
subject: 'test',
|
||||
html: '<html>',
|
||||
|
||||
attachments: [
|
||||
{
|
||||
filename: 'aaa.jpg',
|
||||
contentType: 'image/jpeg',
|
||||
content: binary,
|
||||
cid: 'only.for@embedded.images'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
class DraftHandler {
|
||||
constructor(options) {
|
||||
this.database = options.database;
|
||||
this.redis = options.redis;
|
||||
}
|
||||
|
||||
// should create a new Draft object and return ID
|
||||
create(user, options, callback) {
|
||||
options = options || {};
|
||||
callback(new Error('Future feature'));
|
||||
}
|
||||
|
||||
// should retrieve draft info
|
||||
get(user, draft, callback) {
|
||||
callback(new Error('Future feature'));
|
||||
}
|
||||
|
||||
// should add new attachment to draft and return attachment ID
|
||||
addAttachment(user, draft, attachmentData, callback) {
|
||||
callback(new Error('Future feature'));
|
||||
}
|
||||
|
||||
// should delete an attachment from a draft
|
||||
deleteAttachment(user, draft, attachment, callback) {
|
||||
callback(new Error('Future feature'));
|
||||
}
|
||||
|
||||
// should submit message to queue and delete draft
|
||||
send(user, draft, envelope, callback) {
|
||||
callback(new Error('Future feature'));
|
||||
}
|
||||
|
||||
// should cancel the draft and delete contents
|
||||
discard(user, draft, callback) {
|
||||
callback(new Error('Future feature'));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DraftHandler;
|
|
@ -20,13 +20,13 @@ const defaultSpamHeaderKeys = [
|
|||
value: '^yes',
|
||||
target: '\\Junk'
|
||||
},
|
||||
|
||||
/*
|
||||
{
|
||||
key: 'X-Rspamd-Bar',
|
||||
value: '^\\+{6}',
|
||||
target: '\\Junk'
|
||||
},
|
||||
|
||||
*/
|
||||
{
|
||||
key: 'X-Haraka-Virus',
|
||||
value: '.',
|
||||
|
@ -34,6 +34,9 @@ const defaultSpamHeaderKeys = [
|
|||
}
|
||||
];
|
||||
|
||||
const spamScoreHeader = 'X-Rspamd-Score';
|
||||
const spamScoreValue = 15; // everything over this value is spam, under ham
|
||||
|
||||
class FilterHandler {
|
||||
constructor(options) {
|
||||
this.db = options.db;
|
||||
|
@ -84,7 +87,8 @@ class FilterHandler {
|
|||
autoreply: true,
|
||||
encryptMessages: true,
|
||||
encryptForwarded: true,
|
||||
pubKey: true
|
||||
pubKey: true,
|
||||
spamLevel: true
|
||||
};
|
||||
|
||||
if (collection === 'users') {
|
||||
|
@ -254,6 +258,8 @@ class FilterHandler {
|
|||
let matchingFilters = [];
|
||||
let filterActions = new Map();
|
||||
|
||||
let spamScore = parseFloat([].concat(prepared.mimeTree.parsedHeader[spamScoreHeader.toLowerCase()] || []).shift(), 10) || 0;
|
||||
|
||||
filters
|
||||
// apply all filters to the message
|
||||
.map(filter => checkFilter(filter, prepared, maildata))
|
||||
|
@ -279,6 +285,20 @@ class FilterHandler {
|
|||
});
|
||||
});
|
||||
|
||||
if (typeof userData.spamLevel === 'number' && userData.spamLevel >= 0 && !filterActions.has('spam')) {
|
||||
let isSpam;
|
||||
if (userData.spamLevel === 0) {
|
||||
isSpam = true;
|
||||
} else if (userData.spamLevel === 100) {
|
||||
isSpam = false;
|
||||
} else {
|
||||
isSpam = userData.spamLevel / 100 * spamScoreValue * 2 <= spamScore;
|
||||
}
|
||||
if (isSpam) {
|
||||
filterActions.set('spam', true);
|
||||
}
|
||||
}
|
||||
|
||||
let encryptMessage = (condition, next) => {
|
||||
if (!condition || isEncrypted) {
|
||||
return next();
|
||||
|
|
|
@ -878,6 +878,15 @@ class UserHandler {
|
|||
let hash = data.password ? bcrypt.hashSync(data.password, consts.BCRYPT_ROUNDS) : '';
|
||||
let id = new ObjectID();
|
||||
|
||||
// spamLevel is from 0 (everything is spam) to 100 (accept everything)
|
||||
let spamLevel = 'spamLevel' in data && !isNaN(data.spamLevel) ? Number(data.spamLevel) : 50;
|
||||
if (spamLevel < 0) {
|
||||
spamLevel = 0;
|
||||
}
|
||||
if (spamLevel > 100) {
|
||||
spamLevel = 100;
|
||||
}
|
||||
|
||||
userData = {
|
||||
_id: id,
|
||||
|
||||
|
@ -912,6 +921,8 @@ class UserHandler {
|
|||
encryptMessages: !!data.encryptMessages,
|
||||
encryptForwarded: !!data.encryptForwarded,
|
||||
|
||||
spamLevel,
|
||||
|
||||
// default retention for user mailboxes
|
||||
retention: data.retention || 0,
|
||||
|
||||
|
@ -2163,6 +2174,19 @@ class UserHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
if (key === 'spamLevel') {
|
||||
// spamLevel is from 0 (everything is spam) to 100 (accept everything)
|
||||
let spamLevel = !isNaN(data.spamLevel) ? Number(data.spamLevel) : 50;
|
||||
if (spamLevel < 0) {
|
||||
spamLevel = 0;
|
||||
}
|
||||
if (spamLevel > 100) {
|
||||
spamLevel = 100;
|
||||
}
|
||||
$set.spamLevel = data.spamLevel;
|
||||
return;
|
||||
}
|
||||
|
||||
$set[key] = data[key];
|
||||
updates = true;
|
||||
});
|
||||
|
|
3
lmtp.js
3
lmtp.js
|
@ -80,7 +80,8 @@ const serverOptions = {
|
|||
autoreply: true,
|
||||
encryptMessages: true,
|
||||
encryptForwarded: true,
|
||||
pubKey: true
|
||||
pubKey: true,
|
||||
spamLevel: true
|
||||
},
|
||||
(err, userData) => {
|
||||
if (err) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "wildduck",
|
||||
"version": "1.0.112",
|
||||
"version": "1.0.113",
|
||||
"description": "IMAP server built with Node.js and MongoDB",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
|
|
Loading…
Add table
Reference in a new issue