Philipp Hörist pushed to branch master at gajim / gajim
Commits: 14990103 by Philipp Hörist at 2018-06-24T23:33:42+02:00 Refactor entity time code into own module
- - - - - 81d9e1bc by Philipp Hörist at 2018-06-24T23:33:42+02:00 Refactor software version code into own module
- - - - - 7b957a28 by Philipp Hörist at 2018-06-24T23:33:42+02:00 Refactor ping code into own module
- - - - - 6d2a07df by Philipp Hörist at 2018-06-24T23:33:42+02:00 Remove support for XEP-0090
This is deprecated almost 10 years
- - - - -
14 changed files:
- gajim/chat_control.py - gajim/chat_control_base.py - gajim/command_system/implementation/standard.py - gajim/common/connection.py - gajim/common/connection_handlers.py - gajim/common/connection_handlers_events.py - + gajim/common/modules/__init__.py - + gajim/common/modules/entity_time.py - + gajim/common/modules/ping.py - + gajim/common/modules/software_version.py - gajim/groupchat_control.py - gajim/remote_control.py - gajim/server_info.py - gajim/vcard.py
Changes:
===================================== gajim/chat_control.py ===================================== --- a/gajim/chat_control.py +++ b/gajim/chat_control.py @@ -1230,14 +1230,16 @@ class ChatControl(ChatControlBase): return self.update_ui()
- def _nec_ping_reply(self, obj): - if obj.control: - if obj.control != self: - return - else: - if self.contact != obj.contact: - return - self.print_conversation(_('Pong! (%s s.)') % obj.seconds, 'status') + def _nec_ping(self, obj): + if self.contact != obj.contact: + return + if obj.name == 'ping-sent': + self.print_conversation(_('Ping?'), 'status') + elif obj.name == 'ping-reply': + self.print_conversation( + _('Pong! (%s s.)') % obj.seconds, 'status') + elif obj.name == 'ping-error': + self.print_conversation(_('Error.'), 'status')
def show_avatar(self): if not app.config.get('show_avatar_in_chat'):
===================================== gajim/chat_control_base.py ===================================== --- a/gajim/chat_control_base.py +++ b/gajim/chat_control_base.py @@ -193,16 +193,6 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): if self.parent_win: self.parent_win.redraw_tab(self)
- def _nec_ping_sent(self, obj): - if self.contact != obj.contact: - return - self.print_conversation(_('Ping?'), 'status') - - def _nec_ping_error(self, obj): - if self.contact != obj.contact: - return - self.print_conversation(_('Error.'), 'status') - def status_url_clicked(self, widget, url): helpers.launch_browser_mailer('url', url)
@@ -381,11 +371,11 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools): app.ged.register_event_handler('our-show', ged.GUI1, self._nec_our_status) app.ged.register_event_handler('ping-sent', ged.GUI1, - self._nec_ping_sent) + self._nec_ping) app.ged.register_event_handler('ping-reply', ged.GUI1, - self._nec_ping_reply) + self._nec_ping) app.ged.register_event_handler('ping-error', ged.GUI1, - self._nec_ping_error) + self._nec_ping)
# This is basically a very nasty hack to surpass the inability # to properly use the super, because of the old code.
===================================== gajim/command_system/implementation/standard.py ===================================== --- a/gajim/command_system/implementation/standard.py +++ b/gajim/command_system/implementation/standard.py @@ -178,7 +178,7 @@ class StandardCommonChatCommands(CommandContainer): def ping(self): if self.account == app.ZEROCONF_ACC_NAME: raise CommandError(_('Command is not supported for zeroconf accounts')) - app.connections[self.account].sendPing(self.contact) + app.connections[self.account].get_module('Ping').send_ping(self.contact)
@command @doc(_("Send DTMF sequence through an open audio session")) @@ -391,5 +391,5 @@ class StandardGroupChatCommands(CommandContainer): if self.account == app.ZEROCONF_ACC_NAME: raise CommandError(_('Command is not supported for zeroconf accounts')) gc_c = app.contacts.get_gc_contact(self.account, self.room_jid, nick) - app.connections[self.account].sendPing(gc_c, self) + app.connections[self.account].get_module('Ping').send_ping(gc_c)
===================================== gajim/common/connection.py ===================================== --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -63,6 +63,9 @@ 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.modules.software_version import SoftwareVersion +from gajim.common.modules.ping import Ping from gajim.common.connection_handlers import * from gajim.common.contacts import GC_Contact from gajim.gtkgui_helpers import get_action @@ -81,6 +84,7 @@ class CommonConnection:
def __init__(self, name): self.name = name + self._modules = {} # self.connected: # 0=>offline, # 1=>connection in progress, @@ -162,6 +166,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 @@ -471,12 +484,6 @@ class CommonConnection: def account_changed(self, new_name): self.name = new_name
- def request_os_info(self, jid, resource): - """ - To be implemented by derived classes - """ - raise NotImplementedError - def get_settings(self): """ To be implemented by derived classes @@ -653,6 +660,10 @@ class Connection(CommonConnection, ConnectionHandlers):
self.sm = Smacks(self) # Stream Management
+ self.register_module('EntityTime', EntityTime, self) + self.register_module('SoftwareVersion', SoftwareVersion, self) + self.register_module('Ping', Ping, 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, @@ -1518,45 +1529,6 @@ class Connection(CommonConnection, ConnectionHandlers): if self.connection: self.connection.send(' ')
- def _on_xmpp_ping_answer(self, iq_obj): - self.awaiting_xmpp_ping_id = None - - def sendPing(self, pingTo=None, control=None): - """ - Send XMPP Ping (XEP-0199) request. If pingTo is not set, ping is sent to - server to detect connection failure at application level - If control is set, display result there - """ - if not app.account_is_connected(self.name): - return - id_ = self.connection.getAnID() - if pingTo: - to = pingTo.get_full_jid() - app.nec.push_incoming_event(PingSentEvent(None, conn=self, - contact=pingTo)) - else: - to = app.config.get_per('accounts', self.name, 'hostname') - self.awaiting_xmpp_ping_id = id_ - iq = nbxmpp.Iq('get', to=to) - iq.addChild(name='ping', namespace=nbxmpp.NS_PING) - iq.setID(id_) - def _on_response(resp): - timePong = time.time() - if not nbxmpp.isResultNode(resp): - app.nec.push_incoming_event(PingErrorEvent(None, conn=self, - contact=pingTo)) - return - timeDiff = round(timePong - timePing, 2) - app.nec.push_incoming_event(PingReplyEvent(None, conn=self, - contact=pingTo, seconds=timeDiff, control=control)) - if pingTo: - timePing = time.time() - self.connection.SendAndCallForResponse(iq, _on_response) - else: - self.connection.SendAndCallForResponse(iq, self._on_xmpp_ping_answer) - app.idlequeue.set_alarm(self.check_pingalive, app.config.get_per( - 'accounts', self.name, 'time_for_ping_alive_answer')) - def get_active_default_lists(self): if not app.account_is_connected(self.name): return @@ -1823,8 +1795,10 @@ class Connection(CommonConnection, ConnectionHandlers): self.connection = con if not app.account_is_connected(self.name): return + self.connection.set_send_timeout(self.keepalives, self.send_keepalive) - self.connection.set_send_timeout2(self.pingalives, self.sendPing) + self.connection.set_send_timeout2( + self.pingalives, self.get_module('Ping').send_keepalive_ping) self.connection.onreceive(None)
self.privacy_rules_requested = False @@ -2232,51 +2206,6 @@ class Connection(CommonConnection, ConnectionHandlers): self.connection = con nbxmpp.features_nb.getRegInfo(con, self._hostname)
- def request_os_info(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('OS_INFO', (jid, resource, _('Not fetched because of invisible status'), _('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', queryNS=nbxmpp.NS_VERSION) - id_ = self.connection.getAnID() - iq.setID(id_) - if groupchat_jid: - self.groupchat_jids[id_] = groupchat_jid - 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, @@ -2999,15 +2928,6 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection.send(message)
- def check_pingalive(self): - if not app.config.get_per('accounts', self.name, 'active'): - # Account may have been disabled - return - if self.awaiting_xmpp_ping_id: - # We haven't got the pong in time, disco and reconnect - log.warning("No reply received for keepalive ping. Reconnecting.") - self.disconnectedReconnCB() - def _reconnect_alarm(self): if not app.config.get_per('accounts', self.name, 'active'): # Account may have been disabled
===================================== gajim/common/connection_handlers.py ===================================== --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -1324,16 +1324,11 @@ ConnectionHTTPUpload): # keep the latest subscribed event for each jid to prevent loop when we # acknowledge presences 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 self.disco_info_ids = [] - # ID of urn:xmpp:ping requests - self.awaiting_xmpp_ping_id = None + self.continue_connect_info = None
self.privacy_default_list = None @@ -1353,14 +1348,8 @@ ConnectionHTTPUpload):
app.ged.register_event_handler('http-auth-received', ged.CORE, self._nec_http_auth_received) - app.ged.register_event_handler('version-request-received', ged.CORE, - self._nec_version_request_received) app.ged.register_event_handler('last-request-received', ged.CORE, 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', @@ -1371,8 +1360,6 @@ ConnectionHTTPUpload): self._nec_roster_received) app.ged.register_event_handler('iq-error-received', ged.CORE, self._nec_iq_error_received) - app.ged.register_event_handler('ping-received', ged.CORE, - self._nec_ping_received) app.ged.register_event_handler('subscribe-presence-received', ged.CORE, self._nec_subscribe_presence_received) app.ged.register_event_handler('subscribed-presence-received', @@ -1398,14 +1385,8 @@ ConnectionHTTPUpload): ConnectionHTTPUpload.cleanup(self) app.ged.remove_event_handler('http-auth-received', ged.CORE, self._nec_http_auth_received) - app.ged.remove_event_handler('version-request-received', ged.CORE, - self._nec_version_request_received) app.ged.remove_event_handler('last-request-received', ged.CORE, 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', @@ -1416,8 +1397,6 @@ ConnectionHTTPUpload): self._nec_roster_received) app.ged.remove_event_handler('iq-error-received', ged.CORE, self._nec_iq_error_received) - app.ged.remove_event_handler('ping-received', ged.CORE, - self._nec_ping_received) app.ged.remove_event_handler('subscribe-presence-received', ged.CORE, self._nec_subscribe_presence_received) app.ged.remove_event_handler('subscribed-presence-received', @@ -1585,14 +1564,6 @@ ConnectionHTTPUpload): def _nec_iq_error_received(self, obj): if obj.conn.name != self.name: return - if obj.id_ in self.version_ids: - 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)) @@ -1682,31 +1653,6 @@ ConnectionHTTPUpload): app.config.set_per('accounts', self.name, 'roster_version', obj.version)
- def _VersionCB(self, con, iq_obj): - log.debug('VersionCB') - if not self.connection or self.connected < 2: - return - app.nec.push_incoming_event(VersionRequestEvent(None, conn=self, - stanza=iq_obj)) - raise nbxmpp.NodeProcessed - - def _nec_version_request_received(self, obj): - if obj.conn.name != self.name: - return - send_os = app.config.get_per('accounts', self.name, 'send_os_info') - if send_os: - iq_obj = obj.stanza.buildReply('result') - qp = iq_obj.getQuery() - qp.setTagData('name', 'Gajim') - qp.setTagData('version', app.version) - qp.setTagData('os', helpers.get_os_info()) - 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 _LastCB(self, con, iq_obj): log.debug('LastCB') if not self.connection or self.connected < 2: @@ -1730,66 +1676,6 @@ ConnectionHTTPUpload): iq_obj.addChild(node=err) self.connection.send(iq_obj)
- def _VersionResultCB(self, con, iq_obj): - log.debug('VersionResultCB') - app.nec.push_incoming_event(VersionResultReceivedEvent(None, - conn=self, stanza=iq_obj)) - - def _TimeCB(self, con, iq_obj): - log.debug('TimeCB') - if not self.connection or self.connected < 2: - return - app.nec.push_incoming_event(TimeRequestEvent(None, conn=self, - stanza=iq_obj)) - raise nbxmpp.NodeProcessed - - def _nec_time_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.setQuery() - qp.setTagData('utc', strftime('%Y%m%dT%H:%M:%S', gmtime())) - qp.setTagData('tz', tzname[daylight]) - qp.setTagData('display', strftime('%c', localtime())) - 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 _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 @@ -1983,23 +1869,6 @@ ConnectionHTTPUpload): app.nec.push_incoming_event(MucAdminReceivedEvent(None, conn=self, stanza=iq_obj))
- def _IqPingCB(self, con, iq_obj): - log.debug('IqPingCB') - app.nec.push_incoming_event(PingReceivedEvent(None, conn=self, - stanza=iq_obj)) - raise nbxmpp.NodeProcessed - - def _nec_ping_received(self, obj): - if obj.conn.name != self.name: - return - if not self.connection or self.connected < 2: - return - iq_obj = obj.stanza.buildReply('result') - q = iq_obj.getTag('ping') - if q: - iq_obj.delChild(q) - self.connection.send(iq_obj) - def _PrivacySetCB(self, con, iq_obj): """ Privacy lists (XEP 016) @@ -2261,15 +2130,7 @@ ConnectionHTTPUpload): nbxmpp.NS_DISCO_INFO) con.RegisterHandler('iq', self._DiscoverInfoErrorCB, 'error', 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', @@ -2284,7 +2145,6 @@ ConnectionHTTPUpload): nbxmpp.NS_DISCO_INFO) con.RegisterHandler('iq', self._DiscoverItemsGetCB, 'get', nbxmpp.NS_DISCO_ITEMS) - con.RegisterHandler('iq', self._IqPingCB, 'get', nbxmpp.NS_PING) con.RegisterHandler('iq', self._SearchCB, 'result', nbxmpp.NS_SEARCH) con.RegisterHandler('iq', self._PrivacySetCB, 'set', nbxmpp.NS_PRIVACY) con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM_1) @@ -2310,3 +2170,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)
===================================== gajim/common/connection_handlers_events.py ===================================== --- a/gajim/common/connection_handlers_events.py +++ b/gajim/common/connection_handlers_events.py @@ -193,100 +193,6 @@ class HttpAuthReceivedEvent(nec.NetworkIncomingEvent): self.msg = self.stanza.getTagData('body') return True
-class VersionResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): - name = 'version-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.version_ids: - self.conn.version_ids.remove(self.id_) - - self.client_info = '' - self.os_info = '' - - if self.stanza.getType() == 'error': - return True - - qp = self.stanza.getTag('query') - if qp.getTag('name'): - self.client_info += qp.getTag('name').getData() - if qp.getTag('version'): - self.client_info += ' ' + qp.getTag('version').getData() - if qp.getTag('os'): - self.os_info += qp.getTag('os').getData() - - 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 = [] @@ -332,22 +238,10 @@ class RosterItemExchangeEvent(nec.NetworkIncomingEvent, HelperEvent): if self.exchange_items_list: return True
-class VersionRequestEvent(nec.NetworkIncomingEvent): - name = 'version-request-received' - base_network_events = [] - class LastRequestEvent(nec.NetworkIncomingEvent): name = 'last-request-received' base_network_events = []
-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 = [] @@ -675,10 +569,6 @@ class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.errcode = self.stanza.getErrorCode() return True
-class PingReceivedEvent(nec.NetworkIncomingEvent): - name = 'ping-received' - base_network_events = [] - class StreamReceivedEvent(nec.NetworkIncomingEvent): name = 'stream-received' base_network_events = [] @@ -2072,18 +1962,6 @@ class ConnectionLostEvent(nec.NetworkIncomingEvent): show='offline')) return True
-class PingSentEvent(nec.NetworkIncomingEvent): - name = 'ping-sent' - base_network_events = [] - -class PingReplyEvent(nec.NetworkIncomingEvent): - name = 'ping-reply' - base_network_events = [] - -class PingErrorEvent(nec.NetworkIncomingEvent): - name = 'ping-error' - base_network_events = [] - class CapsPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent, PresenceHelperEvent): name = 'caps-presence-received'
===================================== gajim/common/modules/__init__.py ===================================== --- /dev/null +++ b/gajim/common/modules/__init__.py
===================================== gajim/common/modules/entity_time.py ===================================== --- /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 http://www.gnu.org/licenses/. + +# 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
===================================== gajim/common/modules/ping.py ===================================== --- /dev/null +++ b/gajim/common/modules/ping.py @@ -0,0 +1,122 @@ +# 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 http://www.gnu.org/licenses/. + +# XEP-0199: XMPP Ping + +import logging +import time + +import nbxmpp + +from gajim.common import app +from gajim.common.nec import NetworkIncomingEvent + +log = logging.getLogger('gajim.c.m.ping') + + +class Ping: + def __init__(self, con): + self._con = con + self._account = con.name + self._alarm_time = None + + self.handlers = [ + ('iq', self._answer_request, 'get', nbxmpp.NS_PING), + ] + + def _get_ping_iq(self, to): + iq = nbxmpp.Iq('get', to=to) + iq.addChild(name='ping', namespace=nbxmpp.NS_PING) + return iq + + def send_keepalive_ping(self): + if not app.account_is_connected(self._account): + return + + to = app.config.get_per('accounts', self._account, 'hostname') + self._con.connection.SendAndCallForResponse(self._get_ping_iq(to), + self._keepalive_received) + + log.info('Send keepalive') + + seconds = app.config.get_per('accounts', self._account, + 'time_for_ping_alive_answer') + self._alarm_time = app.idlequeue.set_alarm(self._reconnect, seconds) + + def _keepalive_received(self, stanza): + log.info('Received keepalive') + app.idlequeue.remove_alarm(self._reconnect, self._alarm_time) + + def _reconnect(self): + if not app.config.get_per('accounts', self._account, 'active'): + # Account may have been disabled + return + + # We haven't got the pong in time, disco and reconnect + log.warning('No reply received for keepalive ping. Reconnecting...') + self._con.disconnectedReconnCB() + + def send_ping(self, contact): + if not app.account_is_connected(self._account): + return + + to = contact.get_full_jid() + iq = self._get_ping_iq(to) + + log.info('Send ping to %s', to) + + self._con.connection.SendAndCallForResponse( + iq, self._pong_received, {'ping_time': time.time(), + 'contact': contact}) + + app.nec.push_incoming_event( + PingSentEvent(None, conn=self._con, contact=contact)) + + def _pong_received(self, con, stanza, ping_time, contact): + if not nbxmpp.isResultNode(stanza): + log.info('Error: %s', stanza.getError()) + app.nec.push_incoming_event( + PingErrorEvent(None, conn=self._con, contact=contact)) + return + diff = round(time.time() - ping_time, 2) + log.info('Received pong from %s after %s seconds', + stanza.getFrom(), diff) + app.nec.push_incoming_event( + PingReplyEvent(None, conn=self._con, + contact=contact, + seconds=diff)) + + def _answer_request(self, con, stanza): + iq = stanza.buildReply('result') + q = iq.getTag('ping') + if q is not None: + iq.delChild(q) + self._con.connection.send(iq) + log.info('Send pong to %s', stanza.getFrom()) + raise nbxmpp.NodeProcessed + + +class PingSentEvent(NetworkIncomingEvent): + name = 'ping-sent' + base_network_events = [] + + +class PingReplyEvent(NetworkIncomingEvent): + name = 'ping-reply' + base_network_events = [] + + +class PingErrorEvent(NetworkIncomingEvent): + name = 'ping-error' + base_network_events = []
===================================== gajim/common/modules/software_version.py ===================================== --- /dev/null +++ b/gajim/common/modules/software_version.py @@ -0,0 +1,106 @@ +# 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 http://www.gnu.org/licenses/. + +# XEP-0092: Software Version + +import logging + +import nbxmpp + +from gajim.common import app +from gajim.common.helpers import get_os_info +from gajim.common.nec import NetworkIncomingEvent + +log = logging.getLogger('gajim.c.m.software_version') + + +class SoftwareVersion: + def __init__(self, con): + self._con = con + self._account = con.name + + self.handlers = [ + ('iq', self._answer_request, 'get', nbxmpp.NS_VERSION), + ] + + def request_os_info(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', queryNS=nbxmpp.NS_VERSION) + + log.info('Requested: %s', jid) + + self._con.connection.SendAndCallForResponse(iq, self._result_received) + + def _result_received(self, stanza): + client_info, os_info = None, None + if not nbxmpp.isResultNode(stanza): + log.info('Error: %s', stanza.getError()) + else: + try: + client_info, os_info = self._extract_info(stanza) + except Exception: + log.exception('Error') + log.error(stanza) + + log.info('Received: %s %s %s', + stanza.getFrom(), client_info, os_info) + + app.nec.push_incoming_event( + VersionResultReceivedEvent(None, conn=self._con, + jid=stanza.getFrom(), + client_info=client_info, + os_info=os_info)) + + def _extract_info(self, stanza): + query = stanza.getTag('query') + name = query.getTag('name').getData() + version = query.getTag('version').getData() + client_info = '%s %s' % (name, version) + os_info = query.getTag('os') + if os_info is not None: + os_info = os_info.getData() + return client_info, os_info + + def _answer_request(self, con, stanza): + log.info('%s asked for the software version', stanza.getFrom()) + if app.config.get_per('accounts', self._account, 'send_os_info'): + os_info = get_os_info() + iq = stanza.buildReply('result') + query = iq.getQuery() + query.setTagData('name', 'Gajim') + query.setTagData('version', app.version) + query.setTagData('os', os_info) + log.info('Answer: Gajim %s %s', app.version, os_info) + 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 VersionResultReceivedEvent(NetworkIncomingEvent): + name = 'version-result-received' + base_network_events = [] + + def generate(self): + return True
===================================== gajim/groupchat_control.py ===================================== --- a/gajim/groupchat_control.py +++ b/gajim/groupchat_control.py @@ -1572,14 +1572,18 @@ class GroupchatControl(ChatControlBase): obj.xhtml, self.session, msg_log_id=obj.msg_log_id, encrypted=obj.encrypted, displaymarking=obj.displaymarking)
- def _nec_ping_reply(self, obj): - if obj.control: - if obj.control != self: - return - else: - if self.contact != obj.contact: - return - self.print_conversation(_('Pong! (%s s.)') % obj.seconds) + def _nec_ping(self, obj): + if self.contact.jid != obj.contact.room_jid: + return + + nick = obj.contact.get_shown_name() + if obj.name == 'ping-sent': + self.print_conversation(_('Ping? (%s)') % nick) + elif obj.name == 'ping-reply': + self.print_conversation( + _('Pong! (%s %s s.)') % (nick, obj.seconds)) + elif obj.name == 'ping-error': + self.print_conversation(_('Error.'))
def got_connected(self): # Make autorejoin stop.
===================================== gajim/remote_control.py ===================================== --- a/gajim/remote_control.py +++ b/gajim/remote_control.py @@ -145,12 +145,15 @@ class Remote: obj.jid, obj.message, obj.keyID, chatstate]))
def on_os_info(self, obj): - self.raise_signal('OsInfo', (obj.conn.name, [obj.jid, obj.resource, - obj.client_info, obj.os_info])) + self.raise_signal('OsInfo', (obj.conn.name, [obj.jid.getStripped(), + obj.getResource(), + 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,
===================================== gajim/server_info.py ===================================== --- a/gajim/server_info.py +++ b/gajim/server_info.py @@ -74,7 +74,8 @@ class ServerInfoDialog(Gtk.Dialog): self.version = '' self.uptime = '' self.hostname = app.get_hostname_from_account(account) - app.connections[account].request_os_info(self.hostname, None) + con = app.connections[account] + con.get_module('SoftwareVersion').request_os_info(self.hostname, None) self.request_last_activity()
for feature in self.get_features():
===================================== gajim/vcard.py ===================================== --- a/gajim/vcard.py +++ b/gajim/vcard.py @@ -276,15 +276,15 @@ 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 client = '' os = '' while i in self.os_info: - if self.os_info[i]['resource'] == obj.resource: + if self.os_info[i]['resource'] == obj.jid.getResource(): if obj.client_info: self.os_info[i]['client'] = obj.client_info else: @@ -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')\ @@ -423,11 +425,11 @@ class VcardWindow: else: # Request os info 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_os_info, - j, r, self.contact.jid) + GLib.idle_add(con.get_module('SoftwareVersion').request_os_info, + j, r) else: - GLib.idle_add(app.connections[self.account].request_os_info, - self.contact.jid, self.contact.resource) + GLib.idle_add(con.get_module('SoftwareVersion').request_os_info, + self.contact.jid, self.contact.resource)
# do not wait for entity_time if contact is not connected or has error # additional check for observer is needed, as show is offline for him @@ -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': ''}} @@ -456,10 +458,10 @@ class VcardWindow: uf_resources += '\n' + c.resource + \ _(' resource with priority ') + str(c.priority) 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('SoftwareVersion').request_os_info, + 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': ''} @@ -472,7 +474,6 @@ class VcardWindow:
self.fill_status_label()
- con = app.connections[self.account] if self.gc_contact: con.request_vcard(self._nec_vcard_received, self.gc_contact.get_full_jid(), room=True)
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/10e1e1fe14281efac318ab858af9945954...
--- View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/10e1e1fe14281efac318ab858af9945954... You're receiving this email because of your account on dev.gajim.org.