#!/usr/bin/python3
# -*- coding: utf-8 -*-

import os
import sys

import logging
import configparser
from gi.repository import GLib
import dbus
import dbus.service
import dbus.mainloop.glib

import requests
import platform
import json
import socket
import random
import shutil
import subprocess
from urllib import parse

from datetime import datetime
import base64

import gettext

gettext.bindtextdomain('kim-domain', '/usr/share/kim-client/locale')
gettext.textdomain('kim-domain')
_ = gettext.gettext

logger = logging.getLogger()

SUCCESS = 0
NETWORK_ERROR = 100
REQUEST_ERROR = 101
JOIN_ERROR = 200
UNENROLL_ERROR = 300
KCM_ERROR = 400

kim_dbus = None
KIM_RPC_PORT = 31443
KCM_JOIN_PORT = 30234
KCM_RPC_PORT = 30234

HTTP_SCHEMA = 'https'

kim_config_path = "/etc/kim/default.conf"

# Used by standard_logging_setup() for console message
LOGGING_FORMAT_STANDARD_CONSOLE = '%(name)-12s: %(levelname)-8s %(message)s'
# Used by standard_logging_setup() for file message
LOGGING_FORMAT_STANDARD_FILE = '%(asctime)s %(levelname)s %(message)s'

auth_type = None
remote_server = None
new_hostname = None
auth_token = None

class InstallError(Exception):

    def __init__(self, msg = '', rval = 1):
        if msg is None:
            msg = _('undefined error') #'未定义错误'
        super(InstallError, self).__init__(msg)
        self.rval = rval

    @property
    def msg(self):
        return str(self)

def cmd_run(args, skip_output=False):
    p_in = None
    p_out = None
    p_err = None
    p_in = subprocess.PIPE
    if skip_output:
        p_out = p_err = open(os.devnull, 'w')
    else:
        p_out = subprocess.PIPE
        p_err = subprocess.PIPE
    try:
        # pylint: disable=subprocess-popen-preexec-fn
        p = subprocess.Popen(args, stdin=p_in, stdout=p_out, stderr=p_err)
        stdout, stderr = p.communicate(timeout=5)
    except KeyboardInterrupt:
        logger.debug('Process interrupted')
        p.wait()
        raise
    except:
        logger.debug('Process execution failed')
        raise
    finally:
        if skip_output:
            p_out.close()
    return p.returncode, stdout.decode(), stderr.decode()

def standard_logging_setup(filename=None, verbose=False, debug=False,
                           filemode='w', console_format=None):
    if console_format is None:
        console_format = LOGGING_FORMAT_STANDARD_CONSOLE

    root_logger = logging.getLogger()
    root_logger.setLevel(logging.DEBUG)

    # File output is always logged at debug level
    if filename is not None:
        umask = os.umask(0o133)
        try:
            file_handler = logging.FileHandler(filename, mode=filemode)
        finally:
            os.umask(umask)
        file_handler.setLevel(logging.DEBUG)
        file_handler.setFormatter(logging.Formatter(LOGGING_FORMAT_STANDARD_FILE))
        root_logger.addHandler(file_handler)

    level = logging.ERROR
    if verbose:
        level = logging.INFO
    if debug:
        level = logging.DEBUG

    console_handler = logging.StreamHandler()
    console_handler.setLevel(level)
    console_handler.setFormatter(logging.Formatter(console_format))
    root_logger.addHandler(console_handler)

def extract_ip():
    st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        st.connect(('10.255.255.255', 1))
        IP = st.getsockname()[0]
    except Exception:
        IP = '127.0.0.1'
    finally:
        st.close()
    return IP

### 获取终端序列号sn
def get_device_serialno():
    run_args = ['/usr/sbin/dmidecode', '-s', 'system-serial-number']
    try:
        ret, out, err = cmd_run(run_args)
    except Exception:
        return ''
    else:
        return out.rstrip('\n')

def get_devsn():
    run_args = ['/usr/bin/kylin_gen_register']
    try:
        ret, out, err = cmd_run(run_args)
    except Exception:
        return ''
    else:
        return out.rstrip('\n')

### 获取默认的服务器配置
def get_server_info():
    global KIM_RPC_PORT
    global KCM_JOIN_PORT
    global KCM_RPC_PORT
    config = configparser.ConfigParser()
    config.read('/etc/.kimserver.conf', encoding='utf-8')
    server = config.get('default', 'server')
    port = config.getint('default', 'port')
    if KIM_RPC_PORT != port:
        KIM_RPC_PORT = port
    if KCM_JOIN_PORT != port:
        KCM_JOIN_PORT = port
        KCM_RPC_PORT = KCM_JOIN_PORT
    return server, port

def update_server_info(server, port=None):
    config = configparser.ConfigParser()
    config.read('/etc/.kimserver.conf', encoding='utf-8')
    if not config.has_section('default'):
        config.add_section('default')
    config.set('default', 'server', server)
    if port is None:
        port = KIM_RPC_PORT
    config.set('default', 'port', str(port))
    with open('/etc/.kimserver.conf', 'w') as f:
        config.write(f)

def enable_kimbackend():
    cmd_run(['/usr/bin/systemctl', 'restart', 'kimbackend.service'])
    cmd_run(['/usr/bin/systemctl', 'enable', 'kimbackend.service'])

def disable_kimbackend():
    cmd_run(['/usr/bin/systemctl', 'disable', 'kimbackend.service'])
    cmd_run(['/usr/bin/systemctl', 'stop', 'kimbackend.service'])

def kim_configure(server, port, provider='kim', nossl = False):
    config = configparser.ConfigParser()
    config.add_section('global')
    config.set('global', 'provider', provider)
    config.set('global', 'cachetime', '900')
    config.add_section(provider)
    config.set(provider, 'server', server)
    if port == 0:
        port = KCM_JOIN_PORT
    config.set(provider, 'port', str(port))
    config.set(provider, 'nossl', str(nossl))
    with open(kim_config_path, 'w') as f:
        config.write(f)
    kim_config_krb5()
    enable_kimbackend()

def kim_config_krb5():
    config = configparser.RawConfigParser()
    config.add_section('libdefaults')
    #config.set('libdefaults', 'default_realm', None)
    config.set('libdefaults', 'dns_lookup_realm', 'false')
    config.set('libdefaults', 'dns_lookup_kdc', 'true')
    config.set('libdefaults', 'rdns', 'false')
    config.set('libdefaults', 'dns_canonicalize_hostname', 'false')
    config.set('libdefaults', 'ticket_lifetime', '24h')
    config.set('libdefaults', 'forwardable', 'true')
    config.set('libdefaults', 'udp_preference_limit', '0')
    config.set('libdefaults', 'default_ccache_name', 'KEYRING:persistent:%{uid}')
    with open('/etc/krb5.conf', 'w') as f:
        config.write(f)



FIREFOX_PREFERENCES_FILENAME = "all-kimsso.js"
FIREFOX_PREFERENCES_REL_PATH = "browser/defaults/preferences"

FIREFOX_ESR_PATH = "/usr/lib/firefox-esr"


def configure_firefox(domain, firefox_dir = None):
    try:
        logger.debug("Setting up Firefox configuration.")

        preferences_dir = None

        # Check user specified location of firefox install directory
        if firefox_dir is not None:
            pref_path = os.path.join(firefox_dir,
                                     FIREFOX_PREFERENCES_REL_PATH)
            if os.path.isdir(pref_path):
                preferences_dir = pref_path
            else:
                logger.error("Directory '%s' does not exists.", pref_path)
        else:
            # test if firefox is installed
            if os.path.isdir(FIREFOX_ESR_PATH):
                pref_path = os.path.join(FIREFOX_ESR_PATH, FIREFOX_PREFERENCES_REL_PATH)
                if os.path.isdir(pref_path):
                    preferences_dir = pref_path

            else:
                logger.error(
                    "Firefox configuration skipped (Firefox not found).")
                return

        # setting up firefox
        if preferences_dir is not None:

            # user could specify relative path, we need to store absolute
            preferences_dir = os.path.abspath(preferences_dir)
            logger.debug(
                "Firefox preferences directory found '%s'.", preferences_dir)
            preferences_fname = os.path.join(
                preferences_dir, FIREFOX_PREFERENCES_FILENAME)
            logger.debug(
                "Firefox trusted uris will be set as '.%s' domain.", domain)
            logger.debug(
                "Firefox configuration will be stored in '%s' file.",
                preferences_fname)

            try:
                with open(preferences_fname, 'w') as f:
                    f.write('\n/* Kerberos SSO configuration */\n')
                    f.write('\n/* These are the defaults */\n')
                    f.write('pref("network.negotiate-auth.gsslib", "");\n')
                    f.write('pref("network.negotiate-auth.trusted-uris", ".%s");\n' % domain)
                    f.write('pref("network.negotiate-auth.using-native-gsslib", true);\n')
                    f.write('pref("network.negotiate-auth.allow-proxies", true);\n')
                logger.info("Firefox sucessfully configured.")
            except Exception as e:
                logger.debug(
                    "An error occured during creating preferences file: %s.",
                    e)
                logger.error("Firefox configuration failed.")
        else:
            logger.debug("Firefox preferences directory not found.")
            logger.error("Firefox configuration failed.")

    except Exception as e:
        logger.debug("%s", str(e))
        logger.error("Firefox configuration failed.")

CHROMIUM_PREFERENCES_FILENAME = "kimsso.json"
CHROMIUM_PREFERENCES_REL_PATH = "policies/managed"

CHROMIUM_PATH = "/etc/chromium-browser"

def configure_chromium(domain, chromium_dir=None):
    try:
        logger.debug("Setting up Chrome configuration.")
        preferences_dir = None
        if chromium_dir is not None:
            pref_path = os.path.join(chromium_dir, CHROMIUM_PREFERENCES_REL_PATH)
            if os.path.isdir(pref_path):
                preferences_dir = pref_path
            else:
                logger.error("Directory '%s' does not exists.", pref_path)
        else:
            if os.path.isdir(CHROMIUM_PATH):
                pref_path = os.path.join(CHROMIUM_PATH, CHROMIUM_PREFERENCES_REL_PATH)
                if os.path.isdir(pref_path):
                    preferences_dir = pref_path
            else:
                logger.error("Chromium configuration skipped (Chromium not found).")

        if preferences_dir is not None:
            preferences_dir = os.path.abspath(preferences_dir)
            logger.debug(
                "Chromium preferences directory found '%s'.", preferences_dir)
            preferences_fname = os.path.join(
                preferences_dir, CHROMIUM_PREFERENCES_FILENAME)
            logger.debug(
                "Chromium trusted uris will be set as '.%s' domain.", domain)
            logger.debug(
                "Chromium configuration will be stored in '%s' file.", preferences_fname)

            try:
                with open(preferences_fname, 'w') as f:
                    f.write('{\n')
                    f.write('  "AuthServerWhitelist":  "*.%s",\n' % domain)
                    f.write('  "AuthServerAllowlist": "*.%s",\n' % domain)
                    f.write('  "AuthNegotiateDelegateAllowlist": "*.%s"\n' % domain)
                    f.write('}\n')
                logger.info("Chromium sucessfully configured.")
            except Exception as e:
                logger.debug(
                    "An error occured during creating preferences file: %s.",
                    e)
                logger.error("Chromium configuration failed.")
    except Exception as e:
        logger.debug("%s", str(e))
        logger.error("chrome configuration failed.")

def kim_browser_configure(domain):
    configure_chromium(domain)
    configure_firefox(domain)
    configure_chromium(domain, '/etc/opt/chromium')
    qax_path = '/etc/chromium'
    if not os.path.exists(qax_path):
        pref_path = os.path.join(qax_path, CHROMIUM_PREFERENCES_REL_PATH)
        os.makedirs(pref_path)
    configure_chromium(domain, qax_path)

KRB_CONF_DIR = "/var/lib/kim/krbconf"

def kim_add_krbconf(domain, server):
    realm = domain.upper()
    kdcfile = os.path.join(KRB_CONF_DIR, 'kdcinfo.%s' % realm)
    kpassfile = os.path.join(KRB_CONF_DIR, 'kpasswdinfo.%s' % realm)

    try:
        with open(kdcfile, 'w') as f:
            f.write('%s:30288"\n' % server)
        logger.info("kdcconf sucessfully configured.")

    except Exception as e:
        logger.error("kdc configuration failed.")

def kim_krb5_setall(server):
    domain = None
    if '.' in server:
        short_name, _sep, domain = server.partition('.')
    if domain is None:
        logger.error("need fqdn servername")
        return
    kim_add_krbconf(domain, server)
    kim_browser_configure(domain)

def kim_sethostname(hostname, domain = None):
    if '.' in hostname:
        short_name, _sep, hostdomain = hostname.partition('.')
        if domain is None:
            domain = hostdomain
    else:
        short_name = hostname
        if domain is None:
            logger.warning('Invalid hostname %s.', hostname)
            #raise InstallError(_('Invalid hostname, use fqdn format.'), rval=REQUEST_ERROR) #请指定域名或者设定fqdn格式主机名
    if domain is None:
        fqdn = short_name
    else:
        fqdn = '%s.%s' % (short_name, domain)
    orig_hostname = ''
    try:
        orig_hostname = socket.gethostname()
    except Exception:
        pass
    if orig_hostname != fqdn:
        #ipautil.run([paths.BIN_HOSTNAMECTL, 'set-hostname', hostname])
        cmd_run(['/usr/bin/hostnamectl', 'set-hostname', fqdn])
        with open('/etc/hosts', 'r') as hf:
            with open('/etc/hosts.kimbak', 'w') as fw:
                modify = False
                for line in hf:
                    if line.startswith('127.0.1.1'):
                        new_line = '127.0.1.1\t%s %s\n' % (fqdn, short_name)
                        modify = True
                    else:
                        new_line = line
                    fw.write(new_line)
                if not modify:
                    fw.write('127.0.1.1\t%s %s\n' % (fqdn, short_name))
        shutil.move('/etc/hosts.kimbak', '/etc/hosts')


def kim_setmkhomedir():
    new_config = []
    edited = False
    with open('/usr/share/pam-configs/mkhomedir', 'r') as fr:
        for line in fr:
            if 'pam_mkhomedir.so' in line and 'umask=0077' not in line:
                edited = True
                new_config.append('     optional                        pam_mkhomedir.so umask=0077\n')
            else:
                new_config.append(line)
    if edited:
        with open('/usr/share/pam-configs/mkhomedir', 'w') as fw:
            for lw in new_config:
                fw.write(lw)
    try:
        cmd_run(['/usr/sbin/pam-auth-update', '--package', '--enable', 'mkhomedir'])
    except Exception as e:
        raise InstallError(_('set auto create home directtory failed.'), rval = JOIN_ERROR)



### 获取当前管控状态，1表示已加域，0表示未加域
def get_domain_status():
    status = os.path.exists(kim_config_path)
    return 1 if status else 0

### 是否存在电池，用于判断是否为笔记本
def get_battery_status():
    system_bus = dbus.SystemBus()
    try:
        proxy = system_bus.get_object('org.freedesktop.UPower',
                       '/org/freedesktop/UPower')
        upower = dbus.Interface(proxy,dbus_interface="org.freedesktop.UPower")
        devices = upower.EnumerateDevices()
        for dvc in devices:
            if dvc.startswith('/org/freedesktop/UPower/devices/battery'):
                return True
    except Exception as e:
        pass
    return False

### 判断设备类型为笔记本还是台式机
def get_device_type():
    run_args = ['/usr/sbin/dmidecode', '-s', 'chassis-type']
    notebook_type = ['Notebook', 'Laptop', 'Portable']
    chassis_type = None
    try:
        _ret, out, _err = cmd_run(run_args)
    except Exception:
        out = ''
    else:
        chassis_type = out.rstrip('\n')
    if chassis_type and chassis_type in notebook_type:
        if get_battery_status():
            return 'Notebook'
    return 'Desktop'

def kim_extra_config(server):
    url = "https://%s:%d/aptserver/icbc_config.json" % (server, KCM_RPC_PORT)
    dest_file = '/usr/lib/biometric-authentication/drivers/extra/icbc/configs/icbc_config.json'
    shutil.copy(dest_file, dest_file + '.kimbak')
    try:
        resp = requests.get(url, verify=False, timeout=8)
        if resp.status_code != 200:
            raise InstallError(_('Failed to get config'), rval = REQUEST_ERROR)
        with open(dest_file, 'w') as f:
            f.write(resp.text)
    except InstallError:
        raise
    except Exception as e:
        raise InstallError(_('Failed to get config'), rval = REQUEST_ERROR)


### 退出域，通过dbus调用之前的接口
def unrolling_host(rollback = False):
    #hostname = socket.getfqdn()
    if rollback:
        ret = kcm_force_uninstall()
    else:
        ret, msg = uninstall_kcm()
    if ret != 0 and not rollback:
        logger.error('uninstall failed:%s' % msg)
        raise InstallError(msg, rval = UNENROLL_ERROR)
    if ret != 0:
        logger.warning('rollback failed')
    if os.path.exists(kim_config_path):
        os.remove(kim_config_path)
        disable_kimbackend()
    if os.path.exists('/var/lib/kim/data/kimcd.db'):
        os.remove('/var/lib/kim/data/kimcd.db')
    if os.path.exists('/var/lib/kim/private/pri.db'):
        os.remove('/var/lib/kim/private/pri.db')
    #system_bus = dbus.SystemBus()
    #try:
        #proxy = system_bus.get_object('com.kim.client',
        #               '/clientinstall')
        #client = dbus.Interface(proxy,dbus_interface="com.kim.client.clientinstall")
        #ret, msg = client.kim_client_leave()
    #    ret, msg = system_bus.call_blocking(
    #        'com.kim.client', '/clientinstall', 
    #        'com.kim.client.clientinstall', 'kim_client_leave', '', 
    #        [],timeout=90)
    #    if ret != SUCCESS:
    #        raise InstallError(msg, rval = UNENROLL_ERROR)
    #except InstallError:
    #    raise
    #except Exception as e:
    #    raise InstallError(str(e), rval = UNENROLL_ERROR)


def get_err_from_code(code):
    msg = '未知错误，请查看日志'
    if code == 300000:
        msg = '请求成功'
    elif code == 300001:
        msg = '关联用户不存在'
    elif code == 300002:
        msg = 'sn不存在'
    elif code == 300003:
        msg = 'sn关联主机失败'
    elif code == 300004:
        msg = '主机名已存在或无效的主机名'
    elif code == 300005:
        msg = '用户不存在'
    elif code == 300007:
        msg = '激活注册码不存在'
    elif code == 300008:
        msg = '激活注册码已存在'
    elif code == 300009:
        msg = '添加主机记录失败'
    elif code == 300010:
        msg = '主机关联组织失败'
    elif code == 300011:
        msg = '添加访问规则失败'
    elif code == 300012:
        msg = '用户不在组织内'
    elif code == 300013:
        msg = '发布策略错误'
    elif code == 300014:
        msg = '参数错误，密码编码无效'
    elif code == 300400:
        msg = '用户认证失败，请检查用户名和密码'
    elif code == 300401:
        msg = '请求未认证，请重新认证'
    elif code == 300403:
        msg = '用户权限不足'
    elif code == 300500:
        msg = '服务器错误'
    return msg

def http_check_server_info(server):
    global auth_type
    global remote_server
    remote_server = server
    url = '%s://%s:%d/kim/checkServer' % (HTTP_SCHEMA, server, KCM_JOIN_PORT)
    language = os.environ['LANGUAGE']
    headers = {
        'Accept-Language': language.replace('_', '-')
    }
    try:
        kim_dbus.joinProgress(3)
        resp = requests.get(url=url, headers=headers, verify=False, timeout=8)
        if resp.status_code != 200:
            raise InstallError('服务器请求错误[%d]: 请确认服务器地址是否正确' % resp.status_code, rval = REQUEST_ERROR)
        #resp_json = resp.json
        resp_json = json.loads(resp.text)
        print(resp_json)
        resp_ret = resp_json['ret']
        resp_msg = resp_json['msg']
        if resp_ret != 300000:
            logger.error('Check server error: %s.', resp_msg)
            #join_msg = _('unknow error, please see the infomation in log file')
            if 'Chinese' in resp_json:
                join_msg = resp_json['Chinese']
            else:
                join_msg = get_err_from_code(resp_ret)
            raise InstallError(join_msg, rval = REQUEST_ERROR)
        resp_data = resp_json['data']
        print(resp_data)
        server_time = resp_data['nowtime']
        dt_server = datetime.fromisoformat(server_time)
        #datetime.strptime(server_time,"%Y-%m-%dT%H:%M:%S%z")
        dt_now = datetime.now()
        dt_delta = dt_now.timestamp() - dt_server.timestamp()
        if(dt_delta > 120):
            logger.warning('get server time: %s' % server_time)
            raise InstallError(_('check server failed: The client and server time skews too large.'), rval = REQUEST_ERROR)
        auth_type = resp_data['authtype']
        remote_server = server
        #kim_sethostname(resp_hostname)
    except InstallError:
        raise
    except requests.exceptions.InvalidURL:
        raise InstallError(_('invalid server url'), rval = REQUEST_ERROR) #无效的服务器地址
    except requests.exceptions.ConnectTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = REQUEST_ERROR)
    except requests.exceptions.ReadTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = REQUEST_ERROR)
    except requests.exceptions.ConnectionError as conn_e:
        logger.error(conn_e)
        raise InstallError(_('connect to server failed, please check your network'), rval = REQUEST_ERROR) #'连接服务器失败，请检查网络状态'
    except ValueError as e:
        logger.error(e)
        raise InstallError(_('check server error, invalid params: %s') % str(e), rval = REQUEST_ERROR)
    except Exception as e:
        print('%s:%s' % (type(e), str(e)))
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = REQUEST_ERROR)


def get_hostname_by_sn(server, sn):
    url = 'https://%s:%d/kim/validateSN' % (server, KCM_JOIN_PORT)
    language = os.environ['LANGUAGE']
    headers = {
        'Accept-Language': language.replace('_', '-')
    }
    datas = {
        "sn": sn,
        "device_type": get_device_type(),
        "devsn": get_devsn()
    }
    try:
        kim_dbus.joinProgress(3)
        resp = requests.post(url=url, json=datas, headers=headers, verify=False, timeout=8)
        if resp.status_code != 200:
            raise InstallError(_('Validate sn error: server request error[%d]') % resp.status_code, rval = REQUEST_ERROR)
        #resp_json = resp.json
        resp_json = json.loads(resp.text)
        print(resp_json)
        resp_ret = resp_json['ret']
        resp_msg = resp_json['msg']
        if resp_ret != 300000:
            logger.error('Validate sn error: %s.', resp_msg)
            join_msg = _('unknow error, please see the infomation in log file')
            if resp_ret == 300002:
                join_msg = _('Host authenticaion failed, please input sn to server.')
            elif resp_ret == 300001:
                join_msg = _('Host authentication failed, please bind an employee number.')
            elif resp_ret == 300008:
                join_msg = _('Host authentication failed, devsn has already exists.')
            elif resp_ret == 300500:
                join_msg = _('Host authentication failed, remote server error.')
            raise InstallError(join_msg, rval = REQUEST_ERROR)
        resp_data = resp_json['data']
        print(resp_data)
        resp_hostname = resp_data['hostname']
        kim_sethostname(resp_hostname)
    except InstallError:
        raise
    except requests.exceptions.InvalidURL:
        raise InstallError(_('invalid server url'), rval = REQUEST_ERROR) #无效的服务器地址
    except requests.exceptions.ConnectTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = REQUEST_ERROR)
    except requests.exceptions.ReadTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = REQUEST_ERROR)
    except requests.exceptions.ConnectionError as conn_e:
        logger.error(conn_e)
        raise InstallError(_('connect to server failed, please check your network'), rval = REQUEST_ERROR) #'连接服务器失败，请检查网络状态'
    except Exception as e:
        print('%s:%s' % (type(e), str(e)))
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = REQUEST_ERROR)

def kcm_init(servername):
    system_bus = dbus.SystemBus()
    try:
        proxy = system_bus.get_object('com.kylin.kydevmonit.domainhook',
                       '/com/kylin/kydevmonit/domainhook')
        kcm = dbus.Interface(proxy,dbus_interface="com.kylin.kydevmonit.domainhook")
        ret, msg = kcm.init_kcm(servername, str(KCM_RPC_PORT), timeout=10)
        if ret != 0:
            raise InstallError(_('Client configure error:%s') % msg,rval = JOIN_ERROR)
    except Exception as e:
        pass


def http_join_comfirm(server):
    url = '%s://%s:%d/kim/reportRegistrationStatus' % (HTTP_SCHEMA, server, KCM_JOIN_PORT)
    language = os.environ['LANGUAGE']
    headers = {
        'Accept-Language': language.replace('_', '-'),
        'Authorization': auth_token
    }
    datas = {
        #"sn": sn,
        "hostname": new_hostname,
        "devsn": get_devsn()
    }
    try:
        kim_dbus.joinProgress(80)
        resp = requests.post(url=url, json=datas, headers=headers, verify=False, timeout=8)
        if resp.status_code != 200:
            raise InstallError(_('Confirm error: server request error[%d]') % resp.status_code, rval = REQUEST_ERROR)
        #resp_json = resp.json
        resp_json = json.loads(resp.text)
        print(resp.text)
        resp_ret = resp_json['ret']
        resp_msg = resp_json['msg']
        if resp_ret != 300000:
            logger.error('Validate sn error: %s.', resp_msg)
            join_msg = _('unknow error, please see the infomation in log file')
            if resp_ret == 300003:
                join_msg = _('Enrolled failed, can not bind a user and organization')
            elif resp_ret == 300002:
                join_msg = _('Host authentication failed, please bind an employee number.')
            elif resp_ret == 300003:
                join_msg = _('Enrolled failed, host related error.')
            elif resp_ret == 300004:
                join_msg = _('Enrolled failed, hostname is already exist or invalid, please try again.')
            elif resp_ret == 300009:
                join_msg = _('Enrolled failed, add host error, please try again.')
            elif resp_ret == 300010:
                join_msg = _('Enrolled failed, add host to organization failed.')
            elif resp_ret == 300011:
                join_msg = _('Enrolled failed, bind to user failed.')
            elif resp_ret == 300500:
                join_msg = _('Enrolled failed, remote server error')
            raise InstallError(join_msg, rval = JOIN_ERROR)
        resp_data = resp_json['data']
        resp_hostname = resp_data['hostname']
        print(resp_hostname)
        #kim_sethostname(resp_hostname)
    except InstallError:
        raise
    except requests.exceptions.InvalidURL:
        raise InstallError(_('invalid server url'), rval = JOIN_ERROR) #无效的服务器地址
    except requests.exceptions.ConnectTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = JOIN_ERROR)
    except requests.exceptions.ReadTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = JOIN_ERROR)
    except requests.exceptions.ConnectionError as conn_e:
        logger.error(conn_e)
        raise InstallError(_('connect to server failed, please check your network'), rval = JOIN_ERROR) #'连接服务器失败，请检查网络状态'
    except Exception as e:
        print('%s:%s' % (type(e), str(e)))
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = JOIN_ERROR)

def admin_confirm_join_http(server):
    url = '%s://%s:%d/kim/regconfirm' % (HTTP_SCHEMA, server, KCM_JOIN_PORT)
    language = os.environ['LANGUAGE']
    headers = {
        'Accept-Language': language.replace('_', '-'),
        'Authorization': auth_token
    }
    datas = {
        #"sn": sn,
        "hostname": new_hostname,
        "devsn": get_devsn()
    }
    try:
        kim_dbus.joinProgress(80)
        resp = requests.post(url=url, json=datas, headers=headers, verify=False, timeout=8)
        if resp.status_code != 200:
            raise InstallError('注册确认请求错误[%d]' % resp.status_code, rval = JOIN_ERROR)
        #resp_json = resp.json
        resp_json = json.loads(resp.text)
        print(resp.text)
        resp_ret = resp_json['ret']
        resp_msg = resp_json['msg']
        if resp_ret != 300000:
            logger.error('confirm register error: %s.', resp_msg)
            if 'Chinese' in resp_json:
                join_msg = resp_json['Chinese']
            else:
                join_msg = get_err_from_code(resp_ret)
            #join_msg = get_err_from_code(resp_ret)
            raise InstallError(join_msg, rval = JOIN_ERROR)
        #resp_data = resp_json['data']
        #resp_hostname = resp_data['hostname']
        #print(resp_hostname)
        #kim_sethostname(resp_hostname)
    except InstallError:
        raise
    except requests.exceptions.InvalidURL:
        raise InstallError(_('invalid server url'), rval = JOIN_ERROR) #无效的服务器地址
    except requests.exceptions.ConnectTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = JOIN_ERROR)
    except requests.exceptions.ReadTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = JOIN_ERROR)
    except requests.exceptions.ConnectionError as conn_e:
        logger.error(conn_e)
        raise InstallError(_('connect to server failed, please check your network'), rval = JOIN_ERROR) #'连接服务器失败，请检查网络状态'
    except Exception as e:
        print('%s:%s' % (type(e), str(e)))
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = JOIN_ERROR)

def check_kcm_availability(server):
    kim_server = server
    #try:
    #    url = 'http://%s:%d' % (server, KCM_RPC_PORT)
    #    resp = requests.get(url=url, allow_redirects=False, verify=False, timeout=8)
    #    if resp.status_code > 300 and resp.status_code < 310:
    #        real_addr = resp.headers.get('Location')
    #        kim_server = real_addr.split('//')[-1].split(':')[0]
    #except Exception as e:
    #    pass
    system_bus = dbus.SystemBus()
    try:
        proxy = system_bus.get_object('com.kylin.kydevmonit.domainhook',
                       '/com/kylin/kydevmonit/domainhook')
        kcm = dbus.Interface(proxy,dbus_interface="com.kylin.kydevmonit.domainhook")
        ret, msg = kcm.check_kcm_availability(kim_server, str(KCM_RPC_PORT))
        if ret != SUCCESS:
            raise InstallError(msg, rval = KCM_ERROR)
    except InstallError:
        raise
    except dbus.exceptions.DBusException as dbus_e:
        logger.error(dbus_e)
        raise InstallError(_('dbus error, please check the service status'), rval = KCM_ERROR)
    except Exception as e:
        logger.error(e)
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = REQUEST_ERROR)

def set_lang(lang):
    os.environ['LANGUAGE'] = lang
    system_bus = dbus.SystemBus()
    try:
        proxy = system_bus.get_object('com.kylin.kydevmonit.domainhook',
                       '/com/kylin/kydevmonit/domainhook')
        kcm = dbus.Interface(proxy,dbus_interface="com.kylin.kydevmonit.domainhook")
        kcm.set_lang(lang)
    except dbus.exceptions.DBusException as dbus_e:
        logger.error(dbus_e)
    except Exception as e:
        logger.error(e)

def uninstall_kcm():
    system_bus = dbus.SystemBus()
    ret = 0
    msg = 'uncall'
    try:
        proxy = system_bus.get_object('com.kylin.kydevmonit.domainhook',
                       '/com/kylin/kydevmonit/domainhook')
        kcm = dbus.Interface(proxy,dbus_interface="com.kylin.kydevmonit.domainhook")
        ret, msg = kcm.uninstall()
    except dbus.exceptions.DBusException as dbus_e:
        logger.error(dbus_e)
    except Exception as e:
        logger.error(e)
    return ret, msg

def kcm_force_uninstall():
    system_bus = dbus.SystemBus()
    ret = -1
    try:
        proxy = system_bus.get_object('com.kylin.kydevmonit.domainhook',
                       '/com/kylin/kydevmonit/domainhook')
        kcm = dbus.Interface(proxy,dbus_interface="com.kylin.kydevmonit.domainhook")
        ret = kcm.force_uninstall()
    except dbus.exceptions.UnknownMethodException:
        ret = 0
    except dbus.exceptions.DBusException as dbus_e:
        logger.error(dbus_e)
    except Exception as e:
        logger.error(e)
    return ret

def device_sn_join(server):
    try:
        cmd_run(['setstatus', '-f', 'netctl', 'off', '-p'])
    except Exception as e:
        print(e)
    sn = get_device_serialno()
    try:
        kim_dbus.joinProgress(5)
        get_hostname_by_sn(server, sn)
        kim_dbus.joinProgress(20)
        check_kcm_availability(server)
        #http_join_by_sn(server, sn)
        kim_dbus.joinProgress(40)
        kim_extra_config(server)
        kim_configure(server, KCM_JOIN_PORT, provider='kcm')
        kim_setmkhomedir()
        kim_dbus.joinProgress(60)
        kcm_init(server)
        kim_dbus.joinProgress(80)
        http_join_comfirm(server, sn)
        update_server_info(server, KCM_JOIN_PORT)
        kim_dbus.joinProgress(100)
    except InstallError as ie:
        if ie.rval == JOIN_ERROR:
            try:
                unrolling_host()
            except Exception as e:
                print(e)
    except Exception as e:
        print(e)
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = JOIN_ERROR)

def check_server_info(server, hostname):
    global new_hostname
    http_check_server_info(server)
    new_hostname = hostname

def user_auth_proxy(server, username, password):
    global auth_token
    if server is None:
        raise InstallError(_('can not get server, please check server first.'), rval = REQUEST_ERROR)
    url = '%s://%s:%d/kim/auth' % (HTTP_SCHEMA, server, KCM_JOIN_PORT)
    language = os.environ['LANGUAGE']
    headers = {
        'Accept-Language': language.replace('_', '-')
    }
    datas = {
        'username': username,
        'btoken': base64.b64encode(password.encode('utf-8')),
        'authplugin': auth_type,
        'ipaddr': extract_ip()
    }
    try:
        kim_dbus.joinProgress(20)
        resp = requests.post(url=url, json=datas, headers=headers, verify=False, timeout=8)
        if resp.status_code != 200:
            raise InstallError('请求认证失败[%d]' % resp.status_code, rval = REQUEST_ERROR)
        #resp_json = resp.json
        resp_json = json.loads(resp.text)
        print(resp.text)
        resp_ret = resp_json['ret']
        resp_msg = resp_json['msg']
        if resp_ret != 300000:
            logger.error('auth user error: %s.', resp_msg)
            if 'Chinese' in resp_json:
                join_msg = resp_json['Chinese']
            else:
                join_msg = get_err_from_code(resp_ret)
            #join_msg = get_err_from_code(resp_ret)
            raise InstallError(join_msg, rval = JOIN_ERROR)
        resp_data = resp_json['data']
        auth_token = resp_data['token']
    except InstallError:
        raise
    except requests.exceptions.InvalidURL:
        raise InstallError(_('invalid server url'), rval = JOIN_ERROR) #无效的服务器地址
    except requests.exceptions.ConnectTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = JOIN_ERROR)
    except requests.exceptions.ReadTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = JOIN_ERROR)
    except requests.exceptions.ConnectionError as conn_e:
        logger.error(conn_e)
        raise InstallError(_('connect to server failed, please check your network'), rval = JOIN_ERROR) #'连接服务器失败，请检查网络状态'
    except Exception as e:
        print('%s:%s' % (type(e), str(e)))
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = JOIN_ERROR)

def admin_verify_join_GSSAPI(server, user, password):
    pass

def admin_verify_join_http(server, hostname):
    url = '%s://%s:%d/kim/hostreg' % (HTTP_SCHEMA, server, KCM_JOIN_PORT)
    language = os.environ['LANGUAGE']
    headers = {
        'Accept-Language': language.replace('_', '-'),
        'Authorization': auth_token
    }
    datas = {
        #"sn": sn,
        "hostname": hostname,
        "devsn": get_devsn()
    }
    try:
        kim_dbus.joinProgress(50)
        resp = requests.post(url=url, json=datas, headers=headers, verify=False, timeout=8)
        if resp.status_code != 200:
            raise InstallError('终端注册请求错误[%d]' % resp.status_code, rval = JOIN_ERROR)
        #resp_json = resp.json
        resp_json = json.loads(resp.text)
        print(resp.text)
        resp_ret = resp_json['ret']
        resp_msg = resp_json['msg']
        if resp_ret != 300000:
            logger.error('Host reg error: %s.', resp_msg)
            if 'Chinese' in resp_json:
                join_msg = resp_json['Chinese']
            else:
                join_msg = get_err_from_code(resp_ret)
            #join_msg = get_err_from_code(resp_ret)
            raise InstallError(join_msg, rval = JOIN_ERROR)
        resp_data = resp_json['data']
        resp_hostname = resp_data['hostname']
        print(resp_hostname)
        kim_krb5_setall(server)
        #kim_sethostname(resp_hostname)
    except InstallError:
        raise
    except requests.exceptions.InvalidURL:
        raise InstallError(_('invalid server url'), rval = JOIN_ERROR) #无效的服务器地址
    except requests.exceptions.ConnectTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = JOIN_ERROR)
    except requests.exceptions.ReadTimeout:
        raise InstallError(_('connect to server failed: timeout.'), rval = JOIN_ERROR)
    except requests.exceptions.ConnectionError as conn_e:
        logger.error(conn_e)
        raise InstallError(_('connect to server failed, please check your network'), rval = JOIN_ERROR) #'连接服务器失败，请检查网络状态'
    except Exception as e:
        print('%s:%s' % (type(e), str(e)))
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = JOIN_ERROR)

def admin_verify_join(user, password):
    if auth_type == 'GSSAPI':
        pass
    else:
        user_auth_proxy(remote_server, user, password)
    check_kcm_availability(remote_server)
    admin_verify_join_http(remote_server, new_hostname)

def admin_join_confirm():
    try:
        kim_configure(remote_server, KCM_JOIN_PORT, provider='kcm')
        kim_setmkhomedir()
        kim_dbus.joinProgress(60)
        kcm_init(remote_server)
        kim_dbus.joinProgress(80)
        kim_sethostname(new_hostname)
        admin_confirm_join_http(remote_server)
        #update_server_info(remote_server, KCM_JOIN_PORT)
        kim_dbus.joinProgress(100)
    except InstallError as ie:
        if ie.rval == JOIN_ERROR:
            try:
                unrolling_host(rollback = True)
            except Exception as e:
                logger.error(e)
        raise ie
    except Exception as e:
        logger.error(e)
        raise InstallError(_('unknow error, please see the infomation in log file'), rval = JOIN_ERROR)

class KimDomain(dbus.service.Object):
    def __init__(self, bus, object_path):
        logger.info('kim service start.')
        dbus.service.Object.__init__(self, bus, object_path)
        self._last_input = None

    def getSenderLang(self, sender):
        lang = 'zh_CN'
        try:
            system_bus = dbus.SystemBus()
            send_uid = system_bus.get_unix_user(sender)
            path = '/org/freedesktop/Accounts/User%d' % send_uid
            proxy = system_bus.get_object('org.freedesktop.Accounts', path)
            auser = dbus.Interface(proxy,dbus_interface="org.freedesktop.DBus.Properties")
            lang = auser.Get('org.freedesktop.Accounts.User', 'Language')
        except Exception as e:
            print(e)
        return lang

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='', out_signature='s')
    def getDefaultServer(self):
        try:
            server, _port = get_server_info()
            return server
        except Exception as e:
            return ''

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='', out_signature='s')
    def getSerialNo(self):
        try:
            sn = get_device_serialno()
            return sn
        except Exception as e:
            return ''

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='s', out_signature='is', sender_keyword='sender')
    def deviceSNJoin(self, server, sender = None):
        logger.info('start join server[%s].' % server)
        try:
            lang = self.getSenderLang(sender)
            #os.environ['LANGUAGE'] = lang
            set_lang(lang)
            device_sn_join(server)
        except InstallError as ie:
            return ie.rval, ie.msg
        return SUCCESS, _('register success') #'注册成功'

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='ss', out_signature='is', sender_keyword='sender')
    def checkServer(self, server, hostname, sender = None):
        logger.info('start check server[%s].' % server)
        try:
            lang = self.getSenderLang(sender)
            set_lang(lang)
            check_server_info(server, hostname)
        except InstallError as ie:
            return ie.rval, ie.msg
        return SUCCESS, _('success')

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='ss', out_signature='is', sender_keyword='sender')
    def userVerifyJoin(self, user, password, sender = None):
        logger.info('start join server.')
        try:
            lang = self.getSenderLang(sender)
            #os.environ['LANGUAGE'] = lang
            set_lang(lang)
            admin_verify_join(user, password)
        except InstallError as ie:
            return ie.rval, ie.msg
        return SUCCESS, _('success')

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='', out_signature='is', sender_keyword='sender')
    def joinComfirm(self, sender = None):
        logger.info('comfirm join server.')
        try:
            lang = self.getSenderLang(sender)
            #os.environ['LANGUAGE'] = lang
            set_lang(lang)
            admin_join_confirm()
        except InstallError as ie:
            return ie.rval, ie.msg
        return SUCCESS, _('success')

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='ssss', out_signature='is', sender_keyword='sender')
    def userJoinCommand(self, server, hostname, user, password, sender = None):
        logger.info('start join server[%s].' % server)
        try:
            lang = self.getSenderLang(sender)
            #os.environ['LANGUAGE'] = lang
            set_lang(lang)
            check_server_info(server, hostname)
            admin_verify_join(user, password)
            admin_join_confirm()
        except InstallError as ie:
            return ie.rval, ie.msg
        return SUCCESS, _('success')

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='', out_signature='i')
    def getDomainStatus(self):
        return get_domain_status()

    @dbus.service.method(dbus_interface='com.kylin.kim.domain', in_signature='', out_signature='is', sender_keyword='sender')
    def unenroll(self, sender = None):
        logger.info('start unenroll.')
        try:
            lang = self.getSenderLang(sender)
            #os.environ['LANGUAGE'] = lang
            set_lang(lang)
            unrolling_host()
        except InstallError as ie:
            return ie.rval, ie.msg
        return SUCCESS, _('unenroll success') #'注销成功'

    @dbus.service.signal(dbus_interface='com.kylin.kim.domain', signature='i')
    def joinProgress(self, prog):
        print('join:%d%%' % prog)


def kcmcfg2kim():
    if get_domain_status() != 0:
        return
    kcm_file = '/etc/hedron/client.conf'
    config = configparser.ConfigParser()
    config.read(kcm_file, encoding='utf-8')
    url = config.get('hedron-client', 'url')
    up = parse.urlparse(url)
    kim_configure(up.hostname, up.port, provider='kcm')

def updatekimconfig():
    if get_domain_status() != 0:
        return
    try:
        server, port = get_server_info()
        kim_configure(server, port, provider='kcm')
    except Exception:
        pass

def main():
    global kim_dbus
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    standard_logging_setup('/var/log/kim-domain.log', filemode='a')

    sys_bus = dbus.SystemBus()
    name = dbus.service.BusName('com.kylin.kim.domain', sys_bus)
    kim_dbus = KimDomain(sys_bus, '/')
    mainloop = GLib.MainLoop()
    mainloop.run()


if __name__ == "__main__":
    if len(sys.argv) > 1 and sys.argv[1] == 'kcm2kim':
        kcmcfg2kim()
    elif len(sys.argv) > 1 and sys.argv[1] == 'kimupdate':
        updatekimconfig()
    else:
        main()
