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: