# -*- coding: utf-8 -*-
#
# This file is part of Karesansui.
#
# Copyright (C) 2009 HDE, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#

"""
@authors: Kazuya Hayashi <kazuya@karesansui-project.info>
"""

import os
from os import environ as env 

import web

from karesansui import KaresansuiGadgetException, KaresansuiDBException, \
    config, sheconf
from karesansui.lib.file.k2v import K2V
from karesansui.lib.rest import Rest, auth
from karesansui.lib.file.configfile import LighttpdPortConf,\
    LighttpdSslConf, LighttpdAccessConf
from karesansui.lib.const import LIGHTTPD_COMMAND_UPDATE_CONFIG,\
    LIGHTTPD_CONF_TEMP_DIR, LIGHTTPD_PORT_CONFIG,\
    LIGHTTPD_ACCESS_CONFIG, LIGHTTPD_SSL_CONFIG,\
    LIGHTTPD_DEFAULT_PORT, PORT_MIN_NUMBER, PORT_MAX_NUMBER, \
    PROXY_ENABLE, PROXY_DISABLE
from karesansui.lib.utils import get_no_overlap_list, is_param, is_empty
from karesansui.lib.checker import Checker, \
    CHECK_EMPTY, CHECK_VALID, \
    CHECK_MIN, CHECK_MAX, CHECK_LENGTH, \
    CHECK_ONLYSPACE
from karesansui.db.access.machine import findbyhost1, findby1uniquekey, update,\
    findbyalluniquekey
from karesansui.db.access.machine2jobgroup import new as m2j_new
from karesansui.db.access._2pysilhouette import save_job_collaboration
from karesansui.db.model._2pysilhouette import Job, JobGroup
from pysilhouette.command import dict2command

def validates_server(obj):
    checker = Checker()
    check = True

    _ = obj._
    checker.errors = []

    if not is_param(obj.input, 'uniqkey'):
        check = False
        checker.add_error(_('"%s" is required.') % _('Unique Key'))
    else:
        check = checker.check_unique_key(
                    _('Unique Key'),
                    obj.input.uniqkey,
                    CHECK_EMPTY | CHECK_VALID
                    ) and check

    if not is_param(obj.input, 'port'):
        check = False
        checker.add_error(_('"%s" is required.') % _('Port Number'))
    else:
        if is_empty(obj.input.port):
            obj.input.port = LIGHTTPD_DEFAULT_PORT
        else: 
            check = checker.check_number(
                        _('Port Number'),
                        obj.input.port,
                        CHECK_EMPTY | CHECK_VALID | CHECK_MIN | CHECK_MAX,
                        PORT_MIN_NUMBER,
                        PORT_MAX_NUMBER,
                        ) and check
        
    if not is_param(obj.input, 'access'):
        check = False
        checker.add_error(_('"%s" is required.') % _('Access Policy'))
    else:
        if obj.input.access == 'all':
            check = True and check
    
        elif obj.input.access == 'network':    
            if not obj.input.has_key('network'):
                check = False
                checker.add_error(_('"%s" is required.') % _('Permit same network'))
            else:
                check = checker.check_ipaddr(
                            _('IP Address'),
                            obj.input.network,
                            CHECK_EMPTY | CHECK_VALID
                            ) and check
    
        elif obj.input.access == 'ipaddress':
            if not obj.input.has_key('access_ipaddress'):
                check = False
                checker.add_error(_('"%s" is required.') % _('Permit specified IP address'))
            else:
                obj.input.ip_list = obj.input.access_ipaddress.split() 
                obj.input.ip_list = get_no_overlap_list(obj.input.ip_list)
                if len(obj.input.ip_list) == 0:
                    check = False
                    checker.add_error(_('"%s" is required.') % _('IP Address'))
                for input_ip in obj.input.ip_list:
                    check = checker.check_ipaddr(
                                    _('Permit specified IP address'),
                                    input_ip,
                                    CHECK_EMPTY | CHECK_VALID
                                    ) and check
    
        else:
            check = False
    
    if not is_param(obj.input, 'ssl_status'):
        check = False
        checker.add_error(_('"%s" is required.') % _('SSL Settings'))
    else:
        if obj.input.ssl_status == 'enable':
            check = True and check
        elif obj.input.ssl_status == 'disable':
            check = True and check
        else:
            check = False

    if not is_param(obj.input, 'proxy_status'):
        check = False
        checker.add_error(_('"%s" is required.') % _('Proxy Settings'))
    else:
        if obj.input.proxy_status == PROXY_ENABLE:
            if not is_param(obj.input, 'proxy_server'):
                check = False
                checker.add_error(_('"%s" is required.') % _('Proxy Server'))
            else:
                check = checker.check_domainname(
                                _('Proxy Server'),
                                obj.input.proxy_server,
                                CHECK_EMPTY | CHECK_VALID,
                                None,
                                None,
                                ) and check
            if not is_param(obj.input, 'proxy_port'):
                check = False
                checker.add_error(_('"%s" is required.') % _('Proxy Port Number'))
            else:
                check = checker.check_number(
                                _('Port Number'),
                                obj.input.proxy_port,
                                CHECK_EMPTY | CHECK_VALID | CHECK_MIN | CHECK_MAX,
                                PORT_MIN_NUMBER,
                                PORT_MAX_NUMBER,
                                ) and check
            if not is_param(obj.input, 'proxy_user'):
                check = False
                checker.add_error(_('"%s" is required.') % _('Proxy User Name'))
            else:
                check = checker.check_username(
                                _('Proxy User Name'),
                                obj.input.proxy_user,
                                CHECK_VALID | CHECK_ONLYSPACE,
                                None,
                                None,
                                ) and check
            if not is_param(obj.input, 'proxy_password'):
                check = False
                checker.add_error(_('"%s" is required.') % _('Proxy Password'))
            else:
                check = checker.check_password(
                                _('Proxy Password'),
                                obj.input.proxy_password,
                                obj.input.proxy_password,
                                CHECK_VALID,
                            ) and check;
        
        elif obj.input.proxy_status == PROXY_DISABLE:
            check = True and check
        else:
            check = False

    obj.view.alert = checker.errors
    
    return check

def get_view_server_conf(config):
    try:
        conf_file = config['lighttpd.etc.dir'] + '/' + LIGHTTPD_PORT_CONFIG
        port_conf = LighttpdPortConf(conf_file)
        port_number = port_conf.read()
    
        conf_file = config['lighttpd.etc.dir'] + '/' + LIGHTTPD_ACCESS_CONFIG
        access_conf = LighttpdAccessConf(conf_file)
        access_list = access_conf.read()

        conf_file = config['lighttpd.etc.dir'] + '/' + LIGHTTPD_SSL_CONFIG
        ssl_conf = LighttpdSslConf(conf_file)
        ssl_status = ssl_conf.read()

    except IOError:
        raise KaresansuiGadgetException('Failed to read configuration file. - %s' %\
            conf_file)

    uniqkey = config['application.uniqkey']
    proxy_status = config['application.proxy.status']
    proxy_server = config['application.proxy.server']
    proxy_port = config['application.proxy.port']
    proxy_user = config['application.proxy.user']
    proxy_password = config['application.proxy.password']

    server_config = {'uniqkey' : uniqkey,
                     'port' : port_number, 
                     'access' : access_list,
                     'ssl_status' : ssl_status,
                     'proxy_status' : proxy_status,
                     'proxy_server' : proxy_server,
                     'proxy_port' : proxy_port,
                     'proxy_user' : proxy_user,
                     'proxy_password' : proxy_password,
                     }

    return server_config

def set_server_conf(config, input, session):
    if os.path.exists(LIGHTTPD_CONF_TEMP_DIR) is False:
        os.mkdir(LIGHTTPD_CONF_TEMP_DIR)


    # port.conf
    try:
        port_conf_path = LIGHTTPD_CONF_TEMP_DIR + '/' + LIGHTTPD_PORT_CONFIG
        port_conf = LighttpdPortConf(port_conf_path)
        port_conf.write(input)
    except AttributeError:
        raise KaresansuiGadgetException(
            'Failed to write configuration value input.port=%s at %s' % \
            (input.port, port_conf_path))

    # access.conf
    try:
        access_conf_path = LIGHTTPD_CONF_TEMP_DIR + '/' + LIGHTTPD_ACCESS_CONFIG
        access_conf = LighttpdAccessConf(access_conf_path) 
        access_conf.write(input)
    except AttributeError:
        raise KaresansuiGadgetException(
            'Failed to write configuration value input.access=%s, input.access_ipaddress=%s, input.ip_list=%s at %s' % \
            (input.access, input.network, input.access_ipaddress, access_conf_path))

    # ssl.conf
    try:
        ssl_conf_path = LIGHTTPD_CONF_TEMP_DIR + '/' + LIGHTTPD_SSL_CONFIG
        ssl_conf = LighttpdSslConf(ssl_conf_path)
        ssl_conf.write(input)
    except AttributeError:
        raise KaresansuiGadgetException(
            'Failed to write configuration value input.ssl_status=%s at %s' % \
            (input.ssl_status, ssl_conf_path))

    config['application.proxy.status'] = input.proxy_status
    if input.proxy_status == PROXY_ENABLE:
        config['application.proxy.server'] = input.proxy_server
        config['application.proxy.port'] = input.proxy_port
        config['application.proxy.user'] = input.proxy_user
        config['application.proxy.password'] = input.proxy_password
    config['application.uniqkey'] = input.uniqkey
    

    return True

class Server(Rest):

    @auth
    def _GET(self, *param, **params):
        try:
            conf = env.get('KARESANSUI_CONF')
            _K2V = K2V(conf)
            config = _K2V.read()
            self.view.config = get_view_server_conf(config)
            return True
        except (IOError, KaresansuiGadgetException), kge:
            self.logger.debug(kge)
            raise KaresansuiGadgetException, kge

    @auth
    def _PUT(self, *param, **params):
        if not validates_server(self):
            return web.badrequest(self.view.alert)
       
        try:
            conf = env.get('KARESANSUI_CONF')
            _K2V = K2V(conf)
            config = _K2V.read()
            tmp_unique_key = config['application.uniqkey']
            
            # make whether unique key is unique
            uniq_key_check = findbyalluniquekey(self.orm, self.input.uniqkey)
            if uniq_key_check != [] and config['application.uniqkey'] != self.input.uniqkey:
                self.logger.debug(
                    "Update unique key failed, Already exists Unique key - %s" % \
                    uniq_key_check[0].id)
                return web.conflict(web.ctx.path)

            set_server_conf(config, self.input, self.orm)
            
            _K2V.write(config)
        except (IOError, KaresansuiGadgetException), kge:
            self.logger.debug(kge)
            raise KaresansuiGadgetException, kge
 
        # unique key
        try:
            host = findby1uniquekey(self.orm, tmp_unique_key)
            host.uniq_key = self.input.uniqkey
            update(self.orm, host)
        except KaresansuiDBException:
            # rollback
            config['application.uniqkey'] = tmp_unique_key
            _K2V.write(config)
            raise KaresansuiDBException

        # regist job
        cmdname = u"Update lighttpd config"
        options = {'dest': config['lighttpd.etc.dir']}
        action_cmd = dict2command(
            "%s/%s" % (config['application.bin.dir'], LIGHTTPD_COMMAND_UPDATE_CONFIG),
            options)

        _jobgroup = JobGroup(cmdname, sheconf['env.uniqkey'])
        _job = Job('%s command' % cmdname, 0, action_cmd)
        _jobgroup.jobs.append(_job)

        #TODO: machine_id = 1 is version 1 only
        host = findbyhost1(self.orm, 1);

        _machine2jobgroup = m2j_new(machine=host,
                            jobgroup_id=-1,
                            uniq_key=sheconf['env.uniqkey'],
                            created_user=self.me,
                            modified_user=self.me,
                            )

        save_job_collaboration(self.orm,
                               self.pysilhouette.orm,
                               _machine2jobgroup,
                               _jobgroup
                               )
        ##
        self.view.config = get_view_server_conf(config)

        return web.accepted(url=web.ctx.path)

urls = ('/setting/server/?(\.input|\.part)?$', Server,)
