/
opt
/
dedrads
/
/opt/dedrads
mkdir
upload
Name
Size
Mode
Actions
account_review/
-
0555
rm
check_software_mods/
-
0555
rm
cms_tools/
-
0555
rm
etc/
-
0555
rm
extras/
-
0555
rm
mailparse/
-
0555
rm
mysql/
-
0555
rm
nlp_scripts/
-
0555
rm
oldrads/
-
0555
rm
perl/
-
0555
rm
provision/
-
0555
rm
python/
-
0555
rm
suspended/
-
0555
rm
__pycache__/
-
0555
rm
account-review
799
0555
edit
dl
rm
allfw
6259
0555
edit
dl
rm
alp.py
16553
0555
edit
dl
rm
autossl_sync
6267
0555
edit
dl
rm
check_apache
9205
0555
edit
dl
rm
check_bandwidth
4546
0555
edit
dl
rm
check_boxtrapper
1684
0555
edit
dl
rm
check_cpu
12212
0555
edit
dl
rm
check_crons
1827
0555
edit
dl
rm
check_dcpumon
1973
0555
edit
dl
rm
check_dns
1827
0555
edit
dl
rm
check_domlogs
61
0555
edit
dl
rm
check_exim
3731
0555
edit
dl
rm
check_hacks
22604
0555
edit
dl
rm
check_imap
5249
0555
edit
dl
rm
check_io
6635
0555
edit
dl
rm
check_max_children
16341
0555
edit
dl
rm
check_mem
1660
0555
edit
dl
rm
check_misc
1510
0555
edit
dl
rm
check_pacct
5201
0555
edit
dl
rm
check_php
4344
0555
edit
dl
rm
check_pop3
2295
0555
edit
dl
rm
check_prov.py
4714
0555
edit
dl
rm
check_server
696
0555
edit
dl
rm
check_size
490
0555
edit
dl
rm
check_software
9849
0555
edit
dl
rm
check_spamd
2426
0555
edit
dl
rm
check_traffic
1933
0555
edit
dl
rm
check_user
1887
0555
edit
dl
rm
clean_exim.py
6390
0555
edit
dl
rm
clusterfix
5343
0555
edit
dl
rm
cmspass.py
3970
0555
edit
dl
rm
cms_check.py
9825
0555
edit
dl
rm
cms_counter.py
36734
0555
edit
dl
rm
cms_creds
713
0555
edit
dl
rm
cms_dumpdb
4485
0555
edit
dl
rm
cms_pw
1416
0555
edit
dl
rm
cpanel-api
7040
0555
edit
dl
rm
cpanel_postgres_manager.py
29004
0555
edit
dl
rm
cpmerge
4631
0555
edit
dl
rm
cpumon
2195
0555
edit
dl
rm
dcpumon.pl
1239
0555
edit
dl
rm
dedcheck
18127
0555
edit
dl
rm
dns-sync
14386
0555
edit
dl
rm
docroot.py
4425
0555
edit
dl
rm
du-tree
7450
0555
edit
dl
rm
envinfo.py
6633
0555
edit
dl
rm
exclude_rbl.py
5249
0555
edit
dl
rm
extract-vhost
2691
0555
edit
dl
rm
filescan
3566
0555
edit
dl
rm
find_warez
1460
0555
edit
dl
rm
first_setup.py
19120
0555
edit
dl
rm
fixwpcron.py
4822
0555
edit
dl
rm
fix_dns_cluster
353
0555
edit
dl
rm
goaccess
1217720
0555
edit
dl
rm
hostsfilemods
4467
0555
edit
dl
rm
imap_io
2323
0555
edit
dl
rm
innodb_converter.py
9343
0555
edit
dl
rm
killall911
530
0555
edit
dl
rm
killdns
941
0555
edit
dl
rm
lastcommcache.sh
1374
0555
edit
dl
rm
legal_lock_down.sh
7450
0555
edit
dl
rm
lil-cpanel
52002
0555
edit
dl
rm
listacct
4575
0555
edit
dl
rm
mailscan
4544
0555
edit
dl
rm
mail_sources.py
7407
0555
edit
dl
rm
megaclisas-status
38664
0555
edit
dl
rm
migrate2central.sh
14418
0555
edit
dl
rm
modify-account
40631
0555
edit
dl
rm
modsec_disable.py
11461
0555
edit
dl
rm
monarxctl
8912
0555
edit
dl
rm
msp.pl
27714
0555
edit
dl
rm
mysql_dstat
474
0555
edit
dl
rm
mysql_selector.py
73102
0555
edit
dl
rm
nlp
4582
0555
edit
dl
rm
procscrape
1863
0555
edit
dl
rm
quarantine
11083
0555
edit
dl
rm
radsfunctions.sh
863
0555
edit
dl
rm
recent-cp
16148
0555
edit
dl
rm
remote_dump
18347
0555
edit
dl
rm
reset_cpanel
3205
0555
edit
dl
rm
server-load
2192
0555
edit
dl
rm
show-conns
20105
0555
edit
dl
rm
show-conns-adv.py
54667
0555
edit
dl
rm
sparta.py
382498
0555
edit
dl
rm
sqltop
27129
0555
edit
dl
rm
telcheck
2411
0555
edit
dl
rm
temprootreset
186
0555
edit
dl
rm
unsuspend_user
1307
0555
edit
dl
rm
unsusprunner.sh
1840
0555
edit
dl
rm
updatednsadmin
526
0555
edit
dl
rm
update_spf
14972
0555
edit
dl
rm
upgrade-check
9216
0555
edit
dl
rm
vhost_data.py
6221
0555
edit
dl
rm
wp-xray.sh
85816
0555
edit
dl
rm
Edit:
/opt/dedrads/clean_exim.py
(6390B)
#!/usr/lib/rads/venv/bin/python3 """Python exim cleanup script to reduce the urge to run hacky oneliners""" import argparse import re import subprocess import sys from collections import defaultdict # compile regex once and recycle them throughout the script for efficiency EXIM_BP_RE = re.compile( r'\s*(?P<age>[0-9]+[mhd])\s+[0-9\.]+[M|K]? ' # 5m 15K r'(?P<id>[a-zA-Z0-9\-]+)\s+' # 1XEhho-0006AU-Tx r'\<(?P<sender>.*)\>' # <emailuser@domain> ) def run(command: list[str] | str, shell=False) -> subprocess.CompletedProcess: """ Run a process with arguments. Optionally, as a shell command returns CompletedProcess. """ args = command result = subprocess.run( args, capture_output=True, shell=shell, errors="surrogateescape", encoding="utf-8", check=False, ) return result def get_queue(exclude_bounces=False, bounces_only=False): """Get current exim queue as a list of dicts, each dict containing a single message, with keys 'age', 'id', and 'sender'""" queue = [] out = run(["exim", "-bp"]) queue_lines = out.stdout.splitlines() for line in queue_lines: match = EXIM_BP_RE.match(line) if match is None: continue groupdict = match.groupdict() if exclude_bounces and groupdict['sender'] == '': continue if bounces_only and groupdict['sender'] != '': continue groupdict['age'] = exim_age_to_secs(groupdict['age']) if groupdict['age'] > 3600: queue.append(groupdict) return queue def exim_age_to_secs(age): """convert exim age to seconds""" conversions = {'m': 60, 'h': 3600, 'd': 86400} # find the multiplier based on above conversions multiplier = conversions[age[-1]] # typecast to int and use multiplier above return int(age.rstrip('mhd')) * multiplier def is_boxtrapper(msg_id): """Determine if a message ID is a boxtrapper msg in queue""" try: out = run(['exim', '-Mvh', msg_id]) out.check_returncode() head = out.stdout.strip() except subprocess.CalledProcessError: return False # likely no longer in queue return 'Subject: Your email requires verification verify#' in head def remove_msg(msg_id): """Here, msg_id may be a list or just one string""" if isinstance(msg_id, list): msg_id = ' '.join(msg_id) try: run(['exim', '-Mrm', msg_id]).check_returncode() except subprocess.CalledProcessError: pass # may have already been removed from queue or sent def print_removed(removed): """Given a dict of user:count mappings, print removed""" if len(list(removed.keys())) == 0: print('None to remove') else: print('Removed:') for user, count in removed.items(): print(' %s: %d' % (user, count)) def clean_boxtrapper(): """Remove old boxtrapper messages from queue. The theory is that if still stuck in queue after min_age_secs, they were likely sent from an illigitimate email address and will never clear from queue normally""" print('Removing old boxtrapper messages...') removed = defaultdict(int) queue = get_queue(exclude_bounces=True) queue = [x for x in queue if is_boxtrapper(x['id'])] for msg in queue: remove_msg(msg['id']) removed[msg['sender']] += 1 print_removed(removed) def get_full_quota_user(msg_id): """Get the user for a message which is at full quota, or None""" try: out = run(['exim', '-Mvb', msg_id]) out.check_returncode() body = out.stdout.strip() except subprocess.CalledProcessError: return None if not ' Mailbox quota exceeded' in body: return None body = body.splitlines() addr_line = -1 for line_num, line in enumerate(body): if ' Mailbox quota exceeded' in line: addr_line = line_num - 1 break if addr_line >= 0: return body[addr_line].strip() return None def clean_full_inbox_bounces(): """Remove "mailbox quota exceeded" bounces from queue""" print('Removing old full inbox bounces...') removed = defaultdict(int) queue = get_queue(bounces_only=True) for msg in queue: user = get_full_quota_user(msg['id']) if user is None: continue remove_msg(msg['id']) removed[user] += 1 print_removed(removed) def clean_autoresponders(): """Remove old auto-responder emails from queue which are likely responses to spam if they have been stuck in queue""" print('Removing old auto-responses...') removed = defaultdict(int) queue = get_queue(exclude_bounces=True) for msg in queue: try: out = run(['exim', '-Mvh', msg['id']]) out.check_returncode() headers = out.stdout.strip() except subprocess.CalledProcessError: continue if 'X-Autorespond:' in headers and 'auto_reply' in headers: remove_msg(msg['id']) removed[msg['sender']] += 1 print_removed(removed) def _parse_args(): """Parse commandline arguments""" parser = argparse.ArgumentParser( description="Exim cleanup tool", epilog="One and only one option is allowed", ) parser.add_argument( '-a', '--all', action='store_true', help='Do all cleanup procedures' ) parser.add_argument( '-b', '--boxtrapper', action='store_true', help='Remove old boxtrapper messages', ) parser.add_argument( '-r', '--autorespond', action='store_true', help='Remove old auto-responder messages', ) parser.add_argument( '-f', '--full', action='store_true', help='Remove old bounces for full inbox', ) args = parser.parse_args() chosen = [key for key, value in vars(args).items() if value is True] if len(chosen) != 1: parser.print_help() sys.exit(1) return args def main(): """Main logic""" args = _parse_args() if args.boxtrapper or args.all: clean_boxtrapper() if args.full or args.all: clean_full_inbox_bounces() if args.autorespond or args.all: clean_autoresponders() if __name__ == '__main__': main()
Save
cmd:
run