import re
import json
import subprocess

from bs4 import BeautifulSoup

from lib.utils.utils import appendQueries,iterate,connect,package

def absolute(myrange,domain,url,append):
    if not append:
        return None
    if re.search("^//",append) != None:
        return None

    # If don't have http or https prefix
    if re.search("^https?://",append) == None:
        # absolute url
        if append[0] == '/':
            append = domain + '/' + append.strip('/')
        # relative url
        else:
            append = url + '/' + append.strip('/')
    append = append.partition('#')[0].strip('/')

    if myrange == "domain" and domain not in append:
        append = None
    if myrange == "subdomain" and url not in append:
        append = None
    if myrange == "page":
        append = None
    return append

def page(mission,data,urllist):
    soup = BeautifulSoup(data['response'],'lxml')
    urls = soup.find_all('a')
    tasks = []
    for url in urls:
        url = absolute(mission["range"],mission["domain"],mission["url"],url.get('href'))
        if url and url not in urllist:
            urllist.append(url)
            tasks += generate(mission,url)
    return tasks

def mutate(mutations,initials,mode):
    results = []
    statusTotal = mutations ** len(initials)
    for status in xrange(statusTotal):
        now = []
        for initial in initials:
            now.append(iterate(initial,status % mutations,mode))
            status /= mutations
        results.append(now)
    return results

def generate(mission,url):
    tasks = []
    initials = []
    url = appendQueries(url,mission.get('stable-query'))
    header = mission.get('stable-header')
    postdata = mission.get('stable-postdata')
    for key,value in mission["mutable-query"].iteritems():
        initials.append(value)
    for key,value in mission['mutable-header'].iteritems():
        initials.append(value)
    for key,value in mission['mutable-postdata'].iteritems():
        initials.append(value)
    results = mutate(mission["mutations"],initials,"default")
    for result in results:
        now = 0
        task = {"type" : "page", "url" : url, "header" : header, "postdata" : postdata}
        for key,value in mission["mutable-query"].iteritems():
            task["url"] = appendQueries(task["url"],result[now])
            now += 1
        for key,value in mission['mutable-header'].iteritems():
            task["header"][key] = result[now]
            now += 1
        for key,value in mission['mutable-postdata'].iteritems():
            task["postdata"][key] = result[now]
            now += 1
        tasks.append(task)
    return tasks

def fuzz(mission,data,urllist):
    tasks = []
    soup = BeautifulSoup(data["response"],'lxml')
    forms = soup.find_all("form")
    for form in forms:
        url = absolute(mission["range"],mission["domain"],mission["url"],form.get("action"))
        if not url:
            continue
        method = form.get("method").lower()
        keys = [i["name"] for i in form.find_all("input") + form.find_all("select") if i.get("name")]
        results = mutate(5,[""] * len(keys),"sqli")
        task = []
        for result in results:
            queries = {}
            for i,key in enumerate(keys):
                queries[key] = result[i]
            if method == "get":
                task.append({"type" : "fuzz", "url" : appendQueries(url,queries), "header" : {}, "postdata" : {}})
            else:
                task.append({"type" : "fuzz", "url" : url, "header" : {}, "postdata" : queries})
        tasks += task
    return tasks

def analyze(mission,data):
    results = []
    soup = BeautifulSoup(data["response"],'lxml')

    # iframes CSRF detection
    iframes = soup.find_all("iframe")
    for iframe in iframes:
        url = iframe.get("src")
        if url and "https://www.googletagmanager.com" not in url and iframe.get("width") == 0 and iframe.get("height") == 0:
            results.append(url + " -> an iframe contains this url and has width and height both 0 ( might be CSRF )")

    # images CSRF detection
    images = soup.find_all("img")
    for image in images:
        url = image.get("src")
        if url:
            page,info = connect(url)
        if info.get("Content-Type") and "image" not in info["Content-Type"]:
            results.append(url + " -> this image didn't response with image, it response with type " + info["Content-Type"] + " ( might be CSRF )")

    return [package("vulnerability",data,results)]

def collect(mission,data):
    results = []
    if mission.get("css-selector"):
        soup = BeautifulSoup(data["response"],'lxml')
        items = soup.select(mission["css-selector"])
        results = [package("collection",data,[item.string for item in items if item.string])]
    return results

def version(mission,data,versionlist):
    results = []
    if data["response-header"].get("server") and data["response-header"]["server"] not in versionlist:
        servers = []
        for server in data["response-header"]["server"].split():
            server = server.strip()
            if not ( server[0] == '(' and server[-1] == ')' ):
                _ = server.partition('/')
                servers.append((_[0],_[2]))
        for server in servers:
            cve = json.loads(subprocess.check_output(["searchsploit",server[0] + ' ' + server[1],"-wj"]))
            print cve
        results = [package("vulnerability",data,"The server type and version : " + data["response-header"]["server"] + " which has " + str(len(cve["RESULTS"])) + " cve exploits found!")]
    return results

@@ -4921,65 +4921,8 @@
 er%22)
- and data%5B%22response-header%22%5D%5B%22server%22%5D not in versionlist
 :%0A  
@@ -5186,13 +5186,29 @@
 _%5B0%5D
-,_%5B2%5D
+.lower(),_%5B2%5D.lower()
 ))%0A 
@@ -5253,132 +5253,372 @@
     
-cve = json.loads(subprocess.check_output(%5B%22searchsploit%22,server%5B0%5D + ' ' + server%5B1%5D,%22-wj%22%5D))%0A            print cve%0A
+if server in versionlist:%0A                continue%0A            versionlist.append(server)%0A            cve = subprocess.check_output(%5B%22searchsploit%22,server%5B0%5D + ' ' + server%5B1%5D,%22-wj%22%5D)%0A            cve = cve%5Bcve.find('%5B')+1:cve.find('%5D')%5D.split('%5Cn')%0A            cve = %5B (_.split('%7C')%5B0%5D.strip(),_.split('%7C')%5B1%5D.strip()) for _ in cve if _.find('%7C') != -1 %5D%0A 
         
 resu
@@ -5609,35 +5609,42 @@
  %5D%0A         
+   
 results
- = %5B
+.append(
 package(%22vul
@@ -5762,19 +5762,8 @@
 (cve
-%5B%22RESULTS%22%5D
 )) +
@@ -5786,17 +5786,17 @@
 found!%22)
-%5D
+)
 %0A    ret

version almost complete