'use strict'; /* global $, angular, TASK_TYPES */ /* global async */ angular.module('Application').controller('EmailsController', ['$scope', '$location', '$translate', '$timeout', 'Client', function ($scope, $location, $translate, $timeout, Client) { Client.onReady(function () { if (!Client.getUserInfo().isAtLeastMailManager) $location.path('/'); }); $scope.ready = false; $scope.config = Client.getConfig(); $scope.user = Client.getUserInfo(); $scope.domains = []; $scope.mailLocation = { busy: false, percent: 0, message: '', errorMessage: '', currentLocation: { domain: null, subdomain: '' }, domain: null, subdomain: '', tasks: [], refreshTasks: function () { Client.getTasksByType(TASK_TYPES.TASK_CHANGE_MAIL_LOCATION, function (error, tasks) { if (error) return console.error(error); $scope.mailLocation.tasks = tasks.slice(0, 10); if ($scope.mailLocation.tasks.length && $scope.mailLocation.tasks[0].active) $scope.mailLocation.updateStatus(); }); }, stop: function () { Client.stopTask($scope.mailLocation.tasks[0].id, function (error) { if (error) console.error(error); $scope.mailLocation.busy = false; $scope.mailLocation.refreshTasks(); }); }, refresh: function () { Client.getMailLocation(function (error, location) { if (error) return console.error('Failed to get max email location', error); $scope.mailLocation.currentLocation.subdomain = $scope.mailLocation.subdomain = location.subdomain; $scope.mailLocation.currentLocation.domain = $scope.mailLocation.domain = $scope.domains.find(function (d) { return location.domain === d.domain; }); $scope.mailLocation.refreshTasks(); }); }, updateStatus: function () { var taskId = $scope.mailLocation.tasks[0].id; Client.getTask(taskId, function (error, data) { if (error) return window.setTimeout($scope.mailLocation.updateStatus, 5000); if (!data.active) { $scope.mailLocation.busy = false; $scope.mailLocation.message = ''; $scope.mailLocation.percent = 100; $scope.mailLocation.errorMessage = data.success ? '' : data.error.message; $scope.mailLocation.refreshTasks(); // update the tasks list dropdown return; } $scope.mailLocation.busy = true; $scope.mailLocation.percent = data.percent; $scope.mailLocation.message = data.message; window.setTimeout($scope.mailLocation.updateStatus, 1000); }); }, change: function () { $scope.mailLocation.busy = true; $scope.mailLocation.percent = 0; $scope.mailLocation.message = ''; $scope.mailLocation.errorMessage = ''; Client.setMailLocation($scope.mailLocation.subdomain, $scope.mailLocation.domain.domain, function (error) { if (error) { console.error(error); $scope.mailLocation.errorMessage = error.message; $scope.mailLocation.busy = false; } else { $scope.mailLocation.refreshTasks(); } Client.refreshConfig(); // update config.mailFqdn }); } }; $scope.maxEmailSize = { busy: false, error: null, size: 0, currentSize: 0, refresh: function () { Client.getMaxEmailSize(function (error, size) { if (error) return console.error('Failed to get max email size', error); $scope.maxEmailSize.currentSize = size; }); }, show: function() { $scope.maxEmailSize.busy = false; $scope.maxEmailSize.error = null; $scope.maxEmailSize.size = $scope.maxEmailSize.currentSize; $scope.maxEmailSizeChangeForm.$setUntouched(); $scope.maxEmailSizeChangeForm.$setPristine(); $('#maxEmailSizeChangeModal').modal('show'); }, submit: function () { $scope.maxEmailSize.busy = true; Client.setMaxEmailSize(parseInt($scope.maxEmailSize.size), function (error) { $scope.maxEmailSize.busy = false; if (error) return console.error(error); $scope.maxEmailSize.currentSize = $scope.maxEmailSize.size; $('#maxEmailSizeChangeModal').modal('hide'); }); } }; $scope.virtualAllMail = { busy: false, error: null, enabled: false, refresh: function () { Client.getVirtualAllMail(function (error, enabled) { if (error) return console.error('Failed to get max email size', error); $scope.virtualAllMail.enabled = enabled; }); }, show: function() { $scope.virtualAllMail.busy = false; $scope.virtualAllMail.error = null; $('#virtualAllMailChangeModal').modal('show'); }, submit: function (enable) { $scope.virtualAllMail.busy = true; Client.setVirtualAllMail(enable, function (error) { $scope.virtualAllMail.busy = false; if (error) return console.error(error); $scope.virtualAllMail.enabled = enable; $('#virtualAllMailChangeModal').modal('hide'); }); } }; $scope.mailboxSharing = { busy: false, error: null, enabled: null, // null means we have not refreshed yet refresh: function () { Client.getMailboxSharing(function (error, enabled) { if (error) return console.error('Failed to get mailbox sharing', error); $scope.mailboxSharing.enabled = enabled; }); }, submit: function () { $scope.mailboxSharing.busy = true; Client.setMailboxSharing(!$scope.mailboxSharing.enabled, function (error) { // give sometime for mail server to restart $timeout(function () { $scope.mailboxSharing.busy = false; if (error) return console.error(error); $scope.mailboxSharing.enabled = !$scope.mailboxSharing.enabled; }, 3000); }); } }; $scope.ftsConfig = { busy: false, error: {}, currentConfig: null, // null means not loaded yet enabled: false, running: false, enoughMemory: false, refresh: function () { Client.getService('mail', function (error, result) { if (error) return console.log('Error getting status of mail conatiner', error); $scope.ftsConfig.enoughMemory = result.config.memoryLimit > (1024*1024*1024*2); $scope.ftsConfig.running = result.healthcheck && result.healthcheck.solr.status && result.healthcheck.tika.status; Client.getFtsConfig(function (error, config) { if (error) return console.error('Failed to get solr config', error); $scope.ftsConfig.currentConfig = config; }); }); }, show: function() { $scope.ftsConfig.busy = false; $scope.ftsConfig.error = null; $scope.ftsConfig.enabled = $scope.ftsConfig.currentConfig.enabled; $('#ftsConfigModal').modal('show'); }, submit: function (newState) { $scope.ftsConfig.busy = true; Client.setFtsConfig(newState, function (error) { if (error) return console.error(error); $timeout(function () { $scope.ftsConfig.busy = false; // FIXME: these values are fake. but cannot get current status from mail server since it might be restarting $scope.ftsConfig.currentConfig.enabled = newState; $scope.ftsConfig.running = newState; $timeout(function () { $scope.ftsConfig.refresh(); }, 20000); // get real values after 20 seconds $('#ftsConfigModal').modal('hide'); }, 5000); }); } }; $scope.spamConfig = { busy: false, error: {}, acl: { allowlist: [], blocklist: [] }, customConfig: '', config: '', blocklist: '', // currently, we don't support allowlist because it requires user to understand a bit more of what he is doing refresh: function () { Client.getSpamCustomConfig(function (error, config) { if (error) return console.error('Failed to get custom spam config', error); $scope.spamConfig.customConfig = config; }); Client.getSpamAcl(function (error, acl) { if (error) return console.error('Failed to get spam acl', error); $scope.spamConfig.acl = acl; }); }, show: function() { $scope.spamConfig.busy = false; $scope.spamConfig.error = {}; $scope.spamConfig.blocklist = $scope.spamConfig.acl.blocklist.join('\n'); $scope.spamConfig.config = $scope.spamConfig.customConfig; $scope.spamConfigChangeForm.$setUntouched(); $scope.spamConfigChangeForm.$setPristine(); $('#spamConfigChangeModal').modal('show'); }, submit: function () { $scope.spamConfig.busy = true; $scope.spamConfig.error = {}; var blocklist = $scope.spamConfig.blocklist.split('\n').filter(function (l) { return l !== ''; }); Client.setSpamAcl({ blocklist: blocklist, allowlist: [] }, function (error) { if (error) { $scope.spamConfig.busy = false; $scope.spamConfig.error.blocklist = error.message; $scope.spamConfigChangeForm.blocklist.$setPristine(); return; } Client.setSpamCustomConfig($scope.spamConfig.config, function (error) { if (error) { $scope.spamConfig.busy = false; $scope.spamConfig.error.config = error.message; $scope.spamConfigChangeForm.config.$setPristine(); return; } $scope.spamConfig.busy = false; $scope.spamConfig.refresh(); $('#spamConfigChangeModal').modal('hide'); }); }); } }, $scope.acl = { busy: false, error: {}, dnsblZones: '', dnsblZonesCount: 0, refresh: function () { Client.getDnsblConfig(function (error, result) { if (error) return console.error('Failed to get email acl', error); $scope.acl.dnsblZones = result.zones.join('\n'); $scope.acl.dnsblZonesCount = result.zones.length; }); }, show: function() { $scope.acl.busy = false; $scope.acl.error = {}; $scope.aclChangeForm.$setUntouched(); $scope.aclChangeForm.$setPristine(); $('#aclChangeModal').modal('show'); }, submit: function () { $scope.acl.busy = true; $scope.acl.error = {}; var zones = $scope.acl.dnsblZones.split('\n').filter(function (l) { return l !== ''; }); Client.setDnsblConfig(zones, function (error) { if (error) { $scope.acl.busy = false; $scope.acl.error.dnsblZones = error.message; $scope.aclChangeForm.dnsblZones.$setPristine(); return; } $scope.acl.busy = false; $scope.acl.refresh(); $('#aclChangeModal').modal('hide'); }); } }, $scope.testEmail = { busy: false, error: {}, mailTo: '', domain: null, clearForm: function () { $scope.testEmail.mailTo = ''; }, show: function (domain) { $scope.testEmail.error = {}; $scope.testEmail.busy = false; $scope.testEmail.domain = domain; $scope.testEmail.mailTo = $scope.user.email; $('#testEmailModal').modal('show'); }, submit: function () { $scope.testEmail.error = {}; $scope.testEmail.busy = true; Client.sendTestMail($scope.testEmail.domain.domain, $scope.testEmail.mailTo, function (error) { $scope.testEmail.busy = false; if (error) { $scope.testEmail.error.generic = error.message; console.error(error); $('#inputTestMailTo').focus(); return; } $('#testEmailModal').modal('hide'); }); } }; function refreshMailStatus(domain, done) { Client.getMailStatusForDomain(domain.domain, function (error, result) { if (error) { console.error('Failed to fetch mail status for domain', domain.domain, error); return done(); } domain.status = result; domain.statusOk = Object.keys(result).every(function (k) { if (k === 'dns') return Object.keys(result.dns).every(function (k) { return result.dns[k].status; }); if (!('status' in result[k])) return true; // if status is not present, the test was not run return result[k].status; }); done(); }); } function refreshMailConfig(domain, done) { Client.getMailConfigForDomain(domain.domain, function (error, mailConfig) { if (error) { console.error('Failed to fetch mail config for domain', domain.domain, error); return done(); } domain.inbound = mailConfig.enabled; domain.outbound = mailConfig.relay.provider !== 'noop'; // do this even if no outbound since people forget to remove mailboxes Client.getMailboxCount(domain.domain, function (error, count) { if (error) { console.error('Failed to fetch mailboxes for domain', domain.domain, error); return done(); } domain.mailboxCount = count; done(); }); }); } function refreshMailUsage(domain, done) { Client.getMailUsage(domain.domain, function (error, usage) { if (error) { console.error('Failed to fetch usage for domain', domain.domain, error); return done(); } domain.usage = 0; // we used to use quotaValue here but it's quite different wrt diskSize. so choose diskSize consistently // also, quotaValue can be missing for deleted mailboxes that are on disk but removed from dovecot/ldap itself Object.keys(usage).forEach(function (m) { domain.usage += usage[m].diskSize; }); done(); }); } function refreshDomainStatuses() { async.each($scope.domains, function (domain, iteratorDone) { async.series([ refreshMailStatus.bind(null, domain), refreshMailConfig.bind(null, domain), ], function () { domain.loading = false; iteratorDone(); }); }, function () { // mail usage is loaded separately with a cancellation check. when there are a lot of domains, it runs a long time in background and slows down loading of new views async.eachLimit($scope.domains, 5, function (domain, itemDone) { if ($scope.$$destroyed) return itemDone(); // abort! refreshMailUsage(domain, function () { domain.loadingUsage = false; itemDone(); }); }); }); } Client.onReady(function () { Client.getDomains(function (error, domains) { if (error) return console.error('Unable to get domain listing.', error); domains.forEach(function (domain) { domain.loading = true; domain.loadingUsage = true; }); // used by ui to show 'loading' $scope.domains = domains; $scope.ready = true; if ($scope.user.isAtLeastAdmin) { $scope.mailLocation.refresh(); $scope.maxEmailSize.refresh(); $scope.virtualAllMail.refresh(); $scope.mailboxSharing.refresh(); $scope.spamConfig.refresh(); $scope.ftsConfig.refresh(); $scope.acl.refresh(); } refreshDomainStatuses(); }); }); // setup all the dialog focus handling ['testEmailModal'].forEach(function (id) { $('#' + id).on('shown.bs.modal', function () { $(this).find('[autofocus]:first').focus(); }); }); $('.modal-backdrop').remove(); }]);