summaryrefslogtreecommitdiffstats
path: root/pyload/config
diff options
context:
space:
mode:
Diffstat (limited to 'pyload/config')
-rw-r--r--pyload/config/ConfigManager.py139
-rw-r--r--pyload/config/ConfigParser.py207
-rw-r--r--pyload/config/__init__.py1
-rw-r--r--pyload/config/default.py107
4 files changed, 454 insertions, 0 deletions
diff --git a/pyload/config/ConfigManager.py b/pyload/config/ConfigManager.py
new file mode 100644
index 000000000..f9dc3795b
--- /dev/null
+++ b/pyload/config/ConfigManager.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from new_collections import OrderedDict
+
+from pyload.Api import InvalidConfigSection
+from pyload.utils import from_string, json
+
+from ConfigParser import ConfigParser
+
+
+def convertKeyError(func):
+ """ converts KeyError into InvalidConfigSection """
+
+ def conv(*args, **kwargs):
+ try:
+ return func(*args, **kwargs)
+ except KeyError:
+ raise InvalidConfigSection(args[1])
+
+ return conv
+
+
+class ConfigManager(ConfigParser):
+ """ Manages the core config and configs for addons and single user.
+ Has similar interface to ConfigParser. """
+
+ def __init__(self, core, parser):
+ # No __init__ call to super class is needed!
+
+ self.core = core
+ self.db = core.db
+ # The config parser, holding the core config
+ self.parser = parser
+
+ # similar to parser, separated meta data and values
+ self.config = OrderedDict()
+
+ # Value cache for multiple user configs
+ # Values are populated from db on first access
+ # Entries are saved as (user, section) keys
+ self.values = {}
+ # TODO: similar to a cache, could be deleted periodically
+
+ def save(self):
+ self.parser.save()
+
+ @convertKeyError
+ def get(self, section, option, user=None):
+ """get config value, core config only available for admins.
+ if user is not valid default value will be returned"""
+
+ # Core config loaded from parser, when no user is given or he is admin
+ if section in self.parser and user is None:
+ return self.parser.get(section, option)
+ else:
+ # We need the id and not the instance
+ # Will be None for admin user and so the same as internal access
+ try:
+ # Check if this config exists
+ # Configs without meta data can not be loaded!
+ data = self.config[section].config[option]
+ return self.loadValues(user, section)[option]
+ except KeyError:
+ pass # Returns default value later
+
+ return self.config[section].config[option].default
+
+ def loadValues(self, user, section):
+ if (user, section) not in self.values:
+ conf = self.db.loadConfig(section, user)
+ try:
+ self.values[user, section] = json.loads(conf) if conf else {}
+ except ValueError: # Something did go wrong when parsing
+ self.values[user, section] = {}
+ self.core.print_exc()
+
+ return self.values[user, section]
+
+ @convertKeyError
+ def set(self, section, option, value, sync=True, user=None):
+ """ set config value """
+
+ changed = False
+ if section in self.parser and user is None:
+ changed = self.parser.set(section, option, value, sync)
+ else:
+ data = self.config[section].config[option]
+ value = from_string(value, data.type)
+ old_value = self.get(section, option)
+
+ # Values will always be saved to db, sync is ignored
+ if value != old_value:
+ changed = True
+ self.values[user, section][option] = value
+ if sync: self.saveValues(user, section)
+
+ if changed: self.core.evm.dispatchEvent("config:changed", section, option, value)
+ return changed
+
+ def saveValues(self, user, section):
+ if section in self.parser and user is None:
+ self.save()
+ elif (user, section) in self.values:
+ self.db.saveConfig(section, json.dumps(self.values[user, section]), user)
+
+ def delete(self, section, user=None):
+ """ Deletes values saved in db and cached values for given user, NOT meta data
+ Does not trigger an error when nothing was deleted. """
+ if (user, section) in self.values:
+ del self.values[user, section]
+
+ self.db.deleteConfig(section, user)
+ self.core.evm.dispatchEvent("config:deleted", section, user)
+
+ def iterCoreSections(self):
+ return self.parser.iterSections()
+
+ def iterSections(self, user=None):
+ """ Yields: section, metadata, values """
+ values = self.db.loadConfigsForUser(user)
+
+ # Every section needs to be json decoded
+ for section, data in values.items():
+ try:
+ values[section] = json.loads(data) if data else {}
+ except ValueError:
+ values[section] = {}
+ self.core.print_exc()
+
+ for name, config in self.config.iteritems():
+ yield name, config, values[name] if name in values else {}
+
+ def getSection(self, section, user=None):
+ if section in self.parser and user is None:
+ return self.parser.getSection(section)
+
+ values = self.loadValues(user, section)
+ return self.config.get(section), values
diff --git a/pyload/config/ConfigParser.py b/pyload/config/ConfigParser.py
new file mode 100644
index 000000000..fd22e93b9
--- /dev/null
+++ b/pyload/config/ConfigParser.py
@@ -0,0 +1,207 @@
+# -*- coding: utf-8 -*-
+
+from __future__ import with_statement
+from time import sleep
+from os.path import exists
+from gettext import gettext
+from new_collections import namedtuple, OrderedDict
+
+from pyload.utils import from_string
+from pyload.utils.fs import chmod
+
+from default import make_config
+
+CONF_VERSION = 2
+SectionTuple = namedtuple("SectionTuple", "name description long_desc config")
+ConfigData = namedtuple("ConfigData", "name type description default")
+
+class ConfigParser:
+ """
+ Holds and manages the configuration + meta data for config read from file.
+ """
+
+ CONFIG = "pyload.conf"
+
+ def __init__(self, config=None):
+
+ if config: self.CONFIG = config
+
+ # Meta data information
+ self.config = OrderedDict()
+ # The actual config values
+ self.values = {}
+
+ self.checkVersion()
+
+ self.loadDefault()
+ self.parseValues(self.CONFIG)
+
+ def loadDefault(self):
+ make_config(self)
+
+ def checkVersion(self):
+ """Determines if config needs to be deleted"""
+ e = None
+ # workaround conflict, with GUI (which also accesses the config) so try read in 3 times
+ for i in range(0, 3):
+ try:
+ if exists(self.CONFIG):
+ f = open(self.CONFIG, "rb")
+ v = f.readline()
+ f.close()
+ v = v[v.find(":") + 1:].strip()
+
+ if not v or int(v) < CONF_VERSION:
+ f = open(self.CONFIG, "wb")
+ f.write("version: " + str(CONF_VERSION))
+ f.close()
+ print "Old version of %s deleted" % self.CONFIG
+ else:
+ f = open(self.CONFIG, "wb")
+ f.write("version:" + str(CONF_VERSION))
+ f.close()
+
+ except Exception, ex:
+ e = ex
+ sleep(0.3)
+ if e: raise e
+
+
+ def parseValues(self, filename):
+ """read config values from file"""
+ f = open(filename, "rb")
+ config = f.readlines()[1:]
+
+ # save the current section
+ section = ""
+
+ for line in config:
+ line = line.strip()
+
+ # comment line, different variants
+ if not line or line.startswith("#") or line.startswith("//") or line.startswith(";"): continue
+
+ if line.startswith("["):
+ section = line.replace("[", "").replace("]", "")
+
+ if section not in self.config:
+ print "Unrecognized section", section
+ section = ""
+
+ else:
+ name, non, value = line.rpartition("=")
+ name = name.strip()
+ value = value.strip()
+
+ if not section:
+ print "Value without section", name
+ continue
+
+ if name in self.config[section].config:
+ self.set(section, name, value, sync=False)
+ else:
+ print "Unrecognized option", section, name
+
+
+ def save(self):
+ """saves config to filename"""
+
+ configs = []
+ f = open(self.CONFIG, "wb")
+ configs.append(f)
+ chmod(self.CONFIG, 0600)
+ f.write("version: %i\n\n" % CONF_VERSION)
+
+ for section, data in self.config.iteritems():
+ f.write("[%s]\n" % section)
+
+ for option, data in data.config.iteritems():
+ value = self.get(section, option)
+ if type(value) == unicode: value = value.encode("utf8")
+ else: value = str(value)
+
+ f.write('%s = %s\n' % (option, value))
+
+ f.write("\n")
+
+ f.close()
+
+ def __getitem__(self, section):
+ """provides dictionary like access: c['section']['option']"""
+ return Section(self, section)
+
+ def __contains__(self, section):
+ """ checks if parser contains section """
+ return section in self.config
+
+ def get(self, section, option):
+ """get value or default"""
+ try:
+ return self.values[section][option]
+ except KeyError:
+ return self.config[section].config[option].default
+
+ def set(self, section, option, value, sync=True):
+ """set value"""
+
+ data = self.config[section].config[option]
+ value = from_string(value, data.type)
+ old_value = self.get(section, option)
+
+ # only save when different values
+ if value != old_value:
+ if section not in self.values: self.values[section] = {}
+ self.values[section][option] = value
+ if sync:
+ self.save()
+ return True
+
+ return False
+
+ def getMetaData(self, section, option):
+ """ get all config data for an option """
+ return self.config[section].config[option]
+
+ def iterSections(self):
+ """ Yields section, config info, values, for all sections """
+
+ for name, config in self.config.iteritems():
+ yield name, config, self.values[name] if name in self.values else {}
+
+ def getSection(self, section):
+ """ Retrieves single config as tuple (section, values) """
+ return self.config[section], self.values[section] if section in self.values else {}
+
+ def addConfigSection(self, section, name, desc, long_desc, config):
+ """Adds a section to the config. `config` is a list of config tuples as used in plugin api defined as:
+ The order of the config elements is preserved with OrderedDict
+ """
+ d = OrderedDict()
+
+ for entry in config:
+ if len(entry) == 5:
+ conf_name, type, conf_desc, conf_verbose, default = entry
+ else: # config options without description
+ conf_name, type, conf_desc, default = entry
+ conf_verbose = ""
+
+ d[conf_name] = ConfigData(gettext(conf_desc), type, gettext(conf_verbose), from_string(default, type))
+
+ data = SectionTuple(gettext(name), gettext(desc), gettext(long_desc), d)
+ self.config[section] = data
+
+class Section:
+ """provides dictionary like access for configparser"""
+
+ def __init__(self, parser, section):
+ """Constructor"""
+ self.parser = parser
+ self.section = section
+
+ def __getitem__(self, item):
+ """getitem"""
+ return self.parser.get(self.section, item)
+
+ def __setitem__(self, item, value):
+ """setitem"""
+ self.parser.set(self.section, item, value)
diff --git a/pyload/config/__init__.py b/pyload/config/__init__.py
new file mode 100644
index 000000000..4b31e848b
--- /dev/null
+++ b/pyload/config/__init__.py
@@ -0,0 +1 @@
+__author__ = 'christian'
diff --git a/pyload/config/default.py b/pyload/config/default.py
new file mode 100644
index 000000000..8a2044281
--- /dev/null
+++ b/pyload/config/default.py
@@ -0,0 +1,107 @@
+# -*- coding: utf-8 -*-
+
+"""
+Configuration layout for default base config
+"""
+
+#TODO: write tooltips and descriptions
+#TODO: use apis config related classes
+
+def make_config(config):
+ # Check if gettext is installed
+ _ = lambda x: x
+
+ config.addConfigSection("remote", _("Remote"), _("Description"), _("Long description"),
+ [
+ ("activated", "bool", _("Activated"), _("Tooltip"), True),
+ ("port", "int", _("Port"), _("Tooltip"), 7227),
+ ("listenaddr", "ip", _("Adress"), _("Tooltip"), "0.0.0.0"),
+ ])
+
+ config.addConfigSection("log", _("Log"), _("Description"), _("Long description"),
+ [
+ ("log_size", "int", _("Size in kb"), _("Tooltip"), 100),
+ ("log_folder", "folder", _("Folder"), _("Tooltip"), "Logs"),
+ ("file_log", "bool", _("File Log"), _("Tooltip"), True),
+ ("log_count", "int", _("Count"), _("Tooltip"), 5),
+ ("log_rotate", "bool", _("Log Rotate"), _("Tooltip"), True),
+ ])
+
+ config.addConfigSection("permission", _("Permissions"), _("Description"), _("Long description"),
+ [
+ ("group", "str", _("Groupname"), _("Tooltip"), "users"),
+ ("change_dl", "bool", _("Change Group and User of Downloads"), _("Tooltip"), False),
+ ("change_file", "bool", _("Change file mode of downloads"), _("Tooltip"), False),
+ ("user", "str", _("Username"), _("Tooltip"), "user"),
+ ("file", "str", _("Filemode for Downloads"), _("Tooltip"), "0644"),
+ ("change_group", "bool", _("Change group of running process"), _("Tooltip"), False),
+ ("folder", "str", _("Folder Permission mode"), _("Tooltip"), "0755"),
+ ("change_user", "bool", _("Change user of running process"), _("Tooltip"), False),
+ ])
+
+ config.addConfigSection("general", _("General"), _("Description"), _("Long description"),
+ [
+ ("language", "en;de;fr;it;es;nl;sv;ru;pl;cs;sr;pt_BR", _("Language"), _("Tooltip"), "en"),
+ ("download_folder", "folder", _("Download Folder"), _("Tooltip"), "Downloads"),
+ ("checksum", "bool", _("Use Checksum"), _("Tooltip"), False),
+ ("folder_per_package", "bool", _("Create folder for each package"), _("Tooltip"), True),
+ ("debug_mode", "bool", _("Debug Mode"), _("Tooltip"), False),
+ ("min_free_space", "int", _("Min Free Space (MB)"), _("Tooltip"), 200),
+ ("renice", "int", _("CPU Priority"), _("Tooltip"), 0),
+ ])
+
+ config.addConfigSection("ssl", _("SSL"), _("Description"), _("Long description"),
+ [
+ ("cert", "file", _("SSL Certificate"), _("Tooltip"), "ssl.crt"),
+ ("activated", "bool", _("Activated"), _("Tooltip"), False),
+ ("key", "file", _("SSL Key"), _("Tooltip"), "ssl.key"),
+ ])
+
+ config.addConfigSection("webinterface", _("Webinterface"), _("Description"), _("Long description"),
+ [
+ ("template", "str", _("Template"), _("Tooltip"), "default"),
+ ("activated", "bool", _("Activated"), _("Tooltip"), True),
+ ("prefix", "str", _("Path Prefix"), _("Tooltip"), ""),
+ ("server", "auto;threaded;fallback;fastcgi", _("Server"), _("Tooltip"), "auto"),
+ ("force_server", "str", _("Favor specific server"), _("Tooltip"), ""),
+ ("host", "ip", _("IP"), _("Tooltip"), "0.0.0.0"),
+ ("https", "bool", _("Use HTTPS"), _("Tooltip"), False),
+ ("port", "int", _("Port"), _("Tooltip"), 8001),
+ ("develop", "str", _("Development mode"), _(""), False),
+ ])
+
+ config.addConfigSection("proxy", _("Proxy"), _("Description"), _("Long description"),
+ [
+ ("username", "str", _("Username"), _("Tooltip"), ""),
+ ("proxy", "bool", _("Use Proxy"), _("Tooltip"), False),
+ ("address", "str", _("Address"), _("Tooltip"), "localhost"),
+ ("password", "password", _("Password"), _("Tooltip"), ""),
+ ("type", "http;socks4;socks5", _("Protocol"), _("Tooltip"), "http"),
+ ("port", "int", _("Port"), _("Tooltip"), 7070),
+ ])
+
+ config.addConfigSection("reconnect", _("Reconnect"), _("Description"), _("Long description"),
+ [
+ ("endTime", "time", _("End"), _("Tooltip"), "0:00"),
+ ("activated", "bool", _("Use Reconnect"), _("Tooltip"), False),
+ ("method", "str", _("Method"), _("Tooltip"), "./reconnect.sh"),
+ ("startTime", "time", _("Start"), _("Tooltip"), "0:00"),
+ ])
+
+ config.addConfigSection("download", _("Download"), _("Description"), _("Long description"),
+ [
+ ("max_downloads", "int", _("Max Parallel Downloads"), _("Tooltip"), 3),
+ ("limit_speed", "bool", _("Limit Download Speed"), _("Tooltip"), False),
+ ("interface", "str", _("Download interface to bind (ip or Name)"), _("Tooltip"), ""),
+ ("skip_existing", "bool", _("Skip already existing files"), _("Tooltip"), False),
+ ("max_speed", "int", _("Max Download Speed in kb/s"), _("Tooltip"), -1),
+ ("ipv6", "bool", _("Allow IPv6"), _("Tooltip"), False),
+ ("chunks", "int", _("Max connections for one download"), _("Tooltip"), 3),
+ ("restart_failed", "bool", _("Restart failed downloads on startup"), _("Tooltip"), False),
+ ])
+
+ config.addConfigSection("downloadTime", _("Download Time"), _("Description"), _("Long description"),
+ [
+ ("start", "time", _("Start"), _("Tooltip"), "0:00"),
+ ("end", "time", _("End"), _("Tooltip"), "0:00"),
+ ])