|
|
@ -46,13 +46,14 @@ modified = False # Signals a change in option dictionary |
|
|
|
|
|
|
|
class Option(object): |
|
|
|
""" Basic option class, basic fields """ |
|
|
|
|
|
|
|
def __init__(self, section, keyword, default_val=None, add=True, protect=False): |
|
|
|
""" Basic option |
|
|
|
section : single section or comma-separated list of sections |
|
|
|
a list will be a hierarchy: "foo, bar" --> [foo][[bar]] |
|
|
|
keyword : keyword in the (last) section |
|
|
|
default_val : value returned when no value has been set |
|
|
|
callback : procedure to call when value is succesfully changed |
|
|
|
callback : procedure to call when value is successfully changed |
|
|
|
protect : Do not allow setting via the API (specifically set_dict) |
|
|
|
""" |
|
|
|
self.__sections = section.split(',') |
|
|
@ -88,7 +89,7 @@ class Option(object): |
|
|
|
|
|
|
|
def get_dict(self, safe=False): |
|
|
|
""" Return value a dictionary """ |
|
|
|
return { self.__keyword : self.get() } |
|
|
|
return {self.__keyword: self.get()} |
|
|
|
|
|
|
|
def set_dict(self, dict): |
|
|
|
""" Set value based on dictionary """ |
|
|
@ -125,9 +126,9 @@ class Option(object): |
|
|
|
return self.__sections, self.__keyword |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OptionNumber(Option): |
|
|
|
""" Numeric option class, int/float is determined from default value """ |
|
|
|
|
|
|
|
def __init__(self, section, keyword, default_val=0, minval=None, maxval=None, validation=None, add=True, protect=False): |
|
|
|
Option.__init__(self, section, keyword, default_val, add=add, protect=protect) |
|
|
|
self.__minval = minval |
|
|
@ -159,6 +160,7 @@ class OptionNumber(Option): |
|
|
|
|
|
|
|
class OptionBool(Option): |
|
|
|
""" Boolean option class """ |
|
|
|
|
|
|
|
def __init__(self, section, keyword, default_val=False, add=True, protect=False): |
|
|
|
Option.__init__(self, section, keyword, int(default_val), add=add, protect=protect) |
|
|
|
|
|
|
@ -174,6 +176,7 @@ class OptionBool(Option): |
|
|
|
|
|
|
|
class OptionDir(Option): |
|
|
|
""" Directory option class """ |
|
|
|
|
|
|
|
def __init__(self, section, keyword, default_val='', apply_umask=False, create=True, validation=None, writable=True, add=True): |
|
|
|
self.__validation = validation |
|
|
|
self.__root = '' # Base directory for relative paths |
|
|
@ -193,7 +196,6 @@ class OptionDir(Option): |
|
|
|
else: |
|
|
|
return p.replace('\\', '/') if '\\' in p else p |
|
|
|
|
|
|
|
|
|
|
|
def get_path(self): |
|
|
|
""" Return full absolute path """ |
|
|
|
value = self.get() |
|
|
@ -240,8 +242,10 @@ class OptionDir(Option): |
|
|
|
""" Set auto-creation value """ |
|
|
|
self.__create = value |
|
|
|
|
|
|
|
|
|
|
|
class OptionList(Option): |
|
|
|
""" List option class """ |
|
|
|
|
|
|
|
def __init__(self, section, keyword, default_val=None, validation=None, add=True, protect=False): |
|
|
|
self.__validation = validation |
|
|
|
if default_val is None: |
|
|
@ -249,7 +253,7 @@ class OptionList(Option): |
|
|
|
Option.__init__(self, section, keyword, default_val, add=add, protect=protect) |
|
|
|
|
|
|
|
def set(self, value): |
|
|
|
""" Set the list given a comma-separated string or a list""" |
|
|
|
""" Set the list given a comma-separated string or a list """ |
|
|
|
error = None |
|
|
|
if value is not None: |
|
|
|
if not isinstance(value, list): |
|
|
@ -272,12 +276,14 @@ class OptionList(Option): |
|
|
|
r = len(lst) |
|
|
|
for n in xrange(r): |
|
|
|
txt += lst[n] |
|
|
|
if n < r-1: txt += ', ' |
|
|
|
if n < r - 1: |
|
|
|
txt += ', ' |
|
|
|
return txt |
|
|
|
|
|
|
|
|
|
|
|
class OptionStr(Option): |
|
|
|
""" String class """ |
|
|
|
|
|
|
|
def __init__(self, section, keyword, default_val='', validation=None, add=True, strip=True, protect=False): |
|
|
|
Option.__init__(self, section, keyword, default_val, add=add, protect=protect) |
|
|
|
self.__validation = validation |
|
|
@ -306,6 +312,7 @@ class OptionStr(Option): |
|
|
|
|
|
|
|
class OptionPassword(Option): |
|
|
|
""" Password class """ |
|
|
|
|
|
|
|
def __init__(self, section, keyword, default_val='', add=True): |
|
|
|
Option.__init__(self, section, keyword, default_val, add=add) |
|
|
|
self.get_string = self.get_stars |
|
|
@ -325,9 +332,9 @@ class OptionPassword(Option): |
|
|
|
def get_dict(self, safe=False): |
|
|
|
""" Return value a dictionary """ |
|
|
|
if safe: |
|
|
|
return { self._Option__keyword : self.get_stars() } |
|
|
|
return {self._Option__keyword: self.get_stars()} |
|
|
|
else: |
|
|
|
return { self._Option__keyword : self.get() } |
|
|
|
return {self._Option__keyword: self.get()} |
|
|
|
|
|
|
|
def set(self, pw): |
|
|
|
""" Set password, encode it """ |
|
|
@ -361,6 +368,7 @@ def delete_from_database(section, keyword): |
|
|
|
|
|
|
|
class ConfigServer(object): |
|
|
|
""" Class defining a single server """ |
|
|
|
|
|
|
|
def __init__(self, name, values): |
|
|
|
|
|
|
|
self.__name = name |
|
|
@ -368,7 +376,7 @@ class ConfigServer(object): |
|
|
|
|
|
|
|
self.displayname = OptionStr(name, 'displayname', '', add=False) |
|
|
|
self.host = OptionStr(name, 'host', '', add=False) |
|
|
|
self.port = OptionNumber(name, 'port', 119, 0, 2**16-1, add=False) |
|
|
|
self.port = OptionNumber(name, 'port', 119, 0, 2 ** 16 - 1, add=False) |
|
|
|
self.timeout = OptionNumber(name, 'timeout', 120, 30, 240, add=False) |
|
|
|
self.username = OptionStr(name, 'username', '', add=False) |
|
|
|
self.password = OptionPassword(name, 'password', '', add=False) |
|
|
@ -441,6 +449,7 @@ class ConfigServer(object): |
|
|
|
|
|
|
|
class ConfigCat(object): |
|
|
|
""" Class defining a single category """ |
|
|
|
|
|
|
|
def __init__(self, name, values): |
|
|
|
self.__name = name |
|
|
|
name = 'categories,' + name |
|
|
@ -482,6 +491,7 @@ class ConfigCat(object): |
|
|
|
|
|
|
|
class OptionFilters(Option): |
|
|
|
""" Filter list class """ |
|
|
|
|
|
|
|
def __init__(self, section, keyword, add=True): |
|
|
|
Option.__init__(self, section, keyword, add=add) |
|
|
|
self.set([]) |
|
|
@ -521,7 +531,7 @@ class OptionFilters(Option): |
|
|
|
dict = {} |
|
|
|
n = 0 |
|
|
|
for filter in self.get(): |
|
|
|
dict['filter'+str(n)] = filter |
|
|
|
dict['filter' + str(n)] = filter |
|
|
|
n = n + 1 |
|
|
|
return dict |
|
|
|
|
|
|
@ -545,8 +555,10 @@ class OptionFilters(Option): |
|
|
|
self.set(filters) |
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
class ConfigRSS(object): |
|
|
|
""" Class defining a single Feed definition """ |
|
|
|
|
|
|
|
def __init__(self, name, values): |
|
|
|
self.__name = name |
|
|
|
name = 'rss,' + name |
|
|
@ -598,7 +610,6 @@ class ConfigRSS(object): |
|
|
|
return 'rss', self.__name |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_dconfig(section, keyword, nested=False): |
|
|
|
""" Return a config values dictonary, |
|
|
|
Single item or slices based on 'section', 'keyword' |
|
|
@ -633,16 +644,15 @@ def get_dconfig(section, keyword, nested=False): |
|
|
|
data = item.get_dict(safe=True) |
|
|
|
if not nested: |
|
|
|
if section in ('servers', 'categories', 'rss'): |
|
|
|
data = {section : [ data ]} |
|
|
|
data = {section: [data]} |
|
|
|
else: |
|
|
|
data = {section : data} |
|
|
|
data = {section: data} |
|
|
|
|
|
|
|
return True, data |
|
|
|
|
|
|
|
|
|
|
|
def get_config(section, keyword): |
|
|
|
""" Return a config object, based on 'section', 'keyword' |
|
|
|
""" |
|
|
|
""" Return a config object, based on 'section', 'keyword' """ |
|
|
|
try: |
|
|
|
return database[section][keyword] |
|
|
|
except KeyError: |
|
|
@ -651,8 +661,7 @@ def get_config(section, keyword): |
|
|
|
|
|
|
|
|
|
|
|
def set_config(kwargs): |
|
|
|
""" Set a config item, using values in dictionary |
|
|
|
""" |
|
|
|
""" Set a config item, using values in dictionary """ |
|
|
|
try: |
|
|
|
item = database[kwargs.get('section')][kwargs.get('keyword')] |
|
|
|
except KeyError: |
|
|
@ -662,21 +671,19 @@ def set_config(kwargs): |
|
|
|
|
|
|
|
|
|
|
|
def delete(section, keyword): |
|
|
|
""" Delete specific config item |
|
|
|
""" |
|
|
|
""" Delete specific config item """ |
|
|
|
try: |
|
|
|
database[section][keyword].delete() |
|
|
|
except KeyError: |
|
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
################################################################################ |
|
|
|
# |
|
|
|
############################################################################## |
|
|
|
# INI file support |
|
|
|
# |
|
|
|
# This does input and output of configuration to an INI file. |
|
|
|
# It translates this data structure to the config database. |
|
|
|
|
|
|
|
############################################################################## |
|
|
|
@synchronized(SAVE_CONFIG_LOCK) |
|
|
|
def read_config(path): |
|
|
|
""" Read the complete INI file and check its version number |
|
|
@ -775,7 +782,6 @@ def _read_config(path, try_backup=False): |
|
|
|
return True, "" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@synchronized(SAVE_CONFIG_LOCK) |
|
|
|
def save_config(force=False): |
|
|
|
""" Update Setup file with current option values """ |
|
|
@ -793,7 +799,7 @@ def save_config(force=False): |
|
|
|
CFG[section] = {} |
|
|
|
for subsec in database[section]: |
|
|
|
if section == 'servers': |
|
|
|
subsec_mod = subsec.replace('[', '{').replace(']','}') |
|
|
|
subsec_mod = subsec.replace('[', '{').replace(']', '}') |
|
|
|
else: |
|
|
|
subsec_mod = subsec |
|
|
|
try: |
|
|
@ -833,7 +839,7 @@ def save_config(force=False): |
|
|
|
except: |
|
|
|
# Something wrong with the backup, |
|
|
|
logging.error(T('Cannot create backup file for %s'), bakname) |
|
|
|
logging.info("Traceback: ", exc_info = True) |
|
|
|
logging.info("Traceback: ", exc_info=True) |
|
|
|
return res |
|
|
|
|
|
|
|
# Write new config file |
|
|
@ -843,7 +849,7 @@ def save_config(force=False): |
|
|
|
res = True |
|
|
|
except: |
|
|
|
logging.error(T('Cannot write to INI file %s'), filename) |
|
|
|
logging.info("Traceback: ", exc_info = True) |
|
|
|
logging.info("Traceback: ", exc_info=True) |
|
|
|
try: |
|
|
|
os.remove(filename) |
|
|
|
except: |
|
|
@ -854,7 +860,6 @@ def save_config(force=False): |
|
|
|
return res |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def define_servers(): |
|
|
|
""" Define servers listed in the Setup file |
|
|
|
return a list of ConfigServer instances |
|
|
@ -871,6 +876,7 @@ def define_servers(): |
|
|
|
except KeyError: |
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
def get_servers(): |
|
|
|
global database |
|
|
|
try: |
|
|
@ -909,8 +915,8 @@ def get_categories(cat=0): |
|
|
|
database['categories'] = {} |
|
|
|
cats = database['categories'] |
|
|
|
if '*' not in cats: |
|
|
|
ConfigCat('*', {'pp' : old_def('dirscan_opts', '3'), 'script' : old_def('dirscan_script', 'None'), \ |
|
|
|
'priority' : old_def('dirscan_priority', NORMAL_PRIORITY)}) |
|
|
|
ConfigCat('*', {'pp': old_def('dirscan_opts', '3'), 'script': old_def('dirscan_script', 'None'), |
|
|
|
'priority': old_def('dirscan_priority', NORMAL_PRIORITY)}) |
|
|
|
save_config(True) |
|
|
|
if not isinstance(cat, int): |
|
|
|
try: |
|
|
@ -931,6 +937,7 @@ def define_rss(): |
|
|
|
except KeyError: |
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
def get_rss(): |
|
|
|
global database |
|
|
|
try: |
|
|
@ -938,18 +945,16 @@ def get_rss(): |
|
|
|
except KeyError: |
|
|
|
return {} |
|
|
|
|
|
|
|
|
|
|
|
def get_filename(): |
|
|
|
global CFG |
|
|
|
return CFG.filename |
|
|
|
|
|
|
|
|
|
|
|
################################################################################ |
|
|
|
# |
|
|
|
############################################################################## |
|
|
|
# Default Validation handlers |
|
|
|
# |
|
|
|
############################################################################## |
|
|
|
__PW_PREFIX = '!!!encoded!!!' |
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
def encode_password(pw): |
|
|
|
""" Encode password in hexadecimal if needed """ |
|
|
|
enc = False |
|
|
@ -973,7 +978,7 @@ def decode_password(pw, name): |
|
|
|
if pw and pw.startswith(__PW_PREFIX): |
|
|
|
for n in range(len(__PW_PREFIX), len(pw), 2): |
|
|
|
try: |
|
|
|
ch = chr( int(pw[n] + pw[n+1], 16) ) |
|
|
|
ch = chr(int(pw[n] + pw[n + 1], 16)) |
|
|
|
except ValueError: |
|
|
|
logging.error(T('Incorrectly encoded password %s'), name) |
|
|
|
return '' |
|
|
@ -1041,8 +1046,7 @@ def validate_notempty(root, value, default): |
|
|
|
|
|
|
|
|
|
|
|
def create_api_key(): |
|
|
|
""" Return a new randomized API_KEY |
|
|
|
""" |
|
|
|
""" Return a new randomized API_KEY """ |
|
|
|
import time |
|
|
|
try: |
|
|
|
from hashlib import md5 |
|
|
@ -1061,12 +1065,11 @@ def create_api_key(): |
|
|
|
return m.hexdigest() |
|
|
|
|
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
|
|
_FIXES = \ |
|
|
|
( |
|
|
|
_FIXES = ( |
|
|
|
('enable_par_multicore', 'par2_multicore'), |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
def compatibility_fix(cf): |
|
|
|
""" Convert obsolete INI entries """ |
|
|
|
for item in _FIXES: |
|
|
|