From 6eae782f13953dd0ba2bbe1b582cf33fd4d7d90a Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Mon, 19 Dec 2011 23:10:49 +0100 Subject: configparser v2, warning CONFIG will be DELETED. --- module/Api.py | 135 ++++++++++++++++++++++++---------------------------------- 1 file changed, 56 insertions(+), 79 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index f0bf5e264..c787819e2 100644 --- a/module/Api.py +++ b/module/Api.py @@ -27,11 +27,13 @@ from utils import freeSpace, compare_time from common.packagetools import parseNames from network.RequestFactory import getURL from remote import activated +from config.converter import to_string if activated: try: from remote.thriftbackend.thriftgen.pyload.ttypes import * from remote.thriftbackend.thriftgen.pyload.Pyload import Iface + BaseObject = TBase except ImportError: print "Thrift not imported" @@ -49,7 +51,7 @@ def permission(bits): def __new__(cls, func, *args, **kwargs): permMap[func.__name__] = bits return func - + return _Dec @@ -67,10 +69,12 @@ class PERMS: ACCOUNTS = 256 # can access accounts LOGS = 512 # can see server logs + class ROLE: ADMIN = 0 #admin has all permissions implicit USER = 1 + def has_permission(userperms, perms): # bytewise or perms before if needed return perms == (userperms & perms) @@ -97,65 +101,33 @@ class Api(Iface): def _convertPyFile(self, p): f = FileData(p["id"], p["url"], p["name"], p["plugin"], p["size"], - p["format_size"], p["status"], p["statusmsg"], - p["package"], p["error"], p["order"]) + p["format_size"], p["status"], p["statusmsg"], + p["package"], p["error"], p["order"]) return f - def _convertConfigFormat(self, c): - sections = {} - for sectionName, sub in c.iteritems(): - section = ConfigSection(sectionName, sub["desc"]) - items = [] - for key, data in sub.iteritems(): - if key in ("desc", "outline"): - continue - item = ConfigItem() - item.name = key - item.description = data["desc"] - item.value = str(data["value"]) if not isinstance(data["value"], basestring) else data["value"] - item.type = data["type"] - items.append(item) - section.items = items - sections[sectionName] = section - if "outline" in sub: - section.outline = sub["outline"] - return sections - @permission(PERMS.SETTINGS) - def getConfigValue(self, category, option, section="core"): + def getConfigValue(self, section, option): """Retrieve config value. - :param category: name of category, or plugin + :param section: name of category, or plugin :param option: config option - :param section: 'plugin' or 'core' :return: config value as string """ - if section == "core": - value = self.core.config[category][option] - else: - value = self.core.config.getPlugin(category, option) - - return str(value) if not isinstance(value, basestring) else value + value = self.core.config.get(section, option) + return to_string(value) @permission(PERMS.SETTINGS) - def setConfigValue(self, category, option, value, section="core"): + def setConfigValue(self, section, option, value): """Set new config value. - :param category: + :param section: :param option: :param value: new config value - :param section: 'plugin' or 'core """ - self.core.hookManager.dispatchEvent("configChanged", category, option, value, section) + if option in ("limit_speed", "max_speed"): #not so nice to update the limit + self.core.requestFactory.updateBucket() - if section == "core": - self.core.config[category][option] = value - - if option in ("limit_speed", "max_speed"): #not so nice to update the limit - self.core.requestFactory.updateBucket() - - elif section == "plugin": - self.core.config.setPlugin(category, option, value) + self.core.config.set(section, option, value) @permission(PERMS.SETTINGS) def getConfig(self): @@ -163,14 +135,11 @@ class Api(Iface): :return: list of `ConfigSection` """ - return self._convertConfigFormat(self.core.config.config) + return [ConfigSection(section, data.name, data.description, data.long_desc, [ + ConfigItem(option, d.name, d.description, d.type, d.default, self.core.config.get(section, option)) for + option, d in data.config.iteritems()]) for + section, data in self.core.config.getBaseSectionns()] - def getConfigDict(self): - """Retrieves complete config in dict format, not for RPC. - - :return: dict - """ - return self.core.config.config @permission(PERMS.SETTINGS) def getPluginConfig(self): @@ -178,15 +147,23 @@ class Api(Iface): :return: list of `ConfigSection` """ - return self._convertConfigFormat(self.core.config.plugin) + return [ConfigSection(section, data.name, data.description, data.long_desc) for + section, data in self.core.config.getPluginSections()] - def getPluginConfigDict(self): - """Plugin config as dict, not for RPC. + def configureSection(self, section): + data = self.core.config.config[section] + sec = ConfigSection(section, data.name, data.description, data.long_desc) + sec.items = [ConfigItem(option, d.name, d.description, d.type, d.default, self.core.config.get(section, option)) + for + option, d in data.config.iteritems()] - :return: dict - """ - return self.core.config.plugin + #TODO: config handler + + return sec + def getConfigPointer(self): + """Config instance, not for RPC""" + return self.core.config @permission(PERMS.STATUS) def pauseServer(self): @@ -223,9 +200,9 @@ class Api(Iface): :return: `ServerStatus` """ serverStatus = ServerStatus(self.core.threadManager.pause, len(self.core.threadManager.processingIds()), - self.core.files.getQueueCount(), self.core.files.getFileCount(), 0, - not self.core.threadManager.pause and self.isTimeDownload(), - self.core.config['reconnect']['activated'] and self.isTimeReconnect()) + self.core.files.getQueueCount(), self.core.files.getFileCount(), 0, + not self.core.threadManager.pause and self.isTimeDownload(), + self.core.config['reconnect']['activated'] and self.isTimeReconnect()) for pyfile in [x.active for x in self.core.threadManager.threads if x.active and isinstance(x.active, PyFile)]: serverStatus.speed += pyfile.getSpeed() #bytes/s @@ -471,8 +448,8 @@ class Api(Iface): raise PackageDoesNotExists(pid) pdata = PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"], - data["queue"], data["order"], - links=[self._convertPyFile(x) for x in data["links"].itervalues()]) + data["queue"], data["order"], + links=[self._convertPyFile(x) for x in data["links"].itervalues()]) return pdata @@ -484,13 +461,13 @@ class Api(Iface): :return: `PackageData` with .fid attribute """ data = self.core.files.getPackageData(int(pid)) - + if not data: raise PackageDoesNotExists(pid) pdata = PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"], - data["queue"], data["order"], - fids=[int(x) for x in data["links"]]) + data["queue"], data["order"], + fids=[int(x) for x in data["links"]]) return pdata @@ -538,9 +515,9 @@ class Api(Iface): :return: list of `PackageInfo` """ return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], - pack["password"], pack["queue"], pack["order"], - pack["linksdone"], pack["sizedone"], pack["sizetotal"], - pack["linkstotal"]) + pack["password"], pack["queue"], pack["order"], + pack["linksdone"], pack["sizedone"], pack["sizetotal"], + pack["linkstotal"]) for pack in self.core.files.getInfoData(Destination.Queue).itervalues()] @permission(PERMS.LIST) @@ -551,9 +528,9 @@ class Api(Iface): :return: list of `PackageData` """ return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], - pack["password"], pack["queue"], pack["order"], - pack["linksdone"], pack["sizedone"], pack["sizetotal"], - links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) + pack["password"], pack["queue"], pack["order"], + pack["linksdone"], pack["sizedone"], pack["sizetotal"], + links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) for pack in self.core.files.getCompleteData(Destination.Queue).itervalues()] @permission(PERMS.LIST) @@ -563,9 +540,9 @@ class Api(Iface): :return: list of `PackageInfo` """ return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], - pack["password"], pack["queue"], pack["order"], - pack["linksdone"], pack["sizedone"], pack["sizetotal"], - pack["linkstotal"]) + pack["password"], pack["queue"], pack["order"], + pack["linksdone"], pack["sizedone"], pack["sizetotal"], + pack["linkstotal"]) for pack in self.core.files.getInfoData(Destination.Collector).itervalues()] @permission(PERMS.LIST) @@ -575,9 +552,9 @@ class Api(Iface): :return: list of `PackageInfo` """ return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], - pack["password"], pack["queue"], pack["order"], - pack["linksdone"], pack["sizedone"], pack["sizetotal"], - links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) + pack["password"], pack["queue"], pack["order"], + pack["linksdone"], pack["sizedone"], pack["sizetotal"], + links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) for pack in self.core.files.getCompleteData(Destination.Collector).itervalues()] @@ -876,7 +853,7 @@ class Api(Iface): accounts = [] for group in accs.values(): accounts.extend([AccountInfo(acc["validuntil"], acc["login"], acc["options"], acc["valid"], - acc["trafficleft"], acc["maxtraffic"], acc["premium"], acc["type"]) + acc["trafficleft"], acc["maxtraffic"], acc["premium"], acc["type"]) for acc in group]) return accounts @@ -946,7 +923,7 @@ class Api(Iface): @permission(PERMS.ALL) def getUserData(self, username, password): """similar to `checkAuth` but returns UserData thrift type """ - user = self.checkAuth(username, password) + user = self.checkAuth(username, password) if user: return UserData(user["name"], user["email"], user["role"], user["permission"], user["template"]) else: -- cgit v1.2.3 From 958bf611f5d9d117f19f824990ec6fd6b537e967 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Thu, 22 Dec 2011 23:45:38 +0100 Subject: accountmanager v2, delete your accounts.conf and re-enter them in pyload, new nice debug functions, try core.shell() and core.breakpoint() --- module/Api.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index c787819e2..f6375f266 100644 --- a/module/Api.py +++ b/module/Api.py @@ -849,12 +849,11 @@ class Api(Iface): :param refresh: reload account info :return: list of `AccountInfo` """ - accs = self.core.accountManager.getAccountInfos(False, refresh) + accs = self.core.accountManager.getAllAccounts(refresh) accounts = [] - for group in accs.values(): - accounts.extend([AccountInfo(acc["validuntil"], acc["login"], acc["options"], acc["valid"], - acc["trafficleft"], acc["maxtraffic"], acc["premium"], acc["type"]) - for acc in group]) + for plugin in accs.itervalues(): + accounts.extend(plugin.values()) + return accounts @permission(PERMS.ALL) @@ -863,7 +862,7 @@ class Api(Iface): :return: list """ - return self.core.accountManager.accounts.keys() + return self.core.pluginManager.getAccountPlugins() @permission(PERMS.ACCOUNTS) def updateAccount(self, plugin, account, password=None, options={}): -- cgit v1.2.3 From f852c362fc6283246a9f9e690c456dd3fd245c29 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Fri, 23 Dec 2011 12:15:56 +0100 Subject: closed #473 --- module/Api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index f6375f266..99fb4c1e7 100644 --- a/module/Api.py +++ b/module/Api.py @@ -862,7 +862,7 @@ class Api(Iface): :return: list """ - return self.core.pluginManager.getAccountPlugins() + return self.core.pluginManager.getPlugins("account").keys() @permission(PERMS.ACCOUNTS) def updateAccount(self, plugin, account, password=None, options={}): -- cgit v1.2.3 From d35c003cc53d4723d1dfe0d81eeb9bea78cee594 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sat, 31 Dec 2011 16:01:24 +0100 Subject: new crypter plugin API, now decrypting possible for now. --- module/Api.py | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 99fb4c1e7..deac1a19f 100644 --- a/module/Api.py +++ b/module/Api.py @@ -285,12 +285,13 @@ class Api(Iface): return data @permission(PERMS.ADD) - def addPackage(self, name, links, dest=Destination.Queue): + def addPackage(self, name, links, dest=Destination.Queue, password=""): """Adds a package, with links to desired destination. :param name: name of the new package :param links: list of urls :param dest: `Destination` + :param password: password as string, can be empty :return: package id of the new package """ if self.core.config['general']['folder_per_package']: @@ -300,15 +301,28 @@ class Api(Iface): folder = folder.replace("http://", "").replace(":", "").replace("/", "_").replace("\\", "_") - pid = self.core.files.addPackage(name, folder, dest) + self.core.log.info(_("Added package %(name)s containing %(count)d links") % {"name": name, "count": len(links)}) + pid = self.core.files.addPackage(name, folder, dest, password) + self.addFiles(pid, links) - self.core.files.addLinks(links, pid) + return pid - self.core.log.info(_("Added package %(name)s containing %(count)d links") % {"name": name, "count": len(links)}) + @permission(PERMS.ADD) + def addFiles(self, pid, links): + """Adds files to specific package. - self.core.files.save() + :param pid: package id + :param links: list of urls + """ + hoster, crypter = self.core.pluginManager.parseUrls(links) - return pid + self.core.files.addLinks(hoster, pid) + + self.core.threadManager.createInfoThread(hoster, pid) + self.core.threadManager.createDecryptThread(crypter, pid) + + self.core.log.info(_("Added %(count)d links to package #%(package)d ") % {"count": len(links), "package": pid}) + self.core.files.save() @permission(PERMS.ADD) def parseURLs(self, html=None, url=None): @@ -337,7 +351,7 @@ class Api(Iface): :param urls: :return: {plugin: urls} """ - data = self.core.pluginManager.parseUrls(urls) + data, crypter = self.core.pluginManager.parseUrls(urls) plugins = {} for url, plugin in data: @@ -355,7 +369,7 @@ class Api(Iface): :param urls: :return: initial set of data as `OnlineCheck` instance containing the result id """ - data = self.core.pluginManager.parseUrls(urls) + data, crypter = self.core.pluginManager.parseUrls(urls) rid = self.core.threadManager.createResultThread(data, False) @@ -431,7 +445,7 @@ class Api(Iface): :param dest: `Destination` :return: None """ - data = self.core.pluginManager.parseUrls(links) + data, crypter = self.core.pluginManager.parseUrls(links) self.core.threadManager.createResultThread(data, True) @@ -557,19 +571,6 @@ class Api(Iface): links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) for pack in self.core.files.getCompleteData(Destination.Collector).itervalues()] - - @permission(PERMS.ADD) - def addFiles(self, pid, links): - """Adds files to specific package. - - :param pid: package id - :param links: list of urls - """ - self.core.files.addLinks(links, int(pid)) - - self.core.log.info(_("Added %(count)d links to package #%(package)d ") % {"count": len(links), "package": pid}) - self.core.files.save() - @permission(PERMS.MODIFY) def pushToQueue(self, pid): """Moves package from Collector to Queue. @@ -925,8 +926,8 @@ class Api(Iface): user = self.checkAuth(username, password) if user: return UserData(user["name"], user["email"], user["role"], user["permission"], user["template"]) - else: - return UserData() + + raise UserDoesNotExists(username) def getAllUserData(self): @@ -972,13 +973,12 @@ class Api(Iface): plugin = info.plugin func = info.func args = info.arguments - parse = info.parseArguments if not self.hasService(plugin, func): raise ServiceDoesNotExists(plugin, func) try: - ret = self.core.hookManager.callRPC(plugin, func, args, parse) + ret = self.core.hookManager.callRPC(plugin, func, args) return str(ret) except Exception, e: raise ServiceException(e.message) -- cgit v1.2.3 From 35742c2cb023ac49ab3056752d2040cdb030cc2b Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sun, 1 Jan 2012 13:36:59 +0100 Subject: Happy new Year ! --- module/Api.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index deac1a19f..810613b66 100644 --- a/module/Api.py +++ b/module/Api.py @@ -17,10 +17,12 @@ @author: RaNaN """ +import re from base64 import standard_b64encode from os.path import join from time import time -import re +from itertools import chain + from PyFile import PyFile from utils import freeSpace, compare_time @@ -321,7 +323,7 @@ class Api(Iface): self.core.threadManager.createInfoThread(hoster, pid) self.core.threadManager.createDecryptThread(crypter, pid) - self.core.log.info(_("Added %(count)d links to package #%(package)d ") % {"count": len(links), "package": pid}) + self.core.log.debug("Added %d links to package #%d " % (len(hoster), pid)) self.core.files.save() @permission(PERMS.ADD) @@ -354,7 +356,7 @@ class Api(Iface): data, crypter = self.core.pluginManager.parseUrls(urls) plugins = {} - for url, plugin in data: + for url, plugin in chain(data, crypter): if plugin in plugins: plugins[plugin].append(url) else: @@ -364,15 +366,14 @@ class Api(Iface): @permission(PERMS.ADD) def checkOnlineStatus(self, urls): - """ initiates online status check + """ initiates online status check, will also decrypt files. :param urls: :return: initial set of data as `OnlineCheck` instance containing the result id """ data, crypter = self.core.pluginManager.parseUrls(urls) - rid = self.core.threadManager.createResultThread(data, False) - + # initial result does not contain the crypter links tmp = [(url, (url, OnlineStatus(url, pluginname, "unknown", 3, 0))) for url, pluginname in data] data = parseNames(tmp) result = {} @@ -382,6 +383,9 @@ class Api(Iface): status.packagename = k result[url] = status + data.update(crypter) # hoster and crypter will be processed + rid = self.core.threadManager.createResultThread(data, False) + return OnlineCheck(rid, result) @permission(PERMS.ADD) @@ -396,8 +400,8 @@ class Api(Iface): th = open(join(self.core.config["general"]["download_folder"], "tmp_" + container), "wb") th.write(str(data)) th.close() - - return self.checkOnlineStatus(urls + [th.name]) + urls.append(th.name) + return self.checkOnlineStatus(urls) @permission(PERMS.ADD) def pollResults(self, rid): @@ -436,18 +440,6 @@ class Api(Iface): return [self.addPackage(name, urls, dest) for name, urls in self.generatePackages(links).iteritems()] - @permission(PERMS.ADD) - def checkAndAddPackages(self, links, dest=Destination.Queue): - """Checks online status, retrieves names, and will add packages.\ - Because of this packages are not added immediatly, only for internal use. - - :param links: list of urls - :param dest: `Destination` - :return: None - """ - data, crypter = self.core.pluginManager.parseUrls(links) - self.core.threadManager.createResultThread(data, True) - @permission(PERMS.LIST) def getPackageData(self, pid): @@ -677,7 +669,7 @@ class Api(Iface): th.write(str(data)) th.close() - self.addPackage(th.name, [th.name], Destination.Queue) + self.addPackage(th.name, [th.name]) @permission(PERMS.MODIFY) def orderPackage(self, pid, position): -- cgit v1.2.3 From 5a3e5a8228e4c5421b44d18c9c9ae2f1fe616400 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sun, 1 Jan 2012 18:01:25 +0100 Subject: fixed imports --- module/Api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 810613b66..e6bd10762 100644 --- a/module/Api.py +++ b/module/Api.py @@ -318,7 +318,8 @@ class Api(Iface): """ hoster, crypter = self.core.pluginManager.parseUrls(links) - self.core.files.addLinks(hoster, pid) + if hoster: + self.core.files.addLinks(hoster, pid) self.core.threadManager.createInfoThread(hoster, pid) self.core.threadManager.createDecryptThread(crypter, pid) @@ -998,4 +999,4 @@ class Api(Iface): def setUserPermission(self, user, permission, role): self.core.db.setPermission(user, permission) - self.core.db.setRole(user, role) \ No newline at end of file + self.core.db.setRole(user, role) -- cgit v1.2.3 From 593455fc8beca8c722bddb19e38d4a98e5dbf787 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Tue, 3 Jan 2012 19:23:34 +0100 Subject: closed #483 --- module/Api.py | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index e6bd10762..bafb69408 100644 --- a/module/Api.py +++ b/module/Api.py @@ -813,28 +813,8 @@ class Api(Iface): :param uuid: :return: list of `Events` """ - events = self.core.pullManager.getEvents(uuid) - newEvents = [] - - def convDest(d): - return Destination.Queue if d == "queue" else Destination.Collector - - for e in events: - event = EventInfo() - event.eventname = e[0] - if e[0] in ("update", "remove", "insert"): - event.id = e[3] - event.type = ElementType.Package if e[2] == "pack" else ElementType.File - event.destination = convDest(e[1]) - elif e[0] == "order": - if e[1]: - event.id = e[1] - event.type = ElementType.Package if e[2] == "pack" else ElementType.File - event.destination = convDest(e[3]) - elif e[0] == "reload": - event.destination = convDest(e[1]) - newEvents.append(event) - return newEvents + # TODO + pass @permission(PERMS.ACCOUNTS) def getAccounts(self, refresh): @@ -854,9 +834,9 @@ class Api(Iface): def getAccountTypes(self): """All available account types. - :return: list + :return: string list """ - return self.core.pluginManager.getPlugins("account").keys() + return self.core.pluginManager.getPlugins("accounts").keys() @permission(PERMS.ACCOUNTS) def updateAccount(self, plugin, account, password=None, options={}): -- cgit v1.2.3 From b877847094b0ba03a098dff0fd769eb456b48dd1 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Fri, 6 Jan 2012 17:54:53 +0100 Subject: several improvements, also closes #486, #487 --- module/Api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index bafb69408..769fcdf0d 100644 --- a/module/Api.py +++ b/module/Api.py @@ -810,7 +810,7 @@ class Api(Iface): def getEvents(self, uuid): """Lists occured events, may be affected to changes in future. - :param uuid: + :param uuid: self assigned string uuid which has to be unique :return: list of `Events` """ # TODO -- cgit v1.2.3 From 03ec7f017c882c304be1d0759a348d5305f27d42 Mon Sep 17 00:00:00 2001 From: zoidberg10 <zoidberg@mujmail.cz> Date: Sat, 7 Jan 2012 23:15:49 +0100 Subject: xfilesharengprofolder using new crypter api, allow slash in package name --- module/Api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 769fcdf0d..d707b18fc 100644 --- a/module/Api.py +++ b/module/Api.py @@ -301,7 +301,7 @@ class Api(Iface): else: folder = "" - folder = folder.replace("http://", "").replace(":", "").replace("/", "_").replace("\\", "_") + folder = folder.replace("http://", "").replace(":", "").replace("\\", "_") #.replace("/", "_") self.core.log.info(_("Added package %(name)s containing %(count)d links") % {"name": name, "count": len(links)}) pid = self.core.files.addPackage(name, folder, dest, password) -- cgit v1.2.3 From 6eaa7bb25e2254c80c43fe46166142d590e86c64 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sat, 7 Jan 2012 23:58:28 +0100 Subject: some cleanups --- module/Api.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index d707b18fc..11b06ff32 100644 --- a/module/Api.py +++ b/module/Api.py @@ -25,11 +25,10 @@ from itertools import chain from PyFile import PyFile -from utils import freeSpace, compare_time +from utils import freeSpace, compare_time, to_string from common.packagetools import parseNames from network.RequestFactory import getURL from remote import activated -from config.converter import to_string if activated: try: @@ -134,7 +133,7 @@ class Api(Iface): @permission(PERMS.SETTINGS) def getConfig(self): """Retrieves complete config of core. - + :return: list of `ConfigSection` """ return [ConfigSection(section, data.name, data.description, data.long_desc, [ @@ -198,7 +197,7 @@ class Api(Iface): @permission(PERMS.LIST) def statusServer(self): """Some general information about the current status of pyLoad. - + :return: `ServerStatus` """ serverStatus = ServerStatus(self.core.threadManager.pause, len(self.core.threadManager.processingIds()), @@ -318,7 +317,7 @@ class Api(Iface): """ hoster, crypter = self.core.pluginManager.parseUrls(links) - if hoster: + if hoster: self.core.files.addLinks(hoster, pid) self.core.threadManager.createInfoThread(hoster, pid) @@ -495,7 +494,7 @@ class Api(Iface): @permission(PERMS.DELETE) def deleteFiles(self, fids): """Deletes several file entries from pyload. - + :param fids: list of file ids """ for id in fids: -- cgit v1.2.3 From 1ecdd9f6b53fec45e1d48592e3ff56aa7a576bec Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sun, 8 Jan 2012 16:47:52 +0100 Subject: some cleanups, closed #490 --- module/Api.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 11b06ff32..fba02d574 100644 --- a/module/Api.py +++ b/module/Api.py @@ -19,7 +19,7 @@ import re from base64 import standard_b64encode -from os.path import join +from os.path import join, isabs from time import time from itertools import chain @@ -300,7 +300,10 @@ class Api(Iface): else: folder = "" - folder = folder.replace("http://", "").replace(":", "").replace("\\", "_") #.replace("/", "_") + if isabs(folder): + folder = folder.replace("/", "_") + + folder = folder.replace("http://", "").replace(":", "").replace("\\", "_").replace("..", "") self.core.log.info(_("Added package %(name)s containing %(count)d links") % {"name": name, "count": len(links)}) pid = self.core.files.addPackage(name, folder, dest, password) -- cgit v1.2.3 From cda057e979cbdc8022687e1810b876b371c8c11f Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Fri, 13 Jan 2012 10:58:51 +0100 Subject: fix info page, removed icons --- module/Api.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index fba02d574..ac9ea7f79 100644 --- a/module/Api.py +++ b/module/Api.py @@ -25,7 +25,8 @@ from itertools import chain from PyFile import PyFile -from utils import freeSpace, compare_time, to_string +from utils import compare_time, to_string +from utils.fs import free_space from common.packagetools import parseNames from network.RequestFactory import getURL from remote import activated @@ -213,7 +214,7 @@ class Api(Iface): @permission(PERMS.STATUS) def freeSpace(self): """Available free space at download directory in bytes""" - return freeSpace(self.core.config["general"]["download_folder"]) + return free_space(self.core.config["general"]["download_folder"]) @permission(PERMS.ALL) def getServerVersion(self): -- cgit v1.2.3 From f791fb1a40240595ddc8d5b77b359f70253a0d10 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Fri, 20 Jan 2012 21:28:55 +0100 Subject: closed #519 --- module/Api.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index ac9ea7f79..5dc8916b1 100644 --- a/module/Api.py +++ b/module/Api.py @@ -137,10 +137,10 @@ class Api(Iface): :return: list of `ConfigSection` """ - return [ConfigSection(section, data.name, data.description, data.long_desc, [ - ConfigItem(option, d.name, d.description, d.type, d.default, self.core.config.get(section, option)) for - option, d in data.config.iteritems()]) for - section, data in self.core.config.getBaseSectionns()] + return dict([(section, ConfigSection(section, data.name, data.description, data.long_desc, [ + ConfigItem(option, d.name, d.description, d.type, to_string(d.default), to_string(self.core.config.get(section, option))) for + option, d in data.config.iteritems()])) for + section, data in self.core.config.getBaseSections()]) @permission(PERMS.SETTINGS) @@ -149,13 +149,15 @@ class Api(Iface): :return: list of `ConfigSection` """ - return [ConfigSection(section, data.name, data.description, data.long_desc) for - section, data in self.core.config.getPluginSections()] + return dict([(section, ConfigSection(section, + data.name, data.description, data.long_desc)) for + section, data in self.core.config.getPluginSections()]) def configureSection(self, section): data = self.core.config.config[section] sec = ConfigSection(section, data.name, data.description, data.long_desc) - sec.items = [ConfigItem(option, d.name, d.description, d.type, d.default, self.core.config.get(section, option)) + sec.items = [ConfigItem(option, d.name, d.description, + d.type, to_string(d.default), to_string(self.core.config.get(section, option))) for option, d in data.config.iteritems()] -- cgit v1.2.3 From edb879de69bd258021aae8a600418d21ec2a377f Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sat, 21 Jan 2012 18:21:06 +0100 Subject: return pid for uploadContainer --- module/Api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 5dc8916b1..e5d26631f 100644 --- a/module/Api.py +++ b/module/Api.py @@ -675,7 +675,7 @@ class Api(Iface): th.write(str(data)) th.close() - self.addPackage(th.name, [th.name]) + return self.addPackage(th.name, [th.name]) @permission(PERMS.MODIFY) def orderPackage(self, pid, position): -- cgit v1.2.3 From 4df2b77fdf42046fe19bd371be7c7255986b5980 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Tue, 6 Mar 2012 13:36:39 +0100 Subject: renamed hooks to addons, new filemanager and database, many new api methods you will loose ALL your LINKS, webinterface will NOT work --- module/Api.py | 770 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 377 insertions(+), 393 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index e5d26631f..84712af18 100644 --- a/module/Api.py +++ b/module/Api.py @@ -23,12 +23,6 @@ from os.path import join, isabs from time import time from itertools import chain - -from PyFile import PyFile -from utils import compare_time, to_string -from utils.fs import free_space -from common.packagetools import parseNames -from network.RequestFactory import getURL from remote import activated if activated: @@ -43,6 +37,12 @@ if activated: else: from remote.socketbackend.ttypes import * +from PyFile import PyFile +from utils import compare_time, to_string, bits_set +from utils.fs import free_space +from common.packagetools import parseNames +from network.RequestFactory import getURL + # contains function names mapped to their permissions # unlisted functions are for admins only permMap = {} @@ -64,12 +64,13 @@ class PERMS: ADD = 1 # can add packages DELETE = 2 # can delete packages STATUS = 4 # see and change server status - LIST = 16 # see queue and collector + LIST = 16 # see listed downloads MODIFY = 32 # moddify some attribute of downloads DOWNLOAD = 64 # can download from webinterface SETTINGS = 128 # can access settings ACCOUNTS = 256 # can access accounts LOGS = 512 # can see server logs + INTERACTION = 1024 # can interact with plugins class ROLE: @@ -78,15 +79,14 @@ class ROLE: def has_permission(userperms, perms): - # bytewise or perms before if needed - return perms == (userperms & perms) + return bits_set(perms, userperms) class Api(Iface): """ **pyLoads API** - This is accessible either internal via core.api or via thrift backend. + This is accessible either internal via core.api, thrift backend or json api. see Thrift specification file remote/thriftbackend/pyload.thrift\ for information about data structures and what methods are usuable with rpc. @@ -101,73 +101,30 @@ class Api(Iface): def __init__(self, core): self.core = core - def _convertPyFile(self, p): - f = FileData(p["id"], p["url"], p["name"], p["plugin"], p["size"], - p["format_size"], p["status"], p["statusmsg"], - p["package"], p["error"], p["order"]) - return f - - @permission(PERMS.SETTINGS) - def getConfigValue(self, section, option): - """Retrieve config value. - - :param section: name of category, or plugin - :param option: config option - :return: config value as string - """ - value = self.core.config.get(section, option) - return to_string(value) - - @permission(PERMS.SETTINGS) - def setConfigValue(self, section, option, value): - """Set new config value. - - :param section: - :param option: - :param value: new config value - """ - if option in ("limit_speed", "max_speed"): #not so nice to update the limit - self.core.requestFactory.updateBucket() - - self.core.config.set(section, option, value) - - @permission(PERMS.SETTINGS) - def getConfig(self): - """Retrieves complete config of core. - - :return: list of `ConfigSection` - """ - return dict([(section, ConfigSection(section, data.name, data.description, data.long_desc, [ - ConfigItem(option, d.name, d.description, d.type, to_string(d.default), to_string(self.core.config.get(section, option))) for - option, d in data.config.iteritems()])) for - section, data in self.core.config.getBaseSections()]) + ########################## + # Server Status + ########################## + @permission(PERMS.ALL) + def getServerVersion(self): + """pyLoad Core version """ + return self.core.version - @permission(PERMS.SETTINGS) - def getPluginConfig(self): - """Retrieves complete config for all plugins. + @permission(PERMS.LIST) + def statusServer(self): + """Some general information about the current status of pyLoad. - :return: list of `ConfigSection` + :return: `ServerStatus` """ - return dict([(section, ConfigSection(section, - data.name, data.description, data.long_desc)) for - section, data in self.core.config.getPluginSections()]) - - def configureSection(self, section): - data = self.core.config.config[section] - sec = ConfigSection(section, data.name, data.description, data.long_desc) - sec.items = [ConfigItem(option, d.name, d.description, - d.type, to_string(d.default), to_string(self.core.config.get(section, option))) - for - option, d in data.config.iteritems()] - - #TODO: config handler + serverStatus = ServerStatus(self.core.threadManager.pause, len(self.core.threadManager.processingIds()), + self.core.files.getQueueCount(), self.core.files.getFileCount(), 0, + not self.core.threadManager.pause and self.isTimeDownload(), + self.core.config['reconnect']['activated'] and self.isTimeReconnect()) - return sec + for pyfile in [x.active for x in self.core.threadManager.threads if x.active and isinstance(x.active, PyFile)]: + serverStatus.speed += pyfile.getSpeed() #bytes/s - def getConfigPointer(self): - """Config instance, not for RPC""" - return self.core.config + return serverStatus @permission(PERMS.STATUS) def pauseServer(self): @@ -197,31 +154,11 @@ class Api(Iface): self.core.config["reconnect"]["activated"] ^= True return self.core.config["reconnect"]["activated"] - @permission(PERMS.LIST) - def statusServer(self): - """Some general information about the current status of pyLoad. - - :return: `ServerStatus` - """ - serverStatus = ServerStatus(self.core.threadManager.pause, len(self.core.threadManager.processingIds()), - self.core.files.getQueueCount(), self.core.files.getFileCount(), 0, - not self.core.threadManager.pause and self.isTimeDownload(), - self.core.config['reconnect']['activated'] and self.isTimeReconnect()) - - for pyfile in [x.active for x in self.core.threadManager.threads if x.active and isinstance(x.active, PyFile)]: - serverStatus.speed += pyfile.getSpeed() #bytes/s - - return serverStatus - @permission(PERMS.STATUS) def freeSpace(self): """Available free space at download directory in bytes""" return free_space(self.core.config["general"]["download_folder"]) - @permission(PERMS.ALL) - def getServerVersion(self): - """pyLoad Core version """ - return self.core.version def kill(self): """Clean way to quit pyLoad""" @@ -269,18 +206,22 @@ class Api(Iface): end = self.core.config['reconnect']['endTime'].split(":") return compare_time(start, end) and self.core.config["reconnect"]["activated"] - @permission(PERMS.LIST) - def statusDownloads(self): - """ Status off all currently running downloads. - :return: list of `DownloadStatus` + def scanDownloadFolder(self): + pass + + @permission(PERMS.STATUS) + def getProgressInfo(self): + """ Status of all currently running tasks + + :return: list of `ProgressInfo` """ data = [] for pyfile in self.core.threadManager.getActiveFiles(): if not isinstance(pyfile, PyFile): continue - data.append(DownloadInfo( + data.append(ProgressInfo( pyfile.id, pyfile.name, pyfile.getSpeed(), pyfile.getETA(), pyfile.formatETA(), pyfile.getBytesLeft(), pyfile.getSize(), pyfile.formatSize(), pyfile.getPercent(), pyfile.status, pyfile.getStatusName(), pyfile.formatWait(), @@ -288,49 +229,81 @@ class Api(Iface): return data - @permission(PERMS.ADD) - def addPackage(self, name, links, dest=Destination.Queue, password=""): - """Adds a package, with links to desired destination. + ########################## + # Configuration + ########################## - :param name: name of the new package - :param links: list of urls - :param dest: `Destination` - :param password: password as string, can be empty - :return: package id of the new package + @permission(PERMS.SETTINGS) + def getConfigValue(self, section, option): + """Retrieve config value. + + :param section: name of category, or plugin + :param option: config option + :return: config value as string """ - if self.core.config['general']['folder_per_package']: - folder = name - else: - folder = "" + value = self.core.config.get(section, option) + return to_string(value) - if isabs(folder): - folder = folder.replace("/", "_") + @permission(PERMS.SETTINGS) + def setConfigValue(self, section, option, value): + """Set new config value. - folder = folder.replace("http://", "").replace(":", "").replace("\\", "_").replace("..", "") + :param section: + :param option: + :param value: new config value + """ + if option in ("limit_speed", "max_speed"): #not so nice to update the limit + self.core.requestFactory.updateBucket() + + self.core.config.set(section, option, value) - self.core.log.info(_("Added package %(name)s containing %(count)d links") % {"name": name, "count": len(links)}) - pid = self.core.files.addPackage(name, folder, dest, password) - self.addFiles(pid, links) + @permission(PERMS.SETTINGS) + def getConfig(self): + """Retrieves complete config of core. - return pid + :return: list of `ConfigSection` + """ + return dict([(section, ConfigSection(section, data.name, data.description, data.long_desc, [ + ConfigItem(option, d.name, d.description, d.type, to_string(d.default), to_string(self.core.config.get(section, option))) for + option, d in data.config.iteritems()])) for + section, data in self.core.config.getBaseSections()]) - @permission(PERMS.ADD) - def addFiles(self, pid, links): - """Adds files to specific package. - :param pid: package id - :param links: list of urls + @permission(PERMS.SETTINGS) + def getPluginConfig(self): + """Retrieves complete config for all plugins. + + :return: list of `ConfigSection` """ - hoster, crypter = self.core.pluginManager.parseUrls(links) + return dict([(section, ConfigSection(section, + data.name, data.description, data.long_desc)) for + section, data in self.core.config.getPluginSections()]) - if hoster: - self.core.files.addLinks(hoster, pid) + @permission(PERMS.SETTINGS) + def configureSection(self, section): + data = self.core.config.config[section] + sec = ConfigSection(section, data.name, data.description, data.long_desc) + sec.items = [ConfigItem(option, d.name, d.description, + d.type, to_string(d.default), to_string(self.core.config.get(section, option))) + for + option, d in data.config.iteritems()] - self.core.threadManager.createInfoThread(hoster, pid) - self.core.threadManager.createDecryptThread(crypter, pid) + #TODO: config handler - self.core.log.debug("Added %d links to package #%d " % (len(hoster), pid)) - self.core.files.save() + return sec + + + @permission(PERMS.SETTINGS) + def setConfigHandler(self, plugin, iid, value): + pass + + def getConfigRef(self): + """Config instance, not for RPC""" + return self.core.config + + ########################## + # Download Preparing + ########################## @permission(PERMS.ADD) def parseURLs(self, html=None, url=None): @@ -375,12 +348,12 @@ class Api(Iface): """ initiates online status check, will also decrypt files. :param urls: - :return: initial set of data as `OnlineCheck` instance containing the result id + :return: initial set of data as :class:`OnlineCheck` instance containing the result id """ data, crypter = self.core.pluginManager.parseUrls(urls) # initial result does not contain the crypter links - tmp = [(url, (url, OnlineStatus(url, pluginname, "unknown", 3, 0))) for url, pluginname in data] + tmp = [(url, (url, LinkStatus(url, pluginname, "unknown", 3, 0))) for url, pluginname in data] data = parseNames(tmp) result = {} @@ -401,7 +374,7 @@ class Api(Iface): :param urls: list of urls :param container: container file name :param data: file content - :return: online check + :return: :class:`OnlineCheck` """ th = open(join(self.core.config["general"]["download_folder"], "tmp_" + container), "wb") th.write(str(data)) @@ -435,67 +408,110 @@ class Api(Iface): result = parseNames((x, x) for x in links) return result + ########################## + # Adding/Deleting + ########################## + @permission(PERMS.ADD) - def generateAndAddPackages(self, links, dest=Destination.Queue): + def generateAndAddPackages(self, links, paused=False): """Generates and add packages :param links: list of urls - :param dest: `Destination` + :param paused: paused package :return: list of package ids """ - return [self.addPackage(name, urls, dest) for name, urls + return [self.addPackageP(name, urls, "", paused) for name, urls in self.generatePackages(links).iteritems()] + @permission(PERMS.ADD) + def autoAddLinks(self, links): + pass - @permission(PERMS.LIST) - def getPackageData(self, pid): - """Returns complete information about package, and included files. + @permission(PERMS.ADD) + def createPackage(self, name, folder, root, password="", site="", comment="", paused=False): + """Create a new package. - :param pid: package id - :return: `PackageData` with .links attribute + :param name: display name of the package + :param folder: folder name or relative path, abs path are not allowed + :param root: package id of root package, -1 for top level package + :param password: single pw or list of passwords seperated with new line + :param site: arbitrary url to site for more information + :param comment: arbitrary comment + :param paused: No downloads will be started when True + :return: pid of newly created package """ - data = self.core.files.getPackageData(int(pid)) - if not data: - raise PackageDoesNotExists(pid) + if isabs(folder): + folder = folder.replace("/", "_") - pdata = PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"], - data["queue"], data["order"], - links=[self._convertPyFile(x) for x in data["links"].itervalues()]) + folder = folder.replace("http://", "").replace(":", "").replace("\\", "_").replace("..", "") - return pdata + self.core.log.info(_("Added package %(name)s as folder %(folder)s") % {"name": name, "folder": folder}) + pid = self.core.files.addPackage(name, folder, root, password, site, comment, paused) - @permission(PERMS.LIST) - def getPackageInfo(self, pid): - """Returns information about package, without detailed information about containing files + return pid - :param pid: package id - :return: `PackageData` with .fid attribute + + @permission(PERMS.ADD) + def addPackage(self, name, links, password=""): + """Convenient method to add a package to top-level and adding links. + + :return: package id """ - data = self.core.files.getPackageData(int(pid)) + self.addPackageChild(name, links, password, -1, False) - if not data: - raise PackageDoesNotExists(pid) + @permission(PERMS.ADD) + def addPackageP(self, name, links, password, paused): + """ Same as above with additional paused attribute. """ + self.addPackageChild(name, links, password, -1, paused) - pdata = PackageData(data["id"], data["name"], data["folder"], data["site"], data["password"], - data["queue"], data["order"], - fids=[int(x) for x in data["links"]]) + @permission(PERMS.ADD) + def addPackageChild(self, name, links, password, root, paused): + """Adds a package, with links to desired package. - return pdata + :param root: parents package id + :return: package id of the new package + """ + if self.core.config['general']['folder_per_package']: + folder = name + else: + folder = "" - @permission(PERMS.LIST) - def getFileData(self, fid): - """Get complete information about a specific file. + pid = self.createPackage(name, folder, root, password) + self.addLinks(pid, links) - :param fid: file id - :return: `FileData` - """ - info = self.core.files.getFileData(int(fid)) - if not info: - raise FileDoesNotExists(fid) + return pid + + @permission(PERMS.ADD) + def addLinks(self, pid, links): + """Adds links to specific package. Automatical starts online status fetching. + + :param pid: package id + :param links: list of urls + """ + hoster, crypter = self.core.pluginManager.parseUrls(links) + + if hoster: + self.core.files.addLinks(hoster, pid) + self.core.threadManager.createInfoThread(hoster, pid) + + self.core.threadManager.createDecryptThread(crypter, pid) + + self.core.log.info((_("Added %d links to package") + " #%d" % pid) % len(hoster)) + self.core.files.save() + + @permission(PERMS.ADD) + def uploadContainer(self, filename, data): + """Uploads and adds a container file to pyLoad. + + :param filename: filename, extension is important so it can correctly decrypted + :param data: file content + """ + th = open(join(self.core.config["general"]["download_folder"], "tmp_" + filename), "wb") + th.write(str(data)) + th.close() - fdata = self._convertPyFile(info.values()[0]) - return fdata + return self.addPackage(th.name, [th.name]) @permission(PERMS.DELETE) def deleteFiles(self, fids): @@ -503,8 +519,8 @@ class Api(Iface): :param fids: list of file ids """ - for id in fids: - self.core.files.deleteLink(int(id)) + for fid in fids: + self.core.files.deleteFile(fid) self.core.files.save() @@ -514,76 +530,113 @@ class Api(Iface): :param pids: list of package ids """ - for id in pids: - self.core.files.deletePackage(int(id)) + for pid in pids: + self.core.files.deletePackage(pid) self.core.files.save() + ########################## + # Collector + ########################## + @permission(PERMS.LIST) - def getQueue(self): - """Returns info about queue and packages, **not** about files, see `getQueueData` \ - or `getPackageData` instead. + def getCollector(self): + pass - :return: list of `PackageInfo` - """ - return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], - pack["password"], pack["queue"], pack["order"], - pack["linksdone"], pack["sizedone"], pack["sizetotal"], - pack["linkstotal"]) - for pack in self.core.files.getInfoData(Destination.Queue).itervalues()] + @permission(PERMS.ADD) + def addToCollector(self, links): + pass + + @permission(PERMS.ADD) + def addFromCollector(self, name, paused): + pass + + @permission(PERMS.DELETE) + def deleteCollPack(self, name): + pass + + @permission(PERMS.DELETE) + def deleteCollLink(self, url): + pass + + @permission(PERMS.ADD) + def renameCollPack(self, name, new_name): + pass + + ############################# + # File Information retrival + ############################# @permission(PERMS.LIST) - def getQueueData(self): - """Return complete data about everything in queue, this is very expensive use it sparely.\ - See `getQueue` for alternative. + def getAllFiles(self): + """ same as `getFileTree` for toplevel root and full tree""" + return self.getFileTree(-1, True) - :return: list of `PackageData` - """ - return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], - pack["password"], pack["queue"], pack["order"], - pack["linksdone"], pack["sizedone"], pack["sizetotal"], - links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) - for pack in self.core.files.getCompleteData(Destination.Queue).itervalues()] + @permission(PERMS.LIST) + def getAllUnfinishedFiles(self): + """ same as `getUnfinishedFileTree for toplevel root and full tree""" + return self.getUnfinishedFileTree(-1, True) @permission(PERMS.LIST) - def getCollector(self): - """same as `getQueue` for collector. + def getFileTree(self, pid, full): + """ Retrieve data for specific package. full=True will retrieve all data available + and can result in greater delays. - :return: list of `PackageInfo` + :param pid: package id + :param full: go down the complete tree or only the first layer + :return: :class:`PackageView` """ - return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], - pack["password"], pack["queue"], pack["order"], - pack["linksdone"], pack["sizedone"], pack["sizetotal"], - pack["linkstotal"]) - for pack in self.core.files.getInfoData(Destination.Collector).itervalues()] + return self.core.files.getView(pid, full, False) @permission(PERMS.LIST) - def getCollectorData(self): - """same as `getQueueData` for collector. + def getUnfinishedFileTree(self, pid, full): + """ Same as `getFileTree` but only contains unfinished files. - :return: list of `PackageInfo` + :param pid: package id + :param full: go down the complete tree or only the first layer + :return: :class:`PackageView` """ - return [PackageData(pack["id"], pack["name"], pack["folder"], pack["site"], - pack["password"], pack["queue"], pack["order"], - pack["linksdone"], pack["sizedone"], pack["sizetotal"], - links=[self._convertPyFile(x) for x in pack["links"].itervalues()]) - for pack in self.core.files.getCompleteData(Destination.Collector).itervalues()] + return self.core.files.getView(pid, full, False) - @permission(PERMS.MODIFY) - def pushToQueue(self, pid): - """Moves package from Collector to Queue. + @permission(PERMS.LIST) + def getPackageContent(self, pid): + """ Only retrieve content of a specific package. see `getFileTree`""" + return self.getFileTree(pid, False) + + @permission(PERMS.LIST) + def getPackageInfo(self, pid): + """Returns information about package, without detailed information about containing files :param pid: package id + :raises PackageDoesNotExists: + :return: :class:`PackageInfo` """ - self.core.files.setPackageLocation(pid, Destination.Queue) + info = self.core.files.getPackageInfo(pid) + if not info: + raise PackageDoesNotExists(pid) + return info - @permission(PERMS.MODIFY) - def pullFromQueue(self, pid): - """Moves package from Queue to Collector. + @permission(PERMS.LIST) + def getFileInfo(self, fid): + """ Info for specific file + + :param fid: file id + :raises FileDoesNotExists: + :return: :class:`FileInfo` - :param pid: package id """ - self.core.files.setPackageLocation(pid, Destination.Collector) + info = self.core.files.getFileInfo(fid) + if not info: + raise FileDoesNotExists(fid) + return info + + @permission(PERMS.LIST) + def findFiles(self, pattern): + pass + + ############################# + # Modify Downloads + ############################# @permission(PERMS.MODIFY) def restartPackage(self, pid): @@ -591,30 +644,26 @@ class Api(Iface): :param pid: package id """ - self.core.files.restartPackage(int(pid)) + self.core.files.restartPackage(pid) @permission(PERMS.MODIFY) def restartFile(self, fid): """Resets file status, so it will be downloaded again. - :param fid: file id + :param fid: file id """ - self.core.files.restartFile(int(fid)) + self.core.files.restartFile(fid) @permission(PERMS.MODIFY) def recheckPackage(self, pid): - """Proofes online status of all files in a package, also a default action when package is added. - - :param pid: - :return: - """ - self.core.files.reCheckPackage(int(pid)) + """Check online status of all files in a package, also a default action when package is added. """ + self.core.files.reCheckPackage(pid) @permission(PERMS.MODIFY) def stopAllDownloads(self): """Aborts all running downloads.""" - pyfiles = self.core.files.cache.values() + pyfiles = self.core.files.cachedFiles() for pyfile in pyfiles: pyfile.abortDownload() @@ -625,75 +674,76 @@ class Api(Iface): :param fids: list of file ids :return: """ - pyfiles = self.core.files.cache.values() - + pyfiles = self.core.files.cachedFiles() for pyfile in pyfiles: if pyfile.id in fids: pyfile.abortDownload() @permission(PERMS.MODIFY) - def setPackageName(self, pid, name): - """Renames a package. + def restartFailed(self): + """Restarts all failed failes.""" + self.core.files.restartFailed() - :param pid: package id - :param name: new package name - """ - pack = self.core.files.getPackage(pid) - pack.name = name - pack.sync() + ############################# + # Modify Files/Packages + ############################# @permission(PERMS.MODIFY) - def movePackage(self, destination, pid): - """Set a new package location. + def setFilePaused(self, fid, paused): + pass + + @permission(PERMS.MODIFY) + def setPackagePaused(self, pid, paused): + pass + + @permission(PERMS.MODIFY) + def setPackageFolder(self, pid, path): + pass + + @permission(PERMS.MODIFY) + def movePackage(self, pid, root): + """ Set a new root for specific package. This will also moves the files on disk\ + and will only work when no file is currently downloading. - :param destination: `Destination` :param pid: package id + :param root: package id of new root + :raises PackageDoesNotExists: When pid or root is missing + :return: False if package can't be moved """ - if destination not in (0, 1): return - self.core.files.setPackageLocation(pid, destination) + return self.core.files.movePackage(pid, root) @permission(PERMS.MODIFY) def moveFiles(self, fids, pid): - """Move multiple files to another package + """Move multiple files to another package. This will move the files on disk and\ + only work when files are not downloading. All files needs to be continuous ordered + in the current package. :param fids: list of file ids :param pid: destination package - :return: - """ - #TODO: implement - pass - - - @permission(PERMS.ADD) - def uploadContainer(self, filename, data): - """Uploads and adds a container file to pyLoad. - - :param filename: filename, extension is important so it can correctly decrypted - :param data: file content + :return: False if files can't be moved """ - th = open(join(self.core.config["general"]["download_folder"], "tmp_" + filename), "wb") - th.write(str(data)) - th.close() - - return self.addPackage(th.name, [th.name]) + return self.core.files.moveFiles(fids, pid) @permission(PERMS.MODIFY) def orderPackage(self, pid, position): - """Gives a package a new position. + """Set new position for a package. :param pid: package id - :param position: + :param position: new position, 0 for very beginning """ - self.core.files.reorderPackage(pid, position) + self.core.files.orderPackage(pid, position) @permission(PERMS.MODIFY) - def orderFile(self, fid, position): - """Gives a new position to a file within its package. + def orderFiles(self, fids, pid, position): + """ Set a new position for a bunch of files within a package. + All files have to be in the same package and must be **continuous**\ + in the package. That means no gaps between them. - :param fid: file id - :param position: + :param fids: list of file ids + :param pid: package id of parent package + :param position: new position: 0 for very beginning """ - self.core.files.reorderFile(fid, position) + self.core.files.orderFiles(fids, pid, position) @permission(PERMS.MODIFY) def setPackageData(self, pid, data): @@ -712,104 +762,38 @@ class Api(Iface): p.sync() self.core.files.save() - @permission(PERMS.DELETE) - def deleteFinished(self): - """Deletes all finished files and completly finished packages. - - :return: list of deleted package ids - """ - return self.core.files.deleteFinishedLinks() - - @permission(PERMS.MODIFY) - def restartFailed(self): - """Restarts all failed failes.""" - self.core.files.restartFailed() - - @permission(PERMS.LIST) - def getPackageOrder(self, destination): - """Returns information about package order. + ############################# + # User Interaction + ############################# - :param destination: `Destination` - :return: dict mapping order to package id - """ - - packs = self.core.files.getInfoData(destination) - order = {} - - for pid in packs: - pack = self.core.files.getPackageData(int(pid)) - while pack["order"] in order.keys(): #just in case - pack["order"] += 1 - order[pack["order"]] = pack["id"] - return order - - @permission(PERMS.LIST) - def getFileOrder(self, pid): - """Information about file order within package. - - :param pid: - :return: dict mapping order to file id - """ - rawData = self.core.files.getPackageData(int(pid)) - order = {} - for id, pyfile in rawData["links"].iteritems(): - while pyfile["order"] in order.keys(): #just in case - pyfile["order"] += 1 - order[pyfile["order"]] = pyfile["id"] - return order - - - @permission(PERMS.STATUS) - def isCaptchaWaiting(self): - """Indicates wether a captcha task is available - :return: bool - """ - self.core.lastClientConnected = time() - task = self.core.captchaManager.getTask() - return not task is None + @permission(PERMS.INTERACTION) + def isInteractionWaiting(self, mode): + pass - @permission(PERMS.STATUS) - def getCaptchaTask(self, exclusive=False): - """Returns a captcha task - - :param exclusive: unused - :return: `CaptchaTask` - """ - self.core.lastClientConnected = time() - task = self.core.captchaManager.getTask() - if task: - task.setWatingForUser(exclusive=exclusive) - data, type, result = task.getCaptcha() - t = CaptchaTask(int(task.id), standard_b64encode(data), type, result) - return t - else: - return CaptchaTask(-1) + @permission(PERMS.INTERACTION) + def getInteractionTask(self, mode): + pass - @permission(PERMS.STATUS) - def getCaptchaTaskStatus(self, tid): - """Get information about captcha task + @permission(PERMS.INTERACTION) + def setInteractionResult(self, iid, result): + pass - :param tid: task id - :return: string - """ - self.core.lastClientConnected = time() - t = self.core.captchaManager.getTaskByID(tid) - return t.getStatus() if t else "" + @permission(PERMS.INTERACTION) + def getAddonHandler(self): + pass - @permission(PERMS.STATUS) - def setCaptchaResult(self, tid, result): - """Set result for a captcha task + @permission(PERMS.INTERACTION) + def callAddonHandler(self, plugin, func, pid_or_fid): + pass - :param tid: task id - :param result: captcha result - """ - self.core.lastClientConnected = time() - task = self.core.captchaManager.getTaskByID(tid) - if task: - task.setResult(result) - self.core.captchaManager.removeTask(task) + @permission(PERMS.DOWNLOAD) + def generateDownloadLink(self, fid, timeout): + pass + ############################# + # Event Handling + ############################# @permission(PERMS.STATUS) def getEvents(self, uuid): @@ -821,6 +805,10 @@ class Api(Iface): # TODO pass + ############################# + # Account Methods + ############################# + @permission(PERMS.ACCOUNTS) def getAccounts(self, refresh): """Get information about all entered accounts. @@ -857,6 +845,10 @@ class Api(Iface): """ self.core.accountManager.removeAccount(plugin, account) + ############################# + # Auth+User Information + ############################# + @permission(PERMS.ALL) def login(self, username, password, remoteip=None): """Login into pyLoad, this **must** be called when using rpc before any methods can be used. @@ -881,6 +873,8 @@ class Api(Iface): if self.core.startedInGui and remoteip == "127.0.0.1": return "local" + self.core.log.info(_("User '%s' tried to log in") % username) + return self.core.db.checkAuth(username, password) def isAuthorized(self, func, userdata): @@ -907,7 +901,6 @@ class Api(Iface): raise UserDoesNotExists(username) - def getAllUserData(self): """returns all known user and info""" res = {} @@ -916,58 +909,57 @@ class Api(Iface): return res - @permission(PERMS.STATUS) + def changePassword(self, user, oldpw, newpw): + """ changes password for specific user """ + return self.core.db.changePassword(user, oldpw, newpw) + + def setUserPermission(self, user, permission, role): + self.core.db.setPermission(user, permission) + self.core.db.setRole(user, role) + + ############################# + # RPC Plugin Methods + ############################# + + @permission(PERMS.INTERACTION) def getServices(self): - """ A dict of available services, these can be defined by hook plugins. + """ A dict of available services, these can be defined by addon plugins. :return: dict with this style: {"plugin": {"method": "description"}} """ data = {} - for plugin, funcs in self.core.hookManager.methods.iteritems(): + for plugin, funcs in self.core.addonManager.methods.iteritems(): data[plugin] = funcs return data - @permission(PERMS.STATUS) + @permission(PERMS.INTERACTION) def hasService(self, plugin, func): - """Checks wether a service is available. + pass - :param plugin: - :param func: - :return: bool - """ - cont = self.core.hookManager.methods - return plugin in cont and func in cont[plugin] + @permission(PERMS.INTERACTION) + def call(self, plugin, func, arguments): + """Calls a service (a method in addon plugin). - @permission(PERMS.STATUS) - def call(self, info): - """Calls a service (a method in hook plugin). - - :param info: `ServiceCall` - :return: result :raises: ServiceDoesNotExists, when its not available :raises: ServiceException, when a exception was raised """ - plugin = info.plugin - func = info.func - args = info.arguments - if not self.hasService(plugin, func): raise ServiceDoesNotExists(plugin, func) try: - ret = self.core.hookManager.callRPC(plugin, func, args) - return str(ret) + ret = self.core.addonManager.callRPC(plugin, func, arguments) + return to_string(ret) except Exception, e: raise ServiceException(e.message) @permission(PERMS.STATUS) def getAllInfo(self): - """Returns all information stored by hook plugins. Values are always strings + """Returns all information stored by addon plugins. Values are always strings :return: {"plugin": {"name": value } } """ - return self.core.hookManager.getAllInfo() + return self.core.addonManager.getAllInfo() @permission(PERMS.STATUS) def getInfoByPlugin(self, plugin): @@ -976,12 +968,4 @@ class Api(Iface): :param plugin: pluginname :return: dict of attr names mapped to value {"name": value} """ - return self.core.hookManager.getInfo(plugin) - - def changePassword(self, user, oldpw, newpw): - """ changes password for specific user """ - return self.core.db.changePassword(user, oldpw, newpw) - - def setUserPermission(self, user, permission, role): - self.core.db.setPermission(user, permission) - self.core.db.setRole(user, role) + return self.core.addonManager.getInfo(plugin) -- cgit v1.2.3 From 50d4df8b4d48b855bd18e9922355b7f3f2b4da4e Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Tue, 20 Mar 2012 14:57:45 +0100 Subject: captcha decrypting for all plugin types, new interaction manager --- module/Api.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 84712af18..6d7ac75b6 100644 --- a/module/Api.py +++ b/module/Api.py @@ -766,18 +766,45 @@ class Api(Iface): # User Interaction ############################# - @permission(PERMS.INTERACTION) def isInteractionWaiting(self, mode): - pass + """ Check if task is waiting. + + :param mode: binary or'ed output type + :return: boolean + """ + return self.core.interactionManager.isTaskWaiting(mode) @permission(PERMS.INTERACTION) def getInteractionTask(self, mode): - pass + """Retrieve task for specific mode. + + :param mode: binary or'ed output type + :return: :class:`InteractionTask` + """ + task = self.core.interactionManager.getTask(mode) + return InteractionTask(-1) if not task else task + @permission(PERMS.INTERACTION) def setInteractionResult(self, iid, result): - pass + """Set Result for a interaction task. It will be immediately removed from task queue afterwards + + :param iid: interaction id + :param result: result as string + """ + task = self.core.interactionManager.getTaskByID(iid) + if task: + task.setResult(result) + + @permission(PERMS.INTERACTION) + def getNotifications(self): + """List of all available notifcations. They stay in queue for some time, client should\ + save which notifications it already has seen. + + :return: list of :class:`InteractionTask` + """ + return self.core.interactionManager.getNotifications() @permission(PERMS.INTERACTION) def getAddonHandler(self): -- cgit v1.2.3 From b40b32ee05f611323a7827fad2a25fa0a28dcb24 Mon Sep 17 00:00:00 2001 From: X3n0m0rph59 <X3n0m0rph59@googlemail.com> Date: Sun, 22 Apr 2012 19:56:17 +0200 Subject: a huge pile of spelling fixes --- module/Api.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 6d7ac75b6..d85680cd5 100644 --- a/module/Api.py +++ b/module/Api.py @@ -89,10 +89,10 @@ class Api(Iface): This is accessible either internal via core.api, thrift backend or json api. see Thrift specification file remote/thriftbackend/pyload.thrift\ - for information about data structures and what methods are usuable with rpc. + for information about data structures and what methods are usable with rpc. Most methods requires specific permissions, please look at the source code if you need to know.\ - These can be configured via webinterface. + These can be configured via web interface. Admin user have all permissions, and are the only ones who can access the methods with no specific permission. """ @@ -128,7 +128,7 @@ class Api(Iface): @permission(PERMS.STATUS) def pauseServer(self): - """Pause server: Tt wont start any new downloads, but nothing gets aborted.""" + """Pause server: It won't start any new downloads, but nothing gets aborted.""" self.core.threadManager.pause = True @permission(PERMS.STATUS) @@ -307,7 +307,7 @@ class Api(Iface): @permission(PERMS.ADD) def parseURLs(self, html=None, url=None): - """Parses html content or any arbitaty text for links and returns result of `checkURLs` + """Parses html content or any arbitrary text for links and returns result of `checkURLs` :param html: html source :return: @@ -327,7 +327,7 @@ class Api(Iface): @permission(PERMS.ADD) def checkURLs(self, urls): - """ Gets urls and returns pluginname mapped to list of matches urls. + """ Gets urls and returns pluginname mapped to list of matching urls. :param urls: :return: {plugin: urls} @@ -369,7 +369,7 @@ class Api(Iface): @permission(PERMS.ADD) def checkOnlineStatusContainer(self, urls, container, data): - """ checks online status of urls and a submited container file + """ checks online status of urls and a submitted container file :param urls: list of urls :param container: container file name @@ -387,7 +387,7 @@ class Api(Iface): """ Polls the result available for ResultID :param rid: `ResultID` - :return: `OnlineCheck`, if rid is -1 then no more data available + :return: `OnlineCheck`, if rid is -1 then there is no more data available """ result = self.core.threadManager.getInfoResult(rid) @@ -434,7 +434,7 @@ class Api(Iface): :param name: display name of the package :param folder: folder name or relative path, abs path are not allowed :param root: package id of root package, -1 for top level package - :param password: single pw or list of passwords seperated with new line + :param password: single pw or list of passwords separated with new line :param site: arbitrary url to site for more information :param comment: arbitrary comment :param paused: No downloads will be started when True @@ -454,7 +454,7 @@ class Api(Iface): @permission(PERMS.ADD) def addPackage(self, name, links, password=""): - """Convenient method to add a package to top-level and adding links. + """Convenient method to add a package to the top-level and for adding links. :return: package id """ @@ -484,7 +484,7 @@ class Api(Iface): @permission(PERMS.ADD) def addLinks(self, pid, links): - """Adds links to specific package. Automatical starts online status fetching. + """Adds links to specific package. Initiates online status fetching. :param pid: package id :param links: list of urls -- cgit v1.2.3 From f36bb35cf296c74ff5a3676c038e2ef2a8be9068 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sun, 6 May 2012 17:22:13 +0200 Subject: concept for multiuser api --- module/Api.py | 152 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 113 insertions(+), 39 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index d85680cd5..c969045f8 100644 --- a/module/Api.py +++ b/module/Api.py @@ -18,10 +18,11 @@ """ import re -from base64 import standard_b64encode from os.path import join, isabs -from time import time from itertools import chain +from functools import partial +from new import code +from dis import opmap from remote import activated @@ -45,17 +46,57 @@ from network.RequestFactory import getURL # contains function names mapped to their permissions # unlisted functions are for admins only -permMap = {} +perm_map = {} + +# store which methods needs user context +user_context = {} # decorator only called on init, never initialized, so has no effect on runtime def permission(bits): class _Dec(object): def __new__(cls, func, *args, **kwargs): - permMap[func.__name__] = bits + perm_map[func.__name__] = bits return func return _Dec +# we will bytehacking the method to add user as keyword argument +class UserContext(object): + """Decorator to mark methods that require a specific user""" + + def __new__(cls, f, *args, **kwargs): + fc = f.func_code + + try: + i = fc.co_names.index("user") + except ValueError: # functions does not uses user, so no need to modify + return f + + user_context[f.__name__] = True + new_names = tuple([x for x in fc.co_names if f != "user"]) + new_varnames = tuple([x for x in fc.co_varnames] + ["user"]) + new_code = fc.co_code + + # subtract 1 from higher LOAD_GLOBAL + for x in range(i + 1, len(fc.co_names)): + new_code = new_code.replace(chr(opmap['LOAD_GLOBAL']) + chr(x), chr(opmap['LOAD_GLOBAL']) + chr(x - 1)) + + # load argument instead of global + new_code = new_code.replace(chr(opmap['LOAD_GLOBAL']) + chr(i), chr(opmap['LOAD_FAST']) + chr(fc.co_argcount)) + + new_fc = code(fc.co_argcount + 1, fc.co_nlocals + 1, fc.co_stacksize, fc.co_flags, new_code, fc.co_consts, + new_names, new_varnames, fc.co_filename, fc.co_name, fc.co_firstlineno, fc.co_lnotab, fc.co_freevars, + fc.co_cellvars) + + f.func_code = new_fc + + # None as default argument for user + if f.func_defaults: + f.func_defaults = tuple([x for x in f.func_defaults] + [None]) + else: + f.func_defaults = (None,) + + return f urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-=\\\.&]*)", re.IGNORECASE) @@ -82,6 +123,21 @@ def has_permission(userperms, perms): return bits_set(perms, userperms) +class UserApi(object): + """ Proxy object for api that provides all methods in user context """ + + def __init__(self, api, user): + self.api = api + self.user = user + + def __getattr__(self, item): + f = self.api.__getattribute__(item) + if f.func_name in user_context: + return partial(f, user=self.user) + + return f + + class Api(Iface): """ **pyLoads API** @@ -101,13 +157,30 @@ class Api(Iface): def __init__(self, core): self.core = core + self.t = self.inUserContext("TestUser") + + print self.t.getServerVersion() + + + # TODO, create user instance, work + def inUserContext(self, user): + """ Returns a proxy version of the api, to call method in user context + + :param user: user id + :return: :class:`UserApi` + """ + return UserApi(self, user) + + ########################## # Server Status ########################## + @UserContext #TODO: only for testing @permission(PERMS.ALL) def getServerVersion(self): """pyLoad Core version """ + print user return self.core.version @permission(PERMS.LIST) @@ -264,9 +337,10 @@ class Api(Iface): :return: list of `ConfigSection` """ return dict([(section, ConfigSection(section, data.name, data.description, data.long_desc, [ - ConfigItem(option, d.name, d.description, d.type, to_string(d.default), to_string(self.core.config.get(section, option))) for + ConfigItem(option, d.name, d.description, d.type, to_string(d.default), + to_string(self.core.config.get(section, option))) for option, d in data.config.iteritems()])) for - section, data in self.core.config.getBaseSections()]) + section, data in self.core.config.getBaseSections()]) @permission(PERMS.SETTINGS) @@ -277,7 +351,7 @@ class Api(Iface): """ return dict([(section, ConfigSection(section, data.name, data.description, data.long_desc)) for - section, data in self.core.config.getPluginSections()]) + section, data in self.core.config.getPluginSections()]) @permission(PERMS.SETTINGS) def configureSection(self, section): @@ -442,7 +516,7 @@ class Api(Iface): """ if isabs(folder): - folder = folder.replace("/", "_") + folder = folder.replace("/", "_") folder = folder.replace("http://", "").replace(":", "").replace("\\", "_").replace("..", "") @@ -467,51 +541,51 @@ class Api(Iface): @permission(PERMS.ADD) def addPackageChild(self, name, links, password, root, paused): - """Adds a package, with links to desired package. + """Adds a package, with links to desired package. - :param root: parents package id - :return: package id of the new package - """ - if self.core.config['general']['folder_per_package']: - folder = name - else: - folder = "" + :param root: parents package id + :return: package id of the new package + """ + if self.core.config['general']['folder_per_package']: + folder = name + else: + folder = "" - pid = self.createPackage(name, folder, root, password) - self.addLinks(pid, links) + pid = self.createPackage(name, folder, root, password) + self.addLinks(pid, links) - return pid + return pid @permission(PERMS.ADD) def addLinks(self, pid, links): - """Adds links to specific package. Initiates online status fetching. + """Adds links to specific package. Initiates online status fetching. - :param pid: package id - :param links: list of urls - """ - hoster, crypter = self.core.pluginManager.parseUrls(links) + :param pid: package id + :param links: list of urls + """ + hoster, crypter = self.core.pluginManager.parseUrls(links) - if hoster: - self.core.files.addLinks(hoster, pid) - self.core.threadManager.createInfoThread(hoster, pid) + if hoster: + self.core.files.addLinks(hoster, pid) + self.core.threadManager.createInfoThread(hoster, pid) - self.core.threadManager.createDecryptThread(crypter, pid) + self.core.threadManager.createDecryptThread(crypter, pid) - self.core.log.info((_("Added %d links to package") + " #%d" % pid) % len(hoster)) - self.core.files.save() + self.core.log.info((_("Added %d links to package") + " #%d" % pid) % len(hoster)) + self.core.files.save() @permission(PERMS.ADD) def uploadContainer(self, filename, data): - """Uploads and adds a container file to pyLoad. + """Uploads and adds a container file to pyLoad. - :param filename: filename, extension is important so it can correctly decrypted - :param data: file content - """ - th = open(join(self.core.config["general"]["download_folder"], "tmp_" + filename), "wb") - th.write(str(data)) - th.close() + :param filename: filename, extension is important so it can correctly decrypted + :param data: file content + """ + th = open(join(self.core.config["general"]["download_folder"], "tmp_" + filename), "wb") + th.write(str(data)) + th.close() - return self.addPackage(th.name, [th.name]) + return self.addPackage(th.name, [th.name]) @permission(PERMS.DELETE) def deleteFiles(self, fids): @@ -913,7 +987,7 @@ class Api(Iface): """ if userdata == "local" or userdata["role"] == ROLE.ADMIN: return True - elif func in permMap and has_permission(userdata["permission"], permMap[func]): + elif func in perm_map and has_permission(userdata["permission"], perm_map[func]): return True else: return False -- cgit v1.2.3 From 84efa9d5ccd46a0adddc256a5eba4f8e33c76afd Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Mon, 7 May 2012 18:42:29 +0200 Subject: new multiuser api methods --- module/Api.py | 181 +++++++++++++++++++++++++--------------------------------- 1 file changed, 78 insertions(+), 103 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index c969045f8..bab039ea1 100644 --- a/module/Api.py +++ b/module/Api.py @@ -52,7 +52,7 @@ perm_map = {} user_context = {} # decorator only called on init, never initialized, so has no effect on runtime -def permission(bits): +def RequirePerm(bits): class _Dec(object): def __new__(cls, func, *args, **kwargs): perm_map[func.__name__] = bits @@ -100,27 +100,9 @@ class UserContext(object): urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+\-=\\\.&]*)", re.IGNORECASE) -class PERMS: - ALL = 0 # requires no permission, but login - ADD = 1 # can add packages - DELETE = 2 # can delete packages - STATUS = 4 # see and change server status - LIST = 16 # see listed downloads - MODIFY = 32 # moddify some attribute of downloads - DOWNLOAD = 64 # can download from webinterface - SETTINGS = 128 # can access settings - ACCOUNTS = 256 # can access accounts - LOGS = 512 # can see server logs - INTERACTION = 1024 # can interact with plugins - -class ROLE: - ADMIN = 0 #admin has all permissions implicit - USER = 1 - - -def has_permission(userperms, perms): - return bits_set(perms, userperms) +def has_permission(userPermission, Permission): + return bits_set(Permission, userPermission) class UserApi(object): @@ -157,13 +139,13 @@ class Api(Iface): def __init__(self, core): self.core = core - self.t = self.inUserContext("TestUser") + self.t = self.withUserContext("TestUser") print self.t.getServerVersion() # TODO, create user instance, work - def inUserContext(self, user): + def withUserContext(self, user): """ Returns a proxy version of the api, to call method in user context :param user: user id @@ -177,13 +159,13 @@ class Api(Iface): ########################## @UserContext #TODO: only for testing - @permission(PERMS.ALL) + @RequirePerm(Permission.All) def getServerVersion(self): """pyLoad Core version """ print user return self.core.version - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def statusServer(self): """Some general information about the current status of pyLoad. @@ -199,17 +181,17 @@ class Api(Iface): return serverStatus - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def pauseServer(self): """Pause server: It won't start any new downloads, but nothing gets aborted.""" self.core.threadManager.pause = True - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def unpauseServer(self): """Unpause server: New Downloads will be started.""" self.core.threadManager.pause = False - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def togglePause(self): """Toggle pause state. @@ -218,7 +200,7 @@ class Api(Iface): self.core.threadManager.pause ^= True return self.core.threadManager.pause - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def toggleReconnect(self): """Toggle reconnect activation. @@ -227,7 +209,7 @@ class Api(Iface): self.core.config["reconnect"]["activated"] ^= True return self.core.config["reconnect"]["activated"] - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def freeSpace(self): """Available free space at download directory in bytes""" return free_space(self.core.config["general"]["download_folder"]) @@ -241,7 +223,6 @@ class Api(Iface): """Restart pyload core""" self.core.do_restart = True - @permission(PERMS.LOGS) def getLog(self, offset=0): """Returns most recent log entries. @@ -259,7 +240,7 @@ class Api(Iface): except: return ['No log available'] - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def isTimeDownload(self): """Checks if pyload will start new downloads according to time in config. @@ -269,7 +250,7 @@ class Api(Iface): end = self.core.config['downloadTime']['end'].split(":") return compare_time(start, end) - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def isTimeReconnect(self): """Checks if pyload will try to make a reconnect @@ -283,7 +264,7 @@ class Api(Iface): def scanDownloadFolder(self): pass - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def getProgressInfo(self): """ Status of all currently running tasks @@ -306,7 +287,6 @@ class Api(Iface): # Configuration ########################## - @permission(PERMS.SETTINGS) def getConfigValue(self, section, option): """Retrieve config value. @@ -317,7 +297,6 @@ class Api(Iface): value = self.core.config.get(section, option) return to_string(value) - @permission(PERMS.SETTINGS) def setConfigValue(self, section, option, value): """Set new config value. @@ -330,7 +309,6 @@ class Api(Iface): self.core.config.set(section, option, value) - @permission(PERMS.SETTINGS) def getConfig(self): """Retrieves complete config of core. @@ -343,7 +321,6 @@ class Api(Iface): section, data in self.core.config.getBaseSections()]) - @permission(PERMS.SETTINGS) def getPluginConfig(self): """Retrieves complete config for all plugins. @@ -353,7 +330,6 @@ class Api(Iface): data.name, data.description, data.long_desc)) for section, data in self.core.config.getPluginSections()]) - @permission(PERMS.SETTINGS) def configureSection(self, section): data = self.core.config.config[section] sec = ConfigSection(section, data.name, data.description, data.long_desc) @@ -367,7 +343,6 @@ class Api(Iface): return sec - @permission(PERMS.SETTINGS) def setConfigHandler(self, plugin, iid, value): pass @@ -379,7 +354,7 @@ class Api(Iface): # Download Preparing ########################## - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def parseURLs(self, html=None, url=None): """Parses html content or any arbitrary text for links and returns result of `checkURLs` @@ -399,7 +374,7 @@ class Api(Iface): return self.checkURLs(set(urls)) - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def checkURLs(self, urls): """ Gets urls and returns pluginname mapped to list of matching urls. @@ -417,7 +392,7 @@ class Api(Iface): return plugins - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def checkOnlineStatus(self, urls): """ initiates online status check, will also decrypt files. @@ -441,7 +416,7 @@ class Api(Iface): return OnlineCheck(rid, result) - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def checkOnlineStatusContainer(self, urls, container, data): """ checks online status of urls and a submitted container file @@ -456,7 +431,7 @@ class Api(Iface): urls.append(th.name) return self.checkOnlineStatus(urls) - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def pollResults(self, rid): """ Polls the result available for ResultID @@ -472,7 +447,7 @@ class Api(Iface): return OnlineCheck(rid, result) - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def generatePackages(self, links): """ Parses links, generates packages names from urls @@ -486,7 +461,7 @@ class Api(Iface): # Adding/Deleting ########################## - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def generateAndAddPackages(self, links, paused=False): """Generates and add packages @@ -497,11 +472,11 @@ class Api(Iface): return [self.addPackageP(name, urls, "", paused) for name, urls in self.generatePackages(links).iteritems()] - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def autoAddLinks(self, links): pass - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def createPackage(self, name, folder, root, password="", site="", comment="", paused=False): """Create a new package. @@ -526,7 +501,7 @@ class Api(Iface): return pid - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def addPackage(self, name, links, password=""): """Convenient method to add a package to the top-level and for adding links. @@ -534,12 +509,12 @@ class Api(Iface): """ self.addPackageChild(name, links, password, -1, False) - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def addPackageP(self, name, links, password, paused): """ Same as above with additional paused attribute. """ self.addPackageChild(name, links, password, -1, paused) - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def addPackageChild(self, name, links, password, root, paused): """Adds a package, with links to desired package. @@ -556,7 +531,7 @@ class Api(Iface): return pid - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def addLinks(self, pid, links): """Adds links to specific package. Initiates online status fetching. @@ -574,7 +549,7 @@ class Api(Iface): self.core.log.info((_("Added %d links to package") + " #%d" % pid) % len(hoster)) self.core.files.save() - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def uploadContainer(self, filename, data): """Uploads and adds a container file to pyLoad. @@ -587,7 +562,7 @@ class Api(Iface): return self.addPackage(th.name, [th.name]) - @permission(PERMS.DELETE) + @RequirePerm(Permission.Delete) def deleteFiles(self, fids): """Deletes several file entries from pyload. @@ -598,7 +573,7 @@ class Api(Iface): self.core.files.save() - @permission(PERMS.DELETE) + @RequirePerm(Permission.Delete) def deletePackages(self, pids): """Deletes packages and containing links. @@ -613,27 +588,27 @@ class Api(Iface): # Collector ########################## - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def getCollector(self): pass - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def addToCollector(self, links): pass - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def addFromCollector(self, name, paused): pass - @permission(PERMS.DELETE) + @RequirePerm(Permission.Delete) def deleteCollPack(self, name): pass - @permission(PERMS.DELETE) + @RequirePerm(Permission.Delete) def deleteCollLink(self, url): pass - @permission(PERMS.ADD) + @RequirePerm(Permission.Add) def renameCollPack(self, name, new_name): pass @@ -641,17 +616,17 @@ class Api(Iface): # File Information retrival ############################# - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def getAllFiles(self): """ same as `getFileTree` for toplevel root and full tree""" return self.getFileTree(-1, True) - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def getAllUnfinishedFiles(self): """ same as `getUnfinishedFileTree for toplevel root and full tree""" return self.getUnfinishedFileTree(-1, True) - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def getFileTree(self, pid, full): """ Retrieve data for specific package. full=True will retrieve all data available and can result in greater delays. @@ -662,7 +637,7 @@ class Api(Iface): """ return self.core.files.getView(pid, full, False) - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def getUnfinishedFileTree(self, pid, full): """ Same as `getFileTree` but only contains unfinished files. @@ -672,12 +647,12 @@ class Api(Iface): """ return self.core.files.getView(pid, full, False) - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def getPackageContent(self, pid): """ Only retrieve content of a specific package. see `getFileTree`""" return self.getFileTree(pid, False) - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def getPackageInfo(self, pid): """Returns information about package, without detailed information about containing files @@ -690,7 +665,7 @@ class Api(Iface): raise PackageDoesNotExists(pid) return info - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def getFileInfo(self, fid): """ Info for specific file @@ -704,7 +679,7 @@ class Api(Iface): raise FileDoesNotExists(fid) return info - @permission(PERMS.LIST) + @RequirePerm(Permission.List) def findFiles(self, pattern): pass @@ -712,7 +687,7 @@ class Api(Iface): # Modify Downloads ############################# - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def restartPackage(self, pid): """Restarts a package, resets every containing files. @@ -720,7 +695,7 @@ class Api(Iface): """ self.core.files.restartPackage(pid) - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def restartFile(self, fid): """Resets file status, so it will be downloaded again. @@ -728,12 +703,12 @@ class Api(Iface): """ self.core.files.restartFile(fid) - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def recheckPackage(self, pid): """Check online status of all files in a package, also a default action when package is added. """ self.core.files.reCheckPackage(pid) - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def stopAllDownloads(self): """Aborts all running downloads.""" @@ -741,7 +716,7 @@ class Api(Iface): for pyfile in pyfiles: pyfile.abortDownload() - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def stopDownloads(self, fids): """Aborts specific downloads. @@ -753,7 +728,7 @@ class Api(Iface): if pyfile.id in fids: pyfile.abortDownload() - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def restartFailed(self): """Restarts all failed failes.""" self.core.files.restartFailed() @@ -762,19 +737,19 @@ class Api(Iface): # Modify Files/Packages ############################# - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def setFilePaused(self, fid, paused): pass - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def setPackagePaused(self, pid, paused): pass - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def setPackageFolder(self, pid, path): pass - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def movePackage(self, pid, root): """ Set a new root for specific package. This will also moves the files on disk\ and will only work when no file is currently downloading. @@ -786,7 +761,7 @@ class Api(Iface): """ return self.core.files.movePackage(pid, root) - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def moveFiles(self, fids, pid): """Move multiple files to another package. This will move the files on disk and\ only work when files are not downloading. All files needs to be continuous ordered @@ -798,7 +773,7 @@ class Api(Iface): """ return self.core.files.moveFiles(fids, pid) - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def orderPackage(self, pid, position): """Set new position for a package. @@ -807,7 +782,7 @@ class Api(Iface): """ self.core.files.orderPackage(pid, position) - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def orderFiles(self, fids, pid, position): """ Set a new position for a bunch of files within a package. All files have to be in the same package and must be **continuous**\ @@ -819,7 +794,7 @@ class Api(Iface): """ self.core.files.orderFiles(fids, pid, position) - @permission(PERMS.MODIFY) + @RequirePerm(Permission.Modify) def setPackageData(self, pid, data): """Allows to modify several package attributes. @@ -840,7 +815,7 @@ class Api(Iface): # User Interaction ############################# - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def isInteractionWaiting(self, mode): """ Check if task is waiting. @@ -849,7 +824,7 @@ class Api(Iface): """ return self.core.interactionManager.isTaskWaiting(mode) - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def getInteractionTask(self, mode): """Retrieve task for specific mode. @@ -860,7 +835,7 @@ class Api(Iface): return InteractionTask(-1) if not task else task - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def setInteractionResult(self, iid, result): """Set Result for a interaction task. It will be immediately removed from task queue afterwards @@ -871,7 +846,7 @@ class Api(Iface): if task: task.setResult(result) - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def getNotifications(self): """List of all available notifcations. They stay in queue for some time, client should\ save which notifications it already has seen. @@ -880,15 +855,15 @@ class Api(Iface): """ return self.core.interactionManager.getNotifications() - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def getAddonHandler(self): pass - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def callAddonHandler(self, plugin, func, pid_or_fid): pass - @permission(PERMS.DOWNLOAD) + @RequirePerm(Permission.Download) def generateDownloadLink(self, fid, timeout): pass @@ -896,7 +871,7 @@ class Api(Iface): # Event Handling ############################# - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def getEvents(self, uuid): """Lists occured events, may be affected to changes in future. @@ -910,7 +885,7 @@ class Api(Iface): # Account Methods ############################# - @permission(PERMS.ACCOUNTS) + @RequirePerm(Permission.Accounts) def getAccounts(self, refresh): """Get information about all entered accounts. @@ -924,7 +899,7 @@ class Api(Iface): return accounts - @permission(PERMS.ALL) + @RequirePerm(Permission.All) def getAccountTypes(self): """All available account types. @@ -932,12 +907,12 @@ class Api(Iface): """ return self.core.pluginManager.getPlugins("accounts").keys() - @permission(PERMS.ACCOUNTS) + @RequirePerm(Permission.Accounts) def updateAccount(self, plugin, account, password=None, options={}): """Changes pw/options for specific account.""" self.core.accountManager.updateAccount(plugin, account, password, options) - @permission(PERMS.ACCOUNTS) + @RequirePerm(Permission.Accounts) def removeAccount(self, plugin, account): """Remove account from pyload. @@ -950,7 +925,7 @@ class Api(Iface): # Auth+User Information ############################# - @permission(PERMS.ALL) + @RequirePerm(Permission.All) def login(self, username, password, remoteip=None): """Login into pyLoad, this **must** be called when using rpc before any methods can be used. @@ -993,7 +968,7 @@ class Api(Iface): return False - @permission(PERMS.ALL) + @RequirePerm(Permission.All) def getUserData(self, username, password): """similar to `checkAuth` but returns UserData thrift type """ user = self.checkAuth(username, password) @@ -1022,7 +997,7 @@ class Api(Iface): # RPC Plugin Methods ############################# - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def getServices(self): """ A dict of available services, these can be defined by addon plugins. @@ -1034,11 +1009,11 @@ class Api(Iface): return data - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def hasService(self, plugin, func): pass - @permission(PERMS.INTERACTION) + @RequirePerm(Permission.Interaction) def call(self, plugin, func, arguments): """Calls a service (a method in addon plugin). @@ -1054,7 +1029,7 @@ class Api(Iface): except Exception, e: raise ServiceException(e.message) - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def getAllInfo(self): """Returns all information stored by addon plugins. Values are always strings @@ -1062,7 +1037,7 @@ class Api(Iface): """ return self.core.addonManager.getAllInfo() - @permission(PERMS.STATUS) + @RequirePerm(Permission.Status) def getInfoByPlugin(self, plugin): """Returns information stored by a specific plugin. -- cgit v1.2.3 From 0d2d6daef850ac6bcc7fafccd230e52d2a862c2c Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sun, 3 Jun 2012 17:45:10 +0200 Subject: updates for database + api --- module/Api.py | 60 +++++++++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index bab039ea1..388dfd283 100644 --- a/module/Api.py +++ b/module/Api.py @@ -1,21 +1,20 @@ #!/usr/bin/env python # -*- 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: RaNaN -""" +############################################################################### +# Copyright(c) 2008-2012 pyLoad Team +# http://www.pyload.org +# +# This file is part of pyLoad. +# pyLoad is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# Subjected to the terms and conditions in LICENSE +# +# @author: RaNaN +############################################################################### import re from os.path import join, isabs @@ -38,8 +37,8 @@ if activated: else: from remote.socketbackend.ttypes import * -from PyFile import PyFile -from utils import compare_time, to_string, bits_set +from datatypes import PyFile +from utils import compare_time, to_string, bits_set, get_index from utils.fs import free_space from common.packagetools import parseNames from network.RequestFactory import getURL @@ -60,7 +59,7 @@ def RequirePerm(bits): return _Dec -# we will bytehacking the method to add user as keyword argument +# we will byte-hacking the method to add user as keyword argument class UserContext(object): """Decorator to mark methods that require a specific user""" @@ -68,7 +67,7 @@ class UserContext(object): fc = f.func_code try: - i = fc.co_names.index("user") + i = get_index(fc.co_names, "user") except ValueError: # functions does not uses user, so no need to modify return f @@ -119,6 +118,7 @@ class UserApi(object): return f +# TODO: fix permissions, user context manager class Api(Iface): """ @@ -144,7 +144,7 @@ class Api(Iface): print self.t.getServerVersion() - # TODO, create user instance, work + # TODO, create user instance def withUserContext(self, user): """ Returns a proxy version of the api, to call method in user context @@ -165,7 +165,7 @@ class Api(Iface): print user return self.core.version - @RequirePerm(Permission.List) + @RequirePerm(Permission.Status) def statusServer(self): """Some general information about the current status of pyLoad. @@ -588,7 +588,7 @@ class Api(Iface): # Collector ########################## - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def getCollector(self): pass @@ -616,17 +616,17 @@ class Api(Iface): # File Information retrival ############################# - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def getAllFiles(self): """ same as `getFileTree` for toplevel root and full tree""" return self.getFileTree(-1, True) - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def getAllUnfinishedFiles(self): """ same as `getUnfinishedFileTree for toplevel root and full tree""" return self.getUnfinishedFileTree(-1, True) - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def getFileTree(self, pid, full): """ Retrieve data for specific package. full=True will retrieve all data available and can result in greater delays. @@ -637,7 +637,7 @@ class Api(Iface): """ return self.core.files.getView(pid, full, False) - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def getUnfinishedFileTree(self, pid, full): """ Same as `getFileTree` but only contains unfinished files. @@ -647,12 +647,12 @@ class Api(Iface): """ return self.core.files.getView(pid, full, False) - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def getPackageContent(self, pid): """ Only retrieve content of a specific package. see `getFileTree`""" return self.getFileTree(pid, False) - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def getPackageInfo(self, pid): """Returns information about package, without detailed information about containing files @@ -665,7 +665,7 @@ class Api(Iface): raise PackageDoesNotExists(pid) return info - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def getFileInfo(self, fid): """ Info for specific file @@ -679,7 +679,7 @@ class Api(Iface): raise FileDoesNotExists(fid) return info - @RequirePerm(Permission.List) + @RequirePerm(Permission.All) def findFiles(self, pattern): pass -- cgit v1.2.3 From 941e3021000e59020f66419cc2156aee30972121 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Mon, 13 Aug 2012 17:40:10 +0200 Subject: working login --- module/Api.py | 63 ++++++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 31 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 388dfd283..26a6c757f 100644 --- a/module/Api.py +++ b/module/Api.py @@ -37,7 +37,7 @@ if activated: else: from remote.socketbackend.ttypes import * -from datatypes import PyFile +from datatypes.PyFile import PyFile from utils import compare_time, to_string, bits_set, get_index from utils.fs import free_space from common.packagetools import parseNames @@ -103,21 +103,26 @@ urlmatcher = re.compile(r"((https?|ftps?|xdcc|sftp):((//)|(\\\\))+[\w\d:#@%/;$() def has_permission(userPermission, Permission): return bits_set(Permission, userPermission) +from datatypes.User import User class UserApi(object): """ Proxy object for api that provides all methods in user context """ def __init__(self, api, user): self.api = api - self.user = user + self._user = user def __getattr__(self, item): f = self.api.__getattribute__(item) if f.func_name in user_context: - return partial(f, user=self.user) + return partial(f, user=self._user) return f + @property + def user(self): + return self._user + # TODO: fix permissions, user context manager class Api(Iface): @@ -138,31 +143,31 @@ class Api(Iface): def __init__(self, core): self.core = core + self.user_apis = {} - self.t = self.withUserContext("TestUser") - - print self.t.getServerVersion() - - # TODO, create user instance - def withUserContext(self, user): + def withUserContext(self, uid): """ Returns a proxy version of the api, to call method in user context - :param user: user id + :param uid: user or userData instance or uid :return: :class:`UserApi` """ - return UserApi(self, user) + if uid not in self.user_apis: + user = self.core.db.getUserData(uid=uid) + if not user: #TODO: anonymous user? + return None + + self.user_apis[uid] = UserApi(self, User.fromUserData(self, user)) + return self.user_apis[uid] ########################## # Server Status ########################## - @UserContext #TODO: only for testing @RequirePerm(Permission.All) def getServerVersion(self): """pyLoad Core version """ - print user return self.core.version @RequirePerm(Permission.Status) @@ -873,7 +878,7 @@ class Api(Iface): @RequirePerm(Permission.Status) def getEvents(self, uuid): - """Lists occured events, may be affected to changes in future. + """Lists occurred events, may be affected to changes in future. :param uuid: self assigned string uuid which has to be unique :return: list of `Events` @@ -925,6 +930,8 @@ class Api(Iface): # Auth+User Information ############################# + # TODO + @RequirePerm(Permission.All) def login(self, username, password, remoteip=None): """Login into pyLoad, this **must** be called when using rpc before any methods can be used. @@ -946,48 +953,42 @@ class Api(Iface): """ if self.core.config["remote"]["nolocalauth"] and remoteip == "127.0.0.1": return "local" - if self.core.startedInGui and remoteip == "127.0.0.1": - return "local" self.core.log.info(_("User '%s' tried to log in") % username) return self.core.db.checkAuth(username, password) - def isAuthorized(self, func, userdata): + def isAuthorized(self, func, user): """checks if the user is authorized for specific method :param func: function name - :param userdata: dictionary of user data + :param user: `User` :return: boolean """ - if userdata == "local" or userdata["role"] == ROLE.ADMIN: + if user.isAdmin(): return True - elif func in perm_map and has_permission(userdata["permission"], perm_map[func]): + elif func in perm_map and user.hasPermission(perm_map[func]): return True else: return False - + # TODO @RequirePerm(Permission.All) def getUserData(self, username, password): """similar to `checkAuth` but returns UserData thrift type """ user = self.checkAuth(username, password) - if user: - return UserData(user["name"], user["email"], user["role"], user["permission"], user["template"]) + if not user: + raise UserDoesNotExists(username) - raise UserDoesNotExists(username) + return user.toUserData() def getAllUserData(self): """returns all known user and info""" - res = {} - for user, data in self.core.db.getAllUserData().iteritems(): - res[user] = UserData(user, data["email"], data["role"], data["permission"], data["template"]) - - return res + return self.core.db.getAllUserData() - def changePassword(self, user, oldpw, newpw): + def changePassword(self, username, oldpw, newpw): """ changes password for specific user """ - return self.core.db.changePassword(user, oldpw, newpw) + return self.core.db.changePassword(username, oldpw, newpw) def setUserPermission(self, user, permission, role): self.core.db.setPermission(user, permission) -- cgit v1.2.3 From cbd4f4b5375f89362733e10a9b98e5a74c2a5734 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Fri, 31 Aug 2012 23:25:26 +0200 Subject: first js models/views --- module/Api.py | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 26a6c757f..4671b9cbd 100644 --- a/module/Api.py +++ b/module/Api.py @@ -170,7 +170,7 @@ class Api(Iface): """pyLoad Core version """ return self.core.version - @RequirePerm(Permission.Status) + @RequirePerm(Permission.All) def statusServer(self): """Some general information about the current status of pyLoad. @@ -186,17 +186,16 @@ class Api(Iface): return serverStatus - @RequirePerm(Permission.Status) + # TODO: user sensitive pausing, now only available for admins + def pauseServer(self): """Pause server: It won't start any new downloads, but nothing gets aborted.""" self.core.threadManager.pause = True - @RequirePerm(Permission.Status) def unpauseServer(self): """Unpause server: New Downloads will be started.""" self.core.threadManager.pause = False - @RequirePerm(Permission.Status) def togglePause(self): """Toggle pause state. @@ -205,7 +204,6 @@ class Api(Iface): self.core.threadManager.pause ^= True return self.core.threadManager.pause - @RequirePerm(Permission.Status) def toggleReconnect(self): """Toggle reconnect activation. @@ -214,7 +212,6 @@ class Api(Iface): self.core.config["reconnect"]["activated"] ^= True return self.core.config["reconnect"]["activated"] - @RequirePerm(Permission.Status) def freeSpace(self): """Available free space at download directory in bytes""" return free_space(self.core.config["general"]["download_folder"]) @@ -245,7 +242,7 @@ class Api(Iface): except: return ['No log available'] - @RequirePerm(Permission.Status) + @RequirePerm(Permission.All) def isTimeDownload(self): """Checks if pyload will start new downloads according to time in config. @@ -255,7 +252,7 @@ class Api(Iface): end = self.core.config['downloadTime']['end'].split(":") return compare_time(start, end) - @RequirePerm(Permission.Status) + @RequirePerm(Permission.All) def isTimeReconnect(self): """Checks if pyload will try to make a reconnect @@ -269,7 +266,7 @@ class Api(Iface): def scanDownloadFolder(self): pass - @RequirePerm(Permission.Status) + @RequirePerm(Permission.All) def getProgressInfo(self): """ Status of all currently running tasks @@ -512,12 +509,12 @@ class Api(Iface): :return: package id """ - self.addPackageChild(name, links, password, -1, False) + return self.addPackageChild(name, links, password, -1, False) @RequirePerm(Permission.Add) def addPackageP(self, name, links, password, paused): """ Same as above with additional paused attribute. """ - self.addPackageChild(name, links, password, -1, paused) + return self.addPackageChild(name, links, password, -1, paused) @RequirePerm(Permission.Add) def addPackageChild(self, name, links, password, root, paused): @@ -618,7 +615,7 @@ class Api(Iface): pass ############################# - # File Information retrival + # File Information retrieval ############################# @RequirePerm(Permission.All) @@ -638,9 +635,9 @@ class Api(Iface): :param pid: package id :param full: go down the complete tree or only the first layer - :return: :class:`PackageView` + :return: :class:`TreeCollection` """ - return self.core.files.getView(pid, full, False) + return self.core.files.getTree(pid, full, False) @RequirePerm(Permission.All) def getUnfinishedFileTree(self, pid, full): @@ -648,9 +645,9 @@ class Api(Iface): :param pid: package id :param full: go down the complete tree or only the first layer - :return: :class:`PackageView` + :return: :class:`TreeCollection` """ - return self.core.files.getView(pid, full, False) + return self.core.files.getTree(pid, full, False) @RequirePerm(Permission.All) def getPackageContent(self, pid): @@ -876,13 +873,13 @@ class Api(Iface): # Event Handling ############################# - @RequirePerm(Permission.Status) def getEvents(self, uuid): """Lists occurred events, may be affected to changes in future. :param uuid: self assigned string uuid which has to be unique :return: list of `Events` """ + # TODO: permissions? # TODO pass @@ -1030,7 +1027,8 @@ class Api(Iface): except Exception, e: raise ServiceException(e.message) - @RequirePerm(Permission.Status) + + #TODO: permissions def getAllInfo(self): """Returns all information stored by addon plugins. Values are always strings @@ -1038,7 +1036,6 @@ class Api(Iface): """ return self.core.addonManager.getAllInfo() - @RequirePerm(Permission.Status) def getInfoByPlugin(self, plugin): """Returns information stored by a specific plugin. -- cgit v1.2.3 From 560958b70043ea5b7e0e32d41cb51bd44696d775 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Sun, 9 Sep 2012 15:39:50 +0200 Subject: new config api --- module/Api.py | 72 ++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 30 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 4671b9cbd..d530556fa 100644 --- a/module/Api.py +++ b/module/Api.py @@ -123,8 +123,6 @@ class UserApi(object): def user(self): return self._user -# TODO: fix permissions, user context manager - class Api(Iface): """ **pyLoads API** @@ -263,9 +261,6 @@ class Api(Iface): return compare_time(start, end) and self.core.config["reconnect"]["activated"] - def scanDownloadFolder(self): - pass - @RequirePerm(Permission.All) def getProgressInfo(self): """ Status of all currently running tasks @@ -314,43 +309,64 @@ class Api(Iface): def getConfig(self): """Retrieves complete config of core. - :return: list of `ConfigSection` + :return: map of `ConfigHolder` """ - return dict([(section, ConfigSection(section, data.name, data.description, data.long_desc, [ + # TODO + return dict([(section, ConfigHolder(section, data.name, data.description, data.long_desc, [ ConfigItem(option, d.name, d.description, d.type, to_string(d.default), to_string(self.core.config.get(section, option))) for option, d in data.config.iteritems()])) for section, data in self.core.config.getBaseSections()]) - def getPluginConfig(self): - """Retrieves complete config for all plugins. + def getConfigRef(self): + """Config instance, not for RPC""" + return self.core.config + + def getGlobalPlugins(self): + """All global plugins/addons, only admin can use this - :return: list of `ConfigSection` + :return: list of `ConfigInfo` """ - return dict([(section, ConfigSection(section, - data.name, data.description, data.long_desc)) for - section, data in self.core.config.getPluginSections()]) + pass - def configureSection(self, section): - data = self.core.config.config[section] - sec = ConfigSection(section, data.name, data.description, data.long_desc) - sec.items = [ConfigItem(option, d.name, d.description, - d.type, to_string(d.default), to_string(self.core.config.get(section, option))) - for - option, d in data.config.iteritems()] + @UserContext + @RequirePerm(Permission.Plugins) + def getUserPlugins(self): + """List of plugins every user can configure for himself - #TODO: config handler + :return: list of `ConfigInfo` + """ + pass - return sec + @UserContext + @RequirePerm(Permission.Plugins) + def configurePlugin(self, plugin): + """Get complete config options for an plugin + :param plugin: Name of the plugin to configure + :return: :class:`ConfigHolder` + """ - def setConfigHandler(self, plugin, iid, value): pass - def getConfigRef(self): - """Config instance, not for RPC""" - return self.core.config + @UserContext + @RequirePerm(Permission.Plugins) + def saveConfig(self, config): + """Used to save a configuration, core config can only be saved by admins + + :param config: :class:`ConfigHolder + """ + pass + + @UserContext + @RequirePerm(Permission.Plugins) + def deleteConfig(self, config): + pass + + @RequirePerm(Permission.Plugins) + def setConfigHandler(self, plugin, iid, value): + pass ########################## # Download Preparing @@ -474,10 +490,6 @@ class Api(Iface): return [self.addPackageP(name, urls, "", paused) for name, urls in self.generatePackages(links).iteritems()] - @RequirePerm(Permission.Add) - def autoAddLinks(self, links): - pass - @RequirePerm(Permission.Add) def createPackage(self, name, folder, root, password="", site="", comment="", paused=False): """Create a new package. -- cgit v1.2.3 From 54bc92b4c5e0b3543a313f497cbc2276403c5980 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Mon, 10 Sep 2012 11:49:35 +0200 Subject: changed config + progress api --- module/Api.py | 49 +++++++++++++++++++------------------------------ 1 file changed, 19 insertions(+), 30 deletions(-) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index d530556fa..182024775 100644 --- a/module/Api.py +++ b/module/Api.py @@ -184,8 +184,6 @@ class Api(Iface): return serverStatus - # TODO: user sensitive pausing, now only available for admins - def pauseServer(self): """Pause server: It won't start any new downloads, but nothing gets aborted.""" self.core.threadManager.pause = True @@ -260,25 +258,13 @@ class Api(Iface): end = self.core.config['reconnect']['endTime'].split(":") return compare_time(start, end) and self.core.config["reconnect"]["activated"] - @RequirePerm(Permission.All) def getProgressInfo(self): """ Status of all currently running tasks :return: list of `ProgressInfo` """ - data = [] - for pyfile in self.core.threadManager.getActiveFiles(): - if not isinstance(pyfile, PyFile): - continue - - data.append(ProgressInfo( - pyfile.id, pyfile.name, pyfile.getSpeed(), pyfile.getETA(), pyfile.formatETA(), - pyfile.getBytesLeft(), pyfile.getSize(), pyfile.formatSize(), pyfile.getPercent(), - pyfile.status, pyfile.getStatusName(), pyfile.formatWait(), - pyfile.waitUntil, pyfile.packageid, pyfile.package().name, pyfile.pluginname)) - - return data + pass ########################## # Configuration @@ -361,7 +347,12 @@ class Api(Iface): @UserContext @RequirePerm(Permission.Plugins) - def deleteConfig(self, config): + def deleteConfig(self, plugin): + """Deletes modified config + + :param plugin: plugin name + :return: + """ pass @RequirePerm(Permission.Plugins) @@ -611,21 +602,21 @@ class Api(Iface): pass @RequirePerm(Permission.Add) - def addFromCollector(self, name, paused): + def addFromCollector(self, name, new_name): pass @RequirePerm(Permission.Delete) def deleteCollPack(self, name): pass - @RequirePerm(Permission.Delete) - def deleteCollLink(self, url): - pass - @RequirePerm(Permission.Add) def renameCollPack(self, name, new_name): pass + @RequirePerm(Permission.Delete) + def deleteCollLink(self, url): + pass + ############################# # File Information retrieval ############################# @@ -722,6 +713,11 @@ class Api(Iface): """Check online status of all files in a package, also a default action when package is added. """ self.core.files.reCheckPackage(pid) + @RequirePerm(Permission.Modify) + def restartFailed(self): + """Restarts all failed failes.""" + self.core.files.restartFailed() + @RequirePerm(Permission.Modify) def stopAllDownloads(self): """Aborts all running downloads.""" @@ -742,19 +738,10 @@ class Api(Iface): if pyfile.id in fids: pyfile.abortDownload() - @RequirePerm(Permission.Modify) - def restartFailed(self): - """Restarts all failed failes.""" - self.core.files.restartFailed() - ############################# # Modify Files/Packages ############################# - @RequirePerm(Permission.Modify) - def setFilePaused(self, fid, paused): - pass - @RequirePerm(Permission.Modify) def setPackagePaused(self, pid, paused): pass @@ -1007,6 +994,8 @@ class Api(Iface): # RPC Plugin Methods ############################# + # TODO: obsolete + @RequirePerm(Permission.Interaction) def getServices(self): """ A dict of available services, these can be defined by addon plugins. -- cgit v1.2.3 From 1a55cb6a2eb8784253410b2e93510b5bcebf7f41 Mon Sep 17 00:00:00 2001 From: RaNaN <Mast3rRaNaN@hotmail.de> Date: Mon, 10 Sep 2012 15:12:55 +0200 Subject: userApi for plugins --- module/Api.py | 3 +++ 1 file changed, 3 insertions(+) (limited to 'module/Api.py') diff --git a/module/Api.py b/module/Api.py index 182024775..dec1526b2 100644 --- a/module/Api.py +++ b/module/Api.py @@ -150,6 +150,9 @@ class Api(Iface): :param uid: user or userData instance or uid :return: :class:`UserApi` """ + if isinstance(uid, User): + uid = uid.uid + if uid not in self.user_apis: user = self.core.db.getUserData(uid=uid) if not user: #TODO: anonymous user? -- cgit v1.2.3