mirror of https://github.com/rspamd/rspamd.git
Rapid spam filtering system
https://rspamd.com/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1303 lines
52 KiB
1303 lines
52 KiB
/*
|
|
The MIT License (MIT)
|
|
|
|
Copyright (C) 2012-2013 Anton Simonov <untone@gmail.com>
|
|
Copyright (C) 2014-2015 Vsevolod Stakhov <vsevolod@highsecure.ru>
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
THE SOFTWARE.
|
|
*/
|
|
(function () {
|
|
$(document).ready(function () {
|
|
// begin
|
|
//$.cookie.json = true;
|
|
var pie;
|
|
var history;
|
|
var errors;
|
|
var graph;
|
|
var symbols;
|
|
var read_only = false;
|
|
var stat_timeout;
|
|
|
|
var selected = []; // Keep graph selectors state
|
|
|
|
// Bind event handlers to selectors
|
|
$("#selData").change(function () {
|
|
selected.selData = this.value;
|
|
getGraphData(this.value);
|
|
});
|
|
$("#selConvert").change(function () {
|
|
graph.convert(this.value);
|
|
});
|
|
$("#selType").change(function () {
|
|
graph.type(this.value);
|
|
});
|
|
$("#selInterpolate").change(function () {
|
|
graph.interpolate(this.value);
|
|
});
|
|
|
|
$('#disconnect').on('click', function (event) {
|
|
if (pie) {
|
|
pie.destroy();
|
|
}
|
|
if (graph) {
|
|
graph.destroy();
|
|
graph = undefined;
|
|
}
|
|
if (history) {
|
|
history.destroy();
|
|
}
|
|
if (errors) {
|
|
errors.destroy();
|
|
}
|
|
if (symbols) {
|
|
symbols.destroy();
|
|
symbols = null;
|
|
}
|
|
clearTimeout(stat_timeout);
|
|
cleanCredentials();
|
|
connectRSPAMD();
|
|
// window.location.reload();
|
|
return false;
|
|
});
|
|
$('#refresh').on('click', function (event) {
|
|
statWidgets();
|
|
getChart();
|
|
getGraphData(selected.selData);
|
|
});
|
|
// @supports session storage
|
|
function supportsSessionStorage() {
|
|
return typeof (Storage) !== "undefined";
|
|
}
|
|
// @return password
|
|
function getPassword() {
|
|
if (sessionState()) {
|
|
if (!supportsSessionStorage()) {
|
|
return password = $.cookie('rspamdpasswd');
|
|
} else {
|
|
return password = sessionStorage.getItem('Password');
|
|
}
|
|
}
|
|
}
|
|
// @return session state
|
|
function sessionState() {
|
|
if ((supportsSessionStorage() && (sessionStorage.getItem('Password') !== null)) ||
|
|
(!supportsSessionStorage() && ($.cookie('rspamdsession')) !== null)) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// @detect session storate
|
|
supportsSessionStorage();
|
|
// @save credentials
|
|
function saveCredentials(data, password) {
|
|
if (!supportsSessionStorage()) {
|
|
$.cookie('rspamdsession', data, {
|
|
expires: 1
|
|
}, {
|
|
path: '/'
|
|
});
|
|
$.cookie('rspamdpasswd', password, {
|
|
expires: 1
|
|
}, {
|
|
path: '/'
|
|
});
|
|
} else {
|
|
sessionStorage.setItem('Password', password);
|
|
sessionStorage.setItem('Credentials', JSON.stringify(data));
|
|
}
|
|
}
|
|
// @update credentials
|
|
function saveActions(data) {
|
|
if (!supportsSessionStorage()) {
|
|
$.cookie('rspamdactions', data);
|
|
} else {
|
|
sessionStorage.setItem('Actions', JSON.stringify(data));
|
|
}
|
|
}
|
|
// @update credentials
|
|
function saveMaps(data) {
|
|
if (!supportsSessionStorage()) {
|
|
$.cookie('rspamdmaps', data, {
|
|
expires: 1
|
|
}, {
|
|
path: '/'
|
|
});
|
|
} else {
|
|
sessionStorage.setItem('Maps', JSON.stringify(data));
|
|
}
|
|
}
|
|
// @clean credentials
|
|
function cleanCredentials() {
|
|
if (!supportsSessionStorage()) {
|
|
$.removeCookie('rspamdlogged');
|
|
$.removeCookie('rspamdsession');
|
|
$.removeCookie('rspamdpasswd');
|
|
} else {
|
|
sessionStorage.clear();
|
|
}
|
|
$('#statWidgets').empty();
|
|
$('#listMaps').empty();
|
|
$('#modalBody').empty();
|
|
$('#historyLog tbody').remove();
|
|
$('#errorsLog tbody').remove();
|
|
$('#symbolsTable tbody').remove();
|
|
password = '';
|
|
}
|
|
|
|
function isLogged() {
|
|
if (!supportsSessionStorage()) {
|
|
if ($.cookie('rspamdpasswd') != null) {
|
|
return true;
|
|
}
|
|
} else {
|
|
if (sessionStorage.getItem('Password') != null) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
// @alert popover
|
|
function alertMessage(alertState, alertText) {
|
|
if ($('.alert').is(':visible')) {
|
|
$(alert).hide().remove();
|
|
}
|
|
var alert = $('<div class="alert ' + alertState + '" style="display:none">' +
|
|
'<button type="button" class="close" data-dismiss="alert" tutle="Dismiss">×</button>' +
|
|
'<strong>' + alertText + '</strong>')
|
|
.prependTo('body');
|
|
$(alert).show();
|
|
setTimeout(function () {
|
|
$(alert).remove();
|
|
}, 3600);
|
|
}
|
|
// @get maps id
|
|
function getMaps() {
|
|
var items = [];
|
|
$('#listMaps').closest('.widget-box').hide();
|
|
$.ajax({
|
|
dataType: 'json',
|
|
url: 'maps',
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
error: function (data) {
|
|
alertMessage('alert-modal alert-error', data.statusText);
|
|
},
|
|
success: function (data) {
|
|
$('#listMaps').empty();
|
|
saveMaps(data);
|
|
getMapById();
|
|
$.each(data, function (i, item) {
|
|
var caption;
|
|
var label;
|
|
if ((item.editable == false || read_only)) {
|
|
caption = 'View';
|
|
label = '<span class="label label-default">Read</span>';
|
|
} else {
|
|
caption = 'Edit';
|
|
label = '<span class="label label-default">Read</span> <span class="label label-success">Write</span>';
|
|
}
|
|
items.push('<tr>' +
|
|
'<td class="col-md-2 maps-cell">' + label + '</td>' +
|
|
'<td>' +
|
|
'<span class="map-link" ' +
|
|
'data-source="#' + item.map + '" ' +
|
|
'data-editable="' + item.editable + '" ' +
|
|
'data-target="#modalDialog" ' +
|
|
'data-title="' + item.uri +
|
|
'" data-toggle="modal">' + item.uri + '</span>' +
|
|
'</td>' +
|
|
'<td>' +
|
|
item.description +
|
|
'</td>' +
|
|
'</tr>');
|
|
});
|
|
$('<tbody/>', {
|
|
html: items.join('')
|
|
}).appendTo('#listMaps');
|
|
$('#listMaps').closest('.widget-box').show();
|
|
}
|
|
});
|
|
}
|
|
// @get map by id
|
|
function getMapById(mode) {
|
|
var data;
|
|
if (!supportsSessionStorage()) {
|
|
data = $.cookie('rspamdmaps', data, {
|
|
expires: 1
|
|
}, {
|
|
path: '/'
|
|
});
|
|
} else {
|
|
data = JSON.parse(sessionStorage.getItem('Maps'));
|
|
}
|
|
if (mode === 'update') {
|
|
$('#modalBody').empty();
|
|
getMaps();
|
|
}
|
|
|
|
$.each(data, function (i, item) {
|
|
$.ajax({
|
|
dataType: 'text',
|
|
url: 'getmap',
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
xhr.setRequestHeader('Map', item.map);
|
|
},
|
|
error: function () {
|
|
alertMessage('alert-error', 'Cannot receive maps data');
|
|
},
|
|
success: function (text) {
|
|
var disabled = '';
|
|
if ((item.editable == false || read_only)) {
|
|
disabled = 'disabled="disabled"';
|
|
}
|
|
|
|
$('<form class="form-horizontal form-map" method="post "action="/savemap" data-type="map" id="' +
|
|
item.map + '" style="display:none">' +
|
|
'<textarea class="list-textarea"' + disabled + '>' + text +
|
|
'</textarea>' +
|
|
'</form').appendTo('#modalBody');
|
|
}
|
|
});
|
|
});
|
|
}
|
|
// @ ms to date
|
|
function msToTime(seconds) {
|
|
years = seconds / 31536000 >> 0 // 3600*24*365
|
|
months = seconds % 31536000 / 2628000 >> 0; //3600*24*365/12
|
|
days = seconds % 31536000 % 2628000 / 86400 >> 0; //24*3600
|
|
hours = seconds % 31536000 % 2628000 % 86400 / 3600 >> 0;
|
|
minutes = seconds % 31536000 % 2628000 % 86400 % 3600 / 60 >> 0;
|
|
if (years > 0) {
|
|
if (months > 0) {
|
|
out = years + 'yr ' + months + 'mth';
|
|
} else {
|
|
out = years + 'yr ' + days + 'd';
|
|
}
|
|
} else if (months > 0) {
|
|
out = months + 'mth ' + days + 'd';
|
|
} else if (days > 0) {
|
|
out = days + 'd ' + hours + 'hr';
|
|
} else if (hours > 0) {
|
|
out = hours + 'hr ' + minutes + 'min';
|
|
} else {
|
|
out = minutes + 'min';
|
|
}
|
|
return out;
|
|
}
|
|
// @show widgets
|
|
function statWidgets() {
|
|
$.ajax({
|
|
dataType: 'json',
|
|
type: 'GET',
|
|
url: 'auth',
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function (data) {
|
|
if (!supportsSessionStorage()) {
|
|
$.cookie('rspamdsession', data, {
|
|
expires: 1
|
|
}, {
|
|
path: '/'
|
|
});
|
|
} else {
|
|
sessionStorage.setItem('Credentials', JSON.stringify(data));
|
|
}
|
|
}
|
|
});
|
|
var widgets = $('#statWidgets');
|
|
$(widgets).empty().hide();
|
|
var data;
|
|
if (!supportsSessionStorage()) {
|
|
data = $.cookie('rspamdsession');
|
|
} else {
|
|
data = JSON.parse(sessionStorage.getItem('Credentials'));
|
|
}
|
|
var stat_w = [];
|
|
$.each(data, function (i, item) {
|
|
var widget = '';
|
|
if (i == 'auth') {}
|
|
else if (i == 'error') {}
|
|
else if (i == 'version') {
|
|
widget = '<div class="left"><strong>' + item + '</strong>' +
|
|
i + '</div>';
|
|
$(widget).appendTo(widgets);
|
|
} else if (i == 'uptime') {
|
|
widget = '<div class="right"><strong>' + msToTime(item) +
|
|
'</strong>' + i + '</div>';
|
|
$(widget).appendTo(widgets);
|
|
} else {
|
|
widget = '<li class="stat-box"><div class="widget"><strong>' +
|
|
Humanize.compactInteger(item) + '</strong>' + i + '</div></li>';
|
|
if (i == 'scanned') {
|
|
stat_w[0] = widget;
|
|
} else if (i == 'clean') {
|
|
stat_w[1] = widget;
|
|
} else if (i == 'greylist') {
|
|
stat_w[2] = widget;
|
|
} else if (i == 'probable') {
|
|
stat_w[3] = widget;
|
|
} else if (i == 'reject') {
|
|
stat_w[4] = widget;
|
|
} else if (i == 'learned') {
|
|
stat_w[5] = widget;
|
|
}
|
|
}
|
|
});
|
|
$.each(stat_w, function (i, item) {
|
|
$(item).appendTo(widgets);
|
|
});
|
|
$('#statWidgets .left,#statWidgets .right').wrapAll('<li class="stat-box pull-right"><div class="widget"></div></li>');
|
|
$('#statWidgets').find('li.pull-right').appendTo('#statWidgets');
|
|
$(widgets).show();
|
|
stat_timeout = window.setTimeout(statWidgets, 10000);
|
|
}
|
|
// @opem modal with target form enabled
|
|
$(document).on('click', '[data-toggle="modal"]', function (e) {
|
|
var source = $(this).data('source');
|
|
var editable = $(this).data('editable');
|
|
var title = $(this).data('title');
|
|
var caption = $('#modalTitle').html(title);
|
|
var body = $('#modalBody ' + source).show();
|
|
var target = $(this).data('target');
|
|
var progress = $(target + ' .progress').hide();
|
|
$(target).modal(show = true, backdrop = true, keyboard = show);
|
|
if (editable === false) {
|
|
$('#modalSave').hide();
|
|
} else {
|
|
$('#modalSave').show();
|
|
}
|
|
return false;
|
|
});
|
|
// close modal without saving
|
|
$(document).on('click', '[data-dismiss="modal"]', function (e) {
|
|
$('#modalBody form').hide();
|
|
});
|
|
$(document).on('click', '', function (e) {
|
|
if (event.target == document.getElementById('modalDialog')) {
|
|
$('#modalBody form').hide();
|
|
}
|
|
});
|
|
function getChart() {
|
|
$.ajax({
|
|
dataType: 'json',
|
|
type: 'GET',
|
|
url: 'pie',
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function (data) {
|
|
if (pie) {
|
|
pie.destroy();
|
|
}
|
|
pie = new d3pie("chart", {
|
|
"header": {
|
|
"title": {
|
|
"text": "Rspamd filter stats",
|
|
"fontSize": 24,
|
|
"font": "open sans"
|
|
},
|
|
"subtitle": {
|
|
"color": "#999999",
|
|
"fontSize": 12,
|
|
"font": "open sans"
|
|
},
|
|
"titleSubtitlePadding": 9
|
|
},
|
|
"footer": {
|
|
"color": "#999999",
|
|
"fontSize": 10,
|
|
"font": "open sans",
|
|
"location": "bottom-left"
|
|
},
|
|
"size": {
|
|
"canvasWidth": 600,
|
|
"canvasHeight": 400,
|
|
"pieInnerRadius": "20%",
|
|
"pieOuterRadius": "85%"
|
|
},
|
|
"data": {
|
|
//"sortOrder": "value-desc",
|
|
"content": data.filter(function (elt) {
|
|
return elt.value > 0;
|
|
})
|
|
},
|
|
"labels": {
|
|
"outer": {
|
|
"hideWhenLessThanPercentage": 1,
|
|
"pieDistance": 30
|
|
},
|
|
"inner": {
|
|
"hideWhenLessThanPercentage": 4
|
|
},
|
|
"mainLabel": {
|
|
"fontSize": 14
|
|
},
|
|
"percentage": {
|
|
"color": "#eeeeee",
|
|
"fontSize": 14,
|
|
"decimalPlaces": 0
|
|
},
|
|
"lines": {
|
|
"enabled": true
|
|
},
|
|
"truncation": {
|
|
"enabled": true
|
|
}
|
|
},
|
|
"tooltips": {
|
|
"enabled": true,
|
|
"type": "placeholder",
|
|
"string": "{label}: {value}, {percentage}%"
|
|
},
|
|
"effects": {
|
|
"pullOutSegmentOnClick": {
|
|
"effect": "back",
|
|
"speed": 400,
|
|
"size": 8
|
|
},
|
|
"load": {
|
|
"speed": 500
|
|
}
|
|
},
|
|
"misc": {
|
|
"gradient": {
|
|
"enabled": true,
|
|
"percentage": 100
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function initGraph() {
|
|
// Get selectors' current state
|
|
var selIds = ["selData", "selConvert", "selType", "selInterpolate"];
|
|
selIds.forEach(function (id) {
|
|
var e = document.getElementById(id);
|
|
selected[id] = e.options[e.selectedIndex].value;
|
|
});
|
|
|
|
var options = {
|
|
title: "Rspamd throughput",
|
|
width: 1060,
|
|
height: 370,
|
|
yAxisLabel: "Message rate, msg/s",
|
|
|
|
type: selected.selType,
|
|
interpolate: selected.selInterpolate,
|
|
convert: selected.selConvert,
|
|
|
|
legend: {
|
|
entries: [{
|
|
label: "Rejected",
|
|
color: "#FF0000"
|
|
}, {
|
|
label: "Probable spam",
|
|
color: "#FFD700"
|
|
}, {
|
|
label: "Greylisted",
|
|
color: "#436EEE"
|
|
}, {
|
|
label: "Clean",
|
|
color: "#66cc00"
|
|
}]
|
|
}
|
|
};
|
|
graph = new D3Evolution("graph", options);
|
|
}
|
|
|
|
function getGraphData(type) {
|
|
$.ajax({
|
|
dataType: 'json',
|
|
type: 'GET',
|
|
url: 'graph',
|
|
jsonp: false,
|
|
data: {
|
|
"type": type
|
|
},
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function (data) {
|
|
graph.data(data);
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alertMessage('alert-error', 'Cannot receive throughput data: ' +
|
|
textStatus + ' ' + jqXHR.status + ' ' + errorThrown);
|
|
}
|
|
});
|
|
}
|
|
|
|
// @get history log
|
|
// function getChart() {
|
|
// //console.log(data)
|
|
// $.ajax({
|
|
// dataType: 'json',
|
|
// url: './pie',
|
|
// beforeSend: function(xhr) {
|
|
// xhr.setRequestHeader('Password', getPassword())
|
|
// },
|
|
// error: function() {
|
|
// alertMessage('alert-error', 'Cannot receive history');
|
|
// },
|
|
// success: function(data) {
|
|
// console.log(data);
|
|
// }
|
|
// });
|
|
// }
|
|
// @get history log
|
|
function getHistory() {
|
|
|
|
if (history) {
|
|
var history_length = document.getElementsByName('historyLog_length')[0];
|
|
if (history_length !== undefined) {
|
|
history_length = parseInt(history_length.value);
|
|
} else {
|
|
history_length = 10;
|
|
}
|
|
history.destroy();
|
|
$('#historyLog').children('tbody').remove();
|
|
}
|
|
|
|
var items = [];
|
|
$.ajax({
|
|
dataType: 'json',
|
|
url: 'history',
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
error: function () {
|
|
alertMessage('alert-error', 'Cannot receive history');
|
|
},
|
|
success: function (data) {
|
|
$.each(data, function (i, item) {
|
|
var action;
|
|
|
|
if (item.action === 'clean' || item.action === 'no action') {
|
|
action = 'label-success';
|
|
} else if (item.action === 'rewrite subject' || item.action === 'add header' || item.action === 'probable spam') {
|
|
action = 'label-warning';
|
|
} else if (item.action === 'spam' || item.action === 'reject') {
|
|
action = 'label-danger';
|
|
} else {
|
|
action = 'label-info';
|
|
}
|
|
|
|
var score;
|
|
if (item.score < item.required_score) {
|
|
score = 'label-success';
|
|
} else {
|
|
score = 'label-danger';
|
|
}
|
|
|
|
items.push(
|
|
'<tr><td data-order="' + item.unix_time + '">' + item.time + '</td>' +
|
|
'<td data-order="' + item.id + '"><div class="cell-overflow" tabindex="1" title="' + item.id + '">' + item.id + '</div></td>' +
|
|
'<td data-order="' + item.ip + '"><div class="cell-overflow" tabindex="1" title="' + item.ip + '">' + item.ip + '</div></td>' +
|
|
'<td data-order="' + item.action + '"><span class="label ' + action + '">' + item.action + '</span></td>' +
|
|
'<td data-order="' + item.score + '"><span class="label ' + score + '">' + item.score.toFixed(2) + ' / ' + item.required_score.toFixed(2) + '</span></td>' +
|
|
'<td data-order="' + item.symbols + '"><div class="cell-overflow" tabindex="1" title="' + item.symbols + '">' + item.symbols + '</div></td>' +
|
|
'<td data-order="' + item.size + '">' + item.size + '</td>' +
|
|
'<td data-order="' + item.scan_time + '">' + item.scan_time.toFixed(3) + '</td>' +
|
|
'<td data-order="' + item.user + '"><div class="cell-overflow" tabindex="1" "title="' + item.user + '">' + item.user + '</div></td></tr>');
|
|
});
|
|
$('<tbody/>', {
|
|
html: items.join('')
|
|
}).insertAfter('#historyLog thead');
|
|
history = $('#historyLog').DataTable({
|
|
"aLengthMenu": [
|
|
[100, 200, -1],
|
|
[100, 200, "All"]
|
|
],
|
|
"bStateSave": true,
|
|
"order": [
|
|
[0, "desc"]
|
|
],
|
|
"pageLength": history_length
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function getErrors() {
|
|
if (read_only) return;
|
|
|
|
if (errors) {
|
|
errors.destroy();
|
|
$('#errorsLog').children('tbody').remove();
|
|
}
|
|
|
|
var items = [];
|
|
$.ajax({
|
|
dataType: 'json',
|
|
url: 'errors',
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
error: function () {
|
|
alertMessage('alert-error', 'Cannot receive errors');
|
|
},
|
|
success: function (data) {
|
|
$.each(data, function (i, item) {
|
|
|
|
items.push(
|
|
'<tr><td data-order="' + item.ts + '">' + new Date(item.ts * 1000) + '</td>' +
|
|
'<td data-order="' + item.type + '">' + item.type + '</td>' +
|
|
'<td data-order="' + item.pid + '">' + item.pid + '</td>' +
|
|
'<td data-order="' + item.module + '">' + item.module + '</td>' +
|
|
'<td data-order="' + item.id + '">' + item.id + '</td>' +
|
|
'<td data-order="' + item.message + '"><div class="cell-overflow" tabindex="1" title="' + item.message + '">' + item.message + '</div></td></tr>'
|
|
);
|
|
});
|
|
$('<tbody/>', {
|
|
html: items.join('')
|
|
}).insertAfter('#errorsLog thead');
|
|
errors = $('#errorsLog').DataTable({
|
|
"paging": true,
|
|
"orderMulti": false,
|
|
"order": [
|
|
[0, "desc"],
|
|
],
|
|
"info": false,
|
|
"columns": [
|
|
{"width": "15%", "searchable": true, "orderable": true, "type": "num"},
|
|
{"width": "5%", "searchable": true, "orderable": true},
|
|
{"width": "5%", "searchable": true, "orderable": true},
|
|
{"width": "3%", "searchable": true, "orderable": true},
|
|
{"width": "3%", "searchable": true, "orderable": true},
|
|
{"width": "65%", "searchable": true, "orderable": true},
|
|
],
|
|
});
|
|
errors.columns.adjust().draw();
|
|
}
|
|
});
|
|
}
|
|
|
|
function decimalStep(number) {
|
|
var digits = ((+number).toFixed(20)).replace(/^-?\d*\.?|0+$/g, '').length;
|
|
if (digits == 0 || digits > 4) {
|
|
return 0.1;
|
|
} else {
|
|
return 1.0 / (Math.pow(10, digits));
|
|
}
|
|
}
|
|
// @get symbols into modal form
|
|
function getSymbols() {
|
|
if (symbols) {
|
|
symbols.destroy();
|
|
symbols = null;
|
|
$('#symbolsTable').children('tbody').remove();
|
|
}
|
|
var items = [];
|
|
$.ajax({
|
|
dataType: 'json',
|
|
type: 'GET',
|
|
url: 'symbols',
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function (data) {
|
|
$.each(data, function (i, group) {
|
|
$.each(group.rules, function (i, item) {
|
|
var max = 20;
|
|
var min = -20;
|
|
if (item.weight > max) {
|
|
max = item.weight * 2;
|
|
}
|
|
if (item.weight < min) {
|
|
min = item.weight * 2;
|
|
}
|
|
var label;
|
|
if (item.weight < 0) {
|
|
label_class = 'scorebar-ham';
|
|
} else {
|
|
label_class = 'scorebar-spam';
|
|
}
|
|
|
|
if (!item.time) {
|
|
item.time = 0;
|
|
}
|
|
if (!item.frequency) {
|
|
item.frequency = 0;
|
|
}
|
|
items.push('<tr>' +
|
|
'<td data-order="' + group.group + '"><div class="cell-overflow" tabindex="1" title="' + group.group + '">' + group.group + '</div></td>' +
|
|
'<td data-order="' + item.symbol + '"><strong>' + item.symbol + '</strong></td>' +
|
|
'<td data-order="' + item.description + '"><div class="cell-overflow" tabindex="1" title="' + item.description + '">' + item.description + '</div></td>' +
|
|
'<td data-order="' + item.weight + '"><input class="numeric mb-disabled ' + label_class +
|
|
'" data-role="numerictextbox" autocomplete="off" "type="number" class="input" min="' +
|
|
min + '" max="' +
|
|
max + '" step="' + decimalStep(item.weight) +
|
|
'" tabindex="1" value="' + Number(item.weight).toFixed(3) +
|
|
'" id="_sym_' + item.symbol + '"></span></td>' +
|
|
'<td data-order="' + item.frequency + '">' + item.frequency + '</td>' +
|
|
'<td data-order="' + item.time + '">' + Number(item.time).toFixed(2) + 'ms</td>' +
|
|
'<td><button type="button" class="btn btn-primary btn-sm mb-disabled">Save</button></td></tr>');
|
|
});
|
|
});
|
|
$('<tbody/>', {
|
|
html: items.join('')
|
|
}).insertAfter('#symbolsTable thead');
|
|
symbols = $('#symbolsTable').DataTable({
|
|
"paging": false,
|
|
"orderMulti": true,
|
|
"order": [
|
|
[0, "asc"],
|
|
[1, "asc"],
|
|
[3, "desc"]
|
|
],
|
|
"info": false,
|
|
"columns": [
|
|
{"width": "7%", "searchable": true, "orderable": true},
|
|
{"width": "20%", "searchable": true, "orderable": true},
|
|
{"width": "30%", "searchable": false, "orderable": false},
|
|
{"width": "7%", "searchable": false, "orderable": true, "type": "num"},
|
|
{"searchable": false, "orderable": true, "type": "num"},
|
|
{"searchable": false, "orderable": true, "type": "num"},
|
|
{"width": "5%", "searchable": false, "orderable": false, "type": "html"}
|
|
],
|
|
});
|
|
symbols.columns.adjust().draw();
|
|
$('#symbolsTable :button').on('click',
|
|
function(){saveSymbols("./savesymbols", "symbolsTable")});
|
|
if (read_only) {
|
|
$( ".mb-disabled" ).attr('disabled', true);
|
|
}
|
|
},
|
|
error: function (data) {
|
|
alertMessage('alert-modal alert-error', data.statusText);
|
|
}
|
|
});
|
|
}
|
|
// @reset history log
|
|
$('#resetHistory').on('click', function () {
|
|
if (!confirm("Are you sure you want to reset history log?")) {
|
|
return
|
|
};
|
|
if (history) {
|
|
history.destroy();
|
|
$('#historyLog').children('tbody').remove();
|
|
}
|
|
$.ajax({
|
|
dataType: 'json',
|
|
type: 'GET',
|
|
jsonp: false,
|
|
url: 'historyreset',
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function (data) {
|
|
getHistory();
|
|
getErrors();
|
|
},
|
|
error: function (data) {
|
|
alertMessage('alert-modal alert-error', data.statusText);
|
|
}
|
|
});
|
|
});
|
|
|
|
// @update history log
|
|
$('#updateHistory').on('click', function () {
|
|
getHistory();
|
|
});
|
|
$('#updateErrors').on('click', function () {
|
|
getErrors();
|
|
});
|
|
|
|
$('#updateSymbols').on('click', function () {
|
|
getSymbols();
|
|
});
|
|
|
|
// @upload text
|
|
function uploadText(data, source, headers) {
|
|
if (source === 'spam') {
|
|
var url = 'learnspam';
|
|
} else if (source === 'ham') {
|
|
var url = 'learnham';
|
|
} else if (source == 'fuzzy') {
|
|
var url = 'fuzzyadd';
|
|
} else if (source === 'scan') {
|
|
var url = 'scan';
|
|
}
|
|
$.ajax({
|
|
data: data,
|
|
dataType: 'json',
|
|
type: 'POST',
|
|
url: url,
|
|
processData: false,
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
$.each(headers, function (name, value) {
|
|
xhr.setRequestHeader(name, value);
|
|
});
|
|
},
|
|
success: function (data) {
|
|
cleanTextUpload(source);
|
|
if (data.success) {
|
|
alertMessage('alert-success', 'Data successfully uploaded');
|
|
}
|
|
},
|
|
error: function (xhr, textStatus, errorThrown) {
|
|
try {
|
|
var json = $.parseJSON(xhr.responseText);
|
|
var errorMsg = $('<a>').text(json.error).html();
|
|
} catch (err) {
|
|
var errorMsg = $('<a>').text("Error: [" + textStatus + "] " + errorThrown).html();
|
|
}
|
|
alertMessage('alert-error', errorMsg);
|
|
}
|
|
});
|
|
}
|
|
// @upload text
|
|
function scanText(data) {
|
|
var url = 'scan';
|
|
var items = [];
|
|
$.ajax({
|
|
data: data,
|
|
dataType: 'json',
|
|
type: 'POST',
|
|
url: url,
|
|
processData: false,
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function (input) {
|
|
var data = input['default'];
|
|
if (data.action) {
|
|
alertMessage('alert-success', 'Data successfully scanned');
|
|
if (data.action === 'clean' || 'no action') {
|
|
var action = 'label-success';
|
|
}
|
|
if (data.action === 'rewrite subject' || 'add header' || 'probable spam') {
|
|
var action = 'label-warning';
|
|
}
|
|
if (data.action === 'spam') {
|
|
var action = 'label-danger';
|
|
}
|
|
if (data.score <= data.required_score) {
|
|
var score = 'label-success';
|
|
}
|
|
if (data.score >= data.required_score) {
|
|
var score = 'label-danger';
|
|
}
|
|
$('<tbody id="tmpBody"><tr>' +
|
|
'<td><span class="label ' + action + '">' + data.action + '</span></td>' +
|
|
'<td><span class="label ' + score + '">' + data.score.toFixed(2) + '/' + data.required_score.toFixed(2) + '</span></td>' +
|
|
'</tr></tbody>')
|
|
.insertAfter('#scanOutput thead');
|
|
var sym_desc = {};
|
|
var nsym = 0;
|
|
|
|
$.each(data, function (i, item) {
|
|
if (typeof item == 'object') {
|
|
var sym_id = "sym_" + nsym;
|
|
if (item.description) {
|
|
sym_desc[sym_id] = item.description;
|
|
}
|
|
items.push('<div class="cell-overflow" tabindex="1"><abbr id="' + sym_id +
|
|
'">' + item.name + '</abbr>: ' + item.score.toFixed(2) + '</div>');
|
|
nsym++;
|
|
}
|
|
});
|
|
$('<td/>', {
|
|
id: 'tmpSymbols',
|
|
html: items.join('')
|
|
}).appendTo('#scanResult');
|
|
$('#tmpSymbols').insertAfter('#tmpBody td:last').removeAttr('id');
|
|
$('#tmpBody').removeAttr('id');
|
|
$('#scanResult').show();
|
|
// Show tooltips
|
|
$.each(sym_desc, function (k, v) {
|
|
$('#' + k).tooltip({
|
|
"placement": "bottom",
|
|
"title": v
|
|
});
|
|
});
|
|
$('html, body').animate({
|
|
scrollTop: $('#scanResult').offset().top
|
|
}, 1000);
|
|
} else {
|
|
alertMessage('alert-error', 'Cannot scan data');
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alertMessage('alert-error', 'Cannot upload data: ' +
|
|
textStatus + ", " + errorThrown);
|
|
},
|
|
statusCode: {
|
|
404: function () {
|
|
alertMessage('alert-error', 'Cannot upload data, no server found');
|
|
},
|
|
500: function () {
|
|
alertMessage('alert-error', 'Cannot tokenize message: no text data');
|
|
},
|
|
503: function () {
|
|
alertMessage('alert-error', 'Cannot tokenize message: no text data');
|
|
}
|
|
}
|
|
});
|
|
}
|
|
// @close scan output
|
|
$('#scanClean').on('click', function () {
|
|
$('#scanTextSource').val("");
|
|
$('#scanResult').hide();
|
|
$('#scanOutput tbody').remove();
|
|
$('html, body').animate({scrollTop: 0}, 1000);
|
|
return false;
|
|
});
|
|
// @init upload
|
|
$('[data-upload]').on('click', function () {
|
|
var source = $(this).data('upload');
|
|
var data;
|
|
var headers = {};
|
|
data = $('#' + source + 'TextSource').val();
|
|
if (source == 'fuzzy') {
|
|
//To access the proper
|
|
headers.flag = $('#fuzzyFlagText').val();
|
|
headers.weigth = $('#fuzzyWeightText').val();
|
|
} else {
|
|
data = $('#' + source + 'TextSource').val();
|
|
}
|
|
if (data.length > 0) {
|
|
if (source == 'scan') {
|
|
scanText(data);
|
|
} else {
|
|
uploadText(data, source, headers);
|
|
}
|
|
}
|
|
return false;
|
|
});
|
|
// @empty textarea on upload complete
|
|
function cleanTextUpload(source) {
|
|
$('#' + source + 'TextSource').val('');
|
|
}
|
|
// @get acions
|
|
function getActions() {
|
|
var items = [];
|
|
$.ajax({
|
|
dataType: 'json',
|
|
type: 'GET',
|
|
url: 'actions',
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function (data) {
|
|
// Order of sliders greylist -> probable spam -> spam
|
|
$('#actionsBody').empty();
|
|
$('#actionsForm').empty();
|
|
var items = [];
|
|
var min = 0;
|
|
var max = Number.MIN_VALUE;
|
|
$.each(data, function (i, item) {
|
|
var idx = -1;
|
|
var label;
|
|
if (item.action === 'add header') {
|
|
label = 'Probably Spam';
|
|
idx = 1;
|
|
} else if (item.action === 'greylist') {
|
|
label = 'Greylist';
|
|
idx = 0;
|
|
} else if (item.action === 'rewrite subject') {
|
|
label = 'Rewrite subject';
|
|
idx = 2;
|
|
} else if (item.action === 'reject') {
|
|
label = 'Spam';
|
|
idx = 3;
|
|
}
|
|
if (idx >= 0) {
|
|
items.push({
|
|
idx: idx,
|
|
html: '<div class="form-group">' +
|
|
'<label class="control-label col-sm-2">' + label + '</label>' +
|
|
'<div class="controls slider-controls col-sm-10">' +
|
|
'<input class="slider" type="slider" value="' + item.value + '">' +
|
|
'</div>' +
|
|
'</div>'
|
|
});
|
|
}
|
|
if (item.value > max) {
|
|
max = item.value * 2;
|
|
}
|
|
if (item.value < min) {
|
|
min = item.value;
|
|
}
|
|
});
|
|
|
|
items.sort(function (a, b) {
|
|
return a.idx - b.idx;
|
|
});
|
|
|
|
$('#actionsBody').html('<form id="actionsForm"><fieldset id="actionsFormField">' +
|
|
items.map(function (e) {
|
|
return e.html;
|
|
}).join('') +
|
|
'<br><div class="form-group">' +
|
|
'<button class="btn btn-primary" type="submit">Save actions</button></div></fieldset></form>');
|
|
if (read_only) {
|
|
$('#actionsFormField').attr('disabled', true)
|
|
}
|
|
}
|
|
});
|
|
}
|
|
// @upload edited actions
|
|
$(document).on('submit', '#actionsForm', function () {
|
|
var inputs = $('#actionsForm :input[type="slider"]');
|
|
var url = 'saveactions';
|
|
var values = [];
|
|
// Rspamd order: [spam,probable_spam,greylist]
|
|
values[0] = parseFloat(inputs[2].value);
|
|
values[1] = parseFloat(inputs[1].value);
|
|
values[2] = parseFloat(inputs[0].value);
|
|
$.ajax({
|
|
data: JSON.stringify(values),
|
|
dataType: 'json',
|
|
type: 'POST',
|
|
url: url,
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function () {
|
|
alertMessage('alert-success', 'Actions successfully saved');
|
|
},
|
|
error: function (data) {
|
|
alertMessage('alert-modal alert-error', data.statusText);
|
|
}
|
|
});
|
|
return false;
|
|
});
|
|
// @catch changes of file upload form
|
|
$(window).resize(function (e) {
|
|
var form = $(this).attr('id');
|
|
var height = $(form).height();
|
|
});
|
|
// @watch textarea changes
|
|
$('textarea').change(function () {
|
|
if ($(this).val().length != '') {
|
|
$(this).closest('form').find('button').removeAttr('disabled').removeClass('disabled');
|
|
} else {
|
|
$(this).closest('form').find('button').attr('disabled').addClass('disabled');
|
|
}
|
|
});
|
|
// @save forms from modal
|
|
$(document).on('click', '#modalSave', function () {
|
|
var form = $('#modalBody').children().filter(':visible');
|
|
// var map = $(form).data('map');
|
|
// var type = $(form).data('type');
|
|
var action = $(form).attr('action');
|
|
var id = $(form).attr('id');
|
|
var type = $(form).data('type');
|
|
if (type === 'symbols') {
|
|
saveSymbols(action, id);
|
|
} else if (type === 'map') {
|
|
saveMap(action, id);
|
|
}
|
|
});
|
|
// @upload map from modal
|
|
function saveMap(action, id) {
|
|
var data = $('#' + id).find('textarea').val();
|
|
$.ajax({
|
|
data: data,
|
|
dataType: 'text',
|
|
type: 'POST',
|
|
jsonp: false,
|
|
url: action,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
xhr.setRequestHeader('Map', id);
|
|
xhr.setRequestHeader('Debug', true);
|
|
},
|
|
error: function (data) {
|
|
alertMessage('alert-modal alert-error', data.statusText);
|
|
},
|
|
success: function (data) {
|
|
alertMessage('alert-modal alert-success', 'Map data successfully saved');
|
|
$('#modalDialog').modal('hide');
|
|
}
|
|
});
|
|
}
|
|
// @upload symbols from modal
|
|
function saveSymbols(action, id) {
|
|
var inputs = $('#' + id + ' :input[data-role="numerictextbox"]');
|
|
var url = action;
|
|
var values = [];
|
|
$(inputs).each(function () {
|
|
values.push({
|
|
name: $(this).attr('id').substring(5),
|
|
value: parseFloat($(this).val())
|
|
});
|
|
});
|
|
$.ajax({
|
|
data: JSON.stringify(values),
|
|
dataType: 'json',
|
|
type: 'POST',
|
|
url: url,
|
|
jsonp: false,
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', getPassword());
|
|
},
|
|
success: function () {
|
|
alertMessage('alert-modal alert-success', 'Symbols successfully saved');
|
|
},
|
|
error: function (data) {
|
|
alertMessage('alert-modal alert-error', data.statusText);
|
|
}
|
|
});
|
|
$('#modalDialog').modal('hide');
|
|
return false;
|
|
}
|
|
// @connect to server
|
|
function connectRSPAMD() {
|
|
if (isLogged()) {
|
|
var data;
|
|
if (!supportsSessionStorage()) {
|
|
data = $.cookie('rspamdsession');
|
|
} else {
|
|
data = JSON.parse(sessionStorage.getItem('Credentials'));
|
|
}
|
|
if (data.read_only) {
|
|
read_only = true;
|
|
$('#learning_nav').hide();
|
|
$('#resetHistory').attr('disabled', true);
|
|
$('#errors-history').hide();
|
|
}
|
|
else {
|
|
read_only = false;
|
|
$('#learning_nav').show();
|
|
$('#resetHistory').removeAttr('disabled', true);
|
|
}
|
|
displayUI();
|
|
return;
|
|
}
|
|
var nav = $('#navBar');
|
|
var ui = $('#mainUI');
|
|
var dialog = $('#connectDialog');
|
|
var backdrop = $('#backDrop');
|
|
var disconnect = $('#navBar .pull-right');
|
|
$(ui).hide();
|
|
$(dialog).show();
|
|
$('#connectHost').focus();
|
|
$(backdrop).show();
|
|
document.getElementById("connectPassword").focus();
|
|
$('#connectForm').one('submit', function (e) {
|
|
e.preventDefault();
|
|
var password = $('#connectPassword').val();
|
|
document.getElementById('connectPassword').value = '';
|
|
$.ajax({
|
|
global: false,
|
|
jsonp: false,
|
|
dataType: 'json',
|
|
type: 'GET',
|
|
url: 'auth',
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('Password', password);
|
|
},
|
|
success: function (data) {
|
|
if (data.auth === 'failed') {
|
|
$(form).each(function () {
|
|
$('.form-group').addClass('error');
|
|
});
|
|
} else {
|
|
if (data.read_only) {
|
|
read_only = true;
|
|
$('#learning_nav').hide();
|
|
$('#resetHistory').attr('disabled', true);
|
|
$('#errors-history').hide();
|
|
}
|
|
else {
|
|
read_only = false;
|
|
$('#learning_nav').show();
|
|
$('#resetHistory').removeAttr('disabled', true);
|
|
}
|
|
|
|
saveCredentials(data, password);
|
|
$(dialog).hide();
|
|
$(backdrop).hide();
|
|
displayUI();
|
|
}
|
|
},
|
|
error: function (data) {
|
|
alertMessage('alert-modal alert-error', data.statusText);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function displayUI() {
|
|
// @toggle auth and main
|
|
var disconnect = $('#navBar .pull-right');
|
|
statWidgets();
|
|
$('#mainUI').show();
|
|
$('#progress').show();
|
|
|
|
getChart();
|
|
initGraph();
|
|
if ($("#throughput_nav").attr("aria-expanded") === "true") {
|
|
getGraphData(selected.selData);
|
|
}
|
|
|
|
$('#progress').hide();
|
|
$(disconnect).show();
|
|
}
|
|
|
|
connectRSPAMD();
|
|
|
|
$('#configuration_nav').bind('click', function (e) {
|
|
getActions();
|
|
$('#modalBody').empty();
|
|
getMaps();
|
|
});
|
|
|
|
$(document).ajaxStart(function () {
|
|
$('#navBar').addClass('loading');
|
|
});
|
|
$(document).ajaxComplete(function () {
|
|
$('#navBar').removeClass('loading');
|
|
});
|
|
$('#status_nav').bind('click', function (e) {
|
|
getChart();
|
|
});
|
|
$('#throughput_nav').bind('click', function () {
|
|
getGraphData(selected.selData);
|
|
});
|
|
$('#history_nav').bind('click', function () {
|
|
getHistory();
|
|
getErrors();
|
|
});
|
|
$('#symbols_nav').bind('click', function () {
|
|
getSymbols();
|
|
});
|
|
});
|
|
})();
|