text: """
  autocomplete.py
  
  Copyright 2019 Andres Riancho
  
  This file is part of w3af, http://w3af.org/ .
  
  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 w3af.core.data.parsers.parser_cache as parser_cache
  
  from w3af.core.controllers.plugins.auth_session_plugin import AuthSessionPlugin
  from w3af.core.controllers.exceptions import BaseFrameworkException
  from w3af.core.data.options.opt_factory import opt_factory
  from w3af.core.data.options.option_list import OptionList
  from w3af.core.data.dc.factory import dc_from_form_params
  from w3af.core.data.options.option_types import URL as URL_OPT, STRING
  from w3af.core.data.parsers.doc.url import URL
  from w3af.core.data.request.fuzzable_request import FuzzableRequest
  
  
  class autocomplete(AuthSessionPlugin):
      """
      Fill and submit login forms
      """
  
      def __init__(self):
          AuthSessionPlugin.__init__(self)
  
          # User configured settings
          self.username = ''
          self.password = ''
          self.login_form_url = URL('http://host.tld/login')
          self.check_url = URL('http://host.tld/check')
          self.check_string = ''
  
      def login(self, debugging_id=None):
          """
          Login to the application:
              * HTTP GET `login_form_url`
              * Parse the HTML in `login_form_url` and find the login form
              * Fill the form with the user-configured credentials
              * Submit the form
          """
          #
          # In some cases the authentication plugin is incorrectly configured and
          # we don't want to keep trying over and over to login when we know it
          # will fail
          #
          if not self._attempt_login:
              return False
  
          # Create a new debugging ID for each login() run
          self._set_debugging_id(debugging_id)
          self._clear_log()
  
          msg = 'Logging into the application with user: %s' % self.username
          self._log_debug(msg)
  
          #
          # First we send the request to `login_form_url` and then we extract
          # the HTML form. If there are any problems in this step, just skip
          # the next calls to login()
          #
          form = self._get_login_form()
  
          if not form:
              self._handle_authentication_failure()
              return False
  
          #
          # Complete the parameters and send the form to the server
          #
          form_submitted = self._submit_form(form)
  
          if not form_submitted:
              self._handle_authentication_failure()
              return False
  
          #
          # Check if we're logged in
          #
          if self.has_active_session(debugging_id=debugging_id):
              self._handle_authentication_success(form)
              return True
  
          self._handle_authentication_failure()
          return False
  
      def logout(self):
          """
          User logout
          """
          return None
  
      def _handle_authentication_success(self, form):
          super(autocomplete, self)._handle_authentication_success()
  
          form_url = form.get_action().uri2url()
  
          args = (self.username, form_url)
          msg = 'Login success for username %s with form action %s'
          self._log_debug(msg % args)
  
          self._configure_audit_blacklist(form_url)
  
      def _submit_form(self, form_params):
          """
          Complete the username and password in the form fields and submit it
          to the server.
  
          :param form_params: The form parameters as returned by the HTML parser
          :return: True if form was submitted to the server
          """
          #
          # Create a form instance, using the proper encoding (multipart
          # or url). The form_params instance only has the parameters and can
          # not be sent to the wire.
          #
          form = dc_from_form_params(form_params)
  
          form.set_login_username(self.username)
          form.set_login_password(self.password)
  
          #
          # Transform to a fuzzable request and send to the wire
          #
          fuzzable_request = FuzzableRequest.from_form(form)
  
          try:
              http_response = self._uri_opener.send_mutant(fuzzable_request,
                                                           grep=False,
                                                           cache=False,
                                                           follow_redirects=True,
                                                           debugging_id=self._debugging_id)
          except Exception, e:
              msg = 'Failed to submit the login form: %s'
              self._log_debug(msg % e)
              return False
  
          msg = 'Login form sent to %s in HTTP request ID %s'
          args = (fuzzable_request.get_uri(), http_response.id,)
          self._log_debug(msg % args)
  
          self._log_http_response(http_response)
  
          return True
  
      def _get_login_form(self):
          """
          Parse the HTML returned from `login_form_url` and find the HTML form
          that can be used to login to the application.
  
          :return: A Form instance
          """
          #
          # Send the HTTP GET request to retrieve the HTML
          #
          try:
              http_response = self._uri_opener.GET(self.login_form_url,
                                                   grep=False,
                                                   cache=False,
                                                   follow_redirects=True,
                                                   debugging_id=self._debugging_id)
          except Exception as e:
              msg = 'Failed to HTTP GET the login_form_url: %s'
              self._log_debug(msg % e)
              return
  
          self._log_http_response(http_response)
  
          #
          # Extract the form from the HTML document
          #
          try:
              document_parser = parser_cache.dpc.get_document_parser_for(http_response)
          except BaseFrameworkException as e:
              msg = 'Failed to find a parser for the login_form_url: %s'
              self._log_debug(msg % e)
              return
  
          login_form = None
  
          for form_params in document_parser.get_forms():
              #
              # Find a form that:
              #
              #   * Is a login form
              #   * The action points to the target domain
              #   * This is the only login form in the page
              #
              if not form_params.is_login_form():
                  continue
  
              if form_params.get_action().get_domain() != self.login_form_url.get_domain():
                  continue
  
              if login_form is not None:
                  #
                  # There are two or more login forms in this page
                  #
                  self._log_debug('There are two or more login forms in the login_form_url.'
                                  ' This is not supported by the autocomplete authentication'
                                  ' plugin, will use the first identified form and ignore the'
                                  ' second one.')
                  continue
  
              login_form = form_params
  
          if login_form is None:
              msg = ('Failed to find an HTML login form at %s (id: %s).'
                     ' The authentication plugin is most likely incorrectly configured.')
              args = (self.login_form_url, http_response.id)
              self._log_error(msg % args)
  
              #
              # We get here when:
              #
              #   * The user configured the login form URL incorrectly
              #
              #   * There is an error in the HTTP request, and the HTTP response
              #     does NOT contain the login form.
              #
              # It is impossible to know in which case we are in, so we just return
              # None and wait for the next call to login(). The next call will act
              # as the retry strategy for the potential HTTP request / response error
              #
              # In the past we were setting self._attempt_login = False here, but
              # any errors (timeouts!) in the HTTP request to get the form ended
              # up in an ugly situation where the plugin was disabled
              #
              return None
  
          msg = 'Login form with action %s found in HTTP response with ID %s'
          args = (login_form.get_action(), http_response.id,)
          self._log_debug(msg % args)
  
          return login_form
  
      def _get_main_authentication_url(self):
          return self.login_form_url
  
      def get_options(self):
          """
          :return: A list of option objects for this plugin.
          """
          options = [
              ('username', self.username, STRING,
               'Username for the authentication process'),
  
              ('password', self.password, STRING,
               'Password for the authentication process'),
  
              ('login_form_url', self.login_form_url, URL_OPT,
               'The URL where the login form appears'),
  
              ('check_url', self.check_url, URL_OPT,
               'URL used to verify if the session is active. The plugin sends'
               ' an HTTP GET request to this URL and asserts if `check_string`'
               ' is present.'),
  
              ('check_string', self.check_string, STRING,
               'String to search in the `check_url` page to determine if the'
               ' session is active.'),
          ]
  
          ol = OptionList()
  
          for o in options:
              ol.add(opt_factory(o[0], o[1], o[3], o[2], help=o[3]))
  
          return ol
  
      def set_options(self, options_list):
          """
          This method sets all the options that are configured using
          the user interface generated by the framework using
          the result of get_options().
  
          :param options_list: A dict with the options for the plugin.
          :return: No value is returned.
          """
          self.username = options_list['username'].get_value()
          self.password = options_list['password'].get_value()
          self.check_string = options_list['check_string'].get_value()
          self.login_form_url = options_list['login_form_url'].get_value()
          self.check_url = options_list['check_url'].get_value()
  
          missing_options = []
  
          for o in options_list:
              if not o.get_value():
                  missing_options.append(o.get_name())
  
          if missing_options:
              msg = ('All plugin configuration parameters are required.'
                     ' The missing parameters are: %s')
              raise BaseFrameworkException(msg % ', '.join(missing_options))
  
      def get_long_desc(self):
          """
          :return: A DETAILED description of the plugin functions and features.
          """
          return """
          This authentication plugin can login to Web applications which use
          common authentication schemes, including those which use CSRF tokens.
  
          The plugin performs an HTTP GET request on `login_form_url` to obtain
          the HTML form parameters and values, fills the `username` and
          `password` fields and then submits the form (usually with HTTP POST)
          to authenticate the user. 
  
          The following configurable parameters exist:
              - username
              - password
              - login_form_url
              - check_url
              - check_string
          """
  