import urllib
import urlparse

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

import lasso

from menu import *

from larpe import errors
from larpe import misc
from larpe.hosts import Host
from larpe.form import *
from larpe.admin.liberty_utils import *
#from larpe.filter import filter_misc

class HostUI:
    def __init__(self, host):
        self.host = host
        if self.host.reversed_hostname is None:
           self.host.reversed_hostname = get_request().get_server()

    def form_edit(self):
        form = Form(enctype='multipart/form-data')
        form.add(StringWidget, 'label', title = _('Label'), required = True,
                size = 30, value = self.host.label)
        form.add(StringWidget, 'orig_site', title = _('Original Site Address'), required = True,
                size = 50, value = self.host.orig_site)
        form.add(StringWidget, 'auth_url', title = _('Authentication Page (only if there is a separate authentication page)'), required = False,
                size = 70, value = self.host.auth_url)
        form.add(StringWidget, 'auth_form_page_url',
                title = _('Authentication Form Page (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(StringWidget, '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(StringWidget, 'organization_name', title=_('Organisation Name'), size=30,
                value = self.host.organization_name)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form

    def form_auto_detected_configuration(self):
        form = Form(enctype='multipart/form-data')
        form.add(StringWidget, '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 form_advanced_configuration(self):
        form = Form(enctype='multipart/form-data')
        form.add(RegexStringWidget, 'return_url', title = _('Return Address (After a succesful authentication)'), required = False,
                size = 50, value = self.host.return_url)
        form.add_submit('submit', _('Submit'))
        form.add_submit('cancel', _('Cancel'))
        return form
        
    def submit_edit_form(self, form):
#        filter_misc.reload_cfg()
        metadata_cfg = {}
        for f in ('label', 'orig_site', 'auth_url', 'auth_form_page_url', 'logout_url', 'reversed_hostname',
                    'organization_name'):
            value = form.get_widget(f).parse()
            if f in ('orig_site', 'auth_url', 'auth_form_page_url', 'logout_url') \
                    and value is not None \
                    and not value.startswith('http'):
                value = 'http://' + value
#            metadata_cfg[f] = value
            setattr(self.host, f, value)

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

        self.host.name = self.host.label.lower().replace(' ', '_').replace("'", '_')

        # Liberty Alliance / SAML parameters
        provider_id = 'http://%s/liberty/%s/metadata' % (self.host.reversed_hostname,
                                                         self.host.name)
        metadata_cfg['provider_id'] = provider_id
        metadata_cfg['saml2_provider_id'] = provider_id
        self.host.provider_id = provider_id
        self.host.saml2_provider_id = provider_id

        base_url = 'http://%s/liberty/%s/liberty' % (self.host.reversed_hostname,
                                                     self.host.name)
        metadata_cfg['base_url'] = base_url
        metadata_cfg['saml2_base_url'] = base_url
        self.host.base_url = base_url
        self.host.saml2_base_url = base_url

        # 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 = misc.get_abs_path(os.path.join('..', self.host.reversed_hostname))
        if not os.path.exists(hostname_dir):
            os.mkdir(hostname_dir)
        misc.reload_cfg()
        misc.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)
#            metadata_cfg['privatekey'] = private_key_path
            self.host.private_key = private_key_path
#            metadata_cfg['publickey'] = public_key_path
            self.host.public_key = public_key_path

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

        if hasattr(self.host, 'saml2_provider_id'):
            saml2_metadata_path = os.path.join(site_dir, 'saml2_metadata.xml')
            file(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg))
#            metadata_cfg['saml2_metadata'] = saml2_metadata_path
            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)

        if not hasattr(self, 'host.auth_form_url'):
            self.auto_detect_configuration()

        self.host.store()
#        filter_misc.write_cfg()

        self.write_apache2_vhosts()

    def auto_detect_configuration(self):
        """Guess other SP parameters"""
        if self.host.auth_url is not None:
            self.host.auth_form_url = self.host.auth_url
        else:
            if self.host.auth_form_page_url is not None:
                self.host.auth_form_url = self.host.auth_form_page_url
            else:
                self.host.auth_form_url = self.host.orig_site
                
        # Get the authentication page
        page = misc.get_html_page(self.host.auth_form_url)
        if page is None:
            print 'Failed to get page : %s' % self.host.auth_form_url
            return

        # Get all forms
        regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
        found_forms = regexp.findall(page)
        if not found_forms:
            print 'Failed to find any form'

        # 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 we found one, search for all needed information
        if found:
            # 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]
            if not self.host.auth_check_url.startswith('http'):
                if self.host.auth_check_url.startswith('/'):
                    orig_site_root = 'http://' + 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:
                    print e
                    continue

            # Get select fields
            # FIXME : check if one option is selected
            regexp = re.compile("""<select.*?</select>""", re.DOTALL | re.IGNORECASE)
            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)
                    value = regexp.findall(field)[0]
                    self.host.other_fields[name] = value
                except IndexError, e:
                    print 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
        else:
            print 'Failed to find the authentication form'
            print page

        self.host.store()


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

        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 submit_advanced_configuration_form(self, form):
        f = 'return_url'
        value = form.get_widget(f).parse()
        setattr(self.host, f, value)
        self.host.store()



    def write_apache2_vhosts(self):
        hosts = Host.select(order_by = 'reversed_hostname')
        vhost_dir = get_publisher().app_dir
        vhost_file = None
        reversed_hostname = ''

        vhost_file = open(os.path.join(vhost_dir, 'apache2-vhosts'), 'w')
        locations_file = open(os.path.join(vhost_dir, 'apache2-vhost-locations'), 'w')
        try:
            main_vhost = open('/etc/apache2/sites-available/apache2-vhost-larpe', 'r')
            file_content = main_vhost.read()
            regexp = re.compile('<VirtualHost (.*?)>')
            vhost_ip = regexp.findall(file_content)[0]
        except:
            vhost_ip = '*'

#        vhost_file.write('NameVirtualHost %s\n' % vhost_ip)

        for host in hosts:
            if host.orig_site.startswith('http://'):
                orig_host, orig_query = urllib.splithost(host.orig_site[5:])
            else:
                orig_host, orig_query = urllib.splithost(host.orig_site[6:])

            if host.orig_site.endswith('/'):
                orig_site = host.orig_site
                orig_dir = orig_query
            else:
                orig_site = host.orig_site + '/'
                orig_dir = orig_query + '/'

            old_auth_url = None
            if host.auth_url is not None:
                if host.auth_url.startswith('http://'):
                    chars_to_skip = 5
                else:
                    chars_to_skip = 6
                regexp = re.compile(host.orig_site[chars_to_skip:])
                old_auth_url_short = regexp.sub('', host.auth_url[chars_to_skip:])
                if old_auth_url_short.startswith('/'):
                    old_auth_url = old_auth_url_short
                else:
                    old_auth_url = '/' + old_auth_url_short
                special_characters = ('\\', '.', '?', '*', '+', '^', '$', '|', '(', ')', '[', ']')
                for char in special_characters:
                    old_auth_url = old_auth_url.replace(char, '\%s' % char)

            base_url_tokens = host.base_url.split('/')
            base_url_tokens[-1] = 'login'
            new_auth_url = '/'.join(base_url_tokens)

            old_logout_url = None
            new_logout_url = None
            if host.logout_url is not None:
                if host.logout_url.startswith('http://'):
                    chars_to_skip = 5
                else:
                    chars_to_skip = 6
                regexp = re.compile(host.orig_site[chars_to_skip:])
                old_logout_url_short = regexp.sub('', host.logout_url[chars_to_skip:])
                if old_logout_url_short.startswith('/'):
                    old_logout_url = old_logout_url_short
                else:
                    old_logout_url = '/' + old_logout_url_short
                special_characters = ('\\', '.', '?', '*', '+', '^', '$', '|', '(', ')', '[', ']')
                for char in special_characters:
                    old_logout_url = old_logout_url.replace(char, '\%s' % char)

                base_url_tokens = host.base_url.split('/')
                base_url_tokens[-1] = 'logout'
                new_logout_url = '/'.join(base_url_tokens)

            cfg = { 'vhost_ip': vhost_ip,
                    'reversed_hostname': host.reversed_hostname,
                    'orig_site': orig_site,
                    'orig_dir': orig_dir,
                    'old_auth_url': old_auth_url,
                    'new_auth_url': new_auth_url,
                    'old_logout_url': old_logout_url,
                    'new_logout_url': new_logout_url }

            if host.reversed_directory is None:
                cfg['reversed_directory'] = '/'
            else:
                cfg['reversed_directory'] = '/' + host.reversed_directory + '/'

            if host.reversed_hostname != reversed_hostname and host.reversed_hostname != misc.cfg['proxy_hostname']:
                if reversed_hostname != '' and reversed_hostname != misc.cfg['proxy_hostname']:
                    vhost_file.write('</VirtualHost>\n\n')
                reversed_hostname = host.reversed_hostname
                try:
                    vhost_file.write("""<VirtualHost %(vhost_ip)s>
    ServerName %(reversed_hostname)s
    # ServerAdmin root@localhost

    include /usr/share/larpe/apache2.conf
""" % cfg)

                except:
                    break

            if host.reversed_hostname == misc.cfg['proxy_hostname']:
                conf_file = locations_file
            else:
                conf_file = vhost_file

            conf_file.write("""
    <Location %(reversed_directory)s>
        Allow from all
        RewriteEngine on""" % cfg)

            if old_auth_url is not None:
                conf_file.write("""
        RewriteRule         %(old_auth_url)s     %(new_auth_url)s  [R]""" % cfg)

            if old_logout_url is not None:
                conf_file.write("""
        RewriteRule         %(old_logout_url)s     %(new_logout_url)s  [R]""" % cfg)

            conf_file.write("""
        ProxyPass           %(orig_site)s
        ProxyPassReverse    %(orig_site)s
        ProxyHTMLURLMap     %(orig_dir)s     %(reversed_directory)s
        ProxyHTMLURLMap     %(orig_site)s    %(reversed_directory)s
    </Location>
""" % cfg)                

        if vhost_file is not None and reversed_hostname != '':
            vhost_file.write('</VirtualHost>')
            vhost_file.close()
        if locations_file is not None:
            locations_file.close()

        os.system('/usr/sbin/larpe-reload-apache2')

class HostPage(Directory):
    _q_exports = ['', 'minimal_configuration', 'auto_detect_configuration', 'auto_detected_configuration', 'advanced_configuration', ('public_key.pem', 'public_key'), 'delete']

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

    def _q_index [html] (self):
        misc.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 a 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"))
        '</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> <dt><a href="liberty_sp">%s</a></dt> <dd>%s</dd>' % (
#                    _('Service Provider'), _('Configure Liberty parameters'))

        '<dl>'
        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'))

#        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'))

        '<dt><a href="public_key.pem">%s</a></dt> <dd>%s</dd>' % (
                    _('Public key'),
                    _('Download Service Provider SSL Public Key file'))
        '</dl>'

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

        if not form.is_submitted() or form.has_errors():
            get_response().breadcrumb.append( ('minimal_configuration', _('Edit')) )
            html_top('hosts', title = _('Edit Host'))
            '<h2>%s</h2>' % _('Edit Host')
            form.render()
        else:
            self.host_ui.submit_edit_form(form)
            return redirect('.')

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

    def auto_detected_configuration [html] (self):
        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'), _('This will erase your custom modifications'))
            form.render()
        else:
            self.host_ui.submit_auto_detected_configuration_form(form)
            return redirect('.')

    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('.')

    def public_key(self):
        response = get_response()
        response.set_content_type('text/plain')
        public_key = open(os.path.join(self.host.site_dir, 'public_key.pem')).read()
        return public_key

    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()
            self.host_ui.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(order_by = 'label'):
            '<li>'
            '<strong class="label">%s</strong>' % host.label
            '<p class="commands">'
            command_icon('%s' % host.id, 'edit')
            command_icon('%s/delete' % host.id, 'remove')
            '</p></li>'
        '</ul>'

    def new [html] (self):
        misc.reload_cfg()
        if not misc.cfg.has_key('proxy_hostname'):
            misc.cfg[str('proxy_hostname')] = get_request().get_server().split(str(':'))[0]
            misc.write_cfg()
        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="http://%s/admin/settings/liberty_idp/new">' % misc.cfg['proxy_hostname']
            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_ui = HostUI(Host())
        form = host_ui.form_edit()
        if form.get_widget('cancel').parse():
            return redirect('.')

        if not form.is_submitted() or form.has_errors() or form.get_submit() is True:
            html_top('hosts', title = _('New Host'))
            '<h2>%s</h2>' % _('New Host')
            form.render()
        else:
            host_ui.submit_edit_form(form)
            return redirect('.')
    
    def _q_lookup(self, component):
        get_response().breadcrumb.append(('hosts/', _('Hosts')))
        return HostPage(component)
