1 # -*- coding: utf-8 -*-
3 # Copyright (c) 2009, 2010, 2011, 2013 Jack Kaliko <kaliko@azylum.org>
5 # This file is part of sima
7 # sima is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
12 # sima is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with sima. If not, see <http://www.gnu.org/licenses/>.
23 Deal with configuration and data files.
24 Parse configuration file and set defaults for missing options.
31 from configparser import Error
32 from os import (makedirs, environ, stat, chmod)
33 from os.path import (join, isdir, isfile)
34 from stat import (S_IMODE, ST_MODE, S_IRWXO, S_IRWXG)
40 CONF_FILE = 'sima.cfg'
49 'history_duration': "8",
60 'queue_mode': "track", #TODO control values
61 'single_album': "false",
70 class ConfMan(object): # CONFIG MANAGER CLASS
72 Configuration manager.
73 Default configuration is stored in DEFAULT_CONF dictionnary.
74 First init_config() run to get config from file.
75 Then control_conf() is run and retrieve configuration from defaults if not
77 These settings are then updated with command line options with
78 supersedes_config_with_cmd_line_options().
80 Order of priority for the origin of an option is then (lowest to highest):
82 * Env. Var for MPD host, port and password
83 * configuration file (overrides previous)
84 * command line options (overrides previous)
87 def __init__(self, logger, options=None):
88 # options settings priority:
89 # defauts < conf. file < command line
90 self.conf_file = options.get('conf_file')
92 self.defaults = dict(DEFAULT_CONF)
93 self.startopt = options
102 self.supersedes_config_with_cmd_line_options()
106 self.config.getboolean('MPD', 'password')
107 self.log.debug('No password set, proceeding without ' +
111 # ValueError if password not a boolean, hence an actual password.
112 pw = self.config.get('MPD', 'password')
114 self.log.debug('Password set as an empty string.')
118 def control_mod(self):
120 Controls conf file permissions.
122 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
123 self.log.debug('file permission is: %o' % mode)
124 if mode & S_IRWXO or mode & S_IRWXG:
125 self.log.warning('File is readable by "other" and/or' +
126 ' "group" (actual permission %o octal).' %
128 self.log.warning('Consider setting permissions' +
131 def supersedes_config_with_cmd_line_options(self):
132 """Updates defaults settings with command line options"""
133 for sec in self.config.sections():
134 for opt in self.config.options(sec):
135 if opt in list(self.startopt.keys()):
136 self.config.set(sec, opt, str(self.startopt.get(opt)))
139 """Use MPD en.var. to set defaults"""
140 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
142 self.log.info('Env. variable MPD_HOST set to "%s"' % mpd_host)
143 self.defaults['MPD']['host'] = mpd_host
145 self.log.info('Env. variable MPD_HOST contains password.')
146 self.defaults['MPD']['password'] = passwd
148 self.log.info('Env. variable MPD_PORT set to "%s".'
150 self.defaults['MPD']['port'] = mpd_port
152 def control_conf(self):
153 """Get through options/values and set defaults if not in conf file."""
154 # Control presence of obsolete settings
155 for option in ['history', 'history_length', 'top_tracks']:
156 if self.config.has_option('sima', option):
157 self.log.warning('Obsolete setting found in conf file: "%s"'
159 # Setting default if not specified
160 for section in DEFAULT_CONF.keys():
161 if section not in self.config.sections():
162 self.log.debug('[%s] NOT in conf file' % section)
163 self.config.add_section(section)
164 for option in self.defaults[section]:
165 self.config.set(section,
167 self.defaults[section][option])
169 'Setting option with default value: %s = %s' %
170 (option, self.defaults[section][option]))
171 elif section in self.config.sections():
172 self.log.debug('[%s] present in conf file' % section)
173 for option in self.defaults[section]:
174 if self.config.has_option(section, option):
175 #self.log.debug(u'option "%s" set to "%s" in conf. file' %
176 # (option, self.config.get(section, option)))
180 'Option "%s" missing in section "%s"' %
182 self.log.debug('=> setting default "%s" (may not suit you…)' %
183 self.defaults[section][option])
184 self.config.set(section, option,
185 self.defaults[section][option])
187 def init_config(self):
189 Use XDG directory standard if exists
190 else use "HOME/(.config|.local/share)/sima/"
191 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
194 homedir = environ.get('HOME')
196 if environ.get('XDG_DATA_HOME'):
197 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
198 elif self.startopt.get('var_dir'):
199 # If var folder is provided via CLI set data_dir accordingly
200 data_dir = join(self.startopt.get('var_dir'))
201 elif (homedir and isdir(homedir) and homedir not in ['/']):
202 data_dir = join(homedir, '.local', 'share', DIRNAME)
204 self.log.error('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
205 self.log.error('Please use "--var_dir" to set a proper location')
208 if not isdir(data_dir):
210 chmod(data_dir, 0o700)
212 if self.startopt.get('conf_file'):
213 # No need to handle conf file location
215 elif environ.get('XDG_CONFIG_HOME'):
216 conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
217 elif (homedir and isdir(homedir) and homedir not in ['/']):
218 conf_dir = join(homedir, '.config', DIRNAME)
219 # Create conf_dir if necessary
220 if not isdir(conf_dir):
222 chmod(conf_dir, 0o700)
223 self.conf_file = join(conf_dir, CONF_FILE)
225 self.log.error('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
226 self.log.error('Please use "--config" to locate the conf file')
229 self.db_file = join(data_dir, 'sima.db')
231 config = configparser.SafeConfigParser()
233 # If no conf file present, uses defaults
234 if not isfile(self.conf_file):
238 self.log.info('Loading configuration from: %s' % self.conf_file)
242 config.read(self.conf_file)
250 # vim: ai ts=4 sw=4 sts=4 expandtab