Viewing File: /usr/local/cpanel/base/sharedjs/email_deliverability/views/manageDomainSPF.js
/*
# email_deliverability/controller/manageDomain.js Copyright 2022 cPanel, L.L.C.
# All rights reserved.
# copyright@cpanel.net http://cpanel.net
# This code is subject to the cPanel license. Unauthorized copying is prohibited
*/
/* global define */
define(
[
"angular",
"lodash",
"cjt/util/locale",
"shared/js/email_deliverability/services/domains",
"shared/js/email_deliverability/services/spfParser",
"shared/js/email_deliverability/services/SPFRecordProcessor",
"cjt/directives/copyField",
"cjt/modules",
"cjt/services/cpanel/componentSettingSaverService",
"cjt/directives/callout",
"cjt/directives/multiFieldEditorItem",
"cjt/directives/multiFieldEditor",
"cjt/directives/actionButtonDirective",
"cjt/validator/ip-validators",
"cjt/validator/domain-validators",
],
function(angular, _, LOCALE, DomainsService, SPFParser, SPFRecordProcessor, CopyField) {
"use strict";
/**
* Controller for Managing a Domain
*
* @module ManageDomainController
* @memberof cpanel.emailDeliverability
*
*/
var MODULE_NAMESPACE = "shared.emailDeliverability.views.manageDomainSPF";
var MODULE_REQUIREMENTS = [
DomainsService.namespace,
CopyField.namespace,
];
var CONTROLLER_NAME = "ManageDomainSPFController";
var CONTROLLER_INJECTABLES = ["$scope", "$log", "$location", "$routeParams", "DomainsService", "alertService", "componentSettingSaverService", "ADD_RESOURCE_PANEL"];
var CONTROLLER = function($scope, $log, $location, $routeParams, $domainsService, $alertService, $CSSS, ADD_RESOURCE_PANEL) {
var MECHANISM_ARRAYS = ["additionalHosts", "additionalMXServers", "additionalIPv4Addresses", "additionalIPv6Addresses", "additionalINCLUDEItems"];
$scope._updateSuggestedRecords = function _updateSuggestedRecords() {
$scope.suggestedRecord = $scope.currentDomain.getSuggestedRecord("spf");
var originalExpected = $scope.suggestedRecord.originalExpected || "";
var originalExpectedTerms = originalExpected.trim().split(/\s+/);
$scope.missingMechanisms = originalExpectedTerms.map(SPFRecordProcessor._parseSPFTerm).filter(function(term) {
if ( !term.type ) {
return false;
}
return true;
});
$scope.workingRecord = $scope.suggestedRecord;
$scope.populateFormFrom($scope.workingRecord);
$scope.updatePreview();
};
/**
*
* Initiate the current view
*
*/
$scope.init = function init() {
var domains = $domainsService.getAll();
if (!$scope.currentDomain && domains.length > 1) {
$alertService.add({
"message": LOCALE.maketext("You did not specify a domain to manage."),
"type": "danger",
});
$location.path("/").search("");
return;
} else if (domains.length === 1) {
$scope.currentDomain = domains[0];
}
$CSSS.get(CONTROLLER_NAME).then(function(response) {
if (typeof response !== "undefined" && response) {
$scope.showAllHelp = response.showAllHelp;
}
});
$CSSS.register(CONTROLLER_NAME);
$scope.$on("$destroy", function() {
$CSSS.unregister(CONTROLLER_NAME);
});
MECHANISM_ARRAYS.forEach(function(attr) {
$scope.$watchCollection(attr, $scope.updatePreview.bind($scope));
});
return $domainsService.validateAllRecords([$scope.currentDomain]).then($scope._updateSuggestedRecords).then(function() {
$scope._hasNSAuthority = $scope.currentDomain.hasNSAuthority;
});
};
/**
*
* Parse SPF record into various mechanisms
*
* @param {String} record SPF record to parse
*/
$scope.parseRecord = function parseRecord(record) {
if (!record) {
return;
}
if (!record.value) {
return;
}
var currentRecordParts = SPFParser.parse(record.value);
var mechanisms = currentRecordParts.mechanisms;
mechanisms.forEach(function(mechanism) {
if (mechanism.type !== "all" && !mechanism.value) {
return;
}
if (["version", "all"].indexOf(mechanism.type) === -1 && mechanism.prefix !== "+") {
$log.debug("Non-pass mechanism exists. Presenting warning.", mechanism);
$scope.nonPassPrefixesExist = true;
return;
}
if (mechanism.type === "mx") {
$scope.additionalMXServers.push(mechanism.value);
} else if (mechanism.type === "ip4") {
$scope.additionalIPv4Addresses.push(mechanism.value);
} else if (mechanism.type === "ip6") {
$scope.additionalIPv6Addresses.push(mechanism.value);
} else if (mechanism.type === "all" && mechanism.prefix === "-") {
$scope.excludeAllOtherDomains = true;
} else if (mechanism.type === "a") {
$scope.additionalHosts.push(mechanism.value);
} else if (mechanism.type === "include") {
$scope.additionalINCLUDEItems.push(mechanism.value);
}
});
};
/**
*
* Remove duplicates from each of the mechanism arrays
*
*/
$scope.removeDuplicates = function removeDuplicates() {
MECHANISM_ARRAYS.forEach(function(MECH_AR) {
var original = $scope[MECH_AR].slice(0);
var uniq = _.uniq(original);
$scope[MECH_AR].splice(0, $scope[MECH_AR].length);
uniq.forEach($scope[MECH_AR].push);
});
};
/**
*
* Populate form from passed records
*
* @param {String} record SPF record to populate form from
*/
$scope.populateFormFrom = function populateFormFromRecords(record) {
// Clear current values
MECHANISM_ARRAYS.forEach(function(MECH_AR) {
$scope[MECH_AR].splice(0, $scope[MECH_AR].length);
});
$scope.parseRecord(record);
};
$scope.toggleExcludeAllOtherDomains = function toggleExcludeAllOtherDomains() {
$scope.excludeAllOtherDomains = !$scope.excludeAllOtherDomains;
$scope.updatePreview();
};
/**
*
* Update the $scope.workingPreview variable with the SPF Record
*
*/
$scope.updatePreview = function updatePreview() {
// Build new user requested record.
var newWorkingPreview = ["v=spf1", "+mx", "+a"];
if (!$scope.missingMechanisms) {
return;
}
// add +a records
$scope.additionalHosts.forEach(function(item) {
newWorkingPreview.push("+a:" + item);
});
// add +mx records
$scope.additionalMXServers.forEach(function(item) {
newWorkingPreview.push("+mx:" + item);
});
// add all other ip4 addresses
$scope.additionalIPv4Addresses.forEach(function(item) {
newWorkingPreview.push("+ip4:" + item);
});
// add all other ip6 addresses
$scope.additionalIPv6Addresses.forEach(function(item) {
newWorkingPreview.push("+ip6:" + item);
});
// add all includes
// these need to be last
$scope.additionalINCLUDEItems.forEach(function(item) {
newWorkingPreview.push("+include:" + item);
});
// preserve non-pass mechanisms from current record
// overridden ones will get collapsed away.
var currentRecord = $scope.currentDomain.getCurrentRecord("spf");
var currentRecordParts, mechanisms;
if (currentRecord && currentRecord.value) {
currentRecordParts = SPFParser.parse(currentRecord.value);
mechanisms = currentRecordParts.mechanisms;
mechanisms.forEach(function(mechanism) {
if (["version", "all"].indexOf(mechanism.type) === -1 && mechanism.prefix !== "+") {
// non plus mechanisms
newWorkingPreview.push(mechanism.prefix + mechanism.type + ":" + mechanism.value);
}
});
}
// add ~all?
if ($scope.excludeAllOtherDomains) {
newWorkingPreview.push("-all");
} else {
newWorkingPreview.push("~all");
}
var newWorkingPreviewString = newWorkingPreview.join(" ");
// v=spf1 +a +mx +ip4:10.215.218.115 +a:aaaaaaaaaa.com +mx:mxxxxxx.com +ip4:192.168.1.1 +include:include.com -all
$scope.workingRecord = SPFRecordProcessor.combineRecords(newWorkingPreviewString, $scope.missingMechanisms);
};
/**
*
* Determine if current domain has nameserver authority
*
* @returns {Boolean} nameserver authority status
*/
$scope.hasNSAuthority = function hasNSAuthority() {
return $scope._hasNSAuthority;
};
/**
*
* Return the string message for a bad configuration
*
* @param {String} recordType record type to generate the message for
* @returns {String} bad configuration message
*/
$scope.badConfigurationMessage = function badConfigurationMessage(recordType) {
return LOCALE.maketext("“[_1]” is [output,strong,not] properly configured for this domain.", recordType.toUpperCase());
};
/**
*
* Generate the string message for a user with no authority
*
* @param {String} recordType record type to generate the message for
* @returns {String} the no authority message
*/
$scope.noAuthorityMessage = function noAuthorityMessage(recordType) {
return $domainsService.getNoAuthorityMessage($scope.currentDomain, recordType);
};
/**
*
* Toggle the visible help for the form
*
*/
$scope.toggleHelp = function toggleHelp() {
$scope.showAllHelp = !$scope.showAllHelp;
$scope.saveToComponentSettings();
};
/**
*
* Update the NVData saved aspects of this view
*
*/
$scope.saveToComponentSettings = function saveToComponentSettings() {
$CSSS.set(CONTROLLER_NAME, {
showAllHelp: $scope.showAllHelp,
});
};
/**
*
* Repair the SPF record for the current domain
*
* @returns {Promise} repairRecord promise
*/
$scope.update = function update() {
return $domainsService.repairDomain($scope.currentDomain, ["spf"], [$scope.workingRecord], !$scope.hasNSAuthority())
.then(function() {
// Do this manually if no ns authority because the service does not
if (!$scope.hasNSAuthority()) {
return $domainsService.validateAllRecords([$scope.currentDomain]).then($scope._updateSuggestedRecords);
}
})
.finally(function() {
if (!$scope.hasNSAuthority()) {
$domainsService.unreflectedChangeMessage($scope.currentDomain.domain);
}
$scope.showConfirmDKIM = false;
});
};
$scope.currentDomain = $domainsService.findDomainByName($routeParams["domain"]);
angular.extend($scope, {
getCurrentRecord: $scope.currentDomain.getCurrentRecord.bind($scope.currentDomain),
resourcesPanelTemplate: ADD_RESOURCE_PANEL,
showAllHelp: false,
currentRecord: "",
workingRecord: "",
/* form fields */
excludeAllOtherDomains: false,
additionalHosts: [],
additionalMXServers: [],
additionalIPv4Addresses: [],
additionalIPv6Addresses: [],
additionalINCLUDEItems: [],
});
return $scope.init();
};
CONTROLLER_INJECTABLES.push(CONTROLLER);
var app = angular.module(MODULE_NAMESPACE, MODULE_REQUIREMENTS);
app.controller(CONTROLLER_NAME, CONTROLLER_INJECTABLES);
return {
class: CONTROLLER,
namespace: MODULE_NAMESPACE,
};
}
);
Back to Directory
File Manager