diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 375279b51..f882ecbce 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -49,7 +49,8 @@ if dbus_support.supported: import dbus from music_track_listener import MusicTrackListener -from common.stanza_session import EncryptedStanzaSession +# XXX interface leaking into the back end? +import session STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd', 'invisible', 'error'] @@ -1713,9 +1714,9 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, treat_as = gajim.config.get('treat_incoming_messages') if treat_as: mtype = treat_as - self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, - subject, chatstate, msg_id, composing_xep, user_nick, msghtml, - session, form_node)) + + session.received(frm, msgtxt, tim, encrypted, mtype, subject, chatstate, + msg_id, composing_xep, user_nick, msghtml, form_node) # END messageCB def get_session(self, jid, thread_id, type): @@ -1786,7 +1787,7 @@ returns the session that we last sent a message to.''' return None def make_new_session(self, jid, thread_id = None, type = 'chat'): - sess = EncryptedStanzaSession(self, common.xmpp.JID(jid), thread_id, type) + sess = session.ChatControlSession(self, common.xmpp.JID(jid), thread_id, type) if not jid in self.sessions: self.sessions[jid] = {} diff --git a/src/common/events.py b/src/common/events.py index f676ec7fc..90e6cc08f 100644 --- a/src/common/events.py +++ b/src/common/events.py @@ -168,7 +168,7 @@ class Events: '''returns all events from the given account of the form {jid1: [], jid2: []} if jid is given, returns all events from the given jid in a list: [] - optionnaly only from given type''' + optionally only from given type''' if not self._events.has_key(account): return [] if not jid: diff --git a/src/gajim.py b/src/gajim.py index ad64b9b52..ec798f6c0 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -727,141 +727,6 @@ class Interface: self.handle_event_gc_notify(account, (jid, array[1], status_message, array[3], None, None, None, None, None, [], None, None)) - - def handle_event_msg(self, account, array): - # 'MSG' (account, (jid, msg, time, encrypted, msg_type, subject, - # chatstate, msg_id, composing_xep, user_nick, xhtml, session, form_node)) - # user_nick is JEP-0172 - - full_jid_with_resource = array[0] - jid = gajim.get_jid_without_resource(full_jid_with_resource) - resource = gajim.get_resource_from_jid(full_jid_with_resource) - - message = array[1] - encrypted = array[3] - msg_type = array[4] - subject = array[5] - chatstate = array[6] - msg_id = array[7] - composing_xep = array[8] - xhtml = array[10] - session = array[11] - if gajim.config.get('ignore_incoming_xhtml'): - xhtml = None - if gajim.jid_is_transport(jid): - jid = jid.replace('@', '') - - groupchat_control = self.msg_win_mgr.get_control(jid, account) - if not groupchat_control and \ - jid in self.minimized_controls[account]: - groupchat_control = self.minimized_controls[account][jid] - pm = False - if groupchat_control and groupchat_control.type_id == \ - message_control.TYPE_GC: - # It's a Private message - pm = True - msg_type = 'pm' - - chat_control = None - jid_of_control = full_jid_with_resource - highest_contact = gajim.contacts.get_contact_with_highest_priority( - account, jid) - # Look for a chat control that has the given resource, or default to one - # without resource - ctrl = self.msg_win_mgr.get_control(full_jid_with_resource, account) - if ctrl: - chat_control = ctrl - elif not pm and (not highest_contact or not highest_contact.resource): - # unknow contact or offline message - jid_of_control = jid - chat_control = self.msg_win_mgr.get_control(jid, account) - elif highest_contact and resource != highest_contact.resource and \ - highest_contact.show != 'offline': - jid_of_control = full_jid_with_resource - chat_control = None - elif not pm: - jid_of_control = jid - chat_control = self.msg_win_mgr.get_control(jid, account) - - # Handle chat states - contact = gajim.contacts.get_contact(account, jid, resource) - if contact: - if contact.composing_xep != 'XEP-0085': # We cache xep85 support - contact.composing_xep = composing_xep - if chat_control and chat_control.type_id == message_control.TYPE_CHAT: - if chatstate is not None: - # other peer sent us reply, so he supports jep85 or jep22 - contact.chatstate = chatstate - if contact.our_chatstate == 'ask': # we were jep85 disco? - contact.our_chatstate = 'active' # no more - chat_control.handle_incoming_chatstate() - elif contact.chatstate != 'active': - # got no valid jep85 answer, peer does not support it - contact.chatstate = False - elif chatstate == 'active': - # Brand new message, incoming. - contact.our_chatstate = chatstate - contact.chatstate = 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 message: # empty message text - return - - if gajim.config.get('ignore_unknown_contacts') and \ - not gajim.contacts.get_contacts(account, jid) and not pm: - return - if not contact: - # contact is not in the roster, create a fake one to display - # notification - contact = common.contacts.Contact(jid = jid, resource = resource) - advanced_notif_num = notify.get_advanced_notification('message_received', - account, contact) - - # Is it a first or next message received ? - first = False - if msg_type == 'normal': - if not gajim.events.get_events(account, jid, ['normal']): - first = True - elif not chat_control and not gajim.events.get_events(account, - jid_of_control, [msg_type]): # msg_type can be chat or pm - first = True - - if pm: - nickname = resource - groupchat_control.on_private_message(nickname, message, array[2], - xhtml, session, msg_id) - else: - # array: (jid, msg, time, encrypted, msg_type, subject) - if encrypted: - self.roster.on_message(jid, message, array[2], account, array[3], - msg_type, subject, resource, msg_id, array[9], - advanced_notif_num, session=session, form_node=array[12]) - else: - # xhtml in last element - self.roster.on_message(jid, message, array[2], account, array[3], - msg_type, subject, resource, msg_id, array[9], - advanced_notif_num, xhtml=xhtml, session=session, - form_node=array[12]) - nickname = gajim.get_name_from_jid(account, jid) - # Check and do wanted notifications - msg = message - if subject: - msg = _('Subject: %s') % subject + '\n' + msg - focused = False - if chat_control: - parent_win = chat_control.parent_win - if chat_control == parent_win.get_active_control() and \ - parent_win.window.has_focus: - focused = True - notify.notify('new_message', jid_of_control, account, [msg_type, - first, nickname, msg, focused], advanced_notif_num) - - if self.remote_ctrl: - self.remote_ctrl.raise_signal('NewMessage', (account, array)) - def handle_event_msgerror(self, account, array): #'MSGERROR' (account, (jid, error_code, error_msg, msg, time)) full_jid_with_resource = array[0] @@ -2556,7 +2421,6 @@ class Interface: 'ERROR_ANSWER': self.handle_event_error_answer, 'STATUS': self.handle_event_status, 'NOTIFY': self.handle_event_notify, - 'MSG': self.handle_event_msg, 'MSGERROR': self.handle_event_msgerror, 'MSGSENT': self.handle_event_msgsent, 'MSGNOTSENT': self.handle_event_msgnotsent, diff --git a/src/roster_window.py b/src/roster_window.py index 876d3e81e..485b23cda 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -3929,7 +3929,7 @@ class RosterWindow: # We call this here to avoid race conditions with widget validation chat_control.read_queue() - def new_chat(self, contact, account, resource = None, session = None): + def new_chat(self, session, contact, account, resource = None): # Get target window, create a control, and associate it with the window type_ = message_control.TYPE_CHAT @@ -3945,9 +3945,7 @@ class RosterWindow: mw.new_tab(chat_control) - if len(gajim.events.get_events(account, fjid)): - # We call this here to avoid race conditions with widget validation - chat_control.read_queue() + return chat_control def new_chat_from_jid(self, account, fjid): jid, resource = gajim.get_room_and_nick_from_fjid(fjid) @@ -3963,7 +3961,12 @@ class RosterWindow: resource = resource) if not gajim.interface.msg_win_mgr.has_window(fjid, account): - self.new_chat(contact, account, resource = resource) + session = account.make_new_session(account, fjid) + self.control = self.new_chat(session, contact, account, resource = resource) + + if len(gajim.events.get_events(account, fjid)): + chat_control.read_queue() + mw = gajim.interface.msg_win_mgr.get_window(fjid, account) mw.set_active_tab(fjid, account) mw.window.present() @@ -3983,114 +3986,6 @@ class RosterWindow: is_continued=is_continued) mw.new_tab(gc_control) - def on_message(self, jid, msg, tim, account, encrypted=False, msg_type='', - subject=None, resource='', msg_id=None, user_nick='', - advanced_notif_num=None, xhtml=None, session=None, form_node=None): - '''when we receive a message''' - contact = None - # if chat window will be for specific resource - resource_for_chat = resource - fjid = jid - # Try to catch the contact with correct resource - if resource: - fjid = jid + '/' + resource - contact = gajim.contacts.get_contact(account, jid, resource) - highest_contact = gajim.contacts.get_contact_with_highest_priority( - account, jid) - if not contact: - # If there is another resource, it may be a message from an invisible - # resource - lcontact = gajim.contacts.get_contacts(account, jid) - if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \ - lcontact[0].show != 'offline')) and jid.find('@') > 0: - contact = gajim.contacts.copy_contact(highest_contact) - contact.resource = resource - if resource: - fjid = jid + '/' + resource - contact.priority = 0 - contact.show = 'offline' - contact.status = '' - gajim.contacts.add_contact(account, contact) - - else: - # Default to highest prio - fjid = jid - resource_for_chat = None - contact = highest_contact - if not contact: - # contact is not in roster - contact = self.add_to_not_in_the_roster(account, jid, user_nick) - - path = self.get_path(jid, account) # Try to get line of contact in roster - - # Look for a chat control that has the given resource - ctrl = gajim.interface.msg_win_mgr.get_control(fjid, account) - if not ctrl: - # if not, if message comes from highest prio, get control or open one - # without resource - if highest_contact and contact.resource == highest_contact.resource \ - and not jid == gajim.get_jid_from_account(account): - ctrl = gajim.interface.msg_win_mgr.get_control(jid, account) - fjid = jid - resource_for_chat = None - - # Do we have a queue? - no_queue = len(gajim.events.get_events(account, fjid)) == 0 - - popup = helpers.allow_popup_window(account, advanced_notif_num) - - if msg_type == 'normal' and popup: # it's single message to be autopopuped - dialogs.SingleMessageWindow(account, contact.jid, action='receive', - from_whom=jid, subject=subject, message=msg, resource=resource, - session=session, form_node=form_node) - return - - # We print if window is opened and it's not a single message - if ctrl and msg_type != 'normal': - typ = '' - if msg_type == 'error': - typ = 'status' - if session: - ctrl.set_session(session) - ctrl.print_conversation(msg, typ, tim = tim, encrypted = encrypted, - subject = subject, xhtml = xhtml) - if msg_id: - gajim.logger.set_read_messages([msg_id]) - return - - # We save it in a queue - type_ = 'chat' - event_type = 'message_received' - if msg_type == 'normal': - type_ = 'normal' - event_type = 'single_message_received' - show_in_roster = notify.get_show_in_roster(event_type, account, contact) - show_in_systray = notify.get_show_in_systray(event_type, account, contact) - event = gajim.events.create_event(type_, (msg, subject, msg_type, tim, - encrypted, resource, msg_id, xhtml, session, form_node), - show_in_roster=show_in_roster, show_in_systray=show_in_systray) - gajim.events.add_event(account, fjid, event) - if popup: - if not ctrl: - self.new_chat(contact, account, resource=resource_for_chat) - if path and not self.dragging and gajim.config.get( - 'scroll_roster_to_last_message'): - # we curently see contact in our roster OR he - # is not in the roster at all. - # show and select his line in roster - # do not change selection while DND'ing - self.tree.expand_row(path[0:1], False) - self.tree.expand_row(path[0:2], False) - self.tree.scroll_to_cell(path) - self.tree.set_cursor(path) - else: - if no_queue: # We didn't have a queue: we change icons - self.draw_contact(jid, account) - self.show_title() # we show the * or [n] - # Show contact in roster (if he is invisible for example) and select - # line - self.show_and_select_path(path, jid, account) - def on_preferences_menuitem_activate(self, widget): if gajim.interface.instances.has_key('preferences'): gajim.interface.instances['preferences'].window.present() @@ -4399,7 +4294,7 @@ class RosterWindow: fjid += '/' + resource win = gajim.interface.msg_win_mgr.get_window(fjid, account) if not win: - self.new_chat(contact, account, resource = resource, session = session) + self.new_chat(session, contact, account, resource = resource) win = gajim.interface.msg_win_mgr.get_window(fjid, account) ctrl = win.get_control(fjid, account) # last message is long time ago diff --git a/src/session.py b/src/session.py new file mode 100644 index 000000000..a07a520ff --- /dev/null +++ b/src/session.py @@ -0,0 +1,224 @@ +from common import helpers + +from common import gajim +from common import stanza_session +from common import contacts + +import dialogs + +import message_control + +import notify + +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') + + self.control = None + + def received(self, full_jid_with_resource, message, tim, encrypted, msg_type, subject, chatstate, msg_id, composing_xep, user_nick, xhtml, form_node): + + 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_control(jid, self.conn.name) + if not groupchat_control and \ + jid in gajim.interface.minimized_controls[self.conn.name]: + groupchat_control = self.minimized_controls[self.conn.name][jid] + + pm = False + if groupchat_control and groupchat_control.type_id == \ + message_control.TYPE_GC: + # It's a Private message + pm = True + msg_type = 'pm' + + jid_of_control = full_jid_with_resource + + # Handle chat states + contact = gajim.contacts.get_contact(self.conn.name, jid, 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: + # other peer sent us reply, so he supports jep85 or jep22 + contact.chatstate = chatstate + if contact.our_chatstate == 'ask': # we were jep85 disco? + contact.our_chatstate = 'active' # no more + self.control.handle_incoming_chatstate() + elif contact.chatstate != 'active': + # got no valid jep85 answer, peer does not support it + contact.chatstate = False + elif chatstate == 'active': + # Brand new message, incoming. + contact.our_chatstate = chatstate + contact.chatstate = 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 message: # empty message text + return + + if gajim.config.get('ignore_unknown_contacts') and \ + not gajim.contacts.get_contacts(self.conn.name, jid) and not pm: + return + + if not contact: + # contact is not in the roster, create a fake one to display + # notification + contact = contacts.Contact(jid = jid, resource = resource) + advanced_notif_num = notify.get_advanced_notification('message_received', + self.conn.name, contact) + + # Is it a first or next message received ? + first = False + if not self.control: + first = True + + if pm: + nickname = resource + groupchat_control.on_private_message(nickname, message, array[2], + xhtml, session, msg_id) + else: + self.roster_message(jid, message, tim, encrypted, msg_type, + subject, resource, msg_id, user_nick, advanced_notif_num, + xhtml=xhtml, form_node=form_node) + + nickname = gajim.get_name_from_jid(self.conn.name, jid) + # Check and do wanted notifications + msg = message + if subject: + msg = _('Subject: %s') % subject + '\n' + msg + focused = False + + if self.control: + parent_win = self.control.parent_win + if 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, + 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, message, tim, encrypted, msg_type, subject, chatstate, msg_id, composing_xep, user_nick, xhtml, form_node])) + + def roster_message(self, jid, msg, tim, encrypted=False, msg_type='', + subject=None, resource='', msg_id=None, user_nick='', + advanced_notif_num=None, xhtml=None, form_node=None): + contact = None + # if chat window will be for specific resource + resource_for_chat = resource + + fjid = jid + + # Try to catch the contact with correct resource + if resource: + fjid = jid + '/' + resource + contact = gajim.contacts.get_contact(self.conn.name, jid, resource) + + highest_contact = gajim.contacts.get_contact_with_highest_priority( + self.conn.name, jid) + if not contact: + # If there is another resource, it may be a message from an invisible + # resource + lcontact = gajim.contacts.get_contacts(self.conn.name, jid) + if (len(lcontact) > 1 or (lcontact and lcontact[0].resource and \ + lcontact[0].show != 'offline')) and jid.find('@') > 0: + contact = gajim.contacts.copy_contact(highest_contact) + contact.resource = resource + if resource: + fjid = jid + '/' + resource + contact.priority = 0 + contact.show = 'offline' + contact.status = '' + gajim.contacts.add_contact(self.conn.name, contact) + + else: + # Default to highest prio + fjid = jid + resource_for_chat = None + contact = highest_contact + + if not contact: + # contact is not in roster + contact = gajim.interface.roster.add_to_not_in_the_roster(self.conn.name, jid, user_nick) + + path = gajim.interface.roster.get_path(jid, self.conn.name) # Try to get line of contact in roster + + # Do we have a queue? + no_queue = len(gajim.events.get_events(self.conn.name, fjid)) == 0 + + popup = helpers.allow_popup_window(self.conn.name, advanced_notif_num) + + if msg_type == 'normal' and popup: # it's single message to be autopopuped + dialogs.SingleMessageWindow(self.conn.name, contact.jid, action='receive', + from_whom=jid, subject=subject, message=msg, resource=resource, + session=self, form_node=form_node) + return + + # We print if window is opened and it's not a single message + if self.control and msg_type != 'normal': + typ = '' + + if msg_type == 'error': + typ = 'status' + + self.control.print_conversation(msg, typ, tim = tim, + encrypted = encrypted, subject = subject, xhtml = xhtml) + + if msg_id: + gajim.logger.set_read_messages([msg_id]) + + return + + # We save it in a queue + type_ = 'chat' + event_type = 'message_received' + + if msg_type == 'normal': + type_ = 'normal' + event_type = 'single_message_received' + + show_in_roster = notify.get_show_in_roster(event_type, self.conn.name, contact) + show_in_systray = notify.get_show_in_systray(event_type, self.conn.name, contact) + + event = gajim.events.create_event(type_, (msg, subject, msg_type, tim, + encrypted, resource, msg_id, xhtml, self, form_node), + show_in_roster=show_in_roster, show_in_systray=show_in_systray) + + gajim.events.add_event(self.conn.name, fjid, event) + + if popup: + if not self.control: + self.control = self.new_chat(self, contact, self.conn.name, resource=resource_for_chat) + + if len(gajim.events.get_events(self.conn.name, fjid)): + self.control.read_queue() + + if path and not gajim.interface.dragging and gajim.config.get( + 'scroll_roster_to_last_message'): + # we curently see contact in our roster OR he + # is not in the roster at all. + # show and select his line in roster + # do not change selection while DND'ing + self.tree.expand_row(path[0:1], False) + self.tree.expand_row(path[0:2], False) + self.tree.scroll_to_cell(path) + self.tree.set_cursor(path) + else: + if no_queue: # We didn't have a queue: we change icons + gajim.interface.roster.draw_contact(jid, self.conn.name) + + gajim.interface.roster.show_title() # we show the * or [n] + # Show contact in roster (if he is invisible for example) and select + # line + gajim.interface.roster.show_and_select_path(path, jid, self.conn.name)