summaryrefslogtreecommitdiff
path: root/src/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/config.py')
-rw-r--r--src/config.py685
1 files changed, 0 insertions, 685 deletions
diff --git a/src/config.py b/src/config.py
deleted file mode 100644
index 7f0c75f6..00000000
--- a/src/config.py
+++ /dev/null
@@ -1,685 +0,0 @@
-"""
-Defines the global config instance, used to get or set (and save) values
-from/to the config file.
-
-This module has the particularity that some imports and global variables
-are delayed because it would mean doing an incomplete setup of the python
-loggers.
-
-TODO: get http://bugs.python.org/issue1410680 fixed, one day, in order
-to remove our ugly custom I/O methods.
-"""
-
-DEFSECTION = "Poezio"
-
-import logging.config
-import os
-import stat
-import sys
-import pkg_resources
-
-from configparser import RawConfigParser, NoOptionError, NoSectionError
-from os import environ, makedirs, path, remove
-from shutil import copy2
-from args import parse_args
-
-DEFAULT_CONFIG = {
- 'Poezio': {
- 'ack_message_receipts': True,
- 'add_space_after_completion': True,
- 'after_completion': ',',
- 'alternative_nickname': '',
- 'auto_reconnect': True,
- 'autorejoin_delay': '5',
- 'autorejoin': False,
- 'beep_on': 'highlight private invite',
- 'ca_cert_path': '',
- 'certificate': '',
- 'certfile': '',
- 'ciphers': 'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK:!SRP:!3DES:!aNULL',
- 'connection_check_interval': 60,
- 'connection_timeout_delay': 10,
- 'create_gaps': False,
- 'custom_host': '',
- 'custom_port': '',
- 'default_nick': '',
- 'deterministic_nick_colors': True,
- 'nick_color_aliases': True,
- 'display_activity_notifications': False,
- 'display_gaming_notifications': False,
- 'display_mood_notifications': False,
- 'display_tune_notifications': False,
- 'display_user_color_in_join_part': True,
- 'enable_carbons': True,
- 'enable_user_activity': True,
- 'enable_user_gaming': True,
- 'enable_user_mood': True,
- 'enable_user_nick': True,
- 'enable_user_tune': True,
- 'enable_vertical_tab_list': False,
- 'enable_xhtml_im': True,
- 'eval_password': '',
- 'exec_remote': False,
- 'extract_inline_images': True,
- 'filter_info_messages': '',
- 'force_encryption': True,
- 'force_remote_bookmarks': False,
- 'go_to_previous_tab_on_alt_number': False,
- 'group_corrections': True,
- 'hide_exit_join': -1,
- 'hide_status_change': 120,
- 'hide_user_list': False,
- 'highlight_on': '',
- 'ignore_certificate': False,
- 'ignore_private': False,
- 'information_buffer_popup_on': 'error roster warning help info',
- 'jid': '',
- 'keyfile': '',
- 'lang': 'en',
- 'lazy_resize': True,
- 'load_log': 10,
- 'log_dir': '',
- 'log_errors': True,
- 'max_lines_in_memory': 2048,
- 'max_messages_in_memory': 2048,
- 'max_nick_length': 25,
- 'muc_history_length': 50,
- 'notify_messages': True,
- 'open_all_bookmarks': False,
- 'password': '',
- 'plugins_autoload': '',
- 'plugins_conf_dir': '',
- 'plugins_dir': '',
- 'popup_time': 4,
- 'private_auto_response': '',
- 'remote_fifo_path': './',
- 'request_message_receipts': True,
- 'resource': '',
- 'rooms': '',
- 'roster_group_sort': 'name',
- 'roster_show_offline': False,
- 'roster_sort': 'jid:show',
- 'save_status': True,
- 'self_ping_delay': 0,
- 'send_chat_states': True,
- 'send_initial_presence': True,
- 'send_os_info': True,
- 'send_poezio_info': True,
- 'send_time': True,
- 'separate_history': False,
- 'server': 'anon.jeproteste.info',
- 'show_composing_tabs': 'direct',
- 'show_inactive_tabs': True,
- 'show_jid_in_conversations': True,
- 'show_muc_jid': True,
- 'show_roster_jids': True,
- 'show_roster_subscriptions': '',
- 'show_s2s_errors': True,
- 'show_tab_names': False,
- 'show_tab_numbers': True,
- 'show_timestamps': True,
- 'show_useless_separator': True,
- 'status': '',
- 'status_message': '',
- 'theme': 'default',
- 'themes_dir': '',
- 'tmp_image_dir': '',
- 'use_bookmarks_method': '',
- 'use_log': False,
- 'use_remote_bookmarks': True,
- 'user_list_sort': 'desc',
- 'use_tab_nicks': True,
- 'vertical_tab_list_size': 20,
- 'vertical_tab_list_sort': 'desc',
- 'whitespace_interval': 300,
- 'words': ''
- },
- 'bindings': {
- 'M-i': '^I'
- },
- 'var': {
- 'folded_roster_groups': '',
- 'info_win_height': 2
- },
- 'muc_colors': {
- }
-}
-
-class Config(RawConfigParser):
- """
- load/save the config to a file
- """
- def __init__(self, file_name, default=None):
- RawConfigParser.__init__(self, None)
- # make the options case sensitive
- self.optionxform = str
- self.file_name = file_name
- self.read_file()
- self.default = default
-
- def read_file(self):
- try:
- RawConfigParser.read(self, self.file_name, encoding='utf-8')
- except TypeError: # python < 3.2 sucks
- RawConfigParser.read(self, self.file_name)
- # Check config integrity and fix it if it’s wrong
- # only when the object is the main config
- if self.__class__ is Config:
- for section in ('bindings', 'var'):
- if not self.has_section(section):
- self.add_section(section)
-
- def get(self, option, default=None, section=DEFSECTION):
- """
- get a value from the config but return
- a default value if it is not found
- The type of default defines the type
- returned
- """
- if default is None:
- if self.default:
- default = self.default.get(section, {}).get(option)
- else:
- default = ''
-
- try:
- if type(default) == int:
- res = self.getint(option, section)
- elif type(default) == float:
- res = self.getfloat(option, section)
- elif type(default) == bool:
- res = self.getboolean(option, section)
- else:
- res = self.getstr(option, section)
- except (NoOptionError, NoSectionError, ValueError, AttributeError):
- return default
-
- if res is None:
- return default
- return res
-
- def get_by_tabname(self, option, tabname,
- fallback=True, fallback_server=True, default=''):
- """
- Try to get the value for the option. First we look in
- a section named `tabname`, if the option is not present
- in the section, we search for the global option if fallback is
- True. And we return `default` as a fallback as a last resort.
- """
- if self.default and (not default) and fallback:
- default = self.default.get(DEFSECTION, {}).get(option, '')
- if tabname in self.sections():
- if option in self.options(tabname):
- # We go the tab-specific option
- return self.get(option, default, tabname)
- if fallback_server:
- return self.get_by_servname(tabname, option, default, fallback)
- if fallback:
- # We fallback to the global option
- return self.get(option, default)
- return default
-
- def get_by_servname(self, jid, option, default, fallback=True):
- """
- Try to get the value of an option for a server
- """
- server = safeJID(jid).server
- if server:
- server = '@' + server
- if server in self.sections() and option in self.options(server):
- return self.get(option, default, server)
- if fallback:
- return self.get(option, default)
- return default
-
-
- def __get(self, option, section=DEFSECTION, **kwargs):
- """
- facility for RawConfigParser.get
- """
- return RawConfigParser.get(self, section, option, **kwargs)
-
- def _get(self, section, conv, option, **kwargs):
- """
- Redirects RawConfigParser._get
- """
- return conv(self.__get(option, section, **kwargs))
-
- def getstr(self, option, section=DEFSECTION):
- """
- get a value and returns it as a string
- """
- return self.__get(option, section)
-
- def getint(self, option, section=DEFSECTION):
- """
- get a value and returns it as an int
- """
- return RawConfigParser.getint(self, section, option)
-
- def getfloat(self, option, section=DEFSECTION):
- """
- get a value and returns it as a float
- """
- return RawConfigParser.getfloat(self, section, option)
-
- def getboolean(self, option, section=DEFSECTION):
- """
- get a value and returns it as a boolean
- """
- return RawConfigParser.getboolean(self, section, option)
-
- def write_in_file(self, section, option, value):
- """
- Our own way to save write the value in the file
- Just find the right section, and then find the
- right option, and edit it.
- """
- result = self._parse_file()
- if not result:
- return False
- else:
- sections, result_lines = result
-
- if not section in sections:
- result_lines.append('[%s]' % section)
- result_lines.append('%s = %s' % (option, value))
- else:
- begin, end = sections[section]
- pos = find_line(result_lines, begin, end, option)
-
- if pos is -1:
- result_lines.insert(end, '%s = %s' % (option, value))
- else:
- result_lines[pos] = '%s = %s' % (option, value)
-
- return self._write_file(result_lines)
-
- def remove_in_file(self, section, option):
- """
- Our own way to remove an option from the file.
- """
- result = self._parse_file()
- if not result:
- return False
- else:
- sections, result_lines = result
-
- if not section in sections:
- log.error('Tried to remove the option %s from a non-'
- 'existing section (%s)', option, section)
- return True
- else:
- begin, end = sections[section]
- pos = find_line(result_lines, begin, end, option)
-
- if pos is -1:
- log.error('Tried to remove a non-existing option %s'
- ' from section %s', option, section)
- return True
- else:
- del result_lines[pos]
-
- return self._write_file(result_lines)
-
- def _write_file(self, lines):
- """
- Write the config file, write to a temporary file
- before copying it to the final destination
- """
- try:
- prefix, file = path.split(self.file_name)
- filename = path.join(prefix, '.%s.tmp' % file)
- fd = os.fdopen(
- os.open(
- filename,
- os.O_WRONLY | os.O_CREAT,
- 0o600),
- 'w')
- for line in lines:
- fd.write('%s\n' % line)
- fd.close()
- copy2(filename, self.file_name)
- remove(filename)
- except:
- success = False
- log.error('Unable to save the config file.', exc_info=True)
- else:
- success = True
- return success
-
- def _parse_file(self):
- """
- Parse the config file and return the list of sections with
- their start and end positions, and the lines in the file.
-
- Duplicate sections are preserved but ignored for the parsing.
-
- Returns an empty tuple if reading fails
- """
- if file_ok(self.file_name):
- try:
- with open(self.file_name, 'r', encoding='utf-8') as df:
- lines_before = [line.strip() for line in df]
- except:
- log.error('Unable to read the config file %s',
- self.file_name,
- exc_info=True)
- return tuple()
- else:
- lines_before = []
-
- sections = {}
- duplicate_section = False
- current_section = ''
- current_line = 0
-
- for line in lines_before:
- if line.startswith('['):
- if not duplicate_section and current_section:
- sections[current_section][1] = current_line
-
- duplicate_section = False
- current_section = line[1:-1]
-
- if current_section in sections:
- log.error('Error while reading the configuration file,'
- ' skipping until next section')
- duplicate_section = True
- else:
- sections[current_section] = [current_line, current_line]
-
- current_line += 1
- if not duplicate_section and current_section:
- sections[current_section][1] = current_line
-
- return (sections, lines_before)
-
- def set_and_save(self, option, value, section=DEFSECTION):
- """
- set the value in the configuration then save it
- to the file
- """
- # Special case for a 'toggle' value. We take the current value
- # and set the opposite. Warning if the no current value exists
- # or it is not a bool.
- if value == "toggle":
- current = self.get(option, "", section)
- if isinstance(current, bool):
- value = str(not current)
- else:
- if current.lower() == "false":
- value = "true"
- elif current.lower() == "true":
- value = "false"
- else:
- return ('Could not toggle option: %s.'
- ' Current value is %s.' %
- (option, current or "empty"),
- 'Warning')
- if self.has_section(section):
- RawConfigParser.set(self, section, option, value)
- else:
- self.add_section(section)
- RawConfigParser.set(self, section, option, value)
- if not self.write_in_file(section, option, value):
- return ('Unable to write in the config file', 'Error')
- return ("%s=%s" % (option, value), 'Info')
-
- def remove_and_save(self, option, section=DEFSECTION):
- """
- Remove an option and then save it the config file
- """
- if self.has_section(section):
- RawConfigParser.remove_option(self, section, option)
- if not self.remove_in_file(section, option):
- return ('Unable to save the config file', 'Error')
- return ('Option %s deleted' % option, 'Info')
-
- def silent_set(self, option, value, section=DEFSECTION):
- """
- Set a value, save, and return True on success and False on failure
- """
- if self.has_section(section):
- RawConfigParser.set(self, section, option, value)
- else:
- self.add_section(section)
- RawConfigParser.set(self, section, option, value)
- return self.write_in_file(section, option, value)
-
- def set(self, option, value, section=DEFSECTION):
- """
- Set the value of an option temporarily
- """
- try:
- RawConfigParser.set(self, section, option, value)
- except NoSectionError:
- pass
-
- def to_dict(self):
- """
- Returns a dict of the form {section: {option: value, option: value}, …}
- """
- res = {}
- for section in self.sections():
- res[section] = {}
- for option in self.options(section):
- res[section][option] = self.get(option, "", section)
- return res
-
-
-def find_line(lines, start, end, option):
- """
- Get the number of the line containing the option in the
- relevant part of the config file.
-
- Returns -1 if the option isn’t found
- """
- current = start
- for line in lines[start:end]:
- if (line.startswith('%s ' % option) or
- line.startswith('%s=' % option)):
- return current
- current += 1
- return -1
-
-def file_ok(filepath):
- """
- Returns True if the file exists and is readable and writeable,
- False otherwise.
- """
- val = path.exists(filepath)
- val &= os.access(filepath, os.R_OK | os.W_OK)
- return bool(val)
-
-def check_create_config_dir():
- """
- create the configuration directory if it doesn't exist
- """
- CONFIG_HOME = environ.get("XDG_CONFIG_HOME")
- if not CONFIG_HOME:
- CONFIG_HOME = path.join(environ.get('HOME'), '.config')
- CONFIG_PATH = path.join(CONFIG_HOME, 'poezio')
-
- try:
- makedirs(CONFIG_PATH)
- except OSError:
- pass
- return CONFIG_PATH
-
-def check_create_cache_dir():
- """
- create the cache directory if it doesn't exist
- also create the subdirectories
- """
- global CACHE_DIR
- CACHE_HOME = environ.get("XDG_CACHE_HOME")
- if not CACHE_HOME:
- CACHE_HOME = path.join(environ.get('HOME'), '.cache')
- CACHE_DIR = path.join(CACHE_HOME, 'poezio')
-
- try:
- makedirs(CACHE_DIR)
- makedirs(path.join(CACHE_DIR, 'images'))
- except OSError:
- pass
-
-def check_config():
- """
- Check the config file and print results
- """
- result = {'missing': [], 'changed': []}
- for option in DEFAULT_CONFIG['Poezio']:
- value = config.get(option)
- if value != DEFAULT_CONFIG['Poezio'][option]:
- result['changed'].append((option, value, DEFAULT_CONFIG['Poezio'][option]))
- else:
- value = config.get(option, default='')
- upper = value.upper()
- default = str(DEFAULT_CONFIG['Poezio'][option]).upper()
- if upper != default:
- result['missing'].append(option)
-
- result['changed'].sort(key=lambda x: x[0])
- result['missing'].sort()
- if result['changed']:
- print('\033[1mOptions changed from the default configuration:\033[0m\n')
- for option, new_value, default in result['changed']:
- print(' \033[1m%s\033[0m = \033[33m%s\033[0m (default: \033[32m%s\033[0m)' % (option, new_value, default))
-
- if result['missing']:
- print('\n\033[1mMissing options:\033[0m (the defaults are used)\n')
- for option in result['missing']:
- print(' \033[31m%s\033[0m' % option)
-
-def run_cmdline_args(CONFIG_PATH):
- "Parse the command line arguments"
- global options
- options = parse_args(CONFIG_PATH)
-
- # Copy a default file if none exists
- if not path.isfile(options.filename):
- default = path.join(path.dirname(__file__), '../data/default_config.cfg')
- other = pkg_resources.resource_filename('poezio', 'default_config.cfg')
- if path.isfile(default):
- copy2(default, options.filename)
- elif path.isfile(other):
- copy2(other, options.filename)
-
- # Inside the nixstore and possibly other distributions, the reference
- # file is readonly, so is the copy.
- # Make it writable by the user who just created it.
- if os.path.exists(options.filename):
- os.chmod(options.filename,
- os.stat(options.filename).st_mode | stat.S_IWUSR)
-
- global firstrun
- firstrun = True
-
-def create_global_config():
- "Create the global config object, or crash"
- try:
- global config
- config = Config(options.filename, DEFAULT_CONFIG)
- except:
- import traceback
- sys.stderr.write('Poezio was unable to read or'
- ' parse the config file.\n')
- traceback.print_exc(limit=0)
- sys.exit(1)
-
-def check_create_log_dir():
- "Create the poezio logging directory if it doesn’t exist"
- global LOG_DIR
- LOG_DIR = config.get('log_dir')
-
- if not LOG_DIR:
-
- data_dir = environ.get('XDG_DATA_HOME')
- if not data_dir:
- home = environ.get('HOME')
- data_dir = path.join(home, '.local', 'share')
-
- LOG_DIR = path.join(data_dir, 'poezio', 'logs')
-
- LOG_DIR = path.expanduser(LOG_DIR)
-
- try:
- makedirs(LOG_DIR)
- except:
- pass
-
-def setup_logging():
- "Change the logging config according to the cmdline options and config"
- if config.get('log_errors'):
- LOGGING_CONFIG['root']['handlers'].append('error')
- LOGGING_CONFIG['handlers']['error'] = {
- 'level': 'ERROR',
- 'class': 'logging.FileHandler',
- 'filename': path.join(LOG_DIR, 'errors.log'),
- 'formatter': 'simple',
- }
-
- if options.debug:
- LOGGING_CONFIG['root']['handlers'].append('debug')
- LOGGING_CONFIG['handlers']['debug'] = {
- 'level':'DEBUG',
- 'class':'logging.FileHandler',
- 'filename': options.debug,
- 'formatter': 'simple',
- }
-
-
- if LOGGING_CONFIG['root']['handlers']:
- logging.config.dictConfig(LOGGING_CONFIG)
- else:
- logging.basicConfig(level=logging.CRITICAL)
-
- global log
- log = logging.getLogger(__name__)
-
-def post_logging_setup():
- # common imports slixmpp, which creates then its loggers, so
- # it needs to be after logger configuration
- from common import safeJID as JID
- global safeJID
- safeJID = JID
-
-LOGGING_CONFIG = {
- 'version': 1,
- 'disable_existing_loggers': True,
- 'formatters': {
- 'simple': {
- 'format': '%(asctime)s %(levelname)s:%(module)s:%(message)s'
- }
- },
- 'handlers': {
- },
- 'root': {
- 'handlers': [],
- 'propagate': True,
- 'level': 'DEBUG',
- }
-}
-
-# True if this is the first run, in this case we will display
-# some help in the info buffer
-firstrun = False
-
-# Global config object. Is setup in poezio.py
-config = None
-
-# The logger object for this module
-log = None
-
-# The command-line options
-options = None
-
-# delayed import from common.py
-safeJID = None
-
-# the global log dir
-LOG_DIR = ''
-
-# the global cache dir
-CACHE_DIR = ''