From 03f12cf351e8e3ec3bae7ce27a0fa12bf9cc1af2 Mon Sep 17 00:00:00 2001 From: Nicolas Evrard Date: Fri, 7 Jul 2023 11:55:08 +0200 Subject: [PATCH] Robustly handle config file syntax errors [PREVIEW] --- tryton/tryton/config.py | 16 +++++++++++++++- tryton/tryton/gui/window/dblogin.py | 27 ++++++++++++++++++++------- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/tryton/tryton/config.py b/tryton/tryton/config.py index 8333119f2dc..f964dbdd568 100644 --- a/tryton/tryton/config.py +++ b/tryton/tryton/config.py @@ -6,7 +6,9 @@ import logging import optparse import os +import shutil import sys +import tempfile from gi.repository import GdkPixbuf @@ -152,7 +154,19 @@ def save(self): def load(self): parser = configparser.ConfigParser() - parser.read([self.rcfile]) + try: + parser.read([self.rcfile]) + except configparser.Error: + config_dir = os.path.dirname(self.rcfile) + with tempfile.NamedTemporaryFile( + delete=False, prefix='tryton_', suffix='.conf', + dir=config_dir) as temp_file: + temp_name = temp_file.name + shutil.copy(self.rcfile, temp_name) + logger.error( + f"Failed to parse {self.rcfile}. " + f"A backup can be found at {temp_name}", exc_info=True) + return for section in parser.sections(): for (name, value) in parser.items(section): if value.lower() == 'true': diff --git a/tryton/tryton/gui/window/dblogin.py b/tryton/tryton/gui/window/dblogin.py index b6f598dba46..f4873221a5d 100644 --- a/tryton/tryton/gui/window/dblogin.py +++ b/tryton/tryton/gui/window/dblogin.py @@ -6,6 +6,8 @@ import gettext import logging import os +import shutil +import tempfile import threading from gi.repository import GLib, GObject, Gtk @@ -495,20 +497,31 @@ def __init__(self): grid.attach(self.entry_date, 1, 6, 2, 1) # Profile informations - self.profile_cfg = os.path.join(get_config_dir(), 'profiles.cfg') + config_dir = get_config_dir() + self.profile_cfg = os.path.join(config_dir, 'profiles.cfg') self.profiles = configparser.ConfigParser() - if not os.path.exists(self.profile_cfg): + try: + self.profiles.read(self.profile_cfg) + except configparser.Error: + # reset self.profiles as parsing errors may leave wrong data in + # the parser + self.profiles = configparser.ConfigParser() + with tempfile.NamedTemporaryFile( + delete=False, prefix='profiles_', suffix='.cfg', + dir=config_dir) as temp_file: + temp_name = temp_file.name + shutil.copy(self.profile_cfg, temp_name) + logger.error( + f"Failed to parse {self.profiles_cfg}. " + f"A backup can be found at {temp_name}", + exc_info=True) + if not self.profiles.sections(): short_version = '.'.join(__version__.split('.', 2)[:2]) name = 'demo%s.tryton.org' % short_version self.profiles.add_section(name) self.profiles.set(name, 'host', name) self.profiles.set(name, 'database', 'demo%s' % short_version) self.profiles.set(name, 'username', 'demo') - else: - try: - self.profiles.read(self.profile_cfg) - except configparser.ParsingError: - logger.error("Fail to parse profiles.cfg", exc_info=True) for section in self.profiles.sections(): active = all(self.profiles.has_option(section, option) for option in ('host', 'database'))