#! /usr/bin/python3

'''
frob: fix something
'''
# see also maintenance/protect

import sys
import os
import glob
import pwd
import stat
import re
import random
import time
import socket
import syslog

ipcs_remove_apache_cmd=""" for i in `ipcs -s | grep nobody | awk '{print $2}'` ; do  ipcrm sem $i; done """

def frob():
    r = "/usr/local/afribin/frob"
    if os.path.exists(r): return r
    import inspect
    return inspect.getfile(frob)

class CpanelData:
    def __init__(self,user=None, verbose=True):
        '''Open user file'''
        self.verbose=verbose
        if not user: user=self.userfromcwd()
        self.setuser(user)

    def setuser(self,user):
        '''Make the user blah'''
        self.user=user
        self.getcpaneldata()
        self.makeapacheconfdir()

    def userfromcwd(self):
        '''Choose user from current working directory'''
        user=os.getcwd().split("/home/",1)[1].split("/",1)[0]
        if not user:
            print(os.getcwd(),"does not seem to indicate a user")
            sys.exit()
        return user

    def has_site(self,site):
        # FIXME: this does not handle site aliases in the userdata files
        for key in [ "DNS", "DNS1", "DNS2", "DNS3" ] :
            if self.cpanel.get(key,'')==site: return True
            if "www."+self.cpanel.get(key,'')==site: return True
        return False

    def getcpaneldata(self):
        '''Read the cpanel data file into self.cpanel[]'''
        cpaneldatafile=open("/var/cpanel/users/"+self.user,"r");
        self.cpanel={}
        for line in cpaneldatafile:
            bits=line.split("=",1)
            if len(bits)>1: self.cpanel[bits[0].strip()]=bits[1].strip()
        cpaneldatafile.close()
        return self.cpanel

    def setitem(self,item,value):
        cpaneldatafile=open("/var/cpanel/users/"+self.user,"r");
        out=[]
        for line in cpaneldatafile:
            if line.startswith(item+"="): continue
            out.append(line)
        if len(out)>0 and not out[-1].endswith('\n'):
            out.append("\n")
        cpaneldatafile.close()
        out.append(item+'='+value+"\n")
        cpaneldatafile=open("/var/cpanel/users/"+self.user,"w");
        cpaneldatafile.writelines(out)
        cpaneldatafile.close()
        return value

    def mkdir_p(self,dir):
        bits=dir.split("/")
        for a in range(1,len(bits)+1):
            try:
                os.mkdir("/".join(bits[:a]))
            except:
                pass

    def makeapacheconfdir(self):
        '''Get the apache dir for configs'''
        if not self.cpanel: self.getcpaneldata()
        apacheconfbase="/usr/local/apache/conf/userdata/std/2/"+self.user;
        apachesslbase ="/usr/local/apache/conf/userdata/ssl/2/"+self.user;
        self.mkdir_p(apacheconfbase)
        self.mkdir_p(apachesslbase)
        apacheconfdir=apacheconfbase+"/"+self.cpanel['DNS']
        self.mkdir_p(apacheconfdir)
    
        for dns in ("DNS","DNS1","DNS2","DNS3","DNS4"):
            if dns not in self.cpanel: continue
            for linkdir in (apacheconfbase,apachesslbase):
                linkname = linkdir+"/"+self.cpanel[dns]
                if os.path.exists(linkname): continue
                os.symlink(apacheconfdir,linkname) 
        self.apacheconfdir=apacheconfdir
        return apacheconfdir

    def __update(self):
        '''Get '''
        self.getcpaneldata()

    def get_apachecustomfile(self,filename):
        '''Figure out where to put the modsecurity stuff'''
        self.makeapacheconfdir()
        return self.apacheconfdir+"/"+filename

    def get_modsecinifile(self):
        '''Figure out where to put the modsecurity stuff'''
        self.modsecinifile=self.get_apachecustomfile("modsec.conf")
        return self.modsecinifile

    def read_modsecinifile(self):
        self.get_modsecinifile()
        self.modsec={}
        self.modsec['SecRuleRemoveById']=[]
        if os.path.exists(self.modsecinifile):
            f = open(self.modsecinifile,"r")
            self.modseclines=f.readlines()
            f.close()
        else:
            self.modseclines=[]
        for line in self.modseclines:
            line=line.strip();
            match=re.search(r'^\s*#',line)
            if match: continue
            match=re.search(r'SecRuleEngine +(\S+)',line)
            if match:
                self.modsec['SecRuleEngine']=match.group(1)
            match=re.search(r'SecRuleEngine _(\S+)',line)
            match=re.search(r'SecRuleRemoveById +(.*)',line)
            if match:
                self.modsec['SecRuleRemoveById'].extend(match.group(1).split())
                
    def write_modsecinifile(self,reason=''):
        self.get_modsecinifile()
        nomodsec= \
"""
<IfModule mod_security2.c>
# %(reason)s
SecRuleEngine %(SecRuleEngine)s 
</IfModule>
"""
        template= \
"""
<IfModule mod_security2.c>
# Turn off specific rules
SecRuleEngine On
SecRuleRemoveById %(SecRuleRemoveById)s
</IfModule>
""" 
        self.modsec['reason']=reason
        if self.modsec.get('SecRuleEngine','')=="Off" \
                or len(self.modsec.get("SecRuleRemoveById",[]))==0:
            out=nomodsec % dict(
                SecRuleEngine=self.modsec.get("SecRuleEngine","On"),
                reason=reason,
            )
        else:
            out=template % dict(
                SecRuleRemoveById=" ".join(self.modsec.get("SecRuleRemoveById",[])),
                reason=reason,
            )

        f = open(self.modsecinifile,"w")
        f.write(out)
        f.close()
        if self.verbose:
            print("Wrote %s -- frob rebuild to rebuild and reload" % self.modsecinifile)

def do_frob(i):
    print(r""" If you've got the job, we've got the frob.
                 ___          ______
                /__/\     ___/_____/\
                \  \ \   /         /\\
                 \  \ \_/__       /  \
                 _\  \ \  /\_____/___ \
                // \__\/ /  \       /\ \
        _______//_______/    \     / _\/______
       /      / \       \    /    / /        /\
    __/      /   \       \  /    / /        / _\__
   / /      /     \_______\/    / /        / /   /\
  /_/______/___________________/ /________/ /___/  \
  \ \      \    ___________    \ \        \ \   \  /
   \_\      \  /          /\    \ \        \ \___\/
      \      \/          /  \    \ \        \  /
       \_____/          /    \    \ \________\/
            /__________/      \    \  /
            \   _____  \      /_____\/
             \ /    /\  \    / \  \ \
              /____/  \  \  /   \  \ \
              \    \  /___\/     \  \ \
               \____\/            \__\/ """)


def fix_ipcs():
    # FIXME: this does not handle site aliases in the userdata files
    global ipcs_remove_apache_cmd
    os.system(ipcs_remove_apache_cmd)

def do_strace(usernames):
    "Install (and remove) strace wrapper"
    phpexec = "/usr/local/cpanel/cgi-sys/php5"
    phpexec_o = phpexec+".orig"
    lines = open(phpexec,'r').readlines()
    if lines[0].find("#!")!=0:
        print("It doesn't look like",phpexec,"is a wrapper file")
        return
    open(phpexec_o,'w').writelines(lines)
    out = []
    droplevel=0
    for line in lines:
        if line.find('exec ')==0 and len(usernames):
            userlist='|'.join(usernames)
            out.append('USER=`whoami`\n')
            out.append('case $USER in\n')
            out.append('  %s) mkdir -p /tmp/$USER; chmod 700 /tmp/$USER; exec /usr/bin/strace -f -s 256 -t -o /tmp/$USER/strace-$$ /usr/bin/php "$@" ;;\n' % userlist)
            out.append('esac\n')
        if line.find("USER=")==0:
            droplevel+=1
        if droplevel==0:
            out.append(line)
        if line.find("esac")==0:
            droplevel=0
    
    open(phpexec,'w').writelines(out)

def pwtest(username,password):
    'submit test via smtp'
    import base64
    params = dict(
        u64 = base64.encodestring(username).strip(" \n\r"),
        p64 = base64.encodestring(password).strip(" \n\r"),
        helo = 'i.r.testa',
        mailfrom = username,
        rcptto = 'joe@hostmail.com',
    )
    test=os.popen("exim -dd -bhc 160.124.1.1",'w')
    smtp = """EHLO %(helo)s
AUTH LOGIN
%(u64)s
%(p64)s
MAIL FROM: <%(mailfrom)s>
RCPT TO: <%(rcptto)s>
DATA
From: <%(mailfrom)s>
To: <%(rcptto)s>
Subject: not a real mail

test
.
QUIT
""" % params 
    print(smtp)
    test.write(smtp)
    test.close()

def has_fcgi():
    # grep '^AddHandler fcgid-script .*\.php' /usr/local/apache/conf/php.conf >& /dev/null || exit
    # grep '^Include "/usr/local/apache/conf/php.conf"' /etc/httpd/conf/httpd.conf >& /dev/null || exit
    httpdconf=open("/usr/local/apache/conf/httpd.conf",'r')
    httpdconf_re = re.compile(r'^\s*Include\s+"/usr/local/apache/conf/php.conf"')
    Found=False
    for line in  httpdconf:
        if httpdconf_re.search(line):
            Found=True
            break
    if not Found: return False

    phpconf=open("/usr/local/apache/conf/php.conf",'r')
    phpconf_re = re.compile(r'^\s*AddHandler\s+fcgid-script\s.*\.php')
    Found=False
    for line in  phpconf:
        if phpconf_re.search(line):
            Found=True
            break
    if not Found: return False
    # We got it ...
    return True


# Functions that do something related to files owned by 'nobody'
def uid_nobody():
    'return the uid for nobody'
    return pwd.getpwnam("nobody").pw_uid

def nobodyfile(file):
    'Is the named file a regular file owned by nobody'
    nobody=uid_nobody()
    try:
        fileinfo = os.stat(file)
        if fileinfo.st_uid == nobody and fileinfo.S_ISDIR(stat.st_mode):
            return True
    except:
        pass

    return False

def hasnobodyfile(dir):
    'Check if there are any nobody files in the directory'
    for file in glob.glob(dir+'/*'):
        if nobodyfile(file): return True
    return False

def hasphpfiles(dir):
    'Check if there are any legitimate php files in the directory'
    for file in glob.glob(dir+'/*.php'):
        return True
    return False

def htaccess_patch(htaccessfile):
    'Patch a .htaccess file and return directives required for the php.ini'
    fd = open(htaccessfile,'r')
    lines=[]
    phpinidirectives=[]
    phpvalue = re.compile(r"""^\s*php_(admin_value|value|flag)\s+['"]?([^"']*)""", re.IGNORECASE)
    drupal_disabled_php = re.compile(r"""^\s*SetHandler\s""", re.IGNORECASE)
    optionsymlink = re.compile(r"^\s*Option.*(FollowSymLinks)", re.IGNORECASE)
    ifmodule = re.compile(r"<IfModule mod_php5\.c>", re.IGNORECASE)
    endifmodule = re.compile(r"</IfModule>", re.IGNORECASE)
    modified = False
    adding_ifmodule=False
    ifmodule_depth=0
    isdrupal=False
    for line in fd:
        if line[-1:] != "\n": line += "\n"
        phpvalue_m = phpvalue.search(line.strip())
        ifmodule_m = ifmodule.search(line.strip())
        endifmodule_m = endifmodule.search(line.strip())
        if ifmodule_m:
            ifmodule_depth+=1
        if endifmodule_m:
            ifmodule_depth=max(ifmodule_depth-1,0)

        # print ifmodule_depth,adding_ifmodule,line.strip()
        if phpvalue_m:
            value=phpvalue_m.group(2)
            value=value.strip('=\r\n\t \'"') 
            phpinidirectives.append(value)
            if ( not adding_ifmodule ) and ( ifmodule_depth==0):
                lines.append("<IfModule mod_php5.c>\n")
                ifmodule_depth+=1
                adding_ifmodule= True
                modified = True
            lines.append(line)
            continue
        else:
            if adding_ifmodule and len(line.strip())>0:
                lines.append("</IfModule>\n")
                ifmodule_depth -= 1
                adding_ifmodule=False;
        if drupal_disabled_php.search(line):
            isdrupal = True
        optionsymlink_m = optionsymlink.search(line.strip())
        if optionsymlink_m and not isdrupal:
            lines.append("# FollowSymLinks conflicts with mod_fcgid:\n")  # but if we're not doing fcgi anymore then hey
            lines.append("## "+line)
            modified = True
            continue
        lines.append(line)
    if adding_ifmodule:
        lines.append("</IfModule>\n")
        adding_ifmodule=False;
    fd.close()
    if modified:
        fd = open(htaccessfile,'w')
        fd.writelines(lines)
        fd.close()
    return phpinidirectives


class phpini:
    def __init__(self):
        self.cpanel=CpanelData()
        self.php_ini = "/home/"+self.cpanel.user+"/php.ini"
        self.load()

    def load(self):
        readfile = self.php_ini
        if not os.path.exists(self.php_ini):
            readfile="/usr/lib/php.ini"
        self.lines = open(readfile,'r').readlines()
        
    def save(self):
        open(self.php_ini,'w').writelines(self.lines)
        return

    # Get a php value
    def __getitem__(self,key):
        for line in range(0,len(self.lines)):
            bits = self.lines[line].split("=",1)
            if len(bits)!=2: continue
            if bits[0].strip()==key: 
                return bits[1].strip()

    def __setitem__(self,key,value):
        'Set a value'
        found=None
        for line in range(0,len(self.lines)):
            bits = self.lines[line].split("=",1)
            if len(bits)!=2: continue
            if bits[0].strip()==key:
                found=line
        if found==None:
            found=len(self.lines)
            self.lines.append('')
        before = self.lines[found]
        self.lines[found]=key + '='+ value+'\n';
        self.save()


def htaccess_fixup():
    cpanel = CpanelData()
    php_ini = "/home/"+cpanel.user+"/php.ini"
    print(php_ini)
    phpinidirectives=[]
    if not os.path.exists(php_ini):
        print(php_ini,"does not exist")
        customphpini(rebuild=False)
    maxdepth = ' '
    if options.number:
        maxdepth=' -maxdepth %d ' % options.number
    for line in os.popen("find . %s -name .htaccess" % maxdepth,'r').readlines():
        htaccess=line.strip();
        if not os.path.exists(htaccess): continue
        phpinidirectives.extend( htaccess_patch(htaccess) )
    directives={}
    for m in phpinidirectives:
        try:
            name,value=m.split(" ",1)
        except ValueError:
            print("Ignoring directive \"%s\"" % m)
            continue
        directives[name.strip().lower()]="%s = %s\n" % (name,value)
    if len(phpinidirectives)>0:
        print("\n".join(phpinidirectives))
        ini = open(php_ini,'r')
        inilines = ini.readlines()
        ini.close()
        output=[]
        for line in inilines:
            if line[-1:] != "\n": line += "\n"
            bits=line.split("=",1)
            if len(bits)!=2:
                output.append(line)
                continue
            directive=bits[0].strip().lower()
            if directive in list(directives.keys()):
                output.append("# "+line)
                output.append(directives[directive])
                directives.pop(directive)
            else:
                output.append(line)
        # put unmatched required directives at the head of php.ini
        for k,m in list(directives.items()):
            output.insert(0,m);

        ini = open(php_ini,'w')
        ini.writelines(output)
        ini.close()
            

def mysqllimit(args,max=30):
    users=args
    if max==0: max=30
    if max>100: 
        print("Too many connections")
        sys.exit(1)
    for user in users:
        cpanel = CpanelData(user=user)
        settings=dict(user=cpanel.user, max=max)
        if not cpanel.user:
            print("Huh? What user %s" % user)
            continue

        mysqlcmd="""mysql mysql -e "update user set max_user_connections=%(max)d where User like '%(user)s%%'; flush privileges;" """ \
            % settings
        print(mysqlcmd)
        os.system(mysqlcmd)

def subscribe():
    print("Subscribing everyone on the box to the INBOX.spam folder")
    cmd = """
find /home/*/mail/* -maxdepth 2 -mindepth 2 -name courierimapsubscribed -type f  |
while read f; do
    grep INBOX.spam "$f" >/dev/null || echo >> "$f" INBOX.spam ;
done
    """
    print(cmd)
    os.system(cmd)

# Show phpinfo or something like it:
def show_phpinfo():
    home = droppriv()
    if os.path.exists(home+"/php.ini"):
        os.system("php -c $HOME -r 'phpinfo();' </dev/null 2>&1")
    else:
        os.system("php          -r 'phpinfo();' </dev/null 2>&1")

# Allow custom php.ini
def customphpini(rebuild=False,disable_functions=False, open_basedir=False, setupfw=''):
    cpanel = CpanelData()
    data=dict(homedir="/home/"+cpanel.user)

    if not os.path.exists("/home/"+cpanel.user+"/php.ini"):
        print("Copying /usr/lib/php.ini to homedir")
        os.system("cp /usr/lib/php.ini %(homedir)s" % data )
    else:
        print("/home/"+cpanel.user+"/php.ini already exists.")

    if open("/etc/httpd/conf/php.conf",'r').read().find("runphp")>=0:
        print("This system uses runphp, so we don't change the apache config")
        rebuild = False
    else:
        apacheconfdir=cpanel.makeapacheconfdir()

        fcgiphpconf=apacheconfdir+"/phpini.conf"
        if os.path.exists(fcgiphpconf):
            print("# There is already a file "+fcgiphpconf+" with the contents below:")
            print(open(fcgiphpconf,'r').read())
            print("# There is already a file "+fcgiphpconf+" with the contents above")
            return

        try:
            f = open(fcgiphpconf,'w')
            print("Writing",fcgiphpconf)
            template = """
# Allow custom php.ini
<IfModule mod_fcgid.c>
FCGIWrapper "/usr/local/cpanel/cgi-sys/php5 -c %(homedir)s" .php5
FCGIWrapper "/usr/local/cpanel/cgi-sys/php5 -c %(homedir)s" .php
</IfModule>
"""
            f.write(template % data )
            f.close()
        except IOError:
            print("(Not writing", fcgiphpconf,")")
            rebuild=False

    php = phpini()
    if disable_functions:
        badfunctions=[
             'shell_exec',
             'apache_get_modules',
             'apache_get_version',
             'apache_getenv',
             'apache_note',
             'apache_setenv',
             'dl',
             'escapeshellarg',
             'escapeshellcmd',
             'exec',
             'get_current_user',
             'getmyi',
             'ini_alter',
             'link',
             'mysql_list_dbs',
             'openlog',
             'pass_thru',
             'passthru',
             'pclose',
             'popen',
             'pcntl_exec',
             'pcntl_fork',
             'pcntl_signal',
             'pcntl_waitpid',
             'pcntl_wexitstatus',
             'pcntl_wifexited',
             'pcntl_wifsignaled',
             'pcntl_wifstopped',
             'pcntl_wstopsig',
             'pcntl_wtermsig',
             'pfsockopen',
             'posix_kill',
             'posix_mkfifo',
             'proc_close',
             'proc_get_status',
             'proc_nice',
             'proc_open',
             'proc_terminate',
             'show_source',
             'socket_bind',
             'socket_create',
             'socket_create_listen',
             'socket_create_pair',
             'symlink',
             'syslog',
             'system',
             'virtual',
            ]
        php['disable_functions'] = ','.join(badfunctions)
        php['enable_dl'] = 'Off'
        php['expose_php'] = 'Off'
        if setupfw:
            php['auto_prepend_file'] = '/usr/local/afribin/apache/php/'+setupfw;
        print("expose_php =", php['expose_php'])
        print("disable_functions =", php['disable_functions'])
        print("enable_dl =", php['enable_dl'])
        print("auto_prepend_file =", php['auto_prepend_file'])

    if open_basedir:
        php['open_basedir'] = '/home/'+cpanel.user+'/public_html:/tmp:/usr/lib/php:/usr/local/lib/php:/usr/local/afribin/apache'
        print("open_basedir =", php['open_basedir'])

    if rebuild:
        print("Running /scripts/rebuildhttpdconf")
        os.system("/scripts/rebuildhttpdconf")

    return


# File frobbers ...
def redirect(title,url):
    stiff = dict(title=title,url=url)
    template="""
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">

<html>
<head>
<title>%(title)s</title>
<meta name="keywords" content="%(title)s" /> <meta name="description" content="%(title)s" /> </head>
<frameset rows="100%%">
<frame src="%(url)s" title="%(title)s" frameborder="0" noresize="noresize"/>
<noframes>
<body> <h1>%(title)s</h1> <p><a href="%(url)s">Click here ...</a></p> </body>
</noframes>
</frameset>
</html>
"""
    print("Writing a frame redirect to %(url)s with page title %(title)s" % stiff)
    index=open("index.html","w")
    index.write(template % stiff);
    index.close()
    os.system("chown --reference=. index.html; chmod 644 index.html")

def mysql_sessiontable_repair():
    list=glob.glob("/var/lib/mysql/*/*session*.MYI")
    for tablefile in list:
        bits=tablefile.split("/")
        table=bits[-1]
        database=bits[-2]
        table=table[:table.find(".MYI")];
        print("mysqlcheck -r %s %s" % (database,table))
        os.system( "mysqlcheck -r %s %s" % (database,table))

def safe(dir):
    print("Removing write acess for directories in %s" % dir)
    ocwd=os.getcwd()
    os.chdir(dir)
    os.system("find . -type d -print0 | xargs -0 chmod go-wr")
    print("Removing write acess for files in %s" % dir)
    os.system("find . -type f -print0 | xargs -0 chmod go-w")
    if has_fcgi():
        print("Removing read acess for php files in %s" % dir)
        os.system(r"find . -type f -name \*.php\* -print0 | xargs -0 chmod go-r")
    os.chdir(ocwd)

def do_images(dirname,no_act=False):
    ocwd=os.getcwd()
    os.chdir(dirname)
    if os.path.exists(".htaccess"):
        fd=open(".htaccess",'r')
        htaccess = fd.read()
        fd.close()
        if re.search('# Added ',htaccess):
            print("%s already protected" % dirname)
            os.chdir(ocwd)
            return False # already added

    if no_act:
        print("%s looks unprotected (permits script execution)" % dirname)
        os.chdir(ocwd)
        return False
    
    print("setting %s as restrictive images directory" % dirname)

    cmd = r"""
touch .htaccess
DATE=`date +%Y%m%d`
if ! grep '^# Added' .htaccess ; then
cat >> .htaccess <<EOF

# Added $DATE
<IfModule mod_php5.c>
php_flag engine off
</IfModule>
Options -ExecCGI 
<Files ~ "\.php.*">
  Order deny,allow
  Deny from all
</Files>
EOF
fi
chown root .htaccess 2>/dev/null
chmod 644 .htaccess
"""
    os.system(cmd)
    os.chdir(ocwd);
    return True

def do_script(scriptname,params):
    "Pass parameters through to a script"
    paramstring = ' '.join(map(shellquote,params))
    os.system('%s %s' % (os.path.dirname(frob())+'/'+scriptname, paramstring))

def do_fcgiauthheaders(params):
    "Pass FCGI auth paramters for basic auth"
    cpanel = CpanelData()
    fcgiauthfile = cpanel.get_apachecustomfile('fcgiauth.conf');
    fd=open(fcgiauthfile,'w')
    fd.write("""# Pass Authorization header so Basic Auth can work
# Date: %(date)s 
<IfModule mod_fcgid.c>
    FcgidPassHeader Authorization
</IfModule>
""" % dict(
        date=time.strftime("%Y-%m-%d"),) )
    print("# Wrote", fcgiauthfile)
    print("frob rebuild # will get this loaded up -- run it")
    

def do_generosity(reason, processes=4):
    "Be generous"
    if not reason:
        print("No reason to be generous. Give me a reason.")
        return
    cpanel = CpanelData()
    generosityfile = cpanel.get_apachecustomfile('generosity.conf');
    fd=open(generosityfile,'w')
    fd.write("""# More generous process limits
# Date: %(date)s  Reason: %(reason)s
<IfModule mod_fcgid.c>
DefaultMaxClassProcessCount %(processes)d
MaxRequestLen 90485760
FcgidMaxRequestLen 90485760
</IfModule>
""" % dict(
        date=time.strftime("%Y-%m-%d"),
        reason=reason,
        processes=processes, ) )
    print("# Wrote", generosityfile)
    print("frob rebuild # will get this loaded up")

def modsecurity_onoff(status,reason=''):
    "Turn mod_security on or off"
    cpanel = CpanelData()
    cpanel.read_modsecinifile()
    if status:
        cpanel.modsec['SecRuleEngine'] = 'On'
    else:
        cpanel.modsec['SecRuleEngine'] = 'Off'
    cpanel.write_modsecinifile(reason=reason)

def modsecurity_addrules(rules):
    "Add a list of bypass rules to mod_security"
    cpanel = CpanelData()
    cpanel.read_modsecinifile()
    changed = False
    for r in rules:
        try:
            cpanel.modsec['SecRuleRemoveById'].find(r)
        except:
            cpanel.modsec['SecRuleRemoveById'].append(r)
            changed = True
    if changed:
        cpanel.write_modsecinifile()
    return changed
    
def modsecurity_autorule():
    "Figure out what broke, and allow it"
    cpanel = CpanelData()
    cpanel.read_modsecinifile()
    error_log=open("/usr/local/apache/logs/error_log","r")
    violation={}
    violationline=''
    after_reload=False
    for line in error_log:
        if line.find("Graceful restart requested")>=0 or \
            line.find("module security2_module is already loaded")>=0 or \
            line.find("resuming normal operations")>=0:
            after_reload=False
            continue
        if not line.find(" ModSecurity: ")>=0: continue
        # [Tue Jul 07 06:55:25 2009] [error] [client 196.210.168.36]
        # ModSecurity: Access denied with code 500 (phase 2). Pattern match
        # "\\.php(3|4|5)?(\\?|&).*=(ht|f)tps?:/" at REQUEST_URI. [file
        # "/usr/local/afribin/apache/mod_security/apache2/rules.conf"] [line
        # "158"] [id "300040"] [rev "1"] [msg "Generic PHPcode injection
        # protection in URI"] [severity "CRITICAL"] [hostname "chns.co.za"]
        # [uri "/Home/index.php"] [unique_id "SlLVPcQh44YAADidNL4AAAAM"] 
        bits=line.strip().split("] [");
        tags=dict(hostname='',uri='',msg='')
        for bit in bits[1:]:
            try:
                (tag,value)=bit.split(" ",1)
            except:
                tag=bit
                value=bit
            tags[tag]=value.strip('"[] \t\r\n')
            after_reload=True
        if not cpanel.has_site(tags.get("hostname")):
            continue
        if "id" not in tags: continue
        tags['timestamp']=bit[0][1:]
        violation=tags
        violationline=line
    print(violation)
    if after_reload:
        print("This hit was after the latest reload:")
    print("Last violation: %(id)s (%(msg)s) at http://%(hostname)s%(uri)s" % tags)
    #for r in rules:
    #    cpanel.modsec['SecRuleRemoveById'].append(tags['id'])
    #cpanel.write_modsecinifile()
    return modsecurity_addrules([tags['id']])
        

def cronfix():
    '''Frob all the crontabls to have a random thingy'''
    for file in glob.glob("/var/spool/cron/*"):
        user=file.split("/")[:-1]
        if user == "root" : continue
        cronfile=open(file,"r");
        crondata=[]
        modified=0
        for line in cronfile:
            if line[:2]=='* ':
                line=('%d-59/15 '+line[2:]) % (random.randrange(14)+1 )
                modified=1
            crondata.append(line)
        cronfile.close()
        if modified:
            print("Wrote modified",file)
            cronfile=open(file,"w");
            for line in crondata:
                cronfile.write(line)
            cronfile.close()

def dcpumonview():
    cmd = "/usr/local/cpanel/bin//dcpumonview | sed 's/<[^>]*>/ /g'"
    os.system(cmd)
    
def domlogs():
    sys.stderr.write("files modified in last 10 minutes\n")
    cmd = "find /usr/local/apache/domlogs -maxdepth 1 ! -name '*_*' -mmin -10"
    os.system(cmd)

def killall():
    global ipcs_remove_apache_cmd
    cmd="""
        echo "# We are killing everything because we are panicky";
        echo "# Killing httpd and php processes";
        killall -9 httpd php php5 ;

        echo "# Making rdiff-backup, dovecot and exim nice"
        renice 19 -p `pidof -x rdiff-backup rrdtool`
        renice 15 -p `pidof -x dovecot imap courier-tcpd pop3 `
        renice 12 -p `pidof -x exim`

        echo "# Restarting mysqld (to stop all processes)"
        service mysql restart;

        echo "# Killing apache IPC entries"
        %(ipcs_remove_apache_cmd)s

        echo "# Cleaning up /usr/local/apache/logs/fcgidsock"
        "rm" -r /usr/local/apache/logs/fcgidsock 
        mkdir /usr/local/apache/logs/fcgidsock 
        chown nobody /usr/local/apache/logs/fcgidsock
        chmod 700 /usr/local/apache/logs/fcgidsock 

        echo "# Starting httpd again"
        service httpd start;

        echo "# Running DDOS termination"
        if pidof -x ddos.py | grep . ; then
            echo "Already running"
        else
            /usr/local/afribin/ddos.py -v
        fi
    """ % dict(ipcs_remove_apache_cmd=ipcs_remove_apache_cmd)
    os.system(cmd)

def apachectlgraceful(build=True):
    if build:
        print("Rebuilding httpd.conf and reloading config in apache")
        cmd="/scripts/rebuildhttpdconf && /usr/local/apache/bin/apachectl graceful"
    else:
        print("Reloading config in apache")
        cmd="/usr/local/apache/bin/apachectl graceful"
    print(cmd)
    os.system(cmd)

def takedown(target, taken_down='taken-down'):
    'Password protect the directory'
    os.chdir(target)
    target=os.getcwd()
    if os.path.exists(taken_down):
        print(target+" taken down already")
        return
    n = target.find("/public_html")
    if n==-1: 
        print(target+" does not include '/public_html'")
        return
    file_list=os.listdir('.')
    os.mkdir(taken_down,0o700)
    for file in file_list:
        os.rename(file,taken_down+'/'+file)
    open('.htaccess','w').write("""
RewriteEngine On
RewriteRule ^ index.php
""")
    open('index.php','w').write("""<?php 

header('HTTP/1.1 503 Service Unavailable');
header('Status: 503 Service Unavailable');
// header('Retry-After: 1800');

?><HEAD>
<TITLE>Site unavailable</TITLE>
</HEAD>
<BODY>
<H1>Site unvailable</H1>
This site is not available.
</BODY>
""");
    os.system('chmod 711 . ; chown --reference=%(taken_down)s .' % {'taken_down': taken_down } );

def hacking(target):
    'Password protect the directory'
    droppriv(target=target)
    os.chdir(target)
    target=os.getcwd()
    n = target.find("/public_html")
    if n==-1: 
        print("Cannot find '/public_html' in "+target)
        return
    htpasswd_dir= target[:n]+"/.htpasswd"+target[n:];
    params = {
        'password': easypassword(),
        'htaccess': target+"/.htaccess",
        'htpasswd_dir': htpasswd_dir,
        'htpasswd_file': htpasswd_dir+"/passwd",
        'target': target,
    }
    cmd="""
if grep -i '^Require valid-user' '%(htaccess)s' > /dev/null 2>&1; then
    echo "Basic auth already set up in %(htaccess)s."
else
    #echo n | cp -i '%(htaccess)s' '%(htaccess)s.orig' > /dev/null 2>&1 
    cat >> '%(htaccess)s' <<EOF

# Insist on a password for this directory:
AuthName "Access to this site is restricted, code 418. Check your mail or log in."
AuthType Basic
AuthUserFile %(htpasswd_file)s
Require valid-user
EOF
    true
    mkdir -p '%(htpasswd_dir)s'
    touch '%(htpasswd_file)s'
    PATH=/usr/local/apache/bin:$PATH
    htpasswd -b '%(htpasswd_file)s' user '%(password)s' > /dev/null 2>&1
    echo 'The password for %(target)s is user "user", password "%(password)s"'
fi
""" % params
    os.system(cmd)

def remove_evil_lines(evils,filename):
    ''' Exclude evil lines '''
    fc = open(filename,'r')
    filecontent=[]
    fixes=0
    for line in fc:
        for evil in evils:
            if evil.search(line):
                line=''
                fixes+=1
                break   # ooh, efficiency
        filecontent.append(line)
    fc.close()
    if fixes == 0:
        return []
    fc = open(filename,'w');
    fc.writelines(filecontent)
    fc.close()
    return [ filename, ]

def cleanup_htaccess(args):
    "Cleanup a htaccess injection"
    evils = (
            re.compile(r'^(\s+$|\s{10}).*\n'),
    )
    fixes=[]
    for filename in args:
        if filename.find('htaccess')>=0:
            fixes.extend( remove_evil_lines(evils,filename) )
    return fixes


def remove_evils(evils,filename):
    ''' Removing matching part of evil regex '''
    fixes=0
    fc = open(filename,'r')
    filecontent = fc.read()
    fc.close()
    for evil in evils:
        newcontent=evil.sub('',filecontent)
        if newcontent!=filecontent: fixes+=1
        filecontent=newcontent
    if fixes:
        fc = open(filename,'w');
        fc.write(filecontent);
        fc.close()
        return [ filename, ]
    return []

def cleanup_eval(args):
    "Cleanup an eval injection"
    evils = (
        re.compile(r'<\?php\s+.*strto.*\[\d\].*\?>'),
        re.compile(r'<\?php\s+(eval|preg_replace)\([^;]*\);\s*\?>'),    # php segment consisting entirely of eval or equivalent
        re.compile(r'(eval|echo)\(base64.*?;'),
        re.compile(r'<\?php\s*\?>\s*|/\*\*/ *',re.I),
    );
    fixes=[]
    for filename in args:
        fixes.extend(remove_evils(evils,filename))
    return fixes

def cleanup_iframe(args):
    evils = (
        re.compile(r'echo "<script>document.write.*?iframe.*?</script>";\s*',re.I),
        re.compile(r'<script>document.write.*?<iframe.*?/iframe>.*?</script>',re.I),
        re.compile(r'<u\s[^>]*style=[^>]*?(display:\s*none|visibility:\s*hidden|left:\s*-\d\d\d\d).*?href="?http://.*?</u>',re.I | re.S),
        re.compile(r'<font\s[^>]*style=[^>]*?(display:\s*none|visibility:\s*hidden|left:\s*-\d\d\d\d|(width|height):\s*0|overflow:\s*hidden).*?href="?http://.*?</font>', re.I | re.S),
        re.compile(r'<div\s[^>]*style=[^>]*?(display:\s*none|visibility:\s*hidden|left:\s*-\d\d\d\d).*?href="?http://.*?</div>', re.I | re.S),
        re.compile(r'<script>function \S+\(\S+\)\{ function \S+ \(\) \{return .*?parseInt.*</script>', re.I | re.S),
        re.compile(r'<iframe\s+src="http.*?</iframe\s*>', re.I | re.S),
        re.compile(r'<iframe\s.*?&#\d+;.*?</iframe\s*>', re.I | re.S),
        re.compile(r'<iframe\s.*?(width|height)=[0-4].*?</iframe\s*>', re.I | re.S),
        re.compile(r'<!--[a-f0-9]{32}-->\s*', re.I),
        re.compile(r'<!-- ~ -->\s*',re.S),
        re.compile(r'<script>.*?document.body.appendChild.*?</script>\s*', re.I | re.S),
        re.compile(r'<script.*?>.*?(\%[0-9a-f][0-9a-f]){40}.*?</script>\s*', re.I | re.S),
        re.compile(r'(/\*.*?\*/ )?try.*?document.body.appendChild.*?\s*', re.I),
        re.compile(r'<!-- (\[[a-z0-9]{8,64}|~) -->.*?<!-- ([a-z0-9]{8,64}\]|~) -->', re.I | re.S),
        re.compile(r'<img heigth="1" width="1" .*?>', re.I | re.S),
        )
    fixes=[]
    for filename in args:
        fixes.extend(remove_evils(evils,filename))
    return fixes

def notify_ftp(args):
    cpanel = CpanelData()
    cmd="""
echo "%(domain)s security :"
echo ""
echo "Please notify the owner of %(domain)s that we have detected suspicious activity"
echo "on their account %(user)s on `hostname`."
echo ""
echo "The client should check their desktop systems for spyware, and change their FTP"
echo "password."
echo ""
echo "Here are the log messages:"
echo ""
cat /var/log/messages* | grep '%(vargs)s'
echo 
""" 
    settings = dict(
        vargs= "' '".join(args),
        user=cpanel.user,
        domain=cpanel.cpanel['DNS'])
    os.system( cmd % settings )


def dbaccess_openx(path):
    res={}
    res['file']= path
    res['app']= 'openx'
    f=open(path,'r')
    heading_re = re.compile(r"""^\[(.*)\]""")
    define_re = re.compile(r"""^\s*(\S+)\s*=(.+)""")
    part=''
    for line in f:
        h= heading_re.search(line)
        if h: part=h.group(1)
        if part=='database':
            d= define_re.search(line)
            if d:
                key=d.group(1).lower()
                value=d.group(2)
                if key=='host': res['host']=value.strip('"\'')
                if key=='username': res['user']=value.strip('"\'')
                if key=='password': res['pass']=value.strip('"\'')
                if key=='name': res['db']=value.strip('"\'')
    if 'db' in res:
        return res

def dbaccess_wordpress(path):
    res={}
    res['prefix']= ''
    res['file']= path
    res['app']= 'wordpress'
    f=open(path,'r')
    # define('DB_NAME', 'idols4ci_wrdp1');    // The name of the database
    # define('DB_USER', 'idols4ci_wrdp1');     // Your MySQL username
    # define('DB_PASSWORD', 'rmcd'); // ...and password
    # define('DB_HOST', 'localhost');    // 99% chance you won't need to change this value
    define_re = re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")
    for line in f:
        # if line.find("define") >=0: sys.stdout.write( line)
        definebits=define_re.search(line.strip())
        if definebits:
            res[definebits.group(1)]=definebits.group(2)
    f.close()
    if 'DB_NAME' not in res:
        return None
    res['db']= res.get('DB_NAME')
    res['user']= res.get('DB_USER')
    res['pass']= res.get('DB_PASSWORD')
    res['host']= res.get('DB_HOST')
    return res

def dbaccess_moodle(path):
    res={}
    res['prefix']=''
    res['file']= path
    res['app']= 'moodle'
    f=open(path,'r')
    # $CFG->dbhost    = 'localhost' ;
    # $CFG->dbname    = 'learnnkv_mdle1' ;
    # $CFG->dbuser    = 'learnnkv_mdle1' ;
    # $CFG->dbpass    = '{eB9';
    define_re = re.compile(r"""->\s*([a-z]+)\s*=\s*['"](.*?)['"]""")
    for line in f:
        # if line.find("define") >=0: sys.stdout.write( line)
        definebits=define_re.search(line.strip())
        if definebits:
            res[definebits.group(1)]=definebits.group(2)
    f.close()
    if 'dbname' not in res:
        return None
    res['db']= res.get('dbname')
    res['user']= res.get('dbuser')
    res['pass']= res.get('dbpass')
    res['host']= res.get('dbhost')
    return res

def dbaccess_phpbb(path):
    res={}
    res['prefix']=''
    res['file']= path
    res['app']= 'phpbb'
    #$dbhost = 'localhost';
    #$dbname = 'sapanhnr_dtbase1';
    #$dbuser = 'sapanhnr_devon';
    #$dbpasswd = 'HWD5';
    #$table_prefix = 'phpbb_';
    f=open(path,'r')
    define_re = re.compile(r"""\$(db[a-z]+)\s*=\s*['"](.*?)['"]\s*;""")
    for line in f:
        # if line.find("define") >=0: sys.stdout.write( line)
        definebits=define_re.search(line)
        if definebits:
            res[definebits.group(1)]=definebits.group(2)
    f.close()
    if 'dbname' not in res:
        return None
    res['db']= res.get('dbname')
    res['user']= res.get('dbuser')
    res['pass']= res.get('dbpasswd')
    res['host']= res.get('dbhost')
    # print res
    return res

def randompassword(length=9):
    'Generate a 9 character password of sorts'
    import string
    import random
    return ''.join( random.Random().sample(string.ascii_letters+string.digits, length) )

def easypassword(length=8):
    'Generate an easy 8 character password of sorts'
    import string
    import random
    word = ''.join( random.Random().sample(string.ascii_letters, length*4) )
    word=word.lower()
    word=re.sub("([^aeoiu]{2})[^aeiou]+","\\1",word)
    word=re.sub("([aeoiu]{2})[aeiou]+","\\1",word)
    word=re.sub("([^aeoiu]{2})[^aeiou]+","\\1",word)
    word=word[:length]
    return word

def joomlacrackformat(line):
    'Format joomla passwords for john'
    u,p=tuple ( line.strip('\n').split('\t') )
    i = p.find(':')<0
    if i<0:
        return u+':$dynamic_0$'+p
    return u+':md5_gen(1)'+p.replace(":","$");

def do_crack(res):
    # show passwords for application for cracking
    found=0
    if res['app'].startswith('joomla'):
        # Joomla 2.5:
        for sql in (""" # joomla 2.x
            SELECT users.username,users.password FROM jos_usergroups groups, jos_user_usergroup_map map, jos_users users WHERE groups.title LIKE 'Super%' AND map.group_id=groups.id and users.id=map.user_id
            """, """ # joomla 1.5
            SELECT users.username,users.password FROM jos_core_acl_groups_aro_map map, jos_core_acl_aro aro, jos_users users WHERE map.aro_id=aro.id and aro.value=users.id and map.group_id=25; 
            ""","""
            SELECT users.username,users.password FROM jos_users users order by id limit 5;
            """) :
            res['sql'] = sql.replace('jos_',res['prefix'])
            fd = os.popen('''%(mysql)s -N -B -e "%(sql)s" 2>/dev/null ''' % res , 'r');
            for line in fd:
                print(joomlacrackformat(line))
                found+=1
            if found: break;

    if res['app'].startswith('wordpress'):
        cmd=r'''%(mysql)s -N -B -e "select concat(u.user_login,':',u.user_pass) from %(prefix)susers u left join %(prefix)susermeta m on u.ID=m.user_id and m.meta_key='%(prefix)scapabilities' where m.meta_value like '%%admin%%'  " ''' % res
        if options.verbose:
            sys.stderr.write("%s\n" % cmd);
        os.system(cmd);

def do_upgrade_wordpress(res):
    """
    Upgrade wordpress by clobbering files in the current dir with the distribution version
    Does it work??
    """
    if res['app']!='wordpress':
        return
    # if not os.path.exists("wp-content"): return
    print("Running wordpress upgrade")
    droppriv();
    res['url']='http://wordpress.org/latest.tar.gz';
#    res['url']='http://answers.afrihost.com/updates/wordpress.php';
    #res['url']='http://answers.afrihost.com/updates/wordpress.php';
    os.system("""
            umask 022
            echo Downloading and extracting wordpress update ... 1>&2
            wget -O - %(url)s | tar -zvx --strip-components=1 2>&1 | tail
    """ % res)
    do_wp(['core','update-db']);
    sys.stderr.write("Done\n")

def do_certs():
    os.system(r"""
    curl http://curl.haxx.se/ca/cacert.pem -o /etc/pki/tls/certs/ca-bundle.crt
    """)


def runphpinstall():
    os.system(r"""
        make -C /usr/local/afribin/src ;
    
        find /usr/local/apache/conf/userdata/std/2/ -name phpini.conf | while read x; do mv $x $x.DEPRECATED; done

        echo '
# Fastcgi configuration for PHP5
LoadModule fcgid_module modules/mod_fcgid.so
MaxRequestsPerProcess 500
# 10Mb upload limit 
MaxRequestLen 90485760
AddHandler fcgid-script .php5 .php4 .php .php3 .php2 .phtml
#FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php5
#FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php4
#FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php
#FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php3
#FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .php2
#FCGIWrapper /usr/local/cpanel/cgi-sys/php5 .phtml

# End of autogenerated PHP configuration.

FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php5
FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php4
FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php 
FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php3
FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .php2
FCGIWrapper "/usr/local/cpanel/cgi-sys/runphp /usr/local/cpanel/cgi-sys/php5" .phtml

IdleTimeout 60
ProcessLifeTime 600
MaxProcessCount 400
DefaultMinClassProcessCount 0
DefaultMaxClassProcessCount 3
IPCConnectTimeout 8
IPCCommTimeout 600
BusyTimeout 600


' > /etc/httpd/conf/php.conf; 

# Fixup wrapper if it it php without parameters
egrep 'exec /usr/bin/php ?("\$@")? *$' /usr/local/cpanel/cgi-sys/php5 && {
    perl -p -i -e 's{^exec /usr/bin/php *("\$\@")?}{exec /usr/bin/php "\$\@"}' /usr/local/cpanel/cgi-sys/php5 
    mkdir -p /var/cpanel/conf/apache/wrappers
    cp -a /usr/local/cpanel/cgi-sys/php5 /var/cpanel/conf/apache/wrappers/php5
}

    /scripts/rebuildhttpdconf && /usr/local/apache/bin/apachectl graceful ;
    """)

def do_upgrade():
    # Various upgrades
    res=dbaccess(show=False)
    if res and 'app' in res:
        do_upgrade_drupal(res)
        do_upgrade_mybb(res)
        do_upgrade_joomla(res,do_upgrade=True,do_password=False)
        do_upgrade_joomla16(res,do_upgrade=True);
        do_upgrade_wordpress(res)
    else:
        print("No application")

def do_upgrade_drupal(res):
    if res['app']!='drupal':
        return
    do_drush(['up'])

def do_upgrade_mybb(res):
    if res['version'] > '1.6' or res['app']!='MyBB':
        # return "%(app)s version %(version)s not supported by this MyBB upgrade" % res
        return
    droppriv()
    print("Upgrading to MyBB some other version")
    os.system("""
        mkdir mybb_upgrade_files
        cd mybb_upgrade_files || exit
        curl -O  http://www.mybb.com/download/139
        unzip 139
        [ -f ../images/logo.gif ] && rm Upload/images/logo.gif
        tar -C Upload -c . | tar -C .. -x
        cd ..
        rm -r mybb_upgrade_files
    """)

def do_upgrade_joomla16(res,do_upgrade=True):
    # Joomla 1.6 to 2.2 upgrade to current or something
    'upgrade joomla installation in the current dir'
    if res['app'] != 'joomla-2.x-series' or res['version'][:1] > '2':
        # print "%(app)s version %(version)s not supported by joomla 1.6+ upgrade" % res
        return 

    configuration=res['file']
    print("# application=%(app)s configfile=%(file)s prefix=%(prefix)s" % res)
    update_joomla_config_file(configuration, 'secret', easypassword());

    res['url']="http://joomlacode.org/gf/download/frsrelease/17410/76012/Joomla_2.5.7-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/17715/77262/Joomla_2.5.8-Stable-Full_Package.zip";
    res['url']="http://joomlacode.org/gf/download/frsrelease/17968/78430/Joomla_2.5.9-Stable-Full_Package.zip";
    res['url']="http://joomlacode.org/gf/download/frsrelease/18322/80354/Joomla_2.5.11-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/18624/83477/Joomla_2.5.14-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/18858/91472/Joomla_2.5.16-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/19145/157516/Joomla_2.5.18-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/19754/161033/Joomla_2.5.25-Stable-Full_Package.zip"
    res['url']="http://answers.afrihost.com/updates/Joomla_2.5.14-Stable-Full_Package.zip"
    res['url']="http://answers.afrihost.com/updates/Joomla_2.5.18-Stable-Full_Package.zip"
    res['url']="http://answers.afrihost.com/updates/Joomla_2.5.22-Stable-Full_Package.zip"
    res['url']="http://answers.afrihost.com/updates/Joomla_2.5.24-Stable-Full_Package.zip"
    res['url']="http://answers.afrihost.com/updates/Joomla_2.5.25-Stable-Full_Package.zip"
    res['url']="http://answers.afrihost.com/updates/Joomla_2.5.27-Stable-Full_Package.zip"
    res['url']="http://answers.afrihost.com/updates/Joomla_2.5.28-Stable-Full_Package.zip"
    # res['version']="2.5.11"
    if do_upgrade:
        res['version']=res['url'].split('_')[1].split('-')[0];
        print("Downloading Joomla %(version)s" % res)
        res['downloadfilecache']=os.environ['HOME']+'/'+os.path.basename(res['url']);
        res['downloadfile']='./'+os.path.basename(res['url']);
        os.system(""" 
            find %(downloadfilecache)s -mtime -1 | grep -q . || 
            (
                rm -f %(downloadfilecache)s
                wget -O %(downloadfilecache)s %(url)s
                touch %(downloadfilecache)s
            )
            cp %(downloadfilecache)s %(downloadfile)s
        """ % res );
        # Set non-runnable properties all over
        do_secure(res)
        print("Making autologin script")
        url = autologin_joomla16(show=False,timeout=7200)
        print("Dropping privileges") 
        droppriv()
        print("Moving templates to templates.preserve in case you hacked the default template..." % res)
        os.system("[ -d templates.preserve ] || mv templates templates.preserve" % res) 
        print("Extracting Joomla %(version)s" % res)
        os.system("unzip -o %(downloadfile)s 2>&1 | tail" % res)
        print("Removing installation directory")
        os.system("rm -rf installation")
        os.system("if [ -d templates -a -d templates.preserve ] ; then rm -rf templates ; mv templates.preserve templates; fi" % res)
        print("Random 1.6 to 2.x patches")
        os.system(r"""
            find templates -name \*.php -print0|
                xargs -0 egrep -lZ 'mainframe-|_component->user' | 
                xargs -0 sed -i '
                    s/\$mainframe-/JFactory::getApplication()-/g;
                    s/\$this->_component->user->/JFactory::getUser()->/g;
                    ' 2>/dev/null
        
            FILE=plugins/system/jat3/jat3/core/joomla/modulehelper.php
            if [ -f $FILE ] ; then 
              echo "jat3 update"
              perl -p -i -e 's/new JDatabaseQuery/\$db->getQuery(true)/g' $FILE
            fi

        """)
    print("")
    url_dbfix=url.split('/administrator/')[0]+'/administrator/?option=com_installer&view=database&task=database.fix';
    print("If the automatic thingy doesn't work, you must run the extension manager database update MANUALLY: Extension Manager | Database | Fix now:")
    print("\x1b[31m"+url_dbfix+"\x1b[0m")
    print("Trying it:")
    os.system("""curl -s -L              -c $HOME/.cj '%(url)s'       | wc -l ; 
                 curl -s -L -b $HOME/.cj -c $HOME/.cj '%(url_dbfix)s' | wc -l ;
                 rm $HOME/.cj
                 """ % {'url':url,'url_dbfix':url_dbfix} );
#

def php_str_escape(value):
    return value.replace('\\','\\\\').replace('\'','\\\'');

#define('AUTH_KEY', 'f7sXx[2wbB@LBwlxS{A56y>+[DL9b3FJbvVZXz iq\\Ms}?(;_a#ypoR5\'3w<G5>FL~1X');
#define('SECURE_AUTH_KEY', '-\"r642fed3E1d>}MdGFlh@tR!QPU%/!S_6;CjO0}^>6T[DL9b3FJbvVZ+ <?)3 U#*X;');
#define('LOGGED_IN_KEY', 'Q^l\"6-pL[DL9b3FJbvVZ\zkw>OG<U$%u0:9H:Jl)+d$~jIBTt&CPL[Dp<KY4zL3y');
#define('NONCE_KEY', 'asdasdjkasdjk[DL9b3FJbvVZjklasdjklasdkl&^%^&*(&*(jklasdjklasdjkl&*)))');

def generate_wordpress_key():
    # example https://api.wordpress.org/secret-key/1.1/ :
    # define('AUTH_KEY',        '{p^(,[N|SnRst+`_Fqt23u#dcM[LF!||kg=5*P{a?*6A{Q,XA{yzGmEtG^3=omHz');
    # define('SECURE_AUTH_KEY', 'nu}dQtJhkB,7Elo7f~%XsZZ=ZMt>2,,J{?w}G*b $LE<s_xTG$^nWxxyFhV=9&}b');
    # define('LOGGED_IN_KEY',   'x{j%7wC8-pB.Z3qNin.7-VW#Xdc3;{E||m<m:>ds-JWc;0:&0!<=|=IB6BT2M);_');
    # define('NONCE_KEY',       'D2/S-d>4UI&{Vw9P+<-/:s<y+q(+bQ2DMDi=|-u>]$;E1( ~0FL{BR+-&/&AmjXL');
    alphabet = '`^~<=>|_-,;:!?/.()[]{}@$*&#%+0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqrRsStTuUvVwWxXyYzZ'
    return ''.join(random.Random().sample(alphabet, 64))

def refresh_wordpress_keys(configuration_file):
    newvalues = {
         'AUTH_KEY': generate_wordpress_key(),
         'SECURE_AUTH_KEY': generate_wordpress_key(),
         'LOGGED_IN_KEY': generate_wordpress_key(),
         'NONCE_KEY': generate_wordpress_key(),
    }
    update_wordpress_configuration_file(configuration_file, newvalues)

def update_wordpress_configuration_file(configuration_file, newvalues):
    "Write values to wordpress configuration"
    changed=[]
    fd=open(configuration_file,'r')
    lines=[]
    define_re = re.compile(r"define\('([A-Z_]+)',\s*'(.*)'\);\s*$");
    for line in fd:
        m = define_re.search(line)
        if m and m.group(1) in list(newvalues.keys()):
            configuration_key=m.group(1)
            newvalue=newvalues[configuration_key];
            line = "define('%s', '%s');\n" % (configuration_key, php_str_escape(newvalue))
            changed.append(configuration_key)
        lines.append(line)
    fd.close()

    if len(changed):
        print("Updating "+configuration_file+" setting new "+(' '.join(changed)))
        st = os.stat(configuration_file);
        os.chmod(configuration_file,0o600);   # unwriteable files are a pain ...
        fd=open(configuration_file,"w");
        fd.writelines(lines)
        fd.close()
        os.chmod(configuration_file, st.st_mode & 0o777);  # reset the mode as it was
    return changed

#
def update_joomla_config_file(configuration_file, configuration_key, newvalue):
    changed=0
    fd=open(configuration_file,'r')
    lines=[]
    define_re = re.compile(r"""(var|public) \$([a-z]+)\s*=\s*['"](.*?)['"]\s*;""")
    for line in fd:
        m = define_re.search(line)
        if m and m.group(2)==configuration_key:
            replace = {
                'vartype': m.group(1),
                'key': m.group(2),
                'value': newvalue, }
            line="        %(vartype)s $%(key)s = '%(value)s';\n" % replace;
            changed+=1
        lines.append(line)
    fd.close()

    if changed:
        print("Setting new "+configuration_key+" in "+configuration_file)
        st = os.stat(configuration_file);
        os.chmod(configuration_file,0o600);   # unwriteable files are a pain ...
        fd=open(configuration_file,"w");
        fd.writelines(lines)
        fd.close()
        os.chmod(configuration_file, st.st_mode & 0o777);  # reset the mode as it was
    return changed


def do_upgrade_joomla(res,do_upgrade=True,do_password=True):
    'upgrade joomla installation in the current dir'
    configuration=res['file']
    if not res['app'] in ('joomla',):
        # print "%(app)s version %(version)s not supported by this joomla upgrade" % res
        return 

    # Decide what we're putting in its place:
    res['newdbpassword'] = randompassword()
    res['adminpassword'] = randompassword()

    if do_password: 
        os.system("""mysql --host=%(host)s --password='%(newdbpassword)s' --user=%(user)s %(db)s -e 'UPDATE %(prefix)susers SET password=md5("%(adminpassword)s") WHERE usertype LIKE "%%dministrator%%";' """ % res);
        print(("The new administrator password is %(adminpassword)s") % res)
    
    print("Old connection command: mysql --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s" % res)
    print("Setting new db password" % res)
    rc = os.system(""" mysql --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s -e 'SET password=PASSWORD("%(newdbpassword)s"); ' """ % res);
    if rc==0:
        print("Updating configuration to use new password, and setting new secret")
        update_joomla_config_file(configuration, 'password', res['newdbpassword']);
    update_joomla_config_file(configuration, 'secret', easypassword());
    print(("The database password is now %(newdbpassword)s" ) % res)

    #res['com_fabrik']="http://answers.afrihost.com/updates/com_fabrik_2.1.1.zip"
    res['version']="1.5.26"
    res['url']="http://joomlacode.org/gf/download/frsrelease/12610/53421/Joomla_1.5.20-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/12975/56086/Joomla_1.5.21-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/13105/57240/Joomla_1.5.22-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/14506/63460/Joomla_1.5.23-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/15898/68914/Joomla_1.5.24-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/16026/69665/Joomla_1.5.25-Stable-Full_Package.zip"
    res['url']="http://joomlacode.org/gf/download/frsrelease/16890/73390/Joomla_1.5.26-Stable-Full_Package.zip"
    res['url']="http://answers.afrihost.com/updates/Joomla_1.5.26-Stable-Full_Package.zip"
    if do_upgrade:
        if not res['version'].startswith('1.5.') or res['app']!='joomla':
            # print "%(app)s version %(version)s not supported by this joomla upgrade" % res
            return 
        print("Downloading Joomla %(version)s" % res)
        res['downloadfilecache']=os.environ['HOME']+'/'+os.path.basename(res['url']);
        res['downloadfile']='./'+os.path.basename(res['url']);
        os.system(""" 
            find %(downloadfilecache)s -mtime -1 | grep -q . || 
            (
                rm -f %(downloadfilecache)s
                wget -O %(downloadfilecache)s %(url)s
                touch %(downloadfilecache)s
            )
            cp %(downloadfilecache)s %(downloadfile)s
        """ % res );
        print("Dropping privileges") 
        droppriv()
        print("Moving templates to templates.preserve in case you hacked the default template..." % res)
        os.system("[ -d templates.preserve ] || mv templates templates.preserve" % res) 
        print("Extracting Joomla %(version)s" % res)
        os.system("unzip -o %(downloadfile)s 2>&1 | tail" % res)
        print("Removing installation directory and zip file")
        os.system("if [ -d templates -a -d templates.preserve ] ; then rm -rf templates ; mv templates.preserve templates; fi" % res)
        os.system("rm -rf installation %(downloadfile)s" % res)
        # Upgrade vulnerable stuff too
        # com_fabrik does not like being upgraded:
        #if os.path.exists('components/com_fabrik'):
        #    print "Downloading com_fabrik %(version)s" % res
        #    os.system("wget --continue %(com_fabrik)s -O com_fabrik.zip; unzip -o com_fabrik.zip" % res) 
        ## We don't do this, because we dropped privileges
        #print "Changing ownership of extracted zip files (this won't work on BSD - please do it manually!)"
        #os.system("find . -user root -print0 | xargs -0 chown --reference=.")

    if do_upgrade:
        print("We have updated the Joomla installation to version %(version)s" % res)
    if do_password:
        print(("The new administrator password is %(adminpassword)s") % res)
    print(("The database access password is now %(newdbpassword)s for phpmyadmin" ) % res)

def dbaccess_joomla(path):
    res={}
    res['prefix']= ''
    res['file']= path
    res['app']= 'joomla'
    #var $password = 'Scr';
    #var $host = 'localhost';
    #var $user = 'fairlkrj_gilan';
    #var $db = 'fairlkrj_fairleads';
    f=open(path,'r')
    define_re = re.compile(r"""var \$([a-z]+)\s*=\s*['"](.*?)['"]\s*;""")
    for line in f:
        # if line.find("define") >=0: sys.stdout.write( line)
        definebits=define_re.search(line)
        if definebits:
            res[definebits.group(1)]=definebits.group(2)
    f.close()
    if 'db' not in res:
        return None
    res['prefix']= res.get('dbprefix','')
    res['db']= res.get('db')
    res['user']= res.get('user')
    res['pass']= res.get('password')
    res['host']= res.get('host')
    res['version'] = version_of_joomla()
    return res

def parsephpfile(filename,res,already={}):
    # print "filename =",filename
    if (not os.path.exists(filename)): return;
    f=open(filename,'r');
    include_re = re.compile(r"""(include|require|include_once|require_once)\s*\(['"](.*?)['"]\s*\)""")
    assign_re = re.compile(r"""(\$\S+?)\s*=\s*['"](.*?)['"]""")
    connect_re = re.compile(r"""mysql_p?connect\s*\(\s*(\S+)\s*,\s*(\S+)\s*,\s*(\S+)\s*\)""")
    select_re = re.compile(r"""mysql_select_db\s*\(\s*(\S+?),""")
    for line in f:
        m=include_re.search(line)
        if m:
            include_file = m.group(2)
            if include_file not in already: 
                already[include_file]=True
                parsephpfile(include_file,res,already=already)
        m=assign_re.search(line)
        if m:
            res[m.group(1)]=m.group(2)
        m=connect_re.search(line)
        if m: 
            res['host']=m.group(1).strip('"\'')
            res['user']=m.group(2).strip('"\'')
            res['pass']=m.group(3).strip('"\'')
            if res['host'] in res: res['host']=res[res['host']]
            if res['user'] in res: res['user']=res[res['user']]
            if res['pass'] in res: res['pass']=res[res['pass']]
            res['file']= filename
        m=select_re.search(line)
        if m:
            res['db']=m.group(1).strip('"\'');
            if res['db'] in res: res['db']=res[res['db']]
    f.close()

def dbaccess_guess(path):
    'parse a php app to see what its database is'
    res={}
    res['prefix']= ''
    res['app']= 'some application'
    res['file']= path
    parsephpfile(path,res)
    # print "res.keys() =", ",".join(res.keys())
    return res
    
def urldecode(data):
    import urllib.request, urllib.parse, urllib.error
    data= urllib.parse.unquote_plus(data)
    return data

def dbaccess_other(dummyfilename,verbose=False):
    applications = [
        {
            'app': 'drupal', 
            'configfile': 'sites/default/settings.php',
            'assignments':  [ re.compile(r"^\s*'([^']+)'\s*=>\s*'([^']*)'"), ],
            'map': { 'db': 'database', 'user': 'username', 'pass': 'password', 'host': 'host', 'prefix': 'prefix', },
            #'debug': True
        },
        {
            'app': 'drupal', 
            'configfile': 'sites/default/settings.php',
            'regexlist':  {
                re.compile(r'''^\s*\$.*= *'mysqli?://(\S+):(\S+)@(\S+)/(\S+)['"]'''): [ 'user','pass','host','db' ],
                re.compile(r"\$db_prefix\s*=\s*'(.*?)'") : ['prefix'],
            },
            'map': { 'db': 'db', 'user': 'user', 'pass': 'pass', 'host': 'host'},
            'pass_postprocess': urldecode,
        },
        {
            'app': 'joomla-2.x-series', 
            'configfile': 'configuration.php',
            'assignments': [ re.compile(r"""public \$([a-z]+)\s*=\s*'(.*?)'\s*;""") ],
            'map': { 'db': 'db', 'user': 'user', 'pass': 'password', 'host': 'host', 'prefix': 'dbprefix'},
        },
        {
            'app': 'joomla-1.ancient', 
            'configfile': 'configuration.php',
            'assignments': [re.compile(r"""mosConfig_(\S+)\s*=\s*["'](\S+?)["']""")],
            'map': { 'db':'db', 'user':'user', 'pass':'password', 'host':'host', 'prefix': 'dbprefix', },
        },
        {
            'app': 'vtiger', 
            'configfile': 'config.inc.php',
            'assignments':  [ re.compile(r'''\$dbconfig\['([a-z_]+)'\] *= *'(.*?)';'''), ] ,
            'map': { 'db': 'db_name', 'user': 'db_username', 'pass': 'db_password', 'host': 'db_server'},
        },
        {
            'app': 'b2evolution', 
            'configfile': 'conf/_basic_config.php',
            'assignments':  [ re.compile(r"'([^']+)' *=> *'([^']+)'"), ],
            'map': { 'db': 'name', 'user': 'user', 'pass': 'password', 'host': 'host'},
            'regexlist':  { re.compile(r"\$tableprefix\s*=\s*'(.*?)'") : ['prefix'], },
        },
        {
            'app': 'symfony', 
            'configfile': 'config/databases.yml',
            'regexlist':  {
                re.compile(r"'mysql:host=(.*?);dbname=(.*?)'") : ['host','db'],
            },
            'assignments': [ re.compile(r""" (\S+): (\S+)""") ],
            'map': { 'user': 'username', 'pass': 'password' } ,
        },
        {
            'app': 'cubecart', 
            'configfile': 'includes/global.inc.php',
            'assignments':  [ re.compile(r"\['([^']+)'\s*\] *= *'([^']*)'"), ],
            'map': { 'db': 'dbdatabase', 'user': 'dbusername', 'pass': 'dbpassword', 'host': 'dbhost', 'prefix': 'dbprefix'},
            #'debug': True, 
        },
        # $CONFIG->dbuser = 'whereduw_elgguse';
        # $CONFIG->dbpass = 'bhhgbk13463';
        # $CONFIG->dbname = 'whereduw_elggdb';
        # $CONFIG->dbhost = 'localhost';
        # $CONFIG->dbprefix = 'elgg_';
        {
            'app': 'elgg', 
            'configfile': [ 'engine/settings.php', ],
            'assignments':  [ re.compile(r"\$CONFIG->(\w+) = '(.*?)';"), ],
            'map': { 'db': 'dbname', 'user': 'dbuser', 'pass': 'dbpass', 'host': 'dbhost', 'prefix': 'dbprefix'},
        },
        {
            'app': 'indexhibit.org', 
            'configfile': [ 'ndxz-studio/config/config.php', 'config/config.php', ],
            'assignments':  [ re.compile(r"\['([^']+)'\] *= *'([^']*)'"), ],
            'map': { 'db': 'db', 'user': 'user', 'pass': 'pass', 'host': 'host'},
        },
        {
            'app': 'webSpell.org', 
            'configfile': '_mysql.php',
            'assignments':  [ re.compile(r'''\$([a-z]+)\s*=\s*['"]([^'"]+)['"]'''), ],
            'map': { 'db': 'db', 'user': 'user', 'pass': 'pwd', 'host': 'host'},
        },
        {
            'app': 'e107', 
            #'debug': True, 
            'configfile': 'e107_config.php',
            'assignments':  [ re.compile(r'''\$(mySQL[a-z]+)\s*=\s*['"]([^'"]+)['"]'''), ],
            'map': { 'db': 'mySQLdefaultdb', 'user': 'mySQLuser', 'pass': 'mySQLpassword', 'host': 'mySQLserver', 'prefix': 'mySQLprefix'},
        },
        {
            'app': 'hesk helpdesk', 
            'configfile': 'hesk_settings.inc.php',
            'assignments':  [ re.compile(r"\['([^']+)'\] *= *'([^']+)'"), ],
            'map': { 'db': 'db_name', 'user': 'db_user', 'pass': 'db_pass', 'host': 'db_host'},
        },
        {
            'app': 'eticketsupport.com', 
            'configfile': 'settings.php',
            'assignments': [re.compile(r"""\$(\S+)\s*=\s*["']?(\S+?)["']?;""")],
            'map': { 'db': 'db_name', 'user': 'db_user', 'pass': 'db_pass', 'host': 'db_host', 'prefix': 'db_table_prefix', 'version': 'version'}
        },
        {
            # #pixelpost_db_host      = "localhost";  // database host, often "localhost"
            # #pixelpost_db_user      = "gavinjwd_pp";  // database user
            # #pixelpost_db_pass      = "A5S";  // database user password
            # #pixelpost_db_pixelpost = "gavinjwd_pixelpost";  // database
            'app': 'pixelpost.org', 
            'configfile': 'includes/pixelpost.php',
            'assignments': [re.compile(r"""\$(\S+)\s*=\s*["']?(\S+?)["']?;""")],
            'map': { 'db': 'pixelpost_db_pixelpost', 'user': 'pixelpost_db_user', 'pass': 'pixelpost_db_pass', 'host': 'pixelpost_db_host', }
        },
        {
            'app': 'phplist', 
            'configfile': 'config/config.php',
            'assignments':  [ re.compile(r"\['([^']+)'\] *= *'([^']+)'"), ],
            'map': { 'db': 'database_name', 'user': 'database_user', 'pass': 'database_password', 'host': 'database_host'},
        },
        {
            'app': 'simbarsk', 
            'configfile': 'config.local.php',
            'assignments':  [ re.compile(r"\['([^']+)'\] *= *'([^']+)'"), ],
            'map': { 'db': 'db_host', 'user': 'db_user', 'pass': 'db_password', 'host': 'db_host'},
        },
        {
            'app': 'cake', 
            'configfile': 'config/database.php',
            'assignments':  [ re.compile(r"'([^']+)' *=> *'([^']+)'"), ],
            'map': { 'db': 'database', 'user': 'login', 'pass': 'password', 'host': 'host'},
        },
        # DB["server"] = 'localhost';
        # DB["user"] = 'propemkx_qafaq';
        # DB["password"] = '!+X61$GI(xq';
        # DB["db"] = 'propemkx_faq';
        # DB["prefix"] = '';
        # DB["type"] = 'mysql';
        {
            'app': 'phpmyfaq', 
            'configfile': 'config/database.php',
            'assignments':  [ re.compile(r'''\$DB\["([a-z]+)"\] *= *'(.*?)';'''), ] ,
            'map': { 'db': 'db', 'user': 'user', 'pass': 'password', 'host': 'server', 'prefix': 'prefix'},
        },
        {
            'app': 'bidbutler', 
            'configfile': 'config/config.php',
            'assignments':  [ re.compile(r"'([^']+)' *=> *'([^']+)'"), ],
            'map': { 'db': 'database', 'user': 'login', 'pass': 'password', 'host': 'host'},
        },
        {
            'app': 'SMF forum', 
            'configfile': 'Settings.php',
            'assignments': [re.compile(r"""\$(db_\S+)\s*=\s*["']?(\S+?)["']?;""")],
            'map': { 'db':'db_name', 'user':'db_user', 'pass':'db_passwd', 'host':'db_server'},
        },
        {
            'app': 'kayako', 
            'configfile': 'config/config.php',
            'assignments': [re.compile(r'''^\$_DB\["([a-z]*)"\] *= *"(.*?)"''')],
            'map': { 'db':'name', 'user':'username', 'pass':'password', 'host':'hostname'},
        },
        {
            'app': 'expressionengine', 
            'configfile': 'siteadmin/expressionengine/config/database.php',
            # $db['expressionengine']['hostname'] = "localhost";
            'assignments': [re.compile(r'''^\$db\[['"a-z]*\]\[['"]([a-z]*)['"]\] *= *['"](.*?)['"]''')],
            'map': { 'db':'database', 'user':'username', 'pass':'password', 'host':'hostname'},
        },
        {
            'app': 'MODx', 
            'configfile': [ 'core/config/config.inc.php', 'manager/includes/config.inc.php', ],
            'assignments': [re.compile(r'''\$([a-z_]+)\s+=\s+'`?(.*?)`?';''')],
            'map': { 'db':'dbase', 'user':'database_user', 'pass':'database_password', 'host':'database_server', 'prefix': 'table_prefix', },
        },
        {
            'app': 'lifetype', 
            'configfile': 'config/config.properties.php',
            # $config['db_host'] = 'localhost';
            'assignments': [re.compile(r"""\[['"]([^'"]+)['"]\s*\]\s*=\s*["']?(\S+?)["']?;""")],
            'map': { 'db':'db_database', 'user':'db_username', 'pass':'db_password', 'host':'db_host'},
        },
        {
            'app': 'Comdev One Admin', 
            'configfile': 'config.php',
            'assignments': [re.compile(r"""\[['"]([^'"]+)['"]\s*\]\s*=\s*["']?(\S+?)["']?;""")],
            'map': { 'db':'used', 'user':'user', 'pass':'pass', 'host':'host'},
        },
        {
            'app': 'vbullettin', 
            'configfile': 'includes/config.php',
            'assignments': [re.compile(r"""\[['"]([^'"]+)['"]\s*\]\s*=\s*["']?(\S+?)["']?;""")],
            'map': { 'db':'dbname', 'user':'username', 'pass':'password', 'host':'servername'},
        },
        {
            'app': 'sugarcrm', 
            'configfile': 'config.php',
            'assignments':  [ re.compile(r"'([^']+)' *=> *'([^']+)'"), ],
            'map': { 'db':'db_name', 'user':'db_user_name', 'pass':'db_password', 'host':'db_host_name'},
        },
        {
            'app': 'interspire email marketer', 
            'configfile': 'admin/includes/config.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db':'SENDSTUDIO_DATABASE_NAME', 'user':'SENDSTUDIO_DATABASE_USER', 'pass':'SENDSTUDIO_DATABASE_PASS', 'host':'SENDSTUDIO_DATABASE_HOST', 'prefix': 'SENDSTUDIO_TABLEPREFIX'},
        },
        {
            'app': 'interspire shopping cart', 
            'configfile': 'config/config.php',
            'assignments': [re.compile(r"""\$GLOBALS\['ISC_CFG'\]\["(\S+)"\] *= *'(\S+)'""")],
            'map': { 'db':'dbDatabase', 'user':'dbUser', 'pass':'dbPass', 'host':'dbServer', 'prefix': 'tablePrefix'},
            #  $GLOBALS['ISC_CFG']["dbUser"] = 'beechwor_jodieg';
            #  $GLOBALS['ISC_CFG']["dbPass"] = 'e2otjodf';
            #  $GLOBALS['ISC_CFG']["dbDatabase"] = 'beechwor_shop2';
            #  $GLOBALS['ISC_CFG']["tablePrefix"] = 'isc_';
        },
        {
            'app': 'oxwall', 
            'configfile': 'ow_includes/config.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db':'OW_DB_NAME', 'user':'OW_DB_USER', 'pass':'OW_DB_PASSWORD', 'host':'OW_DB_HOST', 'prefix': 'OW_DB_PREFIX'},
        },
        {
            #       define('DB_HOST', 'localhost'); // database host
            #       define('DB_USER', 'thewenwt_kenneth'); // username
            #       define('DB_PASS', 'k3nn3th'); // password
            #       define('DB_NAME', 'thewenwt_onlineshop'); // database name
            'app': 'wordpress', 
            'configfile': 'wp-config.php',
            'assignments': [re.compile(r"""^\s*define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db':'DB_NAME', 'user':'DB_USER', 'pass':'DB_PASSWORD', 'host':'DB_HOST'},
            'regexlist':  {
                re.compile(r"\$table_prefix\s*=\s*'(.*?)'") : ['prefix'],
            },
        },
        {
            'app': 'CodeIgniter', 
            'configfile': 'config.php',
            'assignments': [re.compile(r"""engine_config\['db'\]\[\'(.*?)'\]\s*=\s*'(.*?)';""")],
            'map': { 'db':'database', 'user':'username', 'pass':'password', 'host':'hostname'},
        },
        {
            # define('DB_DRIVER', 'mysql');
            # define('DB_HOSTNAME', 'localhost');
            # define('DB_USERNAME', 'phenoelc_cube');
            # define('DB_PASSWORD', 'ant1fr33z3');
            # define('DB_DATABASE', 'phenoelc_cubecart');
            # define('DB_PREFIX', 'phenoelc_');
            'app': 'OpenCart', 
            'configfile': 'config.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db':'DB_DATABASE', 'user':'DB_USERNAME', 'pass':'DB_PASSWORD', 'host':'DB_HOSTNAME', 'prefix': 'DB_PREFIX'},
        },
        {
            # define('_DB_NAME_', 'pcgatlfy_pcg');
            # define('_MYSQL_ENGINE_', 'InnoDB');
            # define('_DB_SERVER_', 'localhost');
            # define('_DB_USER_', 'pcgatlfy_pcg');
            # define('_DB_PREFIX_', 'ps_');
            # define('_DB_PASSWD_', 'nUTh');
            'app': 'prestashop', 
            'configfile': 'config/settings.inc.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db':'_DB_NAME_', 'user':'_DB_USER_', 'pass':'_DB_PASSWD_', 'host':'_DB_SERVER_', 'prefix': '_DB_PREFIX_'},
        },
        {
            #       define('DB_HOST', 'localhost'); // database host
            #       define('DB_USER', 'thewenwt_kenneth'); // username
            #       define('DB_PASS', 'k3nn3th'); // password
            #       define('DB_NAME', 'thewenwt_onlineshop'); // database name
            'app': 'shop-script free', 
            'configfile': 'cfg/connect.inc.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db':'DB_NAME', 'user':'DB_USER', 'pass':'DB_PASS', 'host':'DB_HOST'},
        },
        {
            # define('DB_TYPE', 'mysql');
            # define('DB_HOST', 'localhost');
            # define('DB_USERNAME', 'ssidhvcc_admin');
            # define('DB_PASSWORD', 'pw4admin');
            # define('DB_NAME', 'ssidhvcc_ssinov2010');
            'app': 'bake', 
            'configfile': 'config.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db':'DB_NAME', 'user':'DB_USERNAME', 'pass':'DB_PASSWORD', 'host':'DB_HOST'},
        },
        {
            'app': 'magento', 
            'configfile': 'app/etc/local.xml',
            'assignments': [re.compile(r"""<([a-z]+)><!\[CDATA\[(.*?)\]""")],
            'map': { 'db':'dbname', 'user':'username', 'pass':'password', 'host':'host'},
        },
        {
            'app': 'oscommerce', 
            'configfile': 'includes/configure.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db':'DB_DATABASE', 'user':'DB_SERVER_USERNAME', 'pass':'DB_SERVER_PASSWORD', 'host':'DB_SERVER'},
        },
        {
            'app': 'Ad Manager Pro', 
            'configfile': 'data/data.php',
            'assignments': [ re.compile(r"""^[^#]*\$s\[(db[a-z]+)\s*\]\s*=\s*["'](.*?)['"]""") ],
            'map': { 'db':'dbname', 'user':'dbusername', 'pass':'dbpassword', 'host':'dbhost'},
            # $s[dbhost] = 'localhost';
            # $s[dbusername] = 'calorjrv_adbu';
            # $s[dbpassword] = '?h*VE!0vRR)(';
            # $s[dbname] = 'calorjrv_ads';
        },
        {
            'app': 'postnuke', 
            'configfile': 'config.php',
            'assignments': [ re.compile(r"""^[^#]*\$pnconfig\[['"]([a-z]+)['"]\s*\]\s*=\s*["'](.*?)['"]""") ],
            'map': { 'db': 'dbname', 'user':'dbuname', 'pass':'dbpass', 'host':'dbhost'},
        },
        {
            'app': 'WebCalendar', 
            'configfile': 'includes/settings.php',
            'assignments': [ re.compile(r"""^\s*([a-z_]+):\s*(\S+)""") ],
            'map': { 'db': 'db_database', 'user': 'db_login', 'pass': 'db_password', 'host': 'db_host'},
        },
        {
            'app': 'Gallery2', 
            'configfile': 'config.php',
            # $storeConfig['hostname'] = 'localhost';
            # $storeConfig['database'] = 'mihevnyl_gllr1';
            # $storeConfig['username'] = 'mihevnyl_gllr1';
            # $storeConfig['password'] = 'i3Xp';
            'assignments': [ re.compile(r"""^[^#]*\$[a-zA-Z]*\['\S+?'\]\[['"]([a-z]+)['"]\s*\]\s*=\s*["'](.*?)['"]""") ],
            'map': { 'db': 'database', 'user': 'username', 'pass': 'password', 'host': 'hostname'},
        },
        {
            'app': 'Gallery2.3', 
            'configfile': 'config.php',
            # $storeConfig['hostname'] = 'localhost';
            # $storeConfig['database'] = 'mihevnyl_gllr1';
            # $storeConfig['username'] = 'mihevnyl_gllr1';
            # $storeConfig['password'] = 'i3Xp';
            'assignments': [ re.compile(r"""^[^#]*\$[a-zA-Z]*\[['"]([a-zA-Z]+)['"]\s*\]\s*=\s*["'](.*?)['"]""") ],
            'map': { 'db': 'database', 'user': 'username', 'pass': 'password', 'host': 'hostname', 'prefix': 'tablePrefix' },
            # 
            # 'debug': 1,
        },
        {
            'app': 'WeBid', 
            'configfile': 'includes/config.inc.php',
            # $DbHost    = "localhost";
            # $DbDatabase = "joomlaes_webid";
            # $DbUser  = "joomlaes_webid";
            # $DbPassword = "owjfoi";
            # $DBPrefix   = "webid_";
            'assignments': [ re.compile(r"""\$([A-Za-z]+)\s*=\s*["'](.*?)["']""") ],
            'map': { 'db': 'DbDatabase', 'user': 'DbUser', 'pass': 'DbPassword', 'host': 'DbHost', 'prefix': 'DBPrefix'},
        },
        {
            'app': 'MyBB', 
            'configfile': 'inc/config.php',
            # config['database']['database'] = 'photoqgq_mybb';
            # config['database']['table_prefix'] = 'mybb_';
            # config['database']['hostname'] = 'localhost';
            # config['database']['username'] = 'photoqgq_admin';
            # config['database']['password'] = '+.~%y~pTaAwQ';
            'assignments': [ re.compile(r"""^[^#]\s*config\['database'\]\['([a-z_]+)'\]\s*=\s*'(.*?)'""") ],
            'map': { 'db': 'database', 'user': 'username', 'pass': 'password', 'host': 'hostname', 'prefix': 'table_prefix'},
        },
        {
            'app': 'coppermine photo gallery', 
            'configfile': 'include/config.php',
            'assignments': [ re.compile(r"""^[^#]*\$[a-zA-Z]*\[['"]([a-z]+)['"]\s*\]\s*=\s*["'](.*?)['"]""") ],
            'map': { 'db': 'dbname', 'user': 'dbuser', 'pass': 'dbpass', 'host': 'dbhost'},
        },
        {
            'app': 'coppermine photo gallery 1.x', 
            'configfile': 'include/config.inc.php',
            'assignments': [ re.compile(r"""^[^#]*\$CONFIG\[['"]([_a-zA-Z]+)['"]\s*\]\s*=\s*["'](.*?)['"]""") ],
            'map': { 'db': 'dbname',
                   'user': 'dbuser',
                   'pass': 'dbpass',
                   'host': 'dbserver',
                 'prefix': 'TABLE_PREFIX'},
            #'debug': True,
        },
        {
            'app': 'statusnet', 
            'configfile': 'config.php',
            'regexlist':  {
                re.compile(r"\$config\['db'\]\['database'\] = 'mysql://(.+?):(.+?)@(.+?)/(\S+)'") : ['user','pass','host','db'],
            },
            'assignments': [ re.compile(r""" (\S+) = '(\S+)'""") ],
            'map': { 'db': 'db',
                   'user': 'user',
                   'pass': 'pass',
                   'host': 'host', },
        },
        {
            'app': 'AlegroCart', 
            'configfile': 'config.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db': 'DB_NAME',
                   'user': 'DB_USER',
                   'pass': 'DB_PASSWORD',
                   'host': 'DB_HOST', },
        },
        # $SITE_INFO['table_prefix']='rzb_';
        # $SITE_INFO['db_site']='bluejzwy_ocpo822';
        # $SITE_INFO['db_site_host']='localhost';
        # $SITE_INFO['db_site_user']='bluejzwy_ocpo822';
        # $SITE_INFO['db_site_password']='v9Phbk1S84';
        {
            'app': 'ocportal', 
            'configfile': 'info.php',
            'assignments': [ re.compile(r"""^[^#]*\$SITE_INFO\[['"]([_a-zA-Z]+)['"]\s*\]\s*=\s*["'](.*?)['"]""") ],
            'map': { 'db': 'db_site',
                   'user': 'db_site_user',
                   'pass': 'db_site_password',
                   'host': 'db_site_host', 
                    'prefix': 'table_prefix', },
        },

        # $SITE_INFO['db_forums']='bluejzwy_ocpo822';
        # $SITE_INFO['db_forums_host']='localhost';
        # $SITE_INFO['db_forums_user']='bluejzwy_ocpo822';
        # $SITE_INFO['db_forums_password']='v9Phbk1S84';
        # $SITE_INFO['ocf_table_prefix']='rzb_';
        {
            'app': 'xoops', 
            'configfile': 'mainfile.php',
            'assignments': [re.compile(r"""define\s*\(\s*['"](.*?)['"]\s*,\s*['"](.*?)['"]\s*\)""")],
            'map': { 'db': 'XOOPS_DB_NAME', 'user': 'XOOPS_DB_USER', 'pass': 'XOOPS_DB_PASS', 'host': 'XOOPS_DB_HOST', 'prefix': 'XOOPS_DB_PREFIX', },
        },

    ]
    for application in applications:
        if application.get('debug'):
            print("Debug for",application['app'])
            verbose=True
        configfilenames = application['configfile']
        if type(configfilenames)==str: configfilenames=[configfilenames,]
        for configfilename in configfilenames:
            res={}
            res['prefix']=''
            if not os.path.exists(configfilename):
                continue
            if verbose:
                print("%(app)s: %(configfile)s" % application)
            configfile = open(configfilename,'r')
            variables={}
            if application.get('debug'):
                print(configfilename)
            for line in configfile:
                if 'assignments' in application:
                    for regex in application['assignments']:
                        found_it = regex.search(line)
                        if not found_it: continue
                        if found_it.group(1) not in variables:
                            variables[found_it.group(1)]=found_it.group(2)
                        if application.get('debug'):
                            print(variables)
                        # sys.stderr.write( "Found %s=%s in %s\n" % (found_it.group(1),found_it.group(2),configfilename))
                        # sys.stderr.flush()
                        # if hyperverbose: print found_it.group(1), found_it.group(2)
                if 'regexlist' in application:
                    for regex, variablenames in list(application['regexlist'].items()):
                        matches = regex.search(line)
                        if not matches: continue
                        if application.get("Debug"):
                            print("Matched line",line)
                        i=0
                        for variablename in variablenames:
                            i+=1
                            if res.get(variablename):
                                continue
                            # print "XXXXXXXXX", i,variablename, matches.group(i)
                            res[variablename] = matches.group(i);
                            variables[variablename] = matches.group(i);
            res['file']= configfilename
            if verbose: print(res)
            res['app']= application['app']
            for n in ('host','db','pass','user','prefix'):
                res[n]=variables.get(n,'')
            pass_postprocess = application.get('pass_postprocess')
            for (dest,varname) in list(application['map'].items()):
                if varname not in variables and dest!='prefix':
                    #if verbose:
                    #    print variables
                    #    print "File %s [%s] not %s, key {%s} not found" % (configfilename, dest, application['app'], varname)
                    if verbose: print("Did not find key %s" % varname)
                    res={}
                    break;
                # print dest,'[',varname,'] = ',variables[varname]
                res[dest]=variables.get(varname,'')
            if pass_postprocess and 'pass' in res:
                try:
                    res['pass'] = pass_postprocess(res['pass']);
                except:
                    continue
            break
        failed=False
        for n in ('host','db','pass','user'):
            if n not in res or not res[n]:
                failed=True
        if not failed:
            return res

    return None

def john_dump():
    u='*';
    m=re.search('/home/(.*?)/',os.getcwd())
    if m: u=m.group(1)
    shadow_file=glob.glob("/home/"+u+"/etc/*/shadow")
    for f in shadow_file:
        domain=f.split("/")[-2]
        fd=open(f,'r')
        for line in fd:
            bits=line.split(":")
            bits[0] += '@'+domain
            sys.stdout.write(":".join(bits))
        fd.close()
    pass

def panic():
    os.system("""
    echo '# Values before frob'
    sysctl -a | grep panic

    for SYSCTL in vm.panic_on_oom \
        kernel.softlockup_panic \
            kernel.panic_on_unrecovered_nmi \
            kernel.unknown_nmi_panic \
            kernel.panic_on_oops \
            kernel.panic ; do
        grep -q "^$SYSCTL =" /etc/sysctl.conf || echo $SYSCTL = 1 >> /etc/sysctl.conf
    done
    echo '# Setting sysctls'
    sysctl -p

    echo '# Values after frob'
    sysctl -a | grep panic
    """)

    # kernel.modules_disabled was here ... it should probably be used in the firewall setup ..

def droppriv(target=".", quiet=False):
    "Drop root privileges"
    import posix,pwd
    stat = os.stat(target)
    uid = stat.st_uid
    if os.getuid==uid:
        return # job's done already
    # gid = stat.st_gid
    gid = pwd.getpwuid(uid).pw_gid
    try:
        posix.setgroups([])
        posix.setgid(gid)
        posix.setuid(uid)
        posix.seteuid(uid)
    except:
        if not quiet:
            sys.stderr.write("We are already unprivileged :)\n")
    if uid==0:
        raise Exception.create("Oops - we dropped privilege to root: that's probably wrong");
    os.environ['HOME'] = pwd.getpwuid(uid).pw_dir
    os.environ['PS1'] = '\\u@\\h [\\w]\\$ '
    return os.environ['HOME']

def list_myips():
    if_re = re.compile(r'^([^ :]+)');
    inet_re = re.compile(r'inet addr:(\d\S+)')
    ifconfig=os.popen("ifconfig",'r')
    d={'eth': 'eth0'}
    for line in ifconfig:
        m = if_re.search(line)
        if m: d['eth']=m.group(1)
        m = inet_re.search(line)
        if m:
            d['ip']=m.group(1)
            yield d

def arping():
    print("# Sending arping -U for each configured IP address shown by ifconfig:")
    for d in list_myips():
        if d['ip'].startswith('127.') or d['eth']=='lo': continue
        cmd = "arping -U -I %(eth)s -c 2 %(ip)s" % d
        print(cmd)
        os.system(cmd)

# root@foreverresortssouthafrica [~]# ifconfig | grep -o 'addr:[^ ]\+' | cut -f 2 -d : | while read x; do arping -U $x -I eth1 -c 3; done
def mv(src,dst):
    params = dict(src=src,dst=dst,nice='ionice -c 3 nice -n 19')
    if not os.path.exists(src):
        print("# ERROR: source does not exist")
        return
    if os.path.exists(dst):
        print("# ERROR: destination already exists")
        return
    if src==dst:
        print("# ERROR: src==dst: crazy")
        return
    os.system("""
        [ -d '%(src)s' ] || {
            echo "# ERROR: %(src)s is not a directory"
            exit
        }
        echo "# INFO: Making dest dir %(dst)s"
        mkdir -p %(dst)s
        echo "# INFO: Doing nice initial copy to %(dst)s"
        %(nice)s cp -au %(src)s/. %(dst)s/.
        echo "# INFO: Doing nice rsync --delete to %(dst)s"
        %(nice)s rsync -a --delete %(src)s/. %(dst)s/.
        echo "# INFO: Renaming source, and making link to dest"
        mv %(src)s %(src)s.orig 
        ln -s %(dst)s %(src)s
        chown -h --reference=%(dst)s %(src)s
        echo "# INFO: Sleeping ..."
        sleep 15
        echo "# INFO: Doing additional rsync without nice or --delete (possible duplicate mails)"
        rsync -a %(src)s.orig/. %(dst)s/.
        echo "# TODO: Check that everything is working, and remove the source like this:" 
        echo "%(nice)s rm -r '%(src)s.orig'"
    """ % params)

def do_dbaccess(operation,args):
    res=dbaccess()
    shellargs=''
    for arg in args: shellargs+= " '" + (arg.replace("'",r"'\''")) + "'"
    if not res:
        print("Unknown application")
        return 
    if operation=='mysql':
        os.system(res['mysql']+shellargs)
    elif operation=='mysqldump':
        os.system(res['mysqldump']+shellargs)
    elif operation=='grant':
        fd=os.popen("mysql",'w')
        fd.write(res['grant'])
        fd.close()
        
def do_dbpass(operation,args):
    res=dbaccess()
    res['newdbpassword']=randompassword(12)
    print("Rewriting db password from %(pass)s to %(newdbpassword)s" % res)

    # Change database password
    fd = os.popen(res['mysql'],'w')
    fd.write('SET PASSWORD=PASSWORD("%(newdbpassword)s");' % res)
    fd.close()

    # Rewrite the config file
    fd=open(res['file'],'r')
    configfile=fd.read()
    fd.close()
    configfile=configfile.replace(res['pass'],res['newdbpassword'])
    fd=open(res['file'],'w')
    fd.write(configfile)
    fd.close()
        
def do_nocomment():
    # Remove evil components
    res=dbaccess()
    cmd='';

    if res['app'].startswith('joomla'):
        # cmd = """%(mysql)s -e 'update %(prefix)scomponents set enabled=0 where `option` in ("com_fabrik","com_remository");' """ % res
        cmd = """
            echo '
              truncate table %(prefix)srem_review;                   # delete all comments
              alter table %(prefix)srem_review modify id  int(11);   # change from autoincrement to int
              insert into %(prefix)srem_review (id) values (0);      # add a bogus non-comment comment

              truncate table %(prefix)sk2_comments;                   # delete all comments
              alter table %(prefix)sk2_comments modify id  int(11);   # change from autoincrement to int
              insert into %(prefix)sk2_comments (id) values (0);      # add a bogus non-comment comment

              drop table %(prefix)skunena_messages_text;              # delete all comments and break the application: it cannot even index its primary key

              truncate table %(prefix)sudjacomments;                   # delete all comments
              alter table %(prefix)sudjacomments modify id  int(11);   # change from autoincrement to int
              insert into %(prefix)sudjacomments (id) values (0);      # add a bogus non-comment comment

              truncate table %(prefix)skunena_messages;                   # delete all comments
              alter table %(prefix)skunena_messages modify id  int(11);   # change from autoincrement to int
              insert into %(prefix)skunena_messages (id) values (0);      # add a bogus non-comment comment

            ' | %(mysql)s -f 
            """ % res
    if res['app'].startswith('MODx'):
        # cmd = """%(mysql)s -e 'update %(prefix)scomponents set enabled=0 where `option` in ("com_fabrik","com_remository");' """ % res
        cmd = """
            echo '
              truncate table %(prefix)sjot_content ;
              alter table %(prefix)sjot_content  change column id `id` int(10) NOT NULL;
              insert into %(prefix)sjot_content (id) values (0);      # add a bogus non-comment comment
            
            ' | %(mysql)s -f 
            """ % res
    if cmd:
        print(cmd)
        os.system(cmd);
        print("# Done")
    else:
        print("Eish, nocomment not available for this application")

def php_version():
    # Returns 0x050317 for php 5.3.17
    fd = os.popen('php -v','r')
    for line in fd:
        m = re.search(r'(\d+)\.(\d+)\.(\d+)', line)
        if m:
            return int(m.group(1)) * 0x10000 + \
                   int(m.group(2)) * 0x00100 + \
                   int(m.group(3)) * 0x00001 ;
        m = re.search(r'(\d+)\.(\d+)', line)
        if m:
            return int(m.group(1)) * 0x10000 + \
                   int(m.group(2)) * 0x00100 ;
    return 0

def do_autocleanup(parameters):
    os.system(r"""
        pwd | grep -q public_html || exit 1
        /usr/local/afribin/maintenance/linkfarmscan.py -N /dev/null --limit 999999 . -n php -l | /usr/local/afribin/quarantine
    """)

def do_jce():
    "Fix com_jce hack with do_secure and clean up common hacks"
    do_secure()
    os.system(r"""
    cd $( echo $PWD | sed 's/public_html.*/public_html/' )
find ../.htaccess . -name .htaccess -print0| xargs -0 perl -p -i -e 'if (m/^\s+$|^\s{10}/) { $_=""; }'
find images -name \*.php\* -print0|xargs -0 %(frob)s disable
find . -type l -print0|xargs -0 rm
find . -name \*.old.php -print0|xargs -0 quarantine
find . -name x.txt -print0| xargs -0 quarantine
for FILENAME in \
    components/com_user/views/reset/mod.php \
    components/com_banners/models/mod.php \
    components/com_remository_files \
    media/sok.php \
    media/mod.php \
    _vti_pvt/service.pwd \
    modules/mod.php \
    images/stories/pageinfo.php \
    images/stories/error_log \
    images/stories/ll.php \
    images/stories/semi.php \
    images/stories/kabe.php ; do
    [ -f "$FILENAME" ] || continue
    quarantine "$FILENAME"
done
    """ % dict(frob=frob()));

class Slurp:
    def __init__(self):
        self.data=[]
    def write(self,line):
        self.data.append(line)
    def __repr__(self):
        return ''.join(self.data);

def do_john(res, no_act=False, verbose=0,guesses=[]):
    path = os.path.dirname(frob())+'/andrew-scripts';
    if not os.path.exists(path): path='/usr/local/afribin/andrew-scripts';
    sys.path.append(path);
    import john
    slurp=Slurp()
    john.guesses.extend(guesses)
    john.find_vulnerable_installations(user=res['db'], stdout=slurp, res=res, verbose=verbose);
    for line in slurp.data:
        if verbose: sys.stdout.write(line)
        if not line.startswith("#"): continue
        print("Vulnerable password: "+line.strip())
    if not no_act:
        fd=os.popen('/bin/bash -v','w')
        fd.writelines(slurp.data);
        fd.close()
            

def check_jce(no_act=False):
    # Joomla: Remove jce plugin
    if not os.path.exists('plugins/editors/jce.xml'):
        return
    xml = open('plugins/editors/jce.xml','r').read()
    version=re.search(r'<version>(.*)</version>',xml)
    if not version: return
    version=version.group(1)
    if version > '2.2.':
        return
    if no_act:
        print("We found a vulnerable installation of JCE (joomla content editor), version %s" % version)
        return

    print("Removing jce joomla content editor version %s" % version)
    os.system("rm -r plugins/editors/jce{,.xml,.php} administrator/components/com_jce/jce.xml");
    print("Installing files of another random version of jce")
    os.system("curl http://answers.afrihost.com/updates/jce-files.tar.gz | %s su -- tar -zx ." % frob()); # droppriv after fork 

def do_secure(res=None, no_act=False, quick=False):
    # Remove evil components
    if not res:
        res=dbaccess(show=False, unknown_application='an unknown application')
    if no_act:
        print("Checking application security for %(app)s %(version)s:" % res)
    else:
        print("Setting up application security for %(app)s %(version)s:" % res)
    if res['app'].startswith('joomla'):
        # Joomla: Evil / stupid components
        res['sql']='select distinct `option` from %(prefix)scomponents where enabled=1;' % res
        res['sql']=res['sql'].replace("'","'\\''")
        cmd = """%(mysql)s -Ne '%(sql)s' 2>/dev/null """ % res
        p = os.popen(cmd,'r')
        for bad_component in p:

            res['component']=bad_component.strip()
            if res['component'] not in ('com_fabrik','com_remository',): continue
            print("Vulnerable %(app)s component %(component)s found" % res)
            if no_act: continue

            print("Disabling %(app)s component %(component)s by setting 'enabled' to false" % res)
            res['sql']='update %(prefix)scomponents set enabled=0 where `option` = "%(component)s";' % res
            res['sql']=res['sql'].replace("'","'\\''")
            cmd = """%(mysql)s -Ne '%(sql)s' """ % res
            print(cmd)
            os.system(cmd);

        if os.path.exists('components/com_smartformer/smartformer.php'):
            code = open('components/com_smartformer/smartformer.php','r').read()
            m = re.search(r'@version (\d\S+)',code)
            if m and m.group(1) < '2.4.1':
                print("Upgrading com_smartformer version "+m.group(1))
                upgrade = r'curl http://answers.afrihost.com/updates/com_smartformer-files.tar.gz | %s su -- tar -xz' % sys.argv[0];
                os.system(upgrade);

        check_jce(no_act)

    # Protect image directories
    writeable_dirs = [ 'images','uploads','media','wp-content/uploads',
        'upload','cache','tmp','logs','wp-content/flagallery',
        'components/com_smartformer/files',
        'sobi2_downloads','phocadownload',
        'components/com_jnews/includes/openflashchart/tmp-upload-images',
         ]
    for imagedir in writeable_dirs:
        if os.path.exists(imagedir):
            do_images(imagedir,no_act)

    # Check for guessable passwords
    if 'db' in res:
        do_john(res, no_act=no_act)

    # Find 
    # TODO: ./wp-content/themes/*/framework/_scripts/valums_uploader/php.php
    p=os.popen(r'find . -iname \*thumb\*.php -o -iname \*upload\*.php -o -iname \*process-form.php','r')
    for line in p:
        fn=line.strip('\n');
        if not os.path.exists(fn): continue
        if not os.path.isfile(fn): continue
        basename=line.split('/')[-1].lower()
        files=[fn, ]
        if basename.find('thumb')>=0:
            do_patch_timthumb(files ,no_act=no_act);
        elif basename.find('upload')>=0 or basename.find('form')>=0:
            do_patch_upload(files,no_act=no_act);

    # done
    if not no_act and res['app'].startswith('wordpress') and php_version() >= 0x050300:
        print("%(app)s: setting new secrets" % res)
        refresh_wordpress_keys('wp-config.php');
        do_wp(None) # Install
        print("%(app)s: Installing cookies for comments and force-strong-password" % res)
        cmd = """
                echo "Deleting inactive plugins ..."
                $HOME/bin/wp plugin delete `$HOME/bin/wp plugin list | awk '($2 == "inactive"){print $1}'  `
                echo "Updating plugins ..."
                $HOME/bin/wp plugin update --all
                echo "Updating themes ..."
                $HOME/bin/wp theme update --all
                echo "Installing cookies-for-comments plugin"
                $HOME/bin/wp plugin install cookies-for-comments --activate
                echo "Installing force-strong-passwords plugin"
                $HOME/bin/wp plugin install force-strong-passwords --activate
            """
        s = cmd.replace(r"'",r"'\''")
        droppriv(quiet=True)
        os.system(cmd)

def dbaccess(show=True,unknown_application=None):
    try:
        if unknown_application: unknown_application={'app': unknown_application, 'version':''}
        res = None
        cmsconfigs=[ 
            dict(getconfigfunc=dbaccess_joomla,    files=["configuration.php"], ), 
            dict(getconfigfunc=dbaccess_moodle,    files=["config.php"], ), 
            dict(getconfigfunc=dbaccess_phpbb,     files=["config.php"], ), 
            #dict(getconfigfunc=dbaccess_wordpress, files=["wp-config.php"], ), 
            dict(getconfigfunc=dbaccess_openx,     files=glob.glob('var/*.conf.php'), ),
            dict(getconfigfunc=dbaccess_other,     files=[""], ), 
            dict(getconfigfunc=dbaccess_guess,     files=["index.php"], ), 
        ]
        res=None
        for cmsconfig in cmsconfigs:
            for filename in cmsconfig['files']:
                if filename and not os.path.exists(filename):
                    continue
                res = cmsconfig['getconfigfunc'](filename)
                if res:
                    break
            if res:
                break
        if res:
            res.setdefault('prefix','')
            if 'version' not in res:
                res['version'] = version_of_application(res.get('app','None')).replace(",","\\,")
            try:
                res['mysql'] = "mysql     --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s" % res
                res['mysqldump'] = "mysqldump --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s --skip-ext --skip-lock-tables" % res
                res['grant'] = "GRANT ALL on `%(db)s`.* to '%(user)s'@'localhost' IDENTIFIED BY '%(pass)s';" % res
                if show and sys.stderr.isatty() and sys.stdout.isatty() and sys.stdin.isatty():
                    sys.stderr.write( (
                        "# application=%(app)s version=%(version)s configfile=%(file)s prefix=%(prefix)s\n"
                        "mysql     --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s\n"
                        "mysqldump --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s --skip-ext --skip-lock-tables|less\n"
                        "# GRANT ALL on `%(db)s`.* to '%(user)s'@'localhost' IDENTIFIED BY '%(pass)s';\n"
                        ) % res 
                    )
            except KeyError:
                if show:
                    print("Application unknown. Please report if you know what it is.")
                if 'prefix' in res: res.pop('prefix');
                if 'version' in res: res.pop('version');
                if len(res):
                    if show:
                        print(res.__repr__()[:80])
                else:
                    res['user']= pwd.getpwuid(os.stat(".").st_uid).pw_name
                    if show:
                        print("# egrep -1 \"%(user)s_[0-9A-Za-z]*\" {.,inc*,conf*}/*.php" % res)
                        os.system("bash -c 'egrep -1 --color=ALWAYS \"%(user)s_[0-9A-Za-z]*\" {.,inc*,conf*}/*.php 2>/dev/null | head -n 20'" % res)
                #raise
            pass
            if res==None: unknown_application;
            return res
        res=unknown_application
    except AttributeError:
        return None
    return res

def version_of_anything(filenames,regex, group=1, debug=0):
    if type(regex)==str: regex =re.compile(regex)
    if type(filenames)==str: filenames=[filenames,]
    for fn in filenames:
        if debug: print(fn)
        if not os.path.exists(fn): continue
        if debug: print(fn)
        fd = open(fn,'r')
        for line in fd:
            m = regex.search(line)
            if m:
                if debug: print(line) 
                if debug: print('---'.join(m.groups()))
                return m.group(group)
    return ''

def version_of_phplist():
    return version_of_anything("admin/connect.php",r'define\("VERSION","(\S+)"\);')

def version_of_application(app):
    if app=='joomla':
        return version_of_joomla()
    if app=='joomla-2.x-series':
        return version_of_anything("administrator/manifests/files/joomla.xml",r'<version>(.*?)</version>')
    if app=='joomla-1.ancient':
        return version_of_anything("includes/version.php",r"var \$RELEASE\s*=\s*'(.*)'")
    if app=='vtiger':
        return version_of_anything("vtigerversion.php",r"vtiger_current_version\s*=\s*'(.*?)'");
    if app=='phplist':
        return version_of_anything("admin/connect.php",r'define\("VERSION","(\S+)"\);')
    if app=='wordpress':
        return version_of_anything("wp-includes/version.php",r'''\$wp_version\s*=\s*["'](.*?)["']''');
    if app=='oxwall':
        return version_of_anything("ow_version.xml",r'''<version>(.*?)</version>''');
    if app=='OpenCart':
        return version_of_anything("index.php",r'''define\('VERSION', '(.*?)'\);''');
    if app=='MODx':
        if os.path.exists('core/docs/changelog.txt'):
            return version_of_anything("core/docs/changelog.txt",r"MODx Revolution\s+(.*?\(.*,.*\))");
        if os.path.exists('changelog.txt'):
            return version_of_anything("changelog.txt",r"\d+/\d+/\d+: Version\s+([^\r\n]*)");
    if app=='oscommerce':
        return version_of_anything("includes/application_top.php", r"define\('PROJECT_VERSION', *'(.*?)'\)")
    if app=='cubecart':
        return version_of_anything("ini.inc.php",r"""\$ini\['ver'\]\s*=\s*'(.*?)'""")
    if app=='elgg':
        return version_of_anything("CHANGES.txt",r"""Version (\d.*)""")
    if app=='Comdev One Admin':
        return version_of_anything("readme.txt",r"""Comdev One Admin (\d+\..*)""")
    if app=='drupal':
        v=version_of_anything("modules/system/system.module",r"""define\('VERSION', *'(.*?)'\)""")
        if not v: v=version_of_anything('./includes/bootstrap.inc',r"""define\('VERSION', *'(.*?)'\)""")
        return v
    if app=='e107':
        # e107_admin/ver.php:$e107info['e107_version'] = "0.7.25";
        return version_of_anything("e107_admin/ver.php",r'''\['e107_version'\]\s*=\s*"(.*?)"''', group=1, debug=0);
    if app=='WebCalendar':
        return version_of_anything("includes/config.php",r'''PROGRAM_VERSION\s*=\s*['"]v?(.+?)["']''', group=1, debug=0);
    if app=='hesk helpdesk':
        return version_of_anything("hesk_settings.inc.php",r"""hesk_settings\['hesk_version'\]='(.*?)';""")
    if app=='magento':
        return version_of_anything("RELEASE_NOTES.txt",r"""==== ([^=]*?) ====""")
    if app=='coppermine photo gallery 1.x':
        return version_of_anything("admin.php",r"""Coppermine version:\s+(\d+\S*)""")
    if app=='MyBB':
        return version_of_anything("calendar.php",r""" \* MyBB\s+(\d+\.\S*)""")
    if app=='phpmyfaq':
        return version_of_anything("docs/CHANGELOG",r"""phpMyFAQ (\d+\.[0-9.]+)""")
    if app=='interspire shopping cart':
        return version_of_anything("lib/init.php",r"""define\('PRODUCT_VERSION', *'(.*?)'\)""")
    if app=='xoops':
        return version_of_anything("include/version.php",r"""define\('XOOPS_VERSION', *'[XOOPS ]*(.*?)'\)""")
    if app=='Gallery2.3':
        return version_of_anything("./modules/core/module.inc", r"""this->setGalleryVersion\('(.*?)'\);""");
    return ''

def version_of_joomla():
    changelogfile="CHANGELOG.php"
    if not os.path.exists(changelogfile): return 'unknown';
    cl = open(changelogfile,'r')
    version_re=re.compile(r"------ (\d+\.\d+\.\d+[^\[-]*) ")
    for line in cl:
        m = version_re.search(line)
        if m:
            return m.group(1)
    return 'unknown'

def nocommentspam():
    # FIXME  show create table x; drop table x; create table x s/AUTOINCREMENT//
    jos_joomgallery_comments = """
drop table jos_joomgallery_comments;
CREATE TABLE `jos_joomgallery_comments` (
  `cmtid` int(11) NOT NULL,
  `cmtpic` int(11) NOT NULL default '0',
  `cmtip` varchar(15) NOT NULL default '',
  `userid` int(11) unsigned NOT NULL default '0',
  `cmtname` varchar(50) NOT NULL default '',
  `cmttext` text NOT NULL,
  `cmtdate` varchar(20) default NULL,
  `published` tinyint(1) NOT NULL default '0',
  `approved` tinyint(1) NOT NULL default '1',
  PRIMARY KEY  (`cmtid`),
  KEY `idx_cmtpic` (`cmtpic`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
    """

def show_autologin(content,prefix='',show=True,url=None,base_url=None):
    filename=prefix+'autologin-%s.php' % easypassword();
    fd=open(filename,'w')
    fd.write(content);
    fd.close()

    if base_url:
        if not base_url.endswith('/'): base_url+='/';
        url=base_url+filename
    if not url:
        url = os.popen('url "%(filename)s"' % dict(filename=filename),'r').read().strip()
    meh = dict(filename=filename,url=url);
    os.system(r'''
        chmod 644 %(filename)s
        chown --reference=index.php %(filename)s''' % meh )
    if show:
        os.system(r'''
            echo Autologin URL is:
            echo
            echo %(url)s
            echo
            read -t 120 -p "Press Enter to remove %(filename)s ..." || echo
            \rm -f %(filename)s
        ''' % meh)
    return url;

def autologin_joomla():
    autologin_php = """<?php $stat=stat(__FILE__);unlink(__FILE__);if(abs(time()-$stat[9])>1200) { die('Expired'); }
setcookie("RFC3514","1",time()+604800,'/');
// Set flag that this is a parent file
define( '_JEXEC', 1 );
define('JPATH_BASE', dirname(__FILE__) );
define('DS', DIRECTORY_SEPARATOR);
require_once( JPATH_BASE .DS.'includes'.DS.'defines.php' );
require_once( JPATH_BASE .DS.'includes'.DS.'framework.php' );
require_once( JPATH_BASE .DS.'includes'.DS.'helper.php' );
require_once( JPATH_BASE .DS.'includes'.DS.'toolbar.php' );
$mainframe =& JFactory::getApplication('administrator');
$mainframe->initialise(array( 'language' => $mainframe->getUserState( "application.lang", 'lang' )));
JPluginHelper::importPlugin('system');
// trigger the onAfterInitialise events
$mainframe->triggerEvent('onAfterInitialise');
########## BEGIN BACKDOOR #########
$db =& JFactory::getDBO();
$query = 'SELECT username,email from #__users WHERE usertype like "Super administrator%" LIMIT 1';
$db->setQuery($query);
$rows = $db->loadObjectList();
foreach ($rows as $row) {
   $credentials=array('username'=> $row->username, 'password'=> rand());
   break;
}
require_once( JPATH_BASE .DS.'..'.DS.'libraries'.DS.'joomla'.DS.'user'.DS.'authentication.php' );
#jimport( 'joomla.user.authentication');
$response = new JAuthenticationResponse();
$response->type='backdoor';
$response->username=$credentials['username'];
$response->fullname=$credentials['username'];
$response->password=$credentials['password'];
#$session = &JFactory::getSession();
#$session->fork();
#$mainframe->_createSession($session->getId());
JPluginHelper::importPlugin('user');
#$results = $mainframe->triggerEvent('onLoginUser', array((array)$response, array()));
$results = $mainframe->triggerEvent('onLoginUser', array($credentials, array()));
########## END BACKDOOR #########

$mainframe->route();
$mainframe->triggerEvent('onAfterRoute');
$option = JAdministratorHelper::findOption();
$mainframe->dispatch($option);
$mainframe->triggerEvent('onAfterDispatch');
$mainframe->render();
$mainframe->triggerEvent( 'onAfterRender' );
echo JResponse::toString($mainframe->getCfg('gzip'));
"""
    show_autologin(autologin_php,'administrator/')

def autologin_joomla16(show=True,timeout=1200):
    autologin_php = """<?php $stat=stat(__FILE__);unlink(__FILE__);if(abs(time()-$stat[9])>"""+str(timeout)+""") { die('Expired'); }
setcookie("RFC3514","1",time()+604800,'/');
// Set flag that this is a parent file
define( '_JEXEC', 1 );
define('JPATH_BASE', dirname(__FILE__) );
define('DS', DIRECTORY_SEPARATOR);
require_once( JPATH_BASE .DS.'includes'.DS.'defines.php' );
require_once( JPATH_BASE .DS.'includes'.DS.'framework.php' );
require_once( JPATH_BASE .DS.'includes'.DS.'helper.php' );
require_once( JPATH_BASE .DS.'includes'.DS.'toolbar.php' );
$app =& JFactory::getApplication('administrator');
$app->initialise(array( 'language' => $app->getUserState('application.lang')));
JPluginHelper::importPlugin('system');
$app->triggerEvent('onAfterRoute');
$app->triggerEvent('onAfterDispatch');
// trigger the onAfterInitialise events
$app->triggerEvent('onAfterInitialise');
########## BEGIN BACKDOOR #########
$db =& JFactory::getDBO();
#$query = 'SELECT username,email from #__users WHERE usertype REGEXP "Super administrator|Super User" LIMIT 1';
$query = 'SELECT username,email from #__users ORDER by id LIMIT 1';
$db->setQuery($query);
$rows = $db->loadObjectList();
foreach ($rows as $row) {
   $credentials=array('username'=> $row->username, 'password'=> rand());
   break;
}
require_once( JPATH_BASE .DS.'..'.DS.'libraries'.DS.'joomla'.DS.'user'.DS.'authentication.php' );
#jimport( 'joomla.user.authentication');
$response = new JAuthenticationResponse();
$response->type='backdoor';
$response->username=$credentials['username'];
$response->fullname=$credentials['username'];
$response->password=$credentials['password'];
# JAUTHENTICATE_STATUS_SUCCESS is defined by joomla 1.6
if (defined('JAUTHENTICATE_STATUS_SUCCESS')) $response->status=JAUTHENTICATE_STATUS_SUCCESS; else $response->status=JAuthentication::STATUS_SUCCESS;
JPluginHelper::importPlugin('user');
$results = $app->triggerEvent('onUserLogin', array($credentials, array()));
########## END BACKDOOR #########

# Render something 
$app->route();
$app->triggerEvent('onAfterRoute');
$app->dispatch();
$app->triggerEvent('onAfterDispatch');
$app->render();
$app->triggerEvent( 'onAfterRender' );
echo $app;
"""
    return show_autologin(autologin_php,'administrator/',show=show)

def do_login(show=True):
    "Get login link for current application (e.g. wordpress)"
    res=dbaccess(show=False, unknown_application='Unknown application')
    sys.stderr.write("autologin %(app)s %(version)s...\n" % res);
    if res['app']=='joomla':
        return autologin_joomla()
    elif res['app']=='joomla-2.x-series':
        return autologin_joomla16(show=show)
    elif res['app']=='wordpress':
        return autologin_wordpress()
    else:
        print("Not implemented")

def do_software_list(dirlist):
    if len(dirlist)==0:
        dirlist = glob.glob('/home/*/public_html')
        dirlist.sort()
    cwd = os.getcwd()
    for directory in dirlist:
        if not os.path.isdir(directory): continue
        os.chdir(cwd)
        os.chdir(directory)
        res=dbaccess(show=False)
        if not res or not res['app'] or res['app']=='some application': continue
        res['directory'] = directory
        dirname=sys.argv[0]
        dirname=dirname[ :dirname.rfind('/')+1 ]
        if dirname.startswith('.'): dirname=cwd+'/'+dirname
        res['url']=os.popen(dirname+'url','r').read().strip();
        res['pad']=' '* (32 - len(res['app']) - len(res['version']) )
        print("%(app)s %(version)s %(pad)s%(directory)-30s %(url)s" % res)
        sys.stdout.flush();

def wordpress_option(option,default=''):
    if os.getuid()==0:
        droppriv()
    php=r'''<?php
     ini_set('show_errors',0);
     require('wp-load.php');
     echo get_option('%(option)s','%(default)s');
    ''' % { 'option': option, 'default': default, }
    import tempfile
    tmp=tempfile.NamedTemporaryFile('w')
    tmp.write(php)
    tmp.flush()
    fd=os.popen('php < '+tmp.name,'r')
    rc=fd.read();
    fd.close()
    tmp.close()
    if rc.find('\n')>=0:
        return default; # probably some error 
    return rc.strip()
    
def autologin_wordpress():
    autologin_php=r"""<?php
$f=__FILE__; $stat=stat($f); unlink($f);
setcookie("RFC3514","1",time()+604800,'/');
if(abs(time()-$stat[9])<120){
  require(dirname($f).'/wp-load.php');
  if (!$u=$wpdb->get_row("SELECT users.* FROM $wpdb->users users, $wpdb->usermeta users_meta where users.id=users_meta.user_id and users_meta.meta_key like '%capabilities' and users_meta.meta_value like '%administrator%' ORDER BY ID limit 1")) {
    $u=$wpdb->get_row("SELECT * FROM $wpdb->users ORDER BY ID limit 1");
  }
  $user=$u->user_login;
  wp_setcookie($user,rand(),false,"","","");
  do_action('wp_login',$user);
  wp_redirect('wp-admin/'); }
"""
    show_autologin(autologin_php,base_url=wordpress_option('siteurl'));


def do_wp(parameters):
    home = droppriv(quiet=True)
    res=dbaccess(show=False)
    if not res or res['app'] != 'wordpress':
        sys.stderr.write("ERROR: No wordpress here. (?!)... continuing anyway ...\n")
    do_download=1
    try:
        wp_time = os.stat(home+'/bin/wp' )
        if wp_time.st_mtime > time.time()-365*86400 and wp_time.st_size not in ( 817983, 5616279 ) : 
            do_download=False
    except:
        pass
    if do_download:
        cmd='''
                echo "Downloading wp-cli.phar to $HOME/bin/wp"
                mkdir -p $HOME/bin
                wget -O $HOME/bin/wp http://answers.afrihost.com/updates/wp-cli.phar 
                chmod 755 $HOME/bin/wp
        '''
        os.system(cmd);
    
    if parameters:
        cmd='''
                $HOME/bin/wp %s
        '''
        os.system(cmd % (' '.join(map(shellquote,parameters))))
    return home

def do_drush(parameters):
    droppriv()
    res=dbaccess(show=False)
    if res['app']!='drupal':
        print("No drupal here. (?)")
        return 
    if not os.path.isdir("../drush"):
        os.system(r""" curl http://ftp.drupal.org/files/projects/drush-7.x-5.9.tar.gz | tar -C .. -xz """)
    if not os.path.exists("../drush/php.ini"):
        os.system(r""" sed 's/^memory_limit.*/memory_limit = 256M/' < $( php -r 'echo  php_ini_loaded_file();' ) > ../drush/php.ini """)
    
    os.system("../drush/drush %s" % ' '.join(parameters))

def drupal():
    res=dbaccess(show=False)
    if res['app']!='drupal':
        print("No drupal here. (?)")
        return 
    mysql = "mysql  -f   --host=%(host)s --password='%(pass)s' --user=%(user)s %(db)s" % res
    print("Hacking %(app)s as configured in %(file)s" % res)
    fd = os.popen(mysql,'w')
    cmds="""
        UPDATE system SET status='0' WHERE name='tellafriend';
        UPDATE system SET status='0' WHERE name='forward';
        UPDATE system SET status='0' WHERE name='syslog';
        UPDATE system SET status='0' WHERE name='forum';
    """
    fd.write(cmds)
    fd.close();


def shellquote(name):
    return "'" + ( name.replace("'","'\\''") ) + "'"

def do_patch_ereg_replace(files,no_act=False):
    match=re.compile(r"""\b(ereg_replace)\b""");
    already_done=re.compile(r'deprecated_ereg_replace');
    patch=r"""<?php
# This patch defines deprecated_ereg_replace to replace now deprecated ereg_replace functions
if (!is_callable('compat_posix2pcre')) {
  function compat_posix2pcre($re) { return '{'.$re.'}'; }
  function deprecated_ereg_replace($re,$replacement, $string) { return preg_replace(compat_posix2pcre($re),$replacement,$string); }
}
?>""";
    for filename in files:
        if not os.path.exists(filename):
            sys.stderr.write("'%s' does not exist.\n" % filename)
            continue
        fd=open(filename,'r');
        content=fd.read();
        if not match.search(content): continue;
        if already_done.search(content):
            sys.stderr.write("%s: ereg_replace already patched\n" % filename)
            continue;
        if no_act:
            sys.stderr.write("%s is an ereg_replace script that requires a patch\n" % filename)
            return
        content=content.replace('ereg_replace','deprecated_ereg_replace');
        content=patch+content;
        fd=open(filename,'w');
        fd.write(content)
        fd.close()
        sys.stderr.write("ereg_replace patched %s\n" % filename)
        
def do_patch_upload(files,no_act=False):
    # match=re.compile(r"""<?(x|[^x])*\$_FILES(x|[^x])*move_uploaded_file""");
    already_done=re.compile(r"""This security check aims to prevent the uploading of scripts by hackers:\s*(\S*)""");
    existing_patch=re.compile(r"""<\?php.# This security check aims to prevent the uploading of scripts .*?\?>""", re.DOTALL );
    patch=r"""<?php
# This security check aims to prevent the uploading of scripts by hackers: v2
foreach($_FILES as $fi415) {
 foreach ( (is_array($fi415['name']) ? $fi415['name']:array($fi415['name'])) as $name415) {
  if (preg_match('/\.(\.|php|phtml|htaccess|htpasswd|phar|pl)/i',$name415)) {
   header('Status: 415 Unsupported'); die(); } } } 
?>""";
    for filename in files:
        if not os.path.exists(filename):
            sys.stderr.write("'%s' does not exist.\n" % filename)
            continue
        fd=open(filename,'r');
        content=fd.read();
        # if not match.search(content): continue;  # this takes too long on bigger files ...
        i = content.find('<?');                   
        if i < 0: continue
        ii = content.find('$_FILES');             
        if ii < i: continue
        iii = content.find('move_uploaded_file'); 
        if iii < ii: continue
        m = already_done.search(content)
        if m and already_done.search(patch).group(1)==m.group(1):
            sys.stderr.write("%s: file-upload already patched\n" % filename)
            continue;
        if no_act:
            sys.stderr.write("%s is a file-upload script that requires a patch\n" % filename)
            return
        content=existing_patch.sub('',content);
        content=patch+content;
        fd=open(filename,'w');
        fd.write(content)
        fd.close()
        sys.stderr.write("file-upload patched %s\n" % filename)
        
def do_patch_timthumb(files, no_act=False):
    patch=r"""<?php
# Tim thumb allows the uploading of evil php scripts without this:
if ( preg_match('/\.php/i',$_REQUEST['src']) ) { die('Not allowed'); } 
# Die die die!
?>""";
    match=r"""preg_match('/\.php/i',$_REQUEST['src'])""";
    check="""TimThumb"""
    for filename in files:
        if not os.path.exists(filename):
            sys.stderr.write("'%s' does not exist.\n" % filename)
            continue
        fd=open(filename,'r');
        content=fd.read();
        if content.find(match)>=0: continue;
        if content.find(check)<0: continue;
        content=patch+content;
        if no_act:  
            print("We found a vulnerable installation of timthumb, which requires a patch %s\n" % filename);
        else:
            st = os.stat(filename);
            fd=open(filename,'w');
            fd.write(content)
            fd.close()
            os.utime(filename,(st.st_atime, st.st_mtime) ); # ctime changes instead 
            sys.stderr.write("Vulnerable copy of tim thumb patched, %s\n" % filename)
        
def remail(files):
    emailre=re.compile(r'[a-zA-Z0-9+_\-\.]+@[0-9a-zA-Z][.-0-9a-zA-Z]*.[a-zA-Z]+')
    for filename in files:
        recipients=[]
        fd=open(filename,'r')
        while True:
            line = fd.readline()
            if line.strip()=='': break
            if not line.startswith('Envelope-to: '): continue
            recipients = emailre.findall(line)
            line = fd.readline()
            while line.startswith(' ') or line.startswith('\t'):
                recipients.extend( emailre.findall(line) )
                line = fd.readline()
            break
        fd.close()
        if len(recipients):
            recipientparams = ' '.join(map(shellquote,recipients))
            sys.stdout.write('sendmail %s < %s\n' % (recipientparams, shellquote(filename)) )
        else:
            sys.stdout.write('sendmail -t -i < %s\n' % (shellquote(filename)) )
            
def do_limit(mailsperhour):
    cpanel = CpanelData()
    cpanel.setitem("MAX_EMAIL_PER_HOUR",str(mailsperhour))
    os.system("/usr/local/cpanel/scripts/update_email_limits '%s'" % cpanel.user)
    
def suspend():
    cpanel = CpanelData()
    r={'user': cpanel.user, }
    os.system("""
        /scripts/suspendacct %(user)s
    """ % r )

def unsuspend():
    cpanel = CpanelData()
    r={'user': cpanel.user, }
    os.system("""
        /scripts/unsuspendacct %(user)s
    """ % r )

def lockout():
    cpanel = CpanelData()
    r={'user': cpanel.user, }
    print("HARD suspend to lock out account %(user)s for 1 week" % r)
    os.system("""
        [ -f /usr/bin/at ] || yum -y install at && service atd start
        chattr -i /var/cpanel/users/%(user)s
        /scripts/suspendacct %(user)s
        chattr +i /var/cpanel/users/%(user)s
        chattr +i /home/%(user)s/public_html/.htaccess
        echo "chattr -i /var/cpanel/users/%(user)s /home/%(user)s/public_html/.htaccess" | at now + 2 days
    """ % r )
    
def unlockout():
    sys.stderr.write("  frob unlockout auth token:");
    r=sys.stdin.readline()
    if not r.startswith(chr(32)+chr(32)): # auth token is     
        print("  Incorrect auth token")
        return
    cpanel = CpanelData()
    r={'user': cpanel.user, 'taken_down': 'taken-down'}
    os.system("""
        if [ -d '%(taken_down)s' ] ; then
            echo "Undoing takedown"
            rm .htaccess index.php
            mv %(taken_down)s/* %(taken_down)s/.* .
            rmdir %(taken_down)s
            chown --reference=.. .
            chgrp nobody .
            chmod 710 .
        fi
        chattr -i /var/cpanel/users/%(user)s
        chattr -i /home/%(user)s/public_html/.htaccess
    """ % r )
    
def do_slow_query_log(parameters):
    username = pwd.getpwuid(os.stat(".").st_uid).pw_name
    userprefix='# User@Host: '+username+'_'
    r = os.popen( "tail -c 2000000 /var/log/mysql_slow_query_log", 'r');
    printy=False
    for line in r:
        if line.startswith('# User@'): printy=False
        if line.startswith(userprefix): printy=True
        if printy:
            sys.stdout.write(line);
    pass

def do_corrupt(parameters):
    for filename in parameters:
        junkpos=64
        junklength=64
        if not os.path.exists(filename): continue
        fd=open(filename,'r+b')
        fd.seek(0,2)
        length = fd.tell()
        fd.seek(min(junkpos,length),0)
        fd.write(open('/dev/urandom','r').read(junklength))
        fd.close()
    pass

def lockmailbox_exim(username):
    m = re.match(r'(\S+)@(\S+.\S+)',username);
    if not m:
        sys.stderr.write("%s: invalid mailbox\n" % username);
        return {'locked': False }
    user,domain = m.groups()
    user=user.lower()
    domain=domain.lower()
    exim_bt = os.popen("exim -d -bt %s 2>&1" % shellquote(username),'r')
    passwd_file = ''
    passwd_re=re.compile(r'(/(\S*/)?home/\S*/etc/\S+\.\S+/(passwd|quota))\b')
    for line in exim_bt:
        m = passwd_re.search(line)
        if not m: continue
        passwd_file =m.group(1)
    if not passwd_file:
        userdomains = get_userdomains()
        account=userdomains.get(domain,'')
        homedir = pwd.getpwnam(account).pw_dir
        f = homedir+'/etc/'+domain+'/passwd'
        if os.path.exists(f):
            passwd_file=f
    if not passwd_file:
        sys.stderr.write("%s: No password file" % username);
        return {'locked': False }
    if not os.path.exists(passwd_file):
        sys.stderr.write("%s: does not exist" % passwd_file);
        return {'locked': False }
    shadow_file = ('/'.join(passwd_file.split('/')[:-1]))+'/shadow'
    if not os.path.exists(shadow_file):
        sys.stderr.write("%s: does not exist" % passwd_file);
        return {'locked': False }
    result={}
    result['filename']=shadow_file
    result['locked'] = lock_mailbox_shadow_file(shadow_file,user);
    return result

def getcontactaddress(filename, there_can_only_be_one=False):
    'Figure out who to notify about this file'
    from maintenance import config
    more_contacts = config.option('notify_list','support@afrihost.com').split()
    contacts = []
    bits = filename.split('/')
    home_index = bits.index('home')
    homedir='/'.join(bits[:home_index+2])
    contactemailfile = homedir+'/.contactemail'
    if os.path.exists(contactemailfile):
        contacts.append(open(contactemailfile,'r').readline().strip())
    if not there_can_only_be_one:
        contacts.extend(more_contacts);
    return contacts
    

def notify(contacts, info):
    # info['subject'] 
    # info['body'] 
    info['hostname'] = socket.gethostname()
    info['email'] =  '>, <'.join(contacts[1:])
    info['email0'] =  contacts[0]
    headerlines=[]
    headerlines.append( "From: %(app)s Notifier <root@%(hostname)s>\n" )
    headerlines.append( "To: <%(email0)s>\n" )
    if info.get('email'):
        headerlines.append( "Bcc: <%(email)s>\n" )
    headerlines.append( "Subject: %(subject)s\n" )
    template = ''.join(headerlines)+ \
        "\n"+ \
        "Hi %(email0)s,\n"+ \
        "\n" + \
        info['body'] + "\n"

    sys.stderr.write(template % info)
    fd = os.popen('/usr/sbin/sendmail -t -i','w') 
    fd.write(template % info)
    fd.close()

def do_newpassword(parameters,force=0):
    # Returns 0 if something went right
    import posix,pwd
    import ftpsetrandompasswd
    stat = os.stat('.')
    uid = stat.st_uid
    pwent = pwd.getpwuid(stat.st_uid)
    username = pwent.pw_name
    r = ftpsetrandompasswd.setrandompassword(username)
    contacts = getcontactaddress(os.getcwd(), there_can_only_be_one=True)
    r['hostname' ] = socket.gethostname()
    info = {
        'app':'Security',
        'subject':r['sitename']+' password change, user '+username,
        'username': username,
        'body': (
            'The system administrator has set a new password for the account\n'
            '%(username)s on %(hostname)s.\n'
            '\n'
            'The new credentials for %(sitename)s are now:\n'
            '\n'
            '  user name: %(username)s\n'
            '  password : %(newpassword)s\n'
            '\n'
            'This message has been semi-automatically generated.  Please ensure\n'
            'that you use SSL when using this password, to prevent interception.\n'
            'Do not use this password from a desktop system that has viruses or\n'
            'other security problems.\n'
            '\n'),
    }
    info.update(r);
    rc = notify(contacts,info)
    return rc

def do_spamming(parameters,force=0):
    # Returns 0 if something went right
    rc = 1
    for username in parameters:
        results = lockmailbox_exim(username)
        filename=results.get('filename')
        locked=results.get('locked')
        os.system('eximanage --demail %s' % shellquote(username));
        if not filename or not locked:
            continue
        rc = 0 # something went well
        contacts = getcontactaddress(filename)
        if locked:
            has_been_locked = '\nThe password for the mailbox '+username+' has been locked\n'
        else:
            has_been_locked = ''
        info = {
            'app':'Mail system',
            'subject':':: MAIL SECURITY ALERT :: Mailbox password locked. '+username,
            'username': username,
            'has_been_locked': has_been_locked,
            'body': (
                'The mailbox %(username)s has been either sending spam, or has triggered a\n'
                'high vulnerability alert in the form of creation attempts of highly suspect interception\n' 
                'filters to facilitate fraudulent activities.\n'
                '\n'
                'This means that the email password is known to spammers or fraudsters,\n'
                'and your domain is being targeted.\n'
                '\n'
                'The password has been locked, and domain is safe.\n'
                '\n'
                'Reset your password to something new and complex to remain safe,\n'
                'it is suggested that you update email passwords of all email accounts,\n'
                'and be vigilant in regards to unknown forwardres and unusual email activity.\n'
                '\n'  
                'Your password could have been attained due to one of the following:\n'
                '\n'
                ' 1. a poor choice of password (e.g. p@55w0rd!@#)\n'
                ' 2. a desktop virus\n'
                ' 3. failure to use SSL when retrieving mail\n'
                ' 4. Phishing scam\n'
                '%(has_been_locked)s\n'
                '\n'
                'Please check the user systems for viruses, enable SSL and then\n'
                'set a new password (not the same password!).  Also check other\n'
                'mailboxes and systems that may have the same password scheme,\n'
                'virus or security problems.\n'
                '\n'
                'We also suggest you check your email filters and forwarders to ensure no filters or forwarders,\n'
                'have been created in an attempt to intercept mail.\n'
                'https://help.afrihost.com/topic/domains-and-hosting/how-to-manage-email-filters-in-cpanel\n'
                'https://help.afrihost.com/topic/security/how-to-protect-yourself-from-email-interception-fraud\n'
                'Visit http://www.icode.org.za/ for help with desktop security.\n'
                '\n'),
        }
        hostname = socket.gethostname()
        if hostname.endswith('.au') or hostname.find('ewsprou')>0 or username.endswith('.au'):
            info['body'] = info['body'].replace('http://www.icode.org.za/','http://www.icode.net.au/')
        notify(contacts,info)
    return rc

def do_notify(parameters,force=0):
    # Send a nastygram to the client
    rc = 1
    contacts = getcontactaddress('.')
    info = {
        'app':'System notification',
        'subject':'Notification '+username,
        'username': username,
        'body': (
            'Hello,\n'
            '\n'
            'Please take note of the following notification\n'
            '\n' +
            (' '.join(parameters) ) + '\n'
            '\n'
            'Thank you\n'
            '\n'),
    }
    hostname = socket.gethostname()
    notify(contacts,info)
    return rc

def quasiurlparse(quasiurl=''):
    i=quasiurl.find('//')
    if i>=0:
        vhost, fileuri = quasiurl[i+2:].split('/',1)
        fileuri=fileuri.lstrip('/');
    else:
        vhost='localhost'
        fileuri = quasiurl

    if fileuri.find('?')>=0:
        filepart,querystring=fileuri.split('?',1)
        questionmark='?'
    else:
        filepart=fileuri
        querystring='';
        questionmark=''
    if not os.path.exists('../public_html'):
        cwd=os.getcwd();
        public_html = '/public_html/'
        i=cwd.find(public_html);
        if i>0:
            filepart=cwd[i+len(public_html):]+'/'+filepart.lstrip('/')

    url = 'http://'+vhost+'/'+filepart+questionmark+querystring

    filename='index.php'
    bits = filepart.split('/');
    #path_info = filepart
    is_dir = False
    for r in range(0,len(bits)):
        somefile = '/'.join(bits[:r+1])
        if not somefile:
            somefile='.'
            is_dir = True
        if os.path.isfile(somefile+'/index.php'):
            filename = somefile+'/index.php'
            is_dir = True
        if os.path.isfile(somefile):
            filename = somefile;
            is_dir = False
    if is_dir:
        filepart = filepart.rstrip('/')+'/'
    return {
        'url': 'http://'+vhost+'/'+filepart+questionmark+querystring,
        'request_uri': '/'+filepart+questionmark+querystring,
        #'path_info': path_info,
        'vhost': vhost, 
        'filepart': filepart, 
        'uri': filepart+questionmark+querystring,
        'querystring': querystring,
        'questionmark': questionmark,
        'filename': filename, 
        }

def do_phptest(quasiurls):
    for quasiurl in quasiurls:
        for line in phptest(quasiurl):
            sys.stdout.write(line)

def linkfarmscan(filenames):
    import subprocess
    cmd = [ '/usr/local/afribin/maintenance/linkfarmscan.py', '-N', '/dev/null','--limit','999999' ]
    cmd.extend(filenames)
    rc = subprocess.call(cmd, shell=False)

def find_html_urls(line, base_url, urls=[]):
    '''Nasty regex parser to find html urls'''
    '''base_url is like http://site/fir/index.php'''
    re_href=re.compile(r'\<href\s*=\s*"?([^"> ]{1,})',re.I);
    for m in re_href.finditer(line):
        relative_url = m.group(1)
        protocol = relative_url.find('//')
        if protocol>=1 and protocol < 5:
            if relative_url.startswith(base_url):
                urls.append(relative_url)  # same site
            continue
        if relative_url.startswith('/'):
            bits=base_url.split('/')
            urls.append('/'.join(bits[:3])+relative_url)
            continue
        i=base_url.rfind('/')  # okay, so this is sucky, since it assumes get parameters don't contain /, but hey
        urls.append(base_url[:i]+'/'+relative_url)
    return urls

def do_rfc3514(quasiurls):
    droppriv()
    disabledcodeprefix='Reached disabled code in ';
    # Also disabled code: 
    filenames = []
    rfc3514fd=open('.rfc3514','a')
    # Try running directly:
    for quasiurl in quasiurls:
        for iteration in range(0,10000):
            foundsomething = False
            for line in phptest(quasiurl):
                i=line.find(disabledcodeprefix)
                if i<0: continue
                filename=line[i+len(disabledcodeprefix):].strip()
                do_undisable([filename,])
                filenames.append(filename)
                rfc3514fd.write(filename+'\n');
                rfc3514fd.flush() # don't buffer please
                foundsomething=True
            if not foundsomething:
                break
        pass
    # Try running through curl:
    for url in quasiurls:
        if url.find('://')<0: continue
        for iteration in range(0,1000):
            foundsomething = False
            for line in rfc3541_with_curl(url):
                i=line.find(disabledcodeprefix)
                if i<0: continue
                filename=line[i+len(disabledcodeprefix):].strip()
                do_undisable([filename,])
                filenames.append(filename)
                rfc3514fd.write(filename+'\n');
                rfc3514fd.flush() # don't buffer please
                foundsomething=True
            if not foundsomething:
                break
        pass
    if len(filenames):
        linkfarmscan(filenames)

def rfc3541_with_curl(url):
    fd = os.popen('curl --cookie RFC3541=1 \'%s\'' % url.replace("'","'\\''"),'r')
    for line in fd:
        yield(line)
    fd.close();
        
def phptest(quasiurl):
    "Run the PHP code, and return its output"
    quasiurlinfo = quasiurlparse(quasiurl);
    if not os.path.exists(quasiurlinfo['filename']):
        return
    phpcode = r"""<?php

        $request_uri='%(request_uri)s';
        $vhost="%(vhost)s";
        $getcwd=getcwd();
        $filename="%(filename)s";
        $querystring = "%(querystring)s";
        $questionmark = "%(questionmark)s";
        $ip="127.0.0.1";

        $_SERVER["FCGI_ROLE"]="RESPONDER";
        $_SERVER["UNIQUE_ID"]=rand().'x'.rand();
        $_SERVER["SERVER_NAME"]="$vhost";
        $_SERVER["HTTP_HOST"]="$vhost";
        $_SERVER["HTTP_USER_AGENT"]="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0";
        $_SERVER["HTTP_ACCEPT"]="text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
        $_SERVER["HTTP_ACCEPT_LANGUAGE"]="en-ZA,en-GB;q=0.8,en-US;q=0.5,en;q=0.3";
        $_SERVER["HTTP_ACCEPT_ENCODING"]="gzip, deflate";
        $_SERVER["HTTP_COOKIE"]="RFC3514=1";
        $_COOKIE["RFC3514"]="1";
        $_SERVER["HTTP_DNT"]="1";
        $_SERVER["HTTP_CONNECTION"]="close";
        $_SERVER["SERVER_SIGNATURE"]="";
        $_SERVER["SERVER_SOFTWARE"]="Apache";
        $_SERVER["SERVER_ADDR"]="$ip";
        $_SERVER["SERVER_PORT"]="80";
        $_SERVER["REMOTE_ADDR"]="$ip";
        $_SERVER["DOCUMENT_ROOT"]="$getcwd";
        $_SERVER["SERVER_ADMIN"]="webmaster@$vhost";
        $_SERVER["SCRIPT_FILENAME"]="$getcwd/$filename";
        $_SERVER["REMOTE_PORT"]="32769";
        $_SERVER["GATEWAY_INTERFACE"]="CGI/1.1";
        $_SERVER["SERVER_PROTOCOL"]="HTTP/1.1";
        $_SERVER["REQUEST_METHOD"]="GET";
        $_SERVER["QUERY_STRING"]="$querystring";
        $_SERVER["SCRIPT_NAME"]="/$filename";
        $_SERVER["REQUEST_URI"]="$request_uri";
        # $_SERVER["PATH_INFO"]="$path_info";
        # $_SERVER["PATH_TRANSLATED"]="$getcwd/index.html";
        $_SERVER["PHP_SELF"]="/$filename/";
        $_SERVER["REQUEST_TIME"]=time();

        $_ENV=$_SERVER; 
        # print_r($_SERVER);
        include "$filename";
""";
    phpcode = phpcode % quasiurlinfo;
    droppriv(quiet=True)
    import tempfile
    tmp=tempfile.NamedTemporaryFile('w')
    tmp.write(phpcode)
    tmp.flush()
    fd = os.popen("strace -o /tmp/.phptest -s 999 php < "+tmp.name,'r')
    for line in fd:
        yield(line)
    fd.close()

def do_undisable(files):
    # Undo disable done by do_disable
    changes = 0
    for filename in files:
        fd = open(filename,'r')
        lines = fd.readlines();
        fd.close();
        if len(lines)>0:
            line0 = re.sub(r'<\?php .* /\*.* disabled for security \*/ \?>\s*','',lines[0]);
            if line0.strip()=='': line0='';
            if line0==lines[0]:
                sys.stderr.write('frob undisable: %s: no change\n' % filename);
                continue   # no change to this file
            lines[0]=line0
            changes += 1
            st = os.stat(filename);
            fd = open(filename,'w')
            fd.writelines(lines);
            fd.close()
            os.utime(filename,(st.st_atime, st.st_mtime) );
            sys.stdout.write(filename+'\n');
    return changes

#def do_disable(files,no_act=False, die='<?php die(); ?>'):
def do_disable(files,no_act=False,die=r'<?php die(@$_COOKIE["RFC3514"]?"Reached disabled code in ".__FILE__."\n":NULL); ?>'):
    """Hack a disable into a file """
    for filename in files:
        p = { 'file': filename.replace("'","'\\''"),  # shellquote
              'die': die,
              'timestamp': time.strftime("%Y-%m-%d %H:%M:%S"), }

        if not os.path.exists(filename):
            sys.stderr.write("%s not found\n" % filename);
            continue
        if not os.path.isfile(filename):
            sys.stderr.write("%s not a regular file\n" % filename);
            continue
        if filename.find(".php")<0:
            if open(filename,'r').read().find("<?") < 0:
                sys.stderr.write("%s not a php file\n" % filename);
                continue
        fd=open(filename,'r');
        firstline=fd.readline();
        if firstline.startswith(p['die'].strip()):
            sys.stderr.write("frob disable: %s: already disabled\n" % filename);
            fd.close()
            continue
        file_lines=fd.readlines();
        fd.close();
        st = os.stat(filename);
        file_lines.insert(0,firstline);
        file_lines.insert(0,'%(die)s<?php /* %(timestamp)s disabled for security */ ?>\n' % p);
        if no_act:
            print("frob disable: %s: not disabling" % filename)
        else:
            fd=open(filename,'w');
            fd.writelines(file_lines);
            fd.close()
            os.utime(filename,(st.st_atime, st.st_mtime) );
            sys.stderr.write(filename+"\n");

def get_userdomains():
    r={}
    u = open('/etc/userdomains','r')
    for line in u:
        bits=line.split(": ")
        if len(bits)!=2: continue
        r[bits[0].strip()]=bits[1].strip();  # r[domain.co.za] = domaixnq
    u.close()
    return r

def lockmailbox(mailbox):
    'Returns filename locked and true/false'
    name,domain=mailbox.split("@",1)
    userdomains = get_userdomains()
    account=userdomain.get(domain,'')
    if not account:
        print("No account for "+mailbox)
        return
    homedir = pwd.getpwnam(account).pw_dir
    shadow_file = homedir+'/etc/'+domain+'/shadow'
    if not os.path.exists(shadow_file):
        print(shadow_file+' not present')
        return
    return shadow_file, lock_mailbox_shadow_file(shadow_file,name);

def lock_mailbox_shadow_file(shadow_file,name):
    'Lock the user in the shadow file: returns True if a change was made'
    mailboxshadows=open(shadow_file,'r').readlines()
    mod=False
    out=[]
    for mailboxshadow in mailboxshadows:
        mb_user,mb_crypt,mb_more=mailboxshadow.split(":",2)
        if mb_user==name:
            if mb_crypt[:1] in ('X','*','!'):
                sys.stderr.write( shadow_file+' already has locked user '+name+'\n');
                return False
            else:
                mb_crypt='X'+mb_crypt;
                mod=True
        out.append(":".join( (mb_user,mb_crypt, mb_more) ))
    if not mod:
        sys.stderr.write( shadow_file+' does not contain user '+name+'\n');
        return None
    print("locking "+name+" in "+shadow_file)
    open(shadow_file,'w').writelines(out)
    os.system('killall pop3-login imap-login') # kill cached users
    return True

def lockmailboxes(mailbox):
    for mailbox in mailboxes:
        lockmailbox(mailbox)

# VERY basic yml getter
def ymlread(filename, parameter):
    fd = open(filename,'r');
    for line in fd:
        if line.startswith(parameter+': '):
            fd.close()
            return line[line.find(': ')+2:].strip()
    fd.close()

# VERY basic yml setter
def ymlwrite(filename, parameter, value):
    fd = open(filename,'r');
    out=[]
    for line in fd:
        if line.startswith(parameter+': '):
            out.append(parameter+': '+value+'\n');
        else:
            out.append(line)
    fd.close()
    fd=open(filename,'w');
    fd.writelines(out);
    fd.close()

def do_codesniffer(params):
    os.system('''
        [ -d /usr/local/lib/php/PHP/CodeSniffer ] || 
            pear install PHP_CodeSniffer
    ''');
    droppriv()
    os.system('''
            phpcs --extensions=php,phtml  --standard=/usr/local/lib/php/PEAR/PHPCompatibility '%s'
    ''' % os.getcwd())

def tailfile(filename, tail=200000):
    if not os.path.isfile(filename):
        return
    fd = open(filename,'r')
    fd.seek(0,2)
    length = fd.tell()
    fd.seek(max(0,length-tail),0)
    drop = fd.readline();
    for line in fd:
        yield(line);
    fd.close

def tailmmin1domlogs(seconds=60, tail=200000):
    dirlist = glob.glob('/usr/local/apache/domlogs/*')
    time0 = time.time();
    for domlogfile in dirlist:
        if domlogfile.find('_')>0:
            continue
        st=os.stat(domlogfile)
        if abs(time0 - st.st_mtime) > seconds:
            continue
        for line in tailfile(domlogfile, tail):
            yield line
    
def trawl_log_files_for_evilips(additionalregex=[], tail=200000, seconds=60):
    "Trawl the log files for brute force attempts"
    evilips={}
    reasons={}
    accesslog_evil_re = re.compile(r'POST .*(login| (500|40[34]) |administrator/[^/]* )')
    accesslog_evil_re = re.compile(r'POST .*((login|administrator).* HTTP/1\.." (500|40[34]))')
    rip_re = re.compile(r'rip=(\d+\.\d+\.\d+\.\d+),')
    smtp_evil_re = re.compile(r'authenticator failed for')
    smtp_rip_re = re.compile(r'\[(\d+.\d+\.\d+\.\d+)\]:\d+')
    smtp_good_re = re.compile(r' A=dovecot_');
    messages_evil_re = re.compile(r'Authentication failed for')
    messages_ip_re = re.compile(r'@(\d+.\d+\.\d+\.\d+)\)')
    additionalregex_re = list(map(re.compile,additionalregex))
    r_0_255=r'(0|[1-9]\d?|1\d\d|2[0-4]\d|25[0-5])';
    any_ip_re = re.compile(r'\b' + r_0_255 + r'\.' + r_0_255 + r'\.' + r_0_255 + r'\.' + r_0_255 + r'\b' );

    ipwhitelist =  []
    for ipinfo in list_myips():
        ipwhitelist.append(ipinfo['ip'])

    # Attack of the cloned code:
    # HTTP ATTACKS -- this goes first, since we might consider the thing less evil later
    for line in tailmmin1domlogs(tail=tail, seconds=seconds):
        if accesslog_evil_re.search(line):
            ip=line.split(' ')[0]
            evilips[ip]=evilips.get(ip,0)+1
            reasons[ip]='http brute force'

    # SMTP ATTACKS
    for line in tailfile('/var/log/messages',tail=2000000):
        # Nov 14 12:55:43 porkypig pure-ftpd: (?@91.109.5.130) [WARNING] Authentication failed for user [hendrikvanheerden]
        if messages_evil_re.search(line):
            m=messages_ip_re.search(line)
            if m: 
                ip = m.group(1)
                evilips[ip]=evilips.get(ip,0)+1
                reasons[ip]='ftp brute force'
        for r in additionalregex_re:
            for ip in any_ip_re.finditer(line):
                if ip in ipwhitelist: continue
                ip = m.group(1)
                reason='messages abuse'
                evilips[ip]=evilips.get(ip,0)+1
                reasons[ip]=reason
                break

    # SMTP ATTACKS
    for line in tailfile('/var/log/exim_mainlog',tail=2000000):
        for r in additionalregex_re:
            for ip in any_ip_re.finditer(line):
                if ip in ipwhitelist: continue
                ip = m.group(1)
                reason='exim_mainlog abuse'
                evilips[ip]=evilips.get(ip,0)+1
                reasons[ip]=reason
                break
        # 2014-11-14 12:28:21 dovecot_login authenticator failed for (WIN-7IEAI6D9UC9) [188.190.118.90]:54400: 535 Incorrect authentication data (set_id=laurel)
        if smtp_evil_re.search(line):
            m=smtp_rip_re.search(line)
            if m: 
                ip = m.group(1)
                evilips[ip]=evilips.get(ip,0)+1
                reasons[ip]='smtp brute force'
        # 2014-11-14 12:24:51 1XpE3d-0003ik-C1 <= jimmy@jsengineering.co.za H=ti-227-166-130.telkomadsl.co.za (jimmy) [105.227.166.130]:2594 P=esmtpa A=dovecot_login:jimmy@jsengineering.co.za S=103252 id=A4AF717B0E524DA3A3B383ED58EFA129@jimmy T="Estimate from JS Engineering # quotation no: 45437" for sachin.teeka@unilever.com
        if smtp_good_re.search(line):
            m=smtp_rip_re.search(line)
            if m:
                ip = m.group(1)
                if ip in evilips: evilips.pop(ip)

    # POP3 ATTACKS
    for line in tailfile('/var/log/maillog',tail=2000000):
        i=line.find('-login: Login: ');
        if i>0:
            m = rip_re.search(line)
            if m:
                ip=m.group(1)
                if ip in evilips: evilips.pop(ip)
            continue
        i=line.find('auth failed,')
        if i>0:
            m = rip_re.search(line)
            if m:
                ip = m.group(1)
                evilips[ip]=evilips.get(ip,0)+1
                reasons[ip]='pop/imap brute force'
        for r in additionalregex_re:
            for ip in any_ip_re.finditer(line):
                if ip in ipwhitelist: continue
                ip = m.group(1)
                reason='maillog abuse'
                evilips[ip]=evilips.get(ip,0)+1
                reasons[ip]=reason
                break

    return evilips,reasons
        
ddos_times = 0
ddos_blocked_ips = {}
syslog_program = '';

def send_syslog(program, message, priority=syslog.LOG_ERR):
    global syslog_program
    if syslog_program!=program:
        syslog.openlog(program, syslog.LOG_PID, syslog.LOG_DAEMON)
        syslog_program=program
    syslog.syslog(priority,message)

def nullroute_blocker(r):
    return "ip route add prohibit %(ip)s # antibruteforce: %(n)d hits, %(reason)s" % r 

def do_antibruteforce(params, seconds=60, maximum_conections=8192, blocker=nullroute_blocker):
    # number of hits in t
    filecount = 0
    global ddos_blocked_ips
    global ddos_times

    if ddos_times==0:
        fd = os.popen('ip route ls','r')
        prohibit_re = re.compile(r'prohibit (\d+\.\d+\.\d+\.\d+)\s*$')
        for line in fd:
            m=prohibit_re.search(line)
            if not m: continue
            ip=m.group(1)
            ddos_blocked_ips[ip]=True
        fd.close()
    if len(ddos_blocked_ips) > maximum_conections:
        send_syslog('ddos','Dropping %d host null routes' % len(ddos_blocked_ips) )
        os.system('''
            echo 1>&2 "Too many hosts prohibited: dropping ALL host prohibit routes"
            ip route ls | grep 'prohibit [0-9.]* *$'  | while read RULE ; do ip route del $RULE; done
        ''')
        ddos_blocked_ips={}

    ips,reasons = trawl_log_files_for_evilips(seconds=seconds)
    remaining = 64
    cmds=[]
    for ip in sorted(ips, key=ips.get, reverse=True):
        if ip in ddos_blocked_ips:
            continue
        if ip.startswith('127.'):
            continue
        n = ips[ip]
        if n<75:
            break
        r = { 'ip': ip, 'n': ips[ip], 'reason': reasons[ip]}
        send_syslog('ddos','null routing %(ip)s, %(n)s %(reason)s' % r)
        ddos_blocked_ips[ip]=True
        cmds.append ( blocker(r) )
        remaining -= 1
        if remaining==0: 
            break
    if len(cmds):
        print('\n'.join(cmds));
        os.system( '\n'.join(cmds) )
    if ddos_times==0:
        print("# DDOS first time complete")
    ddos_times += 1
    return cmds

def do_php54ini(params):
    for phpini in params:
        ini = open(phpini,'r').read()
        if  not re.search(r'allow_call_time_pass_reference|magic_quotes_gpc|register_globals|ioncube_loader_lin_5',ini,re.I) and \
            not re.search(r'display_errors\s*=\s*(on|true)',ini,re.I):
            continue
        sys.stderr.write(phpini+"\n");
        os.system(r'''
            sed -i '
                /allow_call_time_pass_reference\|magic_quotes_gpc\|register_globals/ d;
                s/ioncube_loader_lin_5...so/ioncube_loader_lin_5.4.so/;
                s/^display_errors.*/display_errors = Off/;
            ' ../php.ini
            '''.replace('../php.ini','%s') % shellquote(phpini) )


def do_active_mailboxes(params):
    cmd = 'find /home/*/mail -mindepth 3 -maxdepth 3 -name cur -mtime -92 -type d'
    fd = os.popen(cmd,'r')
    for cur in fd:
        cur=cur.strip('\n')
        parent = cur[:cur.rfind('/')]
        try:
            # new_mtime = os.stat(parent+'/new').st_mtime
            cur_mtime = os.stat(cur).st_mtime
            parent_mtime = os.stat(parent).st_mtime
        except:
            continue
        if abs(cur_mtime - parent_mtime) < 86400*21: # three weeks of operation on this mailbox please
            continue # unused 
        print(cur)
    fd.close()

def serveralias(names):
    "Add a server alias to the site, all cpanel like"
    cpanel = CpanelData()
    cpanelinfo = dict(
        user=cpanel.user,
        domain=cpanel.cpanel['DNS'])
    userdatafile='/var/cpanel/userdata/%(user)s/%(domain)s' % cpanelinfo
    if not os.path.exists(userdatafile):
        print("User datafile "+userdatafile+" does not exist");
        return False
    serveraliases_in=ymlread(userdatafile,'serveralias');
    aliases=serveraliases_in.split()
    for name in names:
        if not (name in aliases):
            aliases.append(name)
    serveraliases_out = ' '.join(aliases)
    if serveraliases_out != serveraliases_in:
        ymlwrite(userdatafile,'serveralias',serveraliases_out);
        apachectlgraceful(build=True)
    print(serveraliases_out)

def do_vmwaretools(params):
    os.system('''
        set -x
        set -e
        cd 
        rm -rf vmware-tools-distrib VMwareTools-*
        curl -k https://answers.afrihost.com/updates/VMwareTools-latest.tar.gz | tar -xz
        vmware-tools-distrib/vmware-install.pl -d
        vmware-toolbox-cmd timesync enable
    ''')

def do_syslog(params):
    if len(params)==1:
        program='frob'
    else:
        program=params.pop(0)
    message=' '.join(params)
    send_syslog(program, message);

def previewalias(usernames):
    "Add preview aliases for a list of user names"
    if len(usernames)==0:
        usernames=[ CpanelData().user, ]
    mod=False
    for username in usernames:
        i=username.rfind('/')
        username=username[i+1:]  # whole name or something
        if not os.path.exists('/var/cpanel/users/'+username): continue
        cpanel = CpanelData(username)
        cpanelinfo = dict(
            hostname=socket.gethostname(),
            user=cpanel.user,
            domain=cpanel.cpanel['DNS'])
        userdatafile='/var/cpanel/userdata/%(user)s/%(domain)s' % cpanelinfo
        if not os.path.exists(userdatafile):
            sys.stderr.write("User datafile %s does not exist\n" % userdatafile) 
            continue
        serveralias='%(user)s.%(hostname)s' % cpanelinfo
        serveraliases_in=ymlread(userdatafile,'serveralias');
        aliases=serveraliases_in.split()
        if not (serveralias in aliases):
            aliases.append(serveralias)
        serveraliases_out = ' '.join(aliases)
        if serveraliases_out != serveraliases_in:
            ymlwrite(userdatafile,'serveralias',serveraliases_out);
            mod=True
        print(('%(user)s: %(domain)s : ' % cpanelinfo) + serveraliases_out)
    if mod:
        apachectlgraceful(build=True)


if __name__=="__main__":
    dircmd=None
    import optparse
    parser=optparse.OptionParser()
    parser.add_option("-n","--number", dest="number", action="store", type='int', default=0, help="How many")
    parser.add_option("-v","--verbose", dest="verbose", action="count", default=0, help="How verbose")
    (options,args) = parser.parse_args()
    rc = 0
    operation=''
    parameters=[]
    if len(args)>0:
        operation=args[0]
        parameters=args[1:]

    if operation == "complete":
        source = open(sys.argv[0], 'r').read()
        for operation in re.findall(r'''if\s+operation\s*==\s*["'](.*?)['"]''',source):
            if operation not in ('complete',): print(operation)
        sys.exit();

    send_syslog('frob','%s' % (' '.join(args)),priority=syslog.LOG_ERR)

    if operation == "safe": dircmd=safe
    if operation == "images": dircmd=do_images
    if dircmd:
        cwd = os.getcwd()
        for wd in parameters:
            dircmd(wd)

    elif operation=="sessions":
        mysql_sessiontable_repair()

    elif operation=="cronfix":
        cronfix()

    elif operation=="mysqllimit":
        mysqllimit(parameters, options.number)

    elif operation=="redirect":
        redirect(url=parameters[0],title=parameters[1])

    elif operation=="disable":
        do_disable(parameters)

    elif operation=="undisable":
        do_undisable(parameters)

    elif operation=="subscribe":
        subscribe()

    elif operation=="fcgiphpini":
        customphpini(rebuild=True)

    elif operation=="phpinfo":
        show_phpinfo()

    elif operation=="securephp":
        customphpini(disable_functions=True, open_basedir=True, setupfw='fw.php')

    elif operation=="limit":
        do_limit(int(parameters[0]))

    elif operation=="suspend":
        suspend()
    elif operation=="unsuspend":
        unsuspend()
    elif operation=="lockout":
        # Lock out a site which is too easily unsuspended
        lockout()
    elif operation=="unlockout":
        # Un-Lock out a site which is too easily unsuspended
        unlockout()

    elif operation=="phishing":
        print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!")
        print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!")
        print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!")
        customphpini(disable_functions=True, open_basedir=True, setupfw='checksum.php')
        do_limit(25);
        print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!")
        print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!")
        print("WARNING: INPUT POSTED TO THIS SITE WILL BE CORRUPTED!!!")

    elif operation=="htaccess":
        htaccess_fixup()

    elif operation=="modsec":
        modsecurity_onoff(True)

    elif operation=="nomodsec":
        print("Dude. Solve the problem. This is not a troubleshooting step. Solve the problem. Start with the error log.")
    #elif operation=="nomodsec":
    #    if len(parameters)==0 or parameters[0].find("//")<0:
    #        print "MISSING INFORMATION: Please supply the URL which produces an error when mod_security intrusion prevension is enabled"
    #    else:
    #        #print "Checking for timthumb and patching it it found..."
    #        #os.system("find . -name '*thumb*php' -print0|xargs -0 %s timthumb" % frob() )
    #        modsecurity_onoff(False,reason=parameters[0])

    elif operation=="modsecrule":
        modsecurity_addrules(parameters)

    elif operation=="modsecauto":
        if not modsecurity_autorule() :
            print("No change")
        else:
            # build with new rules
            apachectlgraceful(build=True)

    elif operation=="killall":
        if not parameters: killall()
    elif operation=="rebuild":
        apachectlgraceful(build=True)
    elif operation.find("dcpu")==0:
        dcpumonview()
    elif operation=="domlogs":
        domlogs()
    elif operation=="graceful":
        apachectlgraceful(build=False)
    elif operation=="hacking":
        if len(parameters)==0: parameters=['.']
        for dir in parameters:
            hacking(dir)
    elif operation=="takedown":
        if len(parameters)==0: parameters=['.']
        for dir in parameters:
            takedown(dir)
    elif operation=="sanitise":
        f=[]
        for do_cleanup in (cleanup_eval, cleanup_iframe, cleanup_htaccess) :
            f.extend(do_cleanup(parameters))
        print('\n'.join(f))
    elif operation=="autocleanup":
        do_autocleanup(parameters)
    elif operation=="iframe":
        cleanup_iframe(parameters)
    elif operation=="uneval":
        cleanup_eval(parameters)
    elif operation=="spamming":
        rc=do_spamming(parameters)
    elif operation=="newpassword":
        rc=do_newpassword(parameters)
    elif operation=="unhtaccess":
        cleanup_htaccess(parameters)
    elif operation=="notify_ftp":
        notify_ftp(parameters)
    elif operation=="drupal":
        drupal()
    elif operation=="drush":
        do_drush(parameters)
    elif operation=="phptest":
        do_phptest(parameters)
    elif operation=="rfc3514":
        do_rfc3514(parameters)
    elif operation=="Reached":
        do_rfc3514(parameters)
    elif operation=="login":
        do_login()
    elif operation=="strace":
        do_strace(parameters)
    elif operation=="pwtest":
        pwtest(parameters[0],parameters[1])
    elif operation in ("db", 'mysql','mysqldump','grant'):
        do_dbaccess(operation,parameters)
    elif operation in ("dbpass", ):
        do_dbpass(operation,parameters)
    elif operation=="secure":
        do_secure()
    elif operation=="check":
        do_secure(no_act=True)
    elif operation=="jce":
        do_jce()
    elif operation=="nocomment":
        do_nocomment()
    elif operation=="crack":
        res=dbaccess(show=False)
        do_crack(res)

    elif operation=="mv":
        mv(parameters[0],parameters[1])
    elif operation=="arping":
        arping()
    elif operation=="su":
        droppriv()
        if len(parameters):
            cmdline=[]
            for a in parameters: cmdline.append("'"+a.replace("'","'\\''")+"'")
            os.system(' '.join(cmdline))
        else:
            import tempfile
            tmp=tempfile.NamedTemporaryFile('w')
            tmp.write( "PS1='\\u@\\h [\\w]\\$ '\n" )
            tmp.flush()
            os.system("bash --rcfile "+tmp.name+" -i")

    elif operation=="panic":
        panic()
    elif operation=="crack2":
        john_dump()
    elif operation=="john":
        res=dbaccess(show=False, unknown_application='an unknown application')
        do_john(res,options.verbose,guesses=parameters)
    elif operation=="nice":
        niceness = int(options.number)
        if not niceness:
            niceness=14
        if niceness>19:
            ionice="ionice -c 3 ; "
        else:
            ionice='true';
        if len(parameters)==0:
            parameters = ['rdiff-backup','cpanellogd','rrdtool', 'logrunner','analog', ]
        pids=[]
        programs_list=[]
        for n in parameters:
            if n[:1] in '0123456789':
                pids.append(n)
            else:
                programs_list.append(n)
        programs=' '.join(programs_list)
        pidof=os.popen("pidof -x "+programs,'r').readline().strip().split()
        pids.extend(pidof)
        if len(pids):
            params = dict(
                ionice = ionice,
                pids = ' '.join(pids),
                niceness = niceness)
            print("# nicing these PIDs to prio %(niceness)s: %(pids)s" % params)
            os.system("%(ionice)s %(pids)s; renice %(niceness)s %(pids)s; ps %(pids)s" % params )
        else:
            print("# found nothing to nice")
    elif operation=="upgrade":
        do_upgrade()
        # do_upgrade_joomla(do_upgrade=True,do_password=False)
    elif operation=="joomla_password":
        res=dbaccess(show=False)
        do_upgrade_joomla(res,do_upgrade=False)
    elif operation=="ipcs":
        fix_ipcs()
    elif operation=="certs":
        do_certs()  # update the certificates on this box
    elif operation=="runphpinstall":
        runphpinstall()
    elif operation=="ereg_replace":
        do_patch_ereg_replace(parameters)
    elif operation=="patch":
        do_patch_ereg_replace(parameters)
        do_patch_upload(parameters)
        do_patch_timthumb(parameters)
    elif operation=="timthumb":
        do_patch_timthumb(parameters)
    elif operation=="remail":
        remail(parameters)
    elif operation=="lockmailbox":
        lockmailboxes(parameters)
    elif operation=="generosity":
        do_generosity(parameters)
    elif operation=="previewalias":
        previewalias(parameters)
    elif operation=="serveralias":
        serveralias(parameters)
    elif operation=="software":
        do_software_list(parameters)
    elif operation=="fcgiauthheaders":
        do_fcgiauthheaders(parameters)
    elif operation=="quarantine":
        do_script('quarantine',parameters)
    elif operation=="rblcheck":
        do_script('rblcheck',parameters)
    elif operation=="slow_query_log":
        do_slow_query_log(parameters)
    elif operation=="corrupt":
        do_corrupt(parameters)
    elif operation=="antibruteforce":
        do_antibruteforce(parameters)
    elif operation=="active_mailboxes":
        do_active_mailboxes(parameters)
    elif operation=="codesniffer":
        do_codesniffer(parameters)
    elif operation=="php54ini":
        do_php54ini(parameters)
    elif operation=="vmwaretools":
        do_vmwaretools(parameters)
    elif operation=="frob":
        do_frob(parameters)
    elif operation=="wp":
        do_wp(parameters)
    elif operation=="syslog":
        do_syslog(parameters)

    else:
        print("Usage:")
        print("Misc tools:")
        print("   frob fcgiphpini          # Set up link to custom php.ini in the user's home directory")
        print("   frob htaccess            # Fix up htaccess in the current dir to work with fcgi (EXPERIMENTAL)")
        print("   frob sessions            # Check and fix all mysql 'sessions' tables")
        print("   frob redirect url title  # Write an embedded frame redirect for the site")
        print("   frob disable files...    # Disable php files by adding <?php die(); ?> at the head")
        print("   frob undisable files...  # Make php files work by removing die() in the first line")
        print("   frob serveralias domain.test.afrihost.com # Add a host header for this site")
        print("Mod security:")
        # print "   frob nomodsec 'url'      # Turn off mod_security"
        print("   frob modsecrule 123 456  # Turn off specific mod_security rules")
        print("   frob modsec              # Turn on mod_security again")
        print("   frob modsecauto          # Experimental: show most recent site mod_sec violation, bypass and reload")
        print("Big hammer:")
        print("   frob rebuild             # Enter rules into httpd.conf and reload apache")
        print("   frob graceful            # Just reload apache")
        print("   frob killall             # kill apache, php and restart mysql, run ddos.py")
        print("User behaviour cluebats:")
        print("   frob cronfix             # Don't allow user cron jobs more frequently than 15 minutes")
        print("   frob mysqllimit user -n 30 # Set a limit of 30 sql connections for each user")
        print("   frob hacking             # Block access to current dir: set a password for user, password denied123")
        print("   frob takedown            # Take this site down due to a violation, e.g. ISPA")
        print("   frob notify_ftp ...      # Write up a warning message for the user about domain in /var/log/messages*")
        print("   frob drupal              # Hack evil modules out of drupal")
        print("   frob remail new/* | sh   # Remail mail in the new/ directory to whoever it was to")
        print("   frob generosity 'reason' # Set generous limits for this user")
        print("   frob fcgiauthheaders     # Pass Authorization header through to PHP for basic auth")
        print("Troubleshooting:")
        print("   frob strace user         # Hack /usr/local/cpanel/cgi-sys/php5 to strace PHP for user")
        print("   frob strace              # Remove strace hack from /usr/local/cpanel/cgi-sys/php5")
        print("   frob slow_query_log      # Find slow queries for this user")
        print("   frob pwtest user pass    # Test the password and print debugging")
        print("   frob ipcs                # Kill ipcs semaphores left lying around")
        print("   frob panic               # Configure this system to panic easily")
        print("   frob nice ...            # Make processes be nice - by default rdiff-backup and stuff")
        print("   frob mv src dst          # move a live dir src to dest and leave a link")
        print("   frob arping              # announce our IP addresses with arping -U")
        print("   frob vmwaretools         # install vmware tools")
        print("Security:")
        print("   frob su [ -- command ]   # Drop privileges and be the user of the current directory")
        print("   frob upgrade             # Upgrade joomla and change the passwords, or wordpress")
        print("   frob joomla_password     # Change the database passwords for joomla 1.5")
        print("   frob safe dir ...        # Set 755, 644 permissions")
        print("   frob images dir ...      # Set noscript in directory in .htaccess file")
        print("   frob securephp           # set safer php settings for insecure users (assumes /home/user/php.ini is used)")
        print("   frob software            # List installed software")
        print("   frob sanitise            # remove evil iframes, evals and htaccess bits from the files listed")
        print("   frob iframe ...          # remove evil iframes from the files listed")
        print("   frob uneval ...          # remove eval code from the files listed")
        print("   frob spamming u@dom      # kill a spamming mailbox and its mail")
        print("   frob newpassword         # set a new password for this account and send it to the contact e-mail")
        print("   frob corrupt file...     # corrupt a problematic file ")
        print("   frob antibruteforce      # null route hosts that are trying brute force logins")
        print("PHP randoms:")
        print("   frob db|mysql[dump]|grant# Show mysql parameters for the current application")
        print("   frob dbpass              # Set new DB password")
        print("   frob login               # Print a URL to log in to the web app")
        print("   frob codesniffer         # check php compatibility")
        print("   frob phpinfo             # Show phpinfo on the console for the current user")
        print("   frob php54ini            # set php.ini configuration settings for php 5.4")
        print("   frob drush ...           # run drush for a drupal site")
        print("   frob wp ..   .           # run wpcli for a wordpress site")
        print("   frob phptest file.php    # run a php in browser style")
        print("   frob rfc3514 http://url/k # remove disabled code so that the possibly evil code runs - do a manual review")
        print("Pass through:")
        print("   frob rblcheck [ip ...]   # See if this computer is listed in a DNSBL")
        print("   frob quarantine file...  # Move files, directories to quarantine")

    sys.exit(rc)
  
