'''
httpRequestParser.py

Copyright 2008 Andres Riancho

This file is part of w3af, w3af.sourceforge.net .

w3af is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

w3af is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with w3af; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

'''

from core.data.request.frFactory import createFuzzableRequestRaw
from core.controllers.w3afException import w3afException


def httpRequestParser(head, postdata):
    '''
    This function parses HTTP Requests from a string to a fuzzableRequest.
    
    @parameter head: The head of the request.
    @parameter postdata: The post data of the request
    @return: A fuzzableRequest object with all the corresponding information that was sent in head and postdata
    
    @author: Andres Riancho ( andres.riancho@gmail.com )
    '''
    def checkVersionSintax( version ):
        '''
        @return: True if the sintax of the version section of HTTP is valid; else raise an exception.
        '''
        splittedVersion = version.split('/')
        if len(splittedVersion) != 2:
            # Invalid!
            msg = 'The HTTP request has an invalid version token: "' + version +'"'
            raise w3afException( msg )
        elif len(splittedVersion) == 2:
            if splittedVersion[0].lower() != 'http':
                msg = 'The HTTP request has an invalid HTTP token in the version specification: "'
                msg += version + '"'
                raise w3afException( msg )
            if splittedVersion[1] not in ['1.0', '1.1']:
                raise w3afException('HTTP request version "' + version + '" is unsupported')
        return True
    
    def checkURISintax( uri ):
        '''
        @return: True if the sintax of the URI section of HTTP is valid; else raise an exception.
        '''
        if uri.startswith('http://') and len(uri) != len('http://'):
            return True
        elif uri.startswith('https://') and len(uri) != len('https://'):
            return True
        else:
            msg = 'You have to specify the complete URI, including the protocol and the host.'
            msg += ' Invalid URI: ' + uri
            raise w3afException( msg )
    
    # parse the request head
    splittedHead = head.split('\n')
    splittedHead = [ h.strip() for h in splittedHead if h ]
    
    # Get method, uri, version
    metUriVer = splittedHead[0]
    firstLine = metUriVer.split(' ')
    if len(firstLine) == 3:
        # Ok, we have something like "GET / HTTP/1.0"
        # Or something like "GET /hello+world.html HTTP/1.0"
        # This is the best case for us!
        method, uri, version = firstLine
        checkURISintax(uri)
        checkVersionSintax(version)
    elif len(firstLine) < 3:
        # Invalid!
        msg = 'The HTTP request has an invalid <method> <uri> <version> token: "'
        msg += metUriVer +'".'
        raise w3afException( msg )
    elif len(firstLine) > 3:
        # This is mostly because the user sent something like this:
        # GET /hello world.html HTTP/1.0
        # Note that the correct sintax is:
        # GET /hello+world.html HTTP/1.0
        # or
        # GET /hello%20world.html HTTP/1.0
        # Mostly because we are permissive... we are going to try to send the request...
        method = firstLine[0]
        version = firstLine[-1]
        checkVersionSintax(version)
        
        # If we get here, it means that we may send the request after all...
        # FIXME: Should I encode here?
        # FIXME: Should the uri be http://host + uri ?
        uri = ' '.join( firstLine[1:-1] )
        checkURISintax(uri)
        
    # If we got here, we have a nice method, uri, version first line
    # Now we parse the headers (easy!) and finally we send the request
    headers = splittedHead[1:]
    headersDict = {}
    for header in headers:
        oneSplittedHeader = header.split(':')
        if len(oneSplittedHeader) == 2:
            headersDict[ oneSplittedHeader[0].strip() ] = oneSplittedHeader[1].strip()
        elif len(oneSplittedHeader) == 1:
            raise w3afException('The HTTP request has an invalid header: "' + header + '"')
        elif len(oneSplittedHeader) > 2:
            headerValue = ' '.join(oneSplittedHeader[1:]).strip()
            headersDict[ oneSplittedHeader[0].strip() ] = headerValue
    
    fuzzReq = createFuzzableRequestRaw(method, uri, postdata, headersDict )
    return fuzzReq
