Пример #1
0
    def __init__(self, cfgdir, tmpdir, debug=None, restore=False):
        self.__running = Event()
        self.__do_restart = False
        self.__do_exit = False
        self._ = lambda x: x

        self.cfgdir = fullpath(cfgdir)
        self.tmpdir = fullpath(tmpdir)

        os.chdir(self.cfgdir)

        # if self.tmpdir not in sys.path:
        # sys.path.append(self.tmpdir)

        # if refresh:
        # cleanpy(PACKDIR)

        self.config = ConfigParser(self.DEFAULT_CONFIGNAME)
        self.debug = self.config.get('log',
                                     'debug') if debug is None else debug
        self.log = LoggerFactory(self, self.debug)

        self._init_database(restore)
        self._init_managers()

        self.request = self.req = RequestFactory(self)

        self._init_api()

        atexit.register(self.exit)
Пример #2
0
    def __init__(self, file, version):
        self.config = ConfigParser(file, version)
        self.stdin_encoding = console_encoding(sys.stdin.encoding)
        self.lang = None
        self.db = None

        # We will create a timestamp so that the setup will be completed in a
        # specific interval
        self.timestamp = time()

        # TODO: probably unneeded
        self.yes = "yes"
        self.no = "no"
Пример #3
0
    def __init__(self, cfgdir, tmpdir, debug=None, restore=False):
        self.__running = Event()
        self.__do_restart = False
        self.__do_exit = False
        self._ = lambda x: x

        self.cfgdir = fullpath(cfgdir)
        self.tmpdir = fullpath(tmpdir)

        os.chdir(self.cfgdir)

        # if self.tmpdir not in sys.path:
        # sys.path.append(self.tmpdir)

        # if refresh:
        # cleanpy(PACKDIR)

        self.config = ConfigParser(self.DEFAULT_CONFIGNAME)
        self.debug = self.config.get(
            'log', 'debug') if debug is None else debug
        self.log = LoggerFactory(self, self.debug)

        self._init_database(restore)
        self._init_managers()

        self.request = self.req = RequestFactory(self)

        self._init_api()

        atexit.register(self.exit)
Пример #4
0
    def _init_config(self, profile, configdir):
        self.profiledir = _gen_profiledir(profile, configdir)
        self.configdir, self.profile = os.path.split(self.profiledir)

        tmproot = os.path.join(TMPDIR, os.path.basename(self.configdir))
        makedirs(tmproot)
        self.tmpdir = tempfile.mkdtemp(dir=tmproot)

        self._init_pid()

        if self.profiledir not in sys.path:
            sys.path.append(self.profiledir)

        os.chdir(self.profiledir)
        self.configfile = os.path.join(self.profiledir, 'pyload.conf')
        self.config = self.cfg = ConfigParser(self.configfile, self.version)
Пример #5
0
class Core(Process):

    __SESSIONFILENAME = 'session.ini'
    __CONFIGFILENAME = 'config.ini'
    DEFAULT_LANGUAGE = 'english'
    DEFAULT_USERNAME = '******'
    DEFAULT_PASSWORD = '******'
    DEFAULT_STORAGEDIRNAME = 'downloads'
    DEFAULT_LOGDIRNAME = 'logs'
    DEFAULT_LOGFILENAME = 'log.txt'

    def _init_consolelogger(self):
        if self.config.get('log', 'color_console') and ismodule('colorlog'):
            fmt = "%(label)s %(levelname)-8s %(reset)s %(log_color)s%(asctime)s  %(message)s"
            datefmt = "%Y-%m-%d  %H:%M:%S"
            primary_colors = {
                'DEBUG': "bold,cyan",
                'WARNING': "bold,yellow",
                'ERROR': "bold,red",
                'CRITICAL': "bold,purple",
            }
            secondary_colors = {
                'label': {
                    'DEBUG': "bold,white,bg_cyan",
                    'INFO': "bold,white,bg_green",
                    'WARNING': "bold,white,bg_yellow",
                    'ERROR': "bold,white,bg_red",
                    'CRITICAL': "bold,white,bg_purple",
                }
            }
            consoleform = colorlog.ColoredFormatter(
                fmt,
                datefmt,
                primary_colors,
                secondary_log_colors=secondary_colors)
        else:
            fmt = "%(asctime)s  %(levelname)-8s  %(message)s"
            datefmt = "%Y-%m-%d %H:%M:%S"
            consoleform = logging.Formatter(fmt, datefmt)

        consolehdlr = logging.StreamHandler(sys.stdout)
        consolehdlr.setFormatter(consoleform)
        self.log.addHandler(consolehdlr)

    def _init_syslogger(self):
        # try to mimic to normal syslog messages
        fmt = "%(asctime)s %(name)s: %(message)s"
        datefmt = "%b %e %H:%M:%S"
        syslogform = logging.Formatter(fmt, datefmt)
        syslogaddr = None

        syslog = self.config.get('log', 'syslog')
        if syslog == 'remote':
            syslog_host = self.config.get('log', 'syslog_host')
            syslog_port = self.config.get('log', 'syslog_port')
            syslogaddr = (syslog_host, syslog_port)
        else:
            syslog_folder = self.config.get('log', 'syslog_folder')
            if syslogaddr:
                syslogaddr = syslog_folder
            elif sys.platform == 'darwin':
                syslogaddr = '/var/run/syslog'
            elif os.name != 'nt':
                syslogaddr = '/dev/log'

        sysloghdlr = logging.handlers.SysLogHandler(syslogaddr)
        sysloghdlr.setFormatter(syslogform)
        self.log.addHandler(sysloghdlr)

    def _init_filelogger(self):
        fmt = "%(asctime)s  %(levelname)-8s  %(message)s"
        datefmt = "%Y-%m-%d %H:%M:%S"
        fileform = logging.Formatter(fmt, datefmt)

        logfile_folder = self.config.get('log', 'logfile_folder')
        if not logfile_folder:
            logfile_folder = self.DEFAULT_LOGDIRNAME
        makedirs(logfile_folder, exist_ok=True)

        logfile_name = self.config.get('log', 'logfile_name')
        if not logfile_name:
            logfile_name = self.DEFAULT_LOGFILENAME
        logfile = os.path.join(logfile_folder, logfile_name)

        if self.config.get('log', 'rotate'):
            logfile_size = self.config.get('log', 'logfile_size') << 10
            max_logfiles = self.config.get('log', 'max_logfiles')
            filehdlr = logging.handlers.RotatingFileHandler(
                logfile,
                maxBytes=logfile_size,
                backupCount=max_logfiles,
                encoding=locale.getpreferredencoding(do_setlocale=False))
        else:
            filehdlr = logging.FileHandler(
                logfile,
                encoding=locale.getpreferredencoding(do_setlocale=False))

        filehdlr.setFormatter(fileform)
        self.log.addHandler(filehdlr)

    # TODO: Extend `logging.Logger` like `..plugin.Log`
    def _init_logger(self):
        level = logging.DEBUG if self.debug else logging.INFO

        # Init logger
        self.log = logging.getLogger()
        self.log.setLevel(level)

        # Set console handler
        self._init_consolelogger()

        # Set syslog handler
        if self.config.get('log', 'syslog') != 'no':
            self._init_syslogger()

        # Set file handler
        if self.config.get('log', 'logfile'):
            self._init_filelogger()

    def _setup_permissions(self):
        if os.name == 'nt':
            return None

        change_group = self.config.get('permission', 'change_group')
        change_user = self.config.get('permission', 'change_user')

        if change_group:
            try:
                group = self.config.get('permission', 'group')
                set_process_group(group)
            except Exception as e:
                self.log.error(self._("Unable to change gid"), str(e))

        if change_user:
            try:
                user = self.config.get('permission', 'user')
                set_process_user(user)
            except Exception as e:
                self.log.error(self._("Unable to change uid"), str(e))

    def set_language(self, lang):
        localedir = resource_filename(__package__, 'locale')
        lc = locale.locale_alias[lang.lower()].split('_', 1)[0]
        trans = get_translation('core', localedir, (lc, ))
        try:
            self._ = trans.ugettext
        except AttributeError:
            self._ = trans.gettext

    def _setup_language(self):
        self.log.debug("Loading language ...")
        lang = self.config.get('general', 'language')
        default = self.DEFAULT_LANGUAGE
        if not lang:
            code = locale.getlocale()[0] or locale.getdefaultlocale()[0]
            lang = default if code is None else code.lower().split('_', 1)[0]
        try:
            self.set_language(lang)
        except Exception as e:
            if lang == default:
                raise
            self.log.warning(
                self._("Unable to load `{0}` language, "
                       "use default `{1}`").format(lang, default), str(e))
            self.set_language(default)

    def _setup_debug(self):
        if self.__debug is None:
            debug_log = self.config.get('log', 'debug')
            verbose_log = self.config.get('log', 'verbose')
            self.__debug = 0 if not debug_log else 2 if verbose_log else 1

    # def start_interface(self, webui=None, rpc=None):
    # if webui is None:
    # webui = self.__webui
    # if rpc is None:
    # rpc = self.__rpc

    # # TODO: Parse `remote`
    # if rpc or self.config.get('rpc', 'activated'):
    # self.log.debug("Activating RPC interface ...")
    # self.rem.start()
    # elif not webui:
    # webui = True

    # TODO: Parse remote host:port

    # if isinstance(webui, str):
    # host, port = map(str.strip, webui.rsplit(':', 1))
    # webui = True
    # else:
    # host, port = (None, None)
    # kwgs = {
    # 'server': self.config.get('webui', 'server'),
    # 'host': host or self.config.get('webui', 'host'),
    # 'port': port or self.config.get('webui', 'port'),
    # 'key': self.config.get('ssl', 'key'),
    # 'cert': self.config.get('ssl', 'cert'),
    # 'ssl': self.config.get('ssl', 'activated')
    # }
    # if webui or self.config.get('webui', 'activated'):
    # from .thread.webserver import WebServer
    # self.webserver = WebServer(self)
    # self.webserver.start()
    # self.svm.add('webui', **kwgs)
    # self.svm.start()

    def _init_api(self):
        from .api import Api
        self.api = Api(self)

    def _init_database(self):
        from .database import DatabaseBackend
        from .datatype import Permission, Role

        # TODO: Move inside DatabaseBackend
        newdb = not os.path.isfile(DatabaseBackend.DB_FILE)
        self.db = DatabaseBackend(self)
        self.db.setup()

        if self.__restore or newdb:
            self.db.add_user(self.DEFAULT_USERNAME, self.DEFAULT_PASSWORD,
                             Role.Admin, Permission.All)
        if self.__restore:
            self.log.warning(
                self._("Restored default login credentials `admin|pyload`"))

    def _init_managers(self):
        from .manager import (AccountManager, AddonManager, EventManager,
                              ExchangeManager, FileManager, InfoManager,
                              PluginManager, TransferManager)

        self.scheduler = sched.scheduler(time.time, time.sleep)
        self.filemanager = self.files = FileManager(self)
        self.pluginmanager = self.pgm = PluginManager(self)
        self.exchangemanager = self.exm = ExchangeManager(self)
        self.eventmanager = self.evm = EventManager(self)
        self.accountmanager = self.acm = AccountManager(self)
        self.infomanager = self.iom = InfoManager(self)
        self.transfermanager = self.tsm = TransferManager(self)
        self.addonmanager = self.adm = AddonManager(self)
        # self.remotemanager = self.rem = RemoteManager(self)
        # self.servermanager = self.svm = ServerManager(self)
        self.db.manager = self.files  # ugly?

    def _init_requests(self):
        self.request = self.req = RequestFactory(self)

    def _init_config(self):
        session = ConfigParser(self.__SESSIONFILENAME, session_defaults)

        flags = portalocker.LOCK_EX | portalocker.LOCK_NB
        portalocker.lock(session.fp, flags)

        profiledir = os.path.join(self.configdir, self.profile)
        psp = psutil.Process()
        session.set('current', 'id', time.time())
        session.set('current', 'profile', 'path', profiledir)
        session.set('current', 'profile', 'pid', psp.pid)
        session.set('current', 'profile', 'ctime', psp.create_time())

        self.config = ConfigParser(self.__CONFIGFILENAME, config_defaults)
        self.session = session

    def _init_cache(self):
        # Re-use cache
        tempdir = self.__tempdir
        if tempdir is None:
            tempdir = self.session.get('previous', 'cache', 'path')
            if tempdir is None or not os.path.isdir(tempdir):
                pydir = os.path.join(TMPDIR, __namespace__)
                makedirs(pydir, exist_ok=True)
                tempdir = tempfile.mkdtemp(dir=pydir)
        self.session.set('current', 'cache', 'path', tempdir)
        self.cachedir = tempdir
        # if tempdir not in sys.path:
        # sys.path.append(tempdir)

    def _register_signals(self):
        shutfn = lambda s, f: self.shutdown()
        quitfn = lambda s, f: self.terminate()
        try:
            if os.name == 'nt':
                # signal.signal(signal.CTRL_C_EVENT, shutfn)
                signal.signal(signal.CTRL_BREAK_EVENT, shutfn)
            else:
                signal.signal(signal.SIGTERM, shutfn)
                # signal.signal(signal.SIGINT, shutfn)
                signal.signal(signal.SIGQUIT, quitfn)
                # signal.signal(signal.SIGTSTP, lambda s, f: self.stop())
                # signal.signal(signal.SIGCONT, lambda s, f: self.run())
        except Exception:
            pass

    def __init__(self,
                 profiledir=None,
                 tempdir=None,
                 debug=None,
                 restore=None):
        self.__running = Event()
        self.__do_restart = False
        self.__do_shutdown = False
        self.__debug = debug if debug is None else int(debug)
        self.__restore = bool(restore)
        self.__tempdir = tempdir
        self._ = lambda x: x

        self._init_profile(profiledir)

        # if refresh:
        # cleanpy(PACKDIR)

        Process.__init__(self)

    @property
    def version(self):
        return __version__

    @property
    def version_info(self):
        return __version_info__

    @property
    def running(self):
        return self.__running.is_set()

    @property
    def debug(self):
        return self.__debug

    def _init_profile(self, profiledir):
        profiledir = fullpath(profiledir)
        os.chdir(profiledir)
        self.configdir, self.profile = os.path.split(profiledir)

    def _setup_process(self):
        try:
            set_process_name('pyLoad')
        except AttributeError:
            pass
        niceness = self.config.get('general', 'niceness')
        renice(niceness=niceness)
        ioniceness = int(self.config.get('general', 'ioniceness'))
        ionice(niceness=ioniceness)

    def _setup_storage(self):
        storage_folder = self.config.get('general', 'storage_folder')
        if not storage_folder:
            storage_folder = os.path.join(USERDIR, self.DEFAULT_STORAGEDIRNAME)
        self.log.debug("Storage: {0}".format(storage_folder))
        makedirs(storage_folder, exist_ok=True)
        avail_space = format.size(availspace(storage_folder))
        self.log.info(
            self._("Available storage space: {0}").format(avail_space))

    def _workloop(self):
        self.__running.set()
        self.tsm.pause = False  # NOTE: Recheck...
        while True:
            self.__running.wait()
            self.tsm.work()
            self.iom.work()
            self.exm.work()
            if self.__do_restart:
                raise Restart
            if self.__do_shutdown:
                raise Shutdown
            self.scheduler.run()

    def _start_plugins(self):
        # TODO: Move to accountmanager
        self.log.info(self._("Activating accounts ..."))
        self.acm.load_accounts()
        # self.scheduler.enter(0, 0, self.acm.load_accounts)
        self.adm.activate_addons()

    def _show_info(self):
        self.log.info(self._("Welcome to pyLoad v{0}").format(self.version))

        self.log.info(self._("Profile: {0}").format(self.profile))
        self.log.info(self._("Config directory: {0}").format(self.configdir))

        self.log.debug("Cache directory: {0}".format(self.cachedir))

    def run(self):
        self._init_config()
        self._init_cache()
        self._setup_debug()
        self._init_logger()
        try:
            self.log.debug("Running pyLoad ...")

            self._setup_language()
            self._setup_permissions()
            self._init_database()
            self._init_managers()
            self._init_requests()
            self._init_api()

            self._show_info()
            self._setup_storage()
            self._start_plugins()
            self._setup_process()

            self.log.info(self._("pyLoad is up and running"))
            self.evm.fire('pyload:started')

            # # some memory stats
            # from guppy import hpy
            # hp=hpy()
            # print(hp.heap())
            # import objgraph
            # objgraph.show_most_common_types(limit=30)
            # import memdebug
            # memdebug.start(8002)
            # from meliae import scanner
            # scanner.dump_all_objects(os.path.join(PACKDIR, 'objs.json'))

            self._workloop()

        except Restart:
            self.restart()
        except Shutdown:
            self.shutdown()
        except (KeyboardInterrupt, SystemExit):
            self.shutdown()
        except Exception as e:
            self.log.critical(str(e))
            self.terminate()
            raise
        else:
            self.shutdown()

    def _remove_loggers(self):
        for handler in self.log.handlers:
            with closing(handler) as hdlr:
                self.log.removeHandler(hdlr)

    def restart(self):
        self.stop()
        self.log.info(self._("Restarting pyLoad ..."))
        self.evm.fire('pyload:restarting')
        self.start()

    def _register_instance(self):
        profiledir = os.path.join(self.configdir, self.profile)
        if profiledir in _pmap:
            raise RuntimeError("A pyLoad instance using profile `{0}` "
                               "is already running".format(profiledir))
        _pmap[profiledir] = self

    def _unregister_instance(self):
        profiledir = os.path.join(self.configdir, self.profile)
        _pmap.pop(profiledir, None)

    def _close_session(self):
        id = self.session.get('previous', 'id')
        self.session[id] = self.session['previous']
        self.session['previous'] = self.session['current']
        self.session['current'].reset()
        self.session.close()

    def terminate(self):
        try:
            self.log.debug("Killing pyLoad ...")
            self._unregister_instance()
            self._close_session()
        finally:
            Process.terminate(self)

    def shutdown(self):
        try:
            self.stop()
            self.log.info(self._("Exiting pyLoad ..."))
            self.tsm.shutdown()
            self.db.shutdown()  # NOTE: Why here?
            self.config.close()
            self._remove_loggers()
            # if cleanup:
            # self.log.info(self._("Deleting temp files ..."))
            # remove(self.tempdir, ignore_errors=True)
        finally:
            self.terminate()

    def start(self):
        if not self.is_alive():
            self._register_instance()
            self._register_signals()
            Process.start(self)
        elif not self.running:
            self.log.info(self._("Starting pyLoad ..."))
            self.evm.fire('pyload:starting')
            self.__running.set()

    def stop(self):
        if not self.running:
            return None
        try:
            self.log.info(self._("Stopping pyLoad ..."))
            self.evm.fire('pyload:stopping')
            self.adm.deactivate_addons()
            self.api.stop_all_downloads()
        finally:
            self.files.sync_save()
            self.__running.clear()
            self.evm.fire('pyload:stopped')
Пример #6
0
    def _init_config(self):
        session = ConfigParser(self.__SESSIONFILENAME, session_defaults)

        flags = portalocker.LOCK_EX | portalocker.LOCK_NB
        portalocker.lock(session.fp, flags)

        profiledir = os.path.join(self.configdir, self.profile)
        psp = psutil.Process()
        session.set('current', 'id', time.time())
        session.set('current', 'profile', 'path', profiledir)
        session.set('current', 'profile', 'pid', psp.pid)
        session.set('current', 'profile', 'ctime', psp.create_time())

        self.config = ConfigParser(self.__CONFIGFILENAME, config_defaults)
        self.session = session
Пример #7
0
class Core(object):

    DEFAULT_CONFIGNAME = 'config.ini'
    DEFAULT_LANGUAGE = 'english'
    DEFAULT_USERNAME = '******'
    DEFAULT_PASSWORD = '******'
    DEFAULT_STORAGENAME = 'downloads'

    @property
    def version(self):
        return __version__

    @property
    def version_info(self):
        return __version_info__

    @property
    def running(self):
        return self.__running.is_set()

    def __init__(self, cfgdir, tmpdir, debug=None, restore=False):
        self.__running = Event()
        self.__do_restart = False
        self.__do_exit = False
        self._ = lambda x: x

        self.cfgdir = fullpath(cfgdir)
        self.tmpdir = fullpath(tmpdir)

        os.chdir(self.cfgdir)

        # if self.tmpdir not in sys.path:
        # sys.path.append(self.tmpdir)

        # if refresh:
        # cleanpy(PACKDIR)

        self.config = ConfigParser(self.DEFAULT_CONFIGNAME)
        self.debug = self.config.get('log',
                                     'debug') if debug is None else debug
        self.log = LoggerFactory(self, self.debug)

        self._init_database(restore)
        self._init_managers()

        self.request = self.req = RequestFactory(self)

        self._init_api()

        atexit.register(self.exit)

    def _init_api(self):
        from pyload.api import Api
        self.api = Api(self)

    def _init_database(self, restore):
        from pyload.core.database import DatabaseBackend
        from pyload.core.datatype import Permission, Role

        # TODO: Move inside DatabaseBackend
        newdb = not os.path.isfile(DatabaseBackend.DB_FILE)
        self.db = DatabaseBackend(self)
        self.db.setup()

        if restore or newdb:
            self.db.add_user(self.DEFAULT_USERNAME, self.DEFAULT_PASSWORD,
                             Role.Admin, Permission.All)
        if restore:
            self.log.warning(
                self._('Restored default login credentials `admin|pyload`'))

    def _init_managers(self):
        from pyload.core.manager import (AccountManager, AddonManager,
                                         EventManager, ExchangeManager,
                                         FileManager, InfoManager,
                                         PluginManager, TransferManager)

        self.scheduler = sched.scheduler(time.time, time.sleep)
        self.filemanager = self.files = FileManager(self)
        self.pluginmanager = self.pgm = PluginManager(self)
        self.exchangemanager = self.exm = ExchangeManager(self)
        self.eventmanager = self.evm = EventManager(self)
        self.accountmanager = self.acm = AccountManager(self)
        self.infomanager = self.iom = InfoManager(self)
        self.transfermanager = self.tsm = TransferManager(self)
        # TODO: Remove builtins.ADDONMANAGER
        builtins.ADDONMANAGER = self.addonmanager = self.adm = AddonManager(
            self)
        # self.remotemanager = self.rem = RemoteManager(self)
        # self.servermanager = self.svm = ServerManager(self)
        self.db.manager = self.files  # ugly?

    def _setup_permissions(self):
        self.log.debug('Setup permissions...')

        if os.name == 'nt':
            return

        change_group = self.config.get('permission', 'change_group')
        change_user = self.config.get('permission', 'change_user')

        if change_group:
            try:
                group = self.config.get('permission', 'group')
                set_process_group(group)
            except Exception as exc:
                self.log.error(self._('Unable to change gid'))
                self.log.error(exc, exc_info=self.debug)

        if change_user:
            try:
                user = self.config.get('permission', 'user')
                set_process_user(user)
            except Exception as exc:
                self.log.error(self._('Unable to change uid'))
                self.log.error(exc, exc_info=self.debug)

    def set_language(self, lang):
        domain = 'core'
        localedir = resource_filename(__package__, 'locale')
        languages = (locale.locale_alias[lang.lower()].split('_', 1)[0], )
        self._set_language(domain, localedir, languages)

    def _set_language(self, *args, **kwargs):
        trans = gettext.translation(*args, **kwargs)
        try:
            self._ = trans.ugettext
        except AttributeError:
            self._ = trans.gettext

    def _setup_language(self):
        self.log.debug('Setup language...')

        lang = self.config.get('general', 'language')
        if not lang:
            lc = locale.getlocale()[0] or locale.getdefaultlocale()[0]
            lang = lc.split('_', 1)[0] if lc else 'en'

        try:
            self.set_language(lang)
        except IOError as exc:
            self.log.error(exc, exc_info=self.debug)
            self._set_language('core', fallback=True)

    # def _setup_niceness(self):
    # niceness = self.config.get('general', 'niceness')
    # renice(niceness=niceness)
    # ioniceness = int(self.config.get('general', 'ioniceness'))
    # ionice(niceness=ioniceness)

    def _setup_storage(self):
        self.log.debug('Setup storage...')

        storage_folder = self.config.get('general', 'storage_folder')
        if storage_folder is None:
            storage_folder = os.path.join(builtins.USERDIR,
                                          self.DEFAULT_STORAGENAME)
        self.log.info(self._('Storage: {0}'.format(storage_folder)))
        makedirs(storage_folder, exist_ok=True)
        avail_space = format.size(availspace(storage_folder))
        self.log.info(
            self._('Available storage space: {0}').format(avail_space))

    def _setup_network(self):
        self.log.debug('Setup network...')

        # TODO: Move to accountmanager
        self.log.info(self._('Activating accounts...'))
        self.acm.load_accounts()
        # self.scheduler.enter(0, 0, self.acm.load_accounts)
        self.adm.activate_addons()

    def run(self):
        self.log.info('Welcome to pyLoad v{0}'.format(self.version))
        if self.debug:
            self.log.warning('*** DEBUG MODE ***')
        try:
            self.log.debug('Starting pyLoad...')
            self.evm.fire('pyload:starting')
            self.__running.set()

            self._setup_language()
            self._setup_permissions()

            self.log.info(self._('Config directory: {0}').format(self.cfgdir))
            self.log.info(self._('Cache directory: {0}').format(self.tmpdir))

            self._setup_storage()
            self._setup_network()
            # self._setup_niceness()

            # # some memory stats
            # from guppy import hpy
            # hp=hpy()
            # print(hp.heap())
            # import objgraph
            # objgraph.show_most_common_types(limit=30)
            # import memdebug
            # memdebug.start(8002)
            # from meliae import scanner
            # scanner.dump_all_objects(os.path.join(PACKDIR, 'objs.json'))

            self.log.debug('pyLoad is up and running')
            self.evm.fire('pyload:started')

            self.tsm.pause = False  # NOTE: Recheck...
            while True:
                self.__running.wait()
                self.tsm.work()
                self.iom.work()
                self.exm.work()
                if self.__do_restart:
                    raise Restart
                if self.__do_exit:
                    raise Exit
                self.scheduler.run()
                time.sleep(1)

        except Restart:
            self.restart()

        except (Exit, KeyboardInterrupt, SystemExit):
            self.exit()

        except Exception as exc:
            self.log.critical(exc, exc_info=True)
            self.exit()

    def _remove_loggers(self):
        for handler in self.log.handlers:
            with closing(handler) as hdlr:
                self.log.removeHandler(hdlr)

    def restart(self):
        self.stop()
        self.log.info(self._('Restarting pyLoad...'))
        self.evm.fire('pyload:restarting')
        self.run()

    def exit(self):
        self.stop()
        self.log.info(self._('Exiting pyLoad...'))
        self.tsm.exit()
        self.db.exit()  # NOTE: Why here?
        self._remove_loggers()
        # if cleanup:
        # self.log.info(self._("Deleting temp files..."))
        # remove(self.tmpdir, ignore_errors=True)

    def stop(self):
        try:
            self.log.debug('Stopping pyLoad...')
            self.evm.fire('pyload:stopping')
            self.adm.deactivate_addons()
            self.api.stop_all_downloads()
        finally:
            self.files.sync_save()
            self.__running.clear()
            self.evm.fire('pyload:stopped')
Пример #8
0
class SetupAssistant(object):
    """
    pyLoads initial setup configuration assistant.
    """
    __slots__ = [
        'config', 'db', 'lang', 'no', 'stdin_encoding', 'timestamp', 'yes'
    ]

    def __init__(self, file, version):
        self.config = ConfigParser(file, version)
        self.stdin_encoding = console_encoding(sys.stdin.encoding)
        self.lang = None
        self.db = None

        # We will create a timestamp so that the setup will be completed in a
        # specific interval
        self.timestamp = time()

        # TODO: probably unneeded
        self.yes = "yes"
        self.no = "no"

    def start(self):
        web = WebServer(pysetup=self)
        web.start()

        error = web.check_error()

        # TODO: start cli in this case
        if error:  # TODO: errno 44 port already in use
            print(error)

        url = "http://{0}:{1:d}/".format(
            socket.gethostbyname(socket.gethostname()), web.port)

        print("Setup is running at {0}".format(url))

        opened = webbrowser.open_new_tab(url)
        if not opened:
            print("Please point your browser to the url above")

        eval(input())

        return True

    def start_cli(self):
        self.ask_lang()

        print(_("Welcome to the pyLoad Configuration Assistent"))
        print(
            _("It will check your system and make a basic setup in order to run pyLoad"
              ))
        print("")
        print(_("The value in brackets [] always is the default value,"))
        print(
            _("in case you do not want to change it or you are unsure what to choose, just hit enter"
              ))
        print(
            _("Don't forget: You can always rerun this assistent with --setup or -s parameter, when you start pyLoadCore"
              ))
        print(_("If you have any problems with this assistent hit CTRL+C,"))
        print(
            _("to abort and do not let him start with pyLoadCore automatically anymore"
              ))
        print("")
        print(_("When you are ready for system check, hit enter"))
        eval(input())

        # TODO: new system check + deps

        con = self.ask(_("Continue with setup?"), self.yes, bool=True)

        if not con:
            return False

        print("")
        print(
            _("Do you want to change the config path? Current is {0}").format(
                os.path.abspath("")))
        print(
            _("If you use pyLoad on a server or the home partition lives on an internal flash it may be a good idea to change it"
              ))
        path = self.ask(_("Change config path?"), self.no, bool=True)
        if path:
            self.conf_path()
            # calls exit when changed

        print("")
        print(_("Do you want to configure login data and basic settings?"))
        print(_("This is recommend for first run"))
        con = self.ask(_("Make basic setup?"), self.yes, bool=True)
        if con:
            self.conf_basic()

        print("")
        print(_("Do you want to configure ssl?"))
        ssl = self.ask(_("Configure ssl?"), self.no, bool=True)
        if ssl:
            self.conf_ssl()

        print("")
        print(_("Do you want to configure the Web UI?"))
        web = self.ask(_("Configure the Web UI?"), self.yes, bool=True)
        if web:
            self.conf_web()

        print("")
        print(_("Setup finished successfully"))
        print(_("Hit enter to exit and restart pyLoad"))
        eval(input())
        return True

    def conf_basic(self):
        print("")
        print(_("## Basic Setup ##"))

        from pyload.core.database import DatabaseBackend

        db = DatabaseBackend(None)
        db.setup()
        username = self.ask(_("Username"), "User")
        password = self.ask("", "", password=True)
        db.add_user(username, password)
        db.shutdown()

        print("")
        langs = self.config.get_meta_data("general", "language")
        self.config.set('general', 'language',
                        self.ask(_("Language"), "en", langs.type.split(";")))

        self.config.set('general', 'storage_folder',
                        self.ask(_("Storage folder"), "Storage"))
        self.config.set('connection', 'max_transfers',
                        self.ask(_("Max parallel transfers"), "3"))

        reconnect = self.ask(_("Use Reconnect?"), self.no, bool=True)
        self.config.set('reconnect', 'activated', reconnect)
        if reconnect:
            self.config.set(
                'reconnect', 'script',
                self.ask(_("Reconnection script"), "./reconnect.sh"))

    def conf_web(self):
        print("")
        print(_("## Web UI Setup ##"))

        print("")
        self.config.set(
            'webui', 'activated',
            self.ask(_("Activate the Web UI?"), self.yes, bool=True))
        print("")
        print(
            _("Listen address, if you use 127.0.0.1 or localhost, the Web UI will only accessible locally"
              ))
        self.config.set('webui', 'host', self.ask(_("Address"), "localhost"))
        self.config.set('webui', 'port', self.ask(_("Port"), "8010"))
        print("")
        print(
            _("pyLoad offers several server backends, now following a short explanation"
              ))
        print(
            "threaded:",
            _("Default server, this server offers SSL and is a good alternative to builtin"
              ))
        print(
            "fastcgi:",
            _("Can be used by apache, lighttpd, requires you to configure them, which is not too easy job"
              ))
        print(
            "lightweight:",
            _("Very fast alternative written in C, requires libev and linux knowledge"
              ))
        print(
            "\t",
            _("Get it from here: https://github.com/jonashaag/bjoern, compile it"
              ))
        print("\t", _("and copy bjoern.so to pyload/lib"))

        print()
        print(
            _("Attention: In some rare cases the builtin server is not working, if you notice problems with the Web UI"
              ))
        print(
            _("come back here and change the builtin server to the threaded one here"
              ))

        self.config.set(
            'webui', 'server',
            self.ask(_("Server"), "threaded",
                     ["builtin", "threaded", "fastcgi", "lightweight"]))

    def conf_ssl(self):
        print("")
        print(_("## SSL Setup ##"))
        print("")
        print(
            _("Execute these commands from pyLoad config folder to make ssl certificates:"
              ))
        print("")
        print("openssl genrsa -out ssl.key 1024")
        print("openssl req -new -key ssl.key -out ssl.csr")
        print(
            "openssl req -days 36500 -x509 -key ssl.key -in ssl.csr > ssl.crt "
        )
        print("")
        print(
            _("If you're done and everything went fine, you can activate ssl now"
              ))
        self.config.set('ssl', 'activated',
                        self.ask(_("Activate SSL?"), self.yes, bool=True))

    def set_user(self):
        translation = gettext.translation(
            "setup",
            os.path.join(PACKDIR, "locale"),
            languages=[self.config.get('general', 'language'), "en"],
            fallback=True)
        translation.install(True)

        self.open_db()

        try:
            while True:
                print(_("Select action"))
                print(_("1 - Create/Edit user"))
                print(_("2 - List users"))
                print(_("3 - Remove user"))
                print(_("4 - Quit"))
                action = eval(input("[1]/2/3/4: "))
                if action not in ("1", "2", "3", "4"):
                    continue
                elif action == "1":
                    print("")
                    username = self.ask(_("Username"), "User")
                    password = self.ask("", "", password=True)
                    admin = self.ask("Admin?", self.yes, bool=True)

                    self.db.add_user(username, password,
                                     Role.Admin if admin else Role.User,
                                     int('1111111', 2))
                elif action == "2":
                    print("")
                    print(_("Users"))
                    print("-----")
                    users = self.db.get_all_user_data()
                    for user in users.values():
                        print(user.name)
                    print("-----")
                    print("")
                elif action == "3":
                    print("")
                    username = self.ask(_("Username"), "")
                    if username:
                        self.db.remove_user_by_name(username)
                elif action == "4":
                    self.db.sync_save()
                    break
        finally:
            self.close_db()

    def add_user(self, username, password, role=Role.Admin):
        self.open_db()
        try:
            self.db.add_user(username, password, role, int('1111111', 2))
        finally:
            self.close_db()

    def open_db(self):
        from pyload.core.database import DatabaseBackend

        if self.db is None:
            self.db = DatabaseBackend(None)
            self.db.setup()

    def close_db(self):
        if self.db is not None:
            self.db.sync_save()
            self.db.shutdown()

    def save(self):
        self.config.save()
        self.close_db()

    def conf_path(self, trans=False):
        if trans:
            translation = gettext.translation(
                "setup",
                os.path.join(PACKDIR, "locale"),
                languages=[self.config.get('general', 'language'), "en"],
                fallback=True)
            translation.install(True)

        print(
            _("Setting new configpath, current configuration will not be transferred!"
              ))
        path = self.ask(_("Config path"), os.path.abspath(""))
        try:
            path = os.path.join(COREDIR, path)
            if not os.path.exists(path):
                makedirs(path)
            with io.open(os.path.join(COREDIR, "pyload", "config",
                                      "configdir"),
                         mode='wb') as fp:
                fp.write(path)
            print(
                _("Config path changed, setup will now close, please restart to go on"
                  ))
            print(_("Press Enter to exit"))
            eval(input())
            exit()
        except Exception as e:
            print(_("Setting config path failed: {0}").format(e.message))

    def ask_lang(self):
        langs = self.config.get_meta_data("general",
                                          "language").type.split(";")
        self.lang = self.ask(u"Choose your Language / Wähle deine Sprache",
                             "en", langs)
        translation = gettext.translation("setup",
                                          os.path.join(PACKDIR, "locale"),
                                          languages=[self.lang, "en"],
                                          fallback=True)
        translation.install(True)

        # l10n Input shorthand for yes
        self.yes = _("y")
        # l10n Input shorthand for no
        self.no = _("n")

    def ask(self, qst, default, answers=[], bool=False, password=False):
        """
        Generate dialog on command line.
        """
        if answers:
            info = "("
            for i, answer in enumerate(answers):
                info += (", " if i != 0 else "") + str(
                    (answer == default and "[{0}]".format(answer)) or answer)

            info += ")"
        elif bool:
            if default == self.yes:
                info = "([{0}]/{1})".format(self.yes, self.no)
            else:
                info = "({0}/[{1}])".format(self.yes, self.no)
        else:
            info = "[{0}]".format(default)

        if password:
            p1 = True
            p2 = False
            while p1 != p2:
                # getpass(_("Password: "******"Password: "******"")

                if len(p1) < 4:
                    print(_("Password too short. Use at least 4 symbols"))
                    continue

                sys.stdout.write(_("Password (again): "))
                p2 = getpass("")

                if p1 == p2:
                    return p1
                else:
                    print(_("Passwords did not match"))

        while True:
            input = eval(input(qst + " {0}: ".format(info)))
            input = input.decode(self.stdin_encoding)

            if input.strip() == "":
                input = default

            if bool:
                # l10n yes, true,t are inputs for booleans with value true
                if input.lower().strip() in [
                        self.yes, _("yes"),
                        _("true"),
                        _("t"), "yes"
                ]:
                    return True
                # l10n no, false,f are inputs for booleans with value false
                elif input.lower().strip() in [
                        self.no, _("no"),
                        _("false"), _("f"), "no"
                ]:
                    return False
                else:
                    print(_("Invalid Input"))
                    continue

            if not answers:
                return input

            else:
                if input in answers:
                    return input
                else:
                    print(_("Invalid Input"))
Пример #9
0
class Core(object):

    DEFAULT_CONFIGNAME = 'config.ini'
    DEFAULT_LANGUAGE = 'english'
    DEFAULT_USERNAME = '******'
    DEFAULT_PASSWORD = '******'
    DEFAULT_STORAGENAME = 'downloads'

    @property
    def version(self):
        return __version__

    @property
    def version_info(self):
        return __version_info__

    @property
    def running(self):
        return self.__running.is_set()

    def __init__(self, cfgdir, tmpdir, debug=None, restore=False):
        self.__running = Event()
        self.__do_restart = False
        self.__do_exit = False
        self._ = lambda x: x

        self.cfgdir = fullpath(cfgdir)
        self.tmpdir = fullpath(tmpdir)

        os.chdir(self.cfgdir)

        # if self.tmpdir not in sys.path:
        # sys.path.append(self.tmpdir)

        # if refresh:
        # cleanpy(PACKDIR)

        self.config = ConfigParser(self.DEFAULT_CONFIGNAME)
        self.debug = self.config.get(
            'log', 'debug') if debug is None else debug
        self.log = LoggerFactory(self, self.debug)

        self._init_database(restore)
        self._init_managers()

        self.request = self.req = RequestFactory(self)

        self._init_api()

        atexit.register(self.exit)

    def _init_api(self):
        from pyload.api import Api
        self.api = Api(self)

    def _init_database(self, restore):
        from pyload.core.database import DatabaseBackend
        from pyload.core.datatype import Permission, Role

        # TODO: Move inside DatabaseBackend
        newdb = not os.path.isfile(DatabaseBackend.DB_FILE)
        self.db = DatabaseBackend(self)
        self.db.setup()

        if restore or newdb:
            self.db.add_user(
                self.DEFAULT_USERNAME, self.DEFAULT_PASSWORD, Role.Admin,
                Permission.All)
        if restore:
            self.log.warning(
                self._('Restored default login credentials `admin|pyload`'))

    def _init_managers(self):
        from pyload.core.manager import (
            AccountManager, AddonManager, EventManager, ExchangeManager,
            FileManager, InfoManager, PluginManager, TransferManager)

        self.scheduler = sched.scheduler(time.time, time.sleep)
        self.filemanager = self.files = FileManager(self)
        self.pluginmanager = self.pgm = PluginManager(self)
        self.exchangemanager = self.exm = ExchangeManager(self)
        self.eventmanager = self.evm = EventManager(self)
        self.accountmanager = self.acm = AccountManager(self)
        self.infomanager = self.iom = InfoManager(self)
        self.transfermanager = self.tsm = TransferManager(self)
        # TODO: Remove builtins.ADDONMANAGER
        builtins.ADDONMANAGER = self.addonmanager = self.adm = AddonManager(
            self)
        # self.remotemanager = self.rem = RemoteManager(self)
        # self.servermanager = self.svm = ServerManager(self)
        self.db.manager = self.files  # ugly?

    def _setup_permissions(self):
        self.log.debug('Setup permissions...')

        if os.name == 'nt':
            return

        change_group = self.config.get('permission', 'change_group')
        change_user = self.config.get('permission', 'change_user')

        if change_group:
            try:
                group = self.config.get('permission', 'group')
                set_process_group(group)
            except Exception as exc:
                self.log.error(self._('Unable to change gid'))
                self.log.error(exc, exc_info=self.debug)

        if change_user:
            try:
                user = self.config.get('permission', 'user')
                set_process_user(user)
            except Exception as exc:
                self.log.error(self._('Unable to change uid'))
                self.log.error(exc, exc_info=self.debug)

    def set_language(self, lang):
        domain = 'core'
        localedir = resource_filename(__package__, 'locale')
        languages = (locale.locale_alias[lang.lower()].split('_', 1)[0],)
        self._set_language(domain, localedir, languages)

    def _set_language(self, *args, **kwargs):
        trans = gettext.translation(*args, **kwargs)
        try:
            self._ = trans.ugettext
        except AttributeError:
            self._ = trans.gettext

    def _setup_language(self):
        self.log.debug('Setup language...')

        lang = self.config.get('general', 'language')
        if not lang:
            lc = locale.getlocale()[0] or locale.getdefaultlocale()[0]
            lang = lc.split('_', 1)[0] if lc else 'en'

        try:
            self.set_language(lang)
        except IOError as exc:
            self.log.error(exc, exc_info=self.debug)
            self._set_language('core', fallback=True)

    # def _setup_niceness(self):
        # niceness = self.config.get('general', 'niceness')
        # renice(niceness=niceness)
        # ioniceness = int(self.config.get('general', 'ioniceness'))
        # ionice(niceness=ioniceness)

    def _setup_storage(self):
        self.log.debug('Setup storage...')

        storage_folder = self.config.get('general', 'storage_folder')
        if storage_folder is None:
            storage_folder = os.path.join(
                builtins.USERDIR, self.DEFAULT_STORAGENAME)
        self.log.info(self._('Storage: {0}'.format(storage_folder)))
        makedirs(storage_folder, exist_ok=True)
        avail_space = format.size(availspace(storage_folder))
        self.log.info(
            self._('Available storage space: {0}').format(avail_space))

    def _setup_network(self):
        self.log.debug('Setup network...')

        # TODO: Move to accountmanager
        self.log.info(self._('Activating accounts...'))
        self.acm.load_accounts()
        # self.scheduler.enter(0, 0, self.acm.load_accounts)
        self.adm.activate_addons()

    def run(self):
        self.log.info('Welcome to pyLoad v{0}'.format(self.version))
        if self.debug:
            self.log.warning('*** DEBUG MODE ***')
        try:
            self.log.debug('Starting pyLoad...')
            self.evm.fire('pyload:starting')
            self.__running.set()

            self._setup_language()
            self._setup_permissions()

            self.log.info(self._('Config directory: {0}').format(self.cfgdir))
            self.log.info(self._('Cache directory: {0}').format(self.tmpdir))

            self._setup_storage()
            self._setup_network()
            # self._setup_niceness()

            # # some memory stats
            # from guppy import hpy
            # hp=hpy()
            # print(hp.heap())
            # import objgraph
            # objgraph.show_most_common_types(limit=30)
            # import memdebug
            # memdebug.start(8002)
            # from meliae import scanner
            # scanner.dump_all_objects(os.path.join(PACKDIR, 'objs.json'))

            self.log.debug('pyLoad is up and running')
            self.evm.fire('pyload:started')

            self.tsm.pause = False  # NOTE: Recheck...
            while True:
                self.__running.wait()
                self.tsm.work()
                self.iom.work()
                self.exm.work()
                if self.__do_restart:
                    raise Restart
                if self.__do_exit:
                    raise Exit
                self.scheduler.run()
                time.sleep(1)

        except Restart:
            self.restart()

        except (Exit, KeyboardInterrupt, SystemExit):
            self.exit()

        except Exception as exc:
            self.log.critical(exc, exc_info=True)
            self.exit()

    def _remove_loggers(self):
        for handler in self.log.handlers:
            with closing(handler) as hdlr:
                self.log.removeHandler(hdlr)

    def restart(self):
        self.stop()
        self.log.info(self._('Restarting pyLoad...'))
        self.evm.fire('pyload:restarting')
        self.run()

    def exit(self):
        self.stop()
        self.log.info(self._('Exiting pyLoad...'))
        self.tsm.exit()
        self.db.exit()  # NOTE: Why here?
        self._remove_loggers()
        # if cleanup:
        # self.log.info(self._("Deleting temp files..."))
        # remove(self.tmpdir, ignore_errors=True)

    def stop(self):
        try:
            self.log.debug('Stopping pyLoad...')
            self.evm.fire('pyload:stopping')
            self.adm.deactivate_addons()
            self.api.stop_all_downloads()
        finally:
            self.files.sync_save()
            self.__running.clear()
            self.evm.fire('pyload:stopped')