From cc032163d01e08cc06c5687cec4707178dd00214 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Wed, 8 Dec 2010 21:58:13 +0100 Subject: [PATCH] more usage of NEC to handle messages --- src/chat_control.py | 6 +- src/common/connection_handlers.py | 9 +- src/common/connection_handlers_events.py | 67 +++++++- src/session.py | 205 ++++++++--------------- 4 files changed, 148 insertions(+), 139 deletions(-) diff --git a/src/chat_control.py b/src/chat_control.py index 01067e666..b6fbadc20 100644 --- a/src/chat_control.py +++ b/src/chat_control.py @@ -1603,6 +1603,8 @@ class ChatControl(ChatControlBase): self._nec_vcard_received) gajim.ged.register_event_handler('failed-decrypt', ged.GUI1, self._nec_failed_decrypt) + gajim.ged.register_event_handler('chatstate-received', ged.GUI1, + self._nec_chatstate_received) # PluginSystem: adding GUI extension point for this ChatControl # instance object @@ -2593,6 +2595,8 @@ class ChatControl(ChatControlBase): self._nec_vcard_received) gajim.ged.remove_event_handler('failed-decrypt', ged.GUI1, self._nec_failed_decrypt) + gajim.ged.remove_event_handler('chatstate-received', ged.GUI1, + self._nec_chatstate_received) self.send_chatstate('gone', self.contact) self.contact.chatstate = None @@ -2661,7 +2665,7 @@ class ChatControl(ChatControlBase): return on_yes(self) - def handle_incoming_chatstate(self): + def _nec_chatstate_received(self, obj): """ Handle incoming chatstate that jid SENT TO us """ diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 2b1ff5caa..d889927f0 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -1329,18 +1329,15 @@ ConnectionJingle, ConnectionIBBytestream): if obj.mtype == 'error': self.dispatch_error_message(obj.stanza, obj.msgtxt, obj.session, obj.fjid, obj.timestamp) + return True elif obj.mtype == 'groupchat': gajim.nec.push_incoming_event(GcMessageReceivedEvent(None, conn=self, msg_obj=obj)) + return True elif obj.invite_tag is not None: gajim.nec.push_incoming_event(GcInvitationReceivedEvent(None, conn=self, msg_obj=obj)) - else: - if isinstance(obj.session, gajim.default_session_type): - obj.session.received(obj.fjid, obj.msgtxt, obj.timestamp, - obj.encrypted, obj.stanza) - else: - obj.session.received(obj.stanza) + return True # process and dispatch an error message def dispatch_error_message(self, msg, msgtxt, session, frm, tim): diff --git a/src/common/connection_handlers_events.py b/src/common/connection_handlers_events.py index 85734fd8d..204f0d63b 100644 --- a/src/common/connection_handlers_events.py +++ b/src/common/connection_handlers_events.py @@ -65,6 +65,34 @@ class HelperEvent: tim = helpers.datetime_tuple(tag) self.timestamp = localtime(timegm(tim)) + def get_chatstate(self): + """ + Extract chatstate from a stanza + Requires self.stanza and self.msgtxt + """ + self.composing_xep = None + self.chatstate = None + + # chatstates - look for chatstate tags in a message if not delayed + delayed = self.stanza.getTag('x', namespace=xmpp.NS_DELAY) is not None + if not delayed: + self.composing_xep = False + children = self.stanza.getChildren() + for child in children: + if child.getNamespace() == 'http://jabber.org/protocol/chatstates': + self.chatstate = child.getName() + self.composing_xep = 'XEP-0085' + break + # No XEP-0085 support, fallback to XEP-0022 + if not self.chatstate: + chatstate_child = self.stanza.getTag('x', + namespace=xmpp.NS_EVENT) + if chatstate_child: + self.chatstate = 'active' + self.composing_xep = 'XEP-0022' + if not self.msgtxt and chatstate_child.getTag('composing'): + self.chatstate = 'composing' + class HttpAuthReceivedEvent(nec.NetworkIncomingEvent): name = 'http-auth-received' base_network_events = [] @@ -1021,7 +1049,7 @@ class GcInvitationReceivedEvent(nec.NetworkIncomingEvent): return True -class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent): +class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): name = 'decrypted-message-received' base_network_events = [] @@ -1044,6 +1072,43 @@ class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent): namespace=xmpp.NS_RECEIPTS) self.receipt_received_tag = self.stanza.getTag('received', namespace=xmpp.NS_RECEIPTS) + + self.subject = self.stanza.getSubject() + + self.displaymarking = None + self.seclabel = self.stanza.getTag('securitylabel', + namespace=xmpp.NS_SECLABEL) + if self.seclabel: + self.displaymarking = self.seclabel.getTag('displaymarking') + + self.form_node = self.stanza.getTag('x', namespace=xmpp.NS_DATA) + + if gajim.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 '' + + treat_as = gajim.config.get('treat_incoming_messages') + if treat_as: + self.mtype = treat_as + + self.get_chatstate() + return True + +class ChatstateReceivedEvent(nec.NetworkIncomingEvent): + name = 'chatstate-received' + base_network_events = [] + + def generate(self): + self.stanza = self.msg_obj.stanza + self.jid = self.msg_obj.jid + self.fjid = self.msg_obj.fjid + self.resource = self.msg_obj.resource + self.composing_xep = self.msg_obj.composing_xep + self.chatstate = self.msg_obj.chatstate return True class GcMessageReceivedEvent(nec.NetworkIncomingEvent): diff --git a/src/session.py b/src/session.py index 30693ed1d..e50d61f84 100644 --- a/src/session.py +++ b/src/session.py @@ -27,6 +27,8 @@ from common import exceptions from common import gajim from common import stanza_session from common import contacts +from common import ged +from common.connection_handlers_events import ChatstateReceivedEvent import common.xmpp @@ -41,12 +43,16 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): def __init__(self, conn, jid, thread_id, type_='chat'): stanza_session.EncryptedStanzaSession.__init__(self, conn, jid, thread_id, type_='chat') + gajim.ged.register_event_handler('decrypted-message-received', ged.GUI1, + self._nec_decrypted_message_received) self.control = None def detach_from_control(self): if self.control: self.control.set_session(None) + gajim.ged.remove_event_handler('decrypted-message-received', + ged.GUI1, self._nec_decrypted_message_received) def acknowledge_termination(self): self.detach_from_control() @@ -56,175 +62,107 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): stanza_session.EncryptedStanzaSession.terminate(self, send_termination) self.detach_from_control() - def get_chatstate(self, msg, msgtxt): - """ - Extract chatstate from a stanza - """ - composing_xep = None - chatstate = None - - # chatstates - look for chatstate tags in a message if not delayed - delayed = msg.getTag('x', namespace=common.xmpp.NS_DELAY) is not None - if not delayed: - composing_xep = False - children = msg.getChildren() - for child in children: - if child.getNamespace() == 'http://jabber.org/protocol/chatstates': - chatstate = child.getName() - composing_xep = 'XEP-0085' - break - # No XEP-0085 support, fallback to XEP-0022 - if not chatstate: - chatstate_child = msg.getTag('x', namespace=common.xmpp.NS_EVENT) - if chatstate_child: - chatstate = 'active' - composing_xep = 'XEP-0022' - if not msgtxt and chatstate_child.getTag('composing'): - chatstate = 'composing' - - return (composing_xep, chatstate) - - def received(self, full_jid_with_resource, msgtxt, tim, encrypted, msg): + def _nec_decrypted_message_received(self, obj): """ Dispatch a received stanza """ - msg_type = msg.getType() - subject = msg.getSubject() - resource = gajim.get_resource_from_jid(full_jid_with_resource) - if self.resource != resource: - self.resource = resource + if obj.session != self: + return + if self.resource != obj.resource: + self.resource = obj.resource if self.control and self.control.resource: self.control.change_resource(self.resource) - seclabel = None - displaymarking = None - - if not msg_type or msg_type not in ('chat', 'groupchat', 'error'): - msg_type = 'normal' msg_id = None - # XEP-0172 User Nickname - user_nick = msg.getTagData('nick') - if not user_nick: - user_nick = '' - - form_node = None - for xtag in msg.getTags('x'): - if xtag.getNamespace() == common.xmpp.NS_DATA: - form_node = xtag - break - - composing_xep, chatstate = self.get_chatstate(msg, msgtxt) - seclabel = msg.getTag('securitylabel') - if seclabel and seclabel.getNamespace() == common.xmpp.NS_SECLABEL: - displaymarking = seclabel.getTag('displaymarking') - xhtml = msg.getXHTML() - - if msg_type == 'chat': - if not msg.getTag('body') and chatstate is None: + if obj.mtype == 'chat': + if not obj.stanza.getTag('body') and obj.chatstate is None: return log_type = 'chat_msg_recv' else: log_type = 'single_msg_recv' - if self.is_loggable() and msgtxt: + if self.is_loggable() and obj.msgtxt: try: - msg_id = gajim.logger.write(log_type, full_jid_with_resource, - msgtxt, tim=tim, subject=subject) + msg_id = gajim.logger.write(log_type, obj.fjid, + obj.msgtxt, tim=obj.timestamp, subject=obj.subject) except exceptions.PysqliteOperationalError, e: self.conn.dispatch('ERROR', (_('Disk WriteError'), str(e))) except exceptions.DatabaseMalformed: pritext = _('Database Error') - sectext = _('The database file (%s) cannot be read. Try to repair ' - 'it (see http://trac.gajim.org/wiki/DatabaseBackup) or remove ' - 'it (all history will be lost).') % common.logger.LOG_DB_PATH + sectext = _('The database file (%s) cannot be read. Try to ' + 'repair it (see http://trac.gajim.org/wiki/DatabaseBackup) ' + 'or remove it (all history will be lost).') % \ + common.logger.LOG_DB_PATH self.conn.dispatch('ERROR', (pritext, sectext)) - treat_as = gajim.config.get('treat_incoming_messages') - if treat_as: - msg_type = treat_as - - jid = gajim.get_jid_without_resource(full_jid_with_resource) - resource = gajim.get_resource_from_jid(full_jid_with_resource) - - if gajim.config.get('ignore_incoming_xhtml'): - xhtml = None - if gajim.jid_is_transport(jid): - jid = jid.replace('@', '') - - groupchat_control = gajim.interface.msg_win_mgr.get_gc_control(jid, - self.conn.name) - - if not groupchat_control and \ - jid in gajim.interface.minimized_controls[self.conn.name]: - groupchat_control = gajim.interface.minimized_controls[self.conn.name]\ - [jid] - pm = False - if groupchat_control and groupchat_control.type_id == \ - message_control.TYPE_GC and resource: + if obj.gc_control and obj.resource: # It's a Private message pm = True - msg_type = 'pm' + obj.mtype = 'pm' highest_contact = gajim.contacts.get_contact_with_highest_priority( - self.conn.name, jid) + self.conn.name, obj.jid) # does this resource have the highest priority of any available? is_highest = not highest_contact or not highest_contact.resource or \ - resource == highest_contact.resource or highest_contact.show == \ - 'offline' + obj.resource == highest_contact.resource or highest_contact.show ==\ + 'offline' # Handle chat states - contact = gajim.contacts.get_contact(self.conn.name, jid, resource) + contact = gajim.contacts.get_contact(self.conn.name, obj.jid, + obj.resource) if contact: if contact.composing_xep != 'XEP-0085': # We cache xep85 support - contact.composing_xep = composing_xep - if self.control and self.control.type_id == message_control.TYPE_CHAT: - if chatstate is not None: + contact.composing_xep = obj.composing_xep + if self.control and self.control.type_id == \ + message_control.TYPE_CHAT: + if obj.chatstate is not None: # other peer sent us reply, so he supports jep85 or jep22 - contact.chatstate = chatstate + contact.chatstate = obj.chatstate if contact.our_chatstate == 'ask': # we were jep85 disco? contact.our_chatstate = 'active' # no more - self.control.handle_incoming_chatstate() + gajim.nec.push_incoming_event(ChatstateReceivedEvent(None, + conn=obj.conn, msg_obj=obj)) elif contact.chatstate != 'active': # got no valid jep85 answer, peer does not support it contact.chatstate = False - elif chatstate == 'active': + elif obj.chatstate == 'active': # Brand new message, incoming. - contact.our_chatstate = chatstate - contact.chatstate = chatstate + contact.our_chatstate = obj.chatstate + contact.chatstate = obj.chatstate if msg_id: # Do not overwrite an existing msg_id with None contact.msg_id = msg_id # THIS MUST BE AFTER chatstates handling # AND BEFORE playsound (else we ear sounding on chatstates!) - if not msgtxt: # empty message text + if not obj.msgtxt: # empty message text return if gajim.config.get_per('accounts', self.conn.name, 'ignore_unknown_contacts') and not gajim.contacts.get_contacts( - self.conn.name, jid) and not pm: + self.conn.name, obj.jid) and not pm: return if not contact: # contact is not in the roster, create a fake one to display # notification - contact = gajim.contacts.create_not_in_roster_contact(jid=jid, - account=self.conn.name, resource=resource) + contact = gajim.contacts.create_not_in_roster_contact(jid=obj.jid, + account=self.conn.name, resource=obj.resource) - advanced_notif_num = notify.get_advanced_notification('message_received', - self.conn.name, contact) + advanced_notif_num = notify.get_advanced_notification( + 'message_received', self.conn.name, contact) if not pm and is_highest: - jid_of_control = jid + jid_of_control = obj.jid else: - jid_of_control = full_jid_with_resource + jid_of_control = obj.fjid if not self.control: ctrl = gajim.interface.msg_win_mgr.get_control(jid_of_control, - self.conn.name) + self.conn.name) if ctrl: self.control = ctrl self.control.set_session(self) @@ -232,50 +170,55 @@ class ChatControlSession(stanza_session.EncryptedStanzaSession): # Is it a first or next message received ? first = False if not self.control and not gajim.events.get_events(self.conn.name, \ - jid_of_control, [msg_type]): + jid_of_control, [obj.mtype]): first = True if pm: - nickname = resource + nickname = obj.resource if self.control: # print if a control is open - self.control.print_conversation(msgtxt, tim=tim, xhtml=xhtml, - encrypted=encrypted, displaymarking=displaymarking) + self.control.print_conversation(obj.msgtxt, tim=obj.timestamp, + xhtml=obj.xhtml, encrypted=obj.encrypted, + displaymarking=obj.displaymarking) else: # otherwise pass it off to the control to be queued - groupchat_control.on_private_message(nickname, msgtxt, tim, - xhtml, self, msg_id=msg_id, encrypted=encrypted, displaymarking=displaymarking) + obj.gc_control.on_private_message(nickname, obj.msgtxt, + obj.timestamp, obj.xhtml, self, msg_id=msg_id, + encrypted=obj.encrypted, displaymarking=obj.displaymarking) else: - self.roster_message(jid, msgtxt, tim, encrypted, msg_type, - subject, resource, msg_id, user_nick, advanced_notif_num, - xhtml=xhtml, form_node=form_node, displaymarking=displaymarking) + self.roster_message(obj.jid, obj.msgtxt, obj.timestamp, + obj.encrypted, obj.mtype, + obj.subject, obj.resource, msg_id, obj.user_nick, + advanced_notif_num, xhtml=obj.xhtml, form_node=obj.form_node, + displaymarking=obj.displaymarking) - nickname = gajim.get_name_from_jid(self.conn.name, jid) + nickname = gajim.get_name_from_jid(self.conn.name, obj.jid) # Check and do wanted notifications - msg = msgtxt - if subject: - msg = _('Subject: %s') % subject + '\n' + msg + msg = obj.msgtxt + if obj.subject: + msg = _('Subject: %s') % obj.subject + '\n' + msg focused = False if self.control: parent_win = self.control.parent_win - if parent_win and self.control == parent_win.get_active_control() and \ - parent_win.window.has_focus: + if parent_win and self.control == parent_win.get_active_control() \ + and parent_win.window.has_focus: focused = True - notify.notify('new_message', jid_of_control, self.conn.name, [msg_type, + notify.notify('new_message', jid_of_control, self.conn.name, [obj.mtype, first, nickname, msg, focused], advanced_notif_num) if gajim.interface.remote_ctrl: - gajim.interface.remote_ctrl.raise_signal('NewMessage', (self.conn.name, - [full_jid_with_resource, msgtxt, tim, encrypted, msg_type, subject, - chatstate, msg_id, composing_xep, user_nick, xhtml, form_node])) + gajim.interface.remote_ctrl.raise_signal('NewMessage', ( + self.conn.name, [obj.fjid, obj.msgtxt, obj.timestamp, + obj.encrypted, obj.mtype, obj.subject, obj.chatstate, msg_id, + obj.composing_xep, obj.user_nick, obj.xhtml, obj.form_node])) - gajim.ged.raise_event('NewMessage', - (self.conn.name, [full_jid_with_resource, msgtxt, tim, - encrypted, msg_type, subject, chatstate, msg_id, - composing_xep, user_nick, xhtml, form_node])) + gajim.ged.raise_event('NewMessage', + (self.conn.name, [obj.fjid, obj.msgtxt, obj.timestamp, + obj.encrypted, obj.mtype, obj.subject, obj.chatstate, msg_id, + obj.composing_xep, obj.user_nick, obj.xhtml, obj.form_node])) def roster_message(self, jid, msg, tim, encrypted=False, msg_type='', subject=None, resource='', msg_id=None, user_nick='',