From d4590949f7b691e3e1d6eff8fa339e62a44bae51 Mon Sep 17 00:00:00 2001
From: Florent Le Coz <louiz@louiz.org>
Date: Thu, 16 Oct 2014 10:43:57 +0200
Subject: Do not ignore empty topics

---
 src/core/handlers.py | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/src/core/handlers.py b/src/core/handlers.py
index ea67eaa6..75c372bb 100644
--- a/src/core/handlers.py
+++ b/src/core/handlers.py
@@ -960,18 +960,21 @@ def on_groupchat_subject(self, message):
     room_from = message.getMucroom()
     tab = self.get_tab_by_name(room_from, tabs.MucTab)
     subject = message['subject']
-    if not subject or not tab:
+    if subject is None or not tab:
         return
-    if nick_from:
-        tab.add_message(_("\x19%(info_col)s}%(nick)s set the subject to: %(subject)s") %
-                {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT), 'nick':nick_from, 'subject':subject},
-                time=None,
-                typ=2)
-    else:
-        tab.add_message(_("\x19%(info_col)s}The subject is: %(subject)s") %
-                {'subject':subject, 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
-                time=None,
-                typ=2)
+    if subject != tab.topic:
+        # Do not display the message if the subject did not change or if we
+        # receive an empty topic when joining the room.
+        if nick_from:
+            tab.add_message(_("\x19%(info_col)s}%(nick)s set the subject to: %(subject)s") %
+                    {'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT), 'nick':nick_from, 'subject':subject},
+                    time=None,
+                    typ=2)
+        else:
+            tab.add_message(_("\x19%(info_col)s}The subject is: %(subject)s") %
+                    {'subject':subject, 'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT)},
+                    time=None,
+                    typ=2)
     tab.topic = subject
     tab.topic_from = nick_from
     if self.get_tab_by_name(room_from, tabs.MucTab) is self.current_tab():
-- 
cgit v1.2.3


From a9f642f7438fe4489cdb9cc5ac59c929054656c8 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Thu, 16 Oct 2014 18:49:32 +0200
Subject: Extract XHTML-IM inline imags by default

- Add two new options: tmp_image_dir and extract_inline_images
- tmp_image_dir is $XDG_CACHE_HOME(usually ~/.cache)/poezio/images if unset
- Name the images from a SHA-1 of their data and their mimetype
- Output file:// links inside the message
---
 data/default_config.cfg      |  8 ++++++++
 doc/source/configuration.rst | 19 +++++++++++++++++++
 src/config.py                | 21 ++++++++++++++++++++-
 src/core/handlers.py         | 29 +++++++++++++++++++++++------
 src/poezio.py                |  1 +
 src/xhtml.py                 | 40 +++++++++++++++++++++++++++++++++-------
 6 files changed, 104 insertions(+), 14 deletions(-)

diff --git a/data/default_config.cfg b/data/default_config.cfg
index c1f766b0..35bc498b 100644
--- a/data/default_config.cfg
+++ b/data/default_config.cfg
@@ -379,6 +379,14 @@ ack_message_receipts = true
 # Ask for message delivery receipts (XEP-0184)
 request_message_receipts = true
 
+# Extract base64 images received in XHTML-IM messages
+# if true.
+extract_inline_images = true
+
+# The directory where the images will be saved; if unset,
+# defaults to $XDG_CACHE_HOME/poezio/images.
+tmp_image_dir =
+
 # Receive the tune notifications or not (in order to display informations
 # in the roster).
 # If this is set to false, then the display_tune_notifications
diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 32d82f7a..44fd8e11 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -851,6 +851,25 @@ Other
 
         The lang some automated entities will use when replying to you.
 
+    extract_inline_images
+
+        **Default value:** ``true``
+
+        Some clients send inline images in base64 inside some messages, which results in
+        an useless wall of text. If this option is ``true``, then that base64 text will
+        be replaced with a :file:`file://` link to the image file extracted in
+        :term:`tmp_image_dir` or :file:`$XDG_CACHE_HOME/poezio/images` by default, which
+        is usually :file:`~/.cache/poezio/images`
+
+    tmp_image_dir
+
+        **Default value:** ``[empty]``
+
+        The directory where poezio will save the images received, if
+        :term:`extract_inline_images` is set to true. If unset, poezio
+        will default to :file:`$XDG_CACHE_HOME/poezio/images` which is
+        usually :file:`~/.cache/poezio/images`.
+
     muc_history_length
 
         **Default value:** ``50``
diff --git a/src/config.py b/src/config.py
index 354c3447..5bd1ac17 100644
--- a/src/config.py
+++ b/src/config.py
@@ -361,7 +361,6 @@ def file_ok(filepath):
 def check_create_config_dir():
     """
     create the configuration directory if it doesn't exist
-    and copy the default config in it
     """
     CONFIG_HOME = environ.get("XDG_CONFIG_HOME")
     if not CONFIG_HOME:
@@ -374,6 +373,23 @@ def check_create_config_dir():
         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 run_cmdline_args(CONFIG_PATH):
     "Parse the command line arguments"
     global options
@@ -495,3 +511,6 @@ safeJID = None
 
 # the global log dir
 LOG_DIR = ''
+
+# the global cache dir
+CACHE_DIR = ''
diff --git a/src/core/handlers.py b/src/core/handlers.py
index 75c372bb..87aaecd5 100644
--- a/src/core/handlers.py
+++ b/src/core/handlers.py
@@ -10,6 +10,7 @@ import ssl
 import time
 from hashlib import sha1, sha512
 from gettext import gettext as _
+from os import path
 
 from sleekxmpp import InvalidJID
 from sleekxmpp.stanza import Message
@@ -24,7 +25,7 @@ import windows
 import xhtml
 import multiuserchat as muc
 from common import safeJID
-from config import config
+from config import config, CACHE_DIR
 from contact import Resource
 from logger import logger
 from roster import roster
@@ -178,7 +179,11 @@ def on_normal_message(self, message):
         return self.information('%s says: %s' % (message['from'], message['body']), 'Headline')
 
     use_xhtml = config.get('enable_xhtml_im', True)
-    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml)
+    tmp_dir = config.get('tmp_image_dir', '') or path.join(CACHE_DIR, 'images')
+    extract_images = config.get('extract_inline_images', True)
+    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
+                                              tmp_dir=tmp_dir,
+                                              extract_images=extract_images)
     if not body:
         return
 
@@ -223,7 +228,9 @@ def on_normal_message(self, message):
     self.events.trigger('conversation_msg', message, conversation)
     if not message['body']:
         return
-    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml)
+    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
+                                              tmp_dir=tmp_dir,
+                                              extract_images=extract_images)
     delayed, date = common.find_delayed_tag(message)
 
     def try_modify():
@@ -441,7 +448,11 @@ def on_groupchat_message(self, message):
 
     self.events.trigger('muc_msg', message, tab)
     use_xhtml = config.get('enable_xhtml_im', True)
-    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml)
+    tmp_dir = config.get('tmp_image_dir', '') or path.join(CACHE_DIR, 'images')
+    extract_images = config.get('extract_inline_images', True)
+    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
+                                              tmp_dir=tmp_dir,
+                                              extract_images=extract_images)
     if not body:
         return
 
@@ -498,7 +509,11 @@ def on_groupchat_private_message(self, message):
 
     room_from = jid.bare
     use_xhtml = config.get('enable_xhtml_im', True)
-    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml)
+    tmp_dir = config.get('tmp_image_dir', '') or path.join(CACHE_DIR, 'images')
+    extract_images = config.get('extract_inline_images', True)
+    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
+                                              tmp_dir=tmp_dir,
+                                              extract_images=extract_images)
     tab = self.get_tab_by_name(jid.full, tabs.PrivateTab) # get the tab with the private conversation
     ignore = config.get_by_tabname('ignore_private', False, room_from)
     if not tab: # It's the first message we receive: create the tab
@@ -511,7 +526,9 @@ def on_groupchat_private_message(self, message):
             self.xmpp.send_message(mto=jid.full, mbody=msg, mtype='chat')
         return
     self.events.trigger('private_msg', message, tab)
-    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml)
+    body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
+                                              tmp_dir=tmp_dir,
+                                              extract_images=extract_images)
     if not body or not tab:
         return
     replaced_id = message['replace']['id']
diff --git a/src/poezio.py b/src/poezio.py
index 1baf10eb..f82f103f 100644
--- a/src/poezio.py
+++ b/src/poezio.py
@@ -30,6 +30,7 @@ def main():
     config.run_cmdline_args(config_path)
     config.create_global_config()
     config.check_create_log_dir()
+    config.check_create_cache_dir()
     config.setup_logging()
     config.post_logging_setup()
 
diff --git a/src/xhtml.py b/src/xhtml.py
index 48664311..69519f8d 100644
--- a/src/xhtml.py
+++ b/src/xhtml.py
@@ -12,9 +12,13 @@ xhtml code to shell colors,
 poezio colors to xhtml code
 """
 
-import re
+import base64
 import curses
+import hashlib
+import re
+from os import path
 from sleekxmpp.xmlstream import ET
+from urllib.parse import unquote
 
 from io import BytesIO
 from xml import sax
@@ -178,10 +182,12 @@ colors = {
 whitespace_re = re.compile(r'\s+')
 
 xhtml_attr_re = re.compile(r'\x19-?\d[^}]*}|\x19[buaio]')
+xhtml_data_re = re.compile(r'data:image/([a-z]+);base64,(.+)')
 
 xhtml_simple_attr_re = re.compile(r'\x19\d')
 
-def get_body_from_message_stanza(message, use_xhtml=False):
+def get_body_from_message_stanza(message, use_xhtml=False,
+                                 tmp_dir=None, extract_images=False):
     """
     Returns a string with xhtml markups converted to
     poezio colors if there's an xhtml_im element, or
@@ -191,7 +197,8 @@ def get_body_from_message_stanza(message, use_xhtml=False):
         xhtml = message['html'].xml
         xhtml_body = xhtml.find('{http://www.w3.org/1999/xhtml}body')
         if xhtml_body:
-            content = xhtml_to_poezio_colors(xhtml_body)
+            content = xhtml_to_poezio_colors(xhtml_body, tmp_dir=tmp_dir,
+                                             extract_images=extract_images)
             content = content if content else message['body']
             return content or " "
     return message['body']
@@ -281,7 +288,7 @@ def trim(string):
     return re.sub(whitespace_re, ' ', string)
 
 class XHTMLHandler(sax.ContentHandler):
-    def __init__(self, force_ns=False):
+    def __init__(self, force_ns=False, tmp_dir=None, extract_images=False):
         self.builder = []
         self.formatting = []
         self.attrs = []
@@ -291,6 +298,9 @@ class XHTMLHandler(sax.ContentHandler):
         # do not care about xhtml-in namespace
         self.force_ns = force_ns
 
+        self.tmp_dir = tmp_dir
+        self.extract_images = extract_images
+
     @property
     def result(self):
         return ''.join(self.builder).strip()
@@ -331,7 +341,22 @@ class XHTMLHandler(sax.ContentHandler):
         elif name == 'em':
             self.append_formatting('\x19i')
         elif name == 'img':
-            builder.append(trim(attrs['src']))
+            if re.match(xhtml_data_re, attrs['src']) and self.extract_images:
+                type_, data = [i for i in re.split(xhtml_data_re, attrs['src']) if i]
+                bin_data = base64.b64decode(unquote(data))
+                filename = hashlib.sha1(bin_data).hexdigest() + '.' + type_
+                filepath = path.join(self.tmp_dir, filename)
+                if not path.exists(filepath):
+                    try:
+                        with open(filepath, 'wb') as fd:
+                            fd.write(bin_data)
+                        builder.append('file://%s' % filepath)
+                    except Exception as e:
+                        builder.append('[Error while saving image: %s]' % e)
+                else:
+                    builder.append('file://%s' % filepath)
+            else:
+                builder.append(trim(attrs['src']))
             if 'alt' in attrs:
                 builder.append(' (%s)' % trim(attrs['alt']))
         elif name == 'ul':
@@ -389,13 +414,14 @@ class XHTMLHandler(sax.ContentHandler):
         if 'title' in attrs:
             builder.append(' [' + attrs['title'] + ']')
 
-def xhtml_to_poezio_colors(xml, force=False):
+def xhtml_to_poezio_colors(xml, force=False, tmp_dir=None, extract_images=None):
     if isinstance(xml, str):
         xml = xml.encode('utf8')
     elif not isinstance(xml, bytes):
         xml = ET.tostring(xml)
 
-    handler = XHTMLHandler(force_ns=force)
+    handler = XHTMLHandler(force_ns=force, tmp_dir=tmp_dir,
+                           extract_images=extract_images)
     parser = sax.make_parser()
     parser.setFeature(sax.handler.feature_namespaces, True)
     parser.setContentHandler(handler)
-- 
cgit v1.2.3


From 55e6b9c4be2d38f07847f5f412ef3ce586df552e Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Sun, 19 Oct 2014 23:48:14 +0200
Subject: Document the group_corrections option

---
 doc/source/configuration.rst | 8 ++++++++
 doc/source/misc/correct.rst  | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/doc/source/configuration.rst b/doc/source/configuration.rst
index 44fd8e11..d0ff5bbe 100644
--- a/doc/source/configuration.rst
+++ b/doc/source/configuration.rst
@@ -316,6 +316,14 @@ to understand what is :ref:`carbons <carbons-details>` or
         If this is set to ``false``, you will no longer be subscribed to tune events,
         and the :term:`display_tune_notifications` option will be ignored.
 
+    group_corrections
+
+        **Default value:** ``true``
+
+        Enable a message to “correct” (replace) another message in the display if the
+        sender intended it as such. See :ref:`Message Correction <correct-feature>` for
+        more information.
+
     use_bookmark_method
 
         **Default value:** ``[empty]``
diff --git a/doc/source/misc/correct.rst b/doc/source/misc/correct.rst
index 61100634..fda4abcb 100644
--- a/doc/source/misc/correct.rst
+++ b/doc/source/misc/correct.rst
@@ -1,3 +1,5 @@
+.. _correct-feature:
+
 Message Correction
 ==================
 
-- 
cgit v1.2.3


From ece9b2082b9d092541d867211924bc2802f878ad Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 20 Oct 2014 19:52:04 +0200
Subject: =?UTF-8?q?Keep=20the=20default=20config=20options=20in=20a=20dict?=
 =?UTF-8?q?=20and=20make=20the=20=E2=80=9Cdefault=E2=80=9D=20parameter=20o?=
 =?UTF-8?q?f=20config.get()=20optional?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

the Config object now also optionally takes a dict of default values
in its constructor.
---
 src/config.py | 122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 119 insertions(+), 3 deletions(-)

diff --git a/src/config.py b/src/config.py
index 5bd1ac17..79c7381d 100644
--- a/src/config.py
+++ b/src/config.py
@@ -22,16 +22,129 @@ 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': False,
+        'autorejoin_delay': '5',
+        'autorejoin': False,
+        'beep_on': 'highlight private invite',
+        'ca_cert_path': '',
+        'certificate': '',
+        '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': '',
+        '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': False,
+        '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,
+        'exec_remote': False,
+        'extract_inline_images': True,
+        'filter_info_messages': '',
+        'force_encryption': True,
+        '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': '',
+        'lang': 'en',
+        'lazy_resize': True,
+        'load_log': 10,
+        'log_dir': '',
+        'logfile': 'logs',
+        'log_errors': True,
+        'max_lines_in_memory': 2048,
+        'max_messages_in_memory': 2048,
+        'max_nick_length': 25,
+        'muc_history_length': 50,
+        '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,
+        '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_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': False,
+        '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
+    }
+}
+
+
 class Config(RawConfigParser):
     """
     load/save the config to a file
     """
-    def __init__(self, file_name):
+    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:
@@ -43,13 +156,16 @@ class Config(RawConfigParser):
             if not self.has_section(section):
                 self.add_section(section)
 
-    def get(self, option, default, section=DEFSECTION):
+    def get(self, option, default='', 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 self.default and not default \
+                and self.default.get(section, {}).get(option) is not None:
+            default = self.default[section][option]
         try:
             if type(default) == int:
                 res = self.getint(option, section)
@@ -411,7 +527,7 @@ def create_global_config():
     "Create the global config object, or crash"
     try:
         global config
-        config = Config(options.filename)
+        config = Config(options.filename, DEFAULT_CONFIG)
     except:
         import traceback
         sys.stderr.write('Poezio was unable to read or'
-- 
cgit v1.2.3


From 7b01c62e07612a123f3ffe94583f51099e470c3b Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 20 Oct 2014 20:03:16 +0200
Subject: Change the API of Config.get_by_tabname

Make the "default" parameter optional and thus move it to the end of
the command with the other optional parameters.

And change all the calls.
---
 plugins/otr.py              | 12 +++----
 src/bookmark.py             |  2 +-
 src/config.py               |  7 ++--
 src/core/commands.py        |  5 +--
 src/core/handlers.py        | 34 +++++++++----------
 src/logger.py               | 10 +++---
 src/tabs/basetabs.py        |  9 ++---
 src/tabs/conversationtab.py | 15 ++++++---
 src/tabs/muctab.py          | 81 +++++++++++++++++++++------------------------
 src/tabs/privatetab.py      | 18 +++++-----
 10 files changed, 96 insertions(+), 97 deletions(-)

diff --git a/plugins/otr.py b/plugins/otr.py
index c2e5a663..44fdb323 100644
--- a/plugins/otr.py
+++ b/plugins/otr.py
@@ -210,7 +210,7 @@ def hl(tab):
 
     conv_jid = safeJID(tab.name)
     if 'private' in config.get('beep_on', 'highlight private').split():
-        if not config.get_by_tabname('disable_beep', False, conv_jid.bare, False):
+        if not config.get_by_tabname('disable_beep', conv_jid.bare, default=False):
             curses.beep()
 
 class PoezioContext(Context):
@@ -430,11 +430,11 @@ class Plugin(BasePlugin):
         jid = safeJID(jid).full
         if not jid in self.contexts:
             flags = POLICY_FLAGS.copy()
-            policy = self.config.get_by_tabname('encryption_policy', 'ondemand', jid).lower()
-            logging_policy = self.config.get_by_tabname('log', 'false', jid).lower()
-            allow_v2 = self.config.get_by_tabname('allow_v2', 'true', jid).lower()
+            policy = self.config.get_by_tabname('encryption_policy', jid, default='ondemand').lower()
+            logging_policy = self.config.get_by_tabname('log', jid, default='false').lower()
+            allow_v2 = self.config.get_by_tabname('allow_v2', jid, default='true').lower()
             flags['ALLOW_V2'] = (allow_v2 != 'false')
-            allow_v1 = self.config.get_by_tabname('allow_v1', 'false', jid).lower()
+            allow_v1 = self.config.get_by_tabname('allow_v1', jid, default='false').lower()
             flags['ALLOW_V1'] = (allow_v1 == 'true')
             self.contexts[jid] = PoezioContext(self.account, jid, self.core.xmpp, self.core)
             self.contexts[jid].log = 1 if logging_policy != 'false' else 0
@@ -544,7 +544,7 @@ class Plugin(BasePlugin):
             nick_color = get_theme().COLOR_REMOTE_USER
 
         body = txt.decode()
-        if self.config.get_by_tabname('decode_xhtml', True, msg['from'].bare):
+        if self.config.get_by_tabname('decode_xhtml', msg['from'].bare, default=True):
             try:
                 body = xhtml.xhtml_to_poezio_colors(body, force=True)
             except:
diff --git a/src/bookmark.py b/src/bookmark.py
index aa710d24..1807c45e 100644
--- a/src/bookmark.py
+++ b/src/bookmark.py
@@ -226,7 +226,7 @@ def get_local():
             nick = jid.resource
         else:
             nick = None
-        passwd = config.get_by_tabname('password', '', jid.bare, fallback=False) or None
+        passwd = config.get_by_tabname('password', jid.bare, fallback=False) or None
         b = Bookmark(jid.bare, autojoin=True, nick=nick, password=passwd, method='local')
         if not get_by_jid(b.jid):
             bookmarks.append(b)
diff --git a/src/config.py b/src/config.py
index 79c7381d..533838e1 100644
--- a/src/config.py
+++ b/src/config.py
@@ -181,15 +181,16 @@ class Config(RawConfigParser):
             return default
         return res
 
-    def get_by_tabname(
-            self, option, default, tabname,
-            fallback=True, fallback_server=True):
+    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
diff --git a/src/core/commands.py b/src/core/commands.py
index 7c0f56fa..ab441ae9 100644
--- a/src/core/commands.py
+++ b/src/core/commands.py
@@ -375,10 +375,7 @@ def command_join(self, arg, histo_length=None):
     if histo_length is not None:
         histo_length = str(histo_length)
     if password is None: # try to use a saved password
-        password = config.get_by_tabname('password',
-                                         None,
-                                         room,
-                                         fallback=False)
+        password = config.get_by_tabname('password', room, fallback=False)
     if tab and not tab.joined:
         if tab.last_connection:
             if tab.last_connection is not None:
diff --git a/src/core/handlers.py b/src/core/handlers.py
index 87aaecd5..4853c804 100644
--- a/src/core/handlers.py
+++ b/src/core/handlers.py
@@ -235,8 +235,8 @@ def on_normal_message(self, message):
 
     def try_modify():
         replaced_id = message['replace']['id']
-        if replaced_id and (config.get_by_tabname('group_corrections',
-            True, conv_jid.bare)):
+        if replaced_id and config.get_by_tabname('group_corrections',
+                                                 conv_jid.bare):
             try:
                 conversation.modify_message(body, replaced_id, message['id'], jid=jid,
                         nickname=remote_nick)
@@ -260,7 +260,7 @@ def on_normal_message(self, message):
         else:
             conversation.remote_wants_chatstates = False
     if 'private' in config.get('beep_on', 'highlight private').split():
-        if not config.get_by_tabname('disable_beep', False, conv_jid.bare, False):
+        if not config.get_by_tabname('disable_beep', conv_jid.bare):
             curses.beep()
     if self.current_tab() is not conversation:
         conversation.state = 'private'
@@ -310,7 +310,7 @@ def on_gaming_event(self, message):
     if contact.gaming:
         logger.log_roster_change(contact.bare_jid, 'is playing %s' % (common.format_gaming_string(contact.gaming)))
 
-    if old_gaming != contact.gaming and config.get_by_tabname('display_gaming_notifications', False, contact.bare_jid):
+    if old_gaming != contact.gaming and config.get_by_tabname('display_gaming_notifications', contact.bare_jid):
         if contact.gaming:
             self.information('%s is playing %s' % (contact.bare_jid, common.format_gaming_string(contact.gaming)), 'Gaming')
         else:
@@ -343,7 +343,7 @@ def on_mood_event(self, message):
     if contact.mood:
         logger.log_roster_change(contact.bare_jid, 'has now the mood: %s' % contact.mood)
 
-    if old_mood != contact.mood and config.get_by_tabname('display_mood_notifications', False, contact.bare_jid):
+    if old_mood != contact.mood and config.get_by_tabname('display_mood_notifications', contact.bare_jid):
         if contact.mood:
             self.information('Mood from '+ contact.bare_jid + ': ' + contact.mood, 'Mood')
         else:
@@ -382,7 +382,7 @@ def on_activity_event(self, message):
     if contact.activity:
         logger.log_roster_change(contact.bare_jid, 'has now the activity %s' % contact.activity)
 
-    if old_activity != contact.activity and config.get_by_tabname('display_activity_notifications', False, contact.bare_jid):
+    if old_activity != contact.activity and config.get_by_tabname('display_activity_notifications', contact.bare_jid):
         if contact.activity:
             self.information('Activity from '+ contact.bare_jid + ': ' + contact.activity, 'Activity')
         else:
@@ -416,7 +416,7 @@ def on_tune_event(self, message):
     if contact.tune:
         logger.log_roster_change(message['from'].bare, 'is now listening to %s' % common.format_tune_string(contact.tune))
 
-    if old_tune != contact.tune and config.get_by_tabname('display_tune_notifications', False, contact.bare_jid):
+    if old_tune != contact.tune and config.get_by_tabname('display_tune_notifications', contact.bare_jid):
         if contact.tune:
             self.information(
                     'Tune from '+ message['from'].bare + ': ' + common.format_tune_string(contact.tune),
@@ -460,8 +460,8 @@ def on_groupchat_message(self, message):
     delayed, date = common.find_delayed_tag(message)
     replaced_id = message['replace']['id']
     replaced = False
-    if replaced_id is not '' and (config.get_by_tabname(
-        'group_corrections', True, message['from'].bare)):
+    if replaced_id is not '' and config.get_by_tabname('group_corrections',
+                                                       message['from'].bare):
         try:
             if tab.modify_message(body, replaced_id, message['id'], time=date,
                     nickname=nick_from, user=user):
@@ -488,7 +488,7 @@ def on_groupchat_message(self, message):
         self.doupdate()
 
     if 'message' in config.get('beep_on', 'highlight private').split():
-        if (not config.get_by_tabname('disable_beep', False, room_from, False)
+        if (not config.get_by_tabname('disable_beep', room_from)
                 and self.own_nick != message['from'].resource):
             curses.beep()
 
@@ -515,13 +515,13 @@ def on_groupchat_private_message(self, message):
                                               tmp_dir=tmp_dir,
                                               extract_images=extract_images)
     tab = self.get_tab_by_name(jid.full, tabs.PrivateTab) # get the tab with the private conversation
-    ignore = config.get_by_tabname('ignore_private', False, room_from)
+    ignore = config.get_by_tabname('ignore_private', room_from)
     if not tab: # It's the first message we receive: create the tab
         if body and not ignore:
             tab = self.open_private_window(room_from, nick_from, False)
     if ignore:
         self.events.trigger('ignored_private', message, tab)
-        msg = config.get_by_tabname('private_auto_response', None, room_from)
+        msg = config.get_by_tabname('private_auto_response', room_from)
         if msg and body:
             self.xmpp.send_message(mto=jid.full, mbody=msg, mtype='chat')
         return
@@ -534,8 +534,8 @@ def on_groupchat_private_message(self, message):
     replaced_id = message['replace']['id']
     replaced = False
     user = tab.parent_muc.get_user_by_name(nick_from)
-    if replaced_id is not '' and (config.get_by_tabname(
-        'group_corrections', True, room_from)):
+    if replaced_id is not '' and config.get_by_tabname('group_corrections',
+                                                       room_from):
         try:
             tab.modify_message(body, replaced_id, message['id'], user=user, jid=message['from'],
                     nickname=nick_from)
@@ -555,7 +555,7 @@ def on_groupchat_private_message(self, message):
         else:
             tab.remote_wants_chatstates = False
     if 'private' in config.get('beep_on', 'highlight private').split():
-        if not config.get_by_tabname('disable_beep', False, jid.full, False):
+        if not config.get_by_tabname('disable_beep', jid.full):
             curses.beep()
     if tab is self.current_tab():
         self.refresh_window()
@@ -1050,8 +1050,8 @@ def room_error(self, error, room_name):
         msg = _('To provide a password in order to join the room, type "/join / password" (replace "password" by the real password)')
         tab.add_message(msg, typ=2)
     if code == '409':
-        if config.get('alternative_nickname', '') != '':
-            self.command_join('%s/%s'% (tab.name, tab.own_nick+config.get('alternative_nickname', '')))
+        if config.get('alternative_nickname') != '':
+            self.command_join('%s/%s'% (tab.name, tab.own_nick+config.get('alternative_nickname')))
         else:
             if not tab.joined:
                 tab.add_message(_('You can join the room with an other nick, by typing "/join /other_nick"'), typ=2)
diff --git a/src/logger.py b/src/logger.py
index 7ed0692f..53ec4311 100644
--- a/src/logger.py
+++ b/src/logger.py
@@ -78,7 +78,7 @@ class Logger(object):
         Check that the directory where we want to log the messages
         exists. if not, create it
         """
-        if not config.get_by_tabname('use_log', True, room):
+        if not config.get_by_tabname('use_log', room):
             return
         try:
             makedirs(log_dir)
@@ -106,10 +106,10 @@ class Logger(object):
         this function is a little bit more complicated than “read the last
         nb lines”.
         """
-        if config.get_by_tabname('load_log', 10, jid) <= 0:
+        if config.get_by_tabname('load_log', jid) <= 0:
             return
 
-        if not config.get_by_tabname('use_log', True, jid):
+        if not config.get_by_tabname('use_log', jid):
             return
 
         if nb <= 0:
@@ -197,7 +197,7 @@ class Logger(object):
             return True
 
         jid = str(jid).replace('/', '\\')
-        if not config.get_by_tabname('use_log', False, jid):
+        if not config.get_by_tabname('use_log', jid):
             return True
         if jid in self.fds.keys():
             fd = self.fds[jid]
@@ -245,7 +245,7 @@ class Logger(object):
         """
         Log a roster change
         """
-        if not config.get_by_tabname('use_log', False, jid):
+        if not config.get_by_tabname('use_log', jid):
             return True
         self.check_and_create_log_dir('', open_fd=False)
         if not self.roster_logfile:
diff --git a/src/tabs/basetabs.py b/src/tabs/basetabs.py
index c2efc9bc..0fde624c 100644
--- a/src/tabs/basetabs.py
+++ b/src/tabs/basetabs.py
@@ -592,8 +592,8 @@ class ChatTab(Tab):
         if not self.is_muc or self.joined:
             if state in ('active', 'inactive', 'gone') and self.inactive and not always_send:
                 return
-            if config.get_by_tabname('send_chat_states', True, self.general_jid, True) and \
-                    self.remote_wants_chatstates is not False:
+            if (config.get_by_tabname('send_chat_states', self.general_jid)
+                    and self.remote_wants_chatstates is not False):
                 msg = self.core.xmpp.make_message(self.get_dest_jid())
                 msg['type'] = self.message_type
                 msg['chat_state'] = state
@@ -607,7 +607,8 @@ class ChatTab(Tab):
         on the the current status of the input
         """
         name = self.general_jid
-        if config.get_by_tabname('send_chat_states', True, name, True) and self.remote_wants_chatstates:
+        if (config.get_by_tabname('send_chat_states', name)
+                and self.remote_wants_chatstates):
             needed = 'inactive' if self.inactive else 'active'
             self.cancel_paused_delay()
             if not empty_after:
@@ -622,7 +623,7 @@ class ChatTab(Tab):
         we create a timed event that will put us to paused
         in a few seconds
         """
-        if not config.get_by_tabname('send_chat_states', True, self.general_jid, True):
+        if not config.get_by_tabname('send_chat_states', self.general_jid):
             return
         if self.timed_event_paused:
             # check the weakref
diff --git a/src/tabs/conversationtab.py b/src/tabs/conversationtab.py
index 94cfd305..b2d526de 100644
--- a/src/tabs/conversationtab.py
+++ b/src/tabs/conversationtab.py
@@ -108,7 +108,7 @@ class ConversationTab(OneToOneTab):
         replaced = False
         if correct or msg['replace']['id']:
             msg['replace']['id'] = self.last_sent_message['id']
-            if config.get_by_tabname('group_corrections', True, self.name):
+            if config.get_by_tabname('group_corrections', self.name):
                 try:
                     self.modify_message(msg['body'], self.last_sent_message['id'], msg['id'], jid=self.core.xmpp.boundjid,
                             nickname=self.core.own_nick)
@@ -121,7 +121,8 @@ class ConversationTab(OneToOneTab):
             msg.enable('html')
             msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
             msg['body'] = xhtml.clean_text(msg['body'])
-        if config.get_by_tabname('send_chat_states', True, self.general_jid, True) and self.remote_wants_chatstates is not False:
+        if (config.get_by_tabname('send_chat_states', self.general_jid) and
+                self.remote_wants_chatstates is not False):
             needed = 'inactive' if self.inactive else 'active'
             msg['chat_state'] = needed
         if attention and self.remote_supports_attention:
@@ -316,7 +317,9 @@ class ConversationTab(OneToOneTab):
             self.state = 'normal'
         self.text_win.remove_line_separator()
         self.text_win.add_line_separator(self._text_buffer)
-        if config.get_by_tabname('send_chat_states', True, self.general_jid, True) and (not self.input.get_text() or not self.input.get_text().startswith('//')):
+        if (config.get_by_tabname('send_chat_states', self.general_jid)
+                and (not self.input.get_text()
+                    or not self.input.get_text().startswith('//'))):
             if resource:
                 self.send_chat_state('inactive')
         self.check_scrolled()
@@ -334,7 +337,9 @@ class ConversationTab(OneToOneTab):
 
         self.state = 'current'
         curses.curs_set(1)
-        if config.get_by_tabname('send_chat_states', True, self.general_jid, True) and (not self.input.get_text() or not self.input.get_text().startswith('//')):
+        if (config.get_by_tabname('send_chat_states', self.general_jid)
+                and (not self.input.get_text()
+                    or not self.input.get_text().startswith('//'))):
             if resource:
                 self.send_chat_state('active')
 
@@ -349,7 +354,7 @@ class ConversationTab(OneToOneTab):
 
     def on_close(self):
         Tab.on_close(self)
-        if config.get_by_tabname('send_chat_states', True, self.general_jid, True):
+        if config.get_by_tabname('send_chat_states', self.general_jid):
             self.send_chat_state('gone')
 
     def matching_names(self):
diff --git a/src/tabs/muctab.py b/src/tabs/muctab.py
index c36a533d..bd8ed312 100644
--- a/src/tabs/muctab.py
+++ b/src/tabs/muctab.py
@@ -469,8 +469,8 @@ class MucTab(ChatTab):
             char_quit = get_theme().CHAR_QUIT
             spec_col = dump_tuple(get_theme().COLOR_QUIT_CHAR)
 
-            if config.get_by_tabname('display_user_color_in_join_part', True,
-                                     self.general_jid, True):
+            if config.get_by_tabname('display_user_color_in_join_part',
+                                     self.general_jid):
                 color = dump_tuple(get_theme().COLOR_OWN_NICK)
             else:
                 color = 3
@@ -735,8 +735,8 @@ class MucTab(ChatTab):
             msg.enable('html')
             msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
             msg['body'] = xhtml.clean_text(msg['body'])
-        if (config.get_by_tabname('send_chat_states', True, self.general_jid,
-                True) and self.remote_wants_chatstates is not False):
+        if (config.get_by_tabname('send_chat_states', self.general_jid)
+                and self.remote_wants_chatstates is not False):
             msg['chat_state'] = needed
         if correct:
             msg['replace']['id'] = self.last_sent_message['id']
@@ -922,8 +922,8 @@ class MucTab(ChatTab):
             self.state = 'disconnected'
         self.text_win.remove_line_separator()
         self.text_win.add_line_separator(self._text_buffer)
-        if config.get_by_tabname('send_chat_states', True,
-                self.general_jid, True) and not self.input.get_text():
+        if (config.get_by_tabname('send_chat_states', self.general_jid) and
+                not self.input.get_text()):
             self.send_chat_state('inactive')
         self.check_scrolled()
 
@@ -933,8 +933,8 @@ class MucTab(ChatTab):
                 and not config.get('show_useless_separator', False)):
             self.text_win.remove_line_separator()
         curses.curs_set(1)
-        if self.joined and config.get_by_tabname('send_chat_states', True,
-                self.general_jid, True) and not self.input.get_text():
+        if self.joined and config.get_by_tabname('send_chat_states',
+                self.general_jid) and not self.input.get_text():
             self.send_chat_state('active')
 
     def on_info_win_size_changed(self):
@@ -1001,7 +1001,7 @@ class MucTab(ChatTab):
                     new_user.color = get_theme().COLOR_OWN_NICK
 
                     if config.get_by_tabname('display_user_color_in_join_part',
-                                             True, self.general_jid, True):
+                                             self.general_jid):
                         color = dump_tuple(new_user.color)
                     else:
                         color = 3
@@ -1120,11 +1120,11 @@ class MucTab(ChatTab):
         user = User(from_nick, affiliation,
                     show, status, role, jid)
         self.users.append(user)
-        hide_exit_join = config.get_by_tabname('hide_exit_join', -1,
-                                               self.general_jid, True)
+        hide_exit_join = config.get_by_tabname('hide_exit_join',
+                                               self.general_jid)
         if hide_exit_join != 0:
-            if config.get_by_tabname('display_user_color_in_join_part', True,
-                    self.general_jid, True):
+            if config.get_by_tabname('display_user_color_in_join_part',
+                                     self.general_jid):
                 color = dump_tuple(user.color)
             else:
                 color = 3
@@ -1161,8 +1161,8 @@ class MucTab(ChatTab):
             self.core.on_muc_own_nickchange(self)
         user.change_nick(new_nick)
 
-        if config.get_by_tabname('display_user_color_in_join_part', True,
-                                 self.general_jid, True):
+        if config.get_by_tabname('display_user_color_in_join_part',
+                                 self.general_jid):
             color = dump_tuple(user.color)
         else:
             color = 3
@@ -1204,10 +1204,9 @@ class MucTab(ChatTab):
             self.refresh_tab_win()
             self.core.current_tab().input.refresh()
             self.core.doupdate()
-            if config.get_by_tabname('autorejoin', False,
-                                     self.general_jid, True):
-                delay = config.get_by_tabname('autorejoin_delay', '5',
-                                              self.general_jid, True)
+            if config.get_by_tabname('autorejoin', self.general_jid):
+                delay = config.get_by_tabname('autorejoin_delay',
+                                              self.general_jid)
                 delay = common.parse_str_to_secs(delay)
                 if delay <= 0:
                     muc.join_groupchat(self.core, self.name, self.own_nick)
@@ -1221,7 +1220,7 @@ class MucTab(ChatTab):
 
         else:
             if config.get_by_tabname('display_user_color_in_join_part',
-                                     True, self.general_jid, True):
+                                     self.general_jid):
                 color = dump_tuple(user.color)
             else:
                 color = 3
@@ -1276,10 +1275,9 @@ class MucTab(ChatTab):
             self.core.current_tab().input.refresh()
             self.core.doupdate()
             # try to auto-rejoin
-            if config.get_by_tabname('autorejoin', False,
-                                     self.general_jid, True):
-                delay = config.get_by_tabname('autorejoin_delay', "5",
-                                              self.general_jid, True)
+            if config.get_by_tabname('autorejoin', self.general_jid):
+                delay = config.get_by_tabname('autorejoin_delay',
+                                              self.general_jid)
                 delay = common.parse_str_to_secs(delay)
                 if delay <= 0:
                     muc.join_groupchat(self.core, self.name, self.own_nick)
@@ -1291,8 +1289,8 @@ class MucTab(ChatTab):
                         self.name,
                         self.own_nick))
         else:
-            if config.get_by_tabname('display_user_color_in_join_part', True,
-                                     self.general_jid, True):
+            if config.get_by_tabname('display_user_color_in_join_part',
+                                     self.general_jid):
                 color = dump_tuple(user.color)
             else:
                 color = 3
@@ -1325,13 +1323,12 @@ class MucTab(ChatTab):
             self.core.disable_private_tabs(from_room)
             self.refresh_tab_win()
 
-        hide_exit_join = max(config.get_by_tabname('hide_exit_join', -1,
-                                                   self.general_jid, True),
-                             -1)
+        hide_exit_join = config.get_by_tabname('hide_exit_join',
+                                               self.general_jid)
 
-        if hide_exit_join == -1 or user.has_talked_since(hide_exit_join):
-            if config.get_by_tabname('display_user_color_in_join_part', True,
-                    self.general_jid, True):
+        if hide_exit_join <= -1 or user.has_talked_since(hide_exit_join):
+            if config.get_by_tabname('display_user_color_in_join_part',
+                                     self.general_jid):
                 color = dump_tuple(user.color)
             else:
                 color = 3
@@ -1371,8 +1368,8 @@ class MucTab(ChatTab):
         # build the message
         display_message = False # flag to know if something significant enough
                                 # to be displayed has changed
-        if config.get_by_tabname('display_user_color_in_join_part', True,
-                self.general_jid, True):
+        if config.get_by_tabname('display_user_color_in_join_part',
+                                 self.general_jid):
             color = dump_tuple(user.color)
         else:
             color = 3
@@ -1408,8 +1405,8 @@ class MucTab(ChatTab):
         if not display_message:
             return
         msg = msg[:-2] # remove the last ", "
-        hide_status_change = config.get_by_tabname('hide_status_change', -1,
-                                                   self.general_jid, True)
+        hide_status_change = config.get_by_tabname('hide_status_change',
+                                                   self.general_jid)
         if hide_status_change < -1:
             hide_status_change = -1
         if ((hide_status_change == -1 or \
@@ -1469,9 +1466,9 @@ class MucTab(ChatTab):
                     self.state = 'highlight'
                 highlighted = True
             else:
-                highlight_words = config.get_by_tabname('highlight_on', '',
-                                                        self.general_jid,
-                                                        True).split(':')
+                highlight_words = config.get_by_tabname('highlight_on',
+                                                        self.general_jid)
+                highlight_words = highlight_words.split(':')
                 for word in highlight_words:
                     if word and word.lower() in txt.lower():
                         if self.state != 'current':
@@ -1481,8 +1478,7 @@ class MucTab(ChatTab):
         if highlighted:
             beep_on = config.get('beep_on', 'highlight private').split()
             if 'highlight' in beep_on and 'message' not in beep_on:
-                if not config.get_by_tabname('disable_beep', False,
-                                             self.name, False):
+                if not config.get_by_tabname('disable_beep', self.name):
                     curses.beep()
         return highlighted
 
@@ -1521,8 +1517,7 @@ class MucTab(ChatTab):
         if (not time and nickname and nickname != self.own_nick
                     and self.state != 'current'):
             if (self.state != 'highlight' and
-                    config.get_by_tabname('notify_messages',
-                                          True, self.name)):
+                    config.get_by_tabname('notify_messages', self.name)):
                 self.state = 'message'
         if time and not txt.startswith('/me'):
             txt = '\x19%(info_col)s}%(txt)s' % {
diff --git a/src/tabs/privatetab.py b/src/tabs/privatetab.py
index c1e8c8e5..f4fa10cf 100644
--- a/src/tabs/privatetab.py
+++ b/src/tabs/privatetab.py
@@ -139,7 +139,7 @@ class PrivateTab(OneToOneTab):
         replaced = False
         if correct or msg['replace']['id']:
             msg['replace']['id'] = self.last_sent_message['id']
-            if config.get_by_tabname('group_corrections', True, self.name):
+            if config.get_by_tabname('group_corrections', self.name):
                 try:
                     self.modify_message(msg['body'], self.last_sent_message['id'], msg['id'],
                             user=user, jid=self.core.xmpp.boundjid, nickname=self.own_nick)
@@ -153,7 +153,8 @@ class PrivateTab(OneToOneTab):
             msg.enable('html')
             msg['html']['body'] = xhtml.poezio_colors_to_html(msg['body'])
             msg['body'] = xhtml.clean_text(msg['body'])
-        if config.get_by_tabname('send_chat_states', True, self.general_jid, True) and self.remote_wants_chatstates is not False:
+        if (config.get_by_tabname('send_chat_states', self.general_jid) and
+                self.remote_wants_chatstates is not False):
             needed = 'inactive' if self.inactive else 'active'
             msg['chat_state'] = needed
         if attention and self.remote_supports_attention:
@@ -278,9 +279,8 @@ class PrivateTab(OneToOneTab):
         self.text_win.remove_line_separator()
         self.text_win.add_line_separator(self._text_buffer)
         tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab)
-        if tab and tab.joined and config.get_by_tabname(
-                'send_chat_states', True, self.general_jid, True) and\
-                    not self.input.get_text() and self.on:
+        if tab and tab.joined and config.get_by_tabname('send_chat_states',
+                self.general_jid) and not self.input.get_text() and self.on:
             self.send_chat_state('inactive')
         self.check_scrolled()
 
@@ -288,9 +288,8 @@ class PrivateTab(OneToOneTab):
         self.state = 'current'
         curses.curs_set(1)
         tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab)
-        if tab and tab.joined and config.get_by_tabname(
-                'send_chat_states', True, self.general_jid, True) and\
-                    not self.input.get_text() and self.on:
+        if tab and tab.joined and config.get_by_tabname('send_chat_states',
+                self.general_jid,) and not self.input.get_text() and self.on:
             self.send_chat_state('active')
 
     def on_info_win_size_changed(self):
@@ -334,7 +333,8 @@ class PrivateTab(OneToOneTab):
         self.check_features()
         tab = self.core.get_tab_by_name(safeJID(self.name).bare, MucTab)
         color = 3
-        if tab and config.get_by_tabname('display_user_color_in_join_part', '', self.general_jid, True):
+        if tab and config.get_by_tabname('display_user_color_in_join_part',
+                                         self.general_jid):
             user = tab.get_user_by_name(nick)
             if user:
                 color = dump_tuple(user.color)
-- 
cgit v1.2.3


From f9734cde5623aaf701e222b00d5eebdf7a152772 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 20 Oct 2014 21:04:14 +0200
Subject: Remove the (sometimes wrong) default values in the config.get() calls

---
 src/bookmark.py           | 10 +++----
 src/config.py             |  5 ++--
 src/connection.py         | 50 ++++++++++++++++----------------
 src/core/commands.py      | 12 ++++----
 src/core/completions.py   |  6 ++--
 src/core/core.py          | 72 +++++++++++++++++++++++------------------------
 src/core/handlers.py      | 48 +++++++++++++++----------------
 src/logger.py             |  2 +-
 src/plugin_manager.py     |  4 +--
 src/roster.py             |  6 ++--
 src/tabs/basetabs.py      | 10 +++----
 src/tabs/muctab.py        | 18 ++++++------
 src/tabs/privatetab.py    |  2 +-
 src/tabs/rostertab.py     |  4 +--
 src/text_buffer.py        |  4 +--
 src/theming.py            |  4 +--
 src/windows/funcs.py      |  2 +-
 src/windows/info_bar.py   | 16 +++++------
 src/windows/inputs.py     |  2 +-
 src/windows/muc.py        | 10 +++----
 src/windows/roster_win.py | 16 +++++------
 src/windows/text_win.py   |  8 +++---
 22 files changed, 153 insertions(+), 158 deletions(-)

diff --git a/src/bookmark.py b/src/bookmark.py
index 1807c45e..672fb4a5 100644
--- a/src/bookmark.py
+++ b/src/bookmark.py
@@ -23,7 +23,7 @@ def xml_iter(xml, tag=''):
     else:
         return xml.getiterator(tag)
 
-preferred = config.get('use_bookmarks_method', 'pep').lower()
+preferred = config.get('use_bookmarks_method').lower()
 if preferred not in ('pep', 'privatexml'):
     preferred = 'privatexml'
 not_preferred = 'privatexml' if preferred == 'pep' else 'privatexml'
@@ -155,8 +155,8 @@ def save_local():
 def save(xmpp, core=None):
     """Save all the bookmarks."""
     save_local()
-    if config.get('use_remote_bookmarks', True):
-        preferred = config.get('use_bookmarks_method', 'privatexml')
+    if config.get('use_remote_bookmarks'):
+        preferred = config.get('use_bookmarks_method')
         if not save_remote(xmpp, method=preferred) and core:
             core.information('Could not save bookmarks.', 'Error')
             return False
@@ -192,7 +192,7 @@ def get_remote(xmpp):
     """Add the remotely stored bookmarks to the list."""
     if xmpp.anon:
         return
-    method = config.get('use_bookmarks_method', '')
+    method = config.get('use_bookmarks_method')
     if not method:
         pep, privatexml = True, True
         for method in methods[1:]:
@@ -214,7 +214,7 @@ def get_remote(xmpp):
 
 def get_local():
     """Add the locally stored bookmarks to the list."""
-    rooms = config.get('rooms', '')
+    rooms = config.get('rooms')
     if not rooms:
         return
     rooms = rooms.split(':')
diff --git a/src/config.py b/src/config.py
index 533838e1..0504dbd5 100644
--- a/src/config.py
+++ b/src/config.py
@@ -133,7 +133,6 @@ DEFAULT_CONFIG = {
     }
 }
 
-
 class Config(RawConfigParser):
     """
     load/save the config to a file
@@ -539,7 +538,7 @@ def create_global_config():
 def check_create_log_dir():
     "Create the poezio logging directory if it doesn’t exist"
     global LOG_DIR
-    LOG_DIR = config.get('log_dir', '')
+    LOG_DIR = config.get('log_dir')
 
     if not LOG_DIR:
 
@@ -559,7 +558,7 @@ def check_create_log_dir():
 
 def setup_logging():
     "Change the logging config according to the cmdline options and config"
-    if config.get('log_errors', True):
+    if config.get('log_errors'):
         LOGGING_CONFIG['root']['handlers'].append('error')
         LOGGING_CONFIG['handlers']['error'] = {
                 'level': 'ERROR',
diff --git a/src/connection.py b/src/connection.py
index b5b7e12e..e6188e05 100644
--- a/src/connection.py
+++ b/src/connection.py
@@ -29,28 +29,28 @@ class Connection(sleekxmpp.ClientXMPP):
     """
     __init = False
     def __init__(self):
-        resource = config.get('resource', '')
-        if config.get('jid', ''):
+        resource = config.get('resource')
+        if config.get('jid'):
             # Field used to know if we are anonymous or not.
             # many features will be handled differently
             # depending on this setting
             self.anon = False
-            jid = '%s' % config.get('jid', '')
+            jid = '%s' % config.get('jid')
             if resource:
                 jid = '%s/%s'% (jid, resource)
-            password = config.get('password', '') or getpass.getpass()
+            password = config.get('password') or getpass.getpass()
         else: # anonymous auth
             self.anon = True
-            jid = config.get('server', 'anon.jeproteste.info')
+            jid = config.get('server')
             if resource:
                 jid = '%s/%s' % (jid, resource)
             password = None
         jid = safeJID(jid)
         # TODO: use the system language
         sleekxmpp.ClientXMPP.__init__(self, jid, password,
-                                      lang=config.get('lang', 'en'))
+                                      lang=config.get('lang'))
 
-        force_encryption = config.get('force_encryption', True)
+        force_encryption = config.get('force_encryption')
         if force_encryption:
             self['feature_mechanisms'].unencrypted_plain = False
             self['feature_mechanisms'].unencrypted_digest = False
@@ -58,7 +58,7 @@ class Connection(sleekxmpp.ClientXMPP):
             self['feature_mechanisms'].unencrypted_scram = False
 
         self.core = None
-        self.auto_reconnect = config.get('auto_reconnect', False)
+        self.auto_reconnect = config.get('auto_reconnect')
         self.reconnect_max_attempts = 0
         self.auto_authorize = None
         # prosody defaults, lowest is AES128-SHA, it should be a minimum
@@ -66,9 +66,9 @@ class Connection(sleekxmpp.ClientXMPP):
         self.ciphers = config.get('ciphers',
                                   'HIGH+kEDH:HIGH+kEECDH:HIGH:!PSK'
                                     ':!SRP:!3DES:!aNULL')
-        self.ca_certs = config.get('ca_cert_path', '') or None
-        interval = config.get('whitespace_interval', '300')
-        if interval.isdecimal() and int(interval) > 0:
+        self.ca_certs = config.get('ca_cert_path') or None
+        interval = config.get('whitespace_interval')
+        if int(interval) > 0:
             self.whitespace_keepalive_interval = int(interval)
         else:
             self.whitespace_keepalive_interval = 300
@@ -90,34 +90,32 @@ class Connection(sleekxmpp.ClientXMPP):
         # without a body
         XEP_0184._filter_add_receipt_request = fixes._filter_add_receipt_request
         self.register_plugin('xep_0184')
-        self.plugin['xep_0184'].auto_ack = config.get('ack_message_receipts',
-                                                      True)
-        self.plugin['xep_0184'].auto_request = config.get(
-                'request_message_receipts', True)
+        self.plugin['xep_0184'].auto_ack = config.get('ack_message_receipts')
+        self.plugin['xep_0184'].auto_request = config.get('request_message_receipts')
 
         self.register_plugin('xep_0191')
         self.register_plugin('xep_0199')
         self.set_keepalive_values()
 
-        if config.get('enable_user_tune', True):
+        if config.get('enable_user_tune'):
             self.register_plugin('xep_0118')
 
-        if config.get('enable_user_nick', True):
+        if config.get('enable_user_nick'):
             self.register_plugin('xep_0172')
 
-        if config.get('enable_user_mood', True):
+        if config.get('enable_user_mood'):
             self.register_plugin('xep_0107')
 
-        if config.get('enable_user_activity', True):
+        if config.get('enable_user_activity'):
             self.register_plugin('xep_0108')
 
-        if config.get('enable_user_gaming', True):
+        if config.get('enable_user_gaming'):
             self.register_plugin('xep_0196')
 
-        if config.get('send_poezio_info', True):
+        if config.get('send_poezio_info'):
             info = {'name':'poezio',
                     'version': options.version}
-            if config.get('send_os_info', True):
+            if config.get('send_os_info'):
                 info['os'] = common.get_os_info()
             self.plugin['xep_0030'].set_identities(
                     identities=set([('client', 'pc', None, 'Poezio')]))
@@ -126,7 +124,7 @@ class Connection(sleekxmpp.ClientXMPP):
             self.plugin['xep_0030'].set_identities(
                     identities=set([('client', 'pc', None, '')]))
         self.register_plugin('xep_0092', pconfig=info)
-        if config.get('send_time', True):
+        if config.get('send_time'):
             self.register_plugin('xep_0202')
         self.register_plugin('xep_0224')
         self.register_plugin('xep_0249')
@@ -141,8 +139,8 @@ class Connection(sleekxmpp.ClientXMPP):
         is changed.
         Unload and reload the ping plugin, with the new values.
         """
-        ping_interval = config.get('connection_check_interval', 60)
-        timeout_delay = config.get('connection_timeout_delay', 10)
+        ping_interval = config.get('connection_check_interval')
+        timeout_delay = config.get('connection_timeout_delay')
         if timeout_delay <= 0:
             # We help the stupid user (with a delay of 0, poezio will try to
             # reconnect immediately because the timeout is immediately
@@ -161,7 +159,7 @@ class Connection(sleekxmpp.ClientXMPP):
 
         TODO: try multiple servers with anon auth.
         """
-        custom_host = config.get('custom_host', '')
+        custom_host = config.get('custom_host')
         custom_port = config.get('custom_port', 5222)
         if custom_port == -1:
             custom_port = 5222
diff --git a/src/core/commands.py b/src/core/commands.py
index ab441ae9..daf420fb 100644
--- a/src/core/commands.py
+++ b/src/core/commands.py
@@ -369,7 +369,7 @@ def command_join(self, arg, histo_length=None):
         room = room[1:]
     current_status = self.get_status()
     if not histo_length:
-        histo_length = config.get('muc_history_length', 20)
+        histo_length = config.get('muc_history_length')
         if histo_length == -1:
             histo_length = None
     if histo_length is not None:
@@ -473,7 +473,7 @@ def command_bookmark(self, arg=''):
     /bookmark [room][/nick] [autojoin] [password]
     """
 
-    if not config.get('use_remote_bookmarks', True):
+    if not config.get('use_remote_bookmarks'):
         self.command_bookmark_local(arg)
         return
     args = common.shell_split(arg)
@@ -533,7 +533,7 @@ def command_bookmark(self, arg=''):
     if not bm:
         bm = bookmark.Bookmark(roomname)
         bookmark.bookmarks.append(bm)
-    bm.method = config.get('use_bookmarks_method', 'pep')
+    bm.method = config.get('use_bookmarks_method')
     if nick:
         bm.nick = nick
     if password:
@@ -808,11 +808,11 @@ def command_quit(self, arg=''):
         msg = arg
     else:
         msg = None
-    if config.get('enable_user_mood', True):
+    if config.get('enable_user_mood'):
         self.xmpp.plugin['xep_0107'].stop(block=False)
-    if config.get('enable_user_activity', True):
+    if config.get('enable_user_activity'):
         self.xmpp.plugin['xep_0108'].stop(block=False)
-    if config.get('enable_user_gaming', True):
+    if config.get('enable_user_gaming'):
         self.xmpp.plugin['xep_0196'].stop(block=False)
     self.save_config()
     self.plugin_manager.disable_plugins()
diff --git a/src/core/completions.py b/src/core/completions.py
index f8fb7fc8..ca0bb41e 100644
--- a/src/core/completions.py
+++ b/src/core/completions.py
@@ -46,7 +46,7 @@ def completion_presence(self, the_input):
 
 def completion_theme(self, the_input):
     """ Completion for /theme"""
-    themes_dir = config.get('themes_dir', '')
+    themes_dir = config.get('themes_dir')
     themes_dir = themes_dir or\
     os.path.join(os.environ.get('XDG_DATA_HOME') or\
                      os.path.join(os.environ.get('HOME'), '.local', 'share'),
@@ -190,7 +190,7 @@ def completion_bookmark(self, the_input):
         tab = self.get_tab_by_name(jid.bare, tabs.MucTab)
         nicks = [tab.own_nick] if tab else []
         default = os.environ.get('USER') if os.environ.get('USER') else 'poezio'
-        nick = config.get('default_nick', '')
+        nick = config.get('default_nick')
         if not nick:
             if not default in nicks:
                 nicks.append(default)
@@ -371,7 +371,7 @@ def completion_bookmark_local(self, the_input):
         tab = self.get_tab_by_name(jid.bare, tabs.MucTab)
         nicks = [tab.own_nick] if tab else []
         default = os.environ.get('USER') if os.environ.get('USER') else 'poezio'
-        nick = config.get('default_nick', '')
+        nick = config.get('default_nick')
         if not nick:
             if not default in nicks:
                 nicks.append(default)
diff --git a/src/core/core.py b/src/core/core.py
index 985fb752..52199206 100644
--- a/src/core/core.py
+++ b/src/core/core.py
@@ -64,10 +64,10 @@ class Core(object):
         sys.excepthook = self.on_exception
         self.connection_time = time.time()
         self.stdscr = None
-        status = config.get('status', None)
+        status = config.get('status')
         status = possible_show.get(status, None)
         self.status = Status(show=status,
-                message=config.get('status_message', ''))
+                message=config.get('status_message'))
         self.running = True
         self.xmpp = singleton.Singleton(connection.Connection)
         self.xmpp.core = self
@@ -82,7 +82,7 @@ class Core(object):
         # that are displayed in almost all tabs, in an
         # information window.
         self.information_buffer = TextBuffer()
-        self.information_win_size = config.get('info_win_height', 2, 'var')
+        self.information_win_size = config.get('info_win_height', section='var')
         self.information_win = windows.TextWin(300)
         self.information_buffer.add_window(self.information_win)
         self.left_tab_win = None
@@ -96,7 +96,7 @@ class Core(object):
         self._current_tab_nb = 0
         self.previous_tab_nb = 0
 
-        own_nick = config.get('default_nick', '')
+        own_nick = config.get('default_nick')
         own_nick = own_nick or self.xmpp.boundjid.user
         own_nick = own_nick or os.environ.get('USER')
         own_nick = own_nick or 'poezio'
@@ -119,7 +119,7 @@ class Core(object):
         self.register_initial_commands()
 
         # We are invisible
-        if not config.get('send_initial_presence', True):
+        if not config.get('send_initial_presence'):
             del self.commands['status']
             del self.commands['show']
 
@@ -241,19 +241,19 @@ class Core(object):
                                     connection.MatchAll(None),
                                     self.incoming_stanza)
         self.xmpp.register_handler(self.all_stanzas)
-        if config.get('enable_user_tune', True):
+        if config.get('enable_user_tune'):
             self.xmpp.add_event_handler("user_tune_publish",
                                         self.on_tune_event)
-        if config.get('enable_user_nick', True):
+        if config.get('enable_user_nick'):
             self.xmpp.add_event_handler("user_nick_publish",
                                         self.on_nick_received)
-        if config.get('enable_user_mood', True):
+        if config.get('enable_user_mood'):
             self.xmpp.add_event_handler("user_mood_publish",
                                         self.on_mood_event)
-        if config.get('enable_user_activity', True):
+        if config.get('enable_user_activity'):
             self.xmpp.add_event_handler("user_activity_publish",
                                         self.on_activity_event)
-        if config.get('enable_user_gaming', True):
+        if config.get('enable_user_gaming'):
             self.xmpp.add_event_handler("user_gaming_publish",
                                         self.on_gaming_event)
 
@@ -342,13 +342,14 @@ class Core(object):
         """
         Called when the request_message_receipts option changes
         """
-        self.xmpp.plugin['xep_0184'].auto_request = config.get(option, True)
+        self.xmpp.plugin['xep_0184'].auto_request = config.get(option,
+                                                               default=True)
 
     def on_ack_receipts_config_change(self, option, value):
         """
         Called when the ack_message_receipts option changes
         """
-        self.xmpp.plugin['xep_0184'].auto_ack = config.get(option, True)
+        self.xmpp.plugin['xep_0184'].auto_ack = config.get(option, default=True)
 
     def on_plugins_dir_config_change(self, option, value):
         """
@@ -398,7 +399,7 @@ class Core(object):
             old_section = old_config.get(section, {})
             for option in config.options(section):
                 old_value = old_section.get(option)
-                new_value = config.get(option, "", section)
+                new_value = config.get(option, default="", section=section)
                 if new_value != old_value:
                     self.trigger_configuration_change(option, new_value)
         log.debug("Config reloaded.")
@@ -420,11 +421,11 @@ class Core(object):
                 }
 
         log.error("%s received. Exiting…", signals[sig])
-        if config.get('enable_user_mood', True):
+        if config.get('enable_user_mood'):
             self.xmpp.plugin['xep_0107'].stop(block=False)
-        if config.get('enable_user_activity', True):
+        if config.get('enable_user_activity'):
             self.xmpp.plugin['xep_0108'].stop(block=False)
-        if config.get('enable_user_gaming', True):
+        if config.get('enable_user_gaming'):
             self.xmpp.plugin['xep_0196'].stop(block=False)
         self.plugin_manager.disable_plugins()
         self.disconnect('')
@@ -439,7 +440,7 @@ class Core(object):
         """
         Load the plugins on startup.
         """
-        plugins = config.get('plugins_autoload', '')
+        plugins = config.get('plugins_autoload')
         if ':' in plugins:
             for plugin in plugins.split(':'):
                 self.plugin_manager.load(plugin)
@@ -659,9 +660,9 @@ class Core(object):
         work. If you try to do anything else, your |, [, <<, etc will be
         interpreted as normal command arguments, not shell special tokens.
         """
-        if config.get('exec_remote', False):
+        if config.get('exec_remote'):
             # We just write the command in the fifo
-            fifo_path = config.get('remote_fifo_path', './')
+            fifo_path = config.get('remote_fifo_path')
             if not self.remote_fifo:
                 try:
                     self.remote_fifo = Fifo(os.path.join(fifo_path,
@@ -755,7 +756,7 @@ class Core(object):
         or to use it when joining a new muc)
         """
         self.status = Status(show=pres, message=msg)
-        if config.get('save_status', True):
+        if config.get('save_status'):
             ok = config.silent_set('status', pres if pres else '')
             msg = msg.replace('\n', '|') if msg else ''
             ok = ok and config.silent_set('status_message', msg)
@@ -993,7 +994,7 @@ class Core(object):
             return False
         elif not self.tabs[old_pos]:
             return False
-        if config.get('create_gaps', False):
+        if config.get('create_gaps'):
             return self.insert_tab_gaps(old_pos, new_pos)
         return self.insert_tab_nogaps(old_pos, new_pos)
 
@@ -1237,7 +1238,7 @@ class Core(object):
         if self.previous_tab_nb != nb:
             self.current_tab_nb = self.previous_tab_nb
             self.previous_tab_nb = 0
-        if config.get('create_gaps', False):
+        if config.get('create_gaps'):
             if nb >= len(self.tabs) - 1:
                 self.tabs.remove(tab)
                 nb -= 1
@@ -1288,7 +1289,7 @@ class Core(object):
         """
         Displays an informational message in the "Info" buffer
         """
-        filter_messages = config.get('filter_info_messages', '').split(':')
+        filter_messages = config.get('filter_info_messages').split(':')
         for words in filter_messages:
             if words and words in msg:
                 log.debug('Did not show the message:\n\t%s> %s', typ, msg)
@@ -1298,12 +1299,11 @@ class Core(object):
         nb_lines = self.information_buffer.add_message(msg,
                                                        nickname=typ,
                                                        nick_color=color)
-        popup_on = config.get('information_buffer_popup_on',
-                              'error roster warning help info').split()
+        popup_on = config.get('information_buffer_popup_on').split()
         if isinstance(self.current_tab(), tabs.RosterInfoTab):
             self.refresh_window()
         elif typ != '' and typ.lower() in popup_on:
-            popup_time = config.get('popup_time', 4) + (nb_lines - 1) * 2
+            popup_time = config.get('popup_time') + (nb_lines - 1) * 2
             self.pop_information_win_up(nb_lines, popup_time)
         else:
             if self.information_win_size != 0:
@@ -1496,7 +1496,7 @@ class Core(object):
         """
         Enable/disable the left panel.
         """
-        enabled = config.get('enable_vertical_tab_list', False)
+        enabled = config.get('enable_vertical_tab_list')
         if not config.silent_set('enable_vertical_tab_list', str(not enabled)):
             self.information(_('Unable to write in the config file'), 'Error')
         self.call_for_resize()
@@ -1523,14 +1523,14 @@ class Core(object):
         """
         with g_lock:
             height, width = self.stdscr.getmaxyx()
-            if config.get('enable_vertical_tab_list', False):
+            if config.get('enable_vertical_tab_list'):
 
                 if self.size.core_degrade_x:
                     return
                 try:
                     height, _ = self.stdscr.getmaxyx()
                     truncated_win = self.stdscr.subwin(height,
-                            config.get('vertical_tab_list_size', 20),
+                            config.get('vertical_tab_list_size'),
                             0, 0)
                 except:
                     log.error('Curses error on infobar resize', exc_info=True)
@@ -1569,12 +1569,12 @@ class Core(object):
         # on the left remaining space
         with g_lock:
             height, width = self.stdscr.getmaxyx()
-        if (config.get('enable_vertical_tab_list', False) and
+        if (config.get('enable_vertical_tab_list') and
                 not self.size.core_degrade_x):
             with g_lock:
                 try:
                     scr = self.stdscr.subwin(0,
-                            config.get('vertical_tab_list_size', 20))
+                            config.get('vertical_tab_list_size'))
                 except:
                     log.error('Curses error on resize', exc_info=True)
                     return
@@ -1585,7 +1585,7 @@ class Core(object):
         self.resize_global_information_win()
         with g_lock:
             for tab in self.tabs:
-                if config.get('lazy_resize', True):
+                if config.get('lazy_resize'):
                     tab.need_resize = True
                 else:
                     tab.resize()
@@ -1818,7 +1818,7 @@ class Core(object):
                 usage='<jid>',
                 shortdesc=_('List available ad-hoc commands on the given jid'))
 
-        if config.get('enable_user_activity', True):
+        if config.get('enable_user_activity'):
             self.register_command('activity', self.command_activity,
                     usage='[<general> [specific] [text]]',
                     desc=_('Send your current activity to your contacts '
@@ -1826,7 +1826,7 @@ class Core(object):
                            '"stop broadcasting an activity".'),
                     shortdesc=_('Send your activity.'),
                     completion=self.completion_activity)
-        if config.get('enable_user_mood', True):
+        if config.get('enable_user_mood'):
             self.register_command('mood', self.command_mood,
                     usage='[<mood> [text]]',
                     desc=_('Send your current mood to your contacts '
@@ -1834,7 +1834,7 @@ class Core(object):
                            '"stop broadcasting a mood".'),
                     shortdesc=_('Send your mood.'),
                     completion=self.completion_mood)
-        if config.get('enable_user_gaming', True):
+        if config.get('enable_user_gaming'):
             self.register_command('gaming', self.command_gaming,
                     usage='[<game name> [server address]]',
                     desc=_('Send your current gaming activity to '
@@ -1977,7 +1977,7 @@ def replace_key_with_bound(key):
     Replace an inputted key with the one defined as its replacement
     in the config
     """
-    bind = config.get(key, key, 'bindings')
+    bind = config.get(key, default=key, section='bindings')
     if not bind:
         bind = key
     return bind
diff --git a/src/core/handlers.py b/src/core/handlers.py
index 4853c804..dfcb3223 100644
--- a/src/core/handlers.py
+++ b/src/core/handlers.py
@@ -44,7 +44,7 @@ def on_session_start_features(self, _):
         features = iq['disco_info']['features']
         rostertab = self.get_tab_by_name('Roster', tabs.RosterInfoTab)
         rostertab.check_blocking(features)
-        if (config.get('enable_carbons', True) and
+        if (config.get('enable_carbons') and
                 'urn:xmpp:carbons:2' in features):
             self.xmpp.plugin['xep_0280'].enable()
             self.xmpp.add_event_handler('carbon_received', self.on_carbon_received)
@@ -111,7 +111,7 @@ def on_groupchat_invitation(self, message):
     if password:
         msg += ". The password is \"%s\"." % password
     self.information(msg, 'Info')
-    if 'invite' in config.get('beep_on', 'invite').split():
+    if 'invite' in config.get('beep_on').split():
         curses.beep()
     logger.log_roster_change(inviter.full, 'invited you to %s' % jid.full)
     self.pending_invites[jid.bare] = inviter.full
@@ -142,7 +142,7 @@ def on_groupchat_direct_invitation(self, message):
         msg += "\nreason: %s" % reason
 
     self.information(msg, 'Info')
-    if 'invite' in config.get('beep_on', 'invite').split():
+    if 'invite' in config.get('beep_on').split():
         curses.beep()
 
     self.pending_invites[room.bare] = inviter.full
@@ -178,9 +178,9 @@ def on_normal_message(self, message):
     elif message['type'] == 'headline' and message['body']:
         return self.information('%s says: %s' % (message['from'], message['body']), 'Headline')
 
-    use_xhtml = config.get('enable_xhtml_im', True)
-    tmp_dir = config.get('tmp_image_dir', '') or path.join(CACHE_DIR, 'images')
-    extract_images = config.get('extract_inline_images', True)
+    use_xhtml = config.get('enable_xhtml_im')
+    tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images')
+    extract_images = config.get('extract_inline_images')
     body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
                                               tmp_dir=tmp_dir,
                                               extract_images=extract_images)
@@ -197,7 +197,7 @@ def on_normal_message(self, message):
         if conv_jid.bare in roster:
             remote_nick = roster[conv_jid.bare].name
         # check for a received nick
-        if not remote_nick and config.get('enable_user_nick', True):
+        if not remote_nick and config.get('enable_user_nick'):
             if message.xml.find('{http://jabber.org/protocol/nick}nick') is not None:
                 remote_nick = message['nick']['nick']
         if not remote_nick:
@@ -259,7 +259,7 @@ def on_normal_message(self, message):
             conversation.remote_wants_chatstates = True
         else:
             conversation.remote_wants_chatstates = False
-    if 'private' in config.get('beep_on', 'highlight private').split():
+    if 'private' in config.get('beep_on').split():
         if not config.get_by_tabname('disable_beep', conv_jid.bare):
             curses.beep()
     if self.current_tab() is not conversation:
@@ -447,9 +447,9 @@ def on_groupchat_message(self, message):
         return
 
     self.events.trigger('muc_msg', message, tab)
-    use_xhtml = config.get('enable_xhtml_im', True)
-    tmp_dir = config.get('tmp_image_dir', '') or path.join(CACHE_DIR, 'images')
-    extract_images = config.get('extract_inline_images', True)
+    use_xhtml = config.get('enable_xhtml_im')
+    tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images')
+    extract_images = config.get('extract_inline_images')
     body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
                                               tmp_dir=tmp_dir,
                                               extract_images=extract_images)
@@ -487,7 +487,7 @@ def on_groupchat_message(self, message):
             current.input.refresh()
         self.doupdate()
 
-    if 'message' in config.get('beep_on', 'highlight private').split():
+    if 'message' in config.get('beep_on').split():
         if (not config.get_by_tabname('disable_beep', room_from)
                 and self.own_nick != message['from'].resource):
             curses.beep()
@@ -508,9 +508,9 @@ def on_groupchat_private_message(self, message):
         return self.on_groupchat_message(message)
 
     room_from = jid.bare
-    use_xhtml = config.get('enable_xhtml_im', True)
-    tmp_dir = config.get('tmp_image_dir', '') or path.join(CACHE_DIR, 'images')
-    extract_images = config.get('extract_inline_images', True)
+    use_xhtml = config.get('enable_xhtml_im')
+    tmp_dir = config.get('tmp_image_dir') or path.join(CACHE_DIR, 'images')
+    extract_images = config.get('extract_inline_images')
     body = xhtml.get_body_from_message_stanza(message, use_xhtml=use_xhtml,
                                               tmp_dir=tmp_dir,
                                               extract_images=extract_images)
@@ -554,7 +554,7 @@ def on_groupchat_private_message(self, message):
             tab.remote_wants_chatstates = True
         else:
             tab.remote_wants_chatstates = False
-    if 'private' in config.get('beep_on', 'highlight private').split():
+    if 'private' in config.get('beep_on').split():
         if not config.get_by_tabname('disable_beep', jid.full):
             curses.beep()
     if tab is self.current_tab():
@@ -876,23 +876,23 @@ def on_session_start(self, event):
         # request the roster
         self.xmpp.get_roster()
         # send initial presence
-        if config.get('send_initial_presence', True):
+        if config.get('send_initial_presence'):
             pres = self.xmpp.make_presence()
             pres['show'] = self.status.show
             pres['status'] = self.status.message
             self.events.trigger('send_normal_presence', pres)
             pres.send()
     bookmark.get_local()
-    if not self.xmpp.anon and config.get('use_remote_bookmarks', True):
+    if not self.xmpp.anon and config.get('use_remote_bookmarks'):
         bookmark.get_remote(self.xmpp)
     for bm in bookmark.bookmarks:
-        if bm.autojoin or config.get('open_all_bookmarks', False):
+        if bm.autojoin or config.get('open_all_bookmarks'):
             tab = self.get_tab_by_name(bm.jid, tabs.MucTab)
             nick = bm.nick if bm.nick else self.own_nick
             if not tab:
                 self.open_new_room(bm.jid, nick, False)
             self.initial_joins.append(bm.jid)
-            histo_length = config.get('muc_history_length', 20)
+            histo_length = config.get('muc_history_length')
             if histo_length == -1:
                 histo_length = None
             if histo_length is not None:
@@ -906,7 +906,7 @@ def on_session_start(self, event):
                         status=self.status.message,
                         show=self.status.show)
 
-    if config.get('enable_user_nick', True):
+    if config.get('enable_user_nick'):
         self.xmpp.plugin['xep_0172'].publish_nick(nick=self.own_nick, callback=dumb_callback, block=False)
     self.xmpp.plugin['xep_0115'].update_caps()
 
@@ -1081,9 +1081,9 @@ def validate_ssl(self, pem):
     """
     Check the server certificate using the sleekxmpp ssl_cert event
     """
-    if config.get('ignore_certificate', False):
+    if config.get('ignore_certificate'):
         return
-    cert = config.get('certificate', '')
+    cert = config.get('certificate')
     # update the cert representation when it uses the old one
     if cert and not ':' in cert:
         cert = ':'.join(i + j for i, j in zip(cert[::2], cert[1::2])).upper()
@@ -1147,7 +1147,7 @@ def _composing_tab_state(tab, state):
     else:
         return # should not happen
 
-    show = config.get('show_composing_tabs', 'direct')
+    show = config.get('show_composing_tabs')
     show = show in values
 
     if tab.state != 'composing' and state == 'composing':
diff --git a/src/logger.py b/src/logger.py
index 53ec4311..85c7a746 100644
--- a/src/logger.py
+++ b/src/logger.py
@@ -50,7 +50,7 @@ class Logger(object):
     and also log the conversations to logfiles
     """
     def __init__(self):
-        self.logfile = config.get('logfile', 'logs')
+        self.logfile = config.get('logfile')
         self.roster_logfile = None
         # a dict of 'groupchatname': file-object (opened)
         self.fds = dict()
diff --git a/src/plugin_manager.py b/src/plugin_manager.py
index af87a23b..d5cb9bc1 100644
--- a/src/plugin_manager.py
+++ b/src/plugin_manager.py
@@ -325,7 +325,7 @@ class PluginManager(object):
         """
         Create the plugins_conf_dir
         """
-        plugins_conf_dir = config.get('plugins_conf_dir', '')
+        plugins_conf_dir = config.get('plugins_conf_dir')
         if not plugins_conf_dir:
             config_home = os.environ.get('XDG_CONFIG_HOME')
             if not config_home:
@@ -352,7 +352,7 @@ class PluginManager(object):
         """
         Set the plugins_dir on start
         """
-        plugins_dir = config.get('plugins_dir', '')
+        plugins_dir = config.get('plugins_dir')
         plugins_dir = plugins_dir or\
             os.path.join(os.environ.get('XDG_DATA_HOME') or\
                              os.path.join(os.environ.get('HOME'),
diff --git a/src/roster.py b/src/roster.py
index eb898e5e..d18a41c4 100644
--- a/src/roster.py
+++ b/src/roster.py
@@ -36,10 +36,8 @@ class Roster(object):
         self.contact_filter = None # A tuple(function, *args)
                                     # function to filter contacts,
                                     # on search, for example
-        self.folded_groups = set(config.get(
-                'folded_roster_groups',
-                '',
-                section='var').split(':'))
+        self.folded_groups = set(config.get('folded_roster_groups',
+                                            section='var').split(':'))
         self.groups = {}
         self.contacts = {}
 
diff --git a/src/tabs/basetabs.py b/src/tabs/basetabs.py
index 0fde624c..2f5db40a 100644
--- a/src/tabs/basetabs.py
+++ b/src/tabs/basetabs.py
@@ -139,7 +139,7 @@ class Tab(object):
         Returns 1 or 0, depending on if we are using the vertical tab list
         or not.
         """
-        if config.get('enable_vertical_tab_list', False):
+        if config.get('enable_vertical_tab_list'):
             return 0
         return 1
 
@@ -298,7 +298,7 @@ class Tab(object):
             return False
 
     def refresh_tab_win(self):
-        if config.get('enable_vertical_tab_list', False):
+        if config.get('enable_vertical_tab_list'):
             if self.left_tab_win and not self.size.core_degrade_x:
                 self.left_tab_win.refresh()
         elif not self.size.core_degrade_y:
@@ -476,7 +476,7 @@ class ChatTab(Tab):
         self.update_keys()
 
         # Get the logs
-        log_nb = config.get('load_log', 10)
+        log_nb = config.get('load_log')
         logs = self.load_logs(log_nb)
 
         if logs:
@@ -537,7 +537,7 @@ class ChatTab(Tab):
             for word in txt.split():
                 if len(word) >= 4 and word not in words:
                     words.append(word)
-        words.extend([word for word in config.get('words', '').split(':') if word])
+        words.extend([word for word in config.get('words').split(':') if word])
         self.input.auto_completion(words, ' ', quotify=False)
 
     def on_enter(self):
@@ -825,7 +825,7 @@ class OneToOneTab(ChatTab):
         if attention or empty:
             features.append(_('attention requests (/attention)'))
         if (receipts or empty) \
-                and config.get('request_message_receipts', True):
+                and config.get('request_message_receipts'):
             features.append(_('message delivery receipts'))
         if len(features) > 1:
             tail = features.pop()
diff --git a/src/tabs/muctab.py b/src/tabs/muctab.py
index bd8ed312..fb89b0fa 100644
--- a/src/tabs/muctab.py
+++ b/src/tabs/muctab.py
@@ -251,7 +251,7 @@ class MucTab(ChatTab):
     def completion_nick(self, the_input):
         """Completion for /nick"""
         nicks = [os.environ.get('USER'),
-                 config.get('default_nick', ''),
+                 config.get('default_nick'),
                  self.core.get_bookmark_nickname(self.name)]
         nicks = [i for i in nicks if i]
         return the_input.auto_completion(nicks, '', quotify=False)
@@ -801,7 +801,7 @@ class MucTab(ChatTab):
         Resize the whole window. i.e. all its sub-windows
         """
         self.need_resize = False
-        if config.get("hide_user_list", False) or self.size.tab_degrade_x:
+        if config.get('hide_user_list') or self.size.tab_degrade_x:
             display_user_list = False
             text_width = self.width
         else:
@@ -842,7 +842,7 @@ class MucTab(ChatTab):
         if self.need_resize:
             self.resize()
         log.debug('  TAB   Refresh: %s', self.__class__.__name__)
-        if config.get("hide_user_list", False) or self.size.tab_degrade_x:
+        if config.get('hide_user_list') or self.size.tab_degrade_x:
             display_user_list = False
         else:
             display_user_list = True
@@ -885,7 +885,7 @@ class MucTab(ChatTab):
         for user in sorted(self.users, key=compare_users, reverse=True):
             if user.nick != self.own_nick:
                 word_list.append(user.nick)
-        after = config.get('after_completion', ',') + ' '
+        after = config.get('after_completion') + ' '
         input_pos = self.input.pos
         if ' ' not in self.input.get_text()[:input_pos] or (
                 self.input.last_completion and
@@ -893,7 +893,7 @@ class MucTab(ChatTab):
                     self.input.last_completion + after):
             add_after = after
         else:
-            if not config.get('add_space_after_completion', True):
+            if not config.get('add_space_after_completion'):
                 add_after = ''
             else:
                 add_after = ' '
@@ -905,7 +905,7 @@ class MucTab(ChatTab):
         self.send_composing_chat_state(empty_after)
 
     def get_nick(self):
-        if not config.get('show_muc_jid', True):
+        if not config.get('show_muc_jid'):
             return safeJID(self.name).user
         return self.name
 
@@ -930,7 +930,7 @@ class MucTab(ChatTab):
     def on_gain_focus(self):
         self.state = 'current'
         if (self.text_win.built_lines and self.text_win.built_lines[-1] is None
-                and not config.get('show_useless_separator', False)):
+                and not config.get('show_useless_separator')):
             self.text_win.remove_line_separator()
         curses.curs_set(1)
         if self.joined and config.get_by_tabname('send_chat_states',
@@ -940,7 +940,7 @@ class MucTab(ChatTab):
     def on_info_win_size_changed(self):
         if self.core.information_win_size >= self.height-3:
             return
-        if config.get("hide_user_list", False):
+        if config.get("hide_user_list"):
             text_width = self.width
         else:
             text_width = (self.width//10)*9
@@ -1476,7 +1476,7 @@ class MucTab(ChatTab):
                         highlighted = True
                         break
         if highlighted:
-            beep_on = config.get('beep_on', 'highlight private').split()
+            beep_on = config.get('beep_on').split()
             if 'highlight' in beep_on and 'message' not in beep_on:
                 if not config.get_by_tabname('disable_beep', self.name):
                     curses.beep()
diff --git a/src/tabs/privatetab.py b/src/tabs/privatetab.py
index f4fa10cf..4c01cd70 100644
--- a/src/tabs/privatetab.py
+++ b/src/tabs/privatetab.py
@@ -109,7 +109,7 @@ class PrivateTab(OneToOneTab):
         compare_users = lambda x: x.last_talked
         word_list = [user.nick for user in sorted(self.parent_muc.users, key=compare_users, reverse=True)\
                          if user.nick != self.own_nick]
-        after = config.get('after_completion', ',')+" "
+        after = config.get('after_completion') + ' '
         input_pos = self.input.pos
         if ' ' not in self.input.get_text()[:input_pos] or (self.input.last_completion and\
                      self.input.get_text()[:input_pos] == self.input.last_completion + after):
diff --git a/src/tabs/rostertab.py b/src/tabs/rostertab.py
index 3d01046b..26f429d0 100644
--- a/src/tabs/rostertab.py
+++ b/src/tabs/rostertab.py
@@ -349,7 +349,7 @@ class RosterInfoTab(Tab):
         def callback(iq):
             if iq['type'] == 'result':
                 self.core.information('Password updated', 'Account')
-                if config.get('password', ''):
+                if config.get('password'):
                     config.silent_set('password', arg)
             else:
                 self.core.information('Unable to change the password', 'Account')
@@ -763,7 +763,7 @@ class RosterInfoTab(Tab):
         Show or hide offline contacts
         """
         option = 'roster_show_offline'
-        value = config.get(option, False)
+        value = config.get(option)
         success = config.silent_set(option, str(not value))
         roster.modified()
         if not success:
diff --git a/src/text_buffer.py b/src/text_buffer.py
index 4a41fd97..59aa96e1 100644
--- a/src/text_buffer.py
+++ b/src/text_buffer.py
@@ -64,7 +64,7 @@ class TextBuffer(object):
     def __init__(self, messages_nb_limit=None):
 
         if messages_nb_limit is None:
-            messages_nb_limit = config.get('max_messages_in_memory', 2048)
+            messages_nb_limit = config.get('max_messages_in_memory')
         self.messages_nb_limit = messages_nb_limit
         # Message objects
         self.messages = []
@@ -138,7 +138,7 @@ class TextBuffer(object):
             self.messages.pop(0)
 
         ret_val = None
-        show_timestamps = config.get('show_timestamps', True)
+        show_timestamps = config.get('show_timestamps')
         for window in self.windows: # make the associated windows
                                     # build the lines from the new message
             nb = window.build_new_message(msg, history=history,
diff --git a/src/theming.py b/src/theming.py
index 9820addf..dc3052f0 100755
--- a/src/theming.py
+++ b/src/theming.py
@@ -455,7 +455,7 @@ def update_themes_dir(option=None, value=None):
     # import from the user-defined prefs
     themes_dir = path.expanduser(
             value or
-            config.get('themes_dir', '') or
+            config.get('themes_dir') or
             path.join(os.environ.get('XDG_DATA_HOME') or
                 path.join(os.environ.get('HOME'), '.local', 'share'),
                 'poezio', 'themes')
@@ -482,7 +482,7 @@ def update_themes_dir(option=None, value=None):
     log.debug('Theme load path: %s', load_path)
 
 def reload_theme():
-    theme_name = config.get('theme', 'default')
+    theme_name = config.get('theme')
     global theme
     if theme_name == 'default' or not theme_name.strip():
         theme = Theme()
diff --git a/src/windows/funcs.py b/src/windows/funcs.py
index 47011faf..d58d4683 100644
--- a/src/windows/funcs.py
+++ b/src/windows/funcs.py
@@ -20,7 +20,7 @@ def find_first_format_char(text, chars=None):
     return pos
 
 def truncate_nick(nick, size=None):
-    size = size or config.get('max_nick_length', 25)
+    size = size or config.get('max_nick_length')
     if size < 1:
         size = 1
     if nick and len(nick) > size:
diff --git a/src/windows/info_bar.py b/src/windows/info_bar.py
index 9917fa6a..cea4702f 100644
--- a/src/windows/info_bar.py
+++ b/src/windows/info_bar.py
@@ -25,10 +25,10 @@ class GlobalInfoBar(Win):
             self._win.erase()
             self.addstr(0, 0, "[", to_curses_attr(get_theme().COLOR_INFORMATION_BAR))
 
-            create_gaps = config.get('create_gaps', False)
-            show_names = config.get('show_tab_names', False)
-            show_nums = config.get('show_tab_numbers', True)
-            use_nicks = config.get('use_tab_nicks', True)
+            create_gaps = config.get('create_gaps')
+            show_names = config.get('show_tab_names')
+            show_nums = config.get('show_tab_numbers')
+            use_nicks = config.get('use_tab_nicks')
             # ignore any remaining gap tabs if the feature is not enabled
             if create_gaps:
                 sorted_tabs = self.core.tabs[:]
@@ -38,7 +38,7 @@ class GlobalInfoBar(Win):
             for nb, tab in enumerate(sorted_tabs):
                 if not tab: continue
                 color = tab.color
-                if not config.get('show_inactive_tabs', True) and\
+                if not config.get('show_inactive_tabs') and\
                         color is get_theme().COLOR_TAB_NORMAL:
                     continue
                 try:
@@ -72,11 +72,11 @@ class VerticalGlobalInfoBar(Win):
             height, width = self._win.getmaxyx()
             self._win.erase()
             sorted_tabs = [tab for tab in self.core.tabs if tab]
-            if not config.get('show_inactive_tabs', True):
+            if not config.get('show_inactive_tabs'):
                 sorted_tabs = [tab for tab in sorted_tabs if\
                                    tab.vertical_color != get_theme().COLOR_VERTICAL_TAB_NORMAL]
             nb_tabs = len(sorted_tabs)
-            use_nicks = config.get('use_tab_nicks', True)
+            use_nicks = config.get('use_tab_nicks')
             if nb_tabs >= height:
                 for y, tab in enumerate(sorted_tabs):
                     if tab.vertical_color == get_theme().COLOR_VERTICAL_TAB_CURRENT:
@@ -92,7 +92,7 @@ class VerticalGlobalInfoBar(Win):
             for y, tab in enumerate(sorted_tabs):
                 color = tab.vertical_color
 
-                if not config.get('vertical_tab_list_sort', 'desc') != 'asc':
+                if not config.get('vertical_tab_list_sort') != 'asc':
                     y = height - y - 1
                 self.addstr(y, 0, "%2d" % tab.nb,
                         to_curses_attr(get_theme().COLOR_VERTICAL_TAB_NUMBER))
diff --git a/src/windows/inputs.py b/src/windows/inputs.py
index db339b77..8e1673e1 100644
--- a/src/windows/inputs.py
+++ b/src/windows/inputs.py
@@ -564,7 +564,7 @@ class HistoryInput(Input):
         self.current_completed = ''
         self.key_func['^R'] = self.toggle_search
         self.search = False
-        if config.get('separate_history', False):
+        if config.get('separate_history'):
             self.history = list()
 
     def toggle_search(self):
diff --git a/src/windows/muc.py b/src/windows/muc.py
index ce296e26..cd594c4c 100644
--- a/src/windows/muc.py
+++ b/src/windows/muc.py
@@ -34,11 +34,11 @@ class UserList(Win):
 
     def refresh(self, users):
         log.debug('Refresh: %s', self.__class__.__name__)
-        if config.get("hide_user_list", False):
+        if config.get('hide_user_list'):
             return # do not refresh if this win is hidden.
         with g_lock:
             self._win.erase()
-            if config.get('user_list_sort', 'desc').lower() == 'asc':
+            if config.get('user_list_sort').lower() == 'asc':
                 y, x = self._win.getmaxyx()
                 y -= 1
                 users = sorted(users)
@@ -56,7 +56,7 @@ class UserList(Win):
                 self.addstr(y, 2,
                         poopt.cut_by_columns(user.nick, self.width - 2),
                         to_curses_attr(user.color))
-                if config.get('user_list_sort', 'desc').lower() == 'asc':
+                if config.get('user_list_sort').lower() == 'asc':
                     y -= 1
                 else:
                     y += 1
@@ -64,12 +64,12 @@ class UserList(Win):
                     break
             # draw indicators of position in the list
             if self.pos > 0:
-                if config.get('user_list_sort', 'desc').lower() == 'asc':
+                if config.get('user_list_sort').lower() == 'asc':
                     self.draw_plus(self.height-1)
                 else:
                     self.draw_plus(0)
             if self.pos + self.height < len(users):
-                if config.get('user_list_sort', 'desc').lower() == 'asc':
+                if config.get('user_list_sort').lower() == 'asc':
                     self.draw_plus(0)
                 else:
                     self.draw_plus(self.height-1)
diff --git a/src/windows/roster_win.py b/src/windows/roster_win.py
index d5f6d958..d98f27ce 100644
--- a/src/windows/roster_win.py
+++ b/src/windows/roster_win.py
@@ -95,13 +95,13 @@ class RosterWin(Win):
                 # This is a search
                 if roster.contact_filter:
                     self.roster_cache = []
-                    sort = config.get('roster_sort', 'jid:show') or 'jid:show'
+                    sort = config.get('roster_sort') or 'jid:show'
                     for contact in roster.get_contacts_sorted_filtered(sort):
                         self.roster_cache.append(contact)
                 else:
-                    show_offline = config.get('roster_show_offline', False) or roster.contact_filter
-                    sort = config.get('roster_sort', 'jid:show') or 'jid:show'
-                    group_sort = config.get('roster_group_sort', 'name') or 'name'
+                    show_offline = config.get('roster_show_offline') or roster.contact_filter
+                    sort = config.get('roster_sort') or 'jid:show'
+                    group_sort = config.get('roster_group_sort') or 'name'
                     self.roster_cache = []
                     # build the cache
                     for group in roster.get_groups(group_sort):
@@ -230,7 +230,7 @@ class RosterWin(Win):
         self.addstr(y, 0, ' ')
         self.addstr(theme.CHAR_STATUS, to_curses_attr(color))
 
-        show_roster_sub = config.get('show_roster_subscriptions', '')
+        show_roster_sub = config.get('show_roster_subscriptions')
 
         self.addstr(' ')
         if resource:
@@ -238,7 +238,7 @@ class RosterWin(Win):
             added += 4
         if contact.ask:
             added += len(get_theme().CHAR_ROSTER_ASKED)
-        if config.get('show_s2s_errors', True) and contact.error:
+        if config.get('show_s2s_errors') and contact.error:
             added += len(get_theme().CHAR_ROSTER_ERROR)
         if contact.tune:
             added += len(get_theme().CHAR_ROSTER_TUNE)
@@ -251,7 +251,7 @@ class RosterWin(Win):
         if show_roster_sub in ('all', 'incomplete', 'to', 'from', 'both', 'none'):
             added += len(theme.char_subscription(contact.subscription, keep=show_roster_sub))
 
-        if not config.get('show_roster_jids', True) and contact.name:
+        if not config.get('show_roster_jids') and contact.name:
             display_name = '%s' % contact.name
         elif contact.name and contact.name != contact.bare_jid:
             display_name = '%s (%s)' % (contact.name, contact.bare_jid)
@@ -269,7 +269,7 @@ class RosterWin(Win):
             self.addstr(theme.char_subscription(contact.subscription, keep=show_roster_sub), to_curses_attr(theme.COLOR_ROSTER_SUBSCRIPTION))
         if contact.ask:
             self.addstr(get_theme().CHAR_ROSTER_ASKED, to_curses_attr(get_theme().COLOR_IMPORTANT_TEXT))
-        if config.get('show_s2s_errors', True) and contact.error:
+        if config.get('show_s2s_errors') and contact.error:
             self.addstr(get_theme().CHAR_ROSTER_ERROR, to_curses_attr(get_theme().COLOR_ROSTER_ERROR))
         if contact.tune:
             self.addstr(get_theme().CHAR_ROSTER_TUNE, to_curses_attr(get_theme().COLOR_ROSTER_TUNE))
diff --git a/src/windows/text_win.py b/src/windows/text_win.py
index de9b0625..4c6b9a18 100644
--- a/src/windows/text_win.py
+++ b/src/windows/text_win.py
@@ -19,7 +19,7 @@ from theming import to_curses_attr, get_theme, dump_tuple
 
 
 class TextWin(Win):
-    def __init__(self, lines_nb_limit=config.get('max_lines_in_memory', 2048)):
+    def __init__(self, lines_nb_limit=config.get('max_lines_in_memory')):
         Win.__init__(self)
         self.lines_nb_limit = lines_nb_limit
         self.pos = 0
@@ -265,7 +265,7 @@ class TextWin(Win):
             lines = self.built_lines[-self.height:]
         else:
             lines = self.built_lines[-self.height-self.pos:-self.pos]
-        with_timestamps = config.get("show_timestamps", True)
+        with_timestamps = config.get("show_timestamps")
         with g_lock:
             self._win.move(0, 0)
             self._win.erase()
@@ -405,7 +405,7 @@ class TextWin(Win):
 
     def rebuild_everything(self, room):
         self.built_lines = []
-        with_timestamps = config.get("show_timestamps", True)
+        with_timestamps = config.get('show_timestamps')
         for message in room.messages:
             self.build_new_message(message, clean=False, timestamp=with_timestamps)
             if self.separator_after is message:
@@ -418,7 +418,7 @@ class TextWin(Win):
         Find a message, and replace it with a new one
         (instead of rebuilding everything in order to correct a message)
         """
-        with_timestamps = config.get("show_timestamps", True)
+        with_timestamps = config.get('show_timestamps')
         for i in range(len(self.built_lines)-1, -1, -1):
             if self.built_lines[i] and self.built_lines[i].msg.identifier == old_id:
                 index = i
-- 
cgit v1.2.3


From b44dae8fae4884387d4d149a17033b261c940dea Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 20 Oct 2014 21:04:50 +0200
Subject: =?UTF-8?q?If=20the=20whitespace=5Finterval=20value=20set=20in=20t?=
 =?UTF-8?q?he=20config=20is=20null=20or=20negative,=20don=E2=80=99t=20send?=
 =?UTF-8?q?=20any?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/connection.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/connection.py b/src/connection.py
index e6188e05..dc35dd94 100644
--- a/src/connection.py
+++ b/src/connection.py
@@ -71,7 +71,7 @@ class Connection(sleekxmpp.ClientXMPP):
         if int(interval) > 0:
             self.whitespace_keepalive_interval = int(interval)
         else:
-            self.whitespace_keepalive_interval = 300
+            self.whitespace_keepalive = False
         self.register_plugin('xep_0004')
         self.register_plugin('xep_0012')
         self.register_plugin('xep_0030')
-- 
cgit v1.2.3


From b803db7ff67bea7b5898e34853f2af6baaa16f6f Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 20 Oct 2014 21:06:26 +0200
Subject: Make the runtime changes to max_lines_in_memory useful

---
 src/windows/text_win.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/windows/text_win.py b/src/windows/text_win.py
index 4c6b9a18..f634f5a6 100644
--- a/src/windows/text_win.py
+++ b/src/windows/text_win.py
@@ -19,7 +19,9 @@ from theming import to_curses_attr, get_theme, dump_tuple
 
 
 class TextWin(Win):
-    def __init__(self, lines_nb_limit=config.get('max_lines_in_memory')):
+    def __init__(self, lines_nb_limit=None):
+        if lines_nb_limit is None:
+            lines_nb_limit = config.get('max_lines_in_memory')
         Win.__init__(self)
         self.lines_nb_limit = lines_nb_limit
         self.pos = 0
-- 
cgit v1.2.3


From 7bc5d0de663ccb71d70855999188b4ca2b61869a Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 20 Oct 2014 22:52:41 +0200
Subject: Initialize PluginConfig properly

---
 src/plugin.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/plugin.py b/src/plugin.py
index a2412e10..22a17eee 100644
--- a/src/plugin.py
+++ b/src/plugin.py
@@ -20,9 +20,8 @@ class PluginConfig(config.Config):
     and behave like the core Config object.
     """
     def __init__(self, filename, module_name):
-        self.file_name = filename
+        config.Config.__init__(self, filename)
         self.module_name = module_name
-        RawConfigParser.__init__(self, None)
         self.read()
 
     def get(self, option, default, section=None):
-- 
cgit v1.2.3


From 6b8af2413e47fc13796ea061ff759d7c1ca74c5c Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 20 Oct 2014 22:54:00 +0200
Subject: Fix #2713 (make /set <option> return the value of the option)

Should work for any case (plugin, different section, etc)
---
 src/core/commands.py | 38 ++++++++++++++++++++++++++++++--------
 1 file changed, 30 insertions(+), 8 deletions(-)

diff --git a/src/core/commands.py b/src/core/commands.py
index daf420fb..b44f3483 100644
--- a/src/core/commands.py
+++ b/src/core/commands.py
@@ -586,17 +586,36 @@ def command_remove_bookmark(self, arg=''):
 
 def command_set(self, arg):
     """
-    /set [module|][section] <option> <value>
+    /set [module|][section] <option> [value]
     """
     args = common.shell_split(arg)
-    if len(args) != 2 and len(args) != 3:
-        self.command_help('set')
-        return
-    if len(args) == 2:
+    if len(args) == 1:
         option = args[0]
-        value = args[1]
-        info = config.set_and_save(option, value)
-        self.trigger_configuration_change(option, value)
+        value = config.get(option)
+        info = ('%s=%s' % (option, value), 'Info')
+    elif len(args) == 2:
+        if '|' in args[0]:
+            plugin_name, section = args[0].split('|')[:2]
+            if not section:
+                section = plugin_name
+            option = args[1]
+            if not plugin_name in self.plugin_manager.plugins:
+                return
+            plugin = self.plugin_manager.plugins[plugin_name]
+            value = plugin.config.get(option, default='', section=section)
+            info = ('%s=%s' % (option, value), 'Info')
+        else:
+            possible_section = args[0]
+            if config.has_section(possible_section):
+                section = possible_section
+                option = args[1]
+                value = config.get(option, section=section)
+                info = ('%s=%s' % (option, value), 'Info')
+            else:
+                option = args[0]
+                value = args[1]
+                info = config.set_and_save(option, value)
+                self.trigger_configuration_change(option, value)
     elif len(args) == 3:
         if '|' in args[0]:
             plugin_name, section = args[0].split('|')[:2]
@@ -614,6 +633,9 @@ def command_set(self, arg):
             value = args[2]
             info = config.set_and_save(option, value, section)
             self.trigger_configuration_change(option, value)
+    else:
+        self.command_help('set')
+        return
     self.call_for_resize()
     self.information(*info)
 
-- 
cgit v1.2.3


From 9d58c9d32ea67364933b1980c374237de6c3646d Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 20 Oct 2014 23:24:27 +0200
Subject: Fix #2719 (write/read in the plugin config files even when not
 loaded)

---
 src/core/commands.py | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/core/commands.py b/src/core/commands.py
index b44f3483..d212de9b 100644
--- a/src/core/commands.py
+++ b/src/core/commands.py
@@ -6,6 +6,7 @@ import logging
 
 log = logging.getLogger(__name__)
 
+import os
 import sys
 from datetime import datetime
 from gettext import gettext as _
@@ -23,6 +24,7 @@ import tabs
 from common import safeJID
 from config import config, options as config_opts
 import multiuserchat as muc
+from plugin import PluginConfig
 from roster import roster
 from theming import dump_tuple, get_theme
 
@@ -600,9 +602,12 @@ def command_set(self, arg):
                 section = plugin_name
             option = args[1]
             if not plugin_name in self.plugin_manager.plugins:
-                return
-            plugin = self.plugin_manager.plugins[plugin_name]
-            value = plugin.config.get(option, default='', section=section)
+                file_name = self.plugin_manager.plugins_conf_dir
+                file_name = os.path.join(file_name, plugin_name + '.cfg')
+                plugin_config = PluginConfig(file_name, plugin_name)
+            else:
+                plugin_config = self.plugin_manager.plugins[plugin_name].config
+            value = plugin_config.get(option, default='', section=section)
             info = ('%s=%s' % (option, value), 'Info')
         else:
             possible_section = args[0]
@@ -624,9 +629,12 @@ def command_set(self, arg):
             option = args[1]
             value = args[2]
             if not plugin_name in self.plugin_manager.plugins:
-                return
-            plugin = self.plugin_manager.plugins[plugin_name]
-            info = plugin.config.set_and_save(option, value, section)
+                file_name = self.plugin_manager.plugins_conf_dir
+                file_name = os.path.join(file_name, plugin_name + '.cfg')
+                plugin_config = PluginConfig(file_name, plugin_name)
+            else:
+                plugin_config = self.plugin_manager.plugins[plugin_name].config
+            info = plugin_config.set_and_save(option, value, section)
         else:
             section = args[0]
             option = args[1]
-- 
cgit v1.2.3


From 0dc36d959868a4448873686e684cb86ecab2d425 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Wed, 22 Oct 2014 19:10:40 +0200
Subject: Typo

---
 src/tabs/basetabs.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tabs/basetabs.py b/src/tabs/basetabs.py
index 2f5db40a..fbf57dc2 100644
--- a/src/tabs/basetabs.py
+++ b/src/tabs/basetabs.py
@@ -833,7 +833,7 @@ class OneToOneTab(ChatTab):
             tail = None
         features_str = ', '.join(features)
         if tail and empty:
-            features_str += _(', or %s') % tail
+            features_str += _(', nor %s') % tail
         elif tail:
             features_str += _(' and %s') % tail
 
-- 
cgit v1.2.3


From d2ca062eaa190cd837c5073ca83e71d2834c7059 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Sat, 25 Oct 2014 16:50:19 +0200
Subject: Make the features disco in chat more user-friendly

---
 src/tabs/basetabs.py | 35 ++++++++++-------------------------
 src/theming.py       |  6 ++++--
 2 files changed, 14 insertions(+), 27 deletions(-)

diff --git a/src/tabs/basetabs.py b/src/tabs/basetabs.py
index fbf57dc2..40dcb8e6 100644
--- a/src/tabs/basetabs.py
+++ b/src/tabs/basetabs.py
@@ -817,32 +817,17 @@ class OneToOneTab(ChatTab):
         else:
             self.__initial_disco = True
 
-        empty = not any((correct, attention, receipts))
-
-        features = []
-        if correct or empty:
-            features.append(_('message correction (/correct)'))
-        if attention or empty:
-            features.append(_('attention requests (/attention)'))
-        if (receipts or empty) \
-                and config.get('request_message_receipts'):
-            features.append(_('message delivery receipts'))
-        if len(features) > 1:
-            tail = features.pop()
-        else:
-            tail = None
-        features_str = ', '.join(features)
-        if tail and empty:
-            features_str += _(', nor %s') % tail
-        elif tail:
-            features_str += _(' and %s') % tail
-
-        if empty:
-            msg = _('\x19%s}This contact does not support %s.')
-        else:
-            msg = _('\x19%s}This contact supports %s.')
+        ok = get_theme().CHAR_OK
+        nope = get_theme().CHAR_ERROR
+
+        correct = ok if correct else nope
+        attention = ok if attention else nope
+        receipts = ok if receipts else nope
+
+        msg = _('\x19%s}Contact supports: correction [%s], '
+                'attention [%s], receipts [%s].')
         color = dump_tuple(get_theme().COLOR_INFORMATION_TEXT)
-        msg = msg % (color, features_str)
+        msg = msg % (color, correct, attention, receipts)
         self.add_message(msg, typ=0)
         self.core.refresh_window()
 
diff --git a/src/theming.py b/src/theming.py
index dc3052f0..0d83db9d 100755
--- a/src/theming.py
+++ b/src/theming.py
@@ -301,10 +301,12 @@ class Theme(object):
     CHAR_QUIT = '<---'
     CHAR_KICK = '-!-'
     CHAR_NEW_TEXT_SEPARATOR = '- '
-    CHAR_ACK_RECEIVED = '✔'
+    CHAR_OK = '✔'
+    CHAR_ERROR = '✖'
+    CHAR_ACK_RECEIVED = CHAR_OK
     CHAR_COLUMN_ASC = ' ▲'
     CHAR_COLUMN_DESC = ' ▼'
-    CHAR_ROSTER_ERROR = '✖'
+    CHAR_ROSTER_ERROR = CHAR_ERROR
     CHAR_ROSTER_TUNE = '♪'
     CHAR_ROSTER_ASKED = '?'
     CHAR_ROSTER_ACTIVITY = 'A'
-- 
cgit v1.2.3


From 0f1beb89e961b22b1db875cb9278fc6a28bc50be Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 27 Oct 2014 18:39:32 +0100
Subject: Use a setuptools entry point instead of a custom script

---
 scripts/poezio | 4 ----
 setup.py       | 3 ++-
 2 files changed, 2 insertions(+), 5 deletions(-)
 delete mode 100755 scripts/poezio

diff --git a/scripts/poezio b/scripts/poezio
deleted file mode 100755
index 665edcaa..00000000
--- a/scripts/poezio
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/python3
-
-from poezio import main
-main()
diff --git a/setup.py b/setup.py
index aeac3737..6c70c208 100755
--- a/setup.py
+++ b/setup.py
@@ -53,7 +53,8 @@ setup(name="poezio",
                    'poezio_plugins', 'poezio_plugins.gpg', 'poezio_themes'],
        package_dir = {'poezio': 'src', 'poezio_plugins': 'plugins', 'poezio_themes': 'data/themes'},
        package_data = {'poezio': ['default_config.cfg']},
-       scripts = ['scripts/poezio', 'scripts/poezio_gpg_export'],
+       scripts = ['scripts/poezio_gpg_export'],
+       entry_points={ 'console_scripts': [ 'poezio = poezio:main' ] },
        data_files = [('share/man/man1/', ['data/poezio.1'])],
 
        install_requires = ['sleekxmpp==1.2.4',
-- 
cgit v1.2.3


From 22f9b7a66ab4a3f5d3927e429912237df2a3aa15 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 27 Oct 2014 19:00:38 +0100
Subject: Prevent a (harmless) traceback while generating the documentation

---
 plugins/screen_detach.py | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/plugins/screen_detach.py b/plugins/screen_detach.py
index 3552a179..53827c11 100644
--- a/plugins/screen_detach.py
+++ b/plugins/screen_detach.py
@@ -7,15 +7,14 @@ import os
 import stat
 import pyinotify
 
-SCREEN_DIR = '/var/run/screen/S-%s' % (os.getlogin(),)
-
 class Plugin(BasePlugin):
     def init(self):
+        screen_dir = '/var/run/screen/S-%s' % (os.getlogin(),)
         self.timed_event = None
         sock_path = None
         self.thread = None
-        for f in os.listdir(SCREEN_DIR):
-            path = os.path.join(SCREEN_DIR, f)
+        for f in os.listdir(screen_dir):
+            path = os.path.join(screen_dir, f)
             if screen_attached(path):
                 sock_path = path
                 self.attached = True
-- 
cgit v1.2.3


From f55ac9edbfbfeb13e93cb101df8d38bd7bcce15a Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 27 Oct 2014 19:16:32 +0100
Subject: Mention that setuptools is kind of mandatory

---
 doc/source/install.rst | 5 ++++-
 requirements.txt       | 1 +
 setup.py               | 6 ++++++
 3 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/doc/source/install.rst b/doc/source/install.rst
index b655894b..dbe5ddfa 100644
--- a/doc/source/install.rst
+++ b/doc/source/install.rst
@@ -80,6 +80,8 @@ Poezio depends on two libraries:
 - DNSPython_ (the python3 version, often called dnspython3)
 - SleekXMPP_
 
+Additionally, it needs *python3-setuptools* to install an executable file.
+
 If you do not want to install those libraries, you can skip directly to
 the :ref:`installation part <poezio-install-label>`
 
@@ -139,7 +141,8 @@ If you have git installed, it will download and update locally the
 libraries for you. (and if you don’t have git installed, install it)
 
 
-If you really want to install it, run as root (or sudo in ubuntu or whatever):
+If you really want to install it, first install the *python3-setuptools* package
+in your distribution, then run as root (or sudo in ubuntu or whatever):
 
 .. code-block:: bash
 
diff --git a/requirements.txt b/requirements.txt
index 462dc735..fbb5895a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -2,6 +2,7 @@
 sleekxmpp==1.2
 dnspython3==1.11.1
 sphinx==1.2.1
+setuptools
 argparse
 pyinotify
 python-mpd2
diff --git a/setup.py b/setup.py
index 6c70c208..efd914b0 100755
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,13 @@
 #!/usr/bin/env python3
+
 try:
     from setuptools import setup, Extension
 except ImportError:
+    print('Setuptools was not found.\n'
+          'This script will use distutils instead, which will NOT'
+          ' be able to install a `poezio` executable.\nIf you are '
+          'using it to build a package or install poezio, please '
+          'install setuptools.\n\nYou will also see a few warnings.\n')
     from distutils.core import setup, Extension
 
 import os
-- 
cgit v1.2.3


From 29942d38bb88e4654879bcaff0a004c646e1a315 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Mon, 27 Oct 2014 20:01:22 +0100
Subject: Add some unit tests using py.test

- we need to have more
---
 Makefile             |   6 ++-
 src/common.py        |  64 +++--------------------------
 test/test_common.py  |  72 ++++++++++++++++++++++++++++++++
 test/test_config.py  | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++
 test/test_poopt.py   |  14 +++++++
 test/test_windows.py |  76 ++++++++++++++++++++++++++++++++++
 test/test_xhtml.py   |  50 ++++++++++++++++++++++
 7 files changed, 336 insertions(+), 60 deletions(-)
 create mode 100644 test/test_common.py
 create mode 100644 test/test_config.py
 create mode 100644 test/test_poopt.py
 create mode 100644 test/test_windows.py
 create mode 100644 test/test_xhtml.py

diff --git a/Makefile b/Makefile
index 1782fc9c..5b11efa6 100644
--- a/Makefile
+++ b/Makefile
@@ -32,6 +32,10 @@ uninstall:
 
 doc:
 	make -C doc/ html
+
+test:
+	py.test test/
+
 pot:
 	xgettext src/*.py --from-code=utf-8 --keyword=_ -o locale/poezio.pot
 
@@ -45,4 +49,4 @@ release:
 	 tar cJf poezio-$(version).tar.xz poezio-$(version) && \
 	 tar czf poezio-$(version).tar.gz poezio-$(version)
 
-.PHONY : doc
+.PHONY : doc test
diff --git a/src/common.py b/src/common.py
index 20700258..8cb02d4c 100644
--- a/src/common.py
+++ b/src/common.py
@@ -188,17 +188,6 @@ def datetime_tuple(timestamp):
     :param str timestamp: The string containing the formatted date.
     :return: The date.
     :rtype: :py:class:`datetime.datetime`
-
-    >>> time.timezone = 0; time.altzone = 0
-    >>> datetime_tuple('20130226T06:23:12')
-    datetime.datetime(2013, 2, 26, 6, 23, 12)
-    >>> datetime_tuple('2013-02-26T06:23:12+02:00')
-    datetime.datetime(2013, 2, 26, 4, 23, 12)
-    >>> time.timezone = -3600; time.altzone = -3600
-    >>> datetime_tuple('20130226T07:23:12')
-    datetime.datetime(2013, 2, 26, 8, 23, 12)
-    >>> datetime_tuple('2013-02-26T07:23:12+02:00')
-    datetime.datetime(2013, 2, 26, 6, 23, 12)
     """
     timestamp = timestamp.replace('-', '', 2).replace(':', '')
     date = timestamp[:15]
@@ -227,15 +216,10 @@ def datetime_tuple(timestamp):
 
 def get_utc_time(local_time=None):
     """
-    Get the current time in UTC
+    Get the current UTC time
 
     :param datetime local_time: The current local time
     :return: The current UTC time
-    >>> delta = timedelta(seconds=-3600)
-    >>> d = datetime.now()
-    >>> time.timezone = -3600; time.altzone = -3600
-    >>> get_utc_time(local_time=d) == d + delta
-    True
     """
     if local_time is None:
         local_time = datetime.now()
@@ -258,12 +242,6 @@ def get_utc_time(local_time=None):
 def get_local_time(utc_time):
     """
     Get the local time from an UTC time
-
-    >>> delta = timedelta(seconds=-3600)
-    >>> d = datetime.now()
-    >>> time.timezone = -3600; time.altzone = -3600
-    >>> get_local_time(d) == d - delta
-    True
     """
     if OLD_PYTHON:
         isdst = time.localtime(int(utc_time.strftime("%s"))).tm_isdst
@@ -315,16 +293,6 @@ def shell_split(st):
 
     >>> shell_split('"sdf 1" "toto 2"')
     ['sdf 1', 'toto 2']
-    >>> shell_split('toto "titi"')
-    ['toto', 'titi']
-    >>> shell_split('toto ""')
-    ['toto', '']
-    >>> shell_split('to"to titi "a" b')
-    ['to"to', 'titi', 'a', 'b']
-    >>> shell_split('"toto titi" toto ""')
-    ['toto titi', 'toto', '']
-    >>> shell_split('toto "titi')
-    ['toto', 'titi']
     """
     sh = shlex.shlex(st)
     ret = []
@@ -358,18 +326,8 @@ def find_argument(pos, text, quoted=True):
 
 def find_argument_quoted(pos, text):
     """
-    >>> find_argument_quoted(4, 'toto titi tata')
-    3
-    >>> find_argument_quoted(4, '"toto titi" tata')
-    0
-    >>> find_argument_quoted(8, '"toto" "titi tata"')
-    1
-    >>> find_argument_quoted(8, '"toto" "titi tata')
-    1
-    >>> find_argument_quoted(3, '"toto" "titi tata')
-    0
-    >>> find_argument_quoted(18, '"toto" "titi tata" ')
-    2
+    Get the number of the argument at position pos in
+    a string with possibly quoted text.
     """
     sh = shlex.shlex(text)
     count = -1
@@ -384,16 +342,8 @@ def find_argument_quoted(pos, text):
 
 def find_argument_unquoted(pos, text):
     """
-    >>> find_argument_unquoted(2, 'toto titi tata')
-    0
-    >>> find_argument_unquoted(3, 'toto titi tata')
-    0
-    >>> find_argument_unquoted(6, 'toto titi tata')
-    1
-    >>> find_argument_unquoted(4, 'toto titi tata')
-    3
-    >>> find_argument_unquoted(25, 'toto titi tata')
-    3
+    Get the number of the argument at position pos in
+    a string without interpreting quotes.
     """
     ret = text.split()
     search = 0
@@ -531,7 +481,3 @@ def safeJID(*args, **kwargs):
     except InvalidJID:
         return JID('')
 
-
-if __name__ == "__main__":
-    import doctest
-    doctest.testmod()
diff --git a/test/test_common.py b/test/test_common.py
new file mode 100644
index 00000000..36680328
--- /dev/null
+++ b/test/test_common.py
@@ -0,0 +1,72 @@
+"""
+Test the functions in the `common` module
+"""
+
+import pytest
+import sys
+sys.path.append('src')
+
+from common import (datetime_tuple, get_utc_time, get_local_time, shell_split,
+                    find_argument_quoted, find_argument_unquoted,
+                    parse_str_to_secs, parse_secs_to_str)
+import time
+from datetime import timedelta
+import datetime
+
+def test_datetime_tuple():
+    time.timezone = 0
+    time.altzone = 0
+
+    assert datetime_tuple('20130226T06:23:12') == datetime.datetime(2013, 2, 26, 6, 23, 12)
+    assert datetime_tuple('2013-02-26T06:23:12+02:00') == datetime.datetime(2013, 2, 26, 4, 23, 12)
+
+    time.timezone = -3600
+    time.altzone = -3600
+
+    assert datetime_tuple('20130226T07:23:12') == datetime.datetime(2013, 2, 26, 8, 23, 12)
+    assert datetime_tuple('2013-02-26T07:23:12+02:00') == datetime.datetime(2013, 2, 26, 6, 23, 12)
+
+def test_utc_time():
+    delta = timedelta(seconds=-3600)
+    d = datetime.datetime.now()
+    time.timezone = -3600; time.altzone = -3600
+    assert get_utc_time(local_time=d) == d + delta
+
+def test_local_time():
+    delta = timedelta(seconds=-3600)
+    d = datetime.datetime.now()
+    time.timezone = -3600
+    time.altzone = -3600
+    assert get_local_time(d) == d - delta
+
+#def find_delayed_tag(message):
+
+def test_shell_split():
+    assert shell_split('"sdf 1" "toto 2"') == ['sdf 1', 'toto 2']
+    assert shell_split('toto "titi"') == ['toto', 'titi']
+    assert shell_split('toto ""') == ['toto', '']
+    assert shell_split('to"to titi "a" b') == ['to"to', 'titi', 'a', 'b']
+    assert shell_split('"toto titi" toto ""') == ['toto titi', 'toto', '']
+    assert shell_split('toto "titi') == ['toto', 'titi']
+
+def test_argument_quoted():
+    assert find_argument_quoted(4, 'toto titi tata') == 3
+    assert find_argument_quoted(4, '"toto titi" tata') == 0
+    assert find_argument_quoted(8, '"toto" "titi tata"') == 1
+    assert find_argument_quoted(8, '"toto" "titi tata') == 1
+    assert find_argument_quoted(3, '"toto" "titi tata') == 0
+    assert find_argument_quoted(18, '"toto" "titi tata" ') == 2
+
+def test_argument_unquoted():
+    assert find_argument_unquoted(2, 'toto titi tata') == 0
+    assert find_argument_unquoted(3, 'toto titi tata') == 0
+    assert find_argument_unquoted(6, 'toto titi tata') == 1
+    assert find_argument_unquoted(4, 'toto titi tata') == 3
+    assert find_argument_unquoted(25, 'toto titi tata') == 3
+
+def test_parse_str_to_secs():
+    assert parse_str_to_secs("1d3m1h") == 90180
+    assert parse_str_to_secs("1d3mfaiiiiil") == 0
+
+def test_parse_secs_to_str():
+    assert  parse_secs_to_str(3601) == '1h1s'
diff --git a/test/test_config.py b/test/test_config.py
new file mode 100644
index 00000000..f8d06258
--- /dev/null
+++ b/test/test_config.py
@@ -0,0 +1,114 @@
+"""
+Test the config module
+"""
+
+import tempfile
+import pytest
+import sys
+import os
+
+
+sys.path.append('src')
+
+import config
+
+@pytest.yield_fixture(scope="module")
+def config_obj():
+    file_ = tempfile.NamedTemporaryFile(delete=False)
+    conf = config.Config(file_name=file_.name)
+    yield conf
+    del conf
+    os.unlink(file_.name)
+
+class TestConfigSimple(object):
+    def test_get_set(self, config_obj):
+        config_obj.set_and_save('test', value='coucou')
+        config_obj.set_and_save('test2', value='true')
+        assert config_obj.get('test') == 'coucou'
+        assert config_obj.get('test2') == 'true'
+        assert config_obj.get('toto') == ''
+
+    def test_file_content(self, config_obj):
+        with open(config_obj.file_name, 'r') as fd:
+            data = fd.read()
+        supposed_content = '[Poezio]\ntest = coucou\ntest2 = true\n'
+        assert data == supposed_content
+
+    def test_get_types(self, config_obj):
+
+        config_obj.set_and_save('test_int', '99')
+        config_obj.set_and_save('test_int_neg', '-1')
+        config_obj.set_and_save('test_bool_t', 'true')
+        config_obj.set_and_save('test_bool_f', 'false')
+        config_obj.set_and_save('test_float', '1.5')
+
+        assert config_obj.get('test_int', default=0) == 99
+        assert config_obj.get('test_int_neg', default=0) == -1
+        assert config_obj.get('test_bool_t', default=False) == True
+        assert config_obj.get('test_bool_f', default=True) == False
+        assert config_obj.get('test_float', default=1.0) == 1.5
+
+    def test_remove(self, config_obj):
+        with open(config_obj.file_name, 'r') as fd:
+            data = fd.read()
+
+        supposed_content = ('[Poezio]\ntest = coucou\ntest2 = true\n'
+                            'test_int = 99\ntest_int_neg = -1\ntest_bool_t ='
+                            ' true\ntest_bool_f = false\ntest_float = 1.5\n')
+
+        assert data == supposed_content
+
+        config_obj.remove_and_save('test_int')
+        config_obj.remove_and_save('test_int_neg')
+        config_obj.remove_and_save('test_bool_t')
+        config_obj.remove_and_save('test_bool_f')
+        config_obj.remove_and_save('test_float')
+
+        with open(config_obj.file_name, 'r') as fd:
+            data = fd.read()
+
+        supposed_content = '[Poezio]\ntest = coucou\ntest2 = true\n'
+
+        assert data == supposed_content
+
+
+    def test_toggle(self, config_obj):
+        config_obj.set_and_save('test2', value='toggle')
+        assert config_obj.get('test2') == 'false'
+        config_obj.set_and_save('test2', value='toggle')
+        assert config_obj.get('test2') == 'true'
+
+    def test_get_set_default(self, config_obj):
+        assert config_obj.get('doesnotexist', 'toto@tata') == 'toto@tata'
+        assert config_obj.get('doesnotexist2', '1234') == '1234'
+
+class TestConfigSections(object):
+    def test_set_section(self, config_obj):
+        config_obj.set_and_save('option1', 'test', section='NotPoezio')
+        config_obj.set_and_save('option2', 'test2', section='NotPoezio')
+
+        assert config_obj.get('option1', section='NotPoezio') == 'test'
+        assert config_obj.get('option2', section='NotPoezio') == 'test2'
+
+    def test_file_content(self, config_obj):
+        with open(config_obj.file_name, 'r') as fd:
+            data = fd.read()
+        supposed_content = ('[Poezio]\ntest = coucou\ntest2 = true\n'
+                            '[NotPoezio]\noption1 = test\noption2 = test2\n')
+        assert data == supposed_content
+
+class TestTabNames(object):
+    def test_get_tabname(self, config_obj):
+        config.post_logging_setup()
+        config_obj.set_and_save('test', value='value.toto@toto.com',
+                                section='toto@toto.com')
+        config_obj.set_and_save('test2', value='value2@toto.com',
+                                section='@toto.com')
+
+        assert config_obj.get_by_tabname('test', 'toto@toto.com') == 'value.toto@toto.com'
+        assert config_obj.get_by_tabname('test2', 'toto@toto.com') == 'value2@toto.com'
+        assert config_obj.get_by_tabname('test2', 'toto@toto.com', fallback=False) == 'value2@toto.com'
+        assert config_obj.get_by_tabname('test2', 'toto@toto.com', fallback_server=False) == 'true'
+        assert config_obj.get_by_tabname('test_int', 'toto@toto.com', fallback=False) == ''
+
+
diff --git a/test/test_poopt.py b/test/test_poopt.py
new file mode 100644
index 00000000..9b640ff0
--- /dev/null
+++ b/test/test_poopt.py
@@ -0,0 +1,14 @@
+"""
+Test of the poopt module
+"""
+
+import pytest
+import sys
+sys.path.append('src')
+
+from poopt import cut_text
+
+def test_cut_text():
+
+    text = '12345678901234567890'
+    assert cut_text(text, 5) == [(0, 5), (5, 10), (10, 15), (15, 20)]
diff --git a/test/test_windows.py b/test/test_windows.py
new file mode 100644
index 00000000..8fb85295
--- /dev/null
+++ b/test/test_windows.py
@@ -0,0 +1,76 @@
+import pytest
+import sys
+sys.path.append('src')
+
+class ConfigShim(object):
+    def get(self, *args, **kwargs):
+        return ''
+
+import config
+config.config = ConfigShim()
+import core
+
+from windows import Input, HistoryInput, MessageInput, CommandInput
+
+@pytest.fixture
+def input():
+    input = Input()
+    input.rewrite_text = lambda: None
+    return input
+
+class TestInput(object):
+
+    def test_do_command(self, input):
+
+        input.do_command('a')
+        assert input.text == 'a'
+
+        for char in 'coucou':
+            input.do_command(char)
+        assert input.text == 'acoucou'
+
+    def test_empty(self, input):
+        assert input.is_empty() == True
+        input.do_command('a')
+        assert input.is_empty() == False
+
+    def test_key_left(self, input):
+        for char in 'this is a line':
+            input.do_command(char)
+        for i in range(4):
+            input.key_left()
+        for char in 'long ':
+            input.do_command(char)
+
+        assert input.text == 'this is a long line'
+
+    def test_key_right(self, input):
+        for char in 'this is a line':
+            input.do_command(char)
+        for i in range(4):
+            input.key_left()
+        input.key_right()
+
+        for char in 'iii':
+            input.do_command(char)
+
+        assert input.text == 'this is a liiiine'
+
+    def test_key_home(self, input):
+        for char in 'this is a line of text':
+            input.do_command(char)
+        input.do_command('z')
+        input.key_home()
+        input.do_command('a')
+
+        assert input.text == 'athis is a line of textz'
+
+    def test_key_end(self, input):
+        for char in 'this is a line of text':
+            input.do_command(char)
+        input.key_home()
+        input.key_end()
+        input.do_command('z')
+
+        assert input.text == 'this is a line of textz'
+
diff --git a/test/test_xhtml.py b/test/test_xhtml.py
new file mode 100644
index 00000000..58857d67
--- /dev/null
+++ b/test/test_xhtml.py
@@ -0,0 +1,50 @@
+"""
+Test the functions in the `xhtml` module
+"""
+
+import pytest
+import sys
+import xml
+sys.path.append('src')
+
+from xhtml import (poezio_colors_to_html, xhtml_to_poezio_colors,
+                   parse_css, clean_text)
+
+def test_clean_text():
+    example_string = '\x191}Toto \x192,-1}titi\x19b Tata'
+    assert clean_text(example_string) == 'Toto titi Tata'
+
+    clean_string = 'toto titi tata'
+    assert clean_text(clean_string) == clean_string
+
+def test_poezio_colors_to_html():
+    base = "<body xmlns='http://www.w3.org/1999/xhtml'><p>"
+    end = "</p></body>"
+    text = '\x191}coucou'
+    assert poezio_colors_to_html(text) == base + '<span style="color: red;">coucou</span>' + end
+
+    text = '\x19bcoucou\x19o toto \x194}titi'
+    assert poezio_colors_to_html(text) == base + '<span style="font-weight: bold;">coucou</span> toto <span style="color: blue;">titi</span>' + end
+
+def test_xhtml_to_poezio_colors():
+    start = b'<body xmlns="http://www.w3.org/1999/xhtml"><p>'
+    end = b'</p></body>'
+    xhtml = start + b'test' + end
+    assert xhtml_to_poezio_colors(xhtml) == 'test'
+
+    xhtml = start + b'<a href="http://perdu.com">salut</a>' + end
+    assert xhtml_to_poezio_colors(xhtml) == '\x19usalut\x19o (http://perdu.com)'
+
+    xhtml = start + b'<a href="http://perdu.com">http://perdu.com</a>' + end
+    assert xhtml_to_poezio_colors(xhtml) == '\x19uhttp://perdu.com\x19o'
+
+    with pytest.raises(xml.sax._exceptions.SAXParseException):
+        xhtml_to_poezio_colors(b'<p>Invalid xml')
+
+def test_parse_css():
+    example_css = 'text-decoration: underline; color: red;'
+    assert parse_css(example_css) == '\x19u\x19196}'
+
+    example_css = 'text-decoration: underline coucou color: red;'
+    assert parse_css(example_css) == ''
+
-- 
cgit v1.2.3


From d3f9a2f3c47f64f08778233c82635ca26cfaf34b Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Tue, 28 Oct 2014 16:34:21 +0100
Subject: =?UTF-8?q?Fix=20the=20requirements.txt=20(don=E2=80=99t=20install?=
 =?UTF-8?q?=20potr=20in=20editable=20mode)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 requirements.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/requirements.txt b/requirements.txt
index fbb5895a..79d2a470 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
--e git://github.com/afflux/pure-python-otr.git#egg=potr
+git+git://github.com/afflux/pure-python-otr.git#egg=potr
 sleekxmpp==1.2
 dnspython3==1.11.1
 sphinx==1.2.1
-- 
cgit v1.2.3


From 9eb42bea105d37cc5a296be1eb5f14fce93711a0 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Tue, 28 Oct 2014 17:05:15 +0100
Subject: Add a .travis.yml file for travis-ci

---
 .travis.yml | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100644 .travis.yml

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 00000000..3152dfa0
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,7 @@
+language: python
+python:
+    - "3.4"
+install:
+    - pip install -r requirements.txt
+    - python setup.py -q build install
+script: make test
-- 
cgit v1.2.3


From c3aa6c029d67a9612ebabf84784a6571fa603e4c Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Tue, 28 Oct 2014 22:12:18 +0100
Subject: Fix the dependencies in the setup.py

---
 .travis.yml | 2 +-
 setup.py    | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 3152dfa0..2c27b4a1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,5 +3,5 @@ python:
     - "3.4"
 install:
     - pip install -r requirements.txt
-    - python setup.py -q build install
+    - python setup.py build
 script: make test
diff --git a/setup.py b/setup.py
index efd914b0..e9e8e4a7 100755
--- a/setup.py
+++ b/setup.py
@@ -63,8 +63,8 @@ setup(name="poezio",
        entry_points={ 'console_scripts': [ 'poezio = poezio:main' ] },
        data_files = [('share/man/man1/', ['data/poezio.1'])],
 
-       install_requires = ['sleekxmpp==1.2.4',
-                           'dnspython3>=1.11.1'],
+       install_requires = ['sleekxmpp>=1.2.4',
+                           'dnspython3>=1.10.0'],
        extras_require = {'OTR plugin': 'python-potr>=1.0',
                          'Screen autoaway plugin': 'pyinotify==0.9.4'}
 )
-- 
cgit v1.2.3


From 70befec8cad1633baee30530e353bca5da9b8d32 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Wed, 29 Oct 2014 03:04:34 +0100
Subject: Add some tests

- also fix that travis build
---
 .travis.yml          |  2 +-
 Makefile             |  2 +-
 test/test_common.py  | 23 +++++++++++++++--------
 test/test_theming.py | 26 ++++++++++++++++++++++++++
 4 files changed, 43 insertions(+), 10 deletions(-)
 create mode 100644 test/test_theming.py

diff --git a/.travis.yml b/.travis.yml
index 2c27b4a1..28bf3dd3 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,5 +3,5 @@ python:
     - "3.4"
 install:
     - pip install -r requirements.txt
-    - python setup.py build
+    - python setup.py build_ext --inplace
 script: make test
diff --git a/Makefile b/Makefile
index 5b11efa6..7f5eb224 100644
--- a/Makefile
+++ b/Makefile
@@ -34,7 +34,7 @@ doc:
 	make -C doc/ html
 
 test:
-	py.test test/
+	py.test -v test/
 
 pot:
 	xgettext src/*.py --from-code=utf-8 --keyword=_ -o locale/poezio.pot
diff --git a/test/test_common.py b/test/test_common.py
index 36680328..315318bd 100644
--- a/test/test_common.py
+++ b/test/test_common.py
@@ -2,16 +2,17 @@
 Test the functions in the `common` module
 """
 
-import pytest
 import sys
 sys.path.append('src')
 
-from common import (datetime_tuple, get_utc_time, get_local_time, shell_split,
-                    find_argument_quoted, find_argument_unquoted,
-                    parse_str_to_secs, parse_secs_to_str)
 import time
-from datetime import timedelta
+import pytest
 import datetime
+from sleekxmpp import JID
+from datetime import timedelta
+from common import (datetime_tuple, get_utc_time, get_local_time, shell_split,
+                    find_argument_quoted, find_argument_unquoted,
+                    parse_str_to_secs, parse_secs_to_str, safeJID)
 
 def test_datetime_tuple():
     time.timezone = 0
@@ -39,8 +40,6 @@ def test_local_time():
     time.altzone = -3600
     assert get_local_time(d) == d - delta
 
-#def find_delayed_tag(message):
-
 def test_shell_split():
     assert shell_split('"sdf 1" "toto 2"') == ['sdf 1', 'toto 2']
     assert shell_split('toto "titi"') == ['toto', 'titi']
@@ -69,4 +68,12 @@ def test_parse_str_to_secs():
     assert parse_str_to_secs("1d3mfaiiiiil") == 0
 
 def test_parse_secs_to_str():
-    assert  parse_secs_to_str(3601) == '1h1s'
+    assert parse_secs_to_str(3601) == '1h1s'
+    assert parse_secs_to_str(0) == '0s'
+
+    with pytest.raises(TypeError):
+        parse_secs_to_str('toto')
+
+def test_safeJID():
+    assert safeJID('toto@titi/tata') == JID('toto@titi/tata')
+    assert safeJID('é_è') == JID('')
diff --git a/test/test_theming.py b/test/test_theming.py
new file mode 100644
index 00000000..9cdb4829
--- /dev/null
+++ b/test/test_theming.py
@@ -0,0 +1,26 @@
+"""
+Test the functions in the `theming` module
+"""
+
+import sys
+import pytest
+sys.path.append('src')
+
+from theming import dump_tuple, read_tuple
+
+def test_read_tuple():
+    assert read_tuple('1,-1,u') == ((1, -1), 'u')
+    assert read_tuple('1,2') == ((1, 2), None)
+
+    with pytest.raises(IndexError):
+        read_tuple('1')
+
+    with pytest.raises(ValueError):
+        read_tuple('toto')
+
+def test_dump_tuple():
+    assert dump_tuple((1, 2)) == '1,2'
+    assert dump_tuple((1, )) == '1'
+    assert dump_tuple((1, 2, 'u')) == '1,2,u'
+
+
-- 
cgit v1.2.3


From 616d0d2c7301f5e287fb7daf24a835f8dbf250ba Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Wed, 29 Oct 2014 23:28:20 +0100
Subject: Fix two potential tracebacks on /set completion

---
 src/core/completions.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/core/completions.py b/src/core/completions.py
index ca0bb41e..bc21a6c0 100644
--- a/src/core/completions.py
+++ b/src/core/completions.py
@@ -324,7 +324,7 @@ def completion_set(self, the_input):
         if '|' in args[1]:
             plugin_name, section = args[1].split('|')[:2]
             if not plugin_name in self.plugin_manager.plugins:
-                    return the_input.auto_completion([''], n, quotify=True)
+                    return the_input.new_completion([''], n, quotify=True)
             plugin = self.plugin_manager.plugins[plugin_name]
             end_list = plugin.config.options(section or plugin_name)
         elif not config.has_option('Poezio', args[1]):
@@ -339,7 +339,7 @@ def completion_set(self, the_input):
         if '|' in args[1]:
             plugin_name, section = args[1].split('|')[:2]
             if not plugin_name in self.plugin_manager.plugins:
-                    return the_input.auto_completion([''], n, quotify=True)
+                    return the_input.new_completion([''], n, quotify=True)
             plugin = self.plugin_manager.plugins[plugin_name]
             end_list = [plugin.config.get(args[2], '', section or plugin_name), '']
         else:
-- 
cgit v1.2.3


From f93f92517d3597aaf69625200f47da7a243b43a8 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Thu, 30 Oct 2014 21:43:49 +0100
Subject: Use a space instead of a cross while checking features

---
 src/tabs/basetabs.py | 2 +-
 src/theming.py       | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/tabs/basetabs.py b/src/tabs/basetabs.py
index 40dcb8e6..1b6e0d4c 100644
--- a/src/tabs/basetabs.py
+++ b/src/tabs/basetabs.py
@@ -818,7 +818,7 @@ class OneToOneTab(ChatTab):
             self.__initial_disco = True
 
         ok = get_theme().CHAR_OK
-        nope = get_theme().CHAR_ERROR
+        nope = get_theme().CHAR_EMPTY
 
         correct = ok if correct else nope
         attention = ok if attention else nope
diff --git a/src/theming.py b/src/theming.py
index 0d83db9d..1e9d6c40 100755
--- a/src/theming.py
+++ b/src/theming.py
@@ -303,6 +303,7 @@ class Theme(object):
     CHAR_NEW_TEXT_SEPARATOR = '- '
     CHAR_OK = '✔'
     CHAR_ERROR = '✖'
+    CHAR_EMPTY = ' '
     CHAR_ACK_RECEIVED = CHAR_OK
     CHAR_COLUMN_ASC = ' ▲'
     CHAR_COLUMN_DESC = ' ▼'
-- 
cgit v1.2.3


From 58b424a9232dd215afbaf95e8831f09b1a0169c1 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Thu, 30 Oct 2014 22:02:23 +0100
Subject: notify_messages is of course True by default

---
 src/config.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/config.py b/src/config.py
index 0504dbd5..bd703c5a 100644
--- a/src/config.py
+++ b/src/config.py
@@ -77,6 +77,7 @@ DEFAULT_CONFIG = {
         'max_messages_in_memory': 2048,
         'max_nick_length': 25,
         'muc_history_length': 50,
+        'notify_messages': True,
         'open_all_bookmarks': False,
         'password': '',
         'plugins_autoload': '',
-- 
cgit v1.2.3


From f097efd85221b30cd5293b3f7dc52ccbb6fdc05c Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Fri, 31 Oct 2014 15:54:33 +0100
Subject: Fix some tracebacks on /set completion

(introduced by the automated default arguments of config.get)
---
 src/core/completions.py | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/core/completions.py b/src/core/completions.py
index bc21a6c0..9549c13f 100644
--- a/src/core/completions.py
+++ b/src/core/completions.py
@@ -334,19 +334,19 @@ def completion_set(self, the_input):
             else:
                 end_list = []
         else:
-            end_list = [config.get(args[1], ''), '']
+            end_list = [str(config.get(args[1], '')), '']
     elif n == 3:
         if '|' in args[1]:
             plugin_name, section = args[1].split('|')[:2]
             if not plugin_name in self.plugin_manager.plugins:
                     return the_input.new_completion([''], n, quotify=True)
             plugin = self.plugin_manager.plugins[plugin_name]
-            end_list = [plugin.config.get(args[2], '', section or plugin_name), '']
+            end_list = [str(plugin.config.get(args[2], '', section or plugin_name)), '']
         else:
             if not config.has_section(args[1]):
                 end_list = ['']
             else:
-                end_list = [config.get(args[2], '', args[1]), '']
+                end_list = [str(config.get(args[2], '', args[1])), '']
     else:
         return
     return the_input.new_completion(end_list, n, quotify=True)
-- 
cgit v1.2.3


From ea2b703bfd07d293ba9fdd85ac657275d43da2a7 Mon Sep 17 00:00:00 2001
From: mathieui <mathieui@mathieui.net>
Date: Fri, 31 Oct 2014 18:14:56 +0100
Subject: Fix the default value of Config.get to None

Might fix some not-yet-seen bugs
---
 src/config.py | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/src/config.py b/src/config.py
index bd703c5a..1c191eb1 100644
--- a/src/config.py
+++ b/src/config.py
@@ -156,16 +156,19 @@ class Config(RawConfigParser):
             if not self.has_section(section):
                 self.add_section(section)
 
-    def get(self, option, default='', section=DEFSECTION):
+    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 self.default and not default \
-                and self.default.get(section, {}).get(option) is not None:
-            default = self.default[section][option]
+        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)
@@ -177,6 +180,7 @@ class Config(RawConfigParser):
                 res = self.getstr(option, section)
         except (NoOptionError, NoSectionError, ValueError, AttributeError):
             return default
+
         if res is None:
             return default
         return res
-- 
cgit v1.2.3