#!/usr/bin/env python
import re
import os
import random
import string
import glob

'''
Author: Joe Nelson <joe@utahbroadband.com>
Last Revision: Feb 27, 2012

This script reads spamdyke log entries from /var/log/syslog and takes action on any account
that has send more than THRESHOLD emails.  Users can be whitelisted in
/var/qmail/control/not_abusers.

This script relies on the /tmp/mail_abusers/ directory being in existence and writable.
It also relies on qmail-remove (http://www.linuxmagic.com/opensource/qmail/qmail-remove).
It has been written for systems using vpopmail, but could easily be changed to whatever
else might be needed.

A cronjob to run this every five minutes looks like this:
0-55/5 * * * * python /usr/local/bin/mail_abusers.py > /dev/null 2>&1

Be sure to change "notifications@example.com" below to wherever you want notifications
to go to.  Be aware that there are two "notifications@example.com" entries that need
to be changed.
'''

AUTH = {}
USERS = {}
THRESHOLD = 1000
USERS_CHANGED_DIR = '/tmp/mail_abusers/'
WHITELIST_USERS_DIR = '/var/qmail/control/not_abusers/'

if not os.path.exists(USERS_CHANGED_DIR):
    os.makedirs(USERS_CHANGED_DIR)

USERS_CHANGED = [x.split('/')[3] for x in glob.glob(USERS_CHANGED_DIR + '*')]
WHITELIST_USERS = [x.split('/')[5] for x in glob.glob(WHITELIST_USERS_DIR + '*')]
USERS_CHANGED += WHITELIST_USERS

RE_ALLOWED = re.compile(r'''
    .*spamdyke.*ALLOWED.*                       # We're only interested in the ALLOWED lines of spamdyke
    from:\s(?P<from>.+?)\s                      # Capture 'from'
    to:\s(?P<to>.+?)\s                          # Capture 'to'
    origin_ip:\s(?P<origin_ip>.+?)\s            # Capture 'origin_ip'
    origin_rdns:\s(?P<origin_rdns>.+?)\s        # Capture 'origin_rdns'
    auth:\s(?P<auth>.+?)\s                      # Capture 'auth'
    encryption:\s(?P<encryption>.+)             # Capture 'encryption'
''', re.X)

#Put each authenticated user into a dictionary along with the count of emails sent by them
try: lf = open('/var/log/syslog', 'r')
except IOError: print "Can't open /var/log/syslog"
else:
    for line in lf:
        m = re.match(RE_ALLOWED, line)
        if m:
            user = m.group('auth')
            if user in AUTH.keys():
                AUTH[user] +=1
            else:
                AUTH[user] = 1
    lf.close()

#Spamdyke logs all incoming emails with "auth: (unknown)", they're not needed for this script, we're only concerened with authenticated, local users
del AUTH["(unknown)"]

#Put each user whose count is above threshold into a new dictionary
for user, count in AUTH.items():
    if count > THRESHOLD:
        if user not in USERS_CHANGED:
            USERS[user] = count

#Someone's showing up as a hacked account, let's do something about it
if len(USERS) > 0:
    for user, count in USERS.items():
        #Generate a random password
        char_set = string.ascii_uppercase + string.digits
        password = ''.join(random.sample(char_set, 8))

        #Change user's password
        cmd = '/home/vpopmail/bin/vpasswd ' + user + ' ' + password
        #print cmd
        os.system(cmd)

        #Remove queue entries belonging to this user
        cmd = 'qmail-remove -r -p ' + user
        #print cmd
        os.system(cmd)

        #Email important people that you did something
        cmd = "printf 'To: notifications@example.com\nSubject: Abused Email Accounts\n\nAbuser Changed: %s (%s authorizations)' | /var/qmail/bin/qmail-inject notifications@example.com"  % (user, count)
        #print cmd
        os.system(cmd)

        #Create a file in the tmp directory so that this won't get tagged again until tomorrow
        open(USERS_CHANGED_DIR + user, 'w').close()
else:
    print 'No users abusing'
