Add IQ and Gateway module

This commit is contained in:
Philipp Hörist 2019-01-02 23:13:07 +01:00
parent d93fe8b1ea
commit 9ae6654dd3
10 changed files with 204 additions and 159 deletions

View file

@ -367,12 +367,6 @@ class CommonConnection:
additional_data=obj.additional_data, additional_data=obj.additional_data,
stanza_id=obj.stanza_id) 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): def update_contact(self, jid, name, groups):
if self.connection: if self.connection:
self.getRoster().set_item(jid=jid, name=name, groups=groups) self.getRoster().set_item(jid=jid, name=name, groups=groups)
@ -1602,17 +1596,6 @@ class Connection(CommonConnection, ConnectionHandlers):
return return
self.connection.send(stanza) 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): def send_new_account_infos(self, form, is_form):
if is_form: if is_form:
# Get username and password and put them in new_account_info # Get username and password and put them in new_account_info
@ -1656,20 +1639,6 @@ class Connection(CommonConnection, ConnectionHandlers):
self.connection = con self.connection = con
nbxmpp.features_nb.getRegInfo(con, self._hostname) 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): def getRoster(self):
return self.get_module('Roster') return self.get_module('Roster')

View file

@ -38,25 +38,15 @@ from gajim.common.caps_cache import muc_caps_cache
from gajim.common.connection_handlers_events import * from gajim.common.connection_handlers_events import *
from gajim.common.const import KindConstant from gajim.common.const import KindConstant
from gajim.common.jingle import ConnectionJingle 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 ConnectionSocks5Bytestream
from gajim.common.protocol.bytestream import ConnectionIBBytestream from gajim.common.protocol.bytestream import ConnectionIBBytestream
log = logging.getLogger('gajim.c.connection_handlers') 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 # basic connection handlers used here and in zeroconf
class ConnectionHandlersBase: class ConnectionHandlersBase:
def __init__(self): 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 # keep track of sessions this connection has with other JIDs
self.sessions = {} self.sessions = {}
@ -291,35 +281,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream,
app.ged.remove_event_handler('agent-removed', ged.CORE, app.ged.remove_event_handler('agent-removed', ged.CORE,
self._nec_agent_removed) 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): def _nec_agent_removed(self, obj):
if obj.conn.name != self.name: if obj.conn.name != self.name:
return return
@ -445,8 +406,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream,
con.RegisterHandler('iq', self._JingleCB, 'result') con.RegisterHandler('iq', self._JingleCB, 'result')
con.RegisterHandler('iq', self._JingleCB, 'error') con.RegisterHandler('iq', self._JingleCB, 'error')
con.RegisterHandler('iq', self._JingleCB, 'set', nbxmpp.NS_JINGLE) 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('iq', self._ResultCB, 'result')
con.RegisterHandler('unknown', self._StreamCB, con.RegisterHandler('unknown', self._StreamCB,
nbxmpp.NS_XMPP_STREAMS, xmlns=nbxmpp.NS_STREAMS) nbxmpp.NS_XMPP_STREAMS, xmlns=nbxmpp.NS_STREAMS)

View file

@ -119,16 +119,6 @@ class HelperEvent:
self.muc_pm = muc_user.getChildren() == [] self.muc_pm = muc_user.getChildren() == []
return self.muc_pm 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): class StreamReceivedEvent(nec.NetworkIncomingEvent):
name = 'stream-received' name = 'stream-received'
@ -266,16 +256,6 @@ class StanzaReceivedEvent(nec.NetworkIncomingEvent):
class StanzaSentEvent(nec.NetworkIncomingEvent): class StanzaSentEvent(nec.NetworkIncomingEvent):
name = 'stanza-sent' 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): class BadGPGPassphraseEvent(nec.NetworkIncomingEvent):
name = 'bad-gpg-passphrase' name = 'bad-gpg-passphrase'
@ -479,22 +459,6 @@ class FileTransferCompletedEvent(nec.NetworkIncomingEvent):
self.jid = app.get_jid_without_resource(jid) self.jid = app.get_jid_without_resource(jid)
return True 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): class NotificationEvent(nec.NetworkIncomingEvent):
name = 'notification' name = 'notification'
base_network_events = ['decrypted-message-received', base_network_events = ['decrypted-message-received',

View file

@ -26,7 +26,8 @@ from gajim.common.types import ConnectionT
log = logging.getLogger('gajim.c.m') log = logging.getLogger('gajim.c.m')
ZEROCONF_MODULES = ['adhoc_commands', ZEROCONF_MODULES = ['iq',
'adhoc_commands',
'receipts', 'receipts',
'discovery', 'discovery',
'chatstates'] 'chatstates']

View file

@ -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 <http://www.gnu.org/licenses/>.
# 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))

View file

@ -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 <http://www.gnu.org/licenses/>.
# 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

View file

@ -44,8 +44,6 @@ log = logging.getLogger('gajim.c.z.connection_handlers_zeroconf')
STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd', STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible'] 'invisible']
# kind of events we can wait for an answer
AGENT_REMOVED = 'agent_removed'
class ZeroconfMessageReceivedEvent(NetworkIncomingEvent): class ZeroconfMessageReceivedEvent(NetworkIncomingEvent):

View file

@ -115,7 +115,8 @@ class AddNewContactWindow(Gtk.ApplicationWindow):
if account: if account:
for service in self.agents[type_]: 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.protocol_combobox.set_active(0)
self.auto_authorize_checkbutton.show() self.auto_authorize_checkbutton.show()
@ -246,8 +247,8 @@ class AddNewContactWindow(Gtk.ApplicationWindow):
transport = model[row][0] transport = model[row][0]
if self.account and not self.jid_escaped: if self.account and not self.jid_escaped:
self.adding_jid = (jid, transport, type_) self.adding_jid = (jid, transport, type_)
app.connections[self.account].request_gateway_prompt( con = app.connections[self.account]
transport, jid) con.get_module('Gateway').request_gateway_prompt(transport, jid)
else: else:
jid = jid.replace('@', '%') + '@' + transport jid = jid.replace('@', '%') + '@' + transport
self._add_jid(jid, type_) self._add_jid(jid, type_)

View file

@ -87,7 +87,7 @@ from gajim.common import passwords
from gajim.common import logging_helpers from gajim.common import logging_helpers
from gajim.common.i18n import _ from gajim.common.i18n import _
from gajim.common.connection_handlers_events import ( from gajim.common.connection_handlers_events import (
OurShowEvent, FileRequestErrorEvent, FileTransferCompletedEvent, OurShowEvent, FileTransferCompletedEvent,
UpdateRosterAvatarEvent, UpdateGCAvatarEvent, UpdateRoomAvatarEvent) UpdateRosterAvatarEvent, UpdateGCAvatarEvent, UpdateRoomAvatarEvent)
from gajim.common.modules.httpupload import HTTPUploadProgressEvent 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), 'id': obj.iq_id}, sec_msg, on_response_yes=(on_yes, obj),
on_response_no=(response, obj, 'no')) on_response_no=(response, obj, 'no'))
def handle_event_iq_error(self, obj): def handle_event_iq_error(self, event):
#('ERROR_ANSWER', account, (id_, fjid, errmsg, errcode)) ctrl = self.msg_win_mgr.get_control(event.properties.jid.getBare(),
if str(obj.errcode) in ('400', '403', '406') and obj.id_: event.account)
# 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)
if ctrl and ctrl.type_id == message_control.TYPE_GC: 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 @staticmethod
def handle_event_connection_lost(obj): def handle_event_connection_lost(obj):
@ -739,25 +712,23 @@ class Interface:
def handle_event_bookmarks(self, obj): def handle_event_bookmarks(self, obj):
gui_menu_builder.build_bookmark_menu(obj.account) gui_menu_builder.build_bookmark_menu(obj.account)
def handle_event_file_send_error(self, account, array): def handle_event_file_send_error(self, event):
jid = array[0]
file_props = array[1]
ft = self.instances['file_transfers'] ft = self.instances['file_transfers']
ft.set_status(file_props, 'stop') ft.set_status(event.file_props, 'stop')
if helpers.allow_popup_window(account): if helpers.allow_popup_window(event.account):
ft.show_send_error(file_props) ft.show_send_error(event.file_props)
return return
event = events.FileSendErrorEvent(file_props) event = events.FileSendErrorEvent(event.file_props)
self.add_event(account, jid, event) 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') event_type = _('File Transfer Error')
app.notification.popup( app.notification.popup(
event_type, jid, account, event_type, event.jid, event.account,
'file-send-error', 'gajim-ft_error', '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): def handle_event_file_request_error(self, obj):
# ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg)) # ('FILE_REQUEST_ERROR', account, (jid, file_props, error_msg))
@ -1383,7 +1354,7 @@ class Interface:
def create_core_handlers_list(self): def create_core_handlers_list(self):
self.handlers = { self.handlers = {
'DB_ERROR': [self.handle_event_db_error], '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], 'pep-received': [self.handle_atom_entry],
'bad-gpg-passphrase': [self.handle_event_bad_gpg_passphrase], 'bad-gpg-passphrase': [self.handle_event_bad_gpg_passphrase],
'bookmarks-received': [self.handle_event_bookmarks], 'bookmarks-received': [self.handle_event_bookmarks],

View file

@ -2774,7 +2774,7 @@ class RosterWindow:
def remove(list_): def remove(list_):
for (contact, account) in list_: for (contact, account) in list_:
full_jid = contact.get_full_jid() 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 # remove transport from treeview
self.remove_contact(contact.jid, account, backend=True) self.remove_contact(contact.jid, account, backend=True)