diff --git a/src/chat_control.py b/src/chat_control.py index c41e78c228103c00e3462daba86937bb02b5aed4..1cfc653038b11770ea0d0edb9bb23370e1d83b4e 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -2924,12 +2924,12 @@ class ChatControl(ChatControlBase): kind = 'info' else: kind = 'print_queue' - dm = None - if len(data) > 10: - dm = data[10] + if data[11]: + kind = 'out' + dm = data[10] self.print_conversation(data[0], kind, tim=data[3], - encrypted=data[4], subject=data[1], xhtml=data[7], - displaymarking=dm) + encrypted=data[4], subject=data[1], xhtml=data[7], + displaymarking=dm) if len(data) > 6 and isinstance(data[6], int): message_ids.append(data[6]) diff --git a/src/common/config.py b/src/common/config.py index 9ef46b63728725471ccd76fec2bb72c853764bc4..9c1241c1a36659175405b95a081504089b953d1c 100644 --- a/src/common/config.py +++ b/src/common/config.py @@ -381,6 +381,7 @@ class Config: 'roster_version': [opt_str, ''], 'subscription_request_msg': [opt_str, '', _('Message that is sent to contacts you want to add')], 'last_archiving_time': [opt_str, '1970-01-01T00:00:00Z', _('Last time we syncronized with logs from server.')], + 'enable_message_carbons': [ opt_bool, False, _('If enabled and if server supports this feature, Gajim will receive messages sent and received by other resources.')], }, {}), 'statusmsg': ({ 'message': [ opt_str, '' ], diff --git a/src/common/connection.py b/src/common/connection.py index c394acc74c05107dc09bd5ee4024a8c9c9caa96f..f49b83c447ecfa1a505eb7008708edf2868055c1 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -1709,6 +1709,13 @@ class Connection(CommonConnection, ConnectionHandlers): self.archive_manual_supported = True if common.xmpp.NS_ARCHIVE_PREF in obj.features: self.archive_pref_supported = True + if common.xmpp.NS_CARBONS in obj.features and \ + gajim.config.get_per('accounts', self.name, + 'enable_message_carbons'): + # Server supports carbons, activate it + iq = common.xmpp.Iq('set') + iq.setTag('enable', namespace=common.xmpp.NS_CARBONS) + self.connection.send(iq) if common.xmpp.NS_BYTESTREAM in obj.features and \ gajim.config.get_per('accounts', self.name, 'use_ft_proxies'): our_fjid = helpers.parse_jid(our_jid + '/' + \ diff --git a/src/common/connection_handlers_events.py b/src/common/connection_handlers_events.py index 03b04e679ff7fa11f3539cec02d31a4906877ae1..ac14d1aff5db81f6cba53bcaf288c3df553e8556 100644 --- a/src/common/connection_handlers_events.py +++ b/src/common/connection_handlers_events.py @@ -977,6 +977,8 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.conn = self.base_event.conn self.stanza = self.base_event.stanza self.get_id() + self.forwarded = False + self.sent = False account = self.conn.name @@ -1013,6 +1015,39 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): return self.jid = gajim.get_jid_without_resource(self.fjid) + forward_tag = self.stanza.getTag('forwarded', namespace=xmpp.NS_FORWARD) + # Be sure it comes from one of our resource, else ignore forward element + if forward_tag and self.jid == gajim.get_jid_from_account(account): + received_tag = forward_tag.getTag('received', + namespace=xmpp.NS_CARBONS) + sent_tag = forward_tag.getTag('sent', namespace=xmpp.NS_CARBONS) + if received_tag: + msg = forward_tag.getTag('message') + self.stanza = xmpp.Message(node=msg) + try: + self.get_jid_resource() + except helpers.InvalidFormat: + self.conn.dispatch('ERROR', (_('Invalid Jabber ID'), + _('A message from a non-valid JID arrived, it has been ' + 'ignored.'))) + return + self.forwarded = True + elif sent_tag: + msg = forward_tag.getTag('message') + self.stanza = xmpp.Message(node=msg) + to = self.stanza.getTo() + self.stanza.setTo(self.stanza.getFrom()) + self.stanza.setFrom(to) + try: + self.get_jid_resource() + except helpers.InvalidFormat: + self.conn.dispatch('ERROR', (_('Invalid Jabber ID'), + _('A message from a non-valid JID arrived, it has been ' + 'ignored.'))) + return + self.forwarded = True + self.sent = True + self.enc_tag = self.stanza.getTag('x', namespace=xmpp.NS_ENCRYPTED) self.invite_tag = None @@ -1052,7 +1087,8 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.session.last_receive = time_time() # check if the message is a XEP-0020 feature negotiation request - if self.stanza.getTag('feature', namespace=xmpp.NS_FEATURE): + if not self.forwarded and self.stanza.getTag('feature', + namespace=xmpp.NS_FEATURE): if gajim.HAVE_PYCRYPTO: feature = self.stanza.getTag(name='feature', namespace=xmpp.NS_FEATURE) @@ -1069,7 +1105,8 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.conn.connection.send(reply) return - if self.stanza.getTag('init', namespace=xmpp.NS_ESESSION_INIT): + if not self.forwarded and self.stanza.getTag('init', + namespace=xmpp.NS_ESESSION_INIT): init = self.stanza.getTag(name='init', namespace=xmpp.NS_ESESSION_INIT) form = xmpp.DataForm(node=init.getTag('x')) @@ -1084,6 +1121,9 @@ class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): xep_200_encrypted = self.stanza.getTag('c', namespace=xmpp.NS_STANZA_CRYPTO) if xep_200_encrypted: + if self.forwarded: + # Ignore E2E forwarded encrypted messages + return False self.encrypted = 'xep200' return True @@ -1155,6 +1195,8 @@ class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.session = self.msg_obj.session self.timestamp = self.msg_obj.timestamp self.encrypted = self.msg_obj.encrypted + self.forwarded = self.msg_obj.forwarded + self.sent = self.msg_obj.sent self.popup = False self.receipt_request_tag = self.stanza.getTag('request', diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py index 7816cb76de3c1a78a83c193afa18b414eb37763e..ea9fec9700195f14975365e9d6aa4155223b3ca9 100644 --- a/src/common/xmpp/protocol.py +++ b/src/common/xmpp/protocol.py @@ -47,10 +47,11 @@ NS_BOB = 'urn:xmpp:bob' # XEP-0231 NS_BOOKMARKS = 'storage:bookmarks' # XEP-0048 NS_BROWSE = 'jabber:iq:browse' NS_BROWSING = 'http://jabber.org/protocol/browsing' # XEP-0195 -NS_BYTESTREAM = 'http://jabber.org/protocol/bytestreams' # JEP-0065 -NS_CAPS = 'http://jabber.org/protocol/caps' # JEP-0115 +NS_BYTESTREAM = 'http://jabber.org/protocol/bytestreams' # XEP-0065 +NS_CAPS = 'http://jabber.org/protocol/caps' # XEP-0115 NS_CAPTCHA = 'urn:xmpp:captcha' # XEP-0158 -NS_CHATSTATES = 'http://jabber.org/protocol/chatstates' # JEP-0085 +NS_CARBONS = 'urn:xmpp:carbons:1' # XEP-0280 +NS_CHATSTATES = 'http://jabber.org/protocol/chatstates' # XEP-0085 NS_CHATTING = 'http://jabber.org/protocol/chatting' # XEP-0194 NS_CLIENT = 'jabber:client' NS_CONDITIONS = 'urn:xmpp:muc:conditions:0' # XEP-0306 @@ -70,9 +71,10 @@ NS_DISCO_ITEMS = NS_DISCO + '#items' NS_ENCRYPTED = 'jabber:x:encrypted' # XEP-0027 NS_ESESSION = 'http://www.xmpp.org/extensions/xep-0116.html#ns' NS_ESESSION_INIT = 'http://www.xmpp.org/extensions/xep-0116.html#ns-init' # XEP-0116 -NS_EVENT = 'jabber:x:event' # XEP-0022 +NS_EVENT = 'jabber:x:event' # XEP-0022 NS_FEATURE = 'http://jabber.org/protocol/feature-neg' -NS_FILE = 'http://jabber.org/protocol/si/profile/file-transfer' # JEP-0096 +NS_FILE = 'http://jabber.org/protocol/si/profile/file-transfer' # XEP-0096 +NS_FORWARD = 'urn:xmpp:forward:0' # XEP-0297 NS_GAMING = 'http://jabber.org/protocol/gaming' # XEP-0196 NS_GATEWAY = 'jabber:iq:gateway' # XEP-0100 NS_GEOLOC = 'http://jabber.org/protocol/geoloc' # XEP-0080 @@ -102,7 +104,7 @@ NS_MUC_CONFIG = NS_MUC + '#roomconfig' NS_NICK = 'http://jabber.org/protocol/nick' # XEP-0172 NS_OFFLINE = 'http://www.jabber.org/jeps/jep-0030.html' # XEP-0013 NS_PHYSLOC = 'http://jabber.org/protocol/physloc' # XEP-0112 -NS_PING = 'urn:xmpp:ping' # SEP-0199 +NS_PING = 'urn:xmpp:ping' # XEP-0199 NS_PRESENCE = 'presence' # Jabberd2 NS_PRIVACY = 'jabber:iq:privacy' NS_PRIVATE = 'jabber:iq:private' @@ -110,7 +112,7 @@ NS_PROFILE = 'http://jabber.org/protocol/profile' # XEP-0154 NS_PUBSUB = 'http://jabber.org/protocol/pubsub' # XEP-0060 NS_PUBSUB_EVENT = 'http://jabber.org/protocol/pubsub#event' NS_PUBSUB_PUBLISH_OPTIONS = NS_PUBSUB + '#publish-options' # XEP-0060 -NS_PUBSUB_OWNER = 'http://jabber.org/protocol/pubsub#owner' # JEP-0060 +NS_PUBSUB_OWNER = 'http://jabber.org/protocol/pubsub#owner' # XEP-0060 NS_REGISTER = 'jabber:iq:register' NS_ROSTER = 'jabber:iq:roster' NS_ROSTERNOTES = 'storage:rosternotes' diff --git a/src/roster_window.py b/src/roster_window.py index 01727a657bd8c1a9bc6da36201bbe3160b3c0f8a..f6464c8354d0d76a336f29879bc5f3f573e42b39 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -2584,10 +2584,12 @@ class RosterWindow: typ = '' if obj.mtype == 'error': typ = 'error' + if obj.forwarded and obj.sent: + typ = 'out' obj.session.control.print_conversation(obj.msgtxt, typ, - tim=obj.timestamp, encrypted=obj.encrypted, subject=obj.subject, - xhtml=obj.xhtml, displaymarking=obj.displaymarking) + tim=obj.timestamp, encrypted=obj.encrypted, subject=obj.subject, + xhtml=obj.xhtml, displaymarking=obj.displaymarking) if obj.msg_id: gajim.logger.set_read_messages([obj.msg_id]) elif obj.popup: diff --git a/src/session.py b/src/session.py index b5bad4b8e3ba822703852728966a5d7f76e5e37c..b8bb4ac307176e006accf2145ddd0e2f4de1cffb 100644 --- a/src/session.py +++ b/src/session.py @@ -79,9 +79,13 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): if not obj.stanza.getTag('body') and obj.chatstate is None: return - log_type = 'chat_msg_recv' + log_type = 'chat_msg' else: - log_type = 'single_msg_recv' + log_type = 'single_msg' + end = '_recv' + if obj.forwarded and obj.sent: + end = '_sent' + log_type += end if self.is_loggable() and obj.msgtxt: try: @@ -113,7 +117,7 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): # Handle chat states contact = gajim.contacts.get_contact(self.conn.name, obj.jid, obj.resource) - if contact: + if contact and (not obj.forwarded or not obj.sent): if self.control and self.control.type_id == \ message_control.TYPE_CHAT: if obj.chatstate is not None: @@ -237,7 +241,8 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): if not self.control: event = gajim.events.create_event(type_, (obj.msgtxt, obj.subject, obj.mtype, obj.timestamp, obj.encrypted, obj.resource, - obj.msg_id, obj.xhtml, self, obj.form_node, obj.displaymarking), + obj.msg_id, obj.xhtml, self, obj.form_node, obj.displaymarking, + obj.forwarded and obj.sent), show_in_roster=obj.show_in_roster, show_in_systray=obj.show_in_systray)