1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2009-2015, 2019-2021 kaliko <kaliko@azylum.org>
3 # Copyright (c) 2019 sacha <sachahony@gmail.com>
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/>.
20 # pylint: disable=bad-continuation
23 Deal with configuration and data files.
24 Parse configuration file and set defaults for missing options.
32 from configparser import Error
33 from os import (access, makedirs, environ, stat, chmod, W_OK)
34 from os.path import (join, isdir, isfile, dirname, exists)
35 from stat import (S_IMODE, ST_MODE, S_IRWXO, S_IRWXG)
41 CONF_FILE = 'mpd_sima.cfg'
50 'internal': "Crop, Lastfm, Random",
53 'history_duration': 8,
56 'musicbrainzid': True,
57 'repeat_disable_queue': True,
58 'single_disable_queue': True,
59 'mopidy_compat': False,
74 'queue_mode': "track", # TODO control values
76 'single_album': False,
79 'shuffle_album': False,
80 'track_to_add_from_album': 0, # <=0 means keep all
86 'flavour': "sensible", # in pure, sensible
97 'queue_mode': "track",
98 'single_album': False,
104 'queue_mode': "track",
105 'single_album': False,
114 class ConfMan: # CONFIG MANAGER CLASS
116 Configuration manager.
117 Default configuration is stored in DEFAULT_CONF dictionnary.
118 First init_config() run to get config from file.
119 Then control_conf() is run and retrieve configuration from defaults if not
121 These settings are then updated with command line options with
122 supersedes_config_with_cmd_line_options().
124 Order of priority for the origin of an option is then (lowest to highest):
126 * Env. Var for MPD host, port and password
127 * configuration file (overrides previous)
128 * command line options (overrides previous)
131 def __init__(self, options=None):
132 self.log = logging.getLogger('sima')
133 # options settings priority:
134 # defauts < env. var. < conf. file < command line
135 self.conf_file = options.get('conf_file')
136 self.config = configparser.ConfigParser(inline_comment_prefixes='#')
137 self.config.read_dict(DEFAULT_CONF)
138 # update DEFAULT_CONF with env. var.
140 self.startopt = options
144 self.supersedes_config_with_cmd_line_options()
145 # Controls files access
148 self.config['sima']['db_file'] = join(self.config['sima']['var_dir'], 'sima.db')
151 data_dir = self.config['sima']['var_dir']
152 if not isdir(data_dir):
153 self.log.trace('Creating "%s"', data_dir)
155 chmod(data_dir, 0o700)
157 def control_facc(self):
158 """Controls file access.
159 This is relevant only for file provided through the configuration file
160 since files provided on the command line are already checked with
164 for op, ftochk in [('logfile', self.config.get('log', 'logfile')),
165 ('pidfile', self.config.get('daemon', 'pidfile')),]:
169 self.log.critical('Need a file not a directory: "%s"', ftochk)
171 if not exists(ftochk):
172 # Is parent directory writable then
173 filedir = dirname(ftochk)
174 if not access(filedir, W_OK):
175 self.log.critical('no write access to "%s" (%s)', filedir, op)
178 if not access(ftochk, W_OK):
179 self.log.critical('no write access to "%s" (%s)', ftochk, op)
182 if exists(self.conf_file):
183 self.log.warning('Try to check the configuration file: %s', self.conf_file)
186 def control_mod(self):
188 Controls conf file permissions.
190 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
191 self.log.debug('file permission is: %o', mode)
192 if mode & S_IRWXO or mode & S_IRWXG:
193 self.log.warning('File is readable by "other" and/or' +
194 ' "group" (actual permission %o octal).' %
196 self.log.warning('Consider setting permissions' +
199 def supersedes_config_with_cmd_line_options(self):
200 """Updates defaults settings with command line options"""
201 for sec in self.config.sections():
202 for opt in self.config.options(sec):
203 if opt in list(self.startopt.keys()):
204 self.config.set(sec, opt, str(self.startopt.get(opt)))
205 # honor MPD_HOST format as in mpc(1) for command line option --host
206 if self.startopt.get('host'):
207 if '@' in self.startopt.get('host'):
208 passwd, host = self.startopt.get('host').split('@')
209 self.config.set('MPD', 'password', passwd)
210 self.config.set('MPD', 'host', host)
213 """Use MPD en.var. to set defaults"""
214 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
216 self.log.info('Env. variable MPD_HOST set to "%s"', mpd_host)
217 self.config['MPD'].update(host=mpd_host)
219 self.log.info('Env. variable MPD_HOST contains password.')
220 self.config['MPD'].update(password=passwd)
222 self.log.info('Env. variable MPD_PORT set to "%s".', mpd_port)
223 self.config['MPD'].update(port=mpd_port)
225 def init_config(self):
227 Use XDG directory standard if exists
228 else use "HOME/(.config|.local/share)/sima/"
229 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
232 homedir = environ.get('HOME')
234 if environ.get('XDG_DATA_HOME'):
235 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
236 elif homedir and isdir(homedir) and homedir not in ['/']:
237 data_dir = join(homedir, '.local', 'share', DIRNAME)
239 self.log.critical('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
240 self.log.critical('Please use "--var-dir" to set a proper location')
243 if self.startopt.get('conf_file'):
244 # No need to handle conf file location
246 elif environ.get('XDG_CONFIG_HOME'):
247 conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
248 self.conf_file = join(conf_dir, CONF_FILE)
249 elif homedir and isdir(homedir) and homedir not in ['/']:
250 conf_dir = join(homedir, '.config', DIRNAME)
251 self.conf_file = join(conf_dir, CONF_FILE)
253 self.log.critical('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
254 self.log.critical('Please use "--config" to locate the conf file')
258 self.config['sima']['var_dir'] = join(data_dir)
260 # If no conf file present, uses defaults
261 if not isfile(self.conf_file):
264 self.log.info('Loading configuration from: %s', self.conf_file)
268 self.config.read(self.conf_file)
274 # vim: ai ts=4 sw=4 sts=4 expandtab