diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index b66be496a..1164fec77 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -1115,6 +1115,8 @@ ConnectionJingle, ConnectionIBBytestream): self._nec_gmail_new_mail_received) gajim.ged.register_event_handler('ping-received', ged.CORE, self._nec_ping_received) + gajim.ged.register_event_handler('presence-received', ged.CORE, + self._nec_presence_received) def build_http_auth_answer(self, iq_obj, answer): if not self.connection or self.connected < 2: @@ -1743,6 +1745,131 @@ ConnectionJingle, ConnectionIBBytestream): gajim.nec.push_incoming_event(NetworkEvent('raw-pres-received', conn=self, iq_obj=prs)) + def _nec_presence_received(self, obj): + account = obj.conn.name + jid = obj.jid + resource = obj.resource or '' + + statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd', + 'invisible'] + obj.old_show = 0 + obj.new_show = statuss.index(obj.show) + + obj.contact_list = [] + + highest = gajim.contacts.get_contact_with_highest_priority(account, jid) + obj.was_highest = (highest and highest.resource == resource) + + # Update contact + obj.contact_list = gajim.contacts.get_contacts(account, jid) + obj.contact = None + resources = [] + for c in obj.contact_list: + resources.append(c.resource) + if c.resource == resource: + obj.contact = c + break + + if obj.contact: + obj.old_show = statuss.index(obj.contact.show) + # nick changed + if obj.contact_nickname is not None and \ + obj.contact.contact_name != obj.contact_nickname: + obj.contact.contact_name = obj.contact_nickname + obj.need_redraw = True +# gajim.interface.roster.draw_contact(jid, account) + + if obj.old_show == obj.new_show and obj.contact.status == \ + obj.status and obj.contact.priority == obj.prio: # no change + return + else: + obj.contact = gajim.contacts.get_first_contact_from_jid(account, + jid) + if not obj.contact: + # Presence of another resource of our jid + # Create self contact and add to roster + if resource == obj.conn.server_resource: + return + # Ignore offline presence of unknown self resource + if obj.new_show < 2: + return + obj.contact = gajim.contacts.create_self_contact(jid=jid, + account=account, show=obj.show, status=obj.status, + priority=obj.prio, keyID=obj.keyID, + resource=obj.resource) + gajim.contacts.add_contact(account, obj.contact) + obj.contact_list.append(obj.contact) + else: + obj.old_show = statuss.index(obj.contact.show) + if (resources != [''] and (len(obj.contact_list) != 1 or \ + obj.contact_list[0].show != 'offline')) and \ + not gajim.jid_is_transport(jid): + # Another resource of an existing contact connected + obj.old_show = 0 + obj.contact = gajim.contacts.copy_contact(obj.contact) + obj.contact_list.append(obj.contact) + obj.contact.resource = resource + + obj.need_add_in_roster = True +# gajim.interface.roster.add_contact(jid, account) + + if not gajim.jid_is_transport(jid) and len(obj.contact_list) == 1: + # It's not an agent + if obj.old_show == 0 and obj.new_show > 1: + if not jid in gajim.newly_added[account]: + gajim.newly_added[account].append(jid) + if jid in gajim.to_be_removed[account]: + gajim.to_be_removed[account].remove(jid) + elif obj.old_show > 1 and obj.new_show == 0 and \ + obj.conn.connected > 1: + if not jid in gajim.to_be_removed[account]: + gajim.to_be_removed[account].append(jid) + if jid in gajim.newly_added[account]: + gajim.newly_added[account].remove(jid) + obj.need_redraw = True +# self.roster.draw_contact(jid, account) + + obj.contact.show = obj.show + obj.contact.status = obj.status + obj.contact.priority = obj.prio + obj.contact.keyID = obj.keyID + if obj.timestamp: + obj.contact.last_status_time = obj.timestamp + elif not gajim.block_signed_in_notifications[account]: + # We're connected since more that 30 seconds + obj.contact.last_status_time = localtime() + obj.contact.contact_nickname = obj.contact_nickname + + if gajim.jid_is_transport(jid): + return + + # It isn't an agent + # reset chatstate if needed: + # (when contact signs out or has errors) + if obj.show in ('offline', 'error'): + obj.contact.our_chatstate = obj.contact.chatstate = \ + obj.contact.composing_xep = None + + # TODO: This causes problems when another + # resource signs off! + self.stop_all_active_file_transfers(obj.contact) + + # disable encryption, since if any messages are + # lost they'll be not decryptable (note that + # this contradicts XEP-0201 - trying to get that + # in the XEP, though) + + # there won't be any sessions here if the contact terminated + # their sessions before going offline (which we do) + for sess in self.get_sessions(jid): + if obj.fjid != str(sess.jid): + continue + if sess.control: + sess.control.no_autonegotiation = False + if sess.enable_encryption: + sess.terminate_e2e() + self.delete_session(jid, sess.thread_id) + def _StanzaArrivedCB(self, con, obj): self.last_io = gajim.idlequeue.current_time() diff --git a/src/common/connection_handlers_events.py b/src/common/connection_handlers_events.py index aa9d86b25..c7f6377db 100644 --- a/src/common/connection_handlers_events.py +++ b/src/common/connection_handlers_events.py @@ -604,6 +604,10 @@ class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): def generate(self): self.conn = self.base_event.conn self.iq_obj = self.base_event.iq_obj + + self.need_add_in_roster = False + self.need_redraw = False + self.ptype = self.iq_obj.getType() if self.ptype == 'available': self.ptype = None @@ -627,6 +631,10 @@ class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): _('Nickname not allowed: %s') % resource, None, False, None, [])) return + jid_list = gajim.contacts.get_jid_list(self.conn.name) +# if self.jid not in jid_list and self.jid != gajim.get_jid_from_account( +# self.conn.name): +# return self.timestamp = None self.get_id() self.is_gc = False # is it a GC presence ? @@ -957,5 +965,5 @@ class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.conn.server_resource: # We got our own presence self.conn.dispatch('STATUS', self.show) - else: + elif self.jid in jid_list: return True diff --git a/src/dialogs.py b/src/dialogs.py index e9ca65d96..f2003e768 100644 --- a/src/dialogs.py +++ b/src/dialogs.py @@ -995,12 +995,17 @@ _('Please fill in the data of the contact you want to add in account %s') % acco message_buffer.set_text(helpers.get_subscription_request_msg( self.account)) + gajim.ged.register_event_handler('presence-received', ged.GUI1, + self._nec_presence_received) + def on_add_new_contact_window_destroy(self, widget): if self.account: location = gajim.interface.instances[self.account] else: location = gajim.interface.instances del location['add_contact'] + gajim.ged.remove_event_handler('presence-received', ged.GUI1, + self._nec_presence_received) def on_register_button_clicked(self, widget): jid = self.protocol_jid_combobox.get_active_text().decode('utf-8') @@ -1154,6 +1159,14 @@ _('Please fill in the data of the contact you want to add in account %s') % acco self.connected_label.show() self.add_button.set_sensitive(False) + def _nec_presence_received(self, obj): + if gajim.jid_is_transport(obj.jid): + if obj.old_show == 0 and obj.new_show > 1: + self.transport_signed_in(obj.jid) + elif obj.old_show > 1 and obj.new_show == 0: + self.transport_signed_out(obj.jid) + + class AboutDialog: """ Class for about dialog diff --git a/src/gui_interface.py b/src/gui_interface.py index 6b1f8af4a..a99fe0885 100644 --- a/src/gui_interface.py +++ b/src/gui_interface.py @@ -290,249 +290,76 @@ class Interface: # # Contact changed show - # FIXME: Drop and rewrite... - - statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd', - 'invisible'] - account = obj.conn.name jid = obj.jid show = obj.show status = obj.status resource = obj.resource or '' - priority = obj.prio - keyID = obj.keyID - timestamp = obj.timestamp - contact_nickname = obj.contact_nickname - obj.old_show = 0 - obj.new_show = statuss.index(show) - - lcontact = [] - - highest = gajim.contacts.get_contact_with_highest_priority(account, jid) - was_highest = (highest and highest.resource == resource) - - # Update contact jid_list = gajim.contacts.get_jid_list(account) - if jid in jid_list or jid == gajim.get_jid_from_account(account): - lcontact = gajim.contacts.get_contacts(account, jid) - contact1 = None - resources = [] - for c in lcontact: - resources.append(c.resource) - if c.resource == resource: - contact1 = c - break - if contact1: - if contact1.show in statuss: - obj.old_show = statuss.index(contact1.show) - # nick changed - if contact_nickname is not None and \ - contact1.contact_name != contact_nickname: - contact1.contact_name = contact_nickname - self.roster.draw_contact(jid, account) - - if obj.old_show == obj.new_show and contact1.status == status \ - and contact1.priority == priority: # no change - return - else: - contact1 = gajim.contacts.get_first_contact_from_jid(account, - jid) - if not contact1: - # Presence of another resource of our - # jid - # Create self contact and add to roster - if resource == obj.conn.server_resource: - return - # Ignore offline presence of unknown self resource - if obj.new_show < 2: - return - contact1 = gajim.contacts.create_self_contact(jid=jid, - account=account, show=show, status=status, - priority=priority, keyID=keyID, resource=resource) - obj.old_show = 0 - gajim.contacts.add_contact(account, contact1) - lcontact.append(contact1) - elif contact1.show in statuss: - obj.old_show = statuss.index(contact1.show) - if (resources != [''] and (len(lcontact) != 1 or \ - lcontact[0].show != 'offline')) and \ - not gajim.jid_is_transport(jid): - # Another resource of an existing contact connected - obj.old_show = 0 - contact1 = gajim.contacts.copy_contact(contact1) - lcontact.append(contact1) - contact1.resource = resource - - self.roster.add_contact(contact1.jid, account) - - if not gajim.jid_is_transport(contact1.jid) and len(lcontact) == 1: - # It's not an agent - if obj.old_show == 0 and obj.new_show > 1: - if not contact1.jid in gajim.newly_added[account]: - gajim.newly_added[account].append(contact1.jid) - if contact1.jid in gajim.to_be_removed[account]: - gajim.to_be_removed[account].remove(contact1.jid) - gobject.timeout_add_seconds(5, - self.roster.remove_newly_added, contact1.jid, account) - elif obj.old_show > 1 and obj.new_show == 0 and \ - obj.conn.connected > 1: - if not contact1.jid in gajim.to_be_removed[account]: - gajim.to_be_removed[account].append(contact1.jid) - if contact1.jid in gajim.newly_added[account]: - gajim.newly_added[account].remove(contact1.jid) - self.roster.draw_contact(contact1.jid, account) - gobject.timeout_add_seconds(5, - self.roster.remove_to_be_removed, contact1.jid, account) - - # unset custom status - if (obj.old_show == 0 and obj.new_show > 1) or \ - (obj.old_show > 1 and obj.new_show == 0 and obj.conn.connected > 1): - if account in self.status_sent_to_users and \ - jid in self.status_sent_to_users[account]: - del self.status_sent_to_users[account][jid] - - contact1.show = show - contact1.status = status - contact1.priority = priority - contact1.keyID = keyID - if timestamp: - contact1.last_status_time = timestamp - elif not gajim.block_signed_in_notifications[account]: - # We're connected since more that 30 seconds - contact1.last_status_time = time.localtime() - contact1.contact_nickname = contact_nickname + # unset custom status + if (obj.old_show == 0 and obj.new_show > 1) or \ + (obj.old_show > 1 and obj.new_show == 0 and obj.conn.connected > 1): + if account in self.status_sent_to_users and \ + jid in self.status_sent_to_users[account]: + del self.status_sent_to_users[account][jid] if gajim.jid_is_transport(jid): # It must be an agent - if jid in jid_list: - # Update existing iter and group counting - self.roster.draw_contact(jid, account) - self.roster.draw_group(_('Transports'), account) - if obj.new_show > 1 and jid in gajim.transport_avatar[account]: - # transport just signed in. - # request avatars - for jid_ in gajim.transport_avatar[account][jid]: - obj.conn.request_vcard(jid_) - # transport just signed in/out, don't show - # popup notifications for 30s - account_jid = account + '/' + jid - gajim.block_signed_in_notifications[account_jid] = True - gobject.timeout_add_seconds(30, - self.unblock_signed_in_notifications, account_jid) - locations = (self.instances, self.instances[account]) - for location in locations: - if 'add_contact' in location: - if obj.old_show == 0 and obj.new_show > 1: - location['add_contact'].transport_signed_in(jid) - break - elif obj.old_show > 1 and obj.new_show == 0: - location['add_contact'].transport_signed_out(jid) - break - elif jid in jid_list: - # It isn't an agent - # reset chatstate if needed: - # (when contact signs out or has errors) - if show in ('offline', 'error'): - contact1.our_chatstate = contact1.chatstate = \ - contact1.composing_xep = None - # TODO: This causes problems when another - # resource signs off! - obj.conn.stop_all_active_file_transfers(contact1) + # transport just signed in/out, don't show + # popup notifications for 30s + account_jid = account + '/' + jid + gajim.block_signed_in_notifications[account_jid] = True + gobject.timeout_add_seconds(30, + self.unblock_signed_in_notifications, account_jid) - # disable encryption, since if any messages are - # lost they'll be not decryptable (note that - # this contradicts XEP-0201 - trying to get that - # in the XEP, though) - - # there won't be any sessions here if the contact terminated - # their sessions before going offline (which we do) - for sess in obj.conn.get_sessions(jid): - if obj.fjid != str(sess.jid): - continue - if sess.control: - sess.control.no_autonegotiation = False - if sess.enable_encryption: - sess.terminate_e2e() - obj.conn.delete_session(jid, sess.thread_id) - - self.roster.chg_contact_status(contact1, show, status, account) - # Notifications - if obj.old_show < 2 and obj.new_show > 1: - show_notif = True - for c in lcontact: - if c.resource == resource: - # we look for other connected resources - continue - if c.show not in ('offline', 'error'): - show_notif = False - break - if show_notif: - # no other resource is connected, let's look in metacontacts - family = gajim.contacts.get_metacontacts_family(account, - jid) - for info in family: - acct_ = info['account'] - jid_ = info['jid'] - c_ = gajim.contacts.get_contact_with_highest_priority( - acct_, jid_) - if not c_: - continue - if c_.show not in ('offline', 'error'): - show_notif = False - break - if show_notif: - notify.notify('contact_connected', jid, account, - status) - - elif obj.old_show > 1 and obj.new_show < 2: - show_notif = True - for c in lcontact: - if c.resource == resource: - # we look for other connected resources - continue - if c.show not in ('offline', 'error'): - show_notif = False - break - if show_notif: - # no other resource is connected, let's look in metacontacts - family = gajim.contacts.get_metacontacts_family(account, - jid) - for info in family: - acct_ = info['account'] - jid_ = info['jid'] - c_ = gajim.contacts.get_contact_with_highest_priority( - acct_, jid_) - if not c_: - continue - if c_.show not in ('offline', 'error'): - show_notif = False - break - if show_notif: - notify.notify('contact_disconnected', jid, account, status) - # Status change (not connected/disconnected or - # error (<1)) - elif obj.new_show > 1: - notify.notify('status_change', jid, account, [obj.new_show, - status]) else: - # FIXME: MSN transport (CMSN1.2.1 and PyMSN) don't - # follow the XEP, still the case in 2008. - # It's maybe a GC_NOTIFY (specialy for MSN gc) - self.handle_event_gc_notify(account, (jid, show, status, - resource, None, None, None, None, None, [], None, None)) + # It isn't an agent + # Notifications + obj.show_notif = True + for c in obj.contact_list: + if c.resource == resource: + # we look for other connected resources + continue + if c.show not in ('offline', 'error'): + obj.show_notif = False + break + if obj.show_notif: + # no other resource is connected, let's look in metacontacts + family = gajim.contacts.get_metacontacts_family(account, + jid) + for info in family: + acct_ = info['account'] + jid_ = info['jid'] + c_ = gajim.contacts.get_contact_with_highest_priority( + acct_, jid_) + if not c_: + continue + if c_.show not in ('offline', 'error'): + obj.show_notif = False + break + if show_notif: + if obj.old_show < 2 and obj.new_show > 1: + notify.notify('contact_connected', jid, account, status) + + elif obj.old_show > 1 and obj.new_show < 2: + notify.notify('contact_disconnected', jid, account, status) + # Status change (not connected/disconnected or + # error (<1)) + elif obj.new_show > 1: + notify.notify('status_change', jid, account, [obj.new_show, + status]) highest = gajim.contacts.get_contact_with_highest_priority(account, jid) is_highest = (highest and highest.resource == resource) # disconnect the session from the ctrl if the highest resource has # changed - if (was_highest and not is_highest) or (not was_highest and is_highest): + if (obj.was_highest and not is_highest) or \ + (not obj.was_highest and is_highest): ctrl = self.msg_win_mgr.get_control(jid, account) - if ctrl: ctrl.no_autonegotiation = False ctrl.set_session(None) diff --git a/src/roster_window.py b/src/roster_window.py index 049647c2a..372252827 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -62,6 +62,7 @@ from common.exceptions import GajimGeneralException from common import i18n from common import pep from common import location_listener +from common import ged from message_window import MessageWindowMgr @@ -1382,7 +1383,7 @@ class RosterWindow: self.tree.set_model(None) # disable sorting self.model.set_sort_column_id(-2, gtk.SORT_ASCENDING) - + def _after_fill(self): self.model.set_sort_column_id(1, gtk.SORT_ASCENDING) self.tree.set_model(self.modelfilter) @@ -1411,7 +1412,7 @@ class RosterWindow: self._iters = {} # for merged mode self._iters['MERGED'] = {'account': None, 'groups': {}} - + for acct in gajim.contacts.get_accounts(): self._iters[acct] = {'account': None, 'groups': {}, 'contacts': {}} self.add_account(acct) @@ -2437,6 +2438,41 @@ class RosterWindow: else: on_continue('', None) + def _nec_presence_received(self, obj): + account = obj.conn.name + jid = obj.jid + + if obj.need_add_in_roster: + self.add_contact(jid, account) + + jid_list = gajim.contacts.get_jid_list(account) + if jid in jid_list or jid == gajim.get_jid_from_account(account): + if not gajim.jid_is_transport(jid) and len(obj.contact_list) == 1: + if obj.old_show == 0 and obj.new_show > 1: + gobject.timeout_add_seconds(5, self.remove_newly_added, jid, + account) + elif obj.old_show > 1 and obj.new_show == 0 and \ + obj.conn.connected > 1: + gobject.timeout_add_seconds(5, self.remove_to_be_removed, + jid, account) + + if obj.need_redraw: + self.draw_contact(jid, account) + + if gajim.jid_is_transport(jid) and jid in jid_list: + # It must be an agent + # Update existing iter and group counting + self.draw_contact(jid, account) + self.draw_group(_('Transports'), account) + if obj.new_show > 1 and jid in gajim.transport_avatar[account]: + # transport just signed in. + # request avatars + for jid_ in gajim.transport_avatar[account][jid]: + obj.conn.request_vcard(jid_) + + self.chg_contact_status(obj.contact, obj.show, obj.status, account) + + ################################################################################ ### Menu and GUI callbacks ### FIXME: order callbacks in itself... @@ -6148,3 +6184,6 @@ class RosterWindow: accel_group = gtk.accel_groups_from_object(self.window)[0] accel_group.connect_group(gtk.keysyms.j, gtk.gdk.CONTROL_MASK, gtk.ACCEL_MASK, self.on_ctrl_j) + + gajim.ged.register_event_handler('presence-received', ged.GUI1, + self._nec_presence_received) \ No newline at end of file