email limits

This commit is contained in:
usmannasir
2024-02-26 16:12:28 +05:00
parent 63013ed8cc
commit f3e21ef1dd
7 changed files with 607 additions and 13 deletions

View File

@@ -77,7 +77,7 @@
<!-- HELPERS -->
{% with version="2.3.5.3" %}
{% with version="2.3.5.4" %}
<link rel="stylesheet" type="text/css" href="{% static 'baseTemplate/assets/finalBase/finalBase.css' %}">
@@ -586,6 +586,11 @@
title="{% trans 'Delete Email Account' %}"><span>{% trans "Delete Email" %}</span></a>
</li>
{% endif %}
{% if admin or emailForwarding %}
<li><a href="{% url 'EmailLimits' %}"
title="{% trans 'Email Limits' %}"><span>{% trans "Email Limits" %}</span></a>
</li>
{% endif %}
{% if admin or emailForwarding %}
<li><a href="{% url 'emailForwarding' %}"
title="{% trans 'Email Forwarding' %}"><span>{% trans "Email Forwarding" %}</span></a>
@@ -1052,12 +1057,12 @@
<li><a href="{% url 'mailQueue' %}"
title="{% trans 'Mail Queue' %}"><span>{% trans "Mail Queue" %}</span></a>
</li>
<li><a href="{% url 'emailPolicyServer' %}"
title="{% trans 'Email Policy Server' %}"><span>{% trans "Email Policy Server" %}</span></a>
</li>
<li><a href="{% url 'listDomains' %}"
title="{% trans 'Email Limits' %}"><span>{% trans "Email Limits" %}</span></a>
</li>
{# <li><a href="{% url 'emailPolicyServer' %}"#}
{# title="{% trans 'Email Policy Server' %}"><span>{% trans "Email Policy Server" %}</span></a>#}
{# </li>#}
{# <li><a href="{% url 'listDomains' %}"#}
{# title="{% trans 'Email Limits' %}"><span>{% trans "Email Limits" %}</span></a>#}
{# </li>#}
<li><a href="{% url 'SpamAssassin' %}"
title="{% trans 'SpamAssassin Configurations' %}"><span>{% trans "SpamAssassin" %}</span></a>
</li>

View File

@@ -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')

View File

@@ -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 */

View File

@@ -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 %}
<!-- Current language: {{ LANGUAGE_CODE }} -->
<div class="container">
<div id="page-title">
<h2>{% trans "Setup Email Limits" %} - <a target="_blank" href="https://cyberpanel.net/KnowledgeBase/home/email-limits/"
style="height: 23px;line-height: 21px;"
class="btn btn-border btn-alt border-red btn-link font-red"
title=""><span>{% trans "Email Limits Docs" %}</span></a></h2>
<p>{% trans "This page help you setup email limits for your emails." %}</p>
</div>
<div ng-controller="EmailLimitsNew" class="panel">
<div class="panel-body">
<h3 class="content-box-header">
{% trans "Setup Email Limits" %} <img ng-hide="forwardLoading"
src="{% static 'images/loading.gif' %}">
</h3>
<div class="example-box-wrapper">
{% if not status %}
<div class="col-md-12 text-center" style="margin-bottom: 2%;">
<h3>{% trans "Postfix is disabled." %}
<a href="{% url 'managePostfix' %}">
<button class="btn btn-alt btn-hover btn-blue-alt">
<span>{% trans "Enable Now" %}</span>
<i class="glyph-icon icon-arrow-right"></i>
</button>
</a></h3>
</div>
{% else %}
<form action="/" class="form-horizontal bordered-row panel-body">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Website" %} </label>
<div class="col-sm-6">
<select ng-change="showEmailDetails()" ng-model="emailDomain" class="form-control">
{% for items in websiteList %}
<option>{{ items }}</option>
{% endfor %}
</select>
</div>
</div>
<!------ Modification form that appears after a click --------------->
<div ng-hide="emailDetails" class="form-group">
<label class="col-sm-3 control-label">{% trans "Select Email" %} </label>
<div class="col-sm-6">
<select ng-change="selectForwardingEmail()" ng-model="selectedEmail"
class="form-control">
<option ng-repeat="email in emails track by $index">{$ email.email $}</option>
</select>
</div>
</div>
{# <div ng-hide="emailDetails" class="form-group">#}
{# <label class="col-sm-3 control-label">{% trans "Forwarding Options" %} </label>#}
{# <div class="col-sm-6">#}
{# <select ng-change="selectForwardingEmail()" ng-model="forwardingOption" class="form-control">#}
{# <option>Forward to email</option>#}
{# <option>Pipe to program</option>#}
{# </select>#}
{# </div>#}
{# </div>#}
<!------ Modification form that appears after a click --------------->
<div ng-hide="notifyBox" class="form-group">
<label class="col-sm-3 control-label"></label>
<div class="col-sm-4">
<div ng-hide="forwardError" class="alert alert-danger">
<p>{$ errorMessage $}</p>
</div>
<div ng-hide="forwardSuccess" class="alert alert-success">
<p>{$ successMessage $}</p>
</div>
<div ng-hide="couldNotConnect" class="alert alert-danger">
<p>{% trans "Could not connect to server. Please refresh this page." %}</p>
</div>
</div>
</div>
<!------ List of records --------------->
<div ng-hide="creationBox" class="form-group">
<div class="col-sm-12">
<div class="alert alert-info">
<p>{$ selectedEmail $} will be able to send {$ numberofEmails $} emails every {$ duration $}.</p>
</div>
</div>
<div class="col-sm-4">
<input placeholder="{% trans 'Number of Emails' %}" type="number" class="form-control"
ng-model="numberofEmails">
</div>
<div class="col-sm-4">
<input placeholder="{% trans 'Duration' %} {% trans 'or path to the program' %}"
type="text" class="form-control" ng-model="duration" required>
</div>
<div class="col-sm-4">
<button style="width: 100%;" type="button" ng-click="SaveChanges()"
class="btn btn-primary">{% trans "Save Changes" %}</button>
</div>
</div>
</form>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@@ -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
url(r'^EmailLimits$', views.EmailLimits, name='EmailLimits'),
url(r'^SaveEmailLimitsNew$', views.SaveEmailLimitsNew, name='SaveEmailLimitsNew'),
]

View File

@@ -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)

View File

@@ -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()