summaryrefslogtreecommitdiffstats
path: root/interface/js/app/rspamd.js
diff options
context:
space:
mode:
Diffstat (limited to 'interface/js/app/rspamd.js')
-rw-r--r--interface/js/app/rspamd.js486
1 files changed, 486 insertions, 0 deletions
diff --git a/interface/js/app/rspamd.js b/interface/js/app/rspamd.js
new file mode 100644
index 0000000..938f048
--- /dev/null
+++ b/interface/js/app/rspamd.js
@@ -0,0 +1,486 @@
+/*
+ The MIT License (MIT)
+
+ Copyright (C) 2012-2013 Anton Simonov <untone@gmail.com>
+ Copyright (C) 2014-2017 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.
+ */
+
+/* global require, Visibility */
+
+define(["jquery", "app/common", "stickytabs", "visibility",
+ "bootstrap", "fontawesome"],
+($, common) => {
+ "use strict";
+ const ui = {};
+
+ const defaultAjaxTimeout = 20000;
+
+ const ajaxTimeoutBox = ".popover #settings-popover #ajax-timeout";
+ const graphs = {};
+ let checked_server = "All SERVERS";
+ const timer_id = [];
+
+ function ajaxSetup(ajax_timeout, setFieldValue, saveToLocalStorage) {
+ const timeout = (ajax_timeout && ajax_timeout >= 0) ? ajax_timeout : defaultAjaxTimeout;
+ if (saveToLocalStorage) localStorage.setItem("ajax_timeout", timeout);
+ if (setFieldValue) $(ajaxTimeoutBox).val(timeout);
+
+ $.ajaxSetup({
+ timeout: timeout,
+ jsonp: false
+ });
+ }
+
+ function cleanCredentials() {
+ sessionStorage.clear();
+ $("#statWidgets").empty();
+ $("#listMaps").empty();
+ $("#modalBody").empty();
+ }
+
+ function stopTimers() {
+ for (const key in timer_id) {
+ if (!{}.hasOwnProperty.call(timer_id, key)) continue;
+ Visibility.stop(timer_id[key]);
+ }
+ }
+
+ function disconnect() {
+ [graphs, common.tables].forEach((o) => {
+ Object.keys(o).forEach((key) => {
+ o[key].destroy();
+ delete o[key];
+ });
+ });
+
+ // Remove jquery-stickytabs listeners
+ $(window).off("hashchange");
+ $(".nav-tabs-sticky > .nav-item > .nav-link").off("click").removeClass("active");
+
+ stopTimers();
+ cleanCredentials();
+ ui.connect();
+ }
+
+ function tabClick(id) {
+ let tab_id = id;
+ if ($(id).attr("disabled")) return;
+ let navBarControls = $("#selSrv, #navBar li, #navBar a, #navBar button");
+ if (id !== "#autoRefresh") navBarControls.attr("disabled", true).addClass("disabled", true);
+
+ stopTimers();
+
+ if (id === "#refresh" || id === "#autoRefresh") {
+ tab_id = "#" + $(".nav-link.active").attr("id");
+ }
+
+ $("#autoRefresh").hide();
+ $("#refresh").addClass("radius-right");
+
+ function setAutoRefresh(refreshInterval, timer, callback) {
+ function countdown(interval) {
+ Visibility.stop(timer_id.countdown);
+ if (!interval) {
+ $("#countdown").text("--:--");
+ return;
+ }
+
+ let timeLeft = interval;
+ $("#countdown").text("00:00");
+ timer_id.countdown = Visibility.every(1000, 1000, () => {
+ timeLeft -= 1000;
+ $("#countdown").text(new Date(timeLeft).toISOString().substr(14, 5));
+ if (timeLeft <= 0) Visibility.stop(timer_id.countdown);
+ });
+ }
+
+ $("#refresh").removeClass("radius-right");
+ $("#autoRefresh").show();
+
+ countdown(refreshInterval);
+ if (!refreshInterval) return;
+ timer_id[timer] = Visibility.every(refreshInterval, () => {
+ countdown(refreshInterval);
+ if ($("#refresh").attr("disabled")) return;
+ $("#refresh").attr("disabled", true).addClass("disabled", true);
+ callback();
+ });
+ }
+
+ if (["#scan_nav", "#selectors_nav", "#disconnect"].indexOf(tab_id) !== -1) {
+ $("#refresh").hide();
+ } else {
+ $("#refresh").show();
+ }
+
+ switch (tab_id) {
+ case "#status_nav":
+ require(["app/stats"], (module) => {
+ const refreshInterval = $(".dropdown-menu a.active.preset").data("value");
+ setAutoRefresh(refreshInterval, "status",
+ () => module.statWidgets(graphs, checked_server));
+ if (id !== "#autoRefresh") module.statWidgets(graphs, checked_server);
+
+ $(".preset").show();
+ $(".history").hide();
+ $(".dynamic").hide();
+ });
+ break;
+ case "#throughput_nav":
+ require(["app/graph"], (module) => {
+ const selData = common.getSelector("selData"); // Graph's dataset selector state
+ const step = {
+ day: 60000,
+ week: 300000
+ };
+ let refreshInterval = step[selData] || 3600000;
+ $("#dynamic-item").text((refreshInterval / 60000) + " min");
+
+ if (!$(".dropdown-menu a.active.dynamic").data("value")) {
+ refreshInterval = null;
+ }
+ setAutoRefresh(refreshInterval, "throughput",
+ () => module.draw(graphs, common.neighbours, checked_server, selData));
+ if (id !== "#autoRefresh") module.draw(graphs, common.neighbours, checked_server, selData);
+
+ $(".preset").hide();
+ $(".history").hide();
+ $(".dynamic").show();
+ });
+ break;
+ case "#configuration_nav":
+ require(["app/config"], (module) => {
+ module.getActions(checked_server);
+ module.getMaps(checked_server);
+ });
+ break;
+ case "#symbols_nav":
+ require(["app/symbols"], (module) => module.getSymbols(checked_server));
+ break;
+ case "#scan_nav":
+ require(["app/upload"]);
+ break;
+ case "#selectors_nav":
+ require(["app/selectors"], (module) => module.displayUI());
+ break;
+ case "#history_nav":
+ require(["app/history"], (module) => {
+ function getHistoryAndErrors() {
+ module.getHistory();
+ module.getErrors();
+ }
+ const refreshInterval = $(".dropdown-menu a.active.history").data("value");
+ setAutoRefresh(refreshInterval, "history",
+ () => getHistoryAndErrors());
+ if (id !== "#autoRefresh") getHistoryAndErrors();
+
+ $(".preset").hide();
+ $(".history").show();
+ $(".dynamic").hide();
+ });
+ break;
+ case "#disconnect":
+ disconnect();
+ break;
+ default:
+ }
+
+ setTimeout(() => {
+ // Do not enable Refresh button until AJAX requests to all neighbours are finished
+ if (tab_id === "#history_nav") navBarControls = $(navBarControls).not("#refresh");
+
+ navBarControls.removeAttr("disabled").removeClass("disabled");
+ }, (id === "#autoRefresh") ? 0 : 1000);
+ }
+
+ function saveCredentials(password) {
+ sessionStorage.setItem("Password", password);
+ }
+
+ function displayUI() {
+ // In many browsers local storage can only store string.
+ // So when we store the boolean true or false, it actually stores the strings "true" or "false".
+ common.read_only = sessionStorage.getItem("read_only") === "true";
+
+ common.query("auth", {
+ success: function (neighbours_status) {
+ $("#selSrv").empty();
+ $("#selSrv").append($('<option value="All SERVERS">All SERVERS</option>'));
+ neighbours_status.forEach((e) => {
+ $("#selSrv").append($('<option value="' + e.name + '">' + e.name + "</option>"));
+ if (checked_server === e.name) {
+ $('#selSrv [value="' + e.name + '"]').prop("selected", true);
+ } else if (!e.status) {
+ $('#selSrv [value="' + e.name + '"]').prop("disabled", true);
+ }
+ });
+ },
+ complete: function () {
+ ajaxSetup(localStorage.getItem("ajax_timeout"));
+
+ if (common.read_only) {
+ $(".ro-disable").attr("disabled", true);
+ $(".ro-hide").hide();
+ } else {
+ $(".ro-disable").removeAttr("disabled", true);
+ $(".ro-hide").show();
+ }
+
+ $("#preloader").addClass("d-none");
+ $("#navBar, #mainUI").removeClass("d-none");
+ $(".nav-tabs-sticky").stickyTabs({initialTab: "#status_nav"});
+ },
+ errorMessage: "Cannot get server status",
+ server: "All SERVERS"
+ });
+ }
+
+
+ // Public functions
+
+ ui.connect = function () {
+ // Prevent locking out of the WebUI if timeout is too low.
+ let timeout = localStorage.getItem("ajax_timeout");
+ if (timeout < defaultAjaxTimeout) timeout = defaultAjaxTimeout;
+ ajaxSetup(timeout);
+
+ // Query "/stat" to check if user is already logged in or client ip matches "secure_ip"
+ $.ajax({
+ type: "GET",
+ url: "stat",
+ success: function (data) {
+ sessionStorage.setItem("read_only", data.read_only);
+ displayUI();
+ },
+ error: function () {
+ function clearFeedback() {
+ $("#connectPassword").off("input").removeClass("is-invalid");
+ $("#authInvalidCharFeedback,#authUnauthorizedFeedback").hide();
+ }
+
+ $("#connectDialog")
+ .on("show.bs.modal", () => {
+ $("#connectDialog").off("show.bs.modal");
+ clearFeedback();
+ })
+ .on("shown.bs.modal", () => {
+ $("#connectDialog").off("shown.bs.modal");
+ $("#connectPassword").focus();
+ })
+ .modal("show");
+
+ $("#connectForm").off("submit").on("submit", (e) => {
+ e.preventDefault();
+ const password = $("#connectPassword").val();
+
+ function invalidFeedback(tooltip) {
+ $("#connectPassword")
+ .addClass("is-invalid")
+ .off("input").on("input", () => clearFeedback());
+ $(tooltip).show();
+ }
+
+ if (!(/^[\u0020-\u007e]*$/).test(password)) {
+ invalidFeedback("#authInvalidCharFeedback");
+ $("#connectPassword").focus();
+ return;
+ }
+
+ common.query("auth", {
+ headers: {
+ Password: password
+ },
+ success: function (json) {
+ const [{data}] = json;
+ $("#connectPassword").val("");
+ if (data.auth === "ok") {
+ sessionStorage.setItem("read_only", data.read_only);
+ saveCredentials(password);
+ $("#connectForm").off("submit");
+ $("#connectDialog").modal("hide");
+ displayUI();
+ }
+ },
+ error: function (jqXHR, textStatus) {
+ if (textStatus.statusText === "Unauthorized") {
+ invalidFeedback("#authUnauthorizedFeedback");
+ } else {
+ common.alertMessage("alert-modal alert-error", textStatus.statusText);
+ }
+ $("#connectPassword").val("");
+ $("#connectPassword").focus();
+ },
+ params: {
+ global: false,
+ },
+ server: "local"
+ });
+ });
+ }
+ });
+ };
+
+
+ (function initSettings() {
+ let selected_locale = null;
+ let custom_locale = null;
+ const localeTextbox = ".popover #settings-popover #locale";
+
+ function validateLocale(saveToLocalStorage) {
+ function toggle_form_group_class(remove, add) {
+ $(localeTextbox).removeClass("is-" + remove).addClass("is-" + add);
+ }
+
+ const now = new Date();
+
+ if (custom_locale.length) {
+ try {
+ now.toLocaleString(custom_locale);
+
+ if (saveToLocalStorage) localStorage.setItem("custom_locale", custom_locale);
+ common.locale = (selected_locale === "custom") ? custom_locale : null;
+ toggle_form_group_class("invalid", "valid");
+ } catch (err) {
+ common.locale = null;
+ toggle_form_group_class("valid", "invalid");
+ }
+ } else {
+ if (saveToLocalStorage) localStorage.setItem("custom_locale", null);
+ common.locale = null;
+ $(localeTextbox).removeClass("is-valid is-invalid");
+ }
+
+ // Display date example
+ $(".popover #settings-popover #date-example").text(
+ (common.locale)
+ ? now.toLocaleString(common.locale)
+ : now.toLocaleString()
+ );
+ }
+
+ $("#settings").popover({
+ container: "body",
+ placement: "bottom",
+ html: true,
+ sanitize: false,
+ content: function () {
+ // Using .clone() has the side-effect of producing elements with duplicate id attributes.
+ return $("#settings-popover").clone();
+ }
+ // Restore the tooltip of the element that the popover is attached to.
+ }).attr("title", function () {
+ return $(this).attr("data-original-title");
+ });
+ $("#settings").on("click", (e) => {
+ e.preventDefault();
+ });
+ $("#settings").on("inserted.bs.popover", () => {
+ selected_locale = localStorage.getItem("selected_locale") || "browser";
+ custom_locale = localStorage.getItem("custom_locale") || "";
+ validateLocale();
+
+ $('.popover #settings-popover input:radio[name="locale"]').val([selected_locale]);
+ $(localeTextbox).val(custom_locale);
+
+ ajaxSetup(localStorage.getItem("ajax_timeout"), true);
+ });
+ $(document).on("change", '.popover #settings-popover input:radio[name="locale"]', function () {
+ selected_locale = this.value;
+ localStorage.setItem("selected_locale", selected_locale);
+ validateLocale();
+ });
+ $(document).on("input", localeTextbox, () => {
+ custom_locale = $(localeTextbox).val();
+ validateLocale(true);
+ });
+ $(document).on("input", ajaxTimeoutBox, () => {
+ ajaxSetup($(ajaxTimeoutBox).val(), false, true);
+ });
+ $(document).on("click", ".popover #settings-popover #ajax-timeout-restore", () => {
+ ajaxSetup(null, true, true);
+ });
+
+ // Dismiss Bootstrap popover by clicking outside
+ $("body").on("click", (e) => {
+ $(".popover").each(function () {
+ if (
+ // Popover's descendant
+ $(this).has(e.target).length ||
+ // Button (or icon within a button) that triggers the popover.
+ $(e.target).closest("button").attr("aria-describedby") === this.id
+ ) return;
+ $("#settings").popover("hide");
+ });
+ });
+ }());
+
+ $("#selData").change(() => {
+ tabClick("#throughput_nav");
+ });
+
+ $(document).ajaxStart(() => {
+ $("#refresh > svg").addClass("fa-spin");
+ });
+ $(document).ajaxComplete(() => {
+ setTimeout(() => {
+ $("#refresh > svg").removeClass("fa-spin");
+ }, 1000);
+ });
+
+ $('a[data-bs-toggle="tab"]').on("shown.bs.tab", function () {
+ tabClick("#" + $(this).attr("id"));
+ });
+ $("#refresh, #disconnect").on("click", function (e) {
+ e.preventDefault();
+ tabClick("#" + $(this).attr("id"));
+ });
+ $(".dropdown-menu a").click(function (e) {
+ e.preventDefault();
+ const classList = $(this).attr("class");
+ const [menuClass] = (/\b(?:dynamic|history|preset)\b/).exec(classList);
+ $(".dropdown-menu a.active." + menuClass).removeClass("active");
+ $(this).addClass("active");
+ tabClick("#autoRefresh");
+ });
+
+ $("#selSrv").change(function () {
+ checked_server = this.value;
+ $("#selSrv [value=\"" + checked_server + "\"]").prop("checked", true);
+ if (checked_server === "All SERVERS") {
+ $("#learnServers").show();
+ } else {
+ $("#learnServers").hide();
+ }
+ tabClick("#" + $("#tablist > .nav-item > .nav-link.active").attr("id"));
+ });
+
+ // Radio buttons
+ $(document).on("click", "input:radio[name=\"clusterName\"]", function () {
+ if (!this.disabled) {
+ checked_server = this.value;
+ tabClick("#status_nav");
+ }
+ });
+
+ $("#loading").addClass("d-none");
+
+ return ui;
+});