From 14990103942b9b05eddd7a81a879e490e549291d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20H=C3=B6rist?= Date: Sun, 24 Jun 2018 17:00:13 +0200 Subject: [PATCH] Refactor entity time code into own module --- gajim/common/connection.py | 36 ++--- gajim/common/connection_handlers.py | 49 +----- gajim/common/connection_handlers_events.py | 72 --------- gajim/common/modules/__init__.py | 0 gajim/common/modules/entity_time.py | 164 +++++++++++++++++++++ gajim/remote_control.py | 5 +- gajim/vcard.py | 20 +-- 7 files changed, 195 insertions(+), 151 deletions(-) create mode 100644 gajim/common/modules/__init__.py create mode 100644 gajim/common/modules/entity_time.py diff --git a/gajim/common/connection.py b/gajim/common/connection.py index 06df0a2d7..c576006a9 100644 --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -63,6 +63,7 @@ from gajim.common import gpg from gajim.common import passwords from gajim.common import i18n from gajim.common import idle +from gajim.common.modules.entity_time import EntityTime from gajim.common.connection_handlers import * from gajim.common.contacts import GC_Contact from gajim.gtkgui_helpers import get_action @@ -81,6 +82,7 @@ class CommonConnection: def __init__(self, name): self.name = name + self._modules = {} # self.connected: # 0=>offline, # 1=>connection in progress, @@ -162,6 +164,15 @@ class CommonConnection: """ app.ged.raise_event(event, self.name, data) + def get_module(self, name): + return self._modules[name] + + def get_module_handlers(self): + return self._modules.values() + + def register_module(self, name, cls, *args, **kwargs): + self._modules[name] = cls(*args, **kwargs) + def reconnect(self): """ To be implemented by derived classes @@ -653,6 +664,8 @@ class Connection(CommonConnection, ConnectionHandlers): self.sm = Smacks(self) # Stream Management + self.register_module('EntityTime', EntityTime, self) + app.ged.register_event_handler('privacy-list-received', ged.CORE, self._nec_privacy_list_received) app.ged.register_event_handler('agent-info-error-received', ged.CORE, @@ -2254,29 +2267,6 @@ class Connection(CommonConnection, ConnectionHandlers): self.version_ids.append(id_) self.connection.send(iq) - def request_entity_time(self, jid, resource, groupchat_jid=None): - """ - groupchat_jid is used when we want to send a request to a real jid and - act as if the answer comes from the groupchat_jid - """ - if not app.account_is_connected(self.name): - return - # If we are invisible, do not request - if self.connected == app.SHOW_LIST.index('invisible'): - self.dispatch('ENTITY_TIME', (jid, resource, _('Not fetched because of invisible status'))) - return - to_whom_jid = jid - if resource: - to_whom_jid += '/' + resource - iq = nbxmpp.Iq(to=to_whom_jid, typ='get') - iq.addChild('time', namespace=nbxmpp.NS_TIME_REVISED) - id_ = self.connection.getAnID() - iq.setID(id_) - if groupchat_jid: - self.groupchat_jids[id_] = groupchat_jid - self.entity_time_ids.append(id_) - self.connection.send(iq) - def request_gateway_prompt(self, jid, prompt=None): def _on_prompt_result(resp): app.nec.push_incoming_event(GatewayPromptReceivedEvent(None, diff --git a/gajim/common/connection_handlers.py b/gajim/common/connection_handlers.py index 25986ff7e..f0dfe6917 100644 --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -1326,8 +1326,6 @@ ConnectionHTTPUpload): self.subscribed_events = {} # IDs of jabber:iq:version requests self.version_ids = [] - # IDs of urn:xmpp:time requests - self.entity_time_ids = [] # IDs of disco#items requests self.disco_items_ids = [] # IDs of disco#info requests @@ -1359,8 +1357,6 @@ ConnectionHTTPUpload): self._nec_last_request_received) app.ged.register_event_handler('time-request-received', ged.CORE, self._nec_time_request_received) - app.ged.register_event_handler('time-revised-request-received', - ged.CORE, self._nec_time_revised_request_received) app.ged.register_event_handler('roster-set-received', ged.CORE, self._nec_roster_set_received) app.ged.register_event_handler('private-storage-bookmarks-received', @@ -1404,8 +1400,6 @@ ConnectionHTTPUpload): self._nec_last_request_received) app.ged.remove_event_handler('time-request-received', ged.CORE, self._nec_time_request_received) - app.ged.remove_event_handler('time-revised-request-received', - ged.CORE, self._nec_time_revised_request_received) app.ged.remove_event_handler('roster-set-received', ged.CORE, self._nec_roster_set_received) app.ged.remove_event_handler('private-storage-bookmarks-received', @@ -1589,10 +1583,6 @@ ConnectionHTTPUpload): app.nec.push_incoming_event(VersionResultReceivedEvent(None, conn=self, stanza=obj.stanza)) return True - if obj.id_ in self.entity_time_ids: - app.nec.push_incoming_event(TimeResultReceivedEvent(None, - conn=self, stanza=obj.stanza)) - return True if obj.id_ in self.disco_items_ids: app.nec.push_incoming_event(AgentItemsErrorReceivedEvent(None, conn=self, stanza=obj.stanza)) @@ -1759,37 +1749,6 @@ ConnectionHTTPUpload): iq_obj.addChild(node=err) self.connection.send(iq_obj) - def _TimeRevisedCB(self, con, iq_obj): - log.debug('TimeRevisedCB') - if not self.connection or self.connected < 2: - return - app.nec.push_incoming_event(TimeRevisedRequestEvent(None, conn=self, - stanza=iq_obj)) - raise nbxmpp.NodeProcessed - - def _nec_time_revised_request_received(self, obj): - if obj.conn.name != self.name: - return - if app.config.get_per('accounts', self.name, 'send_time_info'): - iq_obj = obj.stanza.buildReply('result') - qp = iq_obj.setTag('time', namespace=nbxmpp.NS_TIME_REVISED) - qp.setTagData('utc', strftime('%Y-%m-%dT%H:%M:%SZ', gmtime())) - isdst = localtime().tm_isdst - zone = -(timezone, altzone)[isdst] / 60.0 - tzo = (zone / 60, abs(zone % 60)) - qp.setTagData('tzo', '%+03d:%02d' % (tzo)) - else: - iq_obj = obj.stanza.buildReply('error') - err = nbxmpp.ErrorNode(name=nbxmpp.NS_STANZAS + \ - ' service-unavailable') - iq_obj.addChild(node=err) - self.connection.send(iq_obj) - - def _TimeRevisedResultCB(self, con, iq_obj): - log.debug('TimeRevisedResultCB') - app.nec.push_incoming_event(TimeResultReceivedEvent(None, conn=self, - stanza=iq_obj)) - def _rosterItemExchangeCB(self, con, msg): """ XEP-0144 Roster Item Echange @@ -2263,13 +2222,9 @@ ConnectionHTTPUpload): nbxmpp.NS_DISCO_INFO) con.RegisterHandler('iq', self._VersionCB, 'get', nbxmpp.NS_VERSION) con.RegisterHandler('iq', self._TimeCB, 'get', nbxmpp.NS_TIME) - con.RegisterHandler('iq', self._TimeRevisedCB, 'get', - nbxmpp.NS_TIME_REVISED) con.RegisterHandler('iq', self._LastCB, 'get', nbxmpp.NS_LAST) con.RegisterHandler('iq', self._VersionResultCB, 'result', nbxmpp.NS_VERSION) - con.RegisterHandler('iq', self._TimeRevisedResultCB, 'result', - nbxmpp.NS_TIME_REVISED) con.RegisterHandler('iq', self._MucOwnerCB, 'result', nbxmpp.NS_MUC_OWNER) con.RegisterHandler('iq', self._MucAdminCB, 'result', @@ -2310,3 +2265,7 @@ ConnectionHTTPUpload): nbxmpp.NS_BLOCKING) con.RegisterHandler('iq', self._BlockingResultCB, 'result', nbxmpp.NS_BLOCKING) + + for module in self.get_module_handlers(): + for handler in module.handlers: + con.RegisterHandler(*handler) diff --git a/gajim/common/connection_handlers_events.py b/gajim/common/connection_handlers_events.py index f451838a5..ac0fb555e 100644 --- a/gajim/common/connection_handlers_events.py +++ b/gajim/common/connection_handlers_events.py @@ -219,74 +219,6 @@ class VersionResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): return True -class TimeResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): - name = 'time-result-received' - base_network_events = [] - - def generate(self): - self.get_id() - self.get_jid_resource(check_fake_jid=True) - if self.id_ in self.conn.entity_time_ids: - self.conn.entity_time_ids.remove(self.id_) - - self.time_info = '' - - if self.stanza.getType() == 'error': - return True - - qp = self.stanza.getTag('time') - if not qp: - # wrong answer - return - tzo = qp.getTag('tzo').getData() - if tzo.lower() == 'z': - tzo = '0:0' - try: - tzoh, tzom = tzo.split(':') - except Exception as e: - # wrong tzo - return - utc_time = qp.getTag('utc').getData() - ZERO = datetime.timedelta(0) - class UTC(datetime.tzinfo): - def utcoffset(self, dt): - return ZERO - def tzname(self, dt): - return "UTC" - def dst(self, dt): - return ZERO - - class contact_tz(datetime.tzinfo): - def utcoffset(self, dt): - return datetime.timedelta(hours=int(tzoh), minutes=int(tzom)) - def tzname(self, dt): - return "remote timezone" - def dst(self, dt): - return ZERO - - if utc_time[-1:] == 'Z': - # Remove the trailing 'Z' - utc_time = utc_time[:-1] - elif utc_time[-6:] == "+00:00": - # Remove the trailing "+00:00" - utc_time = utc_time[:-6] - else: - log.info("Wrong timezone defintion: %s" % utc_time) - return - try: - t = datetime.datetime.strptime(utc_time, '%Y-%m-%dT%H:%M:%S') - except ValueError: - try: - t = datetime.datetime.strptime(utc_time, - '%Y-%m-%dT%H:%M:%S.%f') - except ValueError as e: - log.info('Wrong time format: %s' % str(e)) - return - - t = t.replace(tzinfo=UTC()) - self.time_info = t.astimezone(contact_tz()).strftime('%c') - return True - class RosterItemExchangeEvent(nec.NetworkIncomingEvent, HelperEvent): name = 'roster-item-exchange-received' base_network_events = [] @@ -344,10 +276,6 @@ class TimeRequestEvent(nec.NetworkIncomingEvent): name = 'time-request-received' base_network_events = [] -class TimeRevisedRequestEvent(nec.NetworkIncomingEvent): - name = 'time-revised-request-received' - base_network_events = [] - class RosterReceivedEvent(nec.NetworkIncomingEvent): name = 'roster-received' base_network_events = [] diff --git a/gajim/common/modules/__init__.py b/gajim/common/modules/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/gajim/common/modules/entity_time.py b/gajim/common/modules/entity_time.py new file mode 100644 index 000000000..d9d314385 --- /dev/null +++ b/gajim/common/modules/entity_time.py @@ -0,0 +1,164 @@ +# 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 . + +# XEP-0202: Entity Time + +import logging +import datetime +import time + +import nbxmpp + +from gajim.common import app +from gajim.common.nec import NetworkIncomingEvent + +log = logging.getLogger('gajim.c.m.entity_time') + +ZERO = datetime.timedelta(0) + + +class EntityTime: + def __init__(self, con): + self._con = con + self._account = con.name + + self.handlers = [ + ('iq', self._answer_request, 'get', nbxmpp.NS_TIME_REVISED), + ] + + def request_entity_time(self, jid, resource): + if not app.account_is_connected(self._account): + return + # If we are invisible, do not request + if self._con.connected == app.SHOW_LIST.index('invisible'): + return + + if resource: + jid += '/' + resource + iq = nbxmpp.Iq(to=jid, typ='get') + iq.addChild('time', namespace=nbxmpp.NS_TIME_REVISED) + + log.info('Requested: %s', jid) + + self._con.connection.SendAndCallForResponse(iq, self._result_received) + + def _result_received(self, stanza): + time_info = None + if not nbxmpp.isResultNode(stanza): + log.info('Error: %s', stanza.getError()) + else: + time_info = self._extract_info(stanza) + + log.info('Received: %s %s', + stanza.getFrom(), time_info) + + app.nec.push_incoming_event( + TimeResultReceivedEvent(None, conn=self._con, + jid=stanza.getFrom(), + time_info=time_info)) + + def _extract_info(self, stanza): + time_ = stanza.getTag('time') + if not time_: + log.warning('No time node: %s', stanza) + return + + tzo = time_.getTag('tzo').getData() + if tzo.lower() == 'z': + tzo = '0:0' + try: + tzoh, tzom = tzo.split(':') + except Exception as e: + log.warning('Wrong tzo node: %s', stanza) + return + utc_time = time_.getTag('utc').getData() + + if utc_time[-1:] == 'Z': + # Remove the trailing 'Z' + utc_time = utc_time[:-1] + elif utc_time[-6:] == "+00:00": + # Remove the trailing "+00:00" + utc_time = utc_time[:-6] + else: + log.warning('Wrong timezone defintion: %s', utc_time) + return + + try: + t = datetime.datetime.strptime(utc_time, '%Y-%m-%dT%H:%M:%S') + except ValueError: + try: + t = datetime.datetime.strptime(utc_time, + '%Y-%m-%dT%H:%M:%S.%f') + except ValueError as e: + log.warning('Wrong time format: %s', e) + return + + t = t.replace(tzinfo=UTC()) + return t.astimezone(contact_tz(tzoh, tzom)).strftime('%c') + + def _answer_request(self, con, stanza): + log.info('%s asked for the time', stanza.getFrom()) + if app.config.get_per('accounts', self._account, 'send_time_info'): + iq = stanza.buildReply('result') + time_ = iq.setTag('time', namespace=nbxmpp.NS_TIME_REVISED) + formated_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) + time_.setTagData('utc', formated_time) + isdst = time.localtime().tm_isdst + zone = -(time.timezone, time.altzone)[isdst] / 60.0 + tzo = (zone / 60, abs(zone % 60)) + time_.setTagData('tzo', '%+03d:%02d' % (tzo)) + log.info('Answer: %s %s', formated_time, '%+03d:%02d' % (tzo)) + else: + iq = stanza.buildReply('error') + err = nbxmpp.ErrorNode(nbxmpp.ERR_SERVICE_UNAVAILABLE) + iq.addChild(node=err) + log.info('Send service-unavailable') + self._con.connection.send(iq) + raise nbxmpp.NodeProcessed + + +class TimeResultReceivedEvent(NetworkIncomingEvent): + name = 'time-result-received' + base_network_events = [] + + def generate(self): + return True + + +class UTC(datetime.tzinfo): + def utcoffset(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return ZERO + + +class contact_tz(datetime.tzinfo): + + def __init__(self, tzoh, tzom): + self._tzoh = tzoh + self._tzom = tzom + + def utcoffset(self, dt): + return datetime.timedelta(hours=int(self._tzoh), + minutes=int(self._tzom)) + + def tzname(self, dt): + return "remote timezone" + + def dst(self, dt): + return ZERO diff --git a/gajim/remote_control.py b/gajim/remote_control.py index 856f4c9dc..dd2425c93 100644 --- a/gajim/remote_control.py +++ b/gajim/remote_control.py @@ -149,8 +149,9 @@ class Remote: obj.client_info, obj.os_info])) def on_time(self, obj): - self.raise_signal('EntityTime', (obj.conn.name, [obj.jid, obj.resource, - obj.time_info])) + self.raise_signal('EntityTime', (obj.conn.name, [obj.jid.getStripped(), + obj.jid.getResource(), + obj.time_info])) def on_roster_info(self, obj): self.raise_signal('RosterInfo', (obj.conn.name, [obj.jid, obj.nickname, diff --git a/gajim/vcard.py b/gajim/vcard.py index dceefa901..af0729bc7 100644 --- a/gajim/vcard.py +++ b/gajim/vcard.py @@ -315,14 +315,14 @@ class VcardWindow: if self.xml.get_object('information_notebook').get_n_pages() < 5: return if self.gc_contact: - if obj.fjid != self.contact.jid: + if obj.jid != self.contact.jid: return - elif app.get_jid_without_resource(obj.fjid) != self.contact.jid: + elif obj.jid.getStripped() != self.contact.jid: return i = 0 time_s = '' while i in self.time_info: - if self.time_info[i]['resource'] == obj.resource: + if self.time_info[i]['resource'] == obj.jid.getResource(): if obj.time_info: self.time_info[i]['time'] = obj.time_info else: @@ -415,6 +415,8 @@ class VcardWindow: if not self.contact.status: self.contact.status = '' + con = app.connections[self.account] + # do not wait for os_info if contact is not connected or has error # additional check for observer is needed, as show is offline for him if self.contact.show in ('offline', 'error')\ @@ -437,11 +439,11 @@ class VcardWindow: else: # Request entity time if contact is connected if self.gc_contact: j, r = app.get_room_and_nick_from_fjid(self.real_jid) - GLib.idle_add(app.connections[self.account].\ - request_entity_time, j, r, self.contact.jid) + GLib.idle_add(con.get_module('EntityTime').request_entity_time, + j, r) else: - GLib.idle_add(app.connections[self.account].\ - request_entity_time, self.contact.jid, self.contact.resource) + GLib.idle_add(con.get_module('EntityTime').request_entity_time, + self.contact.jid, self.contact.resource) self.os_info = {0: {'resource': self.real_resource, 'client': '', 'os': ''}} @@ -458,8 +460,8 @@ class VcardWindow: if c.show not in ('offline', 'error'): GLib.idle_add(app.connections[self.account].\ request_os_info, c.jid, c.resource) - GLib.idle_add(app.connections[self.account].\ - request_entity_time, c.jid, c.resource) + GLib.idle_add(con.get_module('EntityTime').request_entity_time, + c.jid, c.resource) self.os_info[i] = {'resource': c.resource, 'client': '', 'os': ''} self.time_info[i] = {'resource': c.resource, 'time': ''}