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/>.
22 Deal with configuration and data files.
23 Parse configuration file and set defaults for missing options.
31 from configparser import Error
32 from os import (access, makedirs, getenv, stat, chmod, W_OK)
33 from os.path import (join, isdir, isfile, dirname, exists)
34 from stat import (S_IMODE, ST_MODE, S_IRWXO, S_IRWXG)
40 CONF_FILE = 'mpd_sima.cfg'
49 'internal': "Crop, Lastfm, Random",
52 'history_duration': 8,
55 'musicbrainzid': True,
56 'repeat_disable_queue': True,
57 'single_disable_queue': True,
58 'mopidy_compat': False,
73 'queue_mode': "track", # TODO control values
75 'single_album': False,
78 'shuffle_album': False,
79 'track_to_add_from_album': 0, # <=0 means keep all
85 'flavour': "sensible", # in pure, sensible
96 'queue_mode': "track",
97 'single_album': False,
103 'queue_mode': "track",
104 'single_album': False,
113 class ConfMan: # CONFIG MANAGER CLASS
115 Configuration manager.
116 Default configuration is stored in DEFAULT_CONF dictionnary.
117 First init_config() run to get config from file.
118 Then control_conf() is run and retrieve configuration from defaults if not
120 These settings are then updated with command line options with
121 supersedes_config_with_cmd_line_options().
123 Order of priority for the origin of an option is then (lowest to highest):
125 * Env. Var for MPD host, port and password
126 * configuration file (overrides previous)
127 * command line options (overrides previous)
130 def __init__(self, options=None):
131 self.log = logging.getLogger('sima')
132 # options settings priority:
133 # defauts < env. var. < conf. file < command line
134 self.conf_file = options.get('conf_file')
135 self.config = configparser.ConfigParser(inline_comment_prefixes='#')
136 self.config.read_dict(DEFAULT_CONF)
137 # update DEFAULT_CONF with env. var.
139 self.startopt = options
143 self.supersedes_config_with_cmd_line_options()
145 self.config['sima']['db_file'] = join(self.config['sima']['var_dir'], 'sima.db')
146 # Controls files access
150 data_dir = self.config['sima']['var_dir']
151 if not isdir(data_dir):
152 self.log.trace('Creating "%s"', data_dir)
154 chmod(data_dir, 0o700)
156 def control_facc(self):
157 """Controls file access.
158 This is relevant only for file provided through the configuration file
159 since files provided on the command line are already checked with
160 argparse. Also add config['sima']['db_file'] contructed here in init
163 for op, ftochk in [('logfile', self.config.get('log', 'logfile')),
164 ('pidfile', self.config.get('daemon', 'pidfile')),
165 ('db file', self.config.get('sima', 'db_file'))]:
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)
184 def control_mod(self):
186 Controls conf file permissions.
188 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
189 self.log.debug('file permission is: %o', mode)
190 if mode & S_IRWXO or mode & S_IRWXG:
191 self.log.warning('File is readable by "other" and/or' +
192 ' "group" (actual permission %o octal).', mode)
193 self.log.warning('Consider setting permissions to 600 octal.')
195 def supersedes_config_with_cmd_line_options(self):
196 """Updates defaults settings with command line options"""
197 for sec in self.config.sections():
198 for opt in self.config.options(sec):
199 if opt in list(self.startopt.keys()):
200 self.config.set(sec, opt, str(self.startopt.get(opt)))
201 # honor MPD_HOST format as in mpc(1) for command line option --host
202 if self.startopt.get('host'):
203 if '@' in self.startopt.get('host'):
204 host, passwd = utils.parse_mpd_host(self.startopt.get('host'))
206 self.config.set('MPD', 'password', passwd)
208 self.config.set('MPD', 'host', host)
211 """Use MPD en.var. to set defaults"""
212 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
214 self.log.info('Env. variable MPD_HOST set to "%s"', mpd_host)
215 self.config['MPD'].update(host=mpd_host)
217 self.log.info('Env. variable MPD_HOST contains password.')
218 self.config['MPD'].update(password=passwd)
220 self.log.info('Env. variable MPD_PORT set to "%s".', mpd_port)
221 self.config['MPD'].update(port=mpd_port)
223 def init_config(self):
225 Use XDG directory standard if exists
226 else use "HOME/(.config|.local/share)/sima/"
227 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
230 homedir = getenv('HOME')
232 if getenv('XDG_DATA_HOME'):
233 data_dir = join(getenv('XDG_DATA_HOME'), DIRNAME)
234 elif homedir and isdir(homedir) and homedir not in ['/']:
235 data_dir = join(homedir, '.local', 'share', DIRNAME)
237 self.log.critical('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
238 self.log.critical('Please use "--var-dir" to set a proper location')
241 if self.startopt.get('conf_file'):
242 # No need to handle conf file location
244 elif getenv('XDG_CONFIG_HOME'):
245 conf_dir = join(getenv('XDG_CONFIG_HOME'), DIRNAME)
246 self.conf_file = join(conf_dir, CONF_FILE)
247 elif homedir and isdir(homedir) and homedir not in ['/']:
248 conf_dir = join(homedir, '.config', DIRNAME)
249 self.conf_file = join(conf_dir, CONF_FILE)
251 self.log.critical('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
252 self.log.critical('Please use "--config" to locate the conf file')
256 self.config['sima']['var_dir'] = join(data_dir)
258 # If no conf file present, uses defaults
259 if not isfile(self.conf_file):
262 self.log.info('Loading configuration from: %s', self.conf_file)
266 self.config.read(self.conf_file)
272 # vim: ai ts=4 sw=4 sts=4 expandtab