diff --git a/gajim/common/connection.py b/gajim/common/connection.py index d06bd7aaa..08777d8c8 100644 --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -367,12 +367,6 @@ class CommonConnection: additional_data=obj.additional_data, stanza_id=obj.stanza_id) - def unsubscribe_agent(self, agent): - """ - To be implemented by derived classes - """ - raise NotImplementedError - def update_contact(self, jid, name, groups): if self.connection: self.getRoster().set_item(jid=jid, name=name, groups=groups) @@ -1602,17 +1596,6 @@ class Connection(CommonConnection, ConnectionHandlers): return self.connection.send(stanza) - def unsubscribe_agent(self, agent): - if not app.account_is_connected(self.name): - return - iq = nbxmpp.Iq('set', nbxmpp.NS_REGISTER, to=agent) - iq.setQuery().setTag('remove') - id_ = self.connection.getAnID() - iq.setID(id_) - self.awaiting_answers[id_] = (AGENT_REMOVED, agent) - self.connection.send(iq) - self.getRoster().del_item(agent) - def send_new_account_infos(self, form, is_form): if is_form: # Get username and password and put them in new_account_info @@ -1656,20 +1639,6 @@ class Connection(CommonConnection, ConnectionHandlers): self.connection = con nbxmpp.features_nb.getRegInfo(con, self._hostname) - def request_gateway_prompt(self, jid, prompt=None): - def _on_prompt_result(resp): - app.nec.push_incoming_event(GatewayPromptReceivedEvent(None, - conn=self, stanza=resp)) - if prompt: - typ_ = 'set' - else: - typ_ = 'get' - iq = nbxmpp.Iq(typ=typ_, to=jid) - query = iq.addChild(name='query', namespace=nbxmpp.NS_GATEWAY) - if prompt: - query.setTagData('prompt', prompt) - self.connection.SendAndCallForResponse(iq, _on_prompt_result) - def getRoster(self): return self.get_module('Roster') diff --git a/gajim/common/connection_handlers.py b/gajim/common/connection_handlers.py index 77234ef86..486c62c3b 100644 --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -38,25 +38,15 @@ from gajim.common.caps_cache import muc_caps_cache from gajim.common.connection_handlers_events import * from gajim.common.const import KindConstant from gajim.common.jingle import ConnectionJingle -from gajim.common.nec import NetworkEvent from gajim.common.protocol.bytestream import ConnectionSocks5Bytestream from gajim.common.protocol.bytestream import ConnectionIBBytestream log = logging.getLogger('gajim.c.connection_handlers') -# kind of events we can wait for an answer -AGENT_REMOVED = 'agent_removed' - # basic connection handlers used here and in zeroconf class ConnectionHandlersBase: def __init__(self): - # List of IDs we are waiting answers for {id: (type_of_request, data), } - self.awaiting_answers = {} - # 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 track of sessions this connection has with other JIDs self.sessions = {} @@ -291,35 +281,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, app.ged.remove_event_handler('agent-removed', ged.CORE, self._nec_agent_removed) - def _ErrorCB(self, con, iq_obj): - log.debug('ErrorCB') - app.nec.push_incoming_event(IqErrorReceivedEvent(None, conn=self, - stanza=iq_obj)) - - def _IqCB(self, con, iq_obj, properties): - id_ = iq_obj.getID() - - app.nec.push_incoming_event(NetworkEvent('raw-iq-received', - conn=self, stanza=iq_obj)) - - # Check if we were waiting a timeout for this id - found_tim = None - for tim in self.awaiting_timeouts: - if id_ == self.awaiting_timeouts[tim][0]: - found_tim = tim - break - if found_tim: - del self.awaiting_timeouts[found_tim] - - if id_ not in self.awaiting_answers: - return - - if self.awaiting_answers[id_][0] == AGENT_REMOVED: - jid = self.awaiting_answers[id_][1] - app.nec.push_incoming_event(AgentRemovedEvent(None, conn=self, - agent=jid)) - del self.awaiting_answers[id_] - def _nec_agent_removed(self, obj): if obj.conn.name != self.name: return @@ -445,8 +406,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, con.RegisterHandler('iq', self._JingleCB, 'result') con.RegisterHandler('iq', self._JingleCB, 'error') con.RegisterHandler('iq', self._JingleCB, 'set', nbxmpp.NS_JINGLE) - con.RegisterHandler('iq', self._ErrorCB, 'error') - con.RegisterHandler('iq', self._IqCB) con.RegisterHandler('iq', self._ResultCB, 'result') con.RegisterHandler('unknown', self._StreamCB, nbxmpp.NS_XMPP_STREAMS, xmlns=nbxmpp.NS_STREAMS) diff --git a/gajim/common/connection_handlers_events.py b/gajim/common/connection_handlers_events.py index 7f9db35a8..da8ddcedb 100644 --- a/gajim/common/connection_handlers_events.py +++ b/gajim/common/connection_handlers_events.py @@ -119,16 +119,6 @@ class HelperEvent: self.muc_pm = muc_user.getChildren() == [] return self.muc_pm -class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): - name = 'iq-error-received' - - def generate(self): - self.get_id() - self.get_jid_resource(check_fake_jid=True) - self.errmsg = self.stanza.getErrorMsg() - self.errcode = self.stanza.getErrorCode() - return True - class StreamReceivedEvent(nec.NetworkIncomingEvent): name = 'stream-received' @@ -266,16 +256,6 @@ class StanzaReceivedEvent(nec.NetworkIncomingEvent): class StanzaSentEvent(nec.NetworkIncomingEvent): name = 'stanza-sent' -class AgentRemovedEvent(nec.NetworkIncomingEvent): - name = 'agent-removed' - - def generate(self): - self.jid_list = [] - for jid in app.contacts.get_jid_list(self.conn.name): - if jid.endswith('@' + self.agent): - self.jid_list.append(jid) - return True - class BadGPGPassphraseEvent(nec.NetworkIncomingEvent): name = 'bad-gpg-passphrase' @@ -479,22 +459,6 @@ class FileTransferCompletedEvent(nec.NetworkIncomingEvent): self.jid = app.get_jid_without_resource(jid) return True -class GatewayPromptReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): - name = 'gateway-prompt-received' - - def generate(self): - self.get_jid_resource() - query = self.stanza.getTag('query') - if query: - self.desc = query.getTagData('desc') - self.prompt = query.getTagData('prompt') - self.prompt_jid = query.getTagData('jid') - else: - self.desc = None - self.prompt = None - self.prompt_jid = None - return True - class NotificationEvent(nec.NetworkIncomingEvent): name = 'notification' base_network_events = ['decrypted-message-received', diff --git a/gajim/common/modules/__init__.py b/gajim/common/modules/__init__.py index c5aea976d..77403cb9d 100644 --- a/gajim/common/modules/__init__.py +++ b/gajim/common/modules/__init__.py @@ -26,7 +26,8 @@ from gajim.common.types import ConnectionT log = logging.getLogger('gajim.c.m') -ZEROCONF_MODULES = ['adhoc_commands', +ZEROCONF_MODULES = ['iq', + 'adhoc_commands', 'receipts', 'discovery', 'chatstates'] diff --git a/gajim/common/modules/gateway.py b/gajim/common/modules/gateway.py new file mode 100644 index 000000000..57a1d7ada --- /dev/null +++ b/gajim/common/modules/gateway.py @@ -0,0 +1,95 @@ +# 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-0100: Gateway Interaction + +import logging + +import nbxmpp + +from gajim.common import app +from gajim.common.nec import NetworkEvent + +log = logging.getLogger('gajim.c.m.gateway') + + +class Gateway: + def __init__(self, con): + self._con = con + self._account = con.name + + self.handlers = [] + + def unsubscribe(self, agent): + if not app.account_is_connected(self._account): + return + iq = nbxmpp.Iq('set', nbxmpp.NS_REGISTER, to=agent) + iq.setQuery().setTag('remove') + + self._con.connection.SendAndCallForResponse( + iq, self._on_unsubscribe_result) + self._con.getRoster().del_item(agent) + + def _on_unsubscribe_result(self, stanza): + if not nbxmpp.isResultNode(stanza): + log.info('Error: %s', stanza.getError()) + return + + agent = stanza.getFrom().getBare() + jid_list = [] + for jid in app.contacts.get_jid_list(self._account): + if jid.endswith('@' + agent): + jid_list.append(jid) + + app.nec.push_incoming_event( + NetworkEvent('agent-removed', + conn=self._con, + agent=agent, + jid_list=jid_list)) + + def request_gateway_prompt(self, jid, prompt=None): + typ_ = 'get' + if prompt: + typ_ = 'set' + iq = nbxmpp.Iq(typ=typ_, to=jid) + query = iq.addChild(name='query', namespace=nbxmpp.NS_GATEWAY) + if prompt: + query.setTagData('prompt', prompt) + self._con.connection.SendAndCallForResponse(iq, self._on_prompt_result) + + def _on_prompt_result(self, stanza): + jid = str(stanza.getFrom()) + fjid = stanza.getFrom().getBare() + resource = stanza.getFrom().getResource() + + query = stanza.getTag('query') + if query is not None: + desc = query.getTagData('desc') + prompt = query.getTagData('prompt') + prompt_jid = query.getTagData('jid') + else: + desc = None + prompt = None + prompt_jid = None + + app.nec.push_incoming_event( + NetworkEvent('gateway-prompt-received', + conn=self._con, + fjid=fjid, + jid=jid, + resource=resource, + desc=desc, + prompt=prompt, + prompt_jid=prompt_jid, + stanza=stanza)) diff --git a/gajim/common/modules/iq.py b/gajim/common/modules/iq.py new file mode 100644 index 000000000..c8e743e4d --- /dev/null +++ b/gajim/common/modules/iq.py @@ -0,0 +1,87 @@ +# 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 . + +# Iq handler + +import logging + +import nbxmpp +from nbxmpp.const import Error +from nbxmpp.structs import StanzaHandler + +from gajim.common import app +from gajim.common.nec import NetworkEvent +from gajim.common.file_props import FilesProp + + +log = logging.getLogger('gajim.c.m.iq') + + +class Iq: + def __init__(self, con): + self._con = con + self._account = con.name + + self.handlers = [ + StanzaHandler(name='iq', + callback=self._iq_error_received, + typ='error', + priority=51), + ] + + def _iq_error_received(self, _con, _stanza, properties): + log.info('Error: %s', properties.error) + if properties.error.type in (Error.JID_MALFORMED, + Error.FORBIDDEN, + Error.NOT_ACCEPTABLE): + sid = self._get_sid(properties.id) + file_props = FilesProp.getFileProp(self._account, sid) + if file_props: + if properties.error.type == Error.JID_MALFORMED: + file_props.error = -3 + else: + file_props.error = -4 + app.nec.push_incoming_event( + NetworkEvent('file-request-error', + conn=self._con, + jid=properties.jid.getBare(), + file_props=file_props, + error_msg=properties.error.message)) + self._con.disconnect_transfer(file_props) + raise nbxmpp.NodeProcessed + + if properties.error.type == Error.ITEM_NOT_FOUND: + sid = self._get_sid(properties.id) + file_props = FilesProp.getFileProp(self._account, sid) + if file_props: + app.nec.push_incoming_event( + NetworkEvent('file-send-error', + account=self._account, + jid=str(properties.jid), + file_props=file_props)) + self._con.disconnect_transfer(file_props) + raise nbxmpp.NodeProcessed + + app.nec.push_incoming_event( + NetworkEvent('iq-error-received', + account=self._account, + properties=properties)) + raise nbxmpp.NodeProcessed + + @staticmethod + def _get_sid(id_): + sid = id_ + if len(id_) > 3 and id_[2] == '_': + sid = id_[3:] + return sid diff --git a/gajim/common/zeroconf/connection_handlers_zeroconf.py b/gajim/common/zeroconf/connection_handlers_zeroconf.py index 8d3f0259b..dba3e8e94 100644 --- a/gajim/common/zeroconf/connection_handlers_zeroconf.py +++ b/gajim/common/zeroconf/connection_handlers_zeroconf.py @@ -44,8 +44,6 @@ log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf') STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd', 'invisible'] -# kind of events we can wait for an answer -AGENT_REMOVED = 'agent_removed' class ZeroconfMessageReceivedEvent(NetworkIncomingEvent): diff --git a/gajim/gtk/add_contact.py b/gajim/gtk/add_contact.py index 5928b2a4a..f1f486089 100644 --- a/gajim/gtk/add_contact.py +++ b/gajim/gtk/add_contact.py @@ -115,7 +115,8 @@ class AddNewContactWindow(Gtk.ApplicationWindow): if account: for service in self.agents[type_]: - app.connections[account].request_gateway_prompt(service) + con = app.connections[account] + con.get_module('Gateway').request_gateway_prompt(service) self.protocol_combobox.set_active(0) self.auto_authorize_checkbutton.show() @@ -246,8 +247,8 @@ class AddNewContactWindow(Gtk.ApplicationWindow): transport = model[row][0] if self.account and not self.jid_escaped: self.adding_jid = (jid, transport, type_) - app.connections[self.account].request_gateway_prompt( - transport, jid) + con = app.connections[self.account] + con.get_module('Gateway').request_gateway_prompt(transport, jid) else: jid = jid.replace('@', '%') + '@' + transport self._add_jid(jid, type_) diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py index 360432ea2..2d33c0fce 100644 --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -87,7 +87,7 @@ from gajim.common import passwords from gajim.common import logging_helpers from gajim.common.i18n import _ from gajim.common.connection_handlers_events import ( - OurShowEvent, FileRequestErrorEvent, FileTransferCompletedEvent, + OurShowEvent, FileTransferCompletedEvent, UpdateRosterAvatarEvent, UpdateGCAvatarEvent, UpdateRoomAvatarEvent) from gajim.common.modules.httpupload import HTTPUploadProgressEvent @@ -199,38 +199,11 @@ class Interface: 'id': obj.iq_id}, sec_msg, on_response_yes=(on_yes, obj), on_response_no=(response, obj, 'no')) - def handle_event_iq_error(self, obj): - #('ERROR_ANSWER', account, (id_, fjid, errmsg, errcode)) - if str(obj.errcode) in ('400', '403', '406') and obj.id_: - # show the error dialog - sid = obj.id_ - if len(obj.id_) > 3 and obj.id_[2] == '_': - sid = obj.id_[3:] - file_props = FilesProp.getFileProp(obj.conn.name, sid) - if file_props: - if str(obj.errcode) == '400': - file_props.error = -3 - else: - file_props.error = -4 - app.nec.push_incoming_event(FileRequestErrorEvent(None, - conn=obj.conn, jid=obj.jid, file_props=file_props, - error_msg=obj.errmsg)) - obj.conn.disconnect_transfer(file_props) - return - elif str(obj.errcode) == '404': - sid = obj.id_ - if len(obj.id_) > 3 and obj.id_[2] == '_': - sid = obj.id_[3:] - file_props = FilesProp.getFileProp(obj.conn.name, sid) - if file_props: - self.handle_event_file_send_error(obj.conn.name, (obj.fjid, - file_props)) - obj.conn.disconnect_transfer(file_props) - return - - ctrl = self.msg_win_mgr.get_control(obj.fjid, obj.conn.name) + def handle_event_iq_error(self, event): + ctrl = self.msg_win_mgr.get_control(event.properties.jid.getBare(), + event.account) if ctrl and ctrl.type_id == message_control.TYPE_GC: - ctrl.print_conversation('Error %s: %s' % (obj.errcode, obj.errmsg)) + ctrl.print_conversation('Error: %s' % event.properties.error) @staticmethod def handle_event_connection_lost(obj): @@ -739,25 +712,23 @@ class Interface: def handle_event_bookmarks(self, obj): gui_menu_builder.build_bookmark_menu(obj.account) - def handle_event_file_send_error(self, account, array): - jid = array[0] - file_props = array[1] + def handle_event_file_send_error(self, event): ft = self.instances['file_transfers'] - ft.set_status(file_props, 'stop') + ft.set_status(event.file_props, 'stop') - if helpers.allow_popup_window(account): - ft.show_send_error(file_props) + if helpers.allow_popup_window(event.account): + ft.show_send_error(event.file_props) return - event = events.FileSendErrorEvent(file_props) - self.add_event(account, jid, event) + event = events.FileSendErrorEvent(event.file_props) + self.add_event(event.account, event.jid, event) - if helpers.allow_showing_notification(account): + if helpers.allow_showing_notification(event.account): event_type = _('File Transfer Error') app.notification.popup( - event_type, jid, account, + event_type, event.jid, event.account, 'file-send-error', 'gajim-ft_error', - event_type, file_props.name) + event_type, event.file_props.name) def handle_event_file_request_error(self, obj): # ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg)) @@ -1383,7 +1354,7 @@ class Interface: def create_core_handlers_list(self): self.handlers = { 'DB_ERROR': [self.handle_event_db_error], - 'FILE_SEND_ERROR': [self.handle_event_file_send_error], + 'file-send-error': [self.handle_event_file_send_error], 'pep-received': [self.handle_atom_entry], 'bad-gpg-passphrase': [self.handle_event_bad_gpg_passphrase], 'bookmarks-received': [self.handle_event_bookmarks], diff --git a/gajim/roster_window.py b/gajim/roster_window.py index c4b02d43d..b09628cff 100644 --- a/gajim/roster_window.py +++ b/gajim/roster_window.py @@ -2774,7 +2774,7 @@ class RosterWindow: def remove(list_): for (contact, account) in list_: full_jid = contact.get_full_jid() - app.connections[account].unsubscribe_agent(full_jid) + app.connections[account].get_module('Gateway').unsubscribe(full_jid) # remove transport from treeview self.remove_contact(contact.jid, account, backend=True)