'''
slash.py

Copyright 2006 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

'''

import core.controllers.outputManager as om

# options
from core.data.options.option import option
from core.data.options.optionList import optionList

import core.data.kb.knowledgeBase as kb

from core.controllers.basePlugin.baseDiscoveryPlugin import baseDiscoveryPlugin
from core.controllers.w3afException import w3afException
from core.controllers.misc.levenshtein import relative_distance_lt

from core.data.db.temp_persist import disk_list
from core.controllers.coreHelpers.fingerprint_404 import is_404


class slash( baseDiscoveryPlugin ):
    '''
    Identify if the resource http://host.tld/spam/ and http://host.tld/spam are the same.
    
    @author: Nicolas Rotta ( nicolas.rotta@gmail.com )  
    '''
    
    def __init__( self ):
        baseDiscoveryPlugin.__init__( self )
        self._already_visited = disk_list()
        
    def discover( self, fuzzableRequest ):
        '''
        Generates a new URL by adding or substracting the '/' character.      
        @parameter fuzzableRequest: A fuzzableRequest instance that contains (among other things) the URL to test.
        '''     
        self._fuzzableRequests = []
        
        url = fuzzableRequest.getURL()
        if url not in self._already_visited:
            self._already_visited.append( url )

            om.out.debug( 'slash plugin is testing: "' + fuzzableRequest.getURI() + '".' )
            
            fr = self._get_fuzzed_request( fuzzableRequest )
            original_response = self._urlOpener.GET( fuzzableRequest.getURL(), useCache = True )
                  
            targs = ( fr, original_response )
            self._tm.startFunction( target = self._do_request, args = targs , ownerObj = self )
           
            self._tm.join( self )
            self._already_visited.append( fr.getURI() )
                
        return self._fuzzableRequests

    def _get_fuzzed_request( self, fuzzableRequest ):
        '''
        Generate a new Url by adding or substracting the '/' character.
        @param fuzzableRequest: The original fuzzableRequest
        @return: The modified fuzzableRequest.
        '''
        fr = fuzzableRequest.copy()
        
        if ( fuzzableRequest.getURL().endswith( '/' ) ):
            fr.setURL( fuzzableRequest.getURL().rstrip( '/' ) )
        else:    
            fr.setURL( fuzzableRequest.getURL() + '/' )
            
        return fr
        
    def _do_request( self, fuzzableRequest, orig_resp ):
        '''
        Sends the request.
        @parameter fuzzableRequest: The fuzzable request object to modify.
        @parameter orig_resp: The response for the original request that was sent.
        '''
        try:
            resp = self._urlOpener.GET(fuzzableRequest.getURI(), useCache=True)
        except KeyboardInterrupt, e:
            raise e
        else:
            resp_body = resp.getBody()
            orig_resp = orig_resp.getBody()
            if relative_distance_lt(resp_body, orig_resp, 0.7) and \
                not is_404(resp):
                self._fuzzableRequests.extend(self._createFuzzableRequests(resp))
                om.out.debug('slash plugin found new URI: "' + fuzzableRequest.getURI() + '".')
        
    def getOptions( self ):
        '''
        @return: A list of option objects for this plugin.
        '''
        ol = optionList()
        return ol
        
    def setOptions( self, OptionList ):
        '''
        This method sets all the options that are configured using the user interface 
        generated by the framework using the result of getOptions().
        
        @parameter OptionList: A dictionary with the options for the plugin.
        @return: No value is returned.
        ''' 
        pass
    
    def getPluginDeps( self ):
        '''
        @return: A list with the names of the plugins that should be runned before the
        current one.
        '''
        return []
    
    def getLongDesc( self ):
        '''
        @return: A DETAILED description of the plugin functions and features.
        '''
        return '''
        Identify if the resource http://host.tld/spam/ and http://host.tld/spam are the same.      
        '''
