Move message handler into own module
This commit is contained in:
parent
d4fd621d11
commit
37f7a80396
|
@ -241,6 +241,9 @@ class ChatControl(ChatControlBase):
|
||||||
app.ged.register_event_handler(
|
app.ged.register_event_handler(
|
||||||
'decrypted-message-received',
|
'decrypted-message-received',
|
||||||
ged.GUI1, self._nec_decrypted_message_received)
|
ged.GUI1, self._nec_decrypted_message_received)
|
||||||
|
app.ged.register_event_handler(
|
||||||
|
'receipt-received',
|
||||||
|
ged.GUI1, self._receipt_received)
|
||||||
|
|
||||||
# PluginSystem: adding GUI extension point for this ChatControl
|
# PluginSystem: adding GUI extension point for this ChatControl
|
||||||
# instance object
|
# instance object
|
||||||
|
@ -984,6 +987,14 @@ class ChatControl(ChatControlBase):
|
||||||
else:
|
else:
|
||||||
self.old_msg_kind = kind
|
self.old_msg_kind = kind
|
||||||
|
|
||||||
|
def _receipt_received(self, event):
|
||||||
|
if event.conn.name != self.account:
|
||||||
|
return
|
||||||
|
if event.jid != self.contact.jid:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.conv_textview.show_xep0184_ack(event.receipt_id)
|
||||||
|
|
||||||
def get_tab_label(self):
|
def get_tab_label(self):
|
||||||
unread = ''
|
unread = ''
|
||||||
if self.resource:
|
if self.resource:
|
||||||
|
@ -1153,6 +1164,9 @@ class ChatControl(ChatControlBase):
|
||||||
app.ged.remove_event_handler(
|
app.ged.remove_event_handler(
|
||||||
'decrypted-message-received',
|
'decrypted-message-received',
|
||||||
ged.GUI1, self._nec_decrypted_message_received)
|
ged.GUI1, self._nec_decrypted_message_received)
|
||||||
|
app.ged.remove_event_handler(
|
||||||
|
'receipt-received',
|
||||||
|
ged.GUI1, self._receipt_received)
|
||||||
|
|
||||||
self.unsubscribe_events()
|
self.unsubscribe_events()
|
||||||
|
|
||||||
|
|
|
@ -292,22 +292,10 @@ class ConnectionHandlersBase:
|
||||||
# We decrypt GPG messages one after the other. Keep queue in mem
|
# We decrypt GPG messages one after the other. Keep queue in mem
|
||||||
self.gpg_messages_to_decrypt = []
|
self.gpg_messages_to_decrypt = []
|
||||||
|
|
||||||
# XEPs that are based on Message
|
|
||||||
self._message_namespaces = set([nbxmpp.NS_HTTP_AUTH,
|
|
||||||
nbxmpp.NS_PUBSUB_EVENT,
|
|
||||||
nbxmpp.NS_ROSTERX,
|
|
||||||
nbxmpp.NS_MAM_1,
|
|
||||||
nbxmpp.NS_MAM_2,
|
|
||||||
nbxmpp.NS_CONFERENCE])
|
|
||||||
|
|
||||||
app.ged.register_event_handler('iq-error-received', ged.CORE,
|
app.ged.register_event_handler('iq-error-received', ged.CORE,
|
||||||
self._nec_iq_error_received)
|
self._nec_iq_error_received)
|
||||||
app.ged.register_event_handler('presence-received', ged.CORE,
|
app.ged.register_event_handler('presence-received', ged.CORE,
|
||||||
self._nec_presence_received)
|
self._nec_presence_received)
|
||||||
app.ged.register_event_handler('message-received', ged.CORE,
|
|
||||||
self._nec_message_received)
|
|
||||||
app.ged.register_event_handler('decrypted-message-received', ged.CORE,
|
|
||||||
self._nec_decrypted_message_received)
|
|
||||||
app.ged.register_event_handler('gc-message-received', ged.CORE,
|
app.ged.register_event_handler('gc-message-received', ged.CORE,
|
||||||
self._nec_gc_message_received)
|
self._nec_gc_message_received)
|
||||||
|
|
||||||
|
@ -316,10 +304,6 @@ class ConnectionHandlersBase:
|
||||||
self._nec_iq_error_received)
|
self._nec_iq_error_received)
|
||||||
app.ged.remove_event_handler('presence-received', ged.CORE,
|
app.ged.remove_event_handler('presence-received', ged.CORE,
|
||||||
self._nec_presence_received)
|
self._nec_presence_received)
|
||||||
app.ged.remove_event_handler('message-received', ged.CORE,
|
|
||||||
self._nec_message_received)
|
|
||||||
app.ged.remove_event_handler('decrypted-message-received', ged.CORE,
|
|
||||||
self._nec_decrypted_message_received)
|
|
||||||
app.ged.remove_event_handler('gc-message-received', ged.CORE,
|
app.ged.remove_event_handler('gc-message-received', ged.CORE,
|
||||||
self._nec_gc_message_received)
|
self._nec_gc_message_received)
|
||||||
|
|
||||||
|
@ -448,96 +432,10 @@ class ConnectionHandlersBase:
|
||||||
message=obj.status,
|
message=obj.status,
|
||||||
show=show)
|
show=show)
|
||||||
|
|
||||||
def _nec_message_received(self, obj):
|
|
||||||
if obj.conn.name != self.name:
|
|
||||||
return
|
|
||||||
|
|
||||||
app.plugin_manager.extension_point(
|
|
||||||
'decrypt', self, obj, self._on_message_received)
|
|
||||||
if not obj.encrypted:
|
|
||||||
eme = parse_eme(obj.stanza)
|
|
||||||
if eme is not None:
|
|
||||||
obj.msgtxt = eme
|
|
||||||
self._on_message_received(obj)
|
|
||||||
|
|
||||||
def _on_message_received(self, obj):
|
|
||||||
app.nec.push_incoming_event(
|
|
||||||
DecryptedMessageReceivedEvent(
|
|
||||||
None, conn=self, msg_obj=obj, stanza_id=obj.unique_id))
|
|
||||||
|
|
||||||
def _nec_decrypted_message_received(self, obj):
|
|
||||||
if obj.conn.name != self.name:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Receipt requested
|
|
||||||
# TODO: We shouldn't answer if we're invisible!
|
|
||||||
contact = app.contacts.get_contact(self.name, obj.jid)
|
|
||||||
nick = obj.resource
|
|
||||||
gc_contact = app.contacts.get_gc_contact(self.name, obj.jid, nick)
|
|
||||||
if obj.sent:
|
|
||||||
jid_to = obj.stanza.getFrom()
|
|
||||||
else:
|
|
||||||
jid_to = obj.stanza.getTo()
|
|
||||||
reply = False
|
|
||||||
if not jid_to:
|
|
||||||
reply = True
|
|
||||||
else:
|
|
||||||
fjid_to = str(jid_to)
|
|
||||||
if self.name != 'Local':
|
|
||||||
# Dont check precis for zeroconf
|
|
||||||
fjid_to = helpers.parse_jid(str(jid_to))
|
|
||||||
jid_to = app.get_jid_without_resource(fjid_to)
|
|
||||||
if jid_to == app.get_jid_from_account(self.name):
|
|
||||||
reply = True
|
|
||||||
|
|
||||||
if obj.jid != app.get_jid_from_account(self.name):
|
|
||||||
if obj.receipt_request_tag and app.config.get_per('accounts',
|
|
||||||
self.name, 'answer_receipts') and ((contact and contact.sub \
|
|
||||||
not in ('to', 'none')) or gc_contact) and obj.mtype != 'error' and \
|
|
||||||
reply:
|
|
||||||
receipt = nbxmpp.Message(to=obj.fjid, typ='chat')
|
|
||||||
receipt.setTag('received', namespace='urn:xmpp:receipts',
|
|
||||||
attrs={'id': obj.id_})
|
|
||||||
|
|
||||||
if obj.thread_id:
|
|
||||||
receipt.setThread(obj.thread_id)
|
|
||||||
self.connection.send(receipt)
|
|
||||||
|
|
||||||
# We got our message's receipt
|
|
||||||
if obj.receipt_received_tag and app.config.get_per('accounts',
|
|
||||||
self.name, 'request_receipt'):
|
|
||||||
ctrl = None
|
|
||||||
if obj.session is not None:
|
|
||||||
ctrl = obj.session.control
|
|
||||||
if not ctrl:
|
|
||||||
# Received <message> doesn't have the <thread> element
|
|
||||||
# or control is not bound to session?
|
|
||||||
# --> search for it
|
|
||||||
ctrl = app.interface.msg_win_mgr.search_control(obj.jid,
|
|
||||||
obj.conn.name, obj.resource)
|
|
||||||
|
|
||||||
if ctrl:
|
|
||||||
id_ = obj.receipt_received_tag.getAttr('id')
|
|
||||||
if not id_:
|
|
||||||
# old XEP implementation
|
|
||||||
id_ = obj.id_
|
|
||||||
ctrl.conv_textview.show_xep0184_ack(id_)
|
|
||||||
|
|
||||||
if obj.mtype == 'error':
|
|
||||||
if not obj.msgtxt:
|
|
||||||
obj.msgtxt = _('message')
|
|
||||||
self.dispatch_error_message(obj.stanza, obj.msgtxt,
|
|
||||||
obj.session, obj.fjid, obj.timestamp)
|
|
||||||
return True
|
|
||||||
elif obj.mtype == 'groupchat':
|
|
||||||
app.nec.push_incoming_event(GcMessageReceivedEvent(None,
|
|
||||||
conn=self, msg_obj=obj, stanza_id=obj.unique_id))
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _check_for_mam_compliance(self, room_jid, stanza_id):
|
def _check_for_mam_compliance(self, room_jid, stanza_id):
|
||||||
namespace = muc_caps_cache.get_mam_namespace(room_jid)
|
namespace = muc_caps_cache.get_mam_namespace(room_jid)
|
||||||
if stanza_id is None and namespace == nbxmpp.NS_MAM_2:
|
if stanza_id is None and namespace == nbxmpp.NS_MAM_2:
|
||||||
log.warning('%s announces mam:2 without stanza-id')
|
log.warning('%s announces mam:2 without stanza-id', room_jid)
|
||||||
|
|
||||||
def _nec_gc_message_received(self, obj):
|
def _nec_gc_message_received(self, obj):
|
||||||
if obj.conn.name != self.name:
|
if obj.conn.name != self.name:
|
||||||
|
@ -743,7 +641,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco,
|
||||||
self.continue_connect_info = None
|
self.continue_connect_info = None
|
||||||
|
|
||||||
app.nec.register_incoming_event(StreamConflictReceivedEvent)
|
app.nec.register_incoming_event(StreamConflictReceivedEvent)
|
||||||
app.nec.register_incoming_event(MessageReceivedEvent)
|
|
||||||
app.nec.register_incoming_event(NotificationEvent)
|
app.nec.register_incoming_event(NotificationEvent)
|
||||||
|
|
||||||
app.ged.register_event_handler('roster-set-received',
|
app.ged.register_event_handler('roster-set-received',
|
||||||
|
@ -939,29 +836,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco,
|
||||||
app.config.set_per('accounts', self.name, 'roster_version',
|
app.config.set_per('accounts', self.name, 'roster_version',
|
||||||
obj.version)
|
obj.version)
|
||||||
|
|
||||||
def _messageCB(self, con, stanza):
|
|
||||||
"""
|
|
||||||
Called when we receive a message
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Check if a child of the message contains any
|
|
||||||
# of these namespaces, so we dont execute the
|
|
||||||
# message handler for them.
|
|
||||||
# They have defined their own message handlers
|
|
||||||
# but nbxmpp executes less common handlers last
|
|
||||||
if self._message_namespaces & set(stanza.getProperties()):
|
|
||||||
return
|
|
||||||
|
|
||||||
muc_user = stanza.getTag('x', namespace=nbxmpp.NS_MUC_USER)
|
|
||||||
if muc_user is not None:
|
|
||||||
if muc_user.getChildren():
|
|
||||||
# Not a PM, handled by MUC module
|
|
||||||
return
|
|
||||||
log.debug('MessageCB')
|
|
||||||
|
|
||||||
app.nec.push_incoming_event(NetworkEvent('raw-message-received',
|
|
||||||
conn=self, stanza=stanza, account=self.name))
|
|
||||||
|
|
||||||
def _dispatch_gc_msg_with_captcha(self, stanza, msg_obj):
|
def _dispatch_gc_msg_with_captcha(self, stanza, msg_obj):
|
||||||
msg_obj.stanza = stanza
|
msg_obj.stanza = stanza
|
||||||
app.nec.push_incoming_event(GcMessageReceivedEvent(None,
|
app.nec.push_incoming_event(GcMessageReceivedEvent(None,
|
||||||
|
@ -1282,7 +1156,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco,
|
||||||
def _register_handlers(self, con, con_type):
|
def _register_handlers(self, con, con_type):
|
||||||
# try to find another way to register handlers in each class
|
# try to find another way to register handlers in each class
|
||||||
# that defines handlers
|
# that defines handlers
|
||||||
con.RegisterHandler('message', self._messageCB)
|
|
||||||
con.RegisterHandler('presence', self._presenceCB)
|
con.RegisterHandler('presence', self._presenceCB)
|
||||||
con.RegisterHandler('iq', self._rosterSetCB, 'set', nbxmpp.NS_ROSTER)
|
con.RegisterHandler('iq', self._rosterSetCB, 'set', nbxmpp.NS_ROSTER)
|
||||||
con.RegisterHandler('iq', self._siSetCB, 'set', nbxmpp.NS_SI)
|
con.RegisterHandler('iq', self._siSetCB, 'set', nbxmpp.NS_SI)
|
||||||
|
|
|
@ -632,242 +632,6 @@ class BeforeChangeShowEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'before-change-show'
|
name = 'before-change-show'
|
||||||
base_network_events = []
|
base_network_events = []
|
||||||
|
|
||||||
class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
|
||||||
name = 'message-received'
|
|
||||||
base_network_events = ['raw-message-received']
|
|
||||||
|
|
||||||
def init(self):
|
|
||||||
self.additional_data = {}
|
|
||||||
|
|
||||||
def generate(self):
|
|
||||||
self.conn = self.base_event.conn
|
|
||||||
self.stanza = self.base_event.stanza
|
|
||||||
self.encrypted = False
|
|
||||||
self.self_message = None
|
|
||||||
self.muc_pm = None
|
|
||||||
account = self.conn.name
|
|
||||||
|
|
||||||
if self.stanza.getFrom() == self.conn.get_own_jid(warn=True):
|
|
||||||
# Drop messages sent from our own full jid
|
|
||||||
# It can happen that when we sent message to our own bare jid
|
|
||||||
# that the server routes that message back to us
|
|
||||||
log.info('Received message from self: %s, message is dropped',
|
|
||||||
self.stanza.getFrom())
|
|
||||||
return
|
|
||||||
|
|
||||||
from gajim.common.modules.carbons import parse_carbon
|
|
||||||
self.stanza, self.sent, self.forwarded = parse_carbon(self.conn,
|
|
||||||
self.stanza)
|
|
||||||
|
|
||||||
self.get_id()
|
|
||||||
try:
|
|
||||||
self.get_jid_resource()
|
|
||||||
except helpers.InvalidFormat:
|
|
||||||
log.warning('Invalid JID: %s, ignoring it',
|
|
||||||
self.stanza.getFrom())
|
|
||||||
return
|
|
||||||
|
|
||||||
# Check for duplicates
|
|
||||||
self.unique_id = self.get_unique_id()
|
|
||||||
# Check groupchat messages for duplicates,
|
|
||||||
# We do this because of MUC History messages
|
|
||||||
type_ = self.stanza.getType()
|
|
||||||
if type_ == 'groupchat' or self.self_message or self.muc_pm:
|
|
||||||
if type_ == 'groupchat':
|
|
||||||
archive_jid = self.stanza.getFrom().getStripped()
|
|
||||||
else:
|
|
||||||
archive_jid = self.conn.get_own_jid().getStripped()
|
|
||||||
if app.logger.find_stanza_id(account,
|
|
||||||
archive_jid,
|
|
||||||
self.unique_id,
|
|
||||||
groupchat=type_ == 'groupchat'):
|
|
||||||
return
|
|
||||||
|
|
||||||
self.thread_id = self.stanza.getThread()
|
|
||||||
self.mtype = self.stanza.getType()
|
|
||||||
if not self.mtype or self.mtype not in ('chat', 'groupchat', 'error'):
|
|
||||||
self.mtype = 'normal'
|
|
||||||
|
|
||||||
self.msgtxt = self.stanza.getBody()
|
|
||||||
|
|
||||||
self.get_gc_control()
|
|
||||||
|
|
||||||
if self.gc_control and self.jid == self.fjid:
|
|
||||||
if self.mtype == 'error':
|
|
||||||
self.msgtxt = _('error while sending %(message)s ( %(error)s )'\
|
|
||||||
) % {'message': self.msgtxt,
|
|
||||||
'error': self.stanza.getErrorMsg()}
|
|
||||||
if self.stanza.getTag('html'):
|
|
||||||
self.stanza.delChild('html')
|
|
||||||
# message from a gc without a resource
|
|
||||||
self.mtype = 'groupchat'
|
|
||||||
|
|
||||||
self.session = None
|
|
||||||
if self.mtype != 'groupchat':
|
|
||||||
if app.interface.is_pm_contact(self.fjid, account) and \
|
|
||||||
self.mtype == 'error':
|
|
||||||
self.session = self.conn.find_session(self.fjid, self.thread_id)
|
|
||||||
if not self.session:
|
|
||||||
self.session = self.conn.get_latest_session(self.fjid)
|
|
||||||
if not self.session:
|
|
||||||
self.session = self.conn.make_new_session(self.fjid,
|
|
||||||
self.thread_id,
|
|
||||||
type_='pm')
|
|
||||||
else:
|
|
||||||
self.session = self.conn.get_or_create_session(self.fjid,
|
|
||||||
self.thread_id)
|
|
||||||
|
|
||||||
if self.thread_id and not self.session.received_thread_id:
|
|
||||||
self.session.received_thread_id = True
|
|
||||||
|
|
||||||
self.session.last_receive = time_time()
|
|
||||||
|
|
||||||
self._generate_timestamp(self.stanza.timestamp)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
def get_unique_id(self):
|
|
||||||
'''
|
|
||||||
Messages to self:
|
|
||||||
|
|
||||||
Messages to self are stored multiple times in MAM so we cant use
|
|
||||||
stanza-id to deduplicate. We use origin-id instead. Its not perfect
|
|
||||||
but there is no better way for now.
|
|
||||||
We drop "received"-Carbons of Message to self, so we dont have to
|
|
||||||
parse origin-id in that case.
|
|
||||||
|
|
||||||
MUC PMs:
|
|
||||||
|
|
||||||
MUC PMs are also stored multiple times, we also depend on origin-id
|
|
||||||
for now.
|
|
||||||
'''
|
|
||||||
|
|
||||||
if self.stanza.getType() == 'groupchat':
|
|
||||||
# TODO: Disco the MUC check if 'urn:xmpp:mam:2' is announced
|
|
||||||
return self.get_stanza_id(self.stanza)
|
|
||||||
|
|
||||||
elif self.stanza.getType() != 'chat':
|
|
||||||
return
|
|
||||||
|
|
||||||
# Messages we receive live
|
|
||||||
if self.conn.get_module('MAM').archiving_namespace != nbxmpp.NS_MAM_2:
|
|
||||||
# Only mam:2 ensures valid stanza-id
|
|
||||||
return
|
|
||||||
|
|
||||||
# Sent Carbon
|
|
||||||
sent_carbon = self.stanza.getTag('sent',
|
|
||||||
namespace=nbxmpp.NS_CARBONS,
|
|
||||||
protocol=True)
|
|
||||||
if sent_carbon is not None:
|
|
||||||
message = self.get_forwarded_message(sent_carbon)
|
|
||||||
if self._is_self_message(message) or self._is_muc_pm(message):
|
|
||||||
return message.getOriginID()
|
|
||||||
return self.get_stanza_id(message)
|
|
||||||
|
|
||||||
# Received Carbon
|
|
||||||
received_carbon = self.stanza.getTag('received',
|
|
||||||
namespace=nbxmpp.NS_CARBONS,
|
|
||||||
protocol=True)
|
|
||||||
if received_carbon is not None:
|
|
||||||
message = self.get_forwarded_message(received_carbon)
|
|
||||||
if self._is_muc_pm(message):
|
|
||||||
return message.getOriginID()
|
|
||||||
return self.get_stanza_id(message)
|
|
||||||
|
|
||||||
# Normal Message
|
|
||||||
if self._is_self_message(self.stanza) or self._is_muc_pm(self.stanza):
|
|
||||||
return self.stanza.getOriginID()
|
|
||||||
return self.get_stanza_id(self.stanza)
|
|
||||||
|
|
||||||
class ZeroconfMessageReceivedEvent(MessageReceivedEvent):
|
|
||||||
name = 'message-received'
|
|
||||||
base_network_events = []
|
|
||||||
|
|
||||||
def get_jid_resource(self):
|
|
||||||
self.fjid = str(self.stanza.getFrom())
|
|
||||||
|
|
||||||
if self.fjid is None:
|
|
||||||
for key in self.conn.connection.zeroconf.contacts:
|
|
||||||
if self.ip == self.conn.connection.zeroconf.contacts[key][
|
|
||||||
Constant.ADDRESS]:
|
|
||||||
self.fjid = key
|
|
||||||
break
|
|
||||||
|
|
||||||
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
|
|
||||||
|
|
||||||
def generate(self):
|
|
||||||
self.base_event = nec.NetworkIncomingEvent(None, conn=self.conn,
|
|
||||||
stanza=self.stanza)
|
|
||||||
return super(ZeroconfMessageReceivedEvent, self).generate()
|
|
||||||
|
|
||||||
|
|
||||||
class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
|
||||||
name = 'decrypted-message-received'
|
|
||||||
base_network_events = []
|
|
||||||
|
|
||||||
def generate(self):
|
|
||||||
self.stanza = self.msg_obj.stanza
|
|
||||||
self.additional_data = self.msg_obj.additional_data
|
|
||||||
self.id_ = self.msg_obj.id_
|
|
||||||
self.unique_id = self.msg_obj.unique_id
|
|
||||||
self.jid = self.msg_obj.jid
|
|
||||||
self.fjid = self.msg_obj.fjid
|
|
||||||
self.resource = self.msg_obj.resource
|
|
||||||
self.mtype = self.msg_obj.mtype
|
|
||||||
self.thread_id = self.msg_obj.thread_id
|
|
||||||
self.msgtxt = self.msg_obj.msgtxt
|
|
||||||
self.gc_control = self.msg_obj.gc_control
|
|
||||||
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.conn = self.msg_obj.conn
|
|
||||||
self.muc_pm = self.msg_obj.muc_pm
|
|
||||||
self.popup = False
|
|
||||||
self.msg_log_id = None # id in log database
|
|
||||||
self.attention = False # XEP-0224
|
|
||||||
self.correct_id = None # XEP-0308
|
|
||||||
self.msghash = None
|
|
||||||
|
|
||||||
self.receipt_request_tag = self.stanza.getTag('request',
|
|
||||||
namespace=nbxmpp.NS_RECEIPTS)
|
|
||||||
self.receipt_received_tag = self.stanza.getTag('received',
|
|
||||||
namespace=nbxmpp.NS_RECEIPTS)
|
|
||||||
|
|
||||||
self.subject = self.stanza.getSubject()
|
|
||||||
|
|
||||||
self.displaymarking = None
|
|
||||||
self.seclabel = self.stanza.getTag('securitylabel',
|
|
||||||
namespace=nbxmpp.NS_SECLABEL)
|
|
||||||
if self.seclabel:
|
|
||||||
self.displaymarking = self.seclabel.getTag('displaymarking')
|
|
||||||
|
|
||||||
if self.stanza.getTag('attention', namespace=nbxmpp.NS_ATTENTION):
|
|
||||||
delayed = self.stanza.getTag('x', namespace=nbxmpp.NS_DELAY) is not\
|
|
||||||
None
|
|
||||||
if not delayed:
|
|
||||||
self.attention = True
|
|
||||||
|
|
||||||
self.form_node = self.stanza.getTag('x', namespace=nbxmpp.NS_DATA)
|
|
||||||
|
|
||||||
if app.config.get('ignore_incoming_xhtml'):
|
|
||||||
self.xhtml = None
|
|
||||||
else:
|
|
||||||
self.xhtml = self.stanza.getXHTML()
|
|
||||||
|
|
||||||
# XEP-0172 User Nickname
|
|
||||||
self.user_nick = self.stanza.getTagData('nick') or ''
|
|
||||||
|
|
||||||
self.get_chatstate()
|
|
||||||
|
|
||||||
self.get_oob_data(self.stanza)
|
|
||||||
|
|
||||||
from gajim.common.modules.misc import parse_correction
|
|
||||||
self.correct_id = parse_correction(self.stanza)
|
|
||||||
|
|
||||||
return True
|
|
||||||
|
|
||||||
class ChatstateReceivedEvent(nec.NetworkIncomingEvent):
|
class ChatstateReceivedEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'chatstate-received'
|
name = 'chatstate-received'
|
||||||
base_network_events = []
|
base_network_events = []
|
||||||
|
|
|
@ -18,7 +18,7 @@ from pathlib import Path
|
||||||
|
|
||||||
log = logging.getLogger('gajim.c.m')
|
log = logging.getLogger('gajim.c.m')
|
||||||
|
|
||||||
ZEROCONF_MODULES = ['adhoc_commands']
|
ZEROCONF_MODULES = ['adhoc_commands', 'receipts']
|
||||||
|
|
||||||
imported_modules = []
|
imported_modules = []
|
||||||
_modules = {}
|
_modules = {}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
# This file is part of Gajim.
|
||||||
|
#
|
||||||
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation; version 3 only.
|
||||||
|
#
|
||||||
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# XEP-0085: Chat State Notifications
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
|
||||||
|
from gajim.common.modules.misc import parse_delay
|
||||||
|
|
||||||
|
log = logging.getLogger('gajim.c.m.chatstates')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_chatstate(stanza):
|
||||||
|
if parse_delay(stanza) is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
children = stanza.getChildren()
|
||||||
|
for child in children:
|
||||||
|
if child.getNamespace() == nbxmpp.NS_CHATSTATES:
|
||||||
|
return child.getName()
|
|
@ -27,6 +27,8 @@ from gajim.common.modules.misc import parse_delay
|
||||||
from gajim.common.modules.misc import parse_oob
|
from gajim.common.modules.misc import parse_oob
|
||||||
from gajim.common.modules.misc import parse_correction
|
from gajim.common.modules.misc import parse_correction
|
||||||
from gajim.common.modules.misc import parse_eme
|
from gajim.common.modules.misc import parse_eme
|
||||||
|
from gajim.common.modules.util import is_self_message
|
||||||
|
from gajim.common.modules.util import is_muc_pm
|
||||||
|
|
||||||
log = logging.getLogger('gajim.c.m.archiving')
|
log = logging.getLogger('gajim.c.m.archiving')
|
||||||
|
|
||||||
|
@ -61,28 +63,6 @@ class MAM:
|
||||||
if archive_jid.bareMatch(expected_archive):
|
if archive_jid.bareMatch(expected_archive):
|
||||||
return archive_jid
|
return archive_jid
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _is_self_message(message, groupchat):
|
|
||||||
if groupchat:
|
|
||||||
return False
|
|
||||||
frm = message.getFrom()
|
|
||||||
to = message.getTo()
|
|
||||||
return frm.bareMatch(to)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _is_muc_pm(message, groupchat, with_):
|
|
||||||
if groupchat:
|
|
||||||
return False
|
|
||||||
muc_user = message.getTag('x', namespace=nbxmpp.NS_MUC_USER)
|
|
||||||
if muc_user is not None:
|
|
||||||
return muc_user.getChildren() == []
|
|
||||||
else:
|
|
||||||
# muc#user namespace was added in MUC 1.28 so we need a fallback
|
|
||||||
# Check if we know the jid, otherwise disco it
|
|
||||||
if app.logger.jid_is_room_jid(with_.getStripped()):
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _get_unique_id(self, result, message, groupchat, self_message, muc_pm):
|
def _get_unique_id(self, result, message, groupchat, self_message, muc_pm):
|
||||||
stanza_id = result.getAttr('id')
|
stanza_id = result.getAttr('id')
|
||||||
if groupchat:
|
if groupchat:
|
||||||
|
@ -150,8 +130,8 @@ class MAM:
|
||||||
else:
|
else:
|
||||||
event_attrs.update(self._parse_chat_attrs(message))
|
event_attrs.update(self._parse_chat_attrs(message))
|
||||||
|
|
||||||
self_message = self._is_self_message(message, groupchat)
|
self_message = is_self_message(message, groupchat)
|
||||||
muc_pm = self._is_muc_pm(message, groupchat, event_attrs['with_'])
|
muc_pm = is_muc_pm(message, event_attrs['with_'], groupchat)
|
||||||
|
|
||||||
stanza_id, origin_id = self._get_unique_id(
|
stanza_id, origin_id = self._get_unique_id(
|
||||||
result, message, groupchat, self_message, muc_pm)
|
result, message, groupchat, self_message, muc_pm)
|
||||||
|
|
|
@ -0,0 +1,297 @@
|
||||||
|
# This file is part of Gajim.
|
||||||
|
#
|
||||||
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation; version 3 only.
|
||||||
|
#
|
||||||
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Message handler
|
||||||
|
|
||||||
|
import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
|
||||||
|
from gajim.common import app
|
||||||
|
from gajim.common import helpers
|
||||||
|
from gajim.common.nec import NetworkIncomingEvent, NetworkEvent
|
||||||
|
from gajim.common.modules.user_nickname import parse_nickname
|
||||||
|
from gajim.common.modules.chatstates import parse_chatstate
|
||||||
|
from gajim.common.modules.carbons import parse_carbon
|
||||||
|
from gajim.common.modules.misc import parse_delay
|
||||||
|
from gajim.common.modules.misc import parse_eme
|
||||||
|
from gajim.common.modules.misc import parse_correction
|
||||||
|
from gajim.common.modules.misc import parse_attention
|
||||||
|
from gajim.common.modules.misc import parse_securitylabel
|
||||||
|
from gajim.common.modules.misc import parse_form
|
||||||
|
from gajim.common.modules.misc import parse_oob
|
||||||
|
from gajim.common.modules.misc import parse_xhtml
|
||||||
|
from gajim.common.modules.util import is_self_message
|
||||||
|
from gajim.common.modules.util import is_muc_pm
|
||||||
|
from gajim.common.connection_handlers_events import GcMessageReceivedEvent
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger('gajim.c.m.message')
|
||||||
|
|
||||||
|
|
||||||
|
class Message:
|
||||||
|
def __init__(self, con):
|
||||||
|
self._con = con
|
||||||
|
self._account = con.name
|
||||||
|
|
||||||
|
self.handlers = [('message', self._message_received)]
|
||||||
|
|
||||||
|
# XEPs for which this message module should not be executed
|
||||||
|
self._message_namespaces = set([nbxmpp.NS_HTTP_AUTH,
|
||||||
|
nbxmpp.NS_PUBSUB_EVENT,
|
||||||
|
nbxmpp.NS_ROSTERX,
|
||||||
|
nbxmpp.NS_MAM_1,
|
||||||
|
nbxmpp.NS_MAM_2,
|
||||||
|
nbxmpp.NS_CONFERENCE,
|
||||||
|
nbxmpp.NS_IBB])
|
||||||
|
|
||||||
|
def _message_received(self, con, stanza):
|
||||||
|
# Check if a child of the message contains any
|
||||||
|
# namespaces that we handle in other modules.
|
||||||
|
# nbxmpp executes less common handlers last
|
||||||
|
if self._message_namespaces & set(stanza.getProperties()):
|
||||||
|
return
|
||||||
|
|
||||||
|
muc_user = stanza.getTag('x', namespace=nbxmpp.NS_MUC_USER)
|
||||||
|
if muc_user is not None:
|
||||||
|
if muc_user.getChildren():
|
||||||
|
# Not a PM, handled by MUC module
|
||||||
|
return
|
||||||
|
|
||||||
|
log.info('Received from %s', stanza.getFrom())
|
||||||
|
|
||||||
|
app.nec.push_incoming_event(NetworkEvent(
|
||||||
|
'raw-message-received',
|
||||||
|
conn=self._con,
|
||||||
|
stanza=stanza,
|
||||||
|
account=self._account))
|
||||||
|
|
||||||
|
if stanza.getFrom() == self._con.get_own_jid(warn=True):
|
||||||
|
# Drop messages sent from our own full jid
|
||||||
|
# It can happen that when we sent message to our own bare jid
|
||||||
|
# that the server routes that message back to us
|
||||||
|
log.info('Received message from self: %s, message is dropped',
|
||||||
|
stanza.getFrom())
|
||||||
|
return
|
||||||
|
|
||||||
|
stanza, sent, forwarded = parse_carbon(self._con, stanza)
|
||||||
|
|
||||||
|
from_ = stanza.getFrom()
|
||||||
|
type_ = stanza.getType()
|
||||||
|
if type_ is None:
|
||||||
|
type_ = 'normal'
|
||||||
|
self_message = is_self_message(stanza, type_ == 'groupchat')
|
||||||
|
muc_pm = is_muc_pm(stanza, from_, type_ == 'groupchat')
|
||||||
|
|
||||||
|
id_ = stanza.getID()
|
||||||
|
|
||||||
|
fjid = None
|
||||||
|
if from_ is not None:
|
||||||
|
try:
|
||||||
|
fjid = helpers.parse_jid(str(from_))
|
||||||
|
except helpers.InvalidFormat:
|
||||||
|
log.warning('Invalid JID: %s, ignoring it',
|
||||||
|
stanza.getFrom())
|
||||||
|
return
|
||||||
|
|
||||||
|
jid, resource = app.get_room_and_nick_from_fjid(fjid)
|
||||||
|
|
||||||
|
# Check for duplicates
|
||||||
|
unique_id = self._get_unique_id(stanza, forwarded, sent,
|
||||||
|
self_message, muc_pm)
|
||||||
|
|
||||||
|
# Check groupchat messages for duplicates,
|
||||||
|
# We do this because of MUC History messages
|
||||||
|
if type_ == 'groupchat' or self_message or muc_pm:
|
||||||
|
if type_ == 'groupchat':
|
||||||
|
archive_jid = stanza.getFrom().getStripped()
|
||||||
|
else:
|
||||||
|
archive_jid = self._con.get_own_jid().getStripped()
|
||||||
|
if app.logger.find_stanza_id(self._account,
|
||||||
|
archive_jid,
|
||||||
|
unique_id,
|
||||||
|
groupchat=type_ == 'groupchat'):
|
||||||
|
return
|
||||||
|
|
||||||
|
thread_id = stanza.getThread()
|
||||||
|
msgtxt = stanza.getBody()
|
||||||
|
|
||||||
|
# TODO: remove all control UI stuff
|
||||||
|
gc_control = app.interface.msg_win_mgr.get_gc_control(
|
||||||
|
jid, self._account)
|
||||||
|
if not gc_control:
|
||||||
|
minimized = app.interface.minimized_controls[self._account]
|
||||||
|
gc_control = minimized.get(jid)
|
||||||
|
|
||||||
|
if gc_control and jid == fjid:
|
||||||
|
if type_ == 'error':
|
||||||
|
msgtxt = _('error while sending %(message)s ( %(error)s )') % {
|
||||||
|
'message': msgtxt, 'error': stanza.getErrorMsg()}
|
||||||
|
# TODO: why is this here?
|
||||||
|
if stanza.getTag('html'):
|
||||||
|
stanza.delChild('html')
|
||||||
|
|
||||||
|
session = None
|
||||||
|
if type_ != 'groupchat':
|
||||||
|
if muc_pm and type_ == 'error':
|
||||||
|
session = self._con.find_session(fjid, thread_id)
|
||||||
|
if not session:
|
||||||
|
session = self._con.get_latest_session(fjid)
|
||||||
|
if not session:
|
||||||
|
session = self._con.make_new_session(
|
||||||
|
fjid, thread_id, type_='pm')
|
||||||
|
else:
|
||||||
|
session = self._con.get_or_create_session(fjid, thread_id)
|
||||||
|
|
||||||
|
if thread_id and not session.received_thread_id:
|
||||||
|
session.received_thread_id = True
|
||||||
|
|
||||||
|
session.last_receive = time.time()
|
||||||
|
|
||||||
|
timestamp = parse_delay(stanza)
|
||||||
|
if timestamp is None:
|
||||||
|
timestamp = time.time()
|
||||||
|
|
||||||
|
event_attr = {
|
||||||
|
'conn': self._con,
|
||||||
|
'stanza': stanza,
|
||||||
|
'account': self._account,
|
||||||
|
'id_': id_,
|
||||||
|
'encrypted': False,
|
||||||
|
'additional_data': {},
|
||||||
|
'forwarded': forwarded,
|
||||||
|
'sent': sent,
|
||||||
|
'timestamp': timestamp,
|
||||||
|
'fjid': fjid,
|
||||||
|
'jid': jid,
|
||||||
|
'resource': resource,
|
||||||
|
'unique_id': unique_id,
|
||||||
|
'mtype': type_,
|
||||||
|
'msgtxt': msgtxt,
|
||||||
|
'thread_id': thread_id,
|
||||||
|
'session': session,
|
||||||
|
'self_message': self_message,
|
||||||
|
'muc_pm': muc_pm,
|
||||||
|
'gc_control': gc_control
|
||||||
|
}
|
||||||
|
|
||||||
|
event = MessageReceivedEvent(None, **event_attr)
|
||||||
|
app.nec.push_incoming_event(event)
|
||||||
|
|
||||||
|
app.plugin_manager.extension_point(
|
||||||
|
'decrypt', self._con, event, self._on_message_decrypted)
|
||||||
|
if not event.encrypted:
|
||||||
|
eme = parse_eme(event.stanza)
|
||||||
|
if eme is not None:
|
||||||
|
event.msgtxt = eme
|
||||||
|
self._on_message_decrypted(event)
|
||||||
|
|
||||||
|
def _on_message_decrypted(self, event):
|
||||||
|
try:
|
||||||
|
self._con.get_module('Receipts').delegate(event)
|
||||||
|
except nbxmpp.NodeProcessed:
|
||||||
|
return
|
||||||
|
|
||||||
|
event_attr = {
|
||||||
|
'popup': False,
|
||||||
|
'msg_log_id': None,
|
||||||
|
'subject': event.stanza.getSubject(),
|
||||||
|
'displaymarking': parse_securitylabel(event.stanza),
|
||||||
|
'attention': parse_attention(event.stanza),
|
||||||
|
'correct_id': parse_correction(event.stanza),
|
||||||
|
'user_nick': parse_nickname(event.stanza),
|
||||||
|
'form_node': parse_form(event.stanza),
|
||||||
|
'xhtml': parse_xhtml(event.stanza),
|
||||||
|
'chatstate': parse_chatstate(event.stanza),
|
||||||
|
'stanza_id': event.unique_id
|
||||||
|
}
|
||||||
|
parse_oob(event.stanza, event.additional_data)
|
||||||
|
|
||||||
|
for name, value in event_attr.items():
|
||||||
|
setattr(event, name, value)
|
||||||
|
|
||||||
|
if event.mtype == 'error':
|
||||||
|
if not event.msgtxt:
|
||||||
|
event.msgtxt = _('message')
|
||||||
|
self._con.dispatch_error_message(
|
||||||
|
event.stanza, event.msgtxt,
|
||||||
|
event.session, event.fjid, event.timestamp)
|
||||||
|
return
|
||||||
|
|
||||||
|
if event.mtype == 'groupchat':
|
||||||
|
app.nec.push_incoming_event(GcMessageReceivedEvent(
|
||||||
|
None,
|
||||||
|
conn=self._con,
|
||||||
|
msg_obj=event,
|
||||||
|
stanza_id=event.unique_id))
|
||||||
|
return
|
||||||
|
|
||||||
|
app.nec.push_incoming_event(
|
||||||
|
DecryptedMessageReceivedEvent(
|
||||||
|
None, **vars(event)))
|
||||||
|
|
||||||
|
def _get_unique_id(self, stanza, forwarded, sent, self_message, muc_pm):
|
||||||
|
if stanza.getType() == 'groupchat':
|
||||||
|
# TODO: Disco the MUC check if 'urn:xmpp:mam:2' is announced
|
||||||
|
return self._get_stanza_id(stanza)
|
||||||
|
|
||||||
|
if stanza.getType() != 'chat':
|
||||||
|
return
|
||||||
|
|
||||||
|
# Messages we receive live
|
||||||
|
if self._con.get_module('MAM').archiving_namespace != nbxmpp.NS_MAM_2:
|
||||||
|
# Only mam:2 ensures valid stanza-id
|
||||||
|
return
|
||||||
|
|
||||||
|
if forwarded:
|
||||||
|
if sent:
|
||||||
|
if self_message or muc_pm:
|
||||||
|
return stanza.getOriginID()
|
||||||
|
return self._get_stanza_id(stanza)
|
||||||
|
else:
|
||||||
|
if muc_pm:
|
||||||
|
return stanza.getOriginID()
|
||||||
|
return self._get_stanza_id(stanza)
|
||||||
|
|
||||||
|
# Normal Message
|
||||||
|
if self_message or muc_pm:
|
||||||
|
return stanza.getOriginID()
|
||||||
|
return self._get_stanza_id(stanza)
|
||||||
|
|
||||||
|
def _get_stanza_id(self, stanza):
|
||||||
|
stanza_id, by = stanza.getStanzaIDAttrs()
|
||||||
|
if by is None:
|
||||||
|
# We can not verify who set this stanza-id, ignore it.
|
||||||
|
return
|
||||||
|
if stanza.getType() == 'groupchat':
|
||||||
|
if stanza.getFrom().bareMatch(by):
|
||||||
|
# by attribute must match the server
|
||||||
|
return stanza_id
|
||||||
|
elif self._con.get_own_jid().bareMatch(by):
|
||||||
|
# by attribute must match the server
|
||||||
|
return stanza_id
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class MessageReceivedEvent(NetworkIncomingEvent):
|
||||||
|
name = 'message-received'
|
||||||
|
|
||||||
|
|
||||||
|
class DecryptedMessageReceivedEvent(NetworkIncomingEvent):
|
||||||
|
name = 'decrypted-message-received'
|
||||||
|
|
||||||
|
|
||||||
|
def get_instance(*args, **kwargs):
|
||||||
|
return Message(*args, **kwargs), 'Message'
|
|
@ -18,6 +18,7 @@ import logging
|
||||||
|
|
||||||
import nbxmpp
|
import nbxmpp
|
||||||
|
|
||||||
|
from gajim.common import app
|
||||||
from gajim.common.modules.date_and_time import parse_datetime
|
from gajim.common.modules.date_and_time import parse_datetime
|
||||||
|
|
||||||
log = logging.getLogger('gajim.c.m.misc')
|
log = logging.getLogger('gajim.c.m.misc')
|
||||||
|
@ -111,3 +112,38 @@ def parse_correction(stanza):
|
||||||
if id_ is not None:
|
if id_ is not None:
|
||||||
return id_
|
return id_
|
||||||
log.warning('No id attr found: %s' % stanza)
|
log.warning('No id attr found: %s' % stanza)
|
||||||
|
|
||||||
|
|
||||||
|
# XEP-0224: Attention
|
||||||
|
|
||||||
|
def parse_attention(stanza):
|
||||||
|
attention = stanza.getTag('attention', namespace=nbxmpp.NS_ATTENTION)
|
||||||
|
if attention is None:
|
||||||
|
return False
|
||||||
|
delayed = stanza.getTag('x', namespace=nbxmpp.NS_DELAY2)
|
||||||
|
if delayed is not None:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
# XEP-0258: Security Labels in XMPP
|
||||||
|
|
||||||
|
def parse_securitylabel(stanza):
|
||||||
|
seclabel = stanza.getTag('securitylabel', namespace=nbxmpp.NS_SECLABEL)
|
||||||
|
if seclabel is None:
|
||||||
|
return None
|
||||||
|
return seclabel.getTag('displaymarking')
|
||||||
|
|
||||||
|
|
||||||
|
# XEP-0004: Data Forms
|
||||||
|
|
||||||
|
def parse_form(stanza):
|
||||||
|
return stanza.getTag('x', namespace=nbxmpp.NS_DATA)
|
||||||
|
|
||||||
|
|
||||||
|
# XEP-0071: XHTML-IM
|
||||||
|
|
||||||
|
def parse_xhtml(stanza):
|
||||||
|
if app.config.get('ignore_incoming_xhtml'):
|
||||||
|
return None
|
||||||
|
return stanza.getXHTML()
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
# This file is part of Gajim.
|
||||||
|
#
|
||||||
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation; version 3 only.
|
||||||
|
#
|
||||||
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# XEP-0184: Message Delivery Receipts
|
||||||
|
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
|
||||||
|
from gajim.common import app
|
||||||
|
from gajim.common.nec import NetworkIncomingEvent
|
||||||
|
|
||||||
|
log = logging.getLogger('gajim.c.m.receipts')
|
||||||
|
|
||||||
|
|
||||||
|
class Receipts:
|
||||||
|
def __init__(self, con):
|
||||||
|
self._con = con
|
||||||
|
self._account = con.name
|
||||||
|
|
||||||
|
self.handlers = []
|
||||||
|
|
||||||
|
def delegate(self, event):
|
||||||
|
request = event.stanza.getTag('request',
|
||||||
|
namespace=nbxmpp.NS_RECEIPTS)
|
||||||
|
if request is not None:
|
||||||
|
self._answer_request(event)
|
||||||
|
return
|
||||||
|
|
||||||
|
received = event.stanza.getTag('received',
|
||||||
|
namespace=nbxmpp.NS_RECEIPTS)
|
||||||
|
if received is not None:
|
||||||
|
self._receipt_received(event, received)
|
||||||
|
raise nbxmpp.NodeProcessed
|
||||||
|
|
||||||
|
def _answer_request(self, event):
|
||||||
|
if not app.config.get_per('accounts', self._account,
|
||||||
|
'answer_receipts'):
|
||||||
|
return
|
||||||
|
|
||||||
|
if event.mtype not in ('chat', 'groupchat'):
|
||||||
|
return
|
||||||
|
|
||||||
|
if event.sent:
|
||||||
|
# Never answer messages that we sent from another device
|
||||||
|
return
|
||||||
|
|
||||||
|
from_ = event.stanza.getFrom()
|
||||||
|
if self._con.get_own_jid().bareMatch(from_):
|
||||||
|
# Dont answer receipts from our other resources
|
||||||
|
return
|
||||||
|
|
||||||
|
receipt_id = event.stanza.getID()
|
||||||
|
|
||||||
|
contact = self._get_contact(event)
|
||||||
|
if contact is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
receipt = self._build_answer_receipt(from_, receipt_id)
|
||||||
|
log.info('Answer %s', receipt_id)
|
||||||
|
self._con.connection.send(receipt)
|
||||||
|
|
||||||
|
def _get_contact(self, event):
|
||||||
|
if event.mtype == 'chat':
|
||||||
|
contact = app.contacts.get_contact(self._account, event.jid)
|
||||||
|
if contact and contact.sub not in ('to', 'none'):
|
||||||
|
return contact
|
||||||
|
else:
|
||||||
|
return app.contacts.get_gc_contact(self._account,
|
||||||
|
event.jid,
|
||||||
|
event.resource)
|
||||||
|
|
||||||
|
def _build_answer_receipt(self, to, receipt_id):
|
||||||
|
receipt = nbxmpp.Message(to=to, typ='chat')
|
||||||
|
receipt.setTag('received',
|
||||||
|
namespace='urn:xmpp:receipts',
|
||||||
|
attrs={'id': receipt_id})
|
||||||
|
return receipt
|
||||||
|
|
||||||
|
def _receipt_received(self, event, received):
|
||||||
|
receipt_id = received.getAttr('id')
|
||||||
|
if receipt_id is None:
|
||||||
|
log.warning('Receipt without ID: %s', event.stanza)
|
||||||
|
return
|
||||||
|
log.info('Received %s', receipt_id)
|
||||||
|
app.nec.push_incoming_event(
|
||||||
|
NetworkIncomingEvent('receipt-received',
|
||||||
|
conn=self._con,
|
||||||
|
receipt_id=receipt_id,
|
||||||
|
jid=event.jid))
|
||||||
|
|
||||||
|
|
||||||
|
def get_instance(*args, **kwargs):
|
||||||
|
return Receipts(*args, **kwargs), 'Receipts'
|
|
@ -78,5 +78,12 @@ class UserNickname(AbstractPEPModule):
|
||||||
'accounts', self._account, 'name')
|
'accounts', self._account, 'name')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_nickname(stanza):
|
||||||
|
nick = stanza.getTag('nick', namespace=nbxmpp.NS_NICK)
|
||||||
|
if nick is None:
|
||||||
|
return ''
|
||||||
|
return nick.getData()
|
||||||
|
|
||||||
|
|
||||||
def get_instance(*args, **kwargs):
|
def get_instance(*args, **kwargs):
|
||||||
return UserNickname(*args, **kwargs), 'UserNickname'
|
return UserNickname(*args, **kwargs), 'UserNickname'
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
# This file is part of Gajim.
|
||||||
|
#
|
||||||
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation; version 3 only.
|
||||||
|
#
|
||||||
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# Util module
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
|
||||||
|
from gajim.common import app
|
||||||
|
|
||||||
|
|
||||||
|
def is_self_message(message, groupchat=False):
|
||||||
|
if groupchat:
|
||||||
|
return False
|
||||||
|
frm = message.getFrom()
|
||||||
|
to = message.getTo()
|
||||||
|
return frm.bareMatch(to)
|
||||||
|
|
||||||
|
|
||||||
|
def is_muc_pm(message, jid, groupchat=False):
|
||||||
|
if groupchat:
|
||||||
|
return False
|
||||||
|
muc_user = message.getTag('x', namespace=nbxmpp.NS_MUC_USER)
|
||||||
|
if muc_user is not None:
|
||||||
|
return muc_user.getChildren() == []
|
||||||
|
else:
|
||||||
|
# muc#user namespace was added in MUC 1.28 so we need a fallback
|
||||||
|
# Check if we know the jid
|
||||||
|
if app.logger.jid_is_room_jid(jid.getStripped()):
|
||||||
|
return True
|
||||||
|
return False
|
|
@ -23,21 +23,40 @@
|
||||||
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
##
|
##
|
||||||
|
|
||||||
|
import time
|
||||||
|
|
||||||
import nbxmpp
|
import nbxmpp
|
||||||
|
|
||||||
from gajim.common import app
|
from gajim.common import app
|
||||||
|
|
||||||
from gajim.common.protocol.bytestream import ConnectionSocks5BytestreamZeroconf
|
from gajim.common.protocol.bytestream import ConnectionSocks5BytestreamZeroconf
|
||||||
from gajim.common.connection_handlers_events import ZeroconfMessageReceivedEvent
|
from gajim.common.zeroconf.zeroconf import Constant
|
||||||
|
from gajim.common import connection_handlers
|
||||||
|
from gajim.common.nec import NetworkIncomingEvent, NetworkEvent
|
||||||
|
from gajim.common.modules.user_nickname import parse_nickname
|
||||||
|
from gajim.common.modules.chatstates import parse_chatstate
|
||||||
|
from gajim.common.modules.misc import parse_eme
|
||||||
|
from gajim.common.modules.misc import parse_correction
|
||||||
|
from gajim.common.modules.misc import parse_attention
|
||||||
|
from gajim.common.modules.misc import parse_oob
|
||||||
|
from gajim.common.modules.misc import parse_xhtml
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf')
|
log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf')
|
||||||
|
|
||||||
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||||
'invisible']
|
'invisible']
|
||||||
# kind of events we can wait for an answer
|
# kind of events we can wait for an answer
|
||||||
AGENT_REMOVED = 'agent_removed'
|
AGENT_REMOVED = 'agent_removed'
|
||||||
|
|
||||||
from gajim.common import connection_handlers
|
|
||||||
|
class ZeroconfMessageReceivedEvent(NetworkIncomingEvent):
|
||||||
|
name = 'message-received'
|
||||||
|
|
||||||
|
|
||||||
|
class DecryptedMessageReceivedEvent(NetworkIncomingEvent):
|
||||||
|
name = 'decrypted-message-received'
|
||||||
|
|
||||||
|
|
||||||
class ConnectionVcard:
|
class ConnectionVcard:
|
||||||
def add_sha(self, p, *args):
|
def add_sha(self, p, *args):
|
||||||
|
@ -57,14 +76,113 @@ connection_handlers.ConnectionJingle):
|
||||||
connection_handlers.ConnectionJingle.__init__(self)
|
connection_handlers.ConnectionJingle.__init__(self)
|
||||||
connection_handlers.ConnectionHandlersBase.__init__(self)
|
connection_handlers.ConnectionHandlersBase.__init__(self)
|
||||||
|
|
||||||
def _messageCB(self, ip, con, msg):
|
def _messageCB(self, ip, con, stanza):
|
||||||
"""
|
"""
|
||||||
Called when we receive a message
|
Called when we receive a message
|
||||||
"""
|
"""
|
||||||
log.debug('Zeroconf MessageCB')
|
log.debug('Zeroconf MessageCB')
|
||||||
app.nec.push_incoming_event(ZeroconfMessageReceivedEvent(None,
|
|
||||||
conn=self, stanza=msg, ip=ip))
|
app.nec.push_incoming_event(NetworkEvent(
|
||||||
return
|
'raw-message-received',
|
||||||
|
conn=self,
|
||||||
|
stanza=stanza,
|
||||||
|
account=self.name))
|
||||||
|
|
||||||
|
type_ = stanza.getType()
|
||||||
|
if type_ is None:
|
||||||
|
type_ = 'normal'
|
||||||
|
|
||||||
|
id_ = stanza.getID()
|
||||||
|
|
||||||
|
fjid = str(stanza.getFrom())
|
||||||
|
|
||||||
|
if fjid is None:
|
||||||
|
for key in self.connection.zeroconf.contacts:
|
||||||
|
if ip == self.connection.zeroconf.contacts[key][
|
||||||
|
Constant.ADDRESS]:
|
||||||
|
fjid = key
|
||||||
|
break
|
||||||
|
|
||||||
|
jid, resource = app.get_room_and_nick_from_fjid(fjid)
|
||||||
|
|
||||||
|
thread_id = stanza.getThread()
|
||||||
|
msgtxt = stanza.getBody()
|
||||||
|
|
||||||
|
session = self.get_or_create_session(fjid, thread_id)
|
||||||
|
|
||||||
|
if thread_id and not session.received_thread_id:
|
||||||
|
session.received_thread_id = True
|
||||||
|
|
||||||
|
session.last_receive = time.time()
|
||||||
|
|
||||||
|
event_attr = {
|
||||||
|
'conn': self,
|
||||||
|
'stanza': stanza,
|
||||||
|
'account': self.name,
|
||||||
|
'id_': id_,
|
||||||
|
'encrypted': False,
|
||||||
|
'additional_data': {},
|
||||||
|
'forwarded': False,
|
||||||
|
'sent': False,
|
||||||
|
'timestamp': time.time(),
|
||||||
|
'fjid': fjid,
|
||||||
|
'jid': jid,
|
||||||
|
'resource': resource,
|
||||||
|
'unique_id': id_,
|
||||||
|
'mtype': type_,
|
||||||
|
'msgtxt': msgtxt,
|
||||||
|
'thread_id': thread_id,
|
||||||
|
'session': session,
|
||||||
|
'self_message': False,
|
||||||
|
'muc_pm': False,
|
||||||
|
'gc_control': None}
|
||||||
|
|
||||||
|
event = ZeroconfMessageReceivedEvent(None, **event_attr)
|
||||||
|
app.nec.push_incoming_event(event)
|
||||||
|
|
||||||
|
app.plugin_manager.extension_point(
|
||||||
|
'decrypt', self, event, self._on_message_decrypted)
|
||||||
|
if not event.encrypted:
|
||||||
|
eme = parse_eme(event.stanza)
|
||||||
|
if eme is not None:
|
||||||
|
event.msgtxt = eme
|
||||||
|
self._on_message_decrypted(event)
|
||||||
|
|
||||||
|
def _on_message_decrypted(self, event):
|
||||||
|
try:
|
||||||
|
self.get_module('Receipts').delegate(event)
|
||||||
|
except nbxmpp.NodeProcessed:
|
||||||
|
return
|
||||||
|
|
||||||
|
event_attr = {
|
||||||
|
'popup': False,
|
||||||
|
'msg_log_id': None,
|
||||||
|
'subject': None,
|
||||||
|
'displaymarking': None,
|
||||||
|
'form_node': None,
|
||||||
|
'attention': parse_attention(event.stanza),
|
||||||
|
'correct_id': parse_correction(event.stanza),
|
||||||
|
'user_nick': parse_nickname(event.stanza),
|
||||||
|
'xhtml': parse_xhtml(event.stanza),
|
||||||
|
'chatstate': parse_chatstate(event.stanza),
|
||||||
|
'stanza_id': event.unique_id
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_oob(event.stanza, event.additional_data)
|
||||||
|
|
||||||
|
for name, value in event_attr.items():
|
||||||
|
setattr(event, name, value)
|
||||||
|
|
||||||
|
if event.mtype == 'error':
|
||||||
|
if not event.msgtxt:
|
||||||
|
event.msgtxt = _('message')
|
||||||
|
self.dispatch_error_message(
|
||||||
|
event.stanza, event.msgtxt,
|
||||||
|
event.session, event.fjid, event.timestamp)
|
||||||
|
return
|
||||||
|
|
||||||
|
app.nec.push_incoming_event(
|
||||||
|
DecryptedMessageReceivedEvent(None, **vars(event)))
|
||||||
|
|
||||||
def store_metacontacts(self, tags):
|
def store_metacontacts(self, tags):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -12,7 +12,7 @@ from gajim.common import app
|
||||||
from gajim.common import nec
|
from gajim.common import nec
|
||||||
from gajim.common import ged
|
from gajim.common import ged
|
||||||
from gajim.common.nec import NetworkEvent
|
from gajim.common.nec import NetworkEvent
|
||||||
from gajim.common.connection_handlers_events import MessageReceivedEvent
|
from gajim.common.modules.message import MessageReceivedEvent
|
||||||
from gajim.common.connection_handlers_events import DecryptedMessageReceivedEvent
|
from gajim.common.connection_handlers_events import DecryptedMessageReceivedEvent
|
||||||
import nbxmpp
|
import nbxmpp
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue