mirror of
https://github.com/marcus-alicia/iRedAdmin-Pro-SQL.git
synced 2026-06-03 02:49:45 +00:00
Add files via upload
This commit is contained in:
56
libs/amavisd/__init__.py
Normal file
56
libs/amavisd/__init__.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
from libs import iredutils
|
||||
|
||||
# mail_id and secret_id are composed of below characters:
|
||||
# - Amavisd-new-2.7+: [ A-Z, a-z, 0-9, -, _ ]
|
||||
# - Amavisd-new-2.6.x: [ A-Z, a-z, 0-9, +, - ]
|
||||
MAIL_ID_CHARACTERS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+-_'
|
||||
|
||||
WBLIST_FORM_INPUT_NAMES = {'wl_sender': 'whitelistSender',
|
||||
'bl_sender': 'blacklistSender',
|
||||
'wl_rcpt': 'whitelistRecipient',
|
||||
'bl_rcpt': 'blacklistRecipient'}
|
||||
|
||||
# Available quarantined types in iRedAdmin web interface, and the short code
|
||||
# in `amavisd.msgs` sql table.
|
||||
QUARANTINE_TYPES = {'spam': 'S',
|
||||
'virus': 'V',
|
||||
'banned': 'B',
|
||||
'clean': 'C',
|
||||
'badheader': 'H',
|
||||
'badmime': 'M'}
|
||||
|
||||
# Value of `msgs.content` and comment.
|
||||
CONTENT_TYPES = {'B': 'Banned',
|
||||
'C': 'Clean',
|
||||
'H': 'Bad header',
|
||||
'M': 'Bad mime',
|
||||
'O': 'Oversized',
|
||||
'S': 'Spam',
|
||||
'T': 'MTA error',
|
||||
'V': 'Virus',
|
||||
'U': 'Unchecked'}
|
||||
|
||||
|
||||
def get_wblist_from_form(form, form_input_name):
|
||||
# Available form_input_name are listed in WBLIST_FORM_INPUT_NAMES
|
||||
input_name = WBLIST_FORM_INPUT_NAMES[form_input_name]
|
||||
|
||||
addresses = []
|
||||
for _line in form.get(input_name, '').splitlines():
|
||||
if _line:
|
||||
try:
|
||||
_line = str(_line)
|
||||
addresses.append(_line)
|
||||
except:
|
||||
pass
|
||||
|
||||
valid_addresses = []
|
||||
for addr in addresses:
|
||||
if iredutils.is_valid_wblist_address(addr) and (addr not in valid_addresses):
|
||||
valid_addresses.append(addr)
|
||||
else:
|
||||
continue
|
||||
|
||||
return valid_addresses
|
||||
572
libs/amavisd/log.py
Normal file
572
libs/amavisd/log.py
Normal file
@@ -0,0 +1,572 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import time
|
||||
import web
|
||||
import settings
|
||||
from libs import iredutils
|
||||
from libs.logger import logger, log_traceback
|
||||
from libs.amavisd import MAIL_ID_CHARACTERS
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
# Import backend related modules.
|
||||
if settings.backend == 'ldap':
|
||||
from libs.ldaplib import admin as ldap_lib_admin
|
||||
elif settings.backend in ['mysql', 'pgsql']:
|
||||
from libs.sqllib import admin as sql_lib_admin
|
||||
|
||||
|
||||
def delete_all_records(log_type=None, account=None):
|
||||
# Delete all records, or delete records older than one week.
|
||||
# :param log_type: sent, received
|
||||
# :param account: single email address, domain name, '@.'
|
||||
account_is_email = False
|
||||
account_is_domain = False
|
||||
maddr_ids = []
|
||||
managed_domains_reversed = []
|
||||
|
||||
if account:
|
||||
if iredutils.is_email(account):
|
||||
account_is_email = True
|
||||
elif iredutils.is_domain(account):
|
||||
account_is_domain = True
|
||||
else:
|
||||
if account == '@.':
|
||||
pass
|
||||
else:
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
# get `maddr.id` of this account
|
||||
if account_is_email:
|
||||
# user
|
||||
qr = web.conn_amavisd.select('maddr',
|
||||
vars={'account': account},
|
||||
what='id',
|
||||
where='email=$account',
|
||||
limit=1)
|
||||
if qr:
|
||||
maddr_ids.append(qr[0].id)
|
||||
elif account_is_domain:
|
||||
# domain
|
||||
reversed_domain = iredutils.reverse_amavisd_domain_names([account])[0]
|
||||
qr = web.conn_amavisd.select('maddr',
|
||||
vars={'account': reversed_domain},
|
||||
what='id',
|
||||
where='domain=$account')
|
||||
if qr:
|
||||
for r in qr:
|
||||
maddr_ids.append(r.id)
|
||||
|
||||
# no `maddr.id`, no mail log.
|
||||
if not maddr_ids:
|
||||
return True,
|
||||
else:
|
||||
if session.get('is_global_admin'):
|
||||
web.conn_amavisd.delete('msgs', where='1=1')
|
||||
web.conn_amavisd.delete('msgrcpt', where='1=1')
|
||||
return True,
|
||||
|
||||
# Get all managed domains by normal admin.
|
||||
managed_domains = []
|
||||
if settings.backend == 'ldap':
|
||||
_qr = ldap_lib_admin.get_managed_domains(admin=session.get('username'))
|
||||
if _qr[0]:
|
||||
managed_domains = _qr[1]
|
||||
|
||||
elif settings.backend in ['mysql', 'pgsql']:
|
||||
qr = sql_lib_admin.get_managed_domains(admin=session.get('username'),
|
||||
domain_name_only=True)
|
||||
|
||||
if qr[0]:
|
||||
managed_domains = qr[1]
|
||||
else:
|
||||
return False, 'UNKNOWN_BACKEND'
|
||||
|
||||
managed_domains_reversed = iredutils.reverse_amavisd_domain_names(managed_domains)
|
||||
if not managed_domains_reversed:
|
||||
return True,
|
||||
|
||||
try:
|
||||
# Delete records in tables: msgs, msgrcpt.
|
||||
if log_type == 'sent':
|
||||
if account:
|
||||
# Delete all records sent by single user
|
||||
web.conn_amavisd.delete('msgs',
|
||||
vars={'maddr_ids': maddr_ids},
|
||||
where='sid IN $maddr_ids')
|
||||
else:
|
||||
# Delete all records sent by domain users
|
||||
web.conn_amavisd.delete('msgs',
|
||||
vars={'managed_domains_reversed': managed_domains_reversed},
|
||||
where='sid IN (SELECT id FROM maddr WHERE domain IN $managed_domains_reversed)')
|
||||
|
||||
elif log_type == 'received':
|
||||
if account:
|
||||
web.conn_amavisd.delete('msgs',
|
||||
vars={'maddr_ids': maddr_ids},
|
||||
where='mail_id IN (SELECT mail_id FROM msgrcpt WHERE rid IN $maddr_ids)')
|
||||
|
||||
web.conn_amavisd.delete('msgrcpt',
|
||||
vars={'maddr_ids': maddr_ids},
|
||||
where='rid IN $maddr_ids')
|
||||
else:
|
||||
all_rcpt_ids = [] # maddr.id
|
||||
|
||||
qr = web.conn_amavisd.select('maddr',
|
||||
vars={'domains': managed_domains_reversed},
|
||||
what='id',
|
||||
where='domain IN $domains')
|
||||
for i in qr:
|
||||
all_rcpt_ids.append(i['id'])
|
||||
|
||||
del qr
|
||||
|
||||
web.conn_amavisd.delete('msgs',
|
||||
vars={'ids': all_rcpt_ids},
|
||||
where='mail_id IN (SELECT mail_id FROM msgrcpt WHERE rid IN $ids)')
|
||||
|
||||
web.conn_amavisd.delete('msgrcpt',
|
||||
vars={'ids': all_rcpt_ids},
|
||||
where='rid IN $ids')
|
||||
|
||||
del all_rcpt_ids
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def delete_records_by_mail_id(log_type='sent', mail_ids=None):
|
||||
# log_type -- received, sent, quarantined, quarantine
|
||||
if not isinstance(mail_ids, list):
|
||||
return False, 'INCORRECT_MAILID'
|
||||
|
||||
# Filter unexpected mail_id strings.
|
||||
mail_ids = [v for v in mail_ids if len(set(v) - set(MAIL_ID_CHARACTERS)) == 0]
|
||||
|
||||
if not mail_ids:
|
||||
return True,
|
||||
|
||||
# Converted into SQL style list.
|
||||
mail_ids = web.sqlquote(mail_ids)
|
||||
|
||||
if log_type in ['received', 'sent', 'quarantined', 'quarantine']:
|
||||
try:
|
||||
# Delete records in tables: msgs, msgrcpt.
|
||||
web.conn_amavisd.delete('msgs', where='mail_id IN %s' % mail_ids)
|
||||
web.conn_amavisd.delete('msgrcpt', where='mail_id IN %s' % mail_ids)
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
if log_type in ['quarantined', 'quarantine']:
|
||||
try:
|
||||
web.conn_amavisd.delete('quarantine', where="mail_id IN %s" % mail_ids)
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
return True,
|
||||
|
||||
|
||||
def count_incoming_mails(reversedDomainNames=None,
|
||||
timeLength=None,
|
||||
sqlAppendWhere=None):
|
||||
# timeLength is seconds.
|
||||
total = 0
|
||||
|
||||
if not reversedDomainNames:
|
||||
if not session.get('account_is_mail_user'):
|
||||
return total
|
||||
|
||||
if sqlAppendWhere:
|
||||
sql_append_where = sqlAppendWhere
|
||||
else:
|
||||
sql_append_where = ' AND recip.domain IN %s' % web.sqlquote(reversedDomainNames)
|
||||
|
||||
if isinstance(timeLength, int):
|
||||
_now = int(time.time())
|
||||
_length_seconds = _now - timeLength
|
||||
sql_append_where += ' AND msgs.time_num > %d' % _length_seconds
|
||||
|
||||
try:
|
||||
qr = web.conn_amavisd.query('''
|
||||
-- Get number of incoming mails.
|
||||
SELECT COUNT(msgs.mail_id) AS total
|
||||
FROM msgs
|
||||
LEFT JOIN msgrcpt ON (msgs.mail_id = msgrcpt.mail_id)
|
||||
LEFT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
LEFT JOIN maddr AS recip ON (msgrcpt.rid = recip.id)
|
||||
WHERE msgs.quar_type <> 'Q' %s
|
||||
''' % sql_append_where)
|
||||
total = qr[0].total or 0
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
|
||||
return total
|
||||
|
||||
|
||||
def count_outgoing_mails(reversedDomainNames=None,
|
||||
timeLength=None,
|
||||
sqlAppendWhere=None):
|
||||
# timeLength is seconds.
|
||||
total = 0
|
||||
sql_append_where = ''
|
||||
|
||||
if not reversedDomainNames:
|
||||
return total
|
||||
|
||||
if sqlAppendWhere:
|
||||
sql_append_where = sqlAppendWhere
|
||||
else:
|
||||
sql_append_where += ' AND sender.domain IN %s' % web.sqlquote(reversedDomainNames)
|
||||
|
||||
if isinstance(timeLength, int):
|
||||
_now = int(time.time())
|
||||
_length_seconds = _now - timeLength
|
||||
sql_append_where += ' AND msgs.time_num > %d' % _length_seconds
|
||||
|
||||
try:
|
||||
qr_count = web.conn_amavisd.query("""
|
||||
-- Get number of outgoing mails.
|
||||
SELECT COUNT(msgs.mail_id) AS total
|
||||
FROM msgs
|
||||
RIGHT JOIN msgrcpt ON (msgs.mail_id = msgrcpt.mail_id)
|
||||
RIGHT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
RIGHT JOIN maddr AS recip ON (msgrcpt.rid = recip.id)
|
||||
WHERE msgs.quar_type <> 'Q' %s""" % sql_append_where)
|
||||
total = qr_count[0].total or 0
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return total
|
||||
|
||||
|
||||
def count_virus_mails(reversedDomainNames=None, timeLength=None):
|
||||
# timeLength is seconds.
|
||||
total = 0
|
||||
sql_append_where = ''
|
||||
|
||||
if not reversedDomainNames:
|
||||
return total
|
||||
|
||||
if session.get('is_global_admin') is not True:
|
||||
sql_append_where += ' AND (sender.domain IN {} OR recip.domain IN {})'.format(
|
||||
web.sqlquote(reversedDomainNames),
|
||||
web.sqlquote(reversedDomainNames),
|
||||
)
|
||||
|
||||
if isinstance(timeLength, int):
|
||||
_now = int(time.time())
|
||||
_length_seconds = _now - timeLength
|
||||
sql_append_where += ' AND msgs.time_num > %d' % _length_seconds
|
||||
|
||||
try:
|
||||
qr = web.conn_amavisd.query("""
|
||||
SELECT COUNT(msgs.mail_id) AS total
|
||||
FROM msgs
|
||||
RIGHT JOIN msgrcpt ON (msgs.mail_id = msgrcpt.mail_id)
|
||||
RIGHT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
RIGHT JOIN maddr AS recip ON (msgrcpt.rid = recip.id)
|
||||
WHERE msgs.content = 'V'
|
||||
AND msgs.quar_type='Q'
|
||||
%s
|
||||
""" % sql_append_where)
|
||||
total = qr[0].total or 0
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return total
|
||||
|
||||
|
||||
def count_quarantined(reversedDomainNames=None, timeLength=None):
|
||||
# timeLength is seconds.
|
||||
total = 0
|
||||
sql_append_where = ''
|
||||
|
||||
if not session.get('is_global_admin'):
|
||||
sql_append_where += ' AND (sender.domain IN {} OR recip.domain IN {})'.format(
|
||||
web.sqlquote(reversedDomainNames),
|
||||
web.sqlquote(reversedDomainNames),
|
||||
)
|
||||
|
||||
if isinstance(timeLength, int):
|
||||
_now = int(time.time())
|
||||
_length_seconds = _now - timeLength
|
||||
sql_append_where += ' AND msgs.time_num > %d' % _length_seconds
|
||||
|
||||
try:
|
||||
if session.get('is_global_admin'):
|
||||
qr = web.conn_amavisd.query("""
|
||||
SELECT COUNT(msgs.mail_id) AS total
|
||||
FROM msgs
|
||||
RIGHT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
WHERE msgs.quar_type = 'Q' %s
|
||||
""" % sql_append_where)
|
||||
else:
|
||||
qr = web.conn_amavisd.query("""
|
||||
SELECT COUNT(msgs.mail_id) AS total
|
||||
FROM msgs
|
||||
RIGHT JOIN msgrcpt ON (msgs.mail_id = msgrcpt.mail_id)
|
||||
RIGHT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
RIGHT JOIN maddr AS recip ON (msgrcpt.rid = recip.id)
|
||||
WHERE msgs.quar_type = 'Q' %s
|
||||
""" % sql_append_where)
|
||||
|
||||
total = qr[0].total or 0
|
||||
except:
|
||||
log_traceback()
|
||||
|
||||
return total
|
||||
|
||||
|
||||
def get_in_out_mails(log_type='sent',
|
||||
cur_page=1,
|
||||
account_type='',
|
||||
account='',
|
||||
page_size_limit=None):
|
||||
"""
|
||||
@account_type: 'domain', 'user', None
|
||||
@log_type: 'sent', 'received', 'all'
|
||||
|
||||
@return (True, {'count': <int>, 'records': <list>}
|
||||
"""
|
||||
log_type = str(log_type)
|
||||
cur_page = int(cur_page)
|
||||
account_type = str(account_type) or None
|
||||
account = str(account) or None
|
||||
|
||||
result = {'count': 0, 'records': []}
|
||||
count = 0 # Number of total mails.
|
||||
records = {} # Detail records.
|
||||
sql_append_where = ''
|
||||
reversed_account = ''
|
||||
|
||||
if not page_size_limit:
|
||||
page_size_limit = settings.PAGE_SIZE_LIMIT
|
||||
|
||||
if account_type == 'domain':
|
||||
reversed_account = iredutils.reverse_amavisd_domain_names([account])
|
||||
|
||||
# Get all managed domain names and reversed names.
|
||||
all_domains = []
|
||||
allReversedDomainNames = []
|
||||
quoted_all_reversed_domain_names = []
|
||||
sql_restricted_sender_domains = ''
|
||||
sql_restricted_recip_domains = ''
|
||||
if not session.get('account_is_mail_user'):
|
||||
if settings.backend == 'ldap':
|
||||
_qr = ldap_lib_admin.get_managed_domains(admin=session.get('username'))
|
||||
if _qr[0]:
|
||||
all_domains = _qr[1]
|
||||
elif settings.backend in ['mysql', 'pgsql']:
|
||||
qr_all_domains = sql_lib_admin.get_managed_domains(admin=session.get('username'),
|
||||
domain_name_only=True)
|
||||
if qr_all_domains[0]:
|
||||
all_domains += qr_all_domains[1]
|
||||
else:
|
||||
result['count'] = count
|
||||
result['records'] = list(records)
|
||||
return True, result
|
||||
|
||||
allReversedDomainNames = iredutils.reverse_amavisd_domain_names(all_domains)
|
||||
quoted_all_reversed_domain_names = web.sqlquote(allReversedDomainNames)
|
||||
sql_restricted_sender_domains = ' AND sender.domain IN %s' % quoted_all_reversed_domain_names
|
||||
sql_restricted_recip_domains = ' AND recip.domain IN %s' % quoted_all_reversed_domain_names
|
||||
|
||||
# restrict permission for per-account search
|
||||
# @log_type == 'sent'
|
||||
# - if domain is under control, no restriction
|
||||
# - if domain is not under control, restrict recipient domain to managed domains
|
||||
# @log_type == 'received'
|
||||
# - if domain is under control, no restriction
|
||||
# - if domain is not under control, restrict sender domain to managed domains
|
||||
verify_domain = account
|
||||
if account_type == 'user':
|
||||
verify_domain = account.split('@', 1)[-1]
|
||||
|
||||
if log_type == 'received':
|
||||
if account_type == 'domain':
|
||||
if session.get('is_global_admin') or verify_domain in all_domains:
|
||||
sql_append_where += ' AND recip.domain IN %s' % web.sqlquote(reversed_account)
|
||||
else:
|
||||
sql_append_where += ' {} AND recip.domain IN {}'.format(sql_restricted_sender_domains, web.sqlquote(reversed_account))
|
||||
elif account_type == 'user':
|
||||
if session.get('is_global_admin') or verify_domain in all_domains:
|
||||
sql_append_where += ' AND recip.email=%s' % web.sqlquote(account)
|
||||
else:
|
||||
sql_append_where += ' {} AND recip.email={}'.format(sql_restricted_sender_domains, web.sqlquote(account))
|
||||
else:
|
||||
if settings.AMAVISD_SHOW_NON_LOCAL_DOMAINS:
|
||||
if session.get('is_global_admin'):
|
||||
pass
|
||||
else:
|
||||
if not quoted_all_reversed_domain_names:
|
||||
return True, result
|
||||
else:
|
||||
sql_append_where += ' AND recip.domain IN %s' % quoted_all_reversed_domain_names
|
||||
else:
|
||||
if not quoted_all_reversed_domain_names:
|
||||
return True, result
|
||||
else:
|
||||
sql_append_where += ' AND recip.domain IN %s' % quoted_all_reversed_domain_names
|
||||
|
||||
elif log_type == 'sent':
|
||||
if account_type == 'domain':
|
||||
if session.get('is_global_admin') or verify_domain in all_domains:
|
||||
sql_append_where += ' AND sender.domain IN %s' % web.sqlquote(reversed_account)
|
||||
else:
|
||||
sql_append_where += ' {} AND sender.domain IN {}'.format(sql_restricted_recip_domains, web.sqlquote(reversed_account))
|
||||
elif account_type == 'user':
|
||||
if session.get('is_global_admin') or verify_domain in all_domains:
|
||||
sql_append_where += ' AND sender.email = %s' % (web.sqlquote(account))
|
||||
else:
|
||||
sql_append_where += ' {} AND sender.email = {}'.format(sql_restricted_recip_domains, web.sqlquote(account))
|
||||
else:
|
||||
if settings.AMAVISD_SHOW_NON_LOCAL_DOMAINS:
|
||||
if session.get('is_global_admin'):
|
||||
pass
|
||||
else:
|
||||
if not quoted_all_reversed_domain_names:
|
||||
return True, result
|
||||
else:
|
||||
sql_append_where += ' AND sender.domain IN %s' % quoted_all_reversed_domain_names
|
||||
else:
|
||||
if not quoted_all_reversed_domain_names:
|
||||
return True, result
|
||||
else:
|
||||
sql_append_where += ' AND sender.domain IN %s' % quoted_all_reversed_domain_names
|
||||
|
||||
########################
|
||||
# Get detail records.
|
||||
#
|
||||
try:
|
||||
if log_type == 'received':
|
||||
count = count_incoming_mails(allReversedDomainNames,
|
||||
sqlAppendWhere=sql_append_where)
|
||||
|
||||
qr = web.conn_amavisd.query(
|
||||
'''
|
||||
-- Get records of received mails.
|
||||
SELECT
|
||||
msgs.mail_id, msgs.subject, msgs.time_num,
|
||||
msgs.size, msgs.spam_level, msgs.client_addr, msgs.policy,
|
||||
sender.email_raw AS sender_email,
|
||||
recip.email_raw AS recipient
|
||||
FROM msgs
|
||||
LEFT JOIN msgrcpt ON (msgs.mail_id = msgrcpt.mail_id)
|
||||
LEFT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
LEFT JOIN maddr AS recip ON (msgrcpt.rid = recip.id)
|
||||
WHERE msgs.quar_type <> 'Q' %s
|
||||
ORDER BY msgs.time_num DESC
|
||||
LIMIT %d
|
||||
OFFSET %d
|
||||
''' % (sql_append_where,
|
||||
page_size_limit,
|
||||
(cur_page - 1) * page_size_limit)
|
||||
)
|
||||
records = iredutils.bytes2str(qr)
|
||||
elif log_type == 'sent':
|
||||
count = count_outgoing_mails(allReversedDomainNames,
|
||||
sqlAppendWhere=sql_append_where)
|
||||
|
||||
qr = web.conn_amavisd.query(
|
||||
'''
|
||||
-- Get records of sent mails.
|
||||
SELECT
|
||||
msgs.mail_id, msgs.subject, msgs.time_num,
|
||||
msgs.size, msgs.client_addr, msgs.policy,
|
||||
sender.email_raw AS sender_email,
|
||||
recip.email_raw AS recipient
|
||||
FROM msgs
|
||||
RIGHT JOIN msgrcpt ON (msgs.mail_id = msgrcpt.mail_id)
|
||||
RIGHT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
RIGHT JOIN maddr AS recip ON (msgrcpt.rid = recip.id)
|
||||
WHERE msgs.quar_type <> 'Q' %s
|
||||
ORDER BY msgs.time_num DESC
|
||||
LIMIT %d
|
||||
OFFSET %d
|
||||
''' % (sql_append_where,
|
||||
page_size_limit,
|
||||
(cur_page - 1) * page_size_limit)
|
||||
)
|
||||
records = iredutils.bytes2str(qr)
|
||||
else:
|
||||
records = {}
|
||||
except:
|
||||
pass
|
||||
|
||||
return True, {'count': count, 'records': list(records)}
|
||||
|
||||
|
||||
def get_top_users(reversedDomainNames=None,
|
||||
log_type='sent',
|
||||
timeLength=None,
|
||||
number=10):
|
||||
records = {}
|
||||
sql_append_where = ''
|
||||
|
||||
if settings.AMAVISD_SHOW_NON_LOCAL_DOMAINS:
|
||||
if session.get('is_global_admin'):
|
||||
pass
|
||||
else:
|
||||
if not reversedDomainNames:
|
||||
return []
|
||||
else:
|
||||
if log_type == 'sent':
|
||||
sql_append_where += ' AND sender.domain IN %s' % web.sqlquote(reversedDomainNames)
|
||||
elif log_type == 'received':
|
||||
sql_append_where += ' AND rcpt.domain IN %s' % web.sqlquote(reversedDomainNames)
|
||||
else:
|
||||
if log_type == 'sent':
|
||||
sql_append_where += ' AND sender.domain IN %s' % web.sqlquote(reversedDomainNames)
|
||||
elif log_type == 'received':
|
||||
sql_append_where += ' AND rcpt.domain IN %s' % web.sqlquote(reversedDomainNames)
|
||||
|
||||
if isinstance(timeLength, int):
|
||||
_now = int(time.time())
|
||||
_length_seconds = _now - timeLength
|
||||
sql_append_where += ' AND msgs.time_num > %d' % _length_seconds
|
||||
|
||||
# `msgs.policy` (Amavisd policy bank) is used to identify account type.
|
||||
# for example, 'MLMMJ' means mlmmj mailing list.
|
||||
if log_type == 'sent':
|
||||
try:
|
||||
result = web.conn_amavisd.query(
|
||||
"""
|
||||
-- Get top 10 senders.
|
||||
SELECT COUNT(msgs.mail_id) AS total,
|
||||
sender.email_raw AS mail,
|
||||
msgs.policy AS policy
|
||||
FROM msgs
|
||||
RIGHT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
WHERE 1=1 %s
|
||||
GROUP BY mail, policy
|
||||
ORDER BY total DESC
|
||||
LIMIT %d
|
||||
""" % (sql_append_where, number))
|
||||
records = list(result)
|
||||
except:
|
||||
log_traceback()
|
||||
|
||||
elif log_type == 'received':
|
||||
try:
|
||||
result = web.conn_amavisd.query(
|
||||
"""
|
||||
-- Get top 10 recipients
|
||||
SELECT COUNT(msgs.mail_id) AS total,
|
||||
rcpt.email_raw AS mail
|
||||
FROM msgs
|
||||
RIGHT JOIN msgrcpt ON (msgs.mail_id = msgrcpt.mail_id)
|
||||
RIGHT JOIN maddr AS sender ON (msgs.sid = sender.id)
|
||||
RIGHT JOIN maddr AS rcpt ON (msgrcpt.rid = rcpt.id)
|
||||
WHERE 1=1 %s
|
||||
GROUP BY mail
|
||||
ORDER BY total DESC
|
||||
LIMIT %d
|
||||
""" % (sql_append_where, number))
|
||||
records = list(result)
|
||||
except:
|
||||
log_traceback()
|
||||
|
||||
records = iredutils.bytes2str(records)
|
||||
return list(records)
|
||||
315
libs/amavisd/quarantine.py
Normal file
315
libs/amavisd/quarantine.py
Normal file
@@ -0,0 +1,315 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import socket
|
||||
import web
|
||||
import settings
|
||||
from libs import iredutils
|
||||
from libs.amavisd import QUARANTINE_TYPES
|
||||
|
||||
session = web.config.get("_session")
|
||||
|
||||
# Import backend related modules.
|
||||
if settings.backend == "ldap":
|
||||
from libs.ldaplib.admin import get_managed_domains
|
||||
elif settings.backend in ["mysql", "pgsql"]:
|
||||
from libs.sqllib.admin import get_managed_domains
|
||||
|
||||
|
||||
def get_raw_message(mail_id: str) -> bytes:
|
||||
"""Get raw mail message of quarantined email specified by `mail_id`."""
|
||||
# TODO Check domain access by sender/recipient of quarantined email
|
||||
if not mail_id:
|
||||
return False, "INVALID_MAILID"
|
||||
|
||||
try:
|
||||
records = web.conn_amavisd.select(
|
||||
"quarantine",
|
||||
vars={"mail_id": mail_id},
|
||||
what="mail_text",
|
||||
where="mail_id=$mail_id",
|
||||
order="chunk_ind ASC",
|
||||
)
|
||||
|
||||
if not records:
|
||||
return False, "INVALID_MAILID"
|
||||
|
||||
# Combine mail_text as RAW mail message.
|
||||
# Note: `mail_text` is bytes type.
|
||||
message = b""
|
||||
records = list(records)
|
||||
for i in records:
|
||||
message += i['mail_text']
|
||||
|
||||
return True, message
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
# If msgs.quar_type != "Q" (SQL), we can't get mail body.
|
||||
def get_quarantined_mails(page=1,
|
||||
account_type=None,
|
||||
account="",
|
||||
quarantined_type="",
|
||||
size_limit=settings.PAGE_SIZE_LIMIT,
|
||||
sort_by_score=False):
|
||||
"""Return ([True | False], (total, records))"""
|
||||
|
||||
page = int(page)
|
||||
account = str(account) or None
|
||||
|
||||
# Pre-defined values.
|
||||
count = 0
|
||||
records = []
|
||||
sql_append_selection = ''
|
||||
|
||||
# Domain names under control.
|
||||
all_domains = []
|
||||
|
||||
# Query SQL.
|
||||
if session.get('is_normal_admin'):
|
||||
# List all managed domains in query if admin is not global admin
|
||||
qr = get_managed_domains(admin=session.get('username'), domain_name_only=True)
|
||||
if qr[0]:
|
||||
all_domains = qr[1]
|
||||
|
||||
all_reversed_domains = iredutils.reverse_amavisd_domain_names(all_domains)
|
||||
|
||||
if all_domains:
|
||||
sql_append_selection += ' AND (sender.domain IN {} OR recip.domain IN {})'.format(
|
||||
web.sqlquote(all_reversed_domains),
|
||||
web.sqlquote(all_reversed_domains),
|
||||
)
|
||||
else:
|
||||
return True, (0, {})
|
||||
|
||||
if account_type == 'domain':
|
||||
if account:
|
||||
reversed_account = iredutils.reverse_amavisd_domain_names([account])[0]
|
||||
|
||||
if not session.get('is_global_admin'):
|
||||
# Make sure account is managed domain
|
||||
if account not in all_domains:
|
||||
# PERMISSION_DENIED
|
||||
return True, (0, {})
|
||||
|
||||
sql_append_selection += ' AND (sender.domain={} OR recip.domain={})'.format(
|
||||
web.sqlquote(reversed_account), web.sqlquote(reversed_account),
|
||||
)
|
||||
elif account_type == 'user':
|
||||
if session.get('is_normal_admin'):
|
||||
# Make sure account is under managed domains
|
||||
if not account.split('@', 1)[-1] in all_domains:
|
||||
# PERMISSION_DENIED
|
||||
return True, (0, {})
|
||||
elif session.get('account_is_mail_user'):
|
||||
if account != session['username']:
|
||||
return True, (0, {})
|
||||
|
||||
sql_append_selection += ' AND (sender.email={} OR recip.email={})'.format(
|
||||
web.sqlquote(account),
|
||||
web.sqlquote(account),
|
||||
)
|
||||
|
||||
if quarantined_type == 'spam':
|
||||
sql_append_selection += " AND msgs.content IN ('S', 's', 'Y')"
|
||||
elif quarantined_type == 'virus':
|
||||
sql_append_selection += " AND msgs.content = 'V'"
|
||||
elif quarantined_type == 'banned':
|
||||
sql_append_selection += " AND msgs.content = 'B'"
|
||||
elif quarantined_type == 'badheader':
|
||||
sql_append_selection += " AND msgs.content = 'H'"
|
||||
elif quarantined_type == 'badmime':
|
||||
sql_append_selection += " AND msgs.content = 'M'"
|
||||
|
||||
# Get number of total records. SQL table: amavisd.msgs
|
||||
try:
|
||||
# Refer to templates/default/macros/amavisd.html for more detail
|
||||
# about msgs.content (content type, spam status), msgs.quar_type
|
||||
# (quarantine type).
|
||||
result = web.conn_amavisd.query(
|
||||
"""
|
||||
-- Get number of quarantined emails
|
||||
SELECT COUNT(msgs.mail_id) AS total
|
||||
FROM msgs
|
||||
LEFT JOIN msgrcpt ON msgs.mail_id = msgrcpt.mail_id
|
||||
LEFT JOIN maddr AS sender ON msgs.sid = sender.id
|
||||
LEFT JOIN maddr AS recip ON msgrcpt.rid = recip.id
|
||||
WHERE
|
||||
-- msgs.content IN ('S', 's', 'Y', 'V', 'B', 'H')
|
||||
-- AND msgs.quar_type = 'Q'
|
||||
msgs.quar_type = 'Q'
|
||||
%s
|
||||
""" % sql_append_selection)
|
||||
|
||||
count = result[0].total or 0
|
||||
except:
|
||||
pass
|
||||
|
||||
# Get records of quarantined mails.
|
||||
try:
|
||||
# msgs.content:
|
||||
# - S: spam(kill)
|
||||
# - s: prior to 2.7.0 the CC_SPAMMY was logged as 's', now 'Y' is used.
|
||||
# msgs.quar_type:
|
||||
# - Q: sql
|
||||
# - F: file
|
||||
sort_column = 'msgs.time_num'
|
||||
if sort_by_score:
|
||||
sort_column = 'msgs.spam_level'
|
||||
|
||||
result = web.conn_amavisd.query(
|
||||
'''
|
||||
-- Get records of quarantined mails.
|
||||
SELECT
|
||||
msgs.mail_id, msgs.secret_id, msgs.subject, msgs.time_num,
|
||||
msgs.content, msgs.size, msgs.spam_level,
|
||||
sender.email AS sender_email,
|
||||
recip.email AS recipient
|
||||
FROM msgs
|
||||
LEFT JOIN msgrcpt ON msgs.mail_id = msgrcpt.mail_id
|
||||
LEFT JOIN maddr AS sender ON msgs.sid = sender.id
|
||||
LEFT JOIN maddr AS recip ON msgrcpt.rid = recip.id
|
||||
WHERE
|
||||
-- msgs.content IN ('S', 's', 'Y', 'V', 'B', 'H')
|
||||
-- AND msgs.quar_type = 'Q'
|
||||
msgs.quar_type = 'Q'
|
||||
%s
|
||||
ORDER BY %s DESC
|
||||
LIMIT %d
|
||||
OFFSET %d
|
||||
''' % (sql_append_selection, sort_column, size_limit, (page - 1) * size_limit)
|
||||
)
|
||||
records = iredutils.bytes2str(result)
|
||||
except:
|
||||
pass
|
||||
|
||||
return True, (count, records)
|
||||
|
||||
|
||||
def delete_all_quarantined(quarantined_type=None):
|
||||
if quarantined_type in QUARANTINE_TYPES:
|
||||
_content = QUARANTINE_TYPES[quarantined_type]
|
||||
|
||||
# Delete them from `msgs`.
|
||||
# Records in `quarantine` will be cleaned up by cron job
|
||||
try:
|
||||
web.conn_amavisd.delete(
|
||||
'msgs',
|
||||
vars={'quar_type': 'Q', 'content': _content},
|
||||
where='quar_type=$quar_type AND content=$content',
|
||||
)
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
else:
|
||||
try:
|
||||
web.conn_amavisd.delete('quarantine', where='1=1')
|
||||
web.conn_amavisd.delete('msgs', where="""quar_type='Q'""")
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def release_quarantined_mails(records=None):
|
||||
# Release quarantined mails.
|
||||
#
|
||||
# records = [
|
||||
# {'mail_id': 'xxx',
|
||||
# 'secret_id': 'yyy',
|
||||
# 'requested_by': session.get('username'),
|
||||
# },
|
||||
# [],
|
||||
# ]
|
||||
#
|
||||
# Refer to amavisd doc 'README.protocol' for more detail:
|
||||
# - Releasing a message from a quarantine
|
||||
|
||||
if not records:
|
||||
return True,
|
||||
|
||||
# TODO Check domain_access
|
||||
# - Get managed domains.
|
||||
# - Check whether mail_id in `records` are one of managed domains.
|
||||
# - Get allowed mail_id list.
|
||||
|
||||
# Pre-defined variables.
|
||||
released_mail_ids = []
|
||||
|
||||
# Create socket.
|
||||
try:
|
||||
quar_server = settings.amavisd_db_host
|
||||
quar_port = int(settings.amavisd_quarantine_port)
|
||||
|
||||
if settings.AMAVISD_QUARANTINE_HOST:
|
||||
quar_server = settings.AMAVISD_QUARANTINE_HOST
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect((quar_server, quar_port))
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
# Generate commands from dict, used for socket communication.
|
||||
# Note: We need to update Amavisd SQL database after mail was released
|
||||
# with success, so do NOT send all release requests in ONE socket
|
||||
# command although it will get better performance (a little).
|
||||
for record in records:
|
||||
# Skip record without 'mail_id'.
|
||||
if 'mail_id' not in record:
|
||||
continue
|
||||
|
||||
cmd_release = 'request=release\r\n'
|
||||
|
||||
for k in record:
|
||||
if record[k] is not None and record[k] != '':
|
||||
cmd_release += '{}={}\r\n'.format(k, record[k])
|
||||
|
||||
cmd_release += 'quar_type=Q\r\n\r\n'
|
||||
try:
|
||||
s.send(cmd_release.encode())
|
||||
|
||||
# Must wait for Amavisd's response before deleting SQL record,
|
||||
# otherwise we may delete sql record BEFORE Amavisd releases
|
||||
# quarantined email.
|
||||
s.recv(1024)
|
||||
|
||||
released_mail_ids += [record.get('mail_id', 'NOT-EXIST')]
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
# Close socket.
|
||||
try:
|
||||
s.close()
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
# Return if no record was released successfully.
|
||||
if len(released_mail_ids) == 0:
|
||||
return True,
|
||||
|
||||
# Update Amavisd SQL database.
|
||||
try:
|
||||
# - Update msgs.content to 'C' (Clean)
|
||||
# UPDATE msgs \
|
||||
# SET msgs.content = 'C' \
|
||||
# WHERE msgs.mail_id IN ('xxx', 'yyy', ..)
|
||||
#
|
||||
# - Delete records in 'quarantine':
|
||||
# DELETE FROM quarantine \
|
||||
# WHERE quarantine.partition_tag = msgs.partition_tag \
|
||||
# AND quarantine.mail_id = msgs.mail_id
|
||||
#
|
||||
web.conn_amavisd.update(
|
||||
'msgs',
|
||||
where='mail_id IN ' + web.sqlquote(released_mail_ids),
|
||||
quar_type='',
|
||||
content='C',
|
||||
)
|
||||
|
||||
web.conn_amavisd.delete(
|
||||
'quarantine',
|
||||
where='mail_id IN ' + web.sqlquote(released_mail_ids),
|
||||
)
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
350
libs/amavisd/spampolicy.py
Normal file
350
libs/amavisd/spampolicy.py
Normal file
@@ -0,0 +1,350 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
|
||||
from libs import form_utils
|
||||
from libs.iredutils import is_valid_amavisd_address
|
||||
from libs.amavisd import utils
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
DEFAULT_SPAM_TAG_LEVEL = 2
|
||||
DEFAULT_SPAM_TAG2_LEVEL = 6
|
||||
|
||||
# Builtin ban rule names.
|
||||
BUILTIN_BAN_RULE_NAMES = [
|
||||
"ALLOW_MS_OFFICE",
|
||||
"ALLOW_MS_WORD",
|
||||
"ALLOW_MS_EXCEL",
|
||||
"ALLOW_MS_PPT",
|
||||
]
|
||||
|
||||
|
||||
def delete_spam_policy(account):
|
||||
account = str(account).lower()
|
||||
|
||||
if not is_valid_amavisd_address(account):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
try:
|
||||
web.conn_amavisd.delete('policy',
|
||||
vars={'account': account},
|
||||
where='policy_name=$account')
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def get_spam_policy(account='@.'):
|
||||
account = str(account).lower()
|
||||
|
||||
if not is_valid_amavisd_address(account):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
try:
|
||||
sql_where = 'users.policy_id=policy.id AND users.email=$account'
|
||||
qr = web.conn_amavisd.select(
|
||||
['policy', 'users'],
|
||||
vars={'account': account},
|
||||
what='policy.*, users.id AS users_id',
|
||||
where=sql_where,
|
||||
limit=1,
|
||||
)
|
||||
if qr:
|
||||
policy = qr[0]
|
||||
return True, policy
|
||||
else:
|
||||
return True, {}
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def get_global_spam_score():
|
||||
score = DEFAULT_SPAM_TAG2_LEVEL
|
||||
|
||||
(success, policy) = get_spam_policy(account='@.')
|
||||
if success and policy:
|
||||
score = policy.get('spam_tag2_level', DEFAULT_SPAM_TAG2_LEVEL)
|
||||
|
||||
return score
|
||||
|
||||
|
||||
def update_spam_policy(account, form):
|
||||
account = str(account).lower()
|
||||
|
||||
if not is_valid_amavisd_address(account):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
if 'delete_policy' in form:
|
||||
try:
|
||||
web.conn_amavisd.delete(
|
||||
'policy',
|
||||
vars={'account': account},
|
||||
where='policy_name=$account',
|
||||
)
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
qr = utils.get_policy_record(account=account, create_if_missing=True)
|
||||
if qr[0]:
|
||||
policy_id = qr[1].id
|
||||
else:
|
||||
return qr
|
||||
|
||||
# Update spam policy
|
||||
updates = {
|
||||
'spam_lover': 'N',
|
||||
'virus_lover': 'N',
|
||||
'banned_files_lover': 'N',
|
||||
'bad_header_lover': 'N',
|
||||
'bypass_spam_checks': 'N',
|
||||
'bypass_virus_checks': 'N',
|
||||
'bypass_banned_checks': 'N',
|
||||
'bypass_header_checks': 'N',
|
||||
'banned_rulenames': "",
|
||||
}
|
||||
|
||||
if 'enable_spam_checks' not in form:
|
||||
updates['bypass_spam_checks'] = 'Y'
|
||||
|
||||
if 'enable_virus_checks' not in form:
|
||||
updates['bypass_virus_checks'] = 'Y'
|
||||
|
||||
if 'enable_banned_checks' not in form:
|
||||
updates['bypass_banned_checks'] = 'Y'
|
||||
|
||||
if 'enable_header_checks' not in form:
|
||||
updates['bypass_header_checks'] = 'Y'
|
||||
|
||||
updates['spam_quarantine_to'] = ''
|
||||
updates['virus_quarantine_to'] = 'virus-quarantine'
|
||||
updates['banned_quarantine_to'] = ''
|
||||
updates['bad_header_quarantine_to'] = ''
|
||||
|
||||
if 'spam_quarantine_to' in form:
|
||||
updates['spam_quarantine_to'] = 'spam-quarantine'
|
||||
# else:
|
||||
# updates['spam_lover'] = 'Y'
|
||||
|
||||
if 'virus_quarantine_to' not in form:
|
||||
# Deliver virus to mailbox.
|
||||
updates['virus_lover'] = 'Y'
|
||||
updates['virus_quarantine_to'] = ''
|
||||
|
||||
if 'banned_quarantine_to' in form:
|
||||
updates['banned_quarantine_to'] = 'banned-quarantine'
|
||||
# else:
|
||||
# updates['banned_files_lover'] = 'Y'
|
||||
|
||||
if 'bad_header_quarantine_to' in form:
|
||||
updates['bad_header_quarantine_to'] = 'bad-header-quarantine'
|
||||
else:
|
||||
updates['bad_header_lover'] = 'Y'
|
||||
|
||||
# Modify spam subject
|
||||
if 'modify_spam_subject' in form:
|
||||
updates['spam_subject_tag2'] = settings.AMAVISD_SPAM_SUBJECT_PREFIX
|
||||
else:
|
||||
updates['spam_subject_tag2'] = None
|
||||
|
||||
updates['spam_tag_level'] = None
|
||||
updates['spam_tag2_level'] = None
|
||||
updates['spam_kill_level'] = None
|
||||
|
||||
if account == '@.' and 'always_insert_x_spam_headers' in form:
|
||||
updates['spam_tag_level'] = -100
|
||||
|
||||
for p in ['spam_tag2_level', 'spam_kill_level']:
|
||||
_score = form.get(p, '')
|
||||
|
||||
if _score:
|
||||
try:
|
||||
updates[p] = float(_score)
|
||||
except:
|
||||
pass
|
||||
|
||||
if "banned_rulenames" in form:
|
||||
names = form.get("banned_rulenames", [])
|
||||
new_names = set()
|
||||
|
||||
for n in names:
|
||||
if (n in BUILTIN_BAN_RULE_NAMES) or (n in settings.AMAVISD_BAN_RULES):
|
||||
new_names.add(n)
|
||||
|
||||
# Sort the result for easier unittest.
|
||||
new_names = sorted(new_names)
|
||||
|
||||
updates["banned_rulenames"] = ",".join(new_names)
|
||||
|
||||
try:
|
||||
web.conn_amavisd.update(
|
||||
'policy',
|
||||
vars={'id': policy_id},
|
||||
where='id=$id',
|
||||
**updates)
|
||||
|
||||
qr = utils.link_policy_to_user(account=account, policy_id=policy_id)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
# Update `policy.spam_tag3_level` and `policy.spam_subject_tag3`
|
||||
# separately, these two columns don't exist in Amavisd-new-2.6.x.
|
||||
try:
|
||||
extra_updates = {'spam_tag3_level': updates['spam_tag2_level'],
|
||||
'spam_subject_tag3': updates['spam_subject_tag2']}
|
||||
|
||||
web.conn_amavisd.update(
|
||||
'policy',
|
||||
vars={'id': policy_id},
|
||||
where='id=$id',
|
||||
**extra_updates)
|
||||
except:
|
||||
pass
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def api_update_spam_policy(account, form):
|
||||
"""Create new spam policy or update existing policy."""
|
||||
account = str(account).lower()
|
||||
|
||||
if not is_valid_amavisd_address(account):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
# Get current `amavisd.policy.id`, it will create a new one if not present.
|
||||
qr = utils.get_policy_record(account=account, create_if_missing=True)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
# Set default policy
|
||||
policy = {
|
||||
'policy_name': account,
|
||||
# Default check policy: don't bypass checks
|
||||
'bypass_spam_checks': 'N',
|
||||
'bypass_virus_checks': 'N',
|
||||
'bypass_banned_checks': 'N',
|
||||
'bypass_header_checks': 'N',
|
||||
# Default quarantining policy: quarantine virus
|
||||
'spam_quarantine_to': None,
|
||||
'virus_quarantine_to': 'virus-quarantine',
|
||||
'banned_quarantine_to': None,
|
||||
'bad_header_quarantine_to': None,
|
||||
# tags/scores
|
||||
'spam_subject_tag': None,
|
||||
'spam_subject_tag2': None,
|
||||
'spam_tag_level': None,
|
||||
'spam_kill_level': None,
|
||||
# ban rules.
|
||||
"banned_rulenames": "",
|
||||
}
|
||||
|
||||
for k in ['spam', 'virus', 'banned', 'header']:
|
||||
# Checks: bypass_<k>_checks
|
||||
_chk = 'bypass_' + k + '_checks'
|
||||
v = form_utils.get_single_value(form, input_name=_chk, to_string=True)
|
||||
if v:
|
||||
if v == 'yes':
|
||||
v = 'Y' # Exclictly enable
|
||||
elif v == 'no':
|
||||
v = 'N' # Exclictly disable
|
||||
else:
|
||||
v = None # Don't set a value, use default policy.
|
||||
|
||||
policy[_chk] = v
|
||||
|
||||
# Quarantining: quarantine_<k>
|
||||
_quar_input = 'quarantine_' + k
|
||||
_quar_key = k + '_quarantine_to'
|
||||
if k == 'header':
|
||||
_quar_input = 'quarantine_bad_header'
|
||||
_quar_key = 'bad_header_quarantine_to'
|
||||
|
||||
v = form_utils.get_single_value(form=form, input_name=_quar_input, to_string=True)
|
||||
if v:
|
||||
if v == 'yes':
|
||||
v = k + '-quarantine'
|
||||
if k == 'header':
|
||||
v = 'bad-header-quarantine'
|
||||
else:
|
||||
v = None
|
||||
|
||||
policy[_quar_key] = v
|
||||
|
||||
# Modify spam subject
|
||||
v = form_utils.get_single_value(form=form, input_name='prefix_spam_in_subject', to_string=True)
|
||||
if v:
|
||||
if v == 'yes':
|
||||
policy['spam_subject_tag'] = settings.AMAVISD_SPAM_SUBJECT_PREFIX
|
||||
policy['spam_subject_tag2'] = settings.AMAVISD_SPAM_SUBJECT_PREFIX
|
||||
else:
|
||||
policy['spam_subject_tag'] = None
|
||||
policy['spam_subject_tag2'] = None
|
||||
|
||||
v = form_utils.get_single_value(form=form, input_name='always_insert_x_spam_headers', to_string=True)
|
||||
if v:
|
||||
if v == 'yes':
|
||||
policy['spam_tag_level'] = -100
|
||||
else:
|
||||
policy['spam_tag_level'] = None
|
||||
|
||||
v = form_utils.get_single_value(form=form, input_name='spam_score', to_string=True)
|
||||
if v.isdigit():
|
||||
try:
|
||||
_score = float(v)
|
||||
policy['spam_tag2_level'] = _score
|
||||
policy['spam_kill_level'] = _score
|
||||
except:
|
||||
return False, 'INVALID_SPAM_SCORE'
|
||||
|
||||
# Get ban rules.
|
||||
names = form_utils.get_multi_values_from_api(form,
|
||||
input_name="banned_rulenames",
|
||||
to_string=True,
|
||||
to_lowercase=False)
|
||||
if names:
|
||||
new_names = set()
|
||||
for n in names:
|
||||
if (n in BUILTIN_BAN_RULE_NAMES) or (n in settings.AMAVISD_BAN_RULES):
|
||||
new_names.add(n)
|
||||
|
||||
policy["banned_rulenames"] = ",".join(new_names)
|
||||
|
||||
qr = delete_spam_policy(account=account)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
# column `users_id` is not a column name in `amavisd.policy` table,
|
||||
# it's set by SQL statement `LEFT JOIN`.
|
||||
if 'users_id' in policy:
|
||||
policy.pop('users_id')
|
||||
|
||||
try:
|
||||
policy_id = web.conn_amavisd.insert('policy', **policy)
|
||||
|
||||
qr = utils.link_policy_to_user(account=account, policy_id=policy_id)
|
||||
if not qr[0]:
|
||||
return qr
|
||||
|
||||
# Update `policy.spam_tag3_level` and `policy.spam_subject_tag3`
|
||||
# separately, these two columns don't exist in Amavisd-new-2.6.x.
|
||||
try:
|
||||
extra_updates = {'spam_tag3_level': policy['spam_tag2_level'],
|
||||
'spam_subject_tag3': policy['spam_subject_tag2']}
|
||||
|
||||
web.conn_amavisd.update(
|
||||
'policy',
|
||||
vars={'id': policy_id},
|
||||
where='id=$id',
|
||||
**extra_updates)
|
||||
except:
|
||||
pass
|
||||
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
207
libs/amavisd/utils.py
Normal file
207
libs/amavisd/utils.py
Normal file
@@ -0,0 +1,207 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
import settings
|
||||
from libs import iredutils
|
||||
from libs.iredutils import is_valid_amavisd_address
|
||||
|
||||
|
||||
def create_mailaddr(addresses):
|
||||
for addr in addresses:
|
||||
addr_type = iredutils.is_valid_amavisd_address(addr)
|
||||
if addr_type in iredutils.MAILADDR_PRIORITIES:
|
||||
try:
|
||||
web.conn_amavisd.insert(
|
||||
'mailaddr',
|
||||
priority=iredutils.MAILADDR_PRIORITIES[addr_type],
|
||||
email=addr,
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def create_user(account, return_record=True):
|
||||
# Create a new record in `amavisd.users`
|
||||
addr_type = is_valid_amavisd_address(account)
|
||||
try:
|
||||
# Use policy_id=0 to make sure it's not linked to any policy.
|
||||
web.conn_amavisd.insert(
|
||||
'users',
|
||||
policy_id=0,
|
||||
email=account,
|
||||
priority=iredutils.MAILADDR_PRIORITIES[addr_type],
|
||||
)
|
||||
|
||||
if return_record:
|
||||
qr = web.conn_amavisd.select(
|
||||
'users',
|
||||
vars={'account': account},
|
||||
what='*',
|
||||
where='email=$account',
|
||||
limit=1,
|
||||
)
|
||||
return True, qr[0]
|
||||
else:
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def get_user_record(account, create_if_missing=True):
|
||||
try:
|
||||
qr = web.conn_amavisd.select(
|
||||
'users',
|
||||
vars={'email': account},
|
||||
what='*',
|
||||
where='email=$email',
|
||||
limit=1,
|
||||
)
|
||||
|
||||
if qr:
|
||||
return True, qr[0]
|
||||
else:
|
||||
if create_if_missing:
|
||||
qr = create_user(account=account, return_record=True)
|
||||
|
||||
if qr[0]:
|
||||
return True, qr[1]
|
||||
else:
|
||||
return qr
|
||||
else:
|
||||
return False, 'ACCOUNT_NOT_EXIST'
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def create_policy(account, return_record=True):
|
||||
# Create a new record in `amavisd.policy`
|
||||
try:
|
||||
values = {
|
||||
'policy_name': account,
|
||||
'spam_quarantine_to': 'spam-quarantine',
|
||||
'virus_quarantine_to': 'virus-quarantine',
|
||||
'spam_subject_tag2': settings.AMAVISD_SPAM_SUBJECT_PREFIX,
|
||||
}
|
||||
|
||||
web.conn_amavisd.insert('policy', **values)
|
||||
|
||||
# Update `policy.spam_tag3_level` and `policy.spam_subject_tag3`
|
||||
# separately, these two columns don't exist in Amavisd-new-2.6.x.
|
||||
try:
|
||||
extra_values = {'spam_subject_tag3': settings.AMAVISD_SPAM_SUBJECT_PREFIX}
|
||||
web.conn_amavisd.update(
|
||||
'policy',
|
||||
vars={'policy_name': account},
|
||||
where='policy_name=$policy_name',
|
||||
**extra_values)
|
||||
except:
|
||||
pass
|
||||
|
||||
if return_record:
|
||||
qr = web.conn_amavisd.select(
|
||||
'policy',
|
||||
vars={'account': account},
|
||||
what='*',
|
||||
where='policy_name=$account',
|
||||
limit=1,
|
||||
)
|
||||
return True, qr[0]
|
||||
else:
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def get_policy_record(account, create_if_missing=False):
|
||||
try:
|
||||
qr = web.conn_amavisd.select(
|
||||
'policy',
|
||||
vars={'account': account},
|
||||
what='id',
|
||||
where='policy_name=$account',
|
||||
limit=1,
|
||||
)
|
||||
|
||||
if qr:
|
||||
return True, qr[0]
|
||||
else:
|
||||
if create_if_missing:
|
||||
qr = create_policy(account=account, return_record=True)
|
||||
|
||||
if qr[0]:
|
||||
return True, qr[1]
|
||||
else:
|
||||
return qr
|
||||
else:
|
||||
return True, {}
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def link_policy_to_user(account, policy_id):
|
||||
qr = get_user_record(account)
|
||||
if qr[0]:
|
||||
user_id = qr[1].id
|
||||
else:
|
||||
return qr
|
||||
|
||||
try:
|
||||
web.conn_amavisd.update(
|
||||
'users',
|
||||
vars={'id': user_id},
|
||||
policy_id=policy_id,
|
||||
where='id=$id',
|
||||
)
|
||||
return True,
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
|
||||
def delete_policy_accounts(accounts):
|
||||
sqlvars = {'accounts': accounts}
|
||||
try:
|
||||
# Get mailaddr.id of accounts
|
||||
qr = web.conn_amavisd.select(
|
||||
'users',
|
||||
vars=sqlvars,
|
||||
what='id',
|
||||
where='email IN $accounts',
|
||||
)
|
||||
|
||||
ids = []
|
||||
for i in qr:
|
||||
ids.append(i.id)
|
||||
|
||||
# Delete wblist
|
||||
web.conn_amavisd.delete(
|
||||
'wblist',
|
||||
vars={'ids': ids},
|
||||
where='rid IN $ids',
|
||||
)
|
||||
|
||||
# Delete outbound wblist
|
||||
web.conn_amavisd.delete(
|
||||
'outbound_wblist',
|
||||
vars={'ids': ids},
|
||||
where='sid IN $ids',
|
||||
)
|
||||
|
||||
# Delete policy
|
||||
web.conn_amavisd.delete(
|
||||
'policy',
|
||||
vars=sqlvars,
|
||||
where='policy_name IN $accounts',
|
||||
)
|
||||
|
||||
# Delete users
|
||||
web.conn_amavisd.delete(
|
||||
'users',
|
||||
vars=sqlvars,
|
||||
where='email IN $accounts',
|
||||
)
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
return True,
|
||||
454
libs/amavisd/wblist.py
Normal file
454
libs/amavisd/wblist.py
Normal file
@@ -0,0 +1,454 @@
|
||||
# Author: Zhang Huangbin <zhb@iredmail.org>
|
||||
|
||||
import web
|
||||
from libs import iredutils
|
||||
from libs.logger import log_activity
|
||||
from libs.amavisd import utils
|
||||
|
||||
session = web.config.get('_session')
|
||||
|
||||
|
||||
def get_wblist(account,
|
||||
whitelist=True,
|
||||
blacklist=True,
|
||||
outbound_whitelist=True,
|
||||
outbound_blacklist=True):
|
||||
"""Get white/blacklists of specified account."""
|
||||
inbound_sql_where = 'users.email=$user AND users.id=wblist.rid AND wblist.sid = mailaddr.id'
|
||||
if whitelist and not blacklist:
|
||||
inbound_sql_where += ' AND wblist.wb=%s' % web.sqlquote('W')
|
||||
if not whitelist and blacklist:
|
||||
inbound_sql_where += ' AND wblist.wb=%s' % web.sqlquote('B')
|
||||
|
||||
outbound_sql_where = 'users.email=$user AND users.id=outbound_wblist.sid AND outbound_wblist.rid = mailaddr.id'
|
||||
if outbound_whitelist and not outbound_blacklist:
|
||||
outbound_sql_where += ' AND outbound_wblist.wb=%s' % web.sqlquote('W')
|
||||
if not whitelist and blacklist:
|
||||
outbound_sql_where += ' AND outbound_wblist.wb=%s' % web.sqlquote('B')
|
||||
|
||||
wl = []
|
||||
bl = []
|
||||
outbound_wl = []
|
||||
outbound_bl = []
|
||||
|
||||
try:
|
||||
qr = web.conn_amavisd.select(
|
||||
['mailaddr', 'users', 'wblist'],
|
||||
vars={'user': account},
|
||||
what='mailaddr.email AS address, wblist.wb AS wb',
|
||||
where=inbound_sql_where,
|
||||
)
|
||||
|
||||
for r in qr:
|
||||
if r.wb == 'W':
|
||||
wl.append(iredutils.bytes2str(r.address))
|
||||
else:
|
||||
bl.append(iredutils.bytes2str(r.address))
|
||||
|
||||
qr = web.conn_amavisd.select(
|
||||
['mailaddr', 'users', 'outbound_wblist'],
|
||||
vars={'user': account},
|
||||
what='mailaddr.email AS address, outbound_wblist.wb AS wb',
|
||||
where=outbound_sql_where,
|
||||
)
|
||||
for r in qr:
|
||||
if r.wb == 'W':
|
||||
outbound_wl.append(iredutils.bytes2str(r.address))
|
||||
else:
|
||||
outbound_bl.append(iredutils.bytes2str(r.address))
|
||||
except Exception as e:
|
||||
return False, e
|
||||
|
||||
wl.sort()
|
||||
bl.sort()
|
||||
outbound_wl.sort()
|
||||
outbound_bl.sort()
|
||||
|
||||
return (True, {'inbound_whitelists': wl,
|
||||
'inbound_blacklists': bl,
|
||||
'outbound_whitelists': outbound_wl,
|
||||
'outbound_blacklists': outbound_bl})
|
||||
|
||||
|
||||
def add_wblist(account,
|
||||
wl_senders=None,
|
||||
bl_senders=None,
|
||||
wl_rcpts=None,
|
||||
bl_rcpts=None,
|
||||
flush_before_import=False):
|
||||
"""Add white/blacklists for specified account.
|
||||
|
||||
wl_senders -- whitelist senders (inbound)
|
||||
bl_senders -- blacklist senders (inbound)
|
||||
wl_rcpts -- whitelist recipients (outbound)
|
||||
bl_rcpts -- blacklist recipients (outbound)
|
||||
flush_before_import -- Delete all existing wblist before importing
|
||||
new wblist
|
||||
"""
|
||||
if not iredutils.is_valid_amavisd_address(account):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
# Remove duplicate.
|
||||
if wl_senders:
|
||||
wl_senders = {str(s).lower()
|
||||
for s in wl_senders
|
||||
if iredutils.is_valid_wblist_address(s)}
|
||||
else:
|
||||
wl_senders = []
|
||||
|
||||
# Whitelist has higher priority, don't include whitelisted sender.
|
||||
if bl_senders:
|
||||
bl_senders = {str(s).lower()
|
||||
for s in bl_senders
|
||||
if iredutils.is_valid_wblist_address(s)}
|
||||
else:
|
||||
bl_senders = []
|
||||
|
||||
if wl_rcpts:
|
||||
wl_rcpts = {str(s).lower()
|
||||
for s in wl_rcpts
|
||||
if iredutils.is_valid_wblist_address(s)}
|
||||
else:
|
||||
wl_rcpts = []
|
||||
|
||||
if bl_rcpts:
|
||||
bl_rcpts = {str(s).lower()
|
||||
for s in bl_rcpts
|
||||
if iredutils.is_valid_wblist_address(s)}
|
||||
else:
|
||||
bl_rcpts = []
|
||||
|
||||
if flush_before_import:
|
||||
if wl_senders:
|
||||
bl_senders = {s for s in bl_senders if s not in wl_senders}
|
||||
|
||||
if wl_rcpts:
|
||||
bl_rcpts = {s for s in bl_rcpts if s not in wl_rcpts}
|
||||
|
||||
sender_addresses = set(wl_senders) | set(bl_senders)
|
||||
rcpt_addresses = set(wl_rcpts) | set(bl_rcpts)
|
||||
all_addresses = list(sender_addresses | rcpt_addresses)
|
||||
|
||||
# Get current user's id from `amavisd.users`
|
||||
qr = utils.get_user_record(account=account)
|
||||
|
||||
if qr[0]:
|
||||
user_id = qr[1].id
|
||||
else:
|
||||
return qr
|
||||
|
||||
# Delete old records
|
||||
if flush_before_import:
|
||||
# user_id = wblist.rid
|
||||
web.conn_amavisd.delete(
|
||||
'wblist',
|
||||
vars={'rid': user_id},
|
||||
where='rid=$rid',
|
||||
)
|
||||
|
||||
# user_id = outbound_wblist.sid
|
||||
web.conn_amavisd.delete(
|
||||
'outbound_wblist',
|
||||
vars={'sid': user_id},
|
||||
where='sid=$sid',
|
||||
)
|
||||
|
||||
if not all_addresses:
|
||||
return True,
|
||||
|
||||
# Insert all senders into `amavisd.mailaddr`
|
||||
utils.create_mailaddr(addresses=all_addresses)
|
||||
|
||||
# Get `mailaddr.id` of senders
|
||||
sender_records = {}
|
||||
if sender_addresses:
|
||||
qr = web.conn_amavisd.select(
|
||||
'mailaddr',
|
||||
vars={'addresses': list(sender_addresses)},
|
||||
what='id, email',
|
||||
where='email IN $addresses',
|
||||
)
|
||||
|
||||
for r in qr:
|
||||
sender_records[iredutils.bytes2str(r.email)] = r.id
|
||||
del qr
|
||||
|
||||
# Get `mailaddr.id` of recipients
|
||||
rcpt_records = {}
|
||||
if rcpt_addresses:
|
||||
qr = web.conn_amavisd.select(
|
||||
'mailaddr',
|
||||
vars={'addresses': list(rcpt_addresses)},
|
||||
what='id, email',
|
||||
where='email IN $addresses',
|
||||
)
|
||||
|
||||
for r in qr:
|
||||
rcpt_records[iredutils.bytes2str(r.email)] = r.id
|
||||
|
||||
del qr
|
||||
|
||||
# Remove existing records of current submitted records before inserting new.
|
||||
try:
|
||||
if sender_records:
|
||||
web.conn_amavisd.delete(
|
||||
'wblist',
|
||||
vars={'rid': user_id, 'sid': list(sender_records.values())},
|
||||
where='rid=$rid AND sid IN $sid',
|
||||
)
|
||||
|
||||
if rcpt_records:
|
||||
web.conn_amavisd.delete(
|
||||
'outbound_wblist',
|
||||
vars={'sid': user_id, 'rid': list(rcpt_records.values())},
|
||||
where='sid=$sid AND rid IN $rid',
|
||||
)
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
# Generate dict used to build SQL statements for importing wblist
|
||||
values = []
|
||||
if sender_addresses:
|
||||
for s in wl_senders:
|
||||
if sender_records.get(s):
|
||||
values.append({'rid': user_id, 'sid': sender_records[s], 'wb': 'W'})
|
||||
|
||||
for s in bl_senders:
|
||||
# Filter out same record in blacklist
|
||||
if sender_records.get(s) and s not in wl_senders:
|
||||
values.append({'rid': user_id, 'sid': sender_records[s], 'wb': 'B'})
|
||||
|
||||
rcpt_values = []
|
||||
if rcpt_addresses:
|
||||
for s in wl_rcpts:
|
||||
if rcpt_records.get(s):
|
||||
rcpt_values.append({'sid': user_id, 'rid': rcpt_records[s], 'wb': 'W'})
|
||||
|
||||
for s in bl_rcpts:
|
||||
# Filter out same record in blacklist
|
||||
if rcpt_records.get(s) and s not in wl_rcpts:
|
||||
rcpt_values.append({'sid': user_id, 'rid': rcpt_records[s], 'wb': 'B'})
|
||||
|
||||
try:
|
||||
if values:
|
||||
web.conn_amavisd.multiple_insert('wblist', values)
|
||||
|
||||
if rcpt_values:
|
||||
web.conn_amavisd.multiple_insert('outbound_wblist', rcpt_values)
|
||||
|
||||
# Log
|
||||
if values:
|
||||
if flush_before_import:
|
||||
log_activity(msg='Update whitelists and/or blacklists for %s.' % account,
|
||||
admin=session['username'],
|
||||
event='update_wblist')
|
||||
else:
|
||||
if wl_senders:
|
||||
log_activity(msg='Add whitelists for {}: {}.'.format(account, ', '.join(wl_senders)),
|
||||
admin=session['username'],
|
||||
event='update_wblist')
|
||||
|
||||
if bl_senders:
|
||||
log_activity(msg='Add blacklists for {}: {}.'.format(account, ', '.join(bl_senders)),
|
||||
admin=session['username'],
|
||||
event='update_wblist')
|
||||
|
||||
if rcpt_values:
|
||||
if flush_before_import:
|
||||
log_activity(msg='Update outbound whitelists and/or blacklists for %s.' % account,
|
||||
admin=session['username'],
|
||||
event='update_wblist')
|
||||
else:
|
||||
if wl_rcpts:
|
||||
log_activity(msg='Add outbound whitelists for {}: {}.'.format(account, ', '.join(wl_senders)),
|
||||
admin=session['username'],
|
||||
event='update_wblist')
|
||||
|
||||
if bl_rcpts:
|
||||
log_activity(msg='Add outbound blacklists for {}: {}.'.format(account, ', '.join(bl_senders)),
|
||||
admin=session['username'],
|
||||
event='update_wblist')
|
||||
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
return True,
|
||||
|
||||
|
||||
def delete_wblist(account,
|
||||
wl_senders=None,
|
||||
bl_senders=None,
|
||||
wl_rcpts=None,
|
||||
bl_rcpts=None):
|
||||
if not iredutils.is_valid_amavisd_address(account):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
# Remove duplicate.
|
||||
if wl_senders:
|
||||
wl_senders = list({str(s).lower()
|
||||
for s in wl_senders
|
||||
if iredutils.is_valid_wblist_address(s)})
|
||||
|
||||
# Whitelist has higher priority, don't include whitelisted sender.
|
||||
if bl_senders:
|
||||
bl_senders = list({str(s).lower()
|
||||
for s in bl_senders
|
||||
if iredutils.is_valid_wblist_address(s)})
|
||||
|
||||
if wl_rcpts:
|
||||
wl_rcpts = list({str(s).lower()
|
||||
for s in wl_rcpts
|
||||
if iredutils.is_valid_wblist_address(s)})
|
||||
|
||||
if bl_rcpts:
|
||||
bl_rcpts = list({str(s).lower()
|
||||
for s in bl_rcpts
|
||||
if iredutils.is_valid_wblist_address(s)})
|
||||
|
||||
# Get account id from `amavisd.users`
|
||||
qr = utils.get_user_record(account=account)
|
||||
|
||||
if qr[0]:
|
||||
user_id = qr[1].id
|
||||
else:
|
||||
return qr
|
||||
|
||||
# Remove wblist.
|
||||
# No need to remove unused senders in `mailaddr` table, because we
|
||||
# have daily cron job to delete them (tools/cleanup_amavisd_db.py).
|
||||
try:
|
||||
# Get `mailaddr.id` for wblist senders
|
||||
if wl_senders:
|
||||
sids = []
|
||||
qr = web.conn_amavisd.select(
|
||||
'mailaddr',
|
||||
vars={'addresses': wl_senders},
|
||||
what='id',
|
||||
where='email IN $addresses',
|
||||
)
|
||||
|
||||
for r in qr:
|
||||
sids.append(r.id)
|
||||
|
||||
if sids:
|
||||
web.conn_amavisd.delete(
|
||||
'wblist',
|
||||
vars={'user_id': user_id, 'sids': sids},
|
||||
where="rid=$user_id AND sid IN $sids AND wb='W'",
|
||||
)
|
||||
|
||||
if bl_senders:
|
||||
sids = []
|
||||
qr = web.conn_amavisd.select(
|
||||
'mailaddr',
|
||||
vars={'addresses': bl_senders},
|
||||
what='id',
|
||||
where='email IN $addresses',
|
||||
)
|
||||
|
||||
for r in qr:
|
||||
sids.append(r.id)
|
||||
|
||||
if sids:
|
||||
web.conn_amavisd.delete(
|
||||
'wblist',
|
||||
vars={'user_id': user_id, 'sids': sids},
|
||||
where="rid=$user_id AND sid IN $sids AND wb='B'",
|
||||
)
|
||||
|
||||
if wl_rcpts:
|
||||
rids = []
|
||||
qr = web.conn_amavisd.select(
|
||||
'mailaddr',
|
||||
vars={'addresses': wl_rcpts},
|
||||
what='id',
|
||||
where='email IN $addresses',
|
||||
)
|
||||
|
||||
for r in qr:
|
||||
rids.append(r.id)
|
||||
|
||||
if rids:
|
||||
web.conn_amavisd.delete(
|
||||
'outbound_wblist',
|
||||
vars={'user_id': user_id, 'rids': rids},
|
||||
where="sid=$user_id AND rid IN $rids AND wb='W'",
|
||||
)
|
||||
|
||||
if bl_rcpts:
|
||||
rids = []
|
||||
qr = web.conn_amavisd.select(
|
||||
'mailaddr',
|
||||
vars={'addresses': bl_rcpts},
|
||||
what='id',
|
||||
where='email IN $addresses',
|
||||
)
|
||||
|
||||
for r in qr:
|
||||
rids.append(r.id)
|
||||
|
||||
if rids:
|
||||
web.conn_amavisd.delete(
|
||||
'outbound_wblist',
|
||||
vars={'user_id': user_id, 'rids': rids},
|
||||
where="sid=$user_id AND rid IN $rids AND wb='B'",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
return True,
|
||||
|
||||
|
||||
def delete_all_wblist(account,
|
||||
wl_senders=False,
|
||||
bl_senders=False,
|
||||
wl_rcpts=False,
|
||||
bl_rcpts=False):
|
||||
if not iredutils.is_valid_amavisd_address(account):
|
||||
return False, 'INVALID_ACCOUNT'
|
||||
|
||||
# Get account id from `amavisd.users`
|
||||
qr = utils.get_user_record(account=account)
|
||||
|
||||
if qr[0]:
|
||||
user_id = qr[1].id
|
||||
else:
|
||||
return qr
|
||||
|
||||
# Remove ALL wblist.
|
||||
# No need to remove unused senders in `mailaddr` table, because we
|
||||
# have daily cron job to delete them (tools/cleanup_amavisd_db.py).
|
||||
try:
|
||||
if wl_senders:
|
||||
web.conn_amavisd.delete(
|
||||
'wblist',
|
||||
vars={'user_id': user_id},
|
||||
where="rid=$user_id AND wb='W'",
|
||||
)
|
||||
|
||||
if bl_senders:
|
||||
web.conn_amavisd.delete(
|
||||
'wblist',
|
||||
vars={'user_id': user_id},
|
||||
where="rid=$user_id AND wb='B'",
|
||||
)
|
||||
|
||||
if wl_rcpts:
|
||||
web.conn_amavisd.delete(
|
||||
'outbound_wblist',
|
||||
vars={'user_id': user_id},
|
||||
where="sid=$user_id AND wb='W'",
|
||||
)
|
||||
|
||||
if bl_rcpts:
|
||||
web.conn_amavisd.delete(
|
||||
'outbound_wblist',
|
||||
vars={'user_id': user_id},
|
||||
where="sid=$user_id AND wb='B'",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
return False, repr(e)
|
||||
|
||||
return True,
|
||||
Reference in New Issue
Block a user