-
-
Notifications
You must be signed in to change notification settings - Fork 702
module.plugins.Account (SourceCode)
Jonas edited this page Jun 8, 2016
·
1 revision
# -*- coding: utf-8 -*-
"""
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License,
or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
@author: mkaay
"""
from random import choice
from time import time
from traceback import print_exc
from threading import RLock
from Plugin import Base
from module.utils import compare_time, parseFileSize, lock
class WrongPassword(Exception):
pass
class Account(Base):
"""
Base class for every Account plugin.
Just overwrite `login` and cookies will be stored and account becomes accessible in\
associated hoster plugin. Plugin should also provide `loadAccountInfo`
"""
__name__ = "Account"
__version__ = "0.2"
__type__ = "account"
__description__ = """Account Plugin"""
__author_name__ = ("mkaay")
__author_mail__ = ("[email protected]")
#: after that time [in minutes] pyload will relogin the account
login_timeout = 600
#: account data will be reloaded after this time
info_threshold = 600
def __init__(self, manager, accounts):
Base.__init__(self, manager.core)
self.manager = manager
self.accounts = {}
self.infos = {} # cache for account information
self.lock = RLock()
self.timestamps = {}
self.setAccounts(accounts)
self.init()
def init(self):
pass
def login(self, user, data, req):
"""login into account, the cookies will be saved so user can be recognized
:param user: loginname
:param data: data dictionary
:param req: `Request` instance
"""
pass
@lock
def _login(self, user, data):
# set timestamp for login
self.timestamps[user] = time()
req = self.getAccountRequest(user)
try:
self.login(user, data, req)
except WrongPassword:
self.logWarning(
_("Could not login with account %(user)s | %(msg)s") % {"user": user
, "msg": _("Wrong Password")})
data["valid"] = False
except Exception, e:
self.logWarning(
_("Could not login with account %(user)s | %(msg)s") % {"user": user
, "msg": e})
data["valid"] = False
if self.core.debug:
print_exc()
finally:
if req: req.close()
def relogin(self, user):
req = self.getAccountRequest(user)
if req:
req.cj.clear()
req.close()
if user in self.infos:
del self.infos[user] #delete old information
self._login(user, self.accounts[user])
def setAccounts(self, accounts):
self.accounts = accounts
for user, data in self.accounts.iteritems():
self._login(user, data)
self.infos[user] = {}
def updateAccounts(self, user, password=None, options={}):
""" updates account and return true if anything changed """
if user in self.accounts:
self.accounts[user]["valid"] = True #do not remove or accounts will not login
if password:
self.accounts[user]["password"] = password
self.relogin(user)
return True
if options:
before = self.accounts[user]["options"]
self.accounts[user]["options"].update(options)
return self.accounts[user]["options"] != before
else:
self.accounts[user] = {"password": password, "options": options, "valid": True}
self._login(user, self.accounts[user])
return True
def removeAccount(self, user):
if user in self.accounts:
del self.accounts[user]
if user in self.infos:
del self.infos[user]
if user in self.timestamps:
del self.timestamps[user]
@lock
def getAccountInfo(self, name, force=False):
"""retrieve account infos for an user, do **not** overwrite this method!\\
just use it to retrieve infos in hoster plugins. see `loadAccountInfo`
:param name: username
:param force: reloads cached account information
:return: dictionary with information
"""
data = Account.loadAccountInfo(self, name)
if force or name not in self.infos:
self.logDebug("Get Account Info for %s" % name)
req = self.getAccountRequest(name)
try:
infos = self.loadAccountInfo(name, req)
if not type(infos) == dict:
raise Exception("Wrong return format")
except Exception, e:
infos = {"error": str(e)}
if req: req.close()
self.logDebug("Account Info: %s" % str(infos))
infos["timestamp"] = time()
self.infos[name] = infos
elif "timestamp" in self.infos[name] and self.infos[name][
"timestamp"] + self.info_threshold * 60 < time():
self.logDebug("Reached timeout for account data")
self.scheduleRefresh(name)
data.update(self.infos[name])
return data
def isPremium(self, user):
info = self.getAccountInfo(user)
return info["premium"]
def loadAccountInfo(self, name, req=None):
"""this should be overwritten in account plugin,\
and retrieving account information for user
:param name:
:param req: `Request` instance
:return:
"""
return {
"validuntil": None, # -1 for unlimited
"login": name,
#"password": self.accounts[name]["password"], #@XXX: security
"options": self.accounts[name]["options"],
"valid": self.accounts[name]["valid"],
"trafficleft": None, # in kb, -1 for unlimited
"maxtraffic": None,
"premium": True, #useful for free accounts
"timestamp": 0, #time this info was retrieved
"type": self.__name__,
}
def getAllAccounts(self, force=False):
return [self.getAccountInfo(user, force) for user, data in self.accounts.iteritems()]
def getAccountRequest(self, user=None):
if not user:
user, data = self.selectAccount()
if not user:
return None
req = self.core.requestFactory.getRequest(self.__name__, user)
return req
def getAccountCookies(self, user=None):
if not user:
user, data = self.selectAccount()
if not user:
return None
cj = self.core.requestFactory.getCookieJar(self.__name__, user)
return cj
def getAccountData(self, user):
return self.accounts[user]
def selectAccount(self):
""" returns an valid account name and data"""
usable = []
for user, data in self.accounts.iteritems():
if not data["valid"]: continue
if "time" in data["options"] and data["options"]["time"]:
time_data = ""
try:
time_data = data["options"]["time"][0]
start, end = time_data.split("-")
if not compare_time(start.split(":"), end.split(":")):
continue
except:
self.logWarning(_("Your Time %s has wrong format, use: 1:22-3:44") % time_data)
if user in self.infos:
if "validuntil" in self.infos[user]:
if self.infos[user]["validuntil"] > 0 and time() > self.infos[user]["validuntil"]:
continue
if "trafficleft" in self.infos[user]:
if self.infos[user]["trafficleft"] == 0:
continue
usable.append((user, data))
if not usable: return None, None
return choice(usable)
def canUse(self):
return False if self.selectAccount() == (None, None) else True
def parseTraffic(self, string): #returns kbyte
return parseFileSize(string) / 1024
def wrongPassword(self):
raise WrongPassword
def empty(self, user):
if user in self.infos:
self.logWarning(_("Account %s has not enough traffic, checking again in 30min") % user)
self.infos[user].update({"trafficleft": 0})
self.scheduleRefresh(user, 30 * 60)
def expired(self, user):
if user in self.infos:
self.logWarning(_("Account %s is expired, checking again in 1h") % user)
self.infos[user].update({"validuntil": time() - 1})
self.scheduleRefresh(user, 60 * 60)
def scheduleRefresh(self, user, time=0, force=True):
""" add task to refresh account info to sheduler """
self.logDebug("Scheduled Account refresh for %s in %s seconds." % (user, time))
self.core.scheduler.addJob(time, self.getAccountInfo, [user, force])
@lock
def checkLogin(self, user):
""" checks if user is still logged in """
if user in self.timestamps:
if self.timestamps[user] + self.login_timeout * 60 < time():
self.logDebug("Reached login timeout for %s" % user)
self.relogin(user)
return False
return True
This page may no longer be valid. If you find something wrong, please report it.