#! /usr/bin/python

'# Finding directories from which over 350 mails have been sent, with lots of php hits in the access log'

import os
import re
import glob
import sys

def get_spamming_dirs(tail=20000000, mails=350):
    cwds = {}
    cwd_re = re.compile(r' cwd=(.*?) \d+ args:')
    cmd = 'tail -c %d /var/log/exim_mainlog|grep cwd=.*home' % tail
    fd = os.popen(cmd,'r')
    for line in fd:
        m=cwd_re.search(line)
        if not m: continue
        dir=m.group(1)
        cwds[dir]=cwds.get(dir,0)+1
    fd.close()

    for dir,mailcount in cwds.items():
        if dir.find('/home/') < 0: 
            continue
        if mailcount < mails:
            continue
        if not os.path.exists(dir):
            continue
        yield dir,mailcount


def log_lines(relative_to, tail=2000000):
    i=relative_to.find('/public_html')
    if i<0:
        return
    access_logs = relative_to[:i]+'/access-logs/*'
    for log_file in glob.glob(access_logs):
        if log_file.find('-ftp_log')>=0: continue
        cmd = "tail -c %d '%s'" % (tail, log_file.replace("'",r"'\''"))
        fd=os.popen(cmd, 'r')
        fd.readline()
        for line in fd:
            yield line
        fd.close()

def list_php_hits(relative_to, tail=20000000):
    for line in log_lines(relative_to, tail=tail):
        # 164.151.129.37 - - [02/Dec/2014:15:06:56 +0200] "GET /product-category/2015-kitesurfing-range/kiteboards/ HTTP/1.1" 200 129644 "http://www.airushkiteboarding.co.za/product-category/2015-kitesurfing-range/kites/" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.71 Safari/537.36"
        # 50.63.194.47 - - [02/Dec/2014:14:41:27 +0200] "POST /wp-content/themes/twentythirteen/languages/defines.php HTTP/1.1" 404 3 "-" "Mozilla/5.0 (Windows; U; Windows NT 5.1
        i=line.find('"')
        if i<0: continue

        phpfile=line[i+1:]
        phpfile=phpfile[phpfile.find(' ')+1:]
        phpfile=phpfile[:phpfile.find(' ')]
        i=phpfile.find('?')
        if i>0: phpfile=phpfile[:i]
        if phpfile.endswith('/'): phpfile += 'index.php'
        while phpfile.find('//')>=0:
            phpfile=phpfile.replace('//','/');
        yield phpfile
        

# Find the files and how many times they were hit lately
def get_php_hit_counts(relative_to, tail=20000000):
    phpfiles={}
    for phpfile in list_php_hits(relative_to, tail=tail):
        phpfiles[phpfile]=phpfiles.get(phpfile,0)+1
    return phpfiles


if __name__=="__main__":
    import optparse

    usage="Usage: %prog [<options>] [whitelist ips]\n"+__doc__+"\n"
    parser=optparse.OptionParser(usage)
    parser.add_option("-v","--verbose",dest="verbose", action="count", default=0, help="Be verbose")
    parser.add_option("-m","--mails",dest="mails", action="store", type='int', default=350, help="this many mails in a directory to trigger alarm")
    parser.add_option("-t","--tail",dest="tail", action="store", type='int', default=20000000, help="this many bytes from the end of the exim files and access log to find evils in")
    parser.add_option("-n","--hits",dest="hits", action="store", type='int', default=180, help="how many hits to require in the access log")
    (options,args) = parser.parse_args()

    sys.stderr.write('# Finding directories from which over 350 mails have been sent, with lots of php hits in the access log\n')
    sys.stderr.flush();
    for dir,mails in get_spamming_dirs(mails=options.mails,tail=options.tail):
        PH='/public_html'   
        this_public_html = dir[:dir.find(PH)+len(PH)]
        evilphpfiles = []
        for filename in glob.glob(dir+'/*.php'):
            evilphpfiles.append(filename[filename.find(PH)+len(PH):])
        # print
        sys.stderr.write( "# %s, %s mails:\n" % (dir, mails))
        sys.stderr.flush();
        # print "Evil file list: ",evilphpfiles
        for filename,hits in get_php_hit_counts(dir, tail=options.tail).items():
            if hits<options.hits:
                continue
            if filename in evilphpfiles:
                print "%s\thits=%d mails=%d" % (this_public_html+filename,hits,mails)
                sys.stdout.flush();
            #else:
            #    print "%s: non-evil file, %d hits" % (this_public_html+filename,hits)
