#!/usr/bin/python3
# -*- mode: python -*-
#
# This file is part of Plinth.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

"""
Configuration helper for Plint PageKite inteface

TODO: Use augeas for manipulating /etc/pagekite.d/* files
"""

# Disable warning about invalid module name # pylint: disable-msg=C0103

import argparse
import os
import re
import subprocess

CONFIG_DIR = '/etc/pagekite.d'

SERVICE_FILE_MAP = {
    'http': {
        'file': '80_httpd.rc',
        'match': 'http:',
        'line': 'service_on = http:@kitename : localhost:80 : @kitesecret'},
    'ssh': {
        'file': '80_sshd.rc',
        'match': 'raw/22:',
        'line': 'service_on = raw/22:@kitename : localhost:22 : @kitesecret'}}


def parse_arguments():
    """Return parsed command line arguments as dictionary"""
    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')

    # Start PageKite
    subparsers.add_parser('start', help='Start PageKite service')

    # Stop PageKite
    subparsers.add_parser('stop', help='Stop PageKite service')

    # Get status
    subparsers.add_parser('get-status', help='Get whether PakeKite is enabled')

    # Set status
    set_status = subparsers.add_parser('set-status',
                                       help='Enable/disable PageKite')
    set_status.add_argument('enable', choices=['enable', 'disable'])

    # Get kite details
    subparsers.add_parser('get-kite',
                          help='Get configured kite name and secret')

    # Set kite details
    set_kite = subparsers.add_parser('set-kite',
                                     help='Configure kite name and its secret')
    set_kite.add_argument('--kite-name',
                          help='Name of the kite (eg: mybox.pagekite.me)')
    set_kite.add_argument('--kite-secret', help='Secret for the kite')

    # Get service status
    get_service = subparsers.add_parser('get-service-status',
                                        help='Get whether service is enabled')
    get_service.add_argument('service', choices=['http', 'ssh'])

    # Set service status
    set_service = subparsers.add_parser('set-service-status',
                                        help='Enable/disable a service')
    set_service.add_argument('service', choices=['http', 'ssh'])
    set_service.add_argument('enable', choices=['enable', 'disable'])

    return parser.parse_args()


def subcommand_start(_):
    """Start PageKite service"""
    status = subprocess.call(['service', 'pagekite', 'start'])
    if status:
        raise Exception('Unable to start PageKite server')


def subcommand_stop(_):
    """Stop PageKite service"""
    status = subprocess.call(['service', 'pagekite', 'stop'])
    if status:
        raise Exception('Unable to stop PageKite server')


def subcommand_get_status(_):
    """Print status of the pagekite service"""
    is_enabled = is_pagekite_enabled()
    print('enabled' if is_enabled else 'disabled')


def is_pagekite_enabled():
    """Return if pagekite is enabled"""
    service_file_path = os.path.join(CONFIG_DIR, '10_account.rc')
    try:
        with open(service_file_path, 'r') as file_object:
            for line in file_object:
                regex = r'^[ \t]*abort_not_configured'
                if re.match(regex, line):
                    return False
    except Exception:
        return True

    return True


def subcommand_set_status(arguments):
    """Enable/disable the pagekite service"""
    enable = arguments.enable == 'enable'
    is_enabled = is_pagekite_enabled()
    if enable and is_enabled:
        print('already enabled')
        return

    if not enable and not is_enabled:
        print('already disabled')
        return

    if enable:
        pagekite_enable()
        print('enabled')
    else:
        pagekite_disable()
        print('disabled')


def pagekite_enable():
    """Enable the pagekite daemon"""
    file_path = os.path.join(CONFIG_DIR, '10_account.rc')
    file_path_new = os.path.join(CONFIG_DIR, '10_account.rc.new')
    with open(file_path, 'r') as read_file_object, \
            open(file_path_new, 'w') as write_file_object:
        for line in read_file_object:
            if not re.match('^[ \t]*abort_not_configured', line):
                write_file_object.write(line)

    os.rename(file_path_new, file_path)


def pagekite_disable():
    """Disable the pagekite daemon"""
    file_path = os.path.join(CONFIG_DIR, '10_account.rc')
    with open(file_path, 'a') as file_object:
        file_object.write('abort_not_configured\n')


def subcommand_get_kite(_):
    """Print details of the currently configure kite"""
    kite_name = ''
    kite_secret = ''

    file_path = os.path.join(CONFIG_DIR, '10_account.rc')
    with open(file_path, 'r') as file_object:
        for line in file_object:
            match = re.match(r'[ \t]*kitename[ \t]*=[ \t]*(.*)', line)
            if match:
                kite_name = match.group(1)
                continue

            match = re.match(r'[ \t]*kitesecret[ \t]*=[ \t]*(.*)', line)
            if match:
                kite_secret = match.group(1)
                continue

    print(kite_name)
    print(kite_secret)


def subcommand_set_kite(arguments):
    """Set details of the kite"""
    kite_name = arguments.kite_name
    kite_secret = arguments.kite_secret

    file_path = os.path.join(CONFIG_DIR, '10_account.rc')
    file_path_new = os.path.join(CONFIG_DIR, '10_account.rc.new')
    with open(file_path, 'r') as read_file_object, \
            os.fdopen(os.open(file_path_new, os.O_WRONLY | os.O_CREAT,
                              0o400), 'w') as write_file_object:
        for line in read_file_object:
            if re.match(r'[ \t]*kitename[ \t]*=.*', line):
                write_file_object.write(
                    'kitename = {kite_name}\n'.format(kite_name=kite_name))
                continue

            if re.match(r'[ \t]*kitesecret[ \t]*=.*', line):
                write_file_object.write('kitesecret = {kite_secret}\n'
                                        .format(kite_secret=kite_secret))
                continue

            write_file_object.write(line)

    os.rename(file_path_new, file_path)


def subcommand_get_service_status(arguments):
    """Print status of the pagekite service"""
    is_enabled = is_service_enabled(arguments.service)
    print('enabled' if is_enabled else 'disabled')


def is_service_enabled(service):
    """Return if a service is enabled"""
    service = SERVICE_FILE_MAP[service]
    service_file_path = os.path.join(CONFIG_DIR, service['file'])
    if not os.path.isfile(service_file_path):
        return False

    try:
        with open(service_file_path, 'r') as file_object:
            for line in file_object:
                regex = '[ \t]*service_on[ \t]*=[ \t]*{match}'
                regex = regex.format(match=service['match'])
                if re.match(regex, line):
                    return True
    except Exception:
        return False

    return False


def subcommand_set_service_status(arguments):
    """Enable/disable a pagekite service"""
    enable = arguments.enable == 'enable'
    is_enabled = is_service_enabled(arguments.service)
    if enable and is_enabled:
        print('already enabled')
        return

    if not enable and not is_enabled:
        print('already disabled')
        return

    if enable:
        service_enable(arguments.service)
        print('enabled')
    else:
        service_disable(arguments.service)
        print('disabled')


def service_enable(service_name):
    """Enable a service"""
    service = SERVICE_FILE_MAP[service_name]
    service_file_path = os.path.join(CONFIG_DIR, service['file'])
    with open(service_file_path, 'w') as file_object:
        file_object.write('''
# Expose the local {service_name} daemon
# File auto-generated by Plinth

{line}
'''.format(service_name=service_name, line=service['line']))


def service_disable(service_name):
    """Disable a service"""
    service = SERVICE_FILE_MAP[service_name]
    service_file_path = os.path.join(CONFIG_DIR, service['file'])
    service_file_path_new = os.path.join(CONFIG_DIR,
                                         service['file'] + '.plinthbak')
    os.rename(service_file_path, service_file_path_new)


def main():
    """Parse arguments and perform all duties"""
    arguments = parse_arguments()

    subcommand = arguments.subcommand.replace('-', '_')
    subcommand_method = globals()['subcommand_' + subcommand]
    subcommand_method(arguments)

if __name__ == "__main__":
    main()
