import os
import re
import urllib
import base64

from quixote import get_publisher, get_request

from larpe.hosts import Host
from Defaults import APP_DIR

def write_apache2_vhosts():
    hosts = Host.select(lambda x: x.name != 'larpe')
    hosts.sort()
    vhosts_dir = os.path.join(APP_DIR, 'vhosts.d')
    vhost_locations_dir = os.path.join(APP_DIR, 'vhost-locations.d')
    vhosts_dir_disabled = os.path.join(APP_DIR, 'vhosts.d.disabled')
    vhost_locations_dir_disabled = os.path.join(APP_DIR, 'vhost-locations.d.disabled')
    vhost_file_name = get_request().get_server().split(':')[0]
    vhost_file = None
    reversed_hostname = ''
    other_locations_hosts = []
    vhost = None

    if get_publisher().cfg.get(str('allow_config_generation'), True):
        vhost_file = open(os.path.join(vhosts_dir, vhost_file_name), 'w')
        locations_file = open(os.path.join(vhost_locations_dir, vhost_file_name), 'w')
    else:
        vhost_file = open(os.path.join(vhosts_dir_disabled, vhost_file_name), 'w')
        locations_file = open(os.path.join(vhost_locations_dir_disabled, vhost_file_name), '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 = '*'

    for host in hosts:
        if host.orig_site is None:
            # This site hasn't been fully configured
            continue
        if host.reversed_hostname != reversed_hostname and host.reversed_hostname != get_publisher().cfg['proxy_hostname']:
            if vhost is not None:
                vhost.close()
            vhost = Vhost(host, vhost_ip)
            vhost.write(vhost_file)
            reversed_hostname = host.reversed_hostname

        if host.reversed_hostname == get_publisher().cfg['proxy_hostname']:
            conf_file = locations_file
        else:
            conf_file = vhost_file

        Location(host).write(conf_file)

    if vhost_file is not None:
        if vhost is not None:
            vhost.close()
        vhost_file.close()
    if locations_file is not None:
        locations_file.close()

    if get_publisher().cfg.get(str('allow_config_generation'), True):
        os.system('/usr/sbin/larpe-reload-apache2')


class Vhost:
    def __init__(self, host, main_ip_port):
        self.host = host
        self.main_ip_port = main_ip_port
        self.conf_file = None

    def get_ip_port(self):
        if self.host.scheme == 'https':
            return self.main_ip_port.replace(':80', ':443')
        else:
            return self.main_ip_port.replace(':443', ':80')
    ip_port = property(get_ip_port)

    def get_proxy_url(self):
        if get_publisher().cfg.get('use_proxy', False) and self.host.use_proxy == True:
            return 'http://%(proxy_ip)s:%(proxy_port)s' % get_publisher().cfg
        return None
    proxy_url = property(get_proxy_url)

    def get_proxy_auth(self):
        if self.get_proxy_url() and get_publisher().cfg.get('proxy_user'):
            credentials = base64.encodestring(
                '%(proxy_user)s:%(proxy_password)s' % get_publisher().cfg)[:-1]
            return '"Basic %s"' % credentials
        return None
    proxy_auth = property(get_proxy_auth)

    def get_cfg(self):
        return { 'ip_port': self.ip_port,
                 'reversed_hostname': self.host.reversed_hostname,
                 'proxy_url': self.proxy_url,
                 'proxy_auth': self.proxy_auth }
    cfg = property(get_cfg)

    def write(self, conf_file):
        self.conf_file = conf_file
        conf_lines = []
        # Start Virtual Host
        conf_lines.append('<VirtualHost %(ip_port)s>' % self.cfg)
        # Server name and administrator 
        conf_lines.append('ServerName %(reversed_hostname)s' % self.cfg)
        conf_lines.append('# ServerAdmin root@localhost\n')
        # Include common vhost configuration
        conf_lines.append('include /usr/share/larpe/apache2.conf\n')
        # SSL
        if self.host.scheme == 'https':
            conf_lines.append('SSLEngine On\n')
        if self.host.orig_site.startswith('https'):
            conf_lines.append('SSLProxyEngine On\n')
        # Remote proxy configuration
        if self.proxy_url is not None:
            conf_lines.append('ProxyRemote * %(proxy_url)s' % self.cfg)
            if self.proxy_auth is not None:
                conf_lines.append('RequestHeader set Proxy-Authorization %(proxy_auth)s\n' % self.cfg)
        # Write it all
        conf_file.write('\n\t'.join(conf_lines))

    def close(self):
        if self.conf_file:
            self.conf_file.write('</VirtualHost>\n\n')


def apache_escape_chars(url):
    special_characters = ('\\', '.', '?', '*', '+', '^', '$', '|', '(', ')', '[', ']')
    for char in special_characters:
        url = url.replace(char, '\%s' % char)
    return url

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

    def get_reversed_directory(self):
        if not self.host.reversed_directory:
            return '%s/' % get_request().environ['SCRIPT_NAME']
        else:
            return '%s/%s/' % (get_request().environ['SCRIPT_NAME'], self.host.reversed_directory)
    reversed_directory = property(get_reversed_directory)

    def get_python_path(self):
        if hasattr(self.host, 'apache_output_python_filters') and \
                hasattr(self.host, 'apache_python_paths') and self.host.apache_python_paths:
            python_path = 'PythonPath "sys.path'
            for path in self.host.apache_python_paths:
                python_path += "+['%s']" % path
            python_path += '"'
            return python_path
        else:
            return None
    python_path = property(get_python_path)

    def get_output_filters(self):
        python_filters = ''
        output_filters = []
        if hasattr(self.host, 'apache_output_python_filters'):
            i = 0
            for filter_file in self.host.apache_output_python_filters:
                filter_name = 'filter%d' % i
                python_filters += 'PythonOutputFilter %s %s\n\t\t' % (filter_file, filter_name)
                output_filters.append(filter_name)
                i += 1
        if hasattr(self.host, 'apache_output_filters'):
            for filter in self.host.apache_output_filters:
                output_filters.append(filter)
        if output_filters:
            return python_filters + 'SetOutputFilter ' + ';'.join(output_filters)
        else:
            return None
    output_filters = property(get_output_filters)

    def get_old_auth_url(self):
        old_auth_url = None
        if self.host.initiate_sso_url:
            old_auth_url = self.host.initiate_sso_url
        elif self.host.auth_url is not None:
            if self.host.auth_url.startswith('http://'):
                chars_to_skip = 5
            else:
                chars_to_skip = 6
            regexp = re.compile(self.host.orig_site[chars_to_skip:])
            old_auth_url_short = regexp.sub('', self.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
        if old_auth_url:
            old_auth_url = apache_escape_chars(old_auth_url)
        return old_auth_url
    old_auth_url = property(get_old_auth_url)

    def get_new_auth_url(self):
        if not hasattr(self.host, 'base_url'):
            return None
        base_url_tokens = self.host.base_url.split('/')
        base_url_tokens[-1] = 'login'
        return '/'.join(base_url_tokens)
    new_auth_url = property(get_new_auth_url)

    def get_old_logout_url(self):
        old_logout_url = None
        if self.host.logout_url is not None:
            if self.host.logout_url.startswith('http://'):
                chars_to_skip = 5
            else:
                chars_to_skip = 6
            regexp = re.compile(self.host.orig_site[chars_to_skip:])
            old_logout_url_short = regexp.sub('', self.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
            old_logout_url = apache_escape_chars(old_logout_url)
        return old_logout_url
    old_logout_url = property(get_old_logout_url)

    def get_new_logout_url(self):
        if not hasattr(self.host, 'base_url'):
            return None
        base_url_tokens = self.host.base_url.split('/')
        base_url_tokens[-1] = 'logout'
        return '/'.join(base_url_tokens)
    new_logout_url = property(get_new_logout_url)

    def get_orig_site_url_and_dir(self):
        # Split url
        if self.host.orig_site.startswith('http://'):
            orig_host, orig_query = urllib.splithost(self.host.orig_site[5:])
        else:
            orig_host, orig_query = urllib.splithost(self.host.orig_site[6:])
        # Add a trailing slash if necessary
        if self.host.orig_site.endswith('/'):
            orig_url = self.host.orig_site
            orig_dir = orig_query
        else:
            orig_url = self.host.orig_site + '/'
            orig_dir = orig_query + '/'
        return orig_url, orig_dir

    def get_orig_url(self):
        orig_url, orig_dir = self.get_orig_site_url_and_dir()
        return orig_url
    orig_url = property(get_orig_url)

    def get_orig_dir(self):
        orig_url, orig_dir = self.get_orig_site_url_and_dir()
        return orig_dir
    orig_dir = property(get_orig_dir)

    def get_cfg(self):
        return { 'reversed_directory': self.reversed_directory,
                 'old_auth_url': self.old_auth_url,
                 'new_auth_url': self.new_auth_url,
                 'old_logout_url': self.old_logout_url,
                 'new_logout_url': self.new_logout_url,
                 'orig_url': self.orig_url,
                 'orig_dir': self.orig_dir }
    cfg = property(get_cfg)

    def write(self, conf_file):
        conf_lines = []
        # Start Location
        conf_lines.append('\n\t<Location %(reversed_directory)s>' % self.cfg)
        # No user restriction
        conf_lines.append('Allow from all')
        # Apache output filters
        if self.python_path:
            conf_lines.append(self.python_path)
        if self.output_filters:
            conf_lines.append(self.output_filters)
        # Enable rewrite module
        if self.old_auth_url is not None or self.old_logout_url is not None:
            conf_lines.append('RewriteEngine On')
        # Redirect old authentication url to the new one
        if self.old_auth_url is not None and self.host.auth_form_places == 'form_once':
            conf_lines.append(
                'RewriteRule         %(old_auth_url)s    %(new_auth_url)s  [R]' % self.cfg)
        # Redirect old logout url to the new one
        if self.old_logout_url is not None:
            conf_lines.append(
                'RewriteRule         %(old_logout_url)s    %(new_logout_url)s  [R]' % self.cfg)
        # Redirect the home page to the login page
        if self.host.redirect_root_to_login is True:
            conf_lines.append('RewriteRule         /$        %(new_auth_url)s  [R]' % self.cfg)
        # Convert urls in http headers to/from the new domain
        conf_lines.append('ProxyPass           %(orig_url)s' % self.cfg)
        conf_lines.append('ProxyPassReverse    %(orig_url)s' % self.cfg)
        # Convert urls in html pages to/from the new domain
        conf_lines.append('ProxyHTMLURLMap     %(orig_dir)s     %(reversed_directory)s' % self.cfg)
        conf_lines.append('ProxyHTMLURLMap     %(orig_url)s    %(reversed_directory)s' % self.cfg)
        # Write it all and close the Location
        conf_file.write('\n\t\t'.join(conf_lines))
        conf_file.write('\n\t</Location>\n')

