#!/usr/bin/python

#
# Copyright (C) 2022 Nethesis S.r.l.
# http://www.nethesis.it - nethserver@nethesis.it
#
# This script is part of NethServer.
#
# NethServer 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 3 of the License,
# or any later version.
#
# NethServer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with NethServer.  If not, see COPYING.
#

import sys
import subprocess
import simplejson
import json
import os
import fcntl, errno

def get_config(appId):
    bash_command = "/sbin/e-smith/config getjson {}".format(appId)
    process = subprocess.Popen(bash_command.split(), stdout=subprocess.PIPE)
    output, error = process.communicate()
    if output == "1":
        return None
    else:
        return simplejson.loads(output)

def is_locked(fflag):
    try:
        fcntl.flock(open(fflag), fcntl.LOCK_SH | fcntl.LOCK_NB)
    except IOError as ioex:
        if ioex.errno == errno.EWOULDBLOCK:
            return True
        else:
            pass

    return False

def can_migrate(app_id):
    """
    Assuming the app_id is installed, check if it is also
    enabled/configured so it can be migrated
    """
    can_migrate = True # Assuming migration is possible

    if app_id == 'nethserver-ejabberd':
        config = get_config("ejabberd")
        if config and config['props']['status'] != 'enabled':
            can_migrate = False

    elif app_id == 'nethserver-mattermost':
        config = get_config("mattermost")
        if config and (config['props']['status'] != 'enabled' or not config['props']['VirtualHost']):
            can_migrate = False

    return can_migrate

def get_migration_status(app_id):
    migrating_flag = "/var/lib/nethserver/nethserver-ns8-migration/%s/bind.env" % app_id
    migrated_flag = "/var/lib/nethserver/nethserver-ns8-migration/%s/migrated" % app_id
    syncing_flag = "/var/lib/nethserver/nethserver-ns8-migration/%s/syncing.lock" % app_id
    skip_flag = "/var/lib/nethserver/nethserver-ns8-migration/%s/skip" % app_id

    if os.path.exists(skip_flag):
        migration_status = "skipped"
    elif app_id == "nethserver-samba":
        # nethserver-samba can be migrated only when used with local AD
        if not os.path.isfile('/etc/e-smith/db/configuration/defaults/nsdc/type'):
            migration_status = "not_migratable"
        else:
            migration_status = get_migration_status('account-provider')
    elif app_id == "nethserver-webtop5":
        migration_status = get_migration_status('nethserver-mail')
    elif app_id == "nethserver-roundcubemail":
        migration_status = get_migration_status('nethserver-mail')
    elif app_id == "nethserver-sogo":
        migration_status = get_migration_status('nethserver-mail')
    elif app_id == "nethserver-mail-getmail":
        migration_status = get_migration_status('nethserver-mail')
    elif os.path.exists(migrated_flag):
        migration_status = "migrated"
    elif is_locked(syncing_flag):
        migration_status = "syncing"
    elif os.path.exists(migrating_flag):
        migration_status = "migrating"
    elif can_migrate(app_id):
        migration_status = "not_migrated"
    else:
        migration_status = "not_available"

    return migration_status

def get_account_provider_info():
    provider = 'ldap'
    domain = subprocess.check_output(["awk", "-F", "=", '/^USER_DOMAIN=/ { print $2 }', "/var/lib/nethserver/nethserver-ns8-migration/environment"]).strip()
    ip_addresses = []
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/nsdc/type'):
        provider = 'ad'
        process = subprocess.Popen('/usr/libexec/nethserver/api/nethserver-ns8-migration/migration/ad-helper', stdout=subprocess.PIPE)
        output, error = process.communicate()
        try:
            ip_addresses = simplejson.loads(output)
        except:
            pass

        # The VPN address (ns8) is not eligible for the DC migration if
        # file server migration is enabled:
        if get_samba_info() and get_migration_status("nethserver-samba") != "skipped":
            ip_addresses = list(filter(lambda el: el["label"] != "ns8", ip_addresses))

    return {
        "id": "account-provider",
        "provider": provider,
        "domain": domain,
        "ip_addresses": ip_addresses
    }


def get_nethvoice_info():
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/nethvoice/type'):
        config = get_config("nethvoice")
        return {
            "id": "nethserver-nethvoice14",
            "name": "NethVoice",
            "config": config
        }
    else:
        return None


def get_nextcloud_info():
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/nextcloud/type'):
        config = get_config("nextcloud")
        return {
            "id": "nethserver-nextcloud",
            "name": "Nextcloud",
            "config": config
        }
    else:
        return None


def get_mattermost_info():
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/mattermost/type'):
        return {
            "id": "nethserver-mattermost",
            "name": "Mattermost"
        }
    else:
        return None


def get_ejabberd_info():
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/ejabberd/type'):
        return {
            "id": "nethserver-ejabberd",
            "name": "Ejabberd"
        }
    else:
        return None


def get_webmail_info():
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/roundcubemail/type') and os.path.isdir('/var/lib/mysql/roundcubemail'):
        return {
            "id": "nethserver-roundcubemail",
            "name": "Webmail"
        }
    else:
        return None


def get_sogo_info():
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/sogod/type'):
        return {
            "id": "nethserver-sogo",
            "name": "SOGo"
        }
    else:
        return None


def get_connector_info():
    if os.path.isfile('/etc/e-smith/events/actions/nethserver-getmail-conf'):
        return {
            "id": "nethserver-mail-getmail",
            "name": "POP3/IMAP Connector"
        }
    else:
        return None


def get_mail_info():
    # list only mail server, not mail relay
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/dovecot/status'):
        return {
            "id": "nethserver-mail",
            "name": "Email"
        }
    else:
        return None


def get_webtop_info():
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/webtop/type'):
        config = get_config("webtop")
        return {
            "id": "nethserver-webtop5",
            "name": "WebTop",
            "config": config
        }
    else:
        return None

def get_samba_info():
    # File server migration is available only with local AD provider
    if os.path.isfile('/etc/e-smith/db/configuration/defaults/smb/type'):
        return {
            "id": "nethserver-samba",
            "name": "File server",
        }

def list_apps():
    apps = []
    for app in ['mattermost', 'nextcloud', 'mail', 'webmail', 'webtop', 'ejabberd', 'samba', 'connector', 'nethvoice', 'sogo']:
        info = globals()["get_%s_info" % app]() # call all functions named get_<app>_info
        if info:
            # add only non-empty apps
            apps.append(info)

    # show account provider as last app
    apps.append(get_account_provider_info())
    return apps

def get_cluster_status():
    bash_command = ["/usr/sbin/ns8-action", "--hidden", "cluster", "get-cluster-status", "null"]
    process = subprocess.check_output(bash_command)
    output = json.loads(process.decode('utf-8'))

    # list all modules
    command = ["/usr/sbin/ns8-action", "--hidden", "cluster", "list-modules", "null"]
    process = subprocess.check_output(command)
    modules = json.loads(process.decode('utf8'))

    # list all AD providers
    command = ["/usr/sbin/ns8-action", "--hidden", "cluster", "list-user-domains", "null"]
    process = subprocess.check_output(command)
    domains = json.loads(process.decode('utf8'))

    # iterate over all nodes and check if mail and ejabberd are installed
    for node in output["data"]["output"]["nodes"]:
        node_id = node["id"]
        node["mail_installed"] = False
        node["ejabberd_installed"] = False
        node["adProvider_installed"] = False
        for module in modules['data']['output']:
            if module.get("id") == "mail":
                for destination in module.get("install_destinations", []):
                    if destination["node_id"] == node_id and not destination["eligible"]:
                        node["mail_installed"] = True
            if module.get("id") == "ejabberd":
                for destination in module.get("install_destinations", []):
                    if destination["node_id"] == node_id and not destination["eligible"]:
                        node["ejabberd_installed"] = True
        for domain in domains['data']['output']['domains']:
            if domain["schema"] == "ad":
                providers = domain["providers"]
                for provider in providers:
                    provider_node_id = provider["node"]
                    if provider_node_id == node_id:
                        node["adProvider_installed"] = True
                        break

    return simplejson.loads(json.dumps(output))

try:
    input_json = simplejson.load(sys.stdin)
    action = input_json["action"]

    if action == 'listApps':
        apps = list_apps()

        for app in apps:
            app["status"] = get_migration_status(app["id"])

        output = simplejson.dumps({'migration': apps})
        print(output)
    elif action == 'getClusterStatus':
        cluster_status = get_cluster_status()
        output = simplejson.dumps({'clusterStatus': cluster_status})
        print(output)
except Exception as e:
    output = simplejson.dumps({'error': "%s" % e})
    print(output)
    sys.exit(1)
