import os
import urllib
import urlparse

from quixote import redirect, get_request, get_publisher
from quixote.directory import Directory

import lasso

from larpe.qommon import get_cfg
from larpe.qommon.form import *
from larpe.qommon.misc import http_get_page, get_abs_path

import site_authentication
from larpe import errors
from larpe import misc
from larpe.hosts import Host
from larpe.admin.liberty_utils import *
#from larpe.filter import filter_misc
from larpe.admin.apache import write_apache2_vhosts

from menu import *

def check_basic_configuration(form):
    get_publisher().reload_cfg()
    # Check reversed_hostname and reversed_directory
    reversed_hostname = form.get_widget('reversed_hostname').parse()
    reversed_directory = form.get_widget('reversed_directory').parse()
    if reversed_hostname == get_publisher().cfg['proxy_hostname'] and not reversed_directory:
        form.set_error('reversed_hostname',
            _('You must either choose a different hostname from Larpe or specify a reversed directory'))

def check_minimal_configuration(form):
    # Check auth_url and auth_form_page_url
    auth_url = form.get_widget('auth_url').parse()
    auth_form_page_url = form.get_widget('auth_form_page_url').parse()
    if auth_url and auth_form_page_url:
        form.set_error('auth_form_page_url',
            _('"Authentication page" and "Authentication form page" are incompatible. Only fill one of them.'))

def convert_label_to_name(label):
    '''Build host name from host label'''
    name = label.lower()
    invalid_characters = [' ', "'"]
    for char in invalid_characters:
        name = name.replace(char, '_')
    return name

class DictWidget(Widget):
    def render_content [html] (self):
        self.render_br = False
        if self.value['enabled'] is True:
            htmltag('input', xml_end=True, type='checkbox', name=self.name + '_enabled', checked='checked')
        else:
            htmltag('input', xml_end=True, type='checkbox', name=self.name + '_enabled')
        ' ' + self.name + ' &nbsp;'
        htmltag('input', xml_end=True, type='text', name=self.name, value=self.value['value'], size='35', **self.attrs)

    def _parse(self, request):
        enabled = request.form.get(self.name + '_enabled')
        value = request.form.get(self.name)
        self.value = { 'enabled': enabled, 'value': value }

class ConfigurationAssistant(Directory):
    _q_exports = ['start', 'check_new_address', 'modify_site_address_and_name', 'authentication_and_logout_adresses',
                  'check_auto_detected_configuration', 'credentials', 'check_authentication', 'send_authentication_request',
                  'see_authentication_response', 'see_response_html_page', 'authentication_success_criteria',
                  'modify_authentication_request', 'auth_request_post_parameters', 'auth_request_http_headers',
                  'sso_init_link', 'metadatas', 'check_full_configuration']

    def __init__(self, host):
        self.host = host

    def start [html] (self):
        # Check the global domain name has been previously set
        get_publisher().reload_cfg()
        if not get_cfg('domain_names') or not get_cfg('domain_names')[0]:
            html_top('preamble', title=_('Need domain name configuration'))
            return htmltext(_('Before configuring hosts, you must <a href="../../../settings/domain_names">setup a global domain name</a> in %(settings)s menu.') % { 'settings': _('Settings') })

        form = self.form_start()

        if form.get_widget('cancel').parse():
            return redirect('../..')

        connection_failure = None
        if form.is_submitted() and not form.has_errors():
            try:
                self.submit_start_form(form)
            except Exception, e:
                connection_failure = e
            else:
                return redirect('check_new_address')

        html_top('step1', title=_('Step 1 - Basic configuration'))
        '<h2>%s</h2>' % _('Step 1 - Basic configuration')

        if connection_failure:
            '<div class="errornotice">%s</div>' % connection_failure

        form.render()

    def form_start(self):
        form = Form(enctype='multipart/form-data')
        form.add(UrlWidget, 'orig_site', title = _('Original site root address'), required = True,
                size = 50, value = self.host.orig_site,
                hint = _('If your site address is http://test.org/index.php, put http://test.org/ here'))
        get_publisher().reload_cfg()
        if get_cfg('use_proxy'):
            form.add(CheckboxWidget, 'use_proxy', title = _('Use a proxy'),
                    hint = _("Uncheck it if Larpe doesn't need to use the proxy to connect to this site"),
                    value = self.host.use_proxy)
        else:
            form.add(HtmlWidget, htmltext('<p>%s</p>' % \
                _('If Larpe needs to use a proxy to connect to this site, you must first configure it in <a href="../../../settings/proxy">global proxy parameters</a>.')))
        form.add_submit('cancel', _('Cancel'))
        form.add_submit('submit', _('Next'))
        return form

    def submit_start_form(self, form):
        fields = ['orig_site']
        if get_cfg('use_proxy'):
            fields += ['use_proxy']
        for f in fields:
            setattr(self.host, f, form.get_widget(f).parse())

        # If no proxy is setup yet, set use_proxy to False for new hosts in case a proxy is later configured
        if not get_cfg('use_proxy'):
            self.host.use_proxy = False

        # Remove what is after the last '/'
        #self.host.orig_site = '/'.join(self.host.orig_site.split('/')[:-1])

        html_page = self.get_data_after_redirects(self.host.orig_site)

        if not self.host.label:
            # Look for html title in original site index page
            regexp = re.compile("""<title.*?>(.*?)</title>""", re.DOTALL | re.IGNORECASE)
            title = regexp.findall(html_page)
            if title:
                self.host.label = title[0]
            # If another site already uses this site title, add trailings "_" until we find an available name
            existing_label = True
            while existing_label is True:
                for any_host in Host.select():
                    if any_host.id != self.host.id and self.host.label == any_host.label:
                        self.host.label += '_'
                        break
                else:
                    existing_label = False

            # Fill host.name attribute
            self.host.name = convert_label_to_name(self.host.label)

        if not self.host.scheme:
            # Get tokens from orig site url
            orig_scheme, rest = urllib.splittype(self.host.orig_site)
            orig_host, rest = urllib.splithost(rest)

            get_publisher().reload_cfg()
            # Set url scheme (HTTP or HTTPS)
            # TODO: Handle the option "Both"
            if get_cfg('sites_url_scheme'):
                self.host.scheme = get_cfg('sites_url_scheme')
            else:
                self.host.scheme = orig_scheme

        if not self.host.reversed_hostname:
            # Build a new domain name
            short_name = orig_host.split('.')[0]
            if short_name == 'www':
                short_name = orig_host.split('.')[1]
            self.host.reversed_hostname = '%s.%s' % (short_name, get_cfg('domain_names')[0])
            # If another site already uses this domain name, add some trailing "_" until we find an available name
            existing_domain = True
            while existing_domain is True:
                for any_host in Host.select():
                    if any_host.id != self.host.id and self.host.reversed_hostname == any_host.reversed_hostname:
                        self.host.reversed_hostname += '-'
                        break
                else:
                    existing_domain = False
            self.host.reversed_directory = None

        if not self.host.new_url:
            # New url for this host
            self.host.new_url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
            # FIXME: Check if the new domain name already exists

            # New url for this host
            #        self.host.new_url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
            #        if self.host.reversed_directory is not None:
            #            self.host.new_url += '%s/' % self.host.reversed_directory

        self.host.store()
        write_apache2_vhosts()

    # XXX: Should use the FancyURLopener class instead when it supports proxies
    def get_data_after_redirects(self, start_url):
        if not start_url:
            return ''
        status = 302
        location = None
        while status // 100 == 3:
            if location is None:
                url = start_url
            elif location.startswith('http'):
                # Location is an absolute path
                url = location
            else:
                # Location is a relative path
                url = urlparse.urljoin(start_url, location)
            response, status, data, auth_headers = http_get_page(url, use_proxy=self.host.use_proxy)
            location = response.getheader('Location', None)
        return data

    def create_dirs(self):
        # Hack : sites must use the configuration which is stored in main Larpe directory,
        # but they need to have a directory named with their hostname, which will contain the
        # main domain name for Larpe so they know where is the main configuration
        hostname_dir = get_abs_path(os.path.join('..', self.host.reversed_hostname))
        if not os.path.exists(hostname_dir):
            os.mkdir(hostname_dir)
        # Load the configuration from the main directory
        get_publisher().reload_cfg()
        # Write it in the site directory
        get_publisher().write_cfg(hostname_dir)

        # Storage directories
        if not self.host.reversed_directory:
            reversed_dir = 'default'
        else:
            reversed_dir = self.host.reversed_directory
        self.host.site_dir = \
            os.path.join(get_publisher().app_dir, 'sp', self.host.reversed_hostname, reversed_dir)
        user_dir = os.path.join(self.host.site_dir, 'users')
        token_dir = os.path.join(self.host.site_dir, 'tokens')
        filter_dir = os.path.join(self.host.site_dir, 'filters')
        for dir in (self.host.site_dir, user_dir, token_dir, filter_dir):
            if not os.path.isdir(dir):
                os.makedirs(dir)

    def generate_ssl_keys(self):
        # Generate SSL keys
        private_key_path = os.path.join(self.host.site_dir, 'private_key.pem')
        public_key_path = os.path.join(self.host.site_dir, 'public_key.pem')
        if not os.path.isfile(private_key_path) or not os.path.isfile(public_key_path):
            set_provider_keys(private_key_path, public_key_path)
        self.host.private_key = private_key_path
        self.host.public_key = public_key_path

    def generate_metadatas(self):
        metadata_cfg = {}

        # Organization name
        self.host.organization_name = self.host.label
        metadata_cfg['organization_name'] = self.host.organization_name

        # Base URL
        base_url = '%s://%s%s/liberty/%s/liberty' % (self.host.scheme,
                                                     self.host.reversed_hostname,
                                                     get_request().environ['SCRIPT_NAME'],
                                                     self.host.name)
        metadata_cfg['base_url'] = base_url
        self.host.base_url = base_url

        if lasso.SAML2_SUPPORT:
            saml2_base_url = '%s://%s%s/saml/%s/saml' % (self.host.scheme,
                                                         self.host.reversed_hostname,
                                                         get_request().environ['SCRIPT_NAME'],
                                                         self.host.name)
            metadata_cfg['saml2_base_url'] = saml2_base_url
            self.host.saml2_base_url = saml2_base_url

        # Provider Id
        provider_id = '%s/metadata' % base_url
        metadata_cfg['provider_id'] = provider_id
        self.host.provider_id = provider_id

        if lasso.SAML2_SUPPORT:
            saml2_provider_id = '%s/metadata' % saml2_base_url
            metadata_cfg['saml2_provider_id'] = saml2_provider_id
            self.host.saml2_provider_id = saml2_provider_id

        # Read public key
        public_key = ''
        if self.host.public_key is not None and os.path.exists(self.host.public_key):
            metadata_cfg['signing_public_key'] = open(self.host.public_key).read()

        # Write metadatas
        metadata_path = os.path.join(self.host.site_dir, 'metadata.xml')
        open(metadata_path, 'w').write(get_metadata(metadata_cfg))
        self.host.metadata = metadata_path

        if lasso.SAML2_SUPPORT:
            saml2_metadata_path = os.path.join(self.host.site_dir, 'saml2_metadata.xml')
            open(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg))
            self.host.saml2_metadata = saml2_metadata_path

    def check_new_address [html] (self):
        form = Form(enctype='multipart/form-data')
        form.add_submit('cancel', _('Previous'))
        form.add_submit('submit', _('Next'))

        if form.get_widget('cancel').parse():
            return redirect('start')

        if form.is_submitted():
            self.create_dirs()
            if self.host.private_key is None:
                self.generate_ssl_keys()
            self.generate_metadatas()
            self.host.store()
            return redirect('authentication_and_logout_adresses')

        html_top('step2', title=_('Step 2 - Check the new site address works'))
        '<h2>%s</h2>' % _('Step 2 - Check the new site address works')

        htmltext(_('''\
<p>Before opening the following link, ensure you have configured your DNS for this address. If you don't 
have a DNS server and you just want to test Larpe, add this domain name in the file "/etc/hosts".</p>
<p>Then you can open this link in a new window or tab and see if your site is displayed. If it's ok, 
you can click the "%(next)s" button. Otherwise, click the "%(previous)s" button and check your settings.</p>
''') % {'next': _('Next'), 'previous': _('Previous')})
        '<p>'
        htmltext(_('The new address of this site is '))
        '<a href="%s">%s</a><br/>' % (self.host.new_url, self.host.new_url)
        htmltext(_('The name of this site is "%s".') % self.host.label)
        '</p><p>'
        htmltext(_('You can also <a href="modify_site_address_and_name">modify the address or the name of this site</a>'))
        '</p>'

        form.render()

    def modify_site_address_and_name [html] (self):
        form = self.form_modify_site_address_and_name()

        if form.get_widget('cancel').parse():
            return redirect('check_new_address')

        if form.is_submitted() and not form.has_errors():
            label = form.get_widget('label').parse()
            name = convert_label_to_name(label)
            for any_host in Host.select():
                if any_host.id != self.host.id and name == any_host.name:
                    form.set_error('label', _('An host with the same name already exists'))
                    break

        if form.is_submitted() and not form.has_errors():
            self.submit_modify_site_address_and_name_form(form)
            return redirect('check_new_address')

        html_top('modify_site_address_and_name', title=_('Modify site address and name'))
        '<h2>%s</h2>' % _('Modify site address and name')
        form.render()

    def form_modify_site_address_and_name(self):
        form = Form(enctype='multipart/form-data')
        form.add(UrlWidget, 'new_url', title = _('Address'), required = True,
                size = 50, value = self.host.new_url)
        form.add(StringWidget, 'label', title = _('Name'), required = True,
                size = 50, value = self.host.label)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form

    def submit_modify_site_address_and_name_form(self, form):
        fields = ['new_url', 'label']
        for f in fields:
            setattr(self.host, f, form.get_widget(f).parse())

        # Split url to retrieve components
        tokens = urlparse.urlparse(self.host.new_url)
        self.host.scheme = tokens[0]
        self.host.reversed_hostname = tokens[1]
        self.host.reversed_directory = tokens[2]
        if self.host.reversed_directory.startswith('/'):
            self.host.reversed_directory = self.host.reversed_directory[1:]

        # Fill host.name attribute
        self.host.name = convert_label_to_name(self.host.label)

        self.host.store()
        write_apache2_vhosts()

    def authentication_and_logout_adresses [html] (self):
        form = self.form_authentication_and_logout_adresses()

        if form.get_widget('cancel').parse():
            return redirect('check_new_address')

        if form.is_submitted() and not form.has_errors():
            self.submit_authentication_and_logout_adresses_form(form)
            return redirect('check_auto_detected_configuration')

        html_top('step3', title=_('Step 3 - Configure authentication and logout pages'))
        '<h2>%s</h2>' % _('Step 3 - Configure authentication and logout pages')
        form.render()

    def form_authentication_and_logout_adresses(self):
        form = Form(enctype='multipart/form-data')
        form.add(ValidUrlWidget, 'auth_url', title = _('Authentication form page address'),
                hint = _('Address of a page on the site which contains the authentication form'),
                required = True, size = 50, value = self.host.auth_url)
        form.add(ValidUrlWidget, 'logout_url', title = _('Logout address'), required = False,
                hint = _('Address of the logout link on the site'),
                size = 50, value = self.host.logout_url)
        form.add_submit('cancel', _('Previous'))
        form.add_submit('submit', _('Next'))
        return form

    def submit_authentication_and_logout_adresses_form(self, form):
        fields = ['auth_url', 'logout_url']
        for f in fields:
            setattr(self.host, f, form.get_widget(f).parse())
        self.host.auth_form_url = self.host.auth_url

        if not self.host.http_headers:
            self.host.http_headers = {
                'Content-Type': { 'enabled': True, 'value': 'application/x-www-form-urlencoded', 'immutable': False },
                'X-Forwarded-For': { 'enabled': True, 'value': _('(computed automatically)'), 'immutable': True },
                'X-Forwarded-Host': { 'enabled': True, 'value': self.host.reversed_hostname, 'immutable': False },
            }

        self.auto_detect_configuration()
        self.host.store()
        write_apache2_vhosts()

    def generate_apache_filter (self):
        # Set Python filter path for Apache configuration
        if not hasattr(self.host, 'apache_python_paths'):
            self.host.apache_python_paths = []
        python_path = os.path.join(self.host.site_dir, 'filters')
        if python_path not in self.host.apache_python_paths:
            self.host.apache_python_paths.append(python_path)

        # Write Python filter
        python_file = open(os.path.join(self.host.site_dir, 'filters', 'output_replace_form.py'), 'w')
        python_file.write(open(os.path.join(get_publisher().data_dir, 'output_filter_base.py'), 'r').read())
        python_file.write('''\
def filter_page(filter, page):
    current_form = re.compile('<form .*?action="%(auth_form_action)s".*?>.*?</form>', re.DOTALL)
    return current_form.sub('<form method="post" action="/liberty/%(name)s/login"><input type="submit" value="Connexion" /></form>', page)
''' % { 'auth_form_action': self.host.auth_form_action, 'name': self.host.name })
        python_file.close()

        # Set Python filter for Apache configuration
        if not hasattr(self.host, 'apache_output_python_filters'):
            self.host.apache_output_python_filters = []
        if not 'output-replace-form' in self.host.apache_output_python_filters:
            self.host.apache_output_python_filters.append('output_replace_form')

    def check_auto_detected_configuration [html] (self):
        form = Form(enctype='multipart/form-data')
        form.add_submit('cancel', _('Previous'))
        form.add_submit('submit', _('Next'))

        if form.get_widget('cancel').parse():
            return redirect('authentication_and_logout_adresses')

        if form.is_submitted():
            return redirect('credentials')

        html_top('step4', title=_('Step 4 - Check automatically detected configuration for the authentication form'))
        '<h2>%s</h2>' % _('Step 4 - Check automatically detected configuration for the authentication form')

        host_attrs = (
            ('auth_check_url', _('Address where the authentication form must be sent')),
            ('login_field_name', _('Name of the login field')),
            ('password_field_name', _('Name of the password field')),
        )

        html_fields = ''
        success = True
        for attr, name in host_attrs:
            color = 'black'
            if attr in ('auth_check_url', 'login_field_name', 'password_field_name') and \
                    not getattr(self.host, str(attr)):
                color = 'red'
                success = False
            html_fields += '<div style="margin-bottom: 0.1em; font-weight: bold; color: %s;">%s</div>' % (color, name)
            html_fields += '<div style="margin-left: 1em; margin-bottom: 0.3em; color: %s;">%s</div>' % \
                (color, getattr(self.host, str(attr)))
            if getattr(self.host, str(attr)) == '':
                html_fields += '<br />'

        '<p>'
        if success:
            htmltext(_('''\
The following authentication form parameters have been detected. If they look right, you can go to the next step.
If you think they are wrong, go back and check your settings then try again.
'''))
        else:
            htmltext(_('''\
The following authentication form parameters in red haven't been correctly detected. Go back and check
your settings then try again.
'''))
        '</p>'

        html_fields
        form.render()

    def credentials [html] (self):
        form = self.form_credentials()

        if form.get_widget('cancel').parse():
            return redirect('check_auto_detected_configuration')

        if form.is_submitted() and not form.has_errors():
            self.submit_credentials_form(form)
            return redirect('send_authentication_request')

        html_top('step5', title=_('Step 5 - Fill in a valid username/password for this site'))
        '<h2>%s</h2>' % _('Step 5 - Fill in a valid username/password for this site')
        form.render()

    def form_credentials(self):
        form = Form(enctype='multipart/form-data')
        form.add(StringWidget, 'username', title = _('Username'), required = True,
                size = 30, value = self.host.valid_username)
        form.add(PasswordWidget, 'password', title = _('Password'), required = True,
                size = 30, value = self.host.valid_password)
        for name, values in self.host.select_fields.iteritems():
            options = []
            if values:
                for value in values:
                    options.append(value)
                form.add(SingleSelectWidget, name, title = name.capitalize(),
                    value = values[0], options = options)
        form.add_submit('cancel', _('Previous'))
        form.add_submit('submit', _('Next'))
        return form

    def submit_credentials_form(self, form):
        self.host.valid_username = form.get_widget('username').parse()
        self.host.valid_password = form.get_widget('password').parse()
        self.host.valid_select = {}
        for name, values in self.host.select_fields.iteritems():
            if form.get_widget(name):
                self.host.valid_select[name] = form.get_widget(name).parse()

        self.host.store()

    def send_authentication_request(self):
        site_auth = site_authentication.get_site_authentication(self.host)
        self.host.auth_request_status, self.host.auth_request_data = site_auth.local_auth_check_dispatch(
            self.host.valid_username, self.host.valid_password, self.host.valid_select)
        self.host.auth_request_success, self.host.auth_request_return_content = \
            site_auth.check_auth(self.host.auth_request_status, self.host.auth_request_data)

        self.host.store()

        return redirect('check_authentication')

    def check_authentication [html] (self):
        form = Form(enctype='multipart/form-data')
        form.add_submit('cancel', _('Previous'))
        form.add_submit('submit', _('Next'))

        if form.get_widget('cancel').parse():
            return redirect('credentials')

        if form.is_submitted():
            return redirect('sso_init_link')

        html_top('step6', title=_('Step 6 - Check the authentication process'))
        '<h2>%s</h2>' % _('Step 6 - Check the authentication process')

        if self.host.auth_request_success:
            htmltext(_('''<p>Authentication succeeded ! You can go to the next step.</p>'''))
        else:
            htmltext(_('''\
<p>Authentication has failed. To resolve this problem, you can :</p>
<ul>
<li><a href="send_authentication_request">Try authentication again</a></li>
<li><a href="see_authentication_response">See the response of the authentication request from the site</a></li>
<li><a href="modify_authentication_request">Modify the parameters of the authentication request</a></li>
<li><a href="authentication_success_criteria">Change the way Larpe detects the authentication is successful or not</a></li>
<li>Go back and change your username and/or password</li>
</ul>
'''))

        form.render()

    def see_authentication_response [html] (self):
        html_top('see_authentication_response', title=_('Authentication response'))
        '<h2>%s</h2>' % _('Authentication response')

        color = str('black')
        name = str(_('HTTP status code'))
        '<div style="margin-bottom: 0.1em; font-weight: bold; color: %s;">%s</div>' % (color, name)
        '<div style="margin-left: 1em; margin-bottom: 0.3em; color: %s;">%s (%s)</div>' % \
                    (color, self.host.auth_request_status, status_reasons[self.host.auth_request_status])

        '<dl>'
        '<dt><a href="see_response_html_page">%s</a></dt>' % _('See HTML page')
        '</dl>'
        '<div class="buttons"><a href="check_authentication"><input type="button" value="%s" /></a></div><br />' % _('Back')

    def see_response_html_page (self):
        return self.host.auth_request_data

    def authentication_success_criteria [html] (self):
        form = self.form_authentication_success_criteria()

        if form.get_widget('cancel').parse():
            return redirect('check_authentication')

        if form.is_submitted() and not form.has_errors():
            self.submit_authentication_success_criteria_form(form)
            return redirect('check_authentication')

        html_top('authentication_success_criteria', title=_('Criteria of authentication success'))
        '<h2>%s</h2>' % _('Criteria of authentication success')
        form.render()

    def form_authentication_success_criteria(self):
        form = Form(enctype='multipart/form-data')
        form.add(RadiobuttonsWidget, 'auth_system', title = _('Authentication system of the original site'),
                options=[
                    ('password', _('Check the existence of a password field'), 'password'),
                    ('match_text', _('Match some text to detect an authentication failure'), 'match_text'),
                ],
                sort=False,
                delim=htmltext('<br />'),
                value = self.host.auth_system)
        form.add(RegexStringWidget, 'auth_match_text', title = _('Text to match in case of authentication failure'),
                required = False, size = 50, value = self.host.auth_match_text)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form

    def submit_authentication_success_criteria_form(self, form):
        for f in ('auth_system', 'auth_match_text'):
            value = form.get_widget(f).parse()
            setattr(self.host, f, value)

        self.host.store()

    def modify_authentication_request [html] (self):
        html_top('modify_authentication_request', title=_('Modify the parameters of the authentication request'))
        '<h2>%s</h2>' % _('Modify the parameters of the authentication request')

        '<dl>'
        '<dt><a href="auth_request_post_parameters">%s</a></dt> <dd>%s</dd>' % (
                _('Modify POST parameters'), _('Configure the form attributes that will be sent within the authentication POST requests'))
        '<dt><a href="auth_request_http_headers">%s</a></dt> <dd>%s</dd>' % (
                _('Modify HTTP headers'), _('Configure the HTTP headers of the authentication requests made by Larpe'))
        '</dl>'
        '<div class="buttons"><a href="check_authentication"><input type="button" value="%s" /></a></div><br />' % _('Back')

    def auth_request_post_parameters [html] (self):
        form = self.form_auth_request_post_parameters()

        if form.get_widget('cancel').parse():
            return redirect('modify_authentication_request')

        if form.is_submitted() and not form.has_errors():
            self.submit_auth_request_post_parameters_form(form)
            return redirect('modify_authentication_request')

        html_top('auth_request_post_parameters', title=_('Configure POST parameters'))
        '<h2>%s</h2>' % _('Configure POST parameters')

        '<p>'
        htmltext(_('''Here are the detected form fields that will be sent as parameters of the authentication POST
 request. You can desactivate some or all of them, or change their value.'''))
        '</p>'

        form.render()

    def form_auth_request_post_parameters(self):
        form = Form(enctype='multipart/form-data')
        for name, value in self.host.post_parameters.iteritems():
            if value['immutable']:
                form.add(DictWidget, name, value, disabled = 'disabled')
            else:
                form.add(DictWidget, name, value)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form

    def submit_auth_request_post_parameters_form(self, form):
        for name, old_value in self.host.post_parameters.iteritems():
            value = form.get_widget(name).parse()
            if value['enabled'] == 'on':
                old_value['enabled'] = True
            else:
                old_value['enabled'] = False
            if old_value['immutable'] is False:
                old_value['value'] = value['value']
            self.host.post_parameters[name] = old_value
        self.host.store()

    def auth_request_http_headers [html] (self):
        form = self.form_auth_request_http_headers()

        if form.get_widget('cancel').parse():
            return redirect('modify_authentication_request')

        if form.is_submitted() and not form.has_errors():
            self.submit_auth_request_http_headers_form(form)
            return redirect('modify_authentication_request')

        html_top('auth_request_http_headers', title=_('Configure HTTP headers'))
        '<h2>%s</h2>' % _('Configure HTTP headers')

        '<p>'
        htmltext(_('''Here are the HTTP headers that will be sent within the authentication POST
 request. You can desactivate some or all of them, or change their value.'''))
        '</p>'

        form.render()

    def form_auth_request_http_headers(self):
        form = Form(enctype='multipart/form-data')
        for name, value in self.host.http_headers.iteritems():
            if value['immutable']:
                form.add(DictWidget, name, value, disabled = 'disabled')
            else:
                form.add(DictWidget, name, value)
        form.add(HtmlWidget, htmltext('<p>%s</p>' % \
            _('The headers "Host", "Accept-Encoding" and "Content-Length" will also automatically be sent.')))
        if get_cfg('use_proxy') and self.host.use_proxy:
            form.add(HtmlWidget, htmltext('<p>%s</p>' % \
                _('As Larpe uses a proxy for this site, the headers "Proxy-Authorization", "Proxy-Connection" and "Keep-Alive" will be sent as well.')))
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form

    def submit_auth_request_http_headers_form(self, form):
        for name, old_value in self.host.http_headers.iteritems():
            value = form.get_widget(name).parse()
            if value['enabled'] == 'on':
                old_value['enabled'] = True
            else:
                old_value['enabled'] = False
            if old_value['immutable'] is False:
                old_value['value'] = value['value']
            self.host.http_headers[name] = old_value
        self.host.store()

    def sso_init_link [html] (self):
        form = self.form_sso_init_link()

        if form.get_widget('cancel').parse():
            return redirect('check_authentication')

        if form.is_submitted() and not form.has_errors():
            self.submit_sso_init_link_form(form)
            return redirect('metadatas')

        html_top('sso_init_link', title=_('Step 7 - Configure how a Single Sign On can be initiated'))
        '<h2>%s</h2>' % _('Step 7 - Configure how a Single Sign On can be initiated')

        '<p>'
        htmltext(_('''\
Most sites use one of the following 2 ways to allow users to initialise an authentication :
<ol>
<li>The site has a single authentication page. It redirects users to this page when they click a "Login" button or try to access a page which require users to be authenticated.</li>
<li>The site includes an authentication form in most or all of his pages. Users can authenticate on any of these pages, and don't need to be redirected to a separate authentication page.</li>
</ol>'''))
        '</p>'

        '<p>'
        htmltext(_('''\
Larpe needs to change this part of the site in a different way depending on the usual way the site works. It can either :
<ol>
<li>Redirect the user to the Single Sign On url instead of the previous authentication page.</li>
<li>Replace the form included in pages with a simple button. When users press this button, they will be redirected to the Single Sign On url.</li>
</ol>'''))
        '</p>'

        form.render()

    def form_sso_init_link(self):
        form = Form(enctype='multipart/form-data')
        form.add(RadiobuttonsWidget, 'auth_form_places', title = _('Authentication page or form'),
                options=[
                    ('form_once', _('The site has a single authentication page'), 'form_once'),
                    ('form_everywhere', _('The site includes an authentication form in most or all pages'), 'form_everywhere'),
                ],
                sort=False, required = True, delim=htmltext('<br />'), value = self.host.auth_form_places)
        form.add_submit('cancel', _('Previous'))
        form.add_submit('submit', _('Next'))
        return form

    def submit_sso_init_link_form(self, form):
        fields = [ 'auth_form_places', ]
        for f in fields:
            setattr(self.host, f, form.get_widget(f).parse())
        self.host.auth_form_url = self.host.auth_url

        if self.host.auth_form_places == 'form_everywhere' and self.host.auth_form_action:
            self.generate_apache_filter()
        self.host.store()
        write_apache2_vhosts()

    def metadatas [html] (self):
        form = Form(enctype='multipart/form-data')
        form.add_submit('cancel', _('Previous'))
        form.add_submit('submit', _('Next'))

        if form.get_widget('cancel').parse():
            return redirect('sso_init_link')

        if form.is_submitted():
            return redirect('check_full_configuration')

        html_top('step7', title=_('Step 8 - Configure the site metadatas on your identity provider'))
        '<h2>%s</h2>' % _('Step 8 - Configure the site metadatas on your identity provider')

        '<p>'
        htmltext(_('''Download the metadatas and the public key for this site and
upload them on your identity provider in order to use Liberty Alliance features'''))
        '</p>'

        '<dl>'
        if hasattr(self.host, str('base_url')):
            metadata_url = '%s/metadata.xml' % self.host.base_url
            '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
                        metadata_url,
                        _('Service Provider Metadata'),
                        _('Download Service Provider ID-FF 1.2 Metadata file'))
        else:
            '<p>%s</p>' % _('No metadata has been generated for this host.')

#           metadata_url = '%s/metadata.xml' % self.host.saml2_base_url
#            '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
#                        metadata_url,
#                        _('Service Provider Metadata'),
#                        _('Download Service Provider SAML 2.0 Metadata file'))

        if hasattr(self.host, str('base_url')) and self.host.public_key and os.path.exists(self.host.public_key):
            public_key_url = '%s/public_key.pem' % self.host.base_url
            '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
                        public_key_url,
                        _('Public key'),
                        _('Download Service Provider SSL Public Key file'))
        else:
            '<p>%s</p>' % _('No public key has been generated for this host.')
        '</dl>'

        form.render()

    def check_full_configuration [html] (self):
        form = Form(enctype='multipart/form-data')
        form.add_submit('cancel', _('Previous'))
        form.add_submit('submit', _('Finish'))

        if form.get_widget('cancel').parse():
            return redirect('metadatas')

        if form.is_submitted():
            return redirect('../..')

        html_top('step8', title=_('Step 9 - Check everything works'))
        '<h2>%s</h2>' % _('Step 9 - Check everything works')

        '<p>'
        htmltext(_('''Now you can fully test your site, start from the home page, initiate a Single Sign On,
federate your identities and do a Single Logout. If everything works, click the "%(finish)s" button,
otherwise go back and check your settings.''')) % { 'finish': _('Finish') }
        '</p>'

        '<p>'
        htmltext(_('The address of your site is : '))
        '<a href="%s">%s</a>' % (self.host.new_url, self.host.new_url)
        '</p>'

        form.render()


    def auto_detect_configuration(self):
#        """Guess other SP parameters"""
#        if self.host.auth_url is not None:
#            # Separate auth page
#            self.host.auth_form_url = self.host.auth_url
#        else:
#            if self.host.auth_form_page_url is not None:
#                # Auth form is not on index page
#                self.host.auth_form_url = self.host.auth_form_page_url
#            else:
#                # Auth form is on index page
#                self.host.auth_form_url = self.host.orig_site

        # Reset previous detected values
        self.host.auth_form = None
        self.host.auth_check_url = None
        self.host.login_field_name = None
        self.host.password_field_name = None
        if not self.host.post_parameters:
            self.host.post_parameters = {}

        self.parse_page(self.host.auth_form_url)

    def parse_page(self, page_url):
        # Get the authentication page
        try:
            response, status, page, auth_header = http_get_page(page_url, use_proxy=self.host.use_proxy)
        except Exception, msg:
            print msg
            return

        # Check if this site uses HTTP authentication
#        if status == 401:
#            if auth_header.startswith('Basic'):
#                self.host.auth_mode = 'http_basic'
#            else:
#                self.host.auth_mode = 'unsupported'
#            self.host.store()
#            return

        if page is None:
            return
            #raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to get page'), self.host.auth_form_url))

        # Default authentication mode
        self.host.auth_mode = 'form'

        self.host.site_authentication_plugin = site_authentication.guess_site_authentication_class(page)
        self.parse_frames(page)
        self.parse_forms(page)
        if self.host.auth_form is not None:
            self.parse_form_action()
            input_fields = self.parse_input_fields()
            self.parse_login_field(input_fields)
            self.parse_password_field(input_fields)
            self.parse_select_fields(input_fields)
            self.parse_other_fields(input_fields)

    def parse_frames(self, page):
        '''If there are frames, parse them recursively'''
        regexp = re.compile("""<frame.*?src=["'](.*?)["'][^>]*?>""", re.DOTALL | re.IGNORECASE)
        found_frames = regexp.findall(page)
        if found_frames:
            for frame_url in found_frames:
                if frame_url.startswith('http'):
                    frame_full_url = frame_url
                else:
                    page_url_tokens = page_url.split('/')
                    page_url_tokens[-1] = frame_url
                    frame_full_url = '/'.join(page_url_tokens)
                self.parse_page(frame_full_url)

    def parse_forms(self, page):
        '''Search for an authentication form'''
        # Get all forms
        regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
        found_forms = regexp.findall(page)
        if not found_forms:
            return
            #raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to find any form'), self.host.auth_form_url))

        # Get the first form with a password field
        for found_form in found_forms:
            regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
            if regexp.search(found_form) is not None:
                self.host.auth_form = found_form
                break

    def parse_form_action(self):
        '''Get the action url of the form'''
        regexp = re.compile("""<form.*?action=["']?(.*?)["']?[\s>].*?>""", re.DOTALL | re.IGNORECASE)
        self.host.auth_form_action = regexp.findall(self.host.auth_form)[0]
        # FIXME: Find a Python module which unescapes html entities
        self.host.auth_check_url = self.host.auth_form_action.replace('&amp;', '&')
        if not self.host.auth_check_url.startswith('http'):
            if self.host.auth_check_url.startswith('/'):
                if self.host.orig_site.startswith('https'):
                    orig_site_root = 'https://%s' % urllib.splithost(self.host.orig_site[6:])[0]
                else:
                    orig_site_root = 'http://%s' % urllib.splithost(self.host.orig_site[5:])[0]
                self.host.auth_check_url = orig_site_root + self.host.auth_check_url
            else:
                auth_form_url_tokens = self.host.auth_form_url.split('/')
                auth_form_url_tokens[-1] = self.host.auth_check_url
                self.host.auth_check_url = '/'.join(auth_form_url_tokens)

    def parse_input_fields(self):
        '''Get all input fields'''
        regexp = re.compile("""<input[^>]*?>""", re.DOTALL | re.IGNORECASE)
        return regexp.findall(self.host.auth_form)

    def parse_login_field(self, input_fields):
        '''Get login field name'''
        try:
            regexp = re.compile("""<input[^>]*?type=["']?text["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
            text_fields = regexp.findall(self.host.auth_form)
            login_field = ''
            if text_fields:
                login_field = text_fields[0]
            else:
                for field in input_fields:
                    if re.search("""type=["']?""", field, re.DOTALL | re.IGNORECASE) is None:
                        login_field = field
                        break
            regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
            self.host.login_field_name = regexp.findall(login_field)[0]
            if not self.host.post_parameters.has_key(self.host.login_field_name):
                self.host.post_parameters[self.host.login_field_name] = \
                    { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
                self.host.store()
        except IndexError, e:
            self.host.login_field_name = None
            print 'Error handling login field : %s' % e

    def parse_password_field(self, input_fields):
        '''Get password field name'''
        try:
            regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
            password_field = regexp.findall(self.host.auth_form)[0]
            regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
            self.host.password_field_name = regexp.findall(password_field)[0]
            if not self.host.post_parameters.has_key(self.host.password_field_name):
                self.host.post_parameters[self.host.password_field_name] = \
                    { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
        except IndexError, e:
            self.host.password_field_name = None
            print 'Error handling password field : %s' % e

    def parse_select_fields(self, input_fields):
        '''Add select fields to host attributes'''
        # First added for Imuse (Rennes)
        regexp = re.compile("""<select.*?</select>""", re.DOTALL | re.IGNORECASE)
        self.host.select_fields = {}
        for field in regexp.findall(self.host.auth_form):
            try:
                regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
                name = regexp.findall(field)[0]
                regexp = re.compile("""<option[^>]*?>.*?</option>""", re.DOTALL | re.IGNORECASE)
                options = regexp.findall(field)
                values = []
                for option in options:
                    regexp = re.compile("""<option[^>]*?>(.*?)</option>""", re.DOTALL | re.IGNORECASE)
                    option_label = regexp.findall(option)
                    regexp = re.compile("""value=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
                    option_value = regexp.findall(option)
                    if option_label:
                        if not option_value:
                            option_value = option_label
                        values.append((option_value[0], option_label[0]))
                    else:
                        print >> sys.stderr, 'W: Could not parse select options'
                self.host.select_fields[name] = values
                if not self.host.post_parameters.has_key(name):
                    self.host.post_parameters[name] = \
                        { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
            except IndexError, e:
                continue

    def parse_other_fields(self, input_fields):
        '''Get the default value of all other fields'''
        self.host.other_fields = {}
        # Get hidden and submit fields
        regexp = re.compile("""<input[^>]*?type=["']?(?:hidden|submit)["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
        other_fields = regexp.findall(self.host.auth_form)
        for field in other_fields:
            try:
                regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
                name = regexp.findall(field)[0]
                regexp = re.compile("""value=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
                value = regexp.findall(field)[0]
                self.host.other_fields[name] = value
                if not self.host.post_parameters.has_key(name):
                    self.host.post_parameters[name] = { 'enabled': True, 'value': value, 'immutable': False }
            except IndexError, e:
                continue


class HostUI:
    def __init__(self, host):
        self.host = host

    def form_edit(self):
        # FIXME : homogeneise the size of the fields
        form = Form(enctype='multipart/form-data')
        form.add(StringWidget, 'label', title = _('Site name'), required = True,
                size = 30, value = self.host.label)
        form.add(UrlWidget, 'orig_site', title = _('Original site root address'), required = True,
                size = 50, value = self.host.orig_site)
        form.add(ValidUrlWidget, 'auth_url', title = _('Authentication page'),
                hint = _('If there is a separate authentication page'),
                required = False, size = 70, value = self.host.auth_url)
        form.add(ValidUrlWidget, 'auth_form_page_url',
                title = _('Authentication form page'),
                hint = _('If the authentication form is not in a separate page and not in the index page either'),
                required = False, size = 70, value = self.host.auth_form_page_url)
        form.add(ValidUrlWidget, 'logout_url', title = _('Logout address'), required = False,
                size = 70, value = self.host.logout_url)
        form.add(StringWidget, 'reversed_hostname', title = _('Reversed host name'),
                size = 30, required = True, value = self.host.reversed_hostname)
        form.add(StringWidget, 'reversed_directory', title = _('Reversed directory'),
                size = 30, required = False, value = self.host.reversed_directory)
        form.add(CheckboxWidget, 'use_ssl', title = _('Use SSL'),
                hint = _('This only affects the connection between the browser and Larpe'),
                value = self.host.use_ssl)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form

    def submit_edit_form(self, form):
        metadata_cfg = {}
        for f in ('label', 'orig_site', 'auth_url', 'auth_form_page_url', 'logout_url', 'reversed_hostname',
                    'reversed_directory', 'use_ssl'):
            widget = form.get_widget(f)
            setattr(self.host, f, widget.parse())
            # Get the special use_proxy attribute if it exists
            if hasattr(widget, 'use_proxy'):
                self.host.use_proxy = widget.use_proxy

        self.host.organization_name = self.host.label
        metadata_cfg['organization_name'] = self.host.organization_name

        # Build host name from host label
        self.host.name = self.host.label.lower()
        invalid_characters = [' ', "'"]
        for char in invalid_characters:
            self.host.name = self.host.name.replace(char, '_')

        # Set url scheme (ie protocol) according to SSL usage
        if self.host.use_ssl:
            self.host.scheme = 'https'
        else:
            self.host.scheme = 'http'

        # Liberty Alliance / SAML parameters
        base_url = '%s://%s%s/liberty/%s/liberty' % (self.host.scheme,
                                                     self.host.reversed_hostname,
                                                     get_request().environ['SCRIPT_NAME'],
                                                     self.host.name)
        metadata_cfg['base_url'] = base_url
        self.host.base_url = base_url

        if lasso.SAML2_SUPPORT:
            saml2_base_url = '%s://%s%s/saml/%s/saml' % (self.host.scheme,
                                                         self.host.reversed_hostname,
                                                         get_request().environ['SCRIPT_NAME'],
                                                         self.host.name)
            metadata_cfg['saml2_base_url'] = saml2_base_url
            self.host.saml2_base_url = saml2_base_url

        provider_id = '%s/metadata' % base_url
        metadata_cfg['provider_id'] = provider_id
        self.host.provider_id = provider_id

        if lasso.SAML2_SUPPORT:
            saml2_provider_id = '%s/metadata' % saml2_base_url
            metadata_cfg['saml2_provider_id'] = saml2_provider_id
            self.host.saml2_provider_id = saml2_provider_id

        # Storage directories
        if self.host.reversed_directory is None:
            reversed_dir = 'None'
        else:
            reversed_dir = self.host.reversed_directory
        site_dir = os.path.join(get_publisher().app_dir, 'sp',
                    self.host.reversed_hostname, reversed_dir)
        user_dir = os.path.join(site_dir, 'users')
        token_dir = os.path.join(site_dir, 'tokens')
        for dir in (site_dir, user_dir, token_dir):
            if not os.path.isdir(dir):
                os.makedirs(dir)
        metadata_cfg['site_dir'] = site_dir
        self.host.site_dir = site_dir

        # Tweaking for larpe vhosts
        hostname_dir = get_abs_path(os.path.join('..', self.host.reversed_hostname))
        if not os.path.exists(hostname_dir):
            os.mkdir(hostname_dir)
        # Load the configuration from the main directory
        get_publisher().reload_cfg()
        get_publisher().write_cfg(hostname_dir)

        # Generate SSL keys
        private_key_path = os.path.join(site_dir, 'private_key.pem')
        public_key_path = os.path.join(site_dir, 'public_key.pem')
        if not os.path.isfile(private_key_path) or not os.path.isfile(public_key_path):
            set_provider_keys(private_key_path, public_key_path)
            self.host.private_key = private_key_path
            self.host.public_key = public_key_path

        # Write metadatas
        metadata_path = os.path.join(site_dir, 'metadata.xml')
        open(metadata_path, 'w').write(get_metadata(metadata_cfg))
        self.host.metadata = metadata_path

        if hasattr(self.host, 'saml2_provider_id'):
            saml2_metadata_path = os.path.join(site_dir, 'saml2_metadata.xml')
            open(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg))
            self.host.saml2_metadata = saml2_metadata_path

        # Use default idps
#        idp_dir = os.path.join(get_publisher().app_dir, 'idp')
#        self.host.idps = os.listdir(idp_dir)

        self.host.store()

        for attr in ('auth_check_url', 'login_field_name', 'password_field_name'):
            if not hasattr(self.host, attr) or not getattr(self.host, attr):
                self.auto_detect_configuration()
                break

        write_apache2_vhosts()

    def auto_detect_configuration(self):
        """Guess other SP parameters"""
        if self.host.auth_url is not None:
            # Separate auth page
            self.host.auth_form_url = self.host.auth_url
        else:
            if self.host.auth_form_page_url is not None:
                # Auth form is not on index page
                self.host.auth_form_url = self.host.auth_form_page_url
            else:
                # Auth form is on index page
                self.host.auth_form_url = self.host.orig_site

        # Reset previous detected values
        self.host.auth_form = None
        self.host.auth_check_url = None
        self.host.login_field_name = None
        self.host.password_field_name = None
        self.host.store()

        self.parse_page(self.host.auth_form_url)

    def parse_page(self, page_url):
        # Get the authentication page
        try:
            response, status, page, auth_header = http_get_page(page_url, use_proxy=self.host.use_proxy)
        except Exception, msg:
            print msg
            return

        # Check if this site uses HTTP authentication
        if status == 401:
            if auth_header.startswith('Basic'):
                self.host.auth_mode = 'http_basic'
            else:
                self.host.auth_mode = 'unsupported'
            self.host.store()
            return

        if page is None:
            return
            #raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to get page'), self.host.auth_form_url))

        self.host.site_authentication_plugin = site_authentication.guess_site_authentication_class(page)

        # If there are frames, parse them recursively
        regexp = re.compile("""<frame.*?src=["'](.*?)["'][^>]*?>""", re.DOTALL | re.IGNORECASE)
        found_frames = regexp.findall(page)
        if found_frames:
            for frame_url in found_frames:
                if frame_url.startswith('http'):
                    frame_full_url = frame_url
                else:
                    page_url_tokens = page_url.split('/')
                    page_url_tokens[-1] = frame_url
                    frame_full_url = '/'.join(page_url_tokens)
                self.parse_page(frame_full_url)

        # Default authentication mode
        self.host.auth_mode = 'form'

        # Get all forms
        regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
        found_forms = regexp.findall(page)
        if not found_forms:
            return
            #raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to find any form'), self.host.auth_form_url))

        # Get the first form with a password field
        found = False
        for found_form in found_forms:
            regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
            if regexp.search(found_form) is not None:
                self.host.auth_form = found_form
                found = True
                break

        if not found:
            return
            #raise FormError, ('auth_check_url', _('Failed to find the authentication form'))

        # If we found a form, search for all needed information

        # Get the action url of the form
        regexp = re.compile("""<form.*?action=["']?(.*?)["']?[\s>].*?>""", re.DOTALL | re.IGNORECASE)
        self.host.auth_check_url = regexp.findall(self.host.auth_form)[0]
        # FIXME : Find a module which unescapes html entities
        self.host.auth_check_url = self.host.auth_check_url.replace('&amp;', '&')
        if not self.host.auth_check_url.startswith('http'):
            if self.host.auth_check_url.startswith('/'):
                if self.host.orig_site.startswith('https'):
                    orig_site_root = 'https://%s' % urllib.splithost(self.host.orig_site[6:])[0]
                else:
                    orig_site_root = 'http://%s' % urllib.splithost(self.host.orig_site[5:])[0]
                self.host.auth_check_url = orig_site_root + self.host.auth_check_url
            else:
                auth_form_url_tokens = self.host.auth_form_url.split('/')
                auth_form_url_tokens[-1] = self.host.auth_check_url
                self.host.auth_check_url = '/'.join(auth_form_url_tokens)
        
        # Get all inputs
        regexp = re.compile("""<input[^>]*?>""", re.DOTALL | re.IGNORECASE)
        inputs = regexp.findall(self.host.auth_form)

        # Get login field name
        try:
            regexp = re.compile("""<input[^>]*?type=["']?text["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
            text_fields = regexp.findall(self.host.auth_form)
            login_field = ''
            if text_fields:
                login_field = text_fields[0]
            else:
                for field in inputs:
                    if re.search("""type=["']?""", field, re.DOTALL | re.IGNORECASE) is None:
                        login_field = field
                        break
            regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
            self.host.login_field_name = regexp.findall(login_field)[0]
        except IndexError, e:
            self.host.login_field_name = None
            print 'Error handling login field : %s' % e

        # Get password field name
        try:
            regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
            password_field = regexp.findall(self.host.auth_form)[0]
            regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
            self.host.password_field_name = regexp.findall(password_field)[0]
        except IndexError, e:
            self.host.password_field_name = None
            print 'Error handling password field : %s' % e

        # Get the default value of all other fields
        self.host.other_fields = {}

        # Get hidden and submit fields
        regexp = re.compile("""<input[^>]*?type=["']?(?:hidden|submit)["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
        other_fields = regexp.findall(self.host.auth_form)
        for field in other_fields:
            try:
                regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
                name = regexp.findall(field)[0]
                regexp = re.compile("""value=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
                value = regexp.findall(field)[0]
                self.host.other_fields[name] = value
            except IndexError, e:
                continue

        # Add select fields to host attributes
        # First added for Imuse (Rennes)
        regexp = re.compile("""<select.*?</select>""", re.DOTALL | re.IGNORECASE)
        self.host.select_fields = {}
        for field in regexp.findall(page):
            try:
                regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
                name = regexp.findall(field)[0]
                regexp = re.compile("""<option[^>]*?>.*?</option>""", re.DOTALL | re.IGNORECASE)
                options = regexp.findall(field)
                values = []
                for option in options:
                    regexp = re.compile("""value=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
                    option_value = regexp.findall(option)
                    regexp = re.compile("""<option[^>]*?>(.*?)</option>""", re.DOTALL | re.IGNORECASE)
                    option_label = regexp.findall(option)
                    if option_value and option_label:
                        values.append((option_value[0], option_label[0]))
                self.host.select_fields[name] = values
            except IndexError, e:
                continue

#            print 'action : %s' % self.host.auth_check_url
#            print 'login field : %s' % self.host.login_field_name
#            print 'password field : %s' % self.host.password_field_name
#            print 'other fields : %s' % self.host.other_fields

        self.host.store()

    def form_auto_detected_configuration(self):
        form = Form(enctype='multipart/form-data')
        form.add(RadiobuttonsWidget, 'auth_mode', title = _('Authentication mode'),
                options=[
                    ('form', _('Form'), 'form'),
                    ('http_basic', _('HTTP Basic'), 'http_basic'),
                ],
                sort=False,
                required = True,
                value = self.host.auth_mode)
        form.add(ValidUrlWidget, 'auth_check_url', title = _('Address where the authentication form must be sent'),
                required = False, size = 70, value = self.host.auth_check_url)
        form.add(StringWidget, 'login_field_name', title = _('Name of the login field'),
                required = False, size = 30, value = self.host.login_field_name)
        form.add(StringWidget, 'password_field_name', title = _('Name of the password field'),
                required = False, size = 30, value = self.host.password_field_name)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form        

    def submit_auto_detected_configuration_form(self, form):
        for f in ('auth_mode', 'auth_check_url', 'login_field_name', 'password_field_name'):
            value = form.get_widget(f).parse()
            setattr(self.host, f, value)

        # Ensure auth_check_url is a full url
        if 'auth_mode' == 'form':
            if not self.host.auth_check_url.startswith('http'):
                if self.host.auth_check_url.startswith('/'):
                    if self.host.orig_site.endswith('/'):
                        self.host.auth_check_url = self.host.orig_site + self.host.auth_check_url[1:]
                    else:
                        self.host.auth_check_url = self.host.orig_site + self.host.auth_check_url
                else:
                    auth_form_url_tokens = self.host.auth_form_url.split('/')
                    auth_form_url_tokens[-1] = self.host.auth_check_url
                    self.host.auth_check_url = '/'.join(auth_form_url_tokens)

        self.host.store()

    def form_advanced_configuration(self):
        form = Form(enctype='multipart/form-data')
        form.add(UrlOrAbsPathWidget, 'return_url', title = _('Return address'),
                hint = _('Where the user will be redirected after a successful authentication'),
                required = False, size = 50, value = self.host.return_url)
        form.add(UrlOrAbsPathWidget, 'root_url', title = _('Error address'),
                hint = _('Where the user will be redirected after a disconnection or an error'),
                required = False, size = 50, value = self.host.root_url)
        form.add(CheckboxWidget, 'redirect_root_to_login',
                title=_('Redirect the root url of the site to the login page.'),
                value = self.host.redirect_root_to_login)
        form.add(CheckboxWidget, 'send_hidden_fields', title=_('Send authentication form hidden fields'),
                value = self.host.send_hidden_fields)
        form.add(RadiobuttonsWidget, 'auth_system', title = _('Authentication system of the original site'),
                options=[
                    ('password', _('Check the existence of a password field'), 'password'),
                    ('match_text', _('Match some text to detect an authentication failure'), 'match_text'),
                ],
                sort=False,
                delim=htmltext('<br />'),
                value = self.host.auth_system)
        form.add(RegexStringWidget, 'auth_match_text', title = _('Text to match in case of authentication failure'),
                required = False, size = 50, value = self.host.auth_match_text)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form

    def submit_advanced_configuration_form(self, form):
        old_redirect_root_to_login = self.host.redirect_root_to_login

        for f in ('return_url', 'root_url', 'redirect_root_to_login', 'send_hidden_fields', 'auth_system', 'auth_match_text'):
            value = form.get_widget(f).parse()
            setattr(self.host, f, value)

        self.host.store()

        if self.host.redirect_root_to_login is not old_redirect_root_to_login:
            write_apache2_vhosts()

    def form_apache_filters(self):
        form = Form(enctype='multipart/form-data')
        if not hasattr(self.host, 'apache_output_filters'):
            self.host.apache_output_filters = [ 'proxy-html' ]
            self.host.store()
        form.add(CheckboxWidget, 'proxy-html', title = _('HTML proxy'),
                hint = _('Converts urls in the html pages according to the host new domain name'),
                value = 'proxy-html' in self.host.apache_output_filters)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form

    def submit_apache_filters_form(self, form):
        f = 'proxy-html'
        value = form.get_widget(f).parse()
        if value is True and f not in self.host.apache_output_filters:
            self.host.apache_output_filters.append(f)
        if value is False and f in self.host.apache_output_filters:
            self.host.apache_output_filters.remove(f)
        self.host.store()
        write_apache2_vhosts()

class HostPage(Directory):
    _q_exports = ['', 'minimal_configuration', 'auto_detect_configuration', 'auto_detected_configuration', 'advanced_configuration', 'apache_filters', 'see_current_configuration', 'delete']

    def __init__(self, host_id):
        self.host = Host.get(host_id)
        self.host_ui = HostUI(self.host)
        get_response().breadcrumb.append((host_id + '/', self.host.label))

    def _q_lookup(self, component):
        if component == 'configuration_assistant':
            return ConfigurationAssistant(self.host)

    def _q_index [html] (self):
        get_publisher().reload_cfg()
        html_top('hosts', title = self.host.label)

        '<h2>%s</h2>' % _('Reverse Proxy')
        
        '<dl>'
        '<dt><a href="minimal_configuration">%s</a></dt> <dd>%s</dd>' % (
                _('Minimal Configuration'), _('Configure the minimum parameters to set up a reverse proxy for this site'))
        '<dt><a href="auto_detected_configuration">%s</a></dt> <dd>%s</dd>' % (
                _('Auto Detected Configuration'), _('Check the auto detected parameters and change them if necessary'))                
        '<dt><a href="advanced_configuration">%s</a></dt> <dd>%s</dd>' % (
                _('Advanced Configuration'), _("Configure advanced parameters if it doesn't work with minimal configuration"))
        '<dt><a href="apache_filters">%s</a></dt> <dd>%s</dd>' % (
                _('Apache filters'), _('Select what apache filters to use'))
        '<dt><a href="see_current_configuration">%s</a></dt> <dd>%s</dd>' % (
                _('See Current Configuration'), _('See the current configuration of this host'))
        '</dl>'

#        if lasso.SAML2_SUPPORT:
#            '<h2>Liberty Alliance & SAML 2.0</h2>'
#            '<dl> <dt><a href="liberty_sp">%s</a></dt> <dd>%s</dd>' % (
#                    _('Service Provider'), _('Configure Liberty / SAML 2.0 parameters'))
#        else:

        '<h2>Liberty Alliance</h2>'

        '<dl>'
        if hasattr(self.host, str('base_url')):
            metadata_url = '%s/metadata.xml' % self.host.base_url
            '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
                        metadata_url,
                        _('Service Provider Metadata'),
                        _('Download Service Provider ID-FF 1.2 Metadata file'))
        else:
            '<p>%s</p>' % _('No metadata has been generated for this host.')

#           metadata_url = '%s/metadata.xml' % self.host.saml2_base_url
#            '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
#                        metadata_url,
#                        _('Service Provider Metadata'),
#                        _('Download Service Provider SAML 2.0 Metadata file'))

        if self.host.public_key is not None and os.path.exists(self.host.public_key):
            '<dt><a href="public_key.pem">%s</a></dt> <dd>%s</dd>' % (
                        _('Public key'),
                        _('Download Service Provider SSL Public Key file'))
        else:
            '<p>%s</p>' % _('No public key has been generated for this host.')
        '</dl>'

    def minimal_configuration [html] (self):
        form = self.host_ui.form_edit()
        if form.get_widget('cancel').parse():
            return redirect('.')

        if form.is_submitted() and not form.has_errors():
            check_minimal_configuration(form)

        if form.is_submitted() and not form.has_errors():
            self.host_ui.submit_edit_form(form)
            return redirect('see_current_configuration')

        get_response().breadcrumb.append( ('minimal_configuration', _('Edit')) )
        html_top('hosts', title = _('Edit Host'))
        '<h2>%s</h2>' % _('Edit Host')

        form.render()

    def auto_detect_configuration (self):
        self.host_ui.auto_detect_configuration()
        return redirect('auto_detected_configuration')

    def auto_detected_configuration [html] (self, detect=False):
        form = self.host_ui.form_auto_detected_configuration()
        if form.get_widget('cancel').parse():
            return redirect('.')

        if not form.is_submitted() or form.has_errors():
            get_response().breadcrumb.append( ('auto_detected_configuration', _('Auto Detected Configuration')) )
            html_top('hosts', title = _('Auto Detected Configuration'))
            '<h2>%s</h2>' % _('Auto Detected Configuration')
            '<div><a href="auto_detect_configuration"><input type="button" value="%s" /></a> %s </div><br />' \
                % (_('Auto detect'), _('Warning, this will erase your custom modifications !'))
            form.render()
        else:
            self.host_ui.submit_auto_detected_configuration_form(form)
            return redirect('see_current_configuration')

    def advanced_configuration [html] (self):
        form = self.host_ui.form_advanced_configuration()
        if form.get_widget('cancel').parse():
            return redirect('.')

        if not form.is_submitted() or form.has_errors():
            get_response().breadcrumb.append( ('advanced_configuration', _('Advanced Configuration')) )
            html_top('hosts', title = _('Advanced Configuration'))
            '<h2>%s</h2>' % _('Advanced Configuration')
            form.render()
        else:
            self.host_ui.submit_advanced_configuration_form(form)
            return redirect('see_current_configuration')

    def apache_filters [html] (self):
        form = self.host_ui.form_apache_filters()
        if form.get_widget('cancel').parse():
            return redirect('.')

        if not form.is_submitted() or form.has_errors():
            get_response().breadcrumb.append( ('apache_filters', _('Apache filters')) )
            html_top('hosts', title = _('Apache filters'))
            '<h2>%s</h2>' % _('Apache filters')
            form.render()
        else:
            self.host_ui.submit_apache_filters_form(form)
            return redirect('see_current_configuration')

    def see_current_configuration [html] (self):
        get_response().breadcrumb.append( ('see_current_configuration', _('See Current Configuration')) )
        html_top('hosts', title = _('Current Host Configuration'))
        '<h2>%s</h2>' % _('Current Host Configuration')

        for attr in ('auth_check_url', 'login_field_name', 'password_field_name'):
            if not getattr(self.host, str(attr)):
                '<div class="errornotice">%s</div>' % _("This site is not fully configured yet. \
The following red fields don't have a correct value.")
                break

        # New url for this host
        url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
        if self.host.reversed_directory is not None:
            url += '%s/' % self.host.reversed_directory
        '<div style="margin-bottom: 0.1em; font-weight: bold;">%s</div>' % _('New url for this host')
        '<div style="margin-left: 1em; margin-bottom: 0.3em;"><a href="%s">%s</a></div>' % (url, url)

        get_publisher().reload_cfg()
        if get_cfg('use_proxy'):
            '<div style="margin-bottom: 0.1em; font-weight: bold;">%s</div>' % _('Use proxy')
            '<div style="margin-left: 1em; margin-bottom: 0.3em;">%s</div>' % self.host.use_proxy

        host_attrs_minimal = (
            ('label', _('Site name')),
            ('orig_site', _('Original site root address')),
            ('auth_url', _('Authentication page')),
            ('auth_form_page_url', _('Authentication form page')),
            ('logout_url', _('Logout address')),
            ('reversed_hostname', _('Reversed host name')),
            ('reversed_directory', _('Reversed directory')),
            ('use_ssl', _('Use SSL'))
        )
        host_attrs_auto = (
            ('auth_mode', _('Authentication mode')),
            ('auth_check_url', _('Address where the authentication form must be sent')),
            ('login_field_name', _('Name of the login field')),
            ('password_field_name', _('Name of the password field')),
            ('site_authentication_plugin', _('Plugin used for site specific authentication behaviour'))
        )
        host_attrs_advanced = (
            ('return_url', _('Return address')),
            ('root_url', _('Root address')),
            ('redirect_root_to_login', _('Redirect the root url of the site to the login page.')),
            ('auth_system', _('Authentication system of the original site')),
            ('auth_match_text', _('Text to match in case of authentication failure')),
            ('send_hidden_fields', _('Send authentication form hidden fields'))
        )
        host_menus = (
            (host_attrs_minimal, '<a href="minimal_configuration">%s</a>' % _('Minimal Configuration')),
            (host_attrs_auto, '<a href="auto_detected_configuration">%s</a>' % _('Auto Detected Configuration')),
            (host_attrs_advanced, '<a href="advanced_configuration">%s</a>' % _('Advanced Configuration')),
        ) 
        for host_attrs, category in host_menus:
            '<h3>%s</h3>' % category
            for attr, name in host_attrs:
                color = 'black'
                if attr in ('auth_check_url', 'login_field_name', 'password_field_name') and \
                        not getattr(self.host, str(attr)):
                    color = 'red'
                '<div style="margin-bottom: 0.1em; font-weight: bold; color: %s;">%s</div>' % (color, name)
                '<div style="margin-left: 1em; margin-bottom: 0.3em; color: %s;">%s</div>' % \
                    (color, getattr(self.host, str(attr)))
                if getattr(self.host, str(attr)) == '':
                    '<br />'
        '<div class="buttons"><a href="."><input type="button" value="%s" /></a></div><br />' % _('Back')

    def delete [html] (self):
        form = Form(enctype='multipart/form-data')
        form.widgets.append(HtmlWidget('<p>%s</p>' % _(
                        'You are about to irrevocably delete this host.')))
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        if form.get_widget('cancel').parse():
            return redirect('..')
        if not form.is_submitted() or form.has_errors():
            get_response().breadcrumb.append(('delete', _('Delete')))
            html_top('hosts', title = _('Delete Host'))
            '<h2>%s : %s</h2>' % (_('Deleting Host'), self.host.label)
            form.render()
        else:
            self.host.remove_self()
            write_apache2_vhosts()
            return redirect('..')


class HostsDirectory(Directory):

    _q_exports = ['', 'new']

    def _q_index [html] (self):
        get_response().breadcrumb.append(('hosts/', _('Hosts')))
        html_top('hosts', title = _('Hosts'))
        """<ul id="nav-hosts-admin">
          <li><a href="new">%s</a></li>
        </ul>""" % _('New Host')

        '<ul class="biglist">'

        for host in Host.select(lambda x: x.name != 'larpe', order_by = 'label'):
            if not host.name:
                continue
            if not hasattr(host, str('scheme')):
                host.scheme = str('http')
            '<li>'
            '<strong class="label">%s</strong>' % host.label
            if hasattr(host, str('new_url')) and host.new_url:
                url = host.new_url
            else:
                # Compat with older Larpe versions
                url = '%s://%s%s/' % (host.scheme, host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
                if host.reversed_directory is not None:
                    url += '%s/' % host.reversed_directory
            '<br /><a href="%s">%s</a>' % (url, url)
            '<p class="commands">'
            command_icon('%s/' % host.id, 'edit')
            command_icon('%s/delete' % host.id, 'remove')
            '</p></li>'
        '</ul>'

    def new [html] (self):
        if not os.path.isdir(os.path.join(get_publisher().app_dir, str('idp'))):
            html_top('hosts', title = _('New Host'))
            html = '<h2>%s</h2>' % _('New Host')
            html += 'You must <a href="%s/admin/settings/liberty_idp/">' % misc.get_root_url()
            html += 'configure an Identity Provider</a> first<br /><br />'
            html += '<a href="."><input type="button" value="%s" /></a>' % _('Back')
            return html

        get_response().breadcrumb.append(('hosts/', _('Hosts')))
        get_response().breadcrumb.append(('new', _('New')) )
        host = Host()
        host.store()
#        configuration_assistant = ConfigurationAssistant(host)
        return redirect('%s/configuration_assistant/start' % host.id)

    def _q_lookup(self, component):
        get_response().breadcrumb.append(('hosts/', _('Hosts')))
        return HostPage(component)

