diff --git a/gajim/common/connection.py b/gajim/common/connection.py index d5cfede73..bcb75b1d3 100644 --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -380,43 +380,6 @@ class CommonConnection: additional_data=obj.additional_data, stanza_id=obj.stanza_id) - def ack_subscribed(self, jid): - """ - To be implemented by derived classes - """ - raise NotImplementedError - - def ack_unsubscribed(self, jid): - """ - To be implemented by derived classes - """ - raise NotImplementedError - - def request_subscription(self, jid, msg='', name='', groups=None, - auto_auth=False): - """ - To be implemented by derived classes - """ - raise NotImplementedError - - def send_authorization(self, jid): - """ - To be implemented by derived classes - """ - raise NotImplementedError - - def refuse_authorization(self, jid): - """ - To be implemented by derived classes - """ - raise NotImplementedError - - def unsubscribe(self, jid, remove_auth = True): - """ - To be implemented by derived classes - """ - raise NotImplementedError - def unsubscribe_agent(self, agent): """ To be implemented by derived classes @@ -585,7 +548,6 @@ class Connection(CommonConnection, ConnectionHandlers): self.on_connect_success = None self.on_connect_failure = None self.retrycount = 0 - self.jids_for_auto_auth = [] # list of jid to auto-authorize self.available_transports = {} # list of available transports on this # server {'icq': ['icq.server.com', 'icq2.server.com'], } self.private_storage_supported = True @@ -1651,75 +1613,6 @@ class Connection(CommonConnection, ConnectionHandlers): return self.connection.send(stanza) - def ack_subscribed(self, jid): - if not app.account_is_connected(self.name): - return - log.debug('ack\'ing subscription complete for %s' % jid) - p = nbxmpp.Presence(jid, 'subscribe') - self.connection.send(p) - - def ack_unsubscribed(self, jid): - if not app.account_is_connected(self.name): - return - log.debug('ack\'ing unsubscription complete for %s' % jid) - p = nbxmpp.Presence(jid, 'unsubscribe') - self.connection.send(p) - - def request_subscription(self, jid, msg='', name='', groups=None, - auto_auth=False, user_nick=''): - if not app.account_is_connected(self.name): - return - if groups is None: - groups = [] - log.debug('subscription request for %s' % jid) - if auto_auth: - self.jids_for_auto_auth.append(jid) - # RFC 3921 section 8.2 - infos = {'jid': jid} - if name: - infos['name'] = name - iq = nbxmpp.Iq('set', nbxmpp.NS_ROSTER) - q = iq.setQuery() - item = q.addChild('item', attrs=infos) - for g in groups: - item.addChild('group').setData(g) - self.connection.send(iq) - - p = nbxmpp.Presence(jid, 'subscribe') - if user_nick: - p.setTag('nick', namespace = nbxmpp.NS_NICK).setData(user_nick) - p = self.add_sha(p) - if msg: - p.setStatus(msg) - self.connection.send(p) - - def send_authorization(self, jid): - if not app.account_is_connected(self.name): - return - p = nbxmpp.Presence(jid, 'subscribed') - p = self.add_sha(p) - self.connection.send(p) - - def refuse_authorization(self, jid): - if not app.account_is_connected(self.name): - return - p = nbxmpp.Presence(jid, 'unsubscribed') - p = self.add_sha(p) - self.connection.send(p) - - def unsubscribe(self, jid, remove_auth = True): - if not app.account_is_connected(self.name): - return - if remove_auth: - self.connection.getRoster().delItem(jid) - jid_list = app.config.get_per('contacts') - for j in jid_list: - if j.startswith(jid): - app.config.del_per('contacts', j) - else: - self.connection.getRoster().Unsubscribe(jid) - self.update_contact(jid, '', []) - def unsubscribe_agent(self, agent): if not app.account_is_connected(self.name): return diff --git a/gajim/common/connection_handlers.py b/gajim/common/connection_handlers.py index e3e4938a3..44a5580e5 100644 --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -78,7 +78,7 @@ class ConnectionDisco: if resp.getType() == 'result': app.nec.push_incoming_event(InformationEvent( None, dialog_name='agent-register-success', args=agent)) - self.request_subscription(agent, auto_auth=True) + self.get_module('Presence').subscribe(agent, auto_auth=True) self.agent_registrations[agent]['roster_push'] = True if self.agent_registrations[agent]['sub_received']: p = nbxmpp.Presence(agent, 'subscribed') @@ -121,9 +121,6 @@ class ConnectionHandlersBase: # List of IDs that will produce a timeout is answer doesn't arrive # {time_of_the_timeout: (id, message to send to gui), } self.awaiting_timeouts = {} - # keep the jids we auto added (transports contacts) to not send the - # SUBSCRIBED event to gui - self.automatically_added = [] # keep track of sessions this connection has with other JIDs self.sessions = {} @@ -461,9 +458,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, ConnectionJingle.__init__(self) ConnectionHandlersBase.__init__(self) - # keep the latest subscribed event for each jid to prevent loop when we - # acknowledge presences - self.subscribed_events = {} # IDs of disco#items requests self.disco_items_ids = [] # IDs of disco#info requests @@ -478,16 +472,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, ged.CORE, self._nec_roster_set_received) app.ged.register_event_handler('roster-received', ged.CORE, self._nec_roster_received) - app.ged.register_event_handler('subscribe-presence-received', - ged.CORE, self._nec_subscribe_presence_received) - app.ged.register_event_handler('subscribed-presence-received', - ged.CORE, self._nec_subscribed_presence_received) - app.ged.register_event_handler('subscribed-presence-received', - ged.POSTGUI, self._nec_subscribed_presence_received_end) - app.ged.register_event_handler('unsubscribed-presence-received', - ged.CORE, self._nec_unsubscribed_presence_received) - app.ged.register_event_handler('unsubscribed-presence-received', - ged.POSTGUI, self._nec_unsubscribed_presence_received_end) app.ged.register_event_handler('agent-removed', ged.CORE, self._nec_agent_removed) @@ -497,16 +481,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, ged.CORE, self._nec_roster_set_received) app.ged.remove_event_handler('roster-received', ged.CORE, self._nec_roster_received) - app.ged.remove_event_handler('subscribe-presence-received', - ged.CORE, self._nec_subscribe_presence_received) - app.ged.remove_event_handler('subscribed-presence-received', - ged.CORE, self._nec_subscribed_presence_received) - app.ged.remove_event_handler('subscribed-presence-received', - ged.POSTGUI, self._nec_subscribed_presence_received_end) - app.ged.remove_event_handler('unsubscribed-presence-received', - ged.CORE, self._nec_unsubscribed_presence_received) - app.ged.remove_event_handler('unsubscribed-presence-received', - ged.POSTGUI, self._nec_unsubscribed_presence_received_end) app.ged.remove_event_handler('agent-removed', ged.CORE, self._nec_agent_removed) @@ -671,101 +645,13 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, self.connection.SendAndCallForResponse(iq, self._on_bob_received, {'cid': cid}) - def _nec_subscribe_presence_received(self, obj): - account = obj.conn.name - if account != self.name: - return - if app.jid_is_transport(obj.fjid) and obj.fjid in \ - self.agent_registrations: - self.agent_registrations[obj.fjid]['sub_received'] = True - if not self.agent_registrations[obj.fjid]['roster_push']: - # We'll reply after roster push result - return True - if app.config.get_per('accounts', self.name, 'autoauth') or \ - app.jid_is_transport(obj.fjid) or obj.jid in self.jids_for_auto_auth \ - or obj.transport_auto_auth: - if self.connection: - p = nbxmpp.Presence(obj.fjid, 'subscribed') - p = self.add_sha(p) - self.connection.send(p) - if app.jid_is_transport(obj.fjid) or obj.transport_auto_auth: - #TODO!?!? - #self.show = 'offline' - #self.status = 'offline' - #emit NOTIFY - pass - if obj.transport_auto_auth: - self.automatically_added.append(obj.jid) - self.request_subscription(obj.jid, name=obj.user_nick) - return True - if not obj.status: - obj.status = _('I would like to add you to my roster.') - - def _nec_subscribed_presence_received(self, obj): - account = obj.conn.name - if account != self.name: - return - # BE CAREFUL: no con.updateRosterItem() in a callback - if obj.jid in self.automatically_added: - self.automatically_added.remove(obj.jid) - return True - # detect a subscription loop - if obj.jid not in self.subscribed_events: - self.subscribed_events[obj.jid] = [] - self.subscribed_events[obj.jid].append(time_time()) - block = False - if len(self.subscribed_events[obj.jid]) > 5: - if time_time() - self.subscribed_events[obj.jid][0] < 5: - block = True - self.subscribed_events[obj.jid] = \ - self.subscribed_events[obj.jid][1:] - if block: - app.config.set_per('account', self.name, 'dont_ack_subscription', - True) - return True - - def _nec_subscribed_presence_received_end(self, obj): - account = obj.conn.name - if account != self.name: - return - if not app.config.get_per('accounts', account, - 'dont_ack_subscription'): - self.ack_subscribed(obj.jid) - - def _nec_unsubscribed_presence_received(self, obj): - account = obj.conn.name - if account != self.name: - return - # detect a unsubscription loop - if obj.jid not in self.subscribed_events: - self.subscribed_events[obj.jid] = [] - self.subscribed_events[obj.jid].append(time_time()) - block = False - if len(self.subscribed_events[obj.jid]) > 5: - if time_time() - self.subscribed_events[obj.jid][0] < 5: - block = True - self.subscribed_events[obj.jid] = \ - self.subscribed_events[obj.jid][1:] - if block: - app.config.set_per('account', self.name, 'dont_ack_subscription', - True) - return True - - def _nec_unsubscribed_presence_received_end(self, obj): - account = obj.conn.name - if account != self.name: - return - if not app.config.get_per('accounts', account, - 'dont_ack_subscription'): - self.ack_unsubscribed(obj.jid) - def _nec_agent_removed(self, obj): if obj.conn.name != self.name: return for jid in obj.jid_list: log.debug('Removing contact %s due to unregistered transport %s' % \ (jid, obj.agent)) - self.unsubscribe(jid) + self.get_module('Presence').unsubscribe(jid) # Transport contacts can't have 2 resources if jid in app.to_be_removed[self.name]: # This way we'll really remove it diff --git a/gajim/common/connection_handlers_events.py b/gajim/common/connection_handlers_events.py index 2b5edd06c..f65b21ab3 100644 --- a/gajim/common/connection_handlers_events.py +++ b/gajim/common/connection_handlers_events.py @@ -383,12 +383,6 @@ PresenceHelperEvent): elif namespace == nbxmpp.NS_DELAY and not self.timestamp: # XEP-0091 self._generate_timestamp(self.stanza.timestamp) - elif namespace == 'http://delx.cjb.net/protocol/roster-subsync': - # see http://trac.gajim.org/ticket/326 - agent = app.get_server_from_jid(self.jid) - if self.conn.connection.getRoster().getItem(agent): - # to be sure it's a transport contact - self.transport_auto_auth = True if not self.is_gc and self.id_ and self.id_.startswith('gajim_muc_') \ and self.ptype == 'error': @@ -413,19 +407,7 @@ PresenceHelperEvent): presence_obj=self)) return - if self.ptype == 'subscribe': - app.nec.push_incoming_event(SubscribePresenceReceivedEvent(None, - conn=self.conn, stanza=self.stanza, presence_obj=self)) - elif self.ptype == 'subscribed': - # BE CAREFUL: no con.updateRosterItem() in a callback - app.nec.push_incoming_event(SubscribedPresenceReceivedEvent(None, - conn=self.conn, stanza=self.stanza, presence_obj=self)) - elif self.ptype == 'unsubscribe': - log.debug(_('unsubscribe request from %s') % self.jid) - elif self.ptype == 'unsubscribed': - app.nec.push_incoming_event(UnsubscribedPresenceReceivedEvent( - None, conn=self.conn, stanza=self.stanza, presence_obj=self)) - elif self.ptype == 'error': + if self.ptype == 'error': return if not self.ptype or self.ptype == 'unavailable': @@ -552,35 +534,6 @@ class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.new_nick = self.stanza.getNewNick() return True -class SubscribePresenceReceivedEvent(nec.NetworkIncomingEvent): - name = 'subscribe-presence-received' - base_network_events = [] - - def generate(self): - self.jid = self.presence_obj.jid - self.fjid = self.presence_obj.fjid - self.status = self.presence_obj.status - self.transport_auto_auth = self.presence_obj.transport_auto_auth - self.user_nick = self.presence_obj.user_nick - return True - -class SubscribedPresenceReceivedEvent(nec.NetworkIncomingEvent): - name = 'subscribed-presence-received' - base_network_events = [] - - def generate(self): - self.jid = self.presence_obj.jid - self.resource = self.presence_obj.resource - return True - -class UnsubscribedPresenceReceivedEvent(nec.NetworkIncomingEvent): - name = 'unsubscribed-presence-received' - base_network_events = [] - - def generate(self): - self.jid = self.presence_obj.jid - return True - class OurShowEvent(nec.NetworkIncomingEvent): name = 'our-show' base_network_events = [] diff --git a/gajim/common/modules/presence.py b/gajim/common/modules/presence.py index a57e3b765..39f2a87d1 100644 --- a/gajim/common/modules/presence.py +++ b/gajim/common/modules/presence.py @@ -16,8 +16,11 @@ import logging +import nbxmpp + from gajim.common import app from gajim.common.nec import NetworkEvent +from gajim.common.modules.user_nickname import parse_nickname log = logging.getLogger('gajim.c.m.presence') @@ -27,15 +30,171 @@ class Presence: self._con = con self._account = con.name - self.handlers = [('presence', self._presence_received)] + self.handlers = [ + ('presence', self._presence_received), + ('presence', self._subscribe_received, 'subscribe'), + ('presence', self._subscribed_received, 'subscribed'), + ('presence', self._unsubscribe_received, 'unsubscribe'), + ('presence', self._unsubscribed_received, 'unsubscribed'), + ] + + # keep the jids we auto added (transports contacts) to not send the + # SUBSCRIBED event to GUI + self.automatically_added = [] + + # list of jid to auto-authorize + self.jids_for_auto_auth = [] def _presence_received(self, con, stanza): + if stanza.getType() in ('subscribe', 'subscribed', + 'unsubscribe', 'unsubscribed'): + # Dont handle that here + return + log.info('Received from %s', stanza.getFrom()) app.nec.push_incoming_event( NetworkEvent('raw-pres-received', conn=self._con, stanza=stanza)) + def _subscribe_received(self, con, stanza): + from_ = stanza.getFrom() + jid = from_.getStripped() + fjid = str(from_) + status = stanza.getStatus() + is_transport = app.jid_is_transport(fjid) + auto_auth = app.config.get_per('accounts', self._account, 'autoauth') + user_nick = parse_nickname(stanza) + + log.info('Received Subscribe: %s, transport: %s, auto_auth: %s, ' + 'user_nick: %s', from_, is_transport, auto_auth, user_nick) + if is_transport and fjid in self._con.agent_registrations: + self._con.agent_registrations[fjid]['sub_received'] = True + if not self.agent_registrations[fjid]['roster_push']: + # We'll reply after roster push result + raise nbxmpp.NodeProcessed + + if auto_auth or is_transport or jid in self.jids_for_auto_auth: + presence = nbxmpp.Presence(fjid, 'subscribed') + presence = self._add_sha(presence) + self._con.connection.send(presence) + + if not status: + status = _('I would like to add you to my roster.') + + app.nec.push_incoming_event(NetworkEvent( + 'subscribe-presence-received', + conn=self._con, + jid=jid, + fjid=fjid, + status=status, + user_nick=user_nick, + is_transport=is_transport)) + + raise nbxmpp.NodeProcessed + + def _subscribed_received(self, con, stanza): + from_ = stanza.getFrom() + jid = from_.getStripped() + resource = from_.getResource() + log.info('Received Subscribed: %s', from_) + if jid in self.automatically_added: + self.automatically_added.remove(jid) + raise nbxmpp.NodeProcessed + + app.nec.push_incoming_event(NetworkEvent( + 'subscribed-presence-received', + conn=self._con, jid=jid, resource=resource)) + raise nbxmpp.NodeProcessed + + def _unsubscribe_received(self, con, stanza): + log.info('Received Unsubscribe: %s', stanza.getFrom()) + raise nbxmpp.NodeProcessed + + def _unsubscribed_received(self, con, stanza): + from_ = stanza.getFrom() + jid = from_.getStripped() + log.info('Received Unsubscribed: %s', from_) + app.nec.push_incoming_event(NetworkEvent( + 'unsubscribed-presence-received', + conn=self._con, jid=jid)) + raise nbxmpp.NodeProcessed + + def subscribed(self, jid): + if not app.account_is_connected(self._account): + return + log.info('Subscribed: %s', jid) + presence = nbxmpp.Presence(jid, 'subscribed') + presence = self._add_sha(presence) + self._con.connection.send(presence) + + def unsubscribed(self, jid): + if not app.account_is_connected(self._account): + return + log.info('Unsubscribed: %s', jid) + presence = nbxmpp.Presence(jid, 'unsubscribed') + presence = self._add_sha(presence) + self._con.connection.send(presence) + + def unsubscribe(self, jid, remove_auth=True): + if not app.account_is_connected(self._account): + return + if remove_auth: + self._con.connection.getRoster().delItem(jid) + jid_list = app.config.get_per('contacts') + for j in jid_list: + if j.startswith(jid): + app.config.del_per('contacts', j) + else: + log.info('Unsubscribe from %s', jid) + self._con.connection.getRoster().Unsubscribe(jid) + self._con.connection.getRoster().setItem(jid) + + def subscribe(self, jid, msg='', name='', groups=None, + auto_auth=False, user_nick=''): + if not app.account_is_connected(self._account): + return + if groups is None: + groups = [] + + log.info('Request Subscription to %s', jid) + + if auto_auth: + self.jids_for_auto_auth.append(jid) + + infos = {'jid': jid} + if name: + infos['name'] = name + iq = nbxmpp.Iq('set', nbxmpp.NS_ROSTER) + query = iq.setQuery() + item = query.addChild('item', attrs=infos) + for group in groups: + item.addChild('group').setData(group) + self._con.connection.send(iq) + + presence = nbxmpp.Presence(jid, 'subscribe') + if user_nick: + nick = presence.setTag('nick', namespace=nbxmpp.NS_NICK) + nick.setData(user_nick) + presence = self._add_sha(presence) + if msg: + presence.setStatus(msg) + self._con.connection.send(presence) + + def _add_sha(self, presence, send_caps=True): + vcard = self._con.get_module('VCardAvatars') + presence = vcard.add_update_node(presence) + if send_caps: + return self._add_caps(presence) + return presence + + def _add_caps(self, presence): + c = presence.setTag('c', namespace=nbxmpp.NS_CAPS) + c.setAttr('hash', 'sha-1') + c.setAttr('node', 'http://gajim.org') + c.setAttr('ver', app.caps_hash[self._account]) + return presence + def parse_show(stanza): show = stanza.getShow() diff --git a/gajim/dialogs.py b/gajim/dialogs.py index 45952f724..205e99cb4 100644 --- a/gajim/dialogs.py +++ b/gajim/dialogs.py @@ -881,7 +881,7 @@ class SubscriptionRequestWindow(Gtk.ApplicationWindow): """ Accept the request """ - app.connections[self.account].send_authorization(self.jid) + app.connections[self.account].get_module('Presence').subscribed(self.jid) self.destroy() contact = app.contacts.get_contact(self.account, self.jid) if not contact or _('Not in Roster') in contact.groups: @@ -911,7 +911,7 @@ class SubscriptionRequestWindow(Gtk.ApplicationWindow): """ Refuse the request """ - app.connections[self.account].refuse_authorization(self.jid) + app.connections[self.account].get_module('Presence').unsubscribed(self.jid) contact = app.contacts.get_contact(self.account, self.jid) if contact and _('Not in Roster') in contact.get_shown_groups(): app.interface.roster.remove_contact(self.jid, self.account) @@ -1271,8 +1271,8 @@ class RosterItemExchangeWindow: groups = [] jid = model[iter_][1] if app.jid_is_transport(self.jid_from): - app.connections[self.account].automatically_added.append( - jid) + con = app.connections[self.account] + con.get_module('Presence').automatically_added.append(jid) app.interface.roster.req_sub(self, jid, message, self.account, groups=groups, nickname=model[iter_][2], auto_auth=True) @@ -1311,7 +1311,7 @@ class RosterItemExchangeWindow: a+=1 # it is selected jid = model[iter_][1] - app.connections[self.account].unsubscribe(jid) + app.connections[self.account].get_module('Presence').unsubscribe(jid) app.interface.roster.remove_contact(jid, self.account) app.contacts.remove_jid(self.account, jid) iter_ = model.iter_next(iter_) diff --git a/gajim/remote_control.py b/gajim/remote_control.py index 9582f4c62..a9068d99b 100644 --- a/gajim/remote_control.py +++ b/gajim/remote_control.py @@ -723,7 +723,7 @@ class SignalObject(dbus.service.Object): for account in accounts: contacts = app.contacts.get_contacts(account, jid) if contacts: - app.connections[account].unsubscribe(jid) + app.connections[account].get_module('Presence').unsubscribe(jid) for contact in contacts: app.interface.roster.remove_contact(contact, account) app.contacts.remove_jid(account, jid) diff --git a/gajim/roster_window.py b/gajim/roster_window.py index 4547fd906..b0310c8c5 100644 --- a/gajim/roster_window.py +++ b/gajim/roster_window.py @@ -2037,7 +2037,7 @@ class RosterWindow: """ Authorize a contact (by re-sending auth menuitem) """ - app.connections[account].send_authorization(jid) + app.connections[account].get_module('Presence').subscribed(jid) InformationDialog(_('Authorization sent'), _('"%s" will now see your status.') %jid) @@ -2047,8 +2047,8 @@ class RosterWindow: Request subscription to a contact """ groups_list = groups or [] - app.connections[account].request_subscription(jid, txt, nickname, - groups_list, auto_auth, app.nicks[account]) + app.connections[account].get_module('Presence').subscribe( + jid, txt, nickname, groups_list, auto_auth, app.nicks[account]) contact = app.contacts.get_contact_with_highest_priority(account, jid) if not contact: keyID = '' @@ -2076,7 +2076,7 @@ class RosterWindow: """ Revoke a contact's authorization """ - app.connections[account].refuse_authorization(jid) + app.connections[account].get_module('Presence').unsubscribed(jid) InformationDialog(_('Authorization removed'), _('Now "%s" will always see you as offline.') %jid) @@ -2792,7 +2792,7 @@ class RosterWindow: contact.jid: # We remove the server contact # remove it from treeview - app.connections[account].unsubscribe(contact.jid) + app.connections[account].get_module('Presence').unsubscribe(contact.jid) self.remove_contact(contact.jid, account, backend=True) return @@ -2977,7 +2977,7 @@ class RosterWindow: self.remove_contact_from_groups(contact.jid, account, [group]) else: - app.connections[account].unsubscribe(contact.jid) + app.connections[account].get_module('Presence').unsubscribe(contact.jid) self.remove_contact(contact.jid, account, backend=True) ConfirmationDialogCheck(_('Remove Group'), @@ -3410,7 +3410,7 @@ class RosterWindow: remove_auth = False for (contact, account) in list_: if _('Not in Roster') not in contact.get_shown_groups(): - app.connections[account].unsubscribe(contact.jid, + app.connections[account].get_module('Presence').unsubscribe(contact.jid, remove_auth) self.remove_contact(contact.jid, account, backend=True) if not remove_auth and contact.sub == 'both':