Commit 91b960b7 authored by mathieui's avatar mathieui

Handle I/O errors better

- Do not crash because of low disk space
- Notify the user whenever it happens
- A few functions now return a boolean instead of nothing
- Config.silent_set is Config.set_and_save without toggle and returning
  strings. It is used whenever we don’t need set_and_save
- Config.set_and_save now returns a tuple (that can be passed directly
  to core.information())

TODO: display the precise error to the user (instead of “unable to…”)
parent 16268ba9
......@@ -12,6 +12,8 @@ from/to the config file
DEFSECTION = "Poezio"
from gettext import gettext as _
from configparser import RawConfigParser, NoOptionError, NoSectionError
from os import environ, makedirs, path
from shutil import copy2
......@@ -171,10 +173,16 @@ class Config(RawConfigParser):
result_lines.append('%s = %s' % (option, value))
df = open(self.file_name, 'w', encoding='utf-8')
for line in result_lines:
df.write('%s\n' % line)
df.close()
try:
df = open(self.file_name, 'w', encoding='utf-8')
for line in result_lines:
df.write('%s\n' % line)
df.close()
except:
success = False
else:
success = True
return success
def set_and_save(self, option, value, section=DEFSECTION):
"""
......@@ -191,15 +199,26 @@ class Config(RawConfigParser):
elif current.lower() == "true":
value = "false"
else:
return "Could not toggle option: %s. Current value is %s." % (option, current or "empty")
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)
self.write_in_file(section, option, value)
return "%s=%s" % (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 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):
"""
......
......@@ -432,8 +432,9 @@ class Core(object):
"""
Save config in the file just before exit
"""
roster.save_to_config_file()
config.set_and_save('info_win_height', self.information_win_size, 'var')
if not roster.save_to_config_file() or \
not config.silent_set('info_win_height', self.information_win_size, 'var'):
self.information(_('Unable to write in the config file'), 'Error')
def on_roster_enter_key(self, roster_row):
"""
......@@ -595,8 +596,9 @@ class Core(object):
"""
self.status = Status(show=pres, message=msg)
if config.get('save_status', 'true').lower() != 'false':
config.set_and_save('status', pres if pres else '')
config.set_and_save('status_message', msg.replace('\n', '|') if msg else '')
if not config.silent_set('status', pres if pres else '') or \
not config.silent_set('status_message', msg.replace('\n', '|') if msg else ''):
self.information(_('Unable to write in the config file'), 'Error')
def get_bookmark_nickname(self, room_name):
"""
......@@ -1238,7 +1240,8 @@ class Core(object):
Enable/disable the left panel.
"""
enabled = config.get('enable_vertical_tab_list', 'false')
config.set_and_save('enable_vertical_tab_list', 'false' if enabled == 'true' else 'true')
if not config.silent_set('enable_vertical_tab_list', 'false' if enabled == 'true' else 'true'):
self.information(_('Unable to write in the config file'), 'Error')
self.call_for_resize()
def resize_global_information_win(self):
......@@ -2051,7 +2054,7 @@ class Core(object):
path = os.path.expanduser(value)
self.plugin_manager.on_plugins_conf_dir_change(path)
self.call_for_resize()
self.information(info, "Info")
self.information(*info)
def completion_set(self, the_input):
"""Completion for /set"""
......@@ -2336,7 +2339,8 @@ class Core(object):
return self.command_help('bind')
elif len(args) < 2:
args.append("")
config.set_and_save(args[0], args[1], section='bindings')
if not config.silent_set(args[0], args[1], section='bindings'):
self.information(_('Unable to write in the config file'), 'Error')
if args[1]:
self.information('%s is now bound to %s' % (args[0], args[1]), 'Info')
else:
......@@ -2716,7 +2720,8 @@ class Core(object):
conversation.remote_wants_chatstates = True
else:
conversation.remote_wants_chatstates = False
logger.log_message(jid.bare, remote_nick, body)
if not logger.log_message(jid.bare, remote_nick, body):
self.information(_('Unable to write in the log file'), 'Error')
if 'private' in config.get('beep_on', 'highlight private').split():
if config.get_by_tabname('disable_beep', 'false', jid.bare, False).lower() != 'true':
curses.beep()
......@@ -2967,7 +2972,8 @@ class Core(object):
if 'private' in config.get('beep_on', 'highlight private').split():
if config.get_by_tabname('disable_beep', 'false', jid.full, False).lower() != 'true':
curses.beep()
logger.log_message(jid.full.replace('/', '\\'), nick_from, body)
if not logger.log_message(jid.full.replace('/', '\\'), nick_from, body):
self.information(_('Unable to write in the log file'), 'Error')
if tab is self.current_tab():
self.refresh_window()
else:
......@@ -3147,7 +3153,8 @@ class Core(object):
if presence.match('presence/muc') or presence.xml.find('{http://jabber.org/protocol/muc#user}x'):
return
jid = presence['from']
logger.log_roster_change(jid.bare, 'got offline')
if not logger.log_roster_change(jid.bare, 'got offline'):
self.information(_('Unable to write in the log file'), 'Error')
# If a resource got offline, display the message in the conversation with this
# precise resource.
if jid.resource:
......@@ -3168,7 +3175,8 @@ class Core(object):
if contact is None:
# Todo, handle presence coming from contacts not in roster
return
logger.log_roster_change(jid.bare, 'got online')
if not logger.log_roster_change(jid.bare, 'got online'):
self.information(_('Unable to write in the log file'), 'Error')
resource = Resource(jid.full, {
'priority': presence.get_priority() or 0,
'status': presence['status'],
......@@ -3430,13 +3438,15 @@ class Core(object):
if input.value:
self.information('Setting new certificate: old: %s, new: %s' % (cert, found_cert), 'Info')
log.debug('Setting certificate to %s', found_cert)
config.set_and_save('certificate', found_cert)
if not config.silent_set('certificate', found_cert):
self.information(_('Unable to write in the config file'), 'Error')
else:
self.information('You refused to validate the certificate. You are now disconnected', 'Info')
self.xmpp.disconnect()
else:
log.debug('First time. Setting certificate to %s', found_cert)
config.set_and_save('certificate', found_cert)
if not config.silent_set('certificate', found_cert):
self.information(_('Unable to write in the config file'), 'Error')
......
......@@ -32,7 +32,10 @@ class Logger(object):
def __del__(self):
for opened_file in self.fds.values():
if opened_file:
opened_file.close()
try:
opened_file.close()
except: # Can't close? too bad
pass
def reload_all(self):
"""Close and reload all the file handles (on SIGHUP)"""
......@@ -106,7 +109,7 @@ class Logger(object):
else:
fd = self.check_and_create_log_dir(jid)
if not fd:
return
return True
try:
msg = clean_text(msg)
if date is None:
......@@ -117,18 +120,26 @@ class Logger(object):
fd.write(''.join((str_time, nick, ': ', msg, '\n')))
else:
fd.write(''.join((str_time, '* ', msg, '\n')))
except IOError:
pass
except:
return False
else:
fd.flush() # TODO do something better here?
try:
fd.flush() # TODO do something better here?
except:
return False
return True
def log_roster_change(self, jid, message):
if not self.roster_logfile:
try:
self.roster_logfile = open(os.path.join(DATA_HOME, 'logs', 'roster.log'), 'a')
except IOError:
return
self.roster_logfile.write('%s %s %s\n' % (datetime.now().strftime('%d-%m-%y [%H:%M:%S]'), jid, message))
self.roster_logfile.flush()
return False
try:
self.roster_logfile.write('%s %s %s\n' % (datetime.now().strftime('%d-%m-%y [%H:%M:%S]'), jid, message))
self.roster_logfile.flush()
except:
return False
return True
logger = Logger()
......@@ -141,7 +141,7 @@ class Roster(object):
folded_groups = ':'.join([group.name for group in self.groups.values()\
if group.folded])
log.debug('folded:%s\n' %folded_groups)
config.set_and_save('folded_roster_groups', folded_groups, 'var')
return config.silent_set('folded_roster_groups', folded_groups, 'var')
def get_nb_connected_contacts(self):
"""
......
......@@ -481,7 +481,8 @@ class ChatTab(Tab):
"""
Log the messages in the archives.
"""
logger.log_message(self.name, nickname, txt, date=time)
if not logger.log_message(self.name, nickname, txt, date=time):
self.core.information(_('Unable to write in the log file'), 'Error')
def add_message(self, txt, time=None, nickname=None, forced_user=None, nick_color=None, identifier=None):
self._text_buffer.add_message(txt, time=time,
......@@ -1696,7 +1697,8 @@ class MucTab(ChatTab):
to be
"""
if time is None and self.joined: # don't log the history messages
logger.log_message(self.name, nickname, txt)
if not logger.log_message(self.name, nickname, txt):
self.core.information(_('Unable to write in the log file'), 'Error')
def do_highlight(self, txt, time, nickname):
"""
......@@ -1844,7 +1846,8 @@ class PrivateTab(ChatTab):
msg = self.core.xmpp.make_message(self.get_name())
msg['type'] = 'chat'
msg['body'] = line
logger.log_message(self.get_name().replace('/', '\\'), self.own_nick, line)
if not logger.log_message(self.get_name().replace('/', '\\'), self.own_nick, line):
self.core.information(_('Unable to write in the log file'), 'Error')
# trigger the event BEFORE looking for colors.
# This lets a plugin insert \x19xxx} colors, that will
# be converted in xhtml.
......@@ -2722,9 +2725,11 @@ class RosterInfoTab(Tab):
"""
option = 'roster_show_offline'
if config.get(option, 'false') == 'false':
config.set_and_save(option, 'true')
success = config.silent_set(option, 'true')
else:
config.set_and_save(option, 'false')
success = config.silent_set(option, 'false')
if not success:
self.information(_('Unable to write in the config file'), 'Error')
return True
def on_slash(self):
......@@ -3036,7 +3041,8 @@ class ConversationTab(ChatTab):
self.core.events.trigger('conversation_say_after', msg, self)
self.last_sent_message = msg
msg.send()
logger.log_message(safeJID(self.get_dest_jid()).bare, self.core.own_nick, line)
if not logger.log_message(safeJID(self.get_dest_jid()).bare, self.core.own_nick, line):
self.core.information(_('Unable to write in the log file'), 'Error')
self.cancel_paused_delay()
self.text_win.refresh()
self.input.refresh()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment