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,
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')

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.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)

View file

@ -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',

View file

@ -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']

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',
'invisible']
# kind of events we can wait for an answer
AGENT_REMOVED = 'agent_removed'
class ZeroconfMessageReceivedEvent(NetworkIncomingEvent):

View file

@ -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_)

View file

@ -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],

View file

@ -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)