#!/usr/bin/env python
# coding: UTF-8
'''This is the helper script to setup/manage your seafile server
'''

import sys

import os
import time
import re
import shutil
import subprocess
import argparse
import uuid

try:
    import readline
    # Avoid pylint 'unused import' warning
    dummy = readline
except ImportError:
    pass

####################
### Cosntants
####################
SERVER_MANUAL_HTTP = 'https://github.com/haiwen/seafile/wiki'
SEAFILE_GOOGLE_GROUP = 'https://groups.google.com/forum/?fromgroups#!forum/seafile'
SEAFILE_WEBSITE = 'http://www.seafile.com'
SEAHUB_DOWNLOAD_URL = 'https://seafile.com.cn/downloads/seahub-latest.tar.gz'

####################
### Global variables
####################
cwd = os.getcwd()
SCRIPT_NAME = os.path.basename(sys.argv[0])
# Use the bundled older Django 1.11 instead of potential global one.
sys.path.insert(0, os.path.join(cwd, 'seafile-server', 'seahub', 'thirdpart'))

PYTHON = sys.executable

conf = {}
CONF_CCNET_DIR = 'ccnet_dir'
CONF_SEAFILE_DIR = 'seafile_dir'
CONF_SEAHUB_DIR = 'seafile_dir'
CONF_SEAFILE_PORT = 'seafile_port'
CONF_FILESERVER_PORT = 'fileserver_port'
CONF_IP_OR_DOMAIN = 'ip_or_domain'

CONF_SEAHUB_CONF = 'seahub_conf'
CONF_SEAHUB_DIR = 'seahub_dir'
CONF_SEAHUB_PORT = 'seahub_port'

CONF_SEAHUB_PIDFILE = 'seahub_pidfile'
CONF_SEAHUB_OUTLOG = 'seahub_outlog'
CONF_SEAHUB_ERRLOG = 'seahub_errlog'

CONF_CCNET_CONF_EXISTS = 'ccnet_conf_exists'
CONF_SEAFILE_CONF_EXISTS = 'seafile_conf_exists'

CONF_ADMIN_EMAIL = 'admin_email'
CONF_ADMIN_PASSWORD = 'admin_password'
CONF_SEAFILE_CENTRAL_CONF_DIR = 'central_config_dir'
CONF_SEAFILE_RPC_PIPE_PATH = 'rpc_pipe_path'

####################
### Common helper functions


def highlight(content):
    '''Add ANSI color to content to get it highlighted on terminal'''
    return '\x1b[33m%s\x1b[m' % content


def info(msg):
    print(msg)


def error(msg):
    print('Error: ' + msg)
    sys.exit(1)


def ask_question(desc,
                 key=None,
                 note=None,
                 default=None,
                 validate=None,
                 yes_or_no=False,
                 invalidate_msg=None):
    '''Ask a question, return the answer. The optional validate param is a
    function used to validate the answer. If yes_or_no is True, then a boolean
    value would be returned.

    '''
    assert key or yes_or_no
    desc = highlight(desc)
    if note:
        desc += '  (%s)' % note
    if default:
        desc += '\n' + ('[default %s ]' % default)
    else:
        if yes_or_no:
            desc += '\n[yes or no]'
        else:
            desc += '\n' + ('[%s ]' % key)

    desc += '  '
    while True:
        answer = input(desc)
        if not answer:
            if default:
                print('')
                return default
            else:
                continue

        answer = answer.strip()

        if yes_or_no:
            if answer != 'yes' and answer != 'no':
                print('\nPlease answer yes or no\n')
                continue
            else:
                return answer == 'yes'
        else:
            if validate and not validate(answer):
                if invalidate_msg:
                    print('\n%s\n' % invalidate_msg)
                else:
                    print('\n"%s" is not a valid %s\n' % (answer, key))
                continue

        print('')
        return answer


def run_argv(argv,
             cwd=None,
             env=None,
             suppress_stdout=False,
             suppress_stderr=False):
    '''Run a program and wait it to finish, and return its exit code. The
    standard output of this program is supressed.

    '''
    with open(os.devnull, 'w') as devnull:
        if suppress_stdout:
            stdout = devnull
        else:
            stdout = sys.stdout

        if suppress_stderr:
            stderr = devnull
        else:
            stderr = sys.stderr

        proc = subprocess.Popen(argv,
                                cwd=cwd,
                                stdout=stdout,
                                stderr=stderr,
                                env=env)
        return proc.wait()


def run(cmdline,
        cwd=None,
        env=None,
        suppress_stdout=False,
        suppress_stderr=False):
    '''Like run_argv but specify a command line string instead of argv'''
    with open(os.devnull, 'w') as devnull:
        if suppress_stdout:
            stdout = devnull
        else:
            stdout = sys.stdout

        if suppress_stderr:
            stderr = devnull
        else:
            stderr = sys.stderr

        proc = subprocess.Popen(cmdline,
                                cwd=cwd,
                                stdout=stdout,
                                stderr=stderr,
                                env=env,
                                shell=True)
        return proc.wait()


def is_running(process):
    '''Detect if there is a process with the given name running'''
    argv = ['pgrep', '-f', process]

    return run_argv(argv, suppress_stdout=True) == 0


def pkill(process):
    '''Kill the program with the given name'''
    argv = ['pkill', '-f', process]

    run_argv(argv)


def kill(pid):
    '''Kill the program with the given pid'''
    argv = ['kill', pid]

    run_argv(argv)


def must_mkdir(path):
    '''Create a directory, exit on failure'''
    try:
        os.mkdir(path)
    except OSError as e:
        error('failed to create directory %s:%s' % (path, e))

### END of Common helper functions
####################


def check_seafile_install():
    '''Check if seafile has been correctly built and installed in this
    system

    '''
    dirs = os.environ['PATH'].split(':')

    def exist_in_path(prog):
        '''Test whether prog exists in system path'''
        for d in dirs:
            if d == '':
                continue
            path = os.path.join(d, prog)
            if os.path.exists(path):
                return True

        return False

    def check_prog(name):
        if not exist_in_path(name):
            error(
                '%s not found in PATH. Have you built and installed seafile server?'
                % name)

    progs = [
        'ccnet-init',
        'seaf-server-init',
        'seaf-server',
        'ccnet-server',
        'seafile-controller',
    ]

    for prog in progs:
        check_prog(prog)


def get_seahub_env():
    '''And PYTHONPATH and CCNET_CONF_DIR/SEAFILE_CONF_DIR to env, which is
    needed by seahub

    '''
    seahub_dir = conf[CONF_SEAHUB_DIR]
    seahub_thirdpart_dir = os.path.join(seahub_dir, 'thirdpart')

    env = dict(os.environ)
    pypath = env.get('PYTHONPATH', '')

    pathlist = [p for p in pypath.split(':') if p != '']
    pathlist.append(seahub_thirdpart_dir)
    newpypath = ':'.join(pathlist)
    env['PYTHONPATH'] = newpypath
    env['CCNET_CONF_DIR'] = conf[CONF_CCNET_DIR]
    env['SEAFILE_CONF_DIR'] = conf[CONF_SEAFILE_DIR]
    env['SEAFILE_CENTRAL_CONF_DIR'] = conf[CONF_SEAFILE_CENTRAL_CONF_DIR]
    env['SEAFILE_RPC_PIPE_PATH'] = conf[CONF_SEAFILE_RPC_PIPE_PATH]
    return env


####################
### <setup> command
####################
def welcome():
    '''Show welcome message when running the <setup> command'''
    welcome_msg = '''\
-----------------------------------------------------------------
This script will guide you to config and setup your seafile server.
Make sure you have read seafile server manual at

        %s

Press [ENTER] to continue
-----------------------------------------------------------------
''' % SERVER_MANUAL_HTTP
    print(welcome_msg)
    input()

def get_server_ip_or_domain():
    def validate(s):
        r = r'^[^.].+\..+[^.]$'
        return bool(re.match(r, s))

    question = 'What is the ip of the server?'
    key = 'ip or domain'
    note = 'For example: www.mycompany.com, 192.168.1.101'
    conf[CONF_IP_OR_DOMAIN] = ask_question(question,
                                           key=key,
                                           note=note,
                                           validate=validate)


def get_ccnet_conf_dir():
    ccnet_conf_dir = os.path.join(cwd, 'ccnet')

    if os.path.exists(ccnet_conf_dir):
        question = 'It seems there already exists ccnet config files in %s, Do you want to use them?' % ccnet_conf_dir
        yesno = ask_question(question, yes_or_no=True)
        if not yesno:
            print(highlight(
                '\nRemove the directory %s first, and run the script again.\n'
                % ccnet_conf_dir))
            sys.exit(1)
        else:
            conf[CONF_CCNET_CONF_EXISTS] = True
    else:
        conf[CONF_CCNET_CONF_EXISTS] = False

    conf[CONF_CCNET_DIR] = ccnet_conf_dir


def get_seafile_port():
    def validate(s):
        try:
            port = int(s)
        except ValueError:
            return False

        return port > 0 and port < 65536

    question = 'Which port do you want to use for the seafile server?'
    key = 'seafile server port'
    default = '12001'
    conf[CONF_SEAFILE_PORT] = ask_question(question,
                                           key=key,
                                           default=default,
                                           validate=validate)


def get_fileserver_port():
    def validate(s):
        try:
            port = int(s)
        except ValueError:
            return False

        return port > 0 and port < 65536

    question = 'Which port do you want to use for the seafile fileserver?'
    key = 'seafile fileserver port'
    default = '8082'
    conf[CONF_FILESERVER_PORT] = ask_question(question,
                                              key=key,
                                              default=default,
                                              validate=validate)


def get_seafile_data_dir():
    question = 'Where do you want to put your seafile data?'
    key = 'seafile-data'
    note = 'Please use a volume with enough free space'
    default = os.path.join(cwd, 'seafile-data')
    seafile_data_dir = ask_question(question,
                                    key=key,
                                    note=note,
                                    default=default)

    if os.path.exists(seafile_data_dir):
        question = 'It seems there already exists seafile data in %s, Do you want to use them?' % seafile_data_dir
        yesno = ask_question(question, yes_or_no=True)
        if not yesno:
            print(highlight(
                '\nRemove the directory %s first, and run the script again.\n'
                % seafile_data_dir))
            sys.exit(1)
        else:
            conf[CONF_SEAFILE_CONF_EXISTS] = True
    else:
        conf[CONF_SEAFILE_CONF_EXISTS] = False

    conf[CONF_SEAFILE_DIR] = seafile_data_dir


def create_gunicorn_conf():
    conf_dir = os.path.join(cwd, 'conf')
    pids_dir = os.path.join(cwd, 'pids')
    confpath = os.path.join(conf_dir, 'gunicorn.conf.py')

    if not os.path.exists(pids_dir):
        must_mkdir(pids_dir)

    if os.path.exists(confpath):
        return

    if not os.path.exists(conf_dir):
        must_mkdir(conf_dir)

    content = '''\
import os

daemon = True
workers = 5

# default localhost:8000
bind = "127.0.0.1:8000"

# Logging
server_dir = os.path.join(os.path.dirname(__file__), '..')
pidfile = os.path.join(server_dir, 'pids', 'seahub.pid')
errorlog = os.path.join(server_dir, 'logs', 'error.log')
accesslog = os.path.join(server_dir, 'logs', 'access.log')

# for file upload, we need a longer timeout value (default is only 30s, too short)
timeout = 1200

limit_request_line = 8190
'''

    try:
        with open(confpath, 'w') as fp:
            fp.write(content)
    except:
        error('Failed to write seahub config')


def create_seafdav_conf():
    conf_dir = os.path.join(cwd, 'conf')
    confpath = os.path.join(conf_dir, 'seafdav.conf')

    if os.path.exists(confpath):
        return

    if not os.path.exists(conf_dir):
        must_mkdir(conf_dir)

    content = '''\
[WEBDAV]
enabled = false
port = 8083
fastcgi = false
host = 127.0.0.1
share_name = /seafdav
'''

    try:
        with open(confpath, 'w') as fp:
            fp.write(content)
    except:
        error('Failed to write WebDAV config')


def gen_seahub_secret_key():
    data = str(uuid.uuid4()) + str(uuid.uuid4())
    return data[:40]


def create_seahub_settings_py():
    seahub_settings_py = os.path.join(cwd, 'conf', 'seahub_settings.py')
    try:
        with open(seahub_settings_py, 'w') as fp:
            line = "SECRET_KEY = '%s'" % gen_seahub_secret_key()
            fp.write(line)
    except Exception as e:
        error('failed to create %s: %s' % (seahub_settings_py, e))


def move_avatar():
    seahub_data_dir = os.path.join(cwd, 'seahub-data')
    outside_avatar_dir = os.path.join(seahub_data_dir, 'avatars')
    seahub_avatar_dir = os.path.join(conf[CONF_SEAHUB_DIR], 'media', 'avatars')

    if os.path.exists(outside_avatar_dir):
        return

    if not os.path.exists(seahub_data_dir):
        must_mkdir(seahub_data_dir)

    # move the avatars dir outside
    shutil.move(seahub_avatar_dir, outside_avatar_dir)
    # make the the original avatars dir a symlink pointing to the outside dir
    os.symlink(outside_avatar_dir, seahub_avatar_dir)

def create_ccnet_db():
    ccnet_dir = os.path.join(cwd, 'ccnet');
    for d in ['GroupMgr', 'misc', 'OrgMgr', 'PeerMgr']:
        d = os.path.join(ccnet_dir, d)
        if not os.path.exists(d):
            must_mkdir(d)

    def run_sqlite3(db, sqlname):
        return run_argv(['sqlite3', db,
            '.read ' + os.path.join(cwd, 'seafile-server', 'sql', 'sqlite', sqlname + '.sql')])

    if run_sqlite3(os.path.join(ccnet_dir, 'GroupMgr', 'groupmgr.db'), 'groupmgr') != 0:
        error('Failed to sync ccnet groupmgr database')
    if run_sqlite3(os.path.join(ccnet_dir, 'misc', 'config.db'), 'config') != 0:
        error('Failed to sync ccnet config database')
    if run_sqlite3(os.path.join(ccnet_dir, 'OrgMgr', 'orgmgr.db'), 'org') != 0:
        error('Failed to sync ccnet org database')
    if run_sqlite3(os.path.join(ccnet_dir, 'PeerMgr', 'usermgr.db'), 'user') != 0:
        error('Failed to sync ccnet user database')
    if run_sqlite3(os.path.join(cwd, 'seafile-data', 'seafile.db'), 'seafile') != 0:
        error('Failed to sync seafille database')
    if run_argv(['sqlite3', os.path.join(cwd, 'seahub.db'),
                 '.read ' + os.path.join(cwd, 'seafile-server', 'seahub', 'sql', 'sqlite3.sql')]) != 0:
        error('Failed to sync seahub database')

def create_library_template():
    template_dir = os.path.join(cwd, 'seafile-data', 'library-template')
    if os.path.exists(template_dir):
        return
    shutil.copytree(os.path.join(cwd, 'seafile-server', 'seafile', 'docs'), template_dir)

def init_seahub():
    seahub_dir = conf[CONF_SEAHUB_DIR]

    # create seahub_settings.py
    create_seahub_settings_py()

    print()
    print()
    info('Now initializing seahub database, please wait...')
    print()

    create_ccnet_db()

    info('done')

    move_avatar()
    create_gunicorn_conf()
    create_seafdav_conf()
    create_library_template()


def check_django_version():
    '''Requires django 1.11'''
    import django
    if django.VERSION[0] != 1 or django.VERSION[1] != 11:
        error('Django 1.11 is required')
    del django


def check_python_module(import_name, package_name=None, silent=False):
    package_name = package_name or import_name
    if not silent:
        info('checking %s' % package_name)
    try:
        __import__(import_name)
    except ImportError:
        error('Python module "%s" not found. Please install it first' %
              package_name)


def check_python_dependencies(silent=False):
    '''Ensure all python libraries we need are installed'''

    if not silent:
        info('check python modules ...')
    check_django_version()
    def check(*a, **kw):
        kw.setdefault('silent', silent)
        check_python_module(*a, **kw)
    pkgs = [
        'sqlite3',
        'chardet',
        'six',
        'pytz',
        'rest_framework',
        # 'compressor',
        'statici18n',
        'jsonfield',
        'dateutil',
        # 'constance',
        'openpyxl',
    ] # yapf: disable
    for pkg in pkgs:
        check(pkg)
    check('PIL', 'python imaging library(PIL)')

    print()


def config_ccnet_seafile():
    get_ccnet_conf_dir()
    if not conf[CONF_CCNET_CONF_EXISTS]:
        get_server_ip_or_domain()

    get_seafile_data_dir()
    if not conf[CONF_SEAFILE_CONF_EXISTS]:
        get_seafile_port()
        get_fileserver_port()

    info('This is your configuration')
    info('------------------------------------------')
    if conf[CONF_CCNET_CONF_EXISTS]:
        info('ccnet config:        use existing config in %s' %
             highlight(conf[CONF_CCNET_DIR]))
    else:
        info('ccnet conf dir:           %s' % highlight(conf[CONF_CCNET_DIR]))
        info('server host:              %s' %
             highlight(conf[CONF_IP_OR_DOMAIN]))

    if conf[CONF_SEAFILE_CONF_EXISTS]:
        info('seafile:             use existing config in %s' %
             highlight(conf[CONF_SEAFILE_DIR]))
    else:
        info('seafile data dir:         %s' %
             highlight(conf[CONF_SEAFILE_DIR]))
        info('seafile port:             %s' %
             highlight(conf[CONF_SEAFILE_PORT]))
        info('seafile fileserver port:  %s' %
             highlight(conf[CONF_FILESERVER_PORT]))

    info('------------------------------------------')
    info('Press ENTER if the config is right, or anything else to re-config ')

    if input() != '':
        config_ccnet_seafile()
    else:
        return


def init_ccnet_seafile():
    if not conf[CONF_CCNET_CONF_EXISTS]:
        info('Generating ccnet configuration...')
        argv = [
            'ccnet-init',
            '-F',
            conf[CONF_SEAFILE_CENTRAL_CONF_DIR],
            '-c',
            conf[CONF_CCNET_DIR],
            '--host',
            conf[CONF_IP_OR_DOMAIN],
        ]

        if run_argv(argv) != 0:
            error('failed to init ccnet configuration')

        info('done')

    if not conf[CONF_SEAFILE_CONF_EXISTS]:
        info('Generating seafile configuration...')
        argv = [
            'seaf-server-init',
            '-F',
            conf[CONF_SEAFILE_CENTRAL_CONF_DIR],
            '--seafile-dir',
            conf[CONF_SEAFILE_DIR],
            '--port',
            conf[CONF_SEAFILE_PORT],
            '--fileserver-port',
            conf[CONF_FILESERVER_PORT],
        ]

        if run_argv(argv) != 0:
            error('failed to init seafile configuration')

        info('done')

    seafile_ini = os.path.join(conf[CONF_CCNET_DIR], 'seafile.ini')
    with open(seafile_ini, 'w') as fp:
        fp.write(conf[CONF_SEAFILE_DIR])


####################
### <start> command
####################
def start_controller():
    argv = [
        'seafile-controller',
        '-c',
        conf[CONF_CCNET_DIR],
        '-d',
        conf[CONF_SEAFILE_DIR],
        '-F',
        conf[CONF_SEAFILE_CENTRAL_CONF_DIR],
    ]
    env = get_seahub_env()

    info('Starting seafile-server...')
    if run_argv(argv, env=env) != 0:
        error('Failed to start seafile')

    # check again after several seconds
    time.sleep(10)

    if not is_running('seafile-controller'):
        error('Failed to start seafile')


def start_seahub_gunicorn():
    argv = [
        'gunicorn',
        'seahub.wsgi:application',
        '-c',
        conf[CONF_SEAHUB_CONF],
        '--preload',
    ]

    info('Starting seahub...')
    env = get_seahub_env()
    if run_argv(argv, cwd=conf[CONF_SEAHUB_DIR], env=env) != 0:
        error('Failed to start seahub')

    info('Seahub running')


def start_seahub_fastcgi():
    info('Starting seahub in fastcgi mode...')
    argv = [
        PYTHON,
        'manage.py',
        'runfcgi',
        'host=%(host)s',
        'port=%(port)s',
        'pidfile=%(pidfile)s',
        'outlog=%(outlog)s',
        'errlog=%(errlog)s',
    ]

    host = os.environ.get('SEAFILE_FASTCGI_HOST', '127.0.0.1')
    cmdline = ' '.join(argv) % \
              dict(host=host,
                   port=conf[CONF_SEAHUB_PORT],
                   pidfile=conf[CONF_SEAHUB_PIDFILE],
                   outlog=conf[CONF_SEAHUB_OUTLOG],
                   errlog=conf[CONF_SEAHUB_ERRLOG])

    env = get_seahub_env()

    if run(cmdline, cwd=conf[CONF_SEAHUB_DIR], env=env) != 0:
        error('Failed to start seahub in fastcgi mode')

    info('Seahub running on port %s (fastcgi)' % conf[CONF_SEAHUB_PORT])


def read_seafile_data_dir(ccnet_conf_dir):
    '''Read the location of seafile-data from seafile.ini, also consider the
    upgrade from older version which do not has the seafile.ini feature

    '''
    seafile_ini = os.path.join(ccnet_conf_dir, 'seafile.ini')
    if os.path.exists(seafile_ini):
        with open(seafile_ini, 'r') as fp:
            seafile_data_dir = fp.read().strip()
    else:
        # In previous seafile-admin, seafiled-data folder must be under
        # the top level directory, so we do not store the location of
        # seafile-data folder in seafile.ini
        seafile_data_dir = os.path.join(cwd, 'seafile-data')
        if os.path.exists(seafile_data_dir):
            with open(seafile_ini, 'w') as fp:
                fp.write(seafile_data_dir)

    return seafile_data_dir


def check_layout(args):
    def error_not_found(path):
        error('%s not found' % path)

    ccnet_conf_dir = os.path.join(cwd, 'ccnet')
    if not os.path.exists(ccnet_conf_dir):
        error_not_found(ccnet_conf_dir)

    central_config_dir = os.path.join(cwd, 'conf')

    ccnet_conf = os.path.join(central_config_dir, 'ccnet.conf')
    if not os.path.exists(ccnet_conf):
        error_not_found(ccnet_conf)

    seafile_data_dir = read_seafile_data_dir(ccnet_conf_dir)
    if not os.path.exists(seafile_data_dir):
        error_not_found(seafile_data_dir)

    seafile_conf = os.path.join(central_config_dir, 'seafile.conf')
    if not os.path.exists(seafile_conf):
        error_not_found(seafile_conf)

    seahub_conf = os.path.join(central_config_dir, 'gunicorn.conf.py')
    if not os.path.exists(seahub_conf):
        error_not_found(seahub_conf)

    seahub_dir = os.path.join(cwd, 'seafile-server', 'seahub')
    if not os.path.exists(seahub_conf):
        error_not_found(seahub_dir)

    rpc_pipe_path = os.path.join(cwd, 'seafile-server', 'runtime')
    if not os.path.exists(rpc_pipe_path):
        error_not_found(rpc_pipe_path)

    conf[CONF_SEAFILE_CENTRAL_CONF_DIR] = central_config_dir
    conf[CONF_SEAFILE_RPC_PIPE_PATH] = rpc_pipe_path
    conf[CONF_CCNET_DIR] = ccnet_conf_dir
    conf[CONF_SEAFILE_DIR] = seafile_data_dir
    conf[CONF_SEAHUB_DIR] = seahub_dir
    conf[CONF_SEAHUB_CONF] = seahub_conf
    conf[CONF_SEAHUB_PIDFILE] = os.path.join(cwd, 'pids', 'seahub.pid')
    conf[CONF_SEAHUB_OUTLOG] = os.path.join(cwd, 'logs', 'access.log')
    conf[CONF_SEAHUB_ERRLOG] = os.path.join(cwd, 'logs', 'error.log')


def check_config(args):
    check_layout(args)

    try:
        port = int(args.port)
    except ValueError:
        error('invalid port: %s' % args.port)
    else:
        if port <= 0 or port > 65535:
            error('invalid port: %s' % args.port)

    conf[CONF_SEAHUB_PORT] = port


def check_directory_layout():
    seaf_server_dir = os.path.join(cwd, 'seafile-server')
    if not os.path.exists(seaf_server_dir):
        error(
            '"seafile-server/" not found in current directory. \nPlease run seafile-admin in the correct directory.')

    seahub_dir = os.path.join(seaf_server_dir, 'seahub')
    if not os.path.exists(seahub_dir):
        error(
            '"seafile-server/seahub/" not found. \nPlease download seahub first.')

    conf[CONF_SEAHUB_DIR] = seahub_dir


def setup_seafile(args):
    # avoid pylint "unused variable" warning
    dummy = args

    welcome()
    check_python_dependencies()
    conf[CONF_SEAFILE_CENTRAL_CONF_DIR] = os.path.join(cwd, 'conf')
    conf[CONF_SEAFILE_RPC_PIPE_PATH] = os.path.join(cwd, 'seafile-server', 'runtime')
    config_ccnet_seafile()
    init_ccnet_seafile()
    init_seahub()

    print()
    print('-----------------------------------------------------------------')
    print('-----------------------------------------------------------------')
    print('Your seafile server configuration has been finished successfully.')
    print('-----------------------------------------------------------------')
    print('-----------------------------------------------------------------')
    print()
    print('To start/stop seafile server:')
    print()
    print(highlight('         $ cd %s' % cwd))
    print(highlight('         $ %s { start | stop }' % SCRIPT_NAME))
    print()
    print('If you have any problem, refer to\n')
    print()
    print(' Seafile server manual:      %s' % SERVER_MANUAL_HTTP)
    print()
    print(' Seafile discussion group:   %s' % SEAFILE_GOOGLE_GROUP)
    print()
    print(' Seafile website:            %s' % SEAFILE_WEBSITE)
    print()
    print('for more information.')
    print()


def check_necessary_files():
    files = [
        os.path.join(cwd, 'conf', 'ccnet.conf'),
        os.path.join(cwd, 'conf', 'gunicorn.conf.py'),
        os.path.join(cwd, 'seahub.db'),
        os.path.join(cwd, 'conf', 'seahub_settings.py'),
    ]

    # seahub.db isn't create with a MySQL installation and crash the startup
    with open(files[3], 'r') as f:
        for line in f:
            if 'DATABASES' in line:
                del(files[2])
                break

    for fpath in files:
        if not os.path.exists(fpath):
            error('%s not found' % fpath)


def start_seafile(args):
    '''start ccnet/seafile/seahub/fileserver'''
    if is_running('seafile-controller'):
        error(highlight('NOTE: Seafile is already running'))

    check_python_dependencies(silent=True)
    if args.fastcgi:
        check_python_module('flup', 'flup', silent=True)
    else:
        check_python_module('gunicorn', 'gunicorn', silent=True)

    check_necessary_files()

    check_config(args)

    start_controller()

    if args.port:
        try:
            port = int(args.port)
        except ValueError:
            error('invalid port: %s' % args.port)
        else:
            if port <= 0 or port > 65535:
                error('invalid port: %s' % args.port)

    if args.fastcgi:
        start_seahub_fastcgi()
    else:
        start_seahub_gunicorn()

    info('Done')


def stop_seafile(dummy):
    info('Stopping seafile server')
    pkill('seafile-controller')
    pidfile = os.path.join(cwd, 'pids', 'seahub.pid')
    try:
        with open(pidfile, 'r') as fp:
            pid = fp.read().strip('\n ')
            if pid:
                kill(pid)
    except:
        pass

    info('done')


def reset_admin(args):
    '''reset seafile admin account'''
    check_layout(args)
    env = get_seahub_env()

    argv = [PYTHON, 'manage.py', 'createsuperuser']

    env = get_seahub_env()
    seahub_dir = conf[CONF_SEAHUB_DIR]
    run_argv(argv, cwd=seahub_dir, env=env)


def main():
    check_seafile_install()
    check_directory_layout()

    parser = argparse.ArgumentParser()
    subparsers = parser.add_subparsers(title='subcommands', description='')

    parser_setup = subparsers.add_parser('setup',
                                         help='setup the seafile server')
    parser_setup.set_defaults(func=setup_seafile)

    parser_start = subparsers.add_parser('start',
                                         help='start the seafile server')
    parser_start.set_defaults(func=start_seafile)

    parser_start.add_argument('--fastcgi',
                              help='start seahub in fastcgi mode',
                              action='store_true')

    parser_start.add_argument('--port',
                              help='start seahub in fastcgi mode',
                              default='8000')

    parser_stop = subparsers.add_parser('stop', help='stop the seafile server')
    parser_stop.set_defaults(func=stop_seafile)

    parser_reset_admin = subparsers.add_parser(
        'reset-admin',
        help='reset seafile admin account')
    parser_reset_admin.set_defaults(func=reset_admin)

    parser_create_admin = subparsers.add_parser(
        'create-admin',
        help='create seafile admin account')
    parser_create_admin.set_defaults(func=reset_admin)

    args = parser.parse_args()
    args.func(args)


if __name__ == '__main__':
    main()
