diff options
| author | 2013-08-08 17:38:35 +0200 | |
|---|---|---|
| committer | 2013-08-08 17:38:35 +0200 | |
| commit | 52f6599748ef61219112111dc5db71f3342b076d (patch) | |
| tree | e3627ded64b7e98493ca1ec7bd182aaa1774252e /pyload | |
| parent | MultiHosters: moved settings to addon plugins. (diff) | |
| download | pyload-52f6599748ef61219112111dc5db71f3342b076d.tar.xz | |
adapted account api to multi user, fixed http referer bug
Diffstat (limited to 'pyload')
25 files changed, 304 insertions, 230 deletions
| diff --git a/pyload/AccountManager.py b/pyload/AccountManager.py index ab753c2e4..1c409b754 100644 --- a/pyload/AccountManager.py +++ b/pyload/AccountManager.py @@ -19,8 +19,10 @@  from threading import Lock  from random import choice +from pyload.Api import AccountInfo  from pyload.utils import lock, json +  class AccountManager:      """manages all accounts""" @@ -30,111 +32,123 @@ class AccountManager:          self.core = core          self.lock = Lock() +        # PluginName mapped to list of account instances +        self.accounts = {} +          self.loadAccounts() -    def loadAccounts(self): -        """loads all accounts available""" +    def _createAccount(self, info, password, options): +        plugin = info.plugin +        loginname = info.loginname +        # Owner != None must be enforced +        if info.owner is None: +            raise ValueError("Owner must not be null") -        self.accounts = {} +        klass = self.core.pluginManager.loadClass("accounts", plugin) +        if not klass: +            self.core.log.warning(_("Unknown account plugin %s") % plugin) +            return -        for plugin, loginname, activated, password, options in self.core.db.loadAccounts(): -            # put into options as used in other context -            options = json.loads(options) if options else {} -            options["activated"] = activated +        if plugin not in self.accounts: +            self.accounts[plugin] = [] -            self.createAccount(plugin, loginname, password, options) +        self.core.log.debug("Create account %s:%s" % (plugin, loginname)) +        # New account instance +        account = klass.fromInfoData(self, info, password, options) +        self.accounts[plugin].append(account) +        return account + +    def loadAccounts(self): +        """loads all accounts available from db""" + +        for info, password, options in self.core.db.loadAccounts(): +            # put into options as used in other context +            options = json.loads(options) if options else {} +            try: +                self._createAccount(info, password, options) +            except: +                self.core.log.error(_("Could not load account %s") % info) +                self.core.print_exc()      def iterAccounts(self):          """ yields login, account  for all accounts""" -        for name, data in self.accounts.iteritems(): -            for login, account in data.iteritems(): -                yield login, account +        for plugin, accounts in self.accounts.iteritems(): +            for account in accounts: +                yield plugin, account      def saveAccounts(self):          """save all account information""" -        # TODO: multi user -        # TODO: activated -          data = [] -        for name, plugin in self.accounts.iteritems(): +        for plugin, accounts in self.accounts.iteritems():              data.extend( -                [(name, acc.loginname, 1 if acc.activated else 0, acc.password, json.dumps(acc.options)) for acc in -                 plugin.itervalues()]) +                [(plugin, acc.loginname, acc.owner, 1 if acc.activated else 0, 1 if acc.shared else 0, acc.password, +                  json.dumps(acc.options)) for acc in +                 accounts])          self.core.db.saveAccounts(data) -    def createAccount(self, plugin, loginname, password, options): -        klass = self.core.pluginManager.loadClass("accounts", plugin) -        if not klass: -            self.core.log.warning(_("Unknown account plugin %s") % plugin) -            return - -        if plugin not in self.accounts: -            self.accounts[plugin] = {} - -        self.core.log.debug("Create account %s:%s" % (plugin, loginname)) - -        self.accounts[plugin][loginname] = klass(self, loginname, password, options) - - -    def getAccount(self, plugin, user): -        return self.accounts[plugin].get(user, None) +    def getAccount(self, plugin, loginname, user=None): +        """ Find a account by specific user (if given) """ +        if plugin in self.accounts: +            for acc in self.accounts[plugin]: +                if acc.loginname == loginname and (not user or acc.owner == user.true_primary): +                    return acc      @lock -    def updateAccount(self, plugin, user, password=None, options={}): +    def updateAccount(self, plugin, loginname, password, user):          """add or update account""" -        if plugin in self.accounts and user in self.accounts[plugin]: -            acc = self.accounts[plugin][user] -            updated = acc.update(password, options) - -            self.saveAccounts() -            if updated: acc.scheduleRefresh(force=True) +        account = self.getAccount(plugin, loginname, user) +        if account: +            if account.setPassword(password): +                self.saveAccounts() +                account.scheduleRefresh(force=True)          else: -            self.createAccount(plugin, user, password, options) +            info = AccountInfo(plugin, loginname, user.true_primary, activated=True) +            account = self._createAccount(info, password, {}) +            account.scheduleRefresh()              self.saveAccounts() -        self.sendChange(plugin, user) +        self.sendChange(plugin, loginname) +        return account      @lock -    def removeAccount(self, plugin, user): +    def removeAccount(self, plugin, loginname, uid):          """remove account""" -        if plugin in self.accounts and user in self.accounts[plugin]: -            del self.accounts[plugin][user] -            self.core.db.removeAccount(plugin, user) -            self.core.eventManager.dispatchEvent("account:deleted", plugin, user) -        else: -            self.core.log.debug("Remove non existent account %s %s" % (plugin, user)) - +        if plugin in self.accounts: +            for acc in self.accounts[plugin]: +                # admins may delete accounts +                if acc.loginname == loginname and (not uid or acc.owner == uid): +                    self.accounts[plugin].remove(acc) +                    self.core.db.removeAccount(plugin, loginname) +                    self.core.evm.dispatchEvent("account:deleted", plugin, loginname) +                    break      @lock -    def getAccountForPlugin(self, plugin): +    def selectAccount(self, plugin, user): +        """ Determines suitable plugins and select one """          if plugin in self.accounts: -            accs = [x for x in self.accounts[plugin].values() if x.isUsable()] +            uid = user.true_primary if user else None +            accs = [x for x in self.accounts[plugin] if x.isUsable() and (x.shared or x.owner == uid)]              if accs: return choice(accs) -        return None -      @lock -    def getAllAccounts(self, refresh=False): +    def getAllAccounts(self, uid):          """ Return account info, refresh afterwards if needed          :param refresh:          :return:          """ -        if refresh: -            self.core.scheduler.addJob(0, self.core.accountManager.getAllAccounts) - -        # load unavailable account info -        for p_dict in self.accounts.itervalues(): -            for acc in p_dict.itervalues(): -                acc.getAccountInfo() +        # filter by owner / shared, but admins see all accounts +        accounts = [] +        for plugin, accs in self.accounts.iteritems(): +            accounts.extend([acc for acc in accs if acc.shared or not uid or acc.owner == uid]) -        return self.accounts +        return accounts      def refreshAllAccounts(self):          """ Force a refresh of every account """          for p in self.accounts.itervalues(): -            for acc in p.itervalues(): +            for acc in p:                  acc.getAccountInfo(True)      def sendChange(self, plugin, name): diff --git a/pyload/PluginManager.py b/pyload/PluginManager.py index 182768689..6886903cc 100644 --- a/pyload/PluginManager.py +++ b/pyload/PluginManager.py @@ -199,8 +199,8 @@ class PluginManager:          # create plugin tuple          plugin = PluginTuple(version, plugin_re, deps, category, bool(home), filename) -        # internals have no config -        if folder == "internal": +        # These have none or their own config +        if folder in ("internal", "accounts", "network"):              return plugin          if folder == "addons" and "config" not in attrs and not attrs["internal"]: diff --git a/pyload/api/AccountApi.py b/pyload/api/AccountApi.py index 999484974..144074d3c 100644 --- a/pyload/api/AccountApi.py +++ b/pyload/api/AccountApi.py @@ -1,44 +1,60 @@  #!/usr/bin/env python  # -*- coding: utf-8 -*- -from pyload.Api import Api, RequirePerm, Permission - +from pyload.Api import Api, RequirePerm, Permission, Conflict  from ApiComponent import ApiComponent  class AccountApi(ApiComponent):      """ All methods to control accounts """ +    @RequirePerm(Permission.All) +    def getAccountTypes(self): +        """All available account types. + +        :return: string list +        """ +        return self.core.pluginManager.getPlugins("accounts").keys() +      @RequirePerm(Permission.Accounts) -    def getAccounts(self, refresh): +    def getAccounts(self):          """Get information about all entered accounts. -        :param refresh: reload account info          :return: list of `AccountInfo`          """ -        accs = self.core.accountManager.getAllAccounts(refresh) -        accounts = [] -        for plugin in accs.itervalues(): -            accounts.extend([acc.toInfoData() for acc in plugin.values()]) +        accounts = self.core.accountManager.getAllAccounts(self.primaryUID) +        return [acc.toInfoData() for acc in accounts] -        return accounts - -    @RequirePerm(Permission.All) -    def getAccountTypes(self): -        """All available account types. +    @RequirePerm(Permission.Accounts) +    def getAccountInfo(self, plugin, loginname, refresh=False): +        """ Returns :class:`AccountInfo` for a specific account -        :return: string list +            :param refresh: reload account info          """ -        return self.core.pluginManager.getPlugins("accounts").keys() +        account = self.core.accountManager.getAccount(plugin, loginname) + +        # Admins can see and refresh accounts +        if not account or (self.primaryUID and self.primaryUID != account.owner): +            return None + +        if refresh: +            # reload account in place +            account.getAccountInfo(True) + +        return account.toInfoData()      @RequirePerm(Permission.Accounts) -    def updateAccount(self, plugin, login, password): -        """Changes pw/options for specific account.""" -        # TODO: options -        self.core.accountManager.updateAccount(plugin, login, password, {}) +    def updateAccount(self, plugin, loginname, password): +        """Creates an account if not existent or updates the password + +        :return: newly created or updated account info +        """ +        return self.core.accountManager.updateAccount(plugin, loginname, password, self.user).toInfoData() + +    @RequirePerm(Permission.Accounts)      def updateAccountInfo(self, account): -        """ Update account from :class:`AccountInfo` """ +        """ Update account settings from :class:`AccountInfo` """          #TODO      @RequirePerm(Permission.Accounts) @@ -47,7 +63,7 @@ class AccountApi(ApiComponent):          :param account: :class:`ÀccountInfo` instance          """ -        self.core.accountManager.removeAccount(account.plugin, account.loginname) +        self.core.accountManager.removeAccount(account.plugin, account.loginname, self.primaryUID)  if Api.extend(AccountApi): diff --git a/pyload/config/ConfigParser.py b/pyload/config/ConfigParser.py index bda3f7bd4..0f96fd8b9 100644 --- a/pyload/config/ConfigParser.py +++ b/pyload/config/ConfigParser.py @@ -1,21 +1,19 @@  # -*- coding: utf-8 -*-  from __future__ import with_statement -from time import sleep  from os.path import exists  from gettext import gettext  from new_collections import namedtuple, OrderedDict -  from pyload.Api import Input, InputType  from pyload.utils.fs import chmod  from default import make_config -from convert import to_input, from_string +from convert import to_configdata, from_string  CONF_VERSION = 2  SectionTuple = namedtuple("SectionTuple", "label description explanation config") -ConfigData = namedtuple("ConfigData", "label description input") +  class ConfigParser:      """ @@ -109,8 +107,10 @@ class ConfigParser:              for option, data in data.config.iteritems():                  value = self.get(section, option) -                if type(value) == unicode: value = value.encode("utf8") -                else: value = str(value) +                if type(value) == unicode: +                    value = value.encode("utf8") +                else: +                    value = str(value)                  f.write('%s = %s\n' % (option, value)) @@ -165,35 +165,19 @@ class ConfigParser:          return self.config[section], self.values[section] if section in self.values else {}      def addConfigSection(self, section, label, desc, expl, config): -        """Adds a section to the config. `config` is a list of config tuples as used in plugin api defined as: +        """Adds a section to the config. `config` is a list of config tuple as used in plugin api defined as:          The order of the config elements is preserved with OrderedDict          """          d = OrderedDict()          for entry in config: -            if len(entry) != 4: -                raise ValueError("Config entry must be of length 4") - -            # Values can have different roles depending on the two config formats -            conf_name, type_label, label_desc, default_input = entry - -            # name, label, desc, input -            if isinstance(default_input, Input): -                input = default_input -                conf_label = type_label -                conf_desc = label_desc -            # name, type, label, default -            else: -                input = Input(to_input(type_label)) -                input.default_value = from_string(default_input, input.type) -                conf_label = label_desc -                conf_desc = "" - -            d[conf_name] = ConfigData(gettext(conf_label), gettext(conf_desc), input) +            name, data = to_configdata(entry) +            d[name] = data          data = SectionTuple(gettext(label), gettext(desc), gettext(expl), d)          self.config[section] = data +  class Section:      """provides dictionary like access for configparser""" diff --git a/pyload/config/convert.py b/pyload/config/convert.py index 7a110e0f3..59f814020 100644 --- a/pyload/config/convert.py +++ b/pyload/config/convert.py @@ -1,7 +1,14 @@ +# -*- coding: utf-8 -*- + +from gettext import gettext + +from new_collections import namedtuple  from pyload.Api import Input, InputType  from pyload.utils import decode, to_bool +ConfigData = namedtuple("ConfigData", "label description input") +  # Maps old config formats to new values  input_dict = {      "int": InputType.Int, @@ -18,6 +25,28 @@ def to_input(typ):      return input_dict.get(typ, InputType.Text) +def to_configdata(entry): +    if len(entry) != 4: +        raise ValueError("Config entry must be of length 4") + +    # Values can have different roles depending on the two config formats +    conf_name, type_label, label_desc, default_input = entry + +    # name, label, desc, input +    if isinstance(default_input, Input): +        _input = default_input +        conf_label = type_label +        conf_desc = label_desc +    # name, type, label, default +    else: +        _input = Input(to_input(type_label)) +        _input.default_value = from_string(default_input, _input.type) +        conf_label = label_desc +        conf_desc = "" + +    return conf_name, ConfigData(gettext(conf_label), gettext(conf_desc), _input) + +  def from_string(value, typ=None):      """ cast value to given type, unicode for strings """ diff --git a/pyload/database/AccountDatabase.py b/pyload/database/AccountDatabase.py index eaa1a3203..3ca841fbc 100644 --- a/pyload/database/AccountDatabase.py +++ b/pyload/database/AccountDatabase.py @@ -1,25 +1,29 @@  # -*- coding: utf-8 -*- -from pyload.database import queue, async -from pyload.database import DatabaseBackend +from pyload.Api import AccountInfo +from pyload.database import DatabaseMethods, queue, async -class AccountMethods: +class AccountMethods(DatabaseMethods):      @queue -    def loadAccounts(db): -        db.c.execute('SELECT plugin, loginname, activated, password, options FROM accounts;') -        return db.c.fetchall() +    def loadAccounts(self): +        self.c.execute('SELECT plugin, loginname, owner, activated, shared, password, options FROM accounts') + +        return [(AccountInfo(r[0], r[1], r[2], activated=r[3] is 1, shared=r[4] is 1), r[5], r[6]) for r in self.c]      @async -    def saveAccounts(db, data): -        # TODO: owner, shared +    def saveAccounts(self, data): -        db.c.executemany( -            'INSERT INTO accounts(plugin, loginname, activated, password, options) VALUES(?,?,?,?,?)', data) +        self.c.executemany( +            'INSERT INTO accounts(plugin, loginname, owner, activated, shared, password, options) VALUES(?,?,?,?,?,?,?)', +            data)      @async -    def removeAccount(db, plugin, loginname): -        db.c.execute('DELETE FROM accounts WHERE plugin=? AND loginname=?', (plugin, loginname)) +    def removeAccount(self, plugin, loginname): +        self.c.execute('DELETE FROM accounts WHERE plugin=? AND loginname=?', (plugin, loginname)) +    @queue +    def purgeAccounts(self): +        self.c.execute('DELETE FROM accounts') -DatabaseBackend.registerSub(AccountMethods)
\ No newline at end of file +AccountMethods.register()
\ No newline at end of file diff --git a/pyload/database/DatabaseBackend.py b/pyload/database/DatabaseBackend.py index 2244a3026..df8c6e704 100644 --- a/pyload/database/DatabaseBackend.py +++ b/pyload/database/DatabaseBackend.py @@ -369,9 +369,9 @@ class DatabaseBackend(Thread):              '"plugin" TEXT NOT NULL, '              '"loginname" TEXT NOT NULL, '              '"owner" INTEGER NOT NULL DEFAULT -1, ' -            '"activated" INTEGER DEFAULT 1, ' +            '"activated" INTEGER NOT NULL DEFAULT 1, '              '"password" TEXT DEFAULT "", ' -            '"shared" INTEGER DEFAULT 0, ' +            '"shared" INTEGER NOT NULL DEFAULT 0, '              '"options" TEXT DEFAULT "", '              'FOREIGN KEY(owner) REFERENCES users(uid), '              'PRIMARY KEY (plugin, loginname, owner) ON CONFLICT REPLACE' diff --git a/pyload/datatypes/User.py b/pyload/datatypes/User.py index 31c9a55cc..645fd0983 100644 --- a/pyload/datatypes/User.py +++ b/pyload/datatypes/User.py @@ -60,4 +60,9 @@ class User(UserData):          Secondary user account share id with primary user. Only Admins have no primary id. """          if self.hasRole(Role.Admin):              return None +        return self.true_primary + +    @property +    def true_primary(self): +        """ Primary handle that does not distinguish admin accounts  """          return self.user if self.user else self.uid
\ No newline at end of file diff --git a/pyload/plugins/Account.py b/pyload/plugins/Account.py index 4492dfa18..b3e26ce58 100644 --- a/pyload/plugins/Account.py +++ b/pyload/plugins/Account.py @@ -1,12 +1,12 @@  # -*- coding: utf-8 -*-  from time import time -from traceback import print_exc  from threading import RLock -from pyload.utils import compare_time, format_size, parseFileSize, lock, to_bool -from pyload.Api import AccountInfo +from pyload.Api import AccountInfo, ConfigItem  from pyload.network.CookieJar import CookieJar +from pyload.config.convert import from_string, to_configdata +from pyload.utils import to_string, compare_time, format_size, parseFileSize, lock  from Base import Base @@ -29,40 +29,30 @@ class Account(Base):      UNLIMITED = -2      # Default values -    owner = None      valid = True      validuntil = -1      trafficleft = -1      maxtraffic = -1      premium = True -    activated = True -    shared = False      #: after that time [in minutes] pyload will relogin the account      login_timeout = 600      #: account data will be reloaded after this time      info_threshold = 600 -    # known options -    known_opt = ("time", "limitDL") +    @classmethod +    def fromInfoData(cls, m, info, password, options): +        return cls(m, info.loginname, info.owner, +                   True if info.activated else False, True if info.shared else False, password, options) -    def __init__(self, manager, loginname, password, options): -        Base.__init__(self, manager.core) - -        if "activated" in options: -            self.activated = to_bool(options["activated"]) -        else: -            self.activated = Account.activated - -        for opt in self.known_opt: -            if opt not in options: -                options[opt] = "" - -        for opt in options.keys(): -            if opt not in self.known_opt: -                del options[opt] +    def __init__(self, manager, loginname, owner, activated, shared, password, options): +        Base.__init__(self, manager.core, owner)          self.loginname = loginname +        self.owner = owner +        self.activated = activated +        self.shared = shared +        self.password = password          self.options = options          self.manager = manager @@ -71,25 +61,58 @@ class Account(Base):          self.timestamp = 0          self.login_ts = 0 # timestamp for login          self.cj = CookieJar() -        self.password = password          self.error = None +        try: +            self.config_data = dict(to_configdata(x) for x in self.__config__) +        except Exception, e: +            self.logError("Invalid config: %s" % e) +            self.config_data = {} +          self.init()      def toInfoData(self): -        return AccountInfo(self.__name__, self.loginname, self.owner, self.valid, self.validuntil, self.trafficleft, -                           self.maxtraffic, -                           self.premium, self.activated, self.shared, self.options) +        info = AccountInfo(self.__name__, self.loginname, self.owner, self.valid, self.validuntil, self.trafficleft, +                           self.maxtraffic, self.premium, self.activated, self.shared, self.options) + +        info.config = [ConfigItem(name, item.label, item.description, item.input, +                                  to_string(self.getConfig(name))) for name, item in +                       self.config_data.iteritems()] +        return info      def init(self):          pass +    def getConfig(self, option): +        """ Gets an option that was configured via the account options dialog and +        is only valid for this specific instance.""" +        if option not in self.config_data: +            return Base.getConfig(self, option) + +        if option in self.options: +            return self.options[option] + +        return self.config_data[option].input.default_value + +    def setConfig(self, option, value): +        """ Sets a config value for this account instance. Fallsback """ +        if option not in self.config_data: +            return Base.setConfig(self, option, value) + +        value = from_string(value, self.config_data[option].input.type) +        # given value is the default value and does not need to be saved at all +        if value == self.config_data[option].input.default_value: +            if option in self.options: +                del self.options[option] +        else: +            self.options[option] = from_string(value, self.config_data[option].input.type) +      def login(self, req):          """login into account, the cookies will be saved so the user can be recognized          :param req: `Request` instance          """ -        raise NotImplemented +        raise NotImplementedError      def relogin(self):          """ Force a login. """ @@ -123,8 +146,7 @@ class Account(Base):                  _("Could not login with account %(user)s | %(msg)s") % {"user": self.loginname                      , "msg": e})              self.valid = False -            if self.core.debug: -                print_exc() +            self.core.print_exc()          return self.valid @@ -134,28 +156,24 @@ class Account(Base):          self.maxtraffic = Account.maxtraffic          self.premium = Account.premium -    def update(self, password=None, options=None): -        """ updates the account and returns true if anything changed """ - -        self.login_ts = 0 -        self.valid = True #set valid, so the login will be retried +    def setPassword(self, password): +        """ updates the password and returns true if anything changed """ -        if "activated" in options: -            self.activated = True if options["activated"] == "True" else False +        if password != self.password: +            self.login_ts = 0 +            self.valid = True #set valid, so the login will be retried -        if password:              self.password = password -            self.relogin()              return True -        if options: -            # remove unknown options -            for opt in options.keys(): -                if opt not in self.known_opt: -                    del options[opt] -            before = self.options -            self.options.update(options) -            return self.options != before +        return False + +    def updateConfig(self, items): +        """  Updates the accounts options from config items """ +        for item in items: +            # Check if a valid option +            if item.name in self.config_data: +                self.setConfig(item.name, item.value)      def getAccountRequest(self):          return self.core.requestFactory.getRequest(self.cj) @@ -163,7 +181,7 @@ class Account(Base):      def getDownloadSettings(self):          """ Can be overwritten to change download settings. Default is no chunkLimit, max dl limit, resumeDownload -        :return: (chunkLimit, limitDL, resumeDownload) / (int, int ,bool) +        :return: (chunkLimit, limitDL, resumeDownload) / (int, int, bool)          """          return -1, 0, True @@ -229,9 +247,11 @@ class Account(Base):      def isUsable(self):          """Check several constraints to determine if account should be used""" +          if not self.valid or not self.activated: return False -        if self.options["time"]: +        # TODO: not in ui currently +        if "time" in self.options and self.options["time"]:              time_data = ""              try:                  time_data = self.options["time"] diff --git a/pyload/plugins/Base.py b/pyload/plugins/Base.py index cd4831d82..3ca8abdd0 100644 --- a/pyload/plugins/Base.py +++ b/pyload/plugins/Base.py @@ -92,7 +92,7 @@ class Base(object):          self.evm = core.eventManager          #: :class:`InteractionManager`          self.im = core.interactionManager -        if user: +        if user is not None:              #: :class:`Api`, user api when user is set              self.api = self.core.api.withUserContext(user)              if not self.api: diff --git a/pyload/plugins/Hoster.py b/pyload/plugins/Hoster.py index 44b10899d..b3be7a9e9 100644 --- a/pyload/plugins/Hoster.py +++ b/pyload/plugins/Hoster.py @@ -82,17 +82,13 @@ class Hoster(Base):          self.ocr = None  #captcha reader instance          #: account handler instance, see :py:class:`Account` -        self.account = self.core.accountManager.getAccountForPlugin(self.__name__) +        self.account = self.core.accountManager.selectAccount(self.__name__, self.user)          #: premium status          self.premium = False -        #: username/login -        self.user = None -        if self.account and not self.account.isUsable(): self.account = None          if self.account: -            self.user = self.account.loginname -            #: Browser instance, see `network.Browser` +            #: Request instance bound to account              self.req = self.account.getAccountRequest()              # Default:  -1, True, True              self.chunkLimit, self.limitDL, self.resumeDownload = self.account.getDownloadSettings() diff --git a/pyload/plugins/ReCaptcha.py b/pyload/plugins/ReCaptcha.py deleted file mode 100644 index e47522b4a..000000000 --- a/pyload/plugins/ReCaptcha.py +++ /dev/null @@ -1,22 +0,0 @@ -import re - -class ReCaptcha(): -    def __init__(self, plugin): -        self.plugin = plugin -        self.plugin.logDebug("Deprecated usage of ReCaptcha: Use CaptchaService instead") -     -    def challenge(self, id): -        js = self.plugin.req.load("http://www.google.com/recaptcha/api/challenge", get={"k":id}, cookies=True) -         -        try: -            challenge = re.search("challenge : '(.*?)',", js).group(1) -            server = re.search("server : '(.*?)',", js).group(1) -        except: -            self.plugin.fail("recaptcha error") -        result = self.result(server,challenge) -         -        return challenge, result - -    def result(self, server, challenge): -        return self.plugin.decryptCaptcha("%simage"%server, get={"c":challenge}, cookies=True, imgtype="jpg") -         diff --git a/pyload/plugins/accounts/Http.py b/pyload/plugins/accounts/Http.py index 5701d1f03..de9490b2c 100644 --- a/pyload/plugins/accounts/Http.py +++ b/pyload/plugins/accounts/Http.py @@ -1,6 +1,6 @@  # -*- coding: utf-8 -*- -from module.plugins.Account import Account +from pyload.plugins.Account import Account  class Http(Account): @@ -11,4 +11,9 @@ class Http(Account):      __author_name__ = ("zoidberg")      __author_mail__ = ("zoidberg@mujmail.cz") +    __config__ = [("domain", "str", "Domain", "")] +      login_timeout = info_threshold = 1000000 + +    def login(self, req): +        pass
\ No newline at end of file diff --git a/pyload/plugins/addons/MultiHoster.py b/pyload/plugins/addons/MultiHoster.py index 329a87e4a..446dfe922 100644 --- a/pyload/plugins/addons/MultiHoster.py +++ b/pyload/plugins/addons/MultiHoster.py @@ -72,18 +72,18 @@ class MultiHoster(Addon):      @AddEventListener("account:deleted") -    def refreshAccounts(self, plugin=None, user=None): +    def refreshAccounts(self, plugin=None, loginname=None):          self.logDebug("Re-checking accounts")          self.plugins = {} -        for name, account in self.core.accountManager.iterAccounts(): +        for plugin, account in self.core.accountManager.iterAccounts():              if isinstance(account, MultiHosterAccount) and account.isUsable():                  self.addHoster(account)      @AddEventListener("account:updated") -    def refreshAccount(self, plugin, user): +    def refreshAccount(self, plugin, loginname): -        account = self.core.accountManager.getAccount(plugin, user) +        account = self.core.accountManager.getAccount(plugin, loginname)          if isinstance(account, MultiHosterAccount) and account.isUsable():              self.addHoster(account) diff --git a/pyload/plugins/internal/CaptchaService.py b/pyload/plugins/internal/CaptchaService.py index b912436a7..4f903e3e6 100644 --- a/pyload/plugins/internal/CaptchaService.py +++ b/pyload/plugins/internal/CaptchaService.py @@ -60,8 +60,6 @@ class AdsCaptcha(CaptchaService):          return self.plugin.decryptCaptcha("%sChallenge.aspx" % server, get={"cid": challenge, "dummy": random()}, cookies=True, imgtype="jpg")  class SolveMedia(CaptchaService): -    def __init__(self,plugin): -        self.plugin = plugin      def challenge(self, src):          html = self.plugin.req.load("http://api.solvemedia.com/papi/challenge.noscript?k=%s" % src, cookies=True) diff --git a/pyload/plugins/network/CurlRequest.py b/pyload/plugins/network/CurlRequest.py index 775c98522..b7e37900b 100644 --- a/pyload/plugins/network/CurlRequest.py +++ b/pyload/plugins/network/CurlRequest.py @@ -152,7 +152,6 @@ class CurlRequest(Request):              url = "%s?%s" % (url, get)          self.c.setopt(pycurl.URL, url) -        self.lastURL = url          if post:              self.c.setopt(pycurl.POST, 1) @@ -222,6 +221,7 @@ class CurlRequest(Request):              rep = self.getResponse()          self.c.setopt(pycurl.POSTFIELDS, "") +        self.lastURL = url          self.lastEffectiveURL = self.c.getinfo(pycurl.EFFECTIVE_URL)          self.code = self.verifyHeader() diff --git a/pyload/network/XDCCRequest.py b/pyload/plugins/network/XDCCRequest.py index 89c4f3b73..6b692ab38 100644 --- a/pyload/network/XDCCRequest.py +++ b/pyload/plugins/network/XDCCRequest.py @@ -30,7 +30,7 @@ from select import select  from pyload.plugins.Plugin import Abort - +# TODO: This must be adapted to the new request interfaces  class XDCCRequest():      def __init__(self, timeout=30, proxies={}): diff --git a/pyload/remote/apitypes.py b/pyload/remote/apitypes.py index 0d9e35963..385f4ca07 100644 --- a/pyload/remote/apitypes.py +++ b/pyload/remote/apitypes.py @@ -97,9 +97,9 @@ class Role:  	User = 1  class AccountInfo(BaseObject): -	__slots__ = ['plugin', 'loginname', 'owner', 'valid', 'validuntil', 'trafficleft', 'maxtraffic', 'premium', 'activated', 'shared', 'options'] +	__slots__ = ['plugin', 'loginname', 'owner', 'valid', 'validuntil', 'trafficleft', 'maxtraffic', 'premium', 'activated', 'shared', 'config'] -	def __init__(self, plugin=None, loginname=None, owner=None, valid=None, validuntil=None, trafficleft=None, maxtraffic=None, premium=None, activated=None, shared=None, options=None): +	def __init__(self, plugin=None, loginname=None, owner=None, valid=None, validuntil=None, trafficleft=None, maxtraffic=None, premium=None, activated=None, shared=None, config=None):  		self.plugin = plugin  		self.loginname = loginname  		self.owner = owner @@ -110,7 +110,7 @@ class AccountInfo(BaseObject):  		self.premium = premium  		self.activated = activated  		self.shared = shared -		self.options = options +		self.config = config  class AddonInfo(BaseObject):  	__slots__ = ['func_name', 'description', 'value'] @@ -161,6 +161,9 @@ class ConfigItem(BaseObject):  		self.input = input  		self.value = value +class Conflict(ExceptionObject): +	pass +  class DownloadInfo(BaseObject):  	__slots__ = ['url', 'plugin', 'hash', 'status', 'statusmsg', 'error'] @@ -415,9 +418,11 @@ class Iface(object):  		pass  	def generatePackages(self, links):  		pass +	def getAccountInfo(self, plugin, loginname, refresh): +		pass  	def getAccountTypes(self):  		pass -	def getAccounts(self, refresh): +	def getAccounts(self):  		pass  	def getAddonHandler(self):  		pass @@ -525,7 +530,7 @@ class Iface(object):  		pass  	def unpauseServer(self):  		pass -	def updateAccount(self, plugin, login, password): +	def updateAccount(self, plugin, loginname, password):  		pass  	def updateAccountInfo(self, account):  		pass diff --git a/pyload/remote/apitypes_debug.py b/pyload/remote/apitypes_debug.py index 0d04a8225..177029054 100644 --- a/pyload/remote/apitypes_debug.py +++ b/pyload/remote/apitypes_debug.py @@ -18,7 +18,7 @@ enums = [  ]  classes = { -	'AccountInfo' : [basestring, basestring, int, bool, int, int, int, bool, bool, bool, (dict, basestring, basestring)], +	'AccountInfo' : [basestring, basestring, int, bool, int, int, int, bool, bool, bool, (list, ConfigItem)],  	'AddonInfo' : [basestring, basestring, basestring],  	'AddonService' : [basestring, basestring, (list, basestring), (None, int)],  	'ConfigHolder' : [basestring, basestring, basestring, basestring, (list, ConfigItem), (None, (list, AddonInfo))], @@ -72,6 +72,7 @@ methods = {  	'generateAndAddPackages': (list, int),  	'generateDownloadLink': basestring,  	'generatePackages': (dict, basestring, list), +	'getAccountInfo': AccountInfo,  	'getAccountTypes': (list, basestring),  	'getAccounts': (list, AccountInfo),  	'getAddonHandler': (dict, basestring, list), @@ -127,7 +128,7 @@ methods = {  	'togglePause': bool,  	'toggleReconnect': bool,  	'unpauseServer': None, -	'updateAccount': None, +	'updateAccount': AccountInfo,  	'updateAccountInfo': None,  	'updatePackage': None,  	'updateUserData': None, diff --git a/pyload/remote/pyload.thrift b/pyload/remote/pyload.thrift index 9f2cfc8ee..702bd9b94 100644 --- a/pyload/remote/pyload.thrift +++ b/pyload/remote/pyload.thrift @@ -294,7 +294,7 @@ struct AccountInfo {    8: bool premium,    9: bool activated,    10: bool shared, -  11: map<string, string> options, +  11: list <ConfigItem> config,  }  struct OnlineCheck { @@ -335,6 +335,9 @@ exception Unauthorized {  exception Forbidden {  } +exception Conflict { +} +  service Pyload { @@ -489,9 +492,12 @@ service Pyload {    // Account Methods    /////////////////////// -  list<AccountInfo> getAccounts(1: bool refresh),    list<string> getAccountTypes(), -  void updateAccount(1: PluginName plugin, 2: string login, 3: string password), + +  list<AccountInfo> getAccounts(), +  AccountInfo getAccountInfo(1: PluginName plugin, 2: string loginname, 3: bool refresh), + +  AccountInfo updateAccount(1: PluginName plugin, 2: string loginname, 3: string password),    void updateAccountInfo(1: AccountInfo account),    void removeAccount(1: AccountInfo account), diff --git a/pyload/remote/wsbackend/AbstractHandler.py b/pyload/remote/wsbackend/AbstractHandler.py index 8012d6cd8..f540435c4 100644 --- a/pyload/remote/wsbackend/AbstractHandler.py +++ b/pyload/remote/wsbackend/AbstractHandler.py @@ -18,6 +18,8 @@  from mod_pywebsocket.msgutil import send_message  from mod_pywebsocket.util import get_class_logger + +from pyload.Api import User  from pyload.remote.json_converter import loads, dumps @@ -115,7 +117,16 @@ class AbstractHandler:              return tuple(o)      def do_login(self, req, args, kwargs): -        user = self.api.checkAuth(*args, **kwargs) +        user = None +        # Cookies login when one argument is given +        if len(args) == 1: +            s = self.load_session(args) +        else: +            s = self.api.checkAuth(*args, **kwargs) +            if s: +                uid = s.get('uid', None) +                user = User(uid=uid) +          if user:              req.api = self.api.withUserContext(user.uid)              return self.send_result(req, self.OK, True) diff --git a/pyload/web/app/scripts/collections/AccountList.js b/pyload/web/app/scripts/collections/AccountList.js index bfc2af5a3..f6a8eda65 100644 --- a/pyload/web/app/scripts/collections/AccountList.js +++ b/pyload/web/app/scripts/collections/AccountList.js @@ -14,8 +14,7 @@ define(['jquery', 'backbone', 'underscore', 'app', 'models/Account'], function($          },          fetch: function(options) { -            // TODO: refresh options? -            options = App.apiRequest('getAccounts/false', null, options); +            options = App.apiRequest('getAccounts', null, options);              return Backbone.Collection.prototype.fetch.call(this, options);          } diff --git a/pyload/web/app/scripts/models/Account.js b/pyload/web/app/scripts/models/Account.js index a2e24b056..9cfc1c0c1 100644 --- a/pyload/web/app/scripts/models/Account.js +++ b/pyload/web/app/scripts/models/Account.js @@ -18,7 +18,7 @@ define(['jquery', 'backbone', 'underscore', 'app', 'utils/apitypes'], function($              premium: false,              activated: false,              shared: false, -            options: null +            config: null          },          // Model Constructor diff --git a/pyload/web/app/scripts/views/accounts/accountModal.js b/pyload/web/app/scripts/views/accounts/accountModal.js index 6c2b226df..85db96b2b 100644 --- a/pyload/web/app/scripts/views/accounts/accountModal.js +++ b/pyload/web/app/scripts/views/accounts/accountModal.js @@ -60,7 +60,7 @@ define(['jquery', 'underscore', 'app', 'views/abstract/modalView', 'hbs!tpl/dial                          self = this;                      $.ajax(App.apiRequest('updateAccount', { -                        plugin: plugin, login: login, password: password +                        plugin: plugin, loginname: login, password: password                      }, { success: function() {                          App.vent.trigger('accounts:updated');                          self.hide(); diff --git a/pyload/web/app/scripts/views/headerView.js b/pyload/web/app/scripts/views/headerView.js index e6e763b26..3fdfe32ba 100644 --- a/pyload/web/app/scripts/views/headerView.js +++ b/pyload/web/app/scripts/views/headerView.js @@ -67,6 +67,9 @@ define(['jquery', 'underscore', 'backbone', 'app', 'models/ServerStatus', 'colle                      console.log(error);                      alert('WebSocket error' + error);                  }; +                ws.onclose = function() { +                    alert('WebSocket was closed'); +                };                  this.ws = ws;              }, | 
