diff --git a/gajim/chat_control.py b/gajim/chat_control.py
index 7f81dc6e87f5fe8802a30110ac815fec5d016585..eea631fa16788f7171495e8e5625953d589319dc 100644
--- a/gajim/chat_control.py
+++ b/gajim/chat_control.py
@@ -42,6 +42,7 @@ from gajim.common import helpers
 from gajim.common import ged
 from gajim.common import i18n
 from gajim.common.i18n import _
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.contacts import GC_Contact
 from gajim.common.const import AvatarSize
 from gajim.common.const import KindConstant
@@ -933,7 +934,7 @@ class ChatControl(ChatControlBase):
         contact = self.contact
 
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
 
         if frm == 'status':
             if not app.config.get('print_status_in_chats'):
diff --git a/gajim/chat_control_base.py b/gajim/chat_control_base.py
index 44eee3f39be15e9cff571ac4fa2dbb6acd921f7b..6b1e33b396158aa6e8847b83cc92099b16657937 100644
--- a/gajim/chat_control_base.py
+++ b/gajim/chat_control_base.py
@@ -38,6 +38,7 @@ from gajim.common import helpers
 from gajim.common import ged
 from gajim.common import i18n
 from gajim.common.i18n import _
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.contacts import GC_Contact
 from gajim.common.connection_handlers_events import MessageOutgoingEvent
 from gajim.common.const import StyleAttr
@@ -905,7 +906,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
         if other_tags_for_text is None:
             other_tags_for_text = []
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
 
         textview.print_conversation_line(text, jid, kind, name, tim,
             other_tags_for_name, other_tags_for_time, other_tags_for_text,
diff --git a/gajim/common/connection_handlers_events.py b/gajim/common/connection_handlers_events.py
index b72339caf6b689dc698fa3d8e8627d5107467a7b..99365b0a814cde9e98d2e1631446d187deb49774 100644
--- a/gajim/common/connection_handlers_events.py
+++ b/gajim/common/connection_handlers_events.py
@@ -28,6 +28,7 @@ from gajim.common import helpers
 from gajim.common import app
 from gajim.common import i18n
 from gajim.common.i18n import _
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.modules import dataforms
 from gajim.common.modules.misc import parse_idle
 from gajim.common.modules.misc import parse_delay
@@ -91,14 +92,12 @@ class HelperEvent:
     def get_oob_data(self, stanza):
         oob_node = stanza.getTag('x', namespace=nbxmpp.NS_X_OOB)
         if oob_node is not None:
-            if 'gajim' not in self.additional_data:
-                self.additional_data['gajim'] = {}
             oob_url = oob_node.getTagData('url')
             if oob_url is not None:
-                self.additional_data['gajim']['oob_url'] = oob_url
+                self.additional_data.set_value('gajim', 'oob_url', oob_url)
             oob_desc = oob_node.getTagData('desc')
             if oob_desc is not None:
-                self.additional_data['gajim']['oob_desc'] = oob_desc
+                self.additional_data.set_value('gajim', 'oob_desc', oob_desc)
 
     def get_stanza_id(self, stanza, query=False):
         if query:
@@ -407,7 +406,7 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
     def generate(self):
         self.stanza = self.msg_obj.stanza
         if not hasattr(self.msg_obj, 'additional_data'):
-            self.additional_data = {}
+            self.additional_data = AdditionalDataDict()
         else:
             self.additional_data = self.msg_obj.additional_data
         self.id_ = self.msg_obj.stanza.getID()
@@ -626,18 +625,9 @@ class ConnectionTypeEvent(nec.NetworkIncomingEvent):
 class StanzaReceivedEvent(nec.NetworkIncomingEvent):
     name = 'stanza-received'
 
-    def init(self):
-        self.additional_data = {}
-
-    def generate(self):
-        return True
-
 class StanzaSentEvent(nec.NetworkIncomingEvent):
     name = 'stanza-sent'
 
-    def init(self):
-        self.additional_data = {}
-
 class AgentRemovedEvent(nec.NetworkIncomingEvent):
     name = 'agent-removed'
 
@@ -1158,7 +1148,7 @@ class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
     name = 'message-outgoing'
 
     def init(self):
-        self.additional_data = {}
+        self.additional_data = AdditionalDataDict()
         self.message = None
         self.keyID = None
         self.type_ = 'chat'
@@ -1211,7 +1201,7 @@ class GcMessageOutgoingEvent(nec.NetworkOutgoingEvent):
     name = 'gc-message-outgoing'
 
     def init(self):
-        self.additional_data = {}
+        self.additional_data = AdditionalDataDict()
         self.message = ''
         self.chatstate = None
         self.xhtml = None
diff --git a/gajim/common/events.py b/gajim/common/events.py
index 27c36eae06a0f018280a51593914de6ec12e5fe9..df2a1fe485fddcbbca94a09266c5d0b0e0663322 100644
--- a/gajim/common/events.py
+++ b/gajim/common/events.py
@@ -81,7 +81,8 @@ class ChatEvent(Event):
         self.displaymarking = displaymarking
         self.sent_forwarded = sent_forwarded
         if additional_data is None:
-            additional_data = {}
+            from gajim.common.helpers import AdditionalDataDict
+            additional_data = AdditionalDataDict()
         self.additional_data = additional_data
 
 class NormalEvent(ChatEvent):
diff --git a/gajim/common/helpers.py b/gajim/common/helpers.py
index 59e031cb1732d1f3228124e28b75427b3fe65e83..ba3baf72c07e8d91b340f8146bd842091ee13acf 100644
--- a/gajim/common/helpers.py
+++ b/gajim/common/helpers.py
@@ -41,6 +41,7 @@ import time
 import logging
 import json
 import shutil
+import collections
 from datetime import datetime, timedelta
 from distutils.version import LooseVersion as V
 from encodings.punycode import punycode_encode
@@ -1485,3 +1486,51 @@ def load_json(path, key=None, default=None):
     if key is None:
         return json_dict
     return json_dict.get(key, default)
+
+class AdditionalDataDict(collections.UserDict):
+    def __init__(self, initialdata=None):
+        collections.UserDict.__init__(self, initialdata)
+
+    @staticmethod
+    def _get_path_childs(full_path):
+        path_childs = [full_path]
+        if ':' in full_path:
+            path_childs = full_path.split(':')
+        return path_childs
+
+    def set_value(self, full_path, key, value):
+        path_childs = self._get_path_childs(full_path)
+        _dict = self.data
+        for path in path_childs:
+            try:
+                _dict = _dict[path]
+            except KeyError:
+                _dict[path] = {}
+                _dict = _dict[path]
+        _dict[key] = value
+
+    def get_value(self, full_path, key, default=None):
+        path_childs = self._get_path_childs(full_path)
+        _dict = self.data
+        for path in path_childs:
+            try:
+                _dict = _dict[path]
+            except KeyError:
+                return default
+        try:
+            return _dict[key]
+        except KeyError:
+            return default
+
+    def remove_value(self, full_path, key):
+        path_childs = self._get_path_childs(full_path)
+        _dict = self.data
+        for path in path_childs:
+            try:
+                _dict = _dict[path]
+            except KeyError:
+                return
+        try:
+            del _dict[key]
+        except KeyError:
+            return
diff --git a/gajim/common/logger.py b/gajim/common/logger.py
index 25a0d542e3138155f42b4b0da7437db89ebab9a6..c4d238f8e2bdf9b4c50982ecc9a023f943fee0d0 100644
--- a/gajim/common/logger.py
+++ b/gajim/common/logger.py
@@ -41,6 +41,7 @@ from gi.repository import GLib
 from gajim.common import exceptions
 from gajim.common import app
 from gajim.common import configpaths
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.i18n import _
 from gajim.common.const import (
     JIDConstant, KindConstant, ShowConstant, TypeConstant,
@@ -265,8 +266,9 @@ class Logger:
         Row = namedtuple("Row", fields)
         named_row = Row(*row)
         if 'additional_data' in fields:
+            _dict = json.loads(named_row.additional_data or '{}')
             named_row = named_row._replace(
-                additional_data=json.loads(named_row.additional_data or '{}'))
+                additional_data=AdditionalDataDict(_dict))
 
         # if an alias `account` for the field `account_id` is used for the
         # query, the account_id is converted to the account jid
@@ -1355,7 +1357,8 @@ class Logger:
             if not kwargs['additional_data']:
                 del kwargs['additional_data']
             else:
-                kwargs['additional_data'] = json.dumps(kwargs["additional_data"])
+                serialized_dict = json.dumps(kwargs["additional_data"].data)
+                kwargs['additional_data'] = serialized_dict
 
         sql = '''
               INSERT INTO logs (account_id, jid_id, time, kind, {columns})
diff --git a/gajim/common/modules/httpupload.py b/gajim/common/modules/httpupload.py
index 1cb6aaa135c0629ac66366325bfb220eb019bff1..77deece65192c4e28cc459cd172eae4481b59858 100644
--- a/gajim/common/modules/httpupload.py
+++ b/gajim/common/modules/httpupload.py
@@ -118,10 +118,7 @@ class HTTPUpload:
             #  to distinguish HTTP File Upload Link from pasted URL
             oob = event.msg_iq.addChild('x', namespace=nbxmpp.NS_X_OOB)
             oob.addChild('url').setData(message)
-            if 'gajim' in event.additional_data:
-                event.additional_data['gajim']['oob_url'] = message
-            else:
-                event.additional_data['gajim'] = {'oob_url': message}
+            event.additional_data.set_value('gajim', 'oob_url', message)
 
     def check_file_before_transfer(self, path, encryption, contact, session,
                                    groupchat=False):
diff --git a/gajim/common/modules/mam.py b/gajim/common/modules/mam.py
index 023e9e67a3c6190b4b3b02e759814e736228ff4e..ffb8f44560870f0a8cec36249ce7f07aa9154b2d 100644
--- a/gajim/common/modules/mam.py
+++ b/gajim/common/modules/mam.py
@@ -27,6 +27,7 @@ from gajim.common.const import KindConstant
 from gajim.common.const import SyncThreshold
 from gajim.common.caps_cache import muc_caps_cache
 from gajim.common.helpers import get_sync_threshold
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.modules.misc import parse_delay
 from gajim.common.modules.misc import parse_oob
 from gajim.common.modules.misc import parse_correction
@@ -179,7 +180,7 @@ class MAM:
 
         event_attrs.update(
             {'conn': self._con,
-             'additional_data': {},
+             'additional_data': AdditionalDataDict(),
              'encrypted': False,
              'timestamp': delay_timestamp,
              'self_message': self_message,
@@ -192,6 +193,7 @@ class MAM:
              'archive_jid': archive_jid,
              'msgtxt': msgtxt,
              'message': message,
+             'stanza': message,
              'namespace': namespace,
              })
 
@@ -256,7 +258,7 @@ class MAM:
                 'gajim', 'user_timestamp', user_timestamp)
 
         event.correct_id = parse_correction(event.message)
-        parse_oob(event.message, event.additional_data)
+        parse_oob(event)
 
         with_ = event.with_.getStripped()
         if event.muc_pm:
diff --git a/gajim/common/modules/message.py b/gajim/common/modules/message.py
index 1980f4baec152d7ef5f08287a4521b708dcab8ec..b9991a911e3d30c15040505346026ad58e58fd78 100644
--- a/gajim/common/modules/message.py
+++ b/gajim/common/modules/message.py
@@ -22,7 +22,9 @@ import nbxmpp
 from gajim.common import app
 from gajim.common import helpers
 from gajim.common.i18n import _
-from gajim.common.nec import NetworkIncomingEvent, NetworkEvent
+from gajim.common.nec import NetworkIncomingEvent
+from gajim.common.nec import NetworkEvent
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.modules.security_labels import parse_securitylabel
 from gajim.common.modules.user_nickname import parse_nickname
 from gajim.common.modules.carbons import parse_carbon
@@ -188,7 +190,7 @@ class Message:
             'account': self._account,
             'id_': id_,
             'encrypted': False,
-            'additional_data': {},
+            'additional_data': AdditionalDataDict(),
             'forwarded': forwarded,
             'sent': sent,
             'fjid': fjid,
@@ -266,7 +268,7 @@ class Message:
             'timestamp': timestamp,
             'delayed': user_timestamp is not None,
         }
-        parse_oob(event.stanza, event.additional_data)
+        parse_oob(event)
 
         for name, value in event_attr.items():
             setattr(event, name, value)
diff --git a/gajim/common/modules/misc.py b/gajim/common/modules/misc.py
index 6abe06320ac3ad16f647d14abd8d76391170c900..ddc396390daf7d05e1db4dcc103a06c594148a46 100644
--- a/gajim/common/modules/misc.py
+++ b/gajim/common/modules/misc.py
@@ -108,27 +108,18 @@ def parse_delay(stanza, epoch=True, convert='utc', from_=None, not_from=None):
 
 # XEP-0066: Out of Band Data
 
-def parse_oob(stanza, dict_=None, key='gajim'):
-    oob_node = stanza.getTag('x', namespace=nbxmpp.NS_X_OOB)
+def parse_oob(event):
+    oob_node = event.stanza.getTag('x', namespace=nbxmpp.NS_X_OOB)
     if oob_node is None:
         return
-    result = {}
+
     url = oob_node.getTagData('url')
     if url is not None:
-        result['oob_url'] = url
+        event.additional_data.set_value('gajim', 'oob_url', url)
+
     desc = oob_node.getTagData('desc')
     if desc is not None:
-        result['oob_desc'] = desc
-
-    if dict_ is None:
-        return result
-
-    if key in dict_:
-        dict_[key] += result
-    else:
-        dict_[key] = result
-
-    return dict_
+        event.additional_data.set_value('gajim', 'oob_desc', desc)
 
 
 # XEP-0308: Last Message Correction
diff --git a/gajim/common/zeroconf/connection_handlers_zeroconf.py b/gajim/common/zeroconf/connection_handlers_zeroconf.py
index 6550f0a0aaea5c59ffcebb8ee80c63efb3c7e975..8d3f0259bdf6750b6adc63449e4224d58512a9a4 100644
--- a/gajim/common/zeroconf/connection_handlers_zeroconf.py
+++ b/gajim/common/zeroconf/connection_handlers_zeroconf.py
@@ -30,6 +30,7 @@ from gajim.common.protocol.bytestream import ConnectionSocks5BytestreamZeroconf
 from gajim.common.zeroconf.zeroconf import Constant
 from gajim.common import connection_handlers
 from gajim.common.i18n import _
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.nec import NetworkIncomingEvent, NetworkEvent
 from gajim.common.modules.user_nickname import parse_nickname
 from gajim.common.modules.misc import parse_eme
@@ -118,7 +119,7 @@ connection_handlers.ConnectionJingle):
             'account': self.name,
             'id_': id_,
             'encrypted': False,
-            'additional_data': {},
+            'additional_data': AdditionalDataDict(),
             'forwarded': False,
             'sent': False,
             'timestamp': time.time(),
@@ -165,7 +166,7 @@ connection_handlers.ConnectionJingle):
             'stanza_id': event.unique_id
         }
 
-        parse_oob(event.stanza, event.additional_data)
+        parse_oob(event)
 
         for name, value in event_attr.items():
             setattr(event, name, value)
diff --git a/gajim/conversation_textview.py b/gajim/conversation_textview.py
index 7c892c1a9142086e1378990a543a892dc20d1282..bbd098eec74d74096c8b4f3d01347abfeaa8d21e 100644
--- a/gajim/conversation_textview.py
+++ b/gajim/conversation_textview.py
@@ -39,6 +39,7 @@ from gajim.common import app
 from gajim.common import helpers
 from gajim.common import i18n
 from gajim.common.i18n import _
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.fuzzyclock import FuzzyClock
 from gajim.common.const import StyleAttr
 
@@ -666,7 +667,7 @@ class ConversationTextview(GObject.GObject):
         if not otext:
             return
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
         buffer_ = self.tv.get_buffer()
         if other_tags:
             insert_tags_func = buffer_.insert_with_tags_by_name
@@ -686,13 +687,10 @@ class ConversationTextview(GObject.GObject):
         specials_limit = 100
 
         # add oob text to the end
-        try:
-            gajim_data = additional_data['gajim']
-            oob_url = gajim_data['oob_url']
-        except KeyError:
-            pass
-        else:
-            oob_desc = additional_data['gajim'].get('oob_desc', 'URL:')
+
+        oob_url = additional_data.get_value('gajim', 'oob_url')
+        if oob_url is not None:
+            oob_desc = additional_data.get_value('gajim', 'oob_desc', 'URL:')
             if oob_url != otext:
                 otext += '\n{} {}'.format(oob_desc, oob_url)
 
@@ -739,7 +737,7 @@ class ConversationTextview(GObject.GObject):
         (emots, links, formatting)
         """
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
 
         # PluginSystem: adding GUI extension point for ConversationTextview
         self.plugin_modified = False
@@ -941,7 +939,7 @@ class ConversationTextview(GObject.GObject):
         Print 'chat' type messages
         """
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
         buffer_ = self.tv.get_buffer()
         buffer_.begin_user_action()
         insert_mark = None
@@ -1172,14 +1170,11 @@ class ConversationTextview(GObject.GObject):
 
     @staticmethod
     def _get_encryption_details(additional_data):
-        encrypted = additional_data.get('encrypted')
-        if encrypted is None:
-            return
-
-        name = encrypted.get('name')
+        name = additional_data.get_value('encrypted', 'name')
         if name is None:
             return
-        fingerprint = encrypted.get('fingerprint')
+
+        fingerprint = additional_data.get_value('encrypted', 'fingerprint')
         return name, fingerprint
 
     def print_time(self, text, kind, tim, simple, direction_mark, other_tags_for_time, iter_):
@@ -1260,7 +1255,7 @@ class ConversationTextview(GObject.GObject):
         if text_tags is None:
             text_tags = []
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
         buffer_ = self.tv.get_buffer()
         if not mark:
             iter_ = buffer_.get_end_iter()
diff --git a/gajim/groupchat_control.py b/gajim/groupchat_control.py
index 4ccd16e9027655ec480caf58248aa68d9b351775..b671d2c168b5991171382d9341434a9d133bd036 100644
--- a/gajim/groupchat_control.py
+++ b/gajim/groupchat_control.py
@@ -55,6 +55,7 @@ from gajim.common import events
 from gajim.common import app
 from gajim.common import helpers
 from gajim.common.helpers import launch_browser_mailer
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.modules import dataforms
 from gajim.common import ged
 from gajim.common import i18n
@@ -1353,7 +1354,7 @@ class GroupchatControl(ChatControlBase):
     def print_old_conversation(self, text, contact='', tim=None, xhtml=None,
     displaymarking=None, msg_stanza_id=None, encrypted=None, additional_data=None):
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
 
         if contact:
             if contact == self.nick: # it's us
@@ -1384,7 +1385,7 @@ class GroupchatControl(ChatControlBase):
         If contact is not set: it's a message from the server or help.
         """
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
         other_tags_for_name = []
         other_tags_for_text = []
         if contact:
diff --git a/gajim/session.py b/gajim/session.py
index f52798f3225c2f0b8bd464f4ae1640f8dba3569c..4880246a42ce922956cbea7435a1bc124f1dbc20 100644
--- a/gajim/session.py
+++ b/gajim/session.py
@@ -27,6 +27,7 @@ from gajim.common import events
 from gajim.common import app
 from gajim.common import contacts
 from gajim.common import ged
+from gajim.common.helpers import AdditionalDataDict
 from gajim.common.const import KindConstant
 from gajim.gtk.single_message import SingleMessageWindow
 
@@ -277,7 +278,7 @@ class ChatControlSession:
         fjid = jid
 
         if additional_data is None:
-            additional_data = {}
+            additional_data = AdditionalDataDict()
 
         # Try to catch the contact with correct resource
         if resource: