#!/usr/bin/env python


import logging
import md5
import socket
import urllib2
import time

from lib.resume import Resume


class Request(Resume):
    """
    This class defines methods used to perform HTTP requests

    @author: Bernardo Damele
    """

    def __init__(self):
        self.logger = logging.getLogger("sqlmapLog")
        socket.setdefaulttimeout(15)


    def encodeParams(self, params):
        """
        This method encodes and returns the url parameters special
        characters in their corresponding HTML hexadecimal value
        """

        charList = {
                     " ": "%20", "!": "%21", '"': "%22", "#": "%23",
                     "$": "%24", "'": "%27", "(": "%28", ")": "%29",
                     "*": "%2A", "+": "%2B", ",": "%2C", "-": "%2D",
                     ".": "%2E", "/": "%2F", ":": "%3A", ";": "%3B",
                     "<": "%3C", ">": "%3E", "?": "%3F", "@": "%40",
                     "[": "%5B", "\\": "%5C", "]": "%5D", "^": "%5E",
                     "_": "%5F", "`": "%60", "{": "%7B", "|": "%7C",
                     "}": "%7D", "~": "%7E",
                     # "%": "%25", "&": "%26", "=": "%3D"
                   }

        for char, encChar in charList.items():
            params = params.replace(char, encChar)

        return params


    def getPage(self, url):
        """
        This method connects to the target url or proxy and returns the
        target url page content
        """

        debugMsg = "requesting url: %s" % url
        self.logger.debug(debugMsg)

        url, params = url.split("?", 1)
        params = self.encodeParams(params)
        url = "%s?%s" % (url, params)

        try:
            if self.args.httpMethod == "GET":
                conn = urllib2.urlopen(url)
            elif self.args.httpMethod == "POST":
                url, params = url.split("?")
                conn = urllib2.urlopen(url, params)

            page = conn.read()
        except urllib2.HTTPError, e:
            page = e.read()
        except:
            warnMsg = "unable to connect to the target url"

            if self.args.googleDork:
                warnMsg += ", skipping to next url"
                self.logger.warn(warnMsg)

                return None
            else:
                warnMsg += " or proxy"
                raise Exception, warnMsg

        return page


    def queryPage(self, url):
        """
        This method calls self.getPage() function to get the target url
        page content and returns its page MD5 hash or a boolean value
        in case of string match check ('--string' command line
        parameter)
        """

        page = self.getPage(url)

        if not self.args.string:
            return md5.new(page).hexdigest()
        elif self.args.string in page:
            return True
        else:
            return False


    def getValue(self, expression):
        """
        This method retrieves the output of a SQL statement character
        by character taking advantage of a blind SQL injection
        vulnerability on the afftected url parameter. The algorithm
        used is a bisection algorithm defined into
        algorithm.Algorithm()
        """

        logMsg = "query: %s" % expression
        self.logger.info(logMsg)

        start = time.time()
        expressionUnescaped = self.unescape(expression)
        evilStm = self.createStm()
        baseUrl = self.urlReplace(newValue=evilStm)

        if self.args.resumedQueries:
            if self.args.url in self.args.resumedQueries.keys():
                if expression in self.args.resumedQueries[self.args.url].keys():
                    value = self.resumeValue(expression, baseUrl)

                    if value:
                        return value.replace("__NEWLINE__", "\n").replace("__TAB__", "\t")

        if self.args.writeFile:
            self.args.writeFile.write("%s][%s][" % (self.args.url, expression))
            self.args.writeFile.flush()

        count, value = self.bisectionAlgorithm(baseUrl, expressionUnescaped)
        duration = int(time.time() - start)

        logMsg = "performed %d queries in %d seconds" % (count, duration)
        self.logger.info(logMsg)

        return value

