1 # -*- coding: utf-8 -*-
2 # Copyright (c) 2009-2015, 2019 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
94 class ConfMan(object): # CONFIG MANAGER CLASS
96 Configuration manager.
97 Default configuration is stored in DEFAULT_CONF dictionnary.
98 First init_config() run to get config from file.
99 Then control_conf() is run and retrieve configuration from defaults if not
101 These settings are then updated with command line options with
102 supersedes_config_with_cmd_line_options().
104 Order of priority for the origin of an option is then (lowest to highest):
106 * Env. Var for MPD host, port and password
107 * configuration file (overrides previous)
108 * command line options (overrides previous)
111 def __init__(self, options=None):
112 self.log = logging.getLogger('sima')
113 # options settings priority:
114 # defauts < env. var. < conf. file < command line
115 self.conf_file = options.get('conf_file')
116 self.config = configparser.ConfigParser(inline_comment_prefixes='#')
117 self.config.read_dict(DEFAULT_CONF)
118 # update DEFAULT_CONF with env. var.
120 self.startopt = options
124 self.supersedes_config_with_cmd_line_options()
125 # Controls files access
128 self.config['sima']['db_file'] = join(self.config['sima']['var_dir'], 'sima.db')
131 data_dir = self.config['sima']['var_dir']
132 if not isdir(data_dir):
133 self.log.trace('Creating "{}"'.format(data_dir))
135 chmod(data_dir, 0o700)
137 def control_facc(self):
138 """Controls file access.
139 This is relevant only for file provided through the configuration file
140 since files provided on the command line are already checked with
144 for op, ftochk in [('logfile', self.config.get('log', 'logfile')),
145 ('pidfile', self.config.get('daemon', 'pidfile')),]:
149 self.log.critical('Need a file not a directory: "%s"', ftochk)
151 if not exists(ftochk):
152 # Is parent directory writable then
153 filedir = dirname(ftochk)
154 if not access(filedir, W_OK):
155 self.log.critical('no write access to "%s" (%s)', filedir, op)
158 if not access(ftochk, W_OK):
159 self.log.critical('no write access to "%s" (%s)', ftochk, op)
162 if exists(self.conf_file):
163 self.log.warning('Try to check the configuration file: %s', self.conf_file)
166 def control_mod(self):
168 Controls conf file permissions.
170 mode = S_IMODE(stat(self.conf_file)[ST_MODE])
171 self.log.debug('file permission is: %o', mode)
172 if mode & S_IRWXO or mode & S_IRWXG:
173 self.log.warning('File is readable by "other" and/or' +
174 ' "group" (actual permission %o octal).' %
176 self.log.warning('Consider setting permissions' +
179 def supersedes_config_with_cmd_line_options(self):
180 """Updates defaults settings with command line options"""
181 for sec in self.config.sections():
182 for opt in self.config.options(sec):
183 if opt in list(self.startopt.keys()):
184 self.config.set(sec, opt, str(self.startopt.get(opt)))
185 # honor MPD_HOST format as in mpc(1) for command line option --host
186 if self.startopt.get('host'):
187 if '@' in self.startopt.get('host'):
188 print(self.startopt.get('host').split('@'))
189 passwd, host = self.startopt.get('host').split('@')
190 self.config.set('MPD', 'password', passwd)
191 self.config.set('MPD', 'host', host)
194 """Use MPD en.var. to set defaults"""
195 mpd_host, mpd_port, passwd = utils.get_mpd_environ()
197 self.log.info('Env. variable MPD_HOST set to "%s"', mpd_host)
198 self.config['MPD'].update(host=mpd_host)
200 self.log.info('Env. variable MPD_HOST contains password.')
201 self.config['MPD'].update(password=passwd)
203 self.log.info('Env. variable MPD_PORT set to "%s".', mpd_port)
204 self.config['MPD'].update(port=mpd_port)
206 def init_config(self):
208 Use XDG directory standard if exists
209 else use "HOME/(.config|.local/share)/sima/"
210 http://standards.freedesktop.org/basedir-spec/basedir-spec-0.6.html
213 homedir = environ.get('HOME')
215 if environ.get('XDG_DATA_HOME'):
216 data_dir = join(environ.get('XDG_DATA_HOME'), DIRNAME)
217 elif homedir and isdir(homedir) and homedir not in ['/']:
218 data_dir = join(homedir, '.local', 'share', DIRNAME)
220 self.log.critical('Can\'t find a suitable location for data folder (XDG_DATA_HOME)')
221 self.log.critical('Please use "--var-dir" to set a proper location')
224 if self.startopt.get('conf_file'):
225 # No need to handle conf file location
227 elif environ.get('XDG_CONFIG_HOME'):
228 conf_dir = join(environ.get('XDG_CONFIG_HOME'), DIRNAME)
229 elif homedir and isdir(homedir) and homedir not in ['/']:
230 conf_dir = join(homedir, '.config', DIRNAME)
231 self.conf_file = join(conf_dir, CONF_FILE)
233 self.log.critical('Can\'t find a suitable location for config folder (XDG_CONFIG_HOME)')
234 self.log.critical('Please use "--config" to locate the conf file')
238 self.config['sima']['var_dir'] = join(data_dir)
240 # If no conf file present, uses defaults
241 if not isfile(self.conf_file):
244 self.log.info('Loading configuration from: %s', self.conf_file)
248 self.config.read(self.conf_file)
254 # vim: ai ts=4 sw=4 sts=4 expandtab