From e140c30eb03e4d959caf95dfcac09ad71d59e7c8 Mon Sep 17 00:00:00 2001 From: usmannasir Date: Mon, 26 Feb 2024 16:12:28 +0500 Subject: [PATCH] email limits --- .../templates/baseTemplate/index.html | 19 +- mailServer/mailserverManager.py | 114 +++++++++- mailServer/static/mailServer/mailServer.js | 209 +++++++++++++++++- .../templates/mailServer/EmailLimits.html | 141 ++++++++++++ mailServer/urls.py | 9 +- mailServer/views.py | 19 ++ plogical/mailUtilities.py | 109 +++++++++ 7 files changed, 607 insertions(+), 13 deletions(-) create mode 100755 mailServer/templates/mailServer/EmailLimits.html diff --git a/baseTemplate/templates/baseTemplate/index.html b/baseTemplate/templates/baseTemplate/index.html index 3627950c6..2f6456fe2 100755 --- a/baseTemplate/templates/baseTemplate/index.html +++ b/baseTemplate/templates/baseTemplate/index.html @@ -77,7 +77,7 @@ - {% with version="2.3.5.3" %} + {% with version="2.3.5.4" %} @@ -586,6 +586,11 @@ title="{% trans 'Delete Email Account' %}">{% trans "Delete Email" %} {% endif %} + {% if admin or emailForwarding %} +
  • {% trans "Email Limits" %} +
  • + {% endif %} {% if admin or emailForwarding %}
  • {% trans "Email Forwarding" %} @@ -1052,12 +1057,12 @@
  • {% trans "Mail Queue" %}
  • -
  • {% trans "Email Policy Server" %} -
  • -
  • {% trans "Email Limits" %} -
  • +{#
  • {% trans "Email Policy Server" %}#} +{#
  • #} +{#
  • {% trans "Email Limits" %}#} +{#
  • #}
  • {% trans "SpamAssassin" %}
  • diff --git a/mailServer/mailserverManager.py b/mailServer/mailserverManager.py index 36c79021b..2e6a7007e 100755 --- a/mailServer/mailserverManager.py +++ b/mailServer/mailserverManager.py @@ -2,7 +2,11 @@ # coding=utf-8 import os.path import sys +from random import randint + import django +from django.shortcuts import redirect + from plogical.httpProc import httpProc sys.path.append('/usr/local/CyberCP') os.environ.setdefault("DJANGO_SETTINGS_MODULE", "CyberCP.settings") @@ -183,7 +187,16 @@ class MailServerManager(multi.Thread): checker = 0 count = 1 for items in emails: - dic = {'id': count, 'email': items.email, 'DiskUsage': '%sMB' % items.DiskUsage} + try: + command = f'sudo awk -v email="{items.email}" \'$1 == email {{print $2}}\' /etc/rspamd/badusers.map || echo "0,0"' + result = ProcessUtilities.outputExecutioner(command, None, True).rstrip('\n').split('/') + numberofEmails = int(result[0]) + duration = result[1] + except: + numberofEmails = 0 + duration = '0m' + + dic = {'id': count, 'email': items.email, 'DiskUsage': '%sMB' % items.DiskUsage, 'numberofEmails': numberofEmails, 'duration': duration} count = count + 1 if checker == 0: @@ -291,6 +304,7 @@ class MailServerManager(multi.Thread): {'websiteList': websitesName, "status": 1}, 'emailForwarding') return proc.render() + def fetchCurrentForwardings(self): try: @@ -1747,6 +1761,104 @@ milter_default_action = accept else: return 1, 'All checks are OK.' + + ### emails for sites + + def EmailLimits(self): + + userID = self.request.session['userID'] + currentACL = ACLManager.loadedACL(userID) + + if not os.path.exists('/home/cyberpanel/postfix'): + proc = httpProc(self.request, 'mailServer/emailForwarding.html', + {"status": 0}, 'emailForwarding') + return proc.render() + + websitesName = ACLManager.findAllSites(currentACL, userID) + websitesName = websitesName + ACLManager.findChildDomains(websitesName) + + try: + from plogical.processUtilities import ProcessUtilities + if ProcessUtilities.decideServer() == ProcessUtilities.OLS: + + url = "https://platform.cyberpersons.com/CyberpanelAdOns/Adonpermission" + data = { + "name": "all", + "IP": ACLManager.fetchIP() + } + + import requests + response = requests.post(url, data=json.dumps(data)) + Status = response.json()['status'] + + if (Status == 1): + template = 'mailServer/EmailLimits.html' + else: + return redirect("https://cyberpanel.net/cyberpanel-addons") + else: + template = 'baseTemplate/EmailLimits.html' + except BaseException as msg: + template = 'baseTemplate/EmailLimits.html' + + + proc = httpProc(self.request, template, + {'websiteList': websitesName, "status": 1}, 'emailForwarding') + return proc.render() + + def SaveEmailLimitsNew(self): + try: + userID = self.request.session['userID'] + currentACL = ACLManager.loadedACL(userID) + if ACLManager.currentContextPermission(currentACL, 'emailForwarding') == 0: + return ACLManager.loadErrorJson('createStatus', 0) + + data = json.loads(self.request.body) + source = data['source'] + numberofEmails = data['numberofEmails'] + duration = data['duration'] + + eUser = EUsers.objects.get(email=source) + + admin = Administrator.objects.get(pk=userID) + if ACLManager.checkOwnership(eUser.emailOwner.domainOwner.domain, admin, currentACL) == 1: + pass + else: + return ACLManager.loadErrorJson() + + if mailUtilities.checkIfRspamdInstalled() == 0: + execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/mailUtilities.py" + execPath = execPath + " installRspamd" + ProcessUtilities.executioner(execPath) + + execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/mailUtilities.py" + execPath = execPath + " SetupEmailLimits" + ProcessUtilities.executioner(execPath) + + + limitString = f'{source} {str(numberofEmails)}/{duration}\n' + + RandomFile = "/home/cyberpanel/" + str(randint(100000, 999999)) + writeToFile = open(RandomFile, 'w') + writeToFile.write(limitString) + writeToFile.close() + + execPath = "/usr/local/CyberCP/bin/python " + virtualHostUtilities.cyberPanel + "/plogical/mailUtilities.py" + execPath = execPath + f" SaveEmailLimitsNew --tempConfigPath {RandomFile}" + result = ProcessUtilities.outputExecutioner(execPath) + + if result.find('1,None') > -1: + data_ret = {'status': 1} + else: + data_ret = {'status': 1, 'error_message': "result",} + + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + except BaseException as msg: + data_ret = {'status': 0, 'createStatus': 0, 'error_message': str(msg)} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + def main(): parser = argparse.ArgumentParser(description='CyberPanel') diff --git a/mailServer/static/mailServer/mailServer.js b/mailServer/static/mailServer/mailServer.js index f46bb4d4a..f5247a1bd 100755 --- a/mailServer/static/mailServer/mailServer.js +++ b/mailServer/static/mailServer/mailServer.js @@ -886,7 +886,7 @@ app.controller('emailForwarding', function ($scope, $http) { $scope.fetchCurrentForwardings = function () { - if($scope.forwardingOption == null || $scope.selectedEmail == null ){ + if ($scope.forwardingOption == null || $scope.selectedEmail == null) { $scope.forwardLoading = true; return 0; } @@ -1345,3 +1345,210 @@ app.controller('listEmails', function ($scope, $http) { /* Java script code for List Emails Ends here */ + + +/* Java script code for EmailLimitsNew */ +app.controller('EmailLimitsNew', function ($scope, $http) { + + $scope.creationBox = true; + $scope.emailDetails = true; + $scope.forwardLoading = true; + $scope.forwardError = true; + $scope.forwardSuccess = true; + $scope.couldNotConnect = true; + $scope.notifyBox = true; + + + $scope.showEmailDetails = function () { + + $scope.creationBox = true; + $scope.emailDetails = true; + $scope.forwardLoading = false; + $scope.forwardError = true; + $scope.forwardSuccess = true; + $scope.couldNotConnect = true; + $scope.notifyBox = true; + + var url = "/email/getEmailsForDomain"; + + + var data = { + domain: $scope.emailDomain + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + + if (response.data.fetchStatus === 1) { + + $scope.emails = JSON.parse(response.data.data); + + $scope.creationBox = true; + $scope.emailDetails = false; + $scope.forwardLoading = true; + $scope.forwardError = true; + $scope.forwardSuccess = true; + $scope.couldNotConnect = true; + $scope.notifyBox = false; + + } else { + $scope.creationBox = true; + $scope.emailDetails = true; + $scope.forwardLoading = true; + $scope.forwardError = false; + $scope.forwardSuccess = true; + $scope.couldNotConnect = true; + $scope.notifyBox = false; + + $scope.errorMessage = response.data.error_message; + + } + + + } + + function cantLoadInitialDatas(response) { + + $scope.creationBox = true; + $scope.emailDetails = true; + $scope.forwardLoading = true; + $scope.forwardError = true; + $scope.forwardSuccess = true; + $scope.couldNotConnect = false; + $scope.notifyBox = false; + + + } + + + }; + + $scope.selectForwardingEmail = function () { + + $scope.creationBox = false; + $scope.emailDetails = false; + $scope.forwardLoading = true; + $scope.forwardError = true; + $scope.forwardSuccess = true; + $scope.couldNotConnect = true; + $scope.notifyBox = true; + + // Given email to search for + var givenEmail = $scope.selectedEmail; + + for (var i = 0; i < $scope.emails.length; i++) { + if ($scope.emails[i].email === givenEmail) { + // Extract numberofEmails and duration + var numberofEmails = $scope.emails[i].numberofEmails; + var duration = $scope.emails[i].duration; + + $scope.numberofEmails = numberofEmails; + $scope.duration = duration; + + // Use numberofEmails and duration as needed + console.log("Number of emails:", numberofEmails); + console.log("Duration:", duration); + + // Break out of the loop since the email is found + break; + } + } + + }; + + $scope.SaveChanges = function () { + + $scope.creationBox = false; + $scope.emailDetails = false; + $scope.forwardLoading = false; + $scope.forwardError = true; + $scope.forwardSuccess = true; + $scope.couldNotConnect = true; + $scope.notifyBox = true; + + var url = "/email/SaveEmailLimitsNew"; + + + var data = { + numberofEmails: $scope.numberofEmails, + source: $scope.selectedEmail, + duration: $scope.duration + }; + + var config = { + headers: { + 'X-CSRFToken': getCookie('csrftoken') + } + }; + + $http.post(url, data, config).then(ListInitialDatas, cantLoadInitialDatas); + + + function ListInitialDatas(response) { + + + if (response.data.status === 1) { + + $scope.creationBox = false; + $scope.emailDetails = false; + $scope.forwardLoading = true; + $scope.forwardError = true; + $scope.forwardSuccess = true; + $scope.couldNotConnect = true; + $scope.notifyBox = true; + + new PNotify({ + title: 'Success!', + text: 'Changes applied.', + type: 'success' + }); + + $scope.showEmailDetails(); + } else { + $scope.creationBox = false; + $scope.emailDetails = false; + $scope.forwardLoading = true; + $scope.forwardError = false; + $scope.forwardSuccess = true; + $scope.couldNotConnect = true; + $scope.notifyBox = false; + + new PNotify({ + title: 'Error!', + text: response.data.error_message, + type: 'error' + }); + + } + + + } + + function cantLoadInitialDatas(response) { + + $scope.creationBox = true; + $scope.emailDetails = true; + $scope.forwardLoading = true; + $scope.forwardError = true; + $scope.forwardSuccess = true; + $scope.couldNotConnect = false; + $scope.notifyBox = false; + + + } + + + }; + + +}); +/* Java script for EmailLimitsNew */ diff --git a/mailServer/templates/mailServer/EmailLimits.html b/mailServer/templates/mailServer/EmailLimits.html new file mode 100755 index 000000000..708792bdf --- /dev/null +++ b/mailServer/templates/mailServer/EmailLimits.html @@ -0,0 +1,141 @@ +{% extends "baseTemplate/index.html" %} +{% load i18n %} +{% block title %}{% trans "Email Limits - CyberPanel" %}{% endblock %} +{% block content %} + + {% load static %} + {% get_current_language as LANGUAGE_CODE %} + + +
    +
    +

    {% trans "Setup Email Limits" %} - {% trans "Email Limits Docs" %}

    +

    {% trans "This page help you setup email limits for your emails." %}

    +
    + +
    +
    +

    + {% trans "Setup Email Limits" %} +

    +
    + + {% if not status %} + +
    +

    {% trans "Postfix is disabled." %} + + +

    +
    + + {% else %} + + +
    + +
    + +
    + +
    +
    + + + + +
    + +
    + +
    +
    + + {#
    #} + {# #} + {#
    #} + {# #} + {#
    #} + {#
    #} + + + +
    + +
    +
    +

    {$ errorMessage $}

    +
    + +
    +

    {$ successMessage $}

    +
    +
    +

    {% trans "Could not connect to server. Please refresh this page." %}

    +
    + +
    + + +
    + + + +
    + +
    + +
    +

    {$ selectedEmail $} will be able to send {$ numberofEmails $} emails every {$ duration $}.

    +
    +
    + +
    + +
    + +
    + +
    + + +
    + +
    +
    + + +
    + + {% endif %} + + +
    +
    +
    + + +
    + + +{% endblock %} diff --git a/mailServer/urls.py b/mailServer/urls.py index a974c6b64..8ecd7cb95 100755 --- a/mailServer/urls.py +++ b/mailServer/urls.py @@ -8,21 +8,18 @@ urlpatterns = [ url(r'^submitEmailCreation', views.submitEmailCreation, name='submitEmailCreation'), url(r'^fetchEmails$', views.fetchEmails, name='fetchEmails'), - ## Mail Forwardings url(r'^emailForwarding$', views.emailForwarding, name='emailForwarding'), url(r'^submitEmailForwardingCreation$', views.submitEmailForwardingCreation, name='submitEmailForwardingCreation'), url(r'^fetchCurrentForwardings$', views.fetchCurrentForwardings, name='fetchCurrentForwardings'), url(r'^submitForwardDeletion$', views.submitForwardDeletion, name='submitForwardDeletion'), - ## Delete email url(r'^deleteEmailAccount', views.deleteEmailAccount, name='deleteEmailAccount'), url(r'^getEmailsForDomain$', views.getEmailsForDomain, name='getEmailsForDomain'), url(r'^submitEmailDeletion', views.submitEmailDeletion, name='submitEmailDeletion'), url(r'^fixMailSSL', views.fixMailSSL, name='fixMailSSL'), - ## Change email password url(r'^changeEmailAccountPassword', views.changeEmailAccountPassword, name='changeEmailAccountPassword'), url(r'^submitPasswordChange', views.submitPasswordChange, name='submitPasswordChange'), @@ -36,5 +33,9 @@ urlpatterns = [ url(r'^installOpenDKIM', views.installOpenDKIM, name='installOpenDKIM'), url(r'^installStatusOpenDKIM', views.installStatusOpenDKIM, name='installStatusOpenDKIM'), + ### email limits -] \ No newline at end of file + url(r'^EmailLimits$', views.EmailLimits, name='EmailLimits'), + url(r'^SaveEmailLimitsNew$', views.SaveEmailLimitsNew, name='SaveEmailLimitsNew'), + +] diff --git a/mailServer/views.py b/mailServer/views.py index 12ce04a91..62f6ca9b8 100755 --- a/mailServer/views.py +++ b/mailServer/views.py @@ -245,3 +245,22 @@ def installStatusOpenDKIM(request): return HttpResponse(final_json) +def EmailLimits(request): + try: + msM = MailServerManager(request) + return msM.EmailLimits() + except KeyError: + return redirect(loadLoginPage) + +def SaveEmailLimitsNew(request): + try: + msM = MailServerManager(request) + coreResult = msM.SaveEmailLimitsNew() + return coreResult + except KeyError as msg: + data_ret = {'createStatus': 0, 'error_message': str(msg)} + json_data = json.dumps(data_ret) + return HttpResponse(json_data) + + + diff --git a/plogical/mailUtilities.py b/plogical/mailUtilities.py index bf801e7c8..bbca4dc45 100755 --- a/plogical/mailUtilities.py +++ b/plogical/mailUtilities.py @@ -710,6 +710,70 @@ milter_default_action = accept logging.CyberCPLogFileWriter.writeToFile(str(msg) + "[installSpamAssassin]") + @staticmethod + def SetupEmailLimits(): + rlFile = '/etc/rspamd/override.d/ratelimit.conf' + rlContent = ''' + custom_keywords = "/etc/rspamd/custom_ratelimit.lua"; +''' + if not os.path.exists(rlFile): + + WriteToFile = open(rlFile, 'w') + WriteToFile.write(rlContent) + WriteToFile.close() + + rlLUA = '/etc/rspamd/custom_ratelimit.lua' + rlLUAContent = ''' +local custom_keywords = {} +local d = {} + +-- create map +d['badusers'] = rspamd_config:add_map({ + ['url']= '/etc/rspamd/badusers.map', + ['type'] = 'map', + ['description'] = 'Bad users' +}) + +custom_keywords.customrl = function(task) + local rspamd_logger = require "rspamd_logger" + -- get authenticated user + local user = task:get_user() + -- define a default ratelimit + local default_rl = "10 / 1m" + if not user then return end -- no user, return nil + local user_rl = d['badusers']:get_key(user) + if user_rl then + local limit, duration, unit = string.match(user_rl, "(%d+)%s-/%s-(%d+)(%a*)") + if limit and duration then + duration = tonumber(duration) + if unit == 'm' then + duration = duration * 60 -- convert minutes to seconds + elseif unit == 'h' then + duration = duration * 3600 -- convert hours to seconds + elseif unit == 'd' then + duration = duration * 86400 -- convert days to seconds + end + local custom_rl = limit .. " / " .. duration .. "s" + rspamd_logger.infox(rspamd_config, "User %s has custom ratelimit: %s", user, custom_rl) + return "rs_customrl_" .. user, custom_rl + else + rspamd_logger.errx(rspamd_config, "Invalid ratelimit format for user %s, using default: %s", user, default_rl) + return "rs_customrl_" .. user, default_rl + end + else + rspamd_logger.infox(rspamd_config, "User %s not found in bad users map, using default ratelimit: %s", user, default_rl) + return "rs_customrl_" .. user, default_rl + end +end + +return custom_keywords +''' + + WriteToFile = open(rlLUA, 'w') + WriteToFile.write(rlLUAContent) + WriteToFile.close() + + @staticmethod def installRspamd(install, rspamd): from manageServices.serviceManager import ServiceManager @@ -1539,6 +1603,47 @@ LogFile /var/log/clamav/clamav.log # Handle errors, e.g., if reverse DNS lookup fails return None + @staticmethod + def SaveEmailLimitsNew(tempPath): + try: + content = open(tempPath, 'r').read() + email = content.split(' ')[0] + path = '/etc/rspamd/badusers.map' + + WriteCheck = 0 + + if os.path.exists(path): + data = open(path, 'r').readlines() + + WriteToFile = open(path, 'w') + + for line in data: + if line.find(email) > -1: + WriteToFile.write(content) + WriteCheck = 1 + else: + WriteToFile.write(line) + + if WriteCheck == 0: + WriteToFile.write(content) + + WriteToFile.close() + + else: + WriteToFile = open(path, 'w') + WriteToFile.write(content) + WriteToFile.close() + + command = 'systemctl restart rspamd' + ProcessUtilities.executioner(command) + + print(f'1,None') + + except BaseException as msg: + print(f'0,{str(msg)}') + + + ####### Imported below functions from mailserver/mailservermanager, need to refactor later class MailServerManagerUtils(multi.Thread): @@ -2547,6 +2652,10 @@ def main(): extraArgs = {'tempStatusPath': args.tempStatusPath} background = MailServerManagerUtils(None, 'ResetEmailConfigurations', extraArgs) background.ResetEmailConfigurations() + elif args.function == 'SetupEmailLimits': + mailUtilities.SetupEmailLimits() + elif args.function == 'SaveEmailLimitsNew': + mailUtilities.SaveEmailLimitsNew(args.tempConfigPath) if __name__ == "__main__": main()