diff --git a/gajim/common/connection.py b/gajim/common/connection.py index 42528cabb..3df254e84 100644 --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -792,11 +792,6 @@ class Connection(CommonConnection, ConnectionHandlers): None, dialog_name='invalid-answer', kwargs={'name': data[0], 'error': data[3]})) return - is_form = data[2] - conf = data[1] - app.nec.push_incoming_event(RegisterAgentInfoReceivedEvent( - None, conn=self, agent=data[0], config=conf, - is_form=is_form)) def _select_next_host(self, hosts): """ diff --git a/gajim/common/connection_handlers.py b/gajim/common/connection_handlers.py index 941139488..58b1b231d 100644 --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -54,61 +54,6 @@ log = logging.getLogger('gajim.c.connection_handlers') # kind of events we can wait for an answer AGENT_REMOVED = 'agent_removed' - -class ConnectionDisco: - - def request_register_agent_info(self, agent): - if not self.connection or self.connected < 2: - return None - iq = nbxmpp.Iq('get', nbxmpp.NS_REGISTER, to=agent) - id_ = self.connection.getAnID() - iq.setID(id_) - # Wait the answer during 30 secondes - self.awaiting_timeouts[app.idlequeue.current_time() + 30] = (id_, - _('Registration information for transport %s has not arrived in ' - 'time') % agent) - self.connection.SendAndCallForResponse(iq, self._ReceivedRegInfo, - {'agent': agent}) - - def _agent_registered_cb(self, con, resp, agent): - if resp.getType() == 'result': - app.nec.push_incoming_event(InformationEvent( - None, dialog_name='agent-register-success', args=agent)) - self.get_module('Presence').subscribe(agent, auto_auth=True) - self.agent_registrations[agent]['roster_push'] = True - if self.agent_registrations[agent]['sub_received']: - p = nbxmpp.Presence(agent, 'subscribed') - p = self.add_sha(p) - self.connection.send(p) - if resp.getType() == 'error': - app.nec.push_incoming_event(InformationEvent( - None, dialog_name='agent-register-error', - kwargs={'agent': agent, - 'error': resp.getError(), - 'error_msg': resp.getErrorMsg()})) - - def register_agent(self, agent, info, is_form=False): - if not self.connection or self.connected < 2: - return - if is_form: - iq = nbxmpp.Iq('set', nbxmpp.NS_REGISTER, to=agent) - query = iq.setQuery() - info.setAttr('type', 'submit') - query.addChild(node=info) - self.connection.SendAndCallForResponse(iq, - self._agent_registered_cb, {'agent': agent}) - else: - # fixed: blocking - nbxmpp.features_nb.register(self.connection, agent, info, - self._agent_registered_cb, {'agent': agent}) - self.agent_registrations[agent] = {'roster_push': False, - 'sub_received': False} - - def _ReceivedRegInfo(self, con, resp, agent): - nbxmpp.features_nb._ReceivedRegInfo(con, resp, agent) - self._IqCB(con, resp) - - # basic connection handlers used here and in zeroconf class ConnectionHandlersBase: def __init__(self): @@ -443,7 +388,7 @@ class ConnectionHandlersBase: return sess -class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, +class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream): def __init__(self): diff --git a/gajim/common/connection_handlers_events.py b/gajim/common/connection_handlers_events.py index 6c72a05c3..9acd6ecdb 100644 --- a/gajim/common/connection_handlers_events.py +++ b/gajim/common/connection_handlers_events.py @@ -868,10 +868,6 @@ class SignedInEvent(nec.NetworkIncomingEvent): name = 'signed-in' base_network_events = [] -class RegisterAgentInfoReceivedEvent(nec.NetworkIncomingEvent): - name = 'register-agent-info-received' - base_network_events = [] - class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): name = 'file-request-received' base_network_events = [] diff --git a/gajim/common/modules/register.py b/gajim/common/modules/register.py index 6f52b57c5..e6586c9b5 100644 --- a/gajim/common/modules/register.py +++ b/gajim/common/modules/register.py @@ -31,6 +31,8 @@ class Register: self.handlers = [] + self.agent_registrations = {} + def change_password(self, password, success_cb, error_cb): if not app.account_is_connected(self._account): return @@ -52,11 +54,87 @@ class Register: if not nbxmpp.isResultNode(stanza): error = stanza.getErrorMsg() log.info('Error: %s', error) - error_cb()(error) + if error_cb() is not None: + error_cb()(error) else: log.info('Password changed') + if success_cb() is not None: + success_cb()() + + def register_agent(self, agent, form, is_form, success_cb, error_cb): + if not app.account_is_connected(self._account): + return + + weak_success_cb = weakref.WeakMethod(success_cb) + weak_error_cb = weakref.WeakMethod(error_cb) + + iq = nbxmpp.Iq('set', nbxmpp.NS_REGISTER, to=agent) + if is_form: + query = iq.setQuery() + form.setAttr('type', 'submit') + query.addChild(node=form) + else: + for field in form.keys(): + iq.setTag('query').setTagData(field, form[field]) + + self._con.connection.SendAndCallForResponse( + iq, self._register_agent_response, {'agent': agent, + 'success_cb': weak_success_cb, + 'error_cb': weak_error_cb}) + + self.agent_registrations[agent] = {'roster_push': False, + 'sub_received': False} + + def _register_agent_response(self, con, stanza, agent, + success_cb, error_cb): + if not nbxmpp.isResultNode(stanza): + error = stanza.getErrorMsg() + log.info('Error: %s', error) + if error_cb() is not None: + error_cb()(error) + return + + self._con.get_module('Presence').subscribe(agent, auto_auth=True) + + self.agent_registrations[agent]['roster_push'] = True + if self.agent_registrations[agent]['sub_received']: + self._con.get_module('Presence').subscribed(agent) + + if success_cb() is not None: success_cb()() + def get_register_form(self, jid, success_cb, error_cb): + if not app.account_is_connected(self._account): + return + + weak_success_cb = weakref.WeakMethod(success_cb) + weak_error_cb = weakref.WeakMethod(error_cb) + + iq = nbxmpp.Iq('get', nbxmpp.NS_REGISTER, to=jid) + self._con.connection.SendAndCallForResponse( + iq, self._register_info_response, {'success_cb': weak_success_cb, + 'error_cb': weak_error_cb}) + + def _register_info_response(self, con, stanza, success_cb, error_cb): + if not nbxmpp.isResultNode(stanza): + error = stanza.getErrorMsg() + log.info('Error: %s', error) + if error_cb() is not None: + error_cb()(error) + else: + log.info('Register form received') + form = stanza.getQuery().getTag('x', namespace=nbxmpp.NS_DATA) + is_form = form is not None + if not is_form: + form = {} + for field in stanza.getQueryPayload(): + if not isinstance(field, nbxmpp.Node): + continue + form[field.getName()] = field.getData() + + if success_cb() is not None: + success_cb()(form, is_form) + def get_instance(*args, **kwargs): return Register(*args, **kwargs), 'Register' diff --git a/gajim/config.py b/gajim/config.py index 301812e54..e4928b4d8 100644 --- a/gajim/config.py +++ b/gajim/config.py @@ -1486,57 +1486,6 @@ class FakeDataForm(Gtk.Table, object): self.infos[name] = self.entries[name].get_text() return self.infos -class ServiceRegistrationWindow: - """ - Class for Service registration window. Window that appears when we want to - subscribe to a service if is_form we use dataforms_widget else we use - service_registarion_window - """ - def __init__(self, service, infos, account, is_form): - self.service = service - self.account = account - self.is_form = is_form - self.xml = gtkgui_helpers.get_gtk_builder('service_registration_window.ui') - self.window = self.xml.get_object('service_registration_window') - self.window.set_transient_for(app.interface.roster.window) - if self.is_form: - dataform = dataforms.ExtendForm(node = infos) - self.data_form_widget = dataforms_widget.DataFormWidget(dataform) - if self.data_form_widget.title: - self.window.set_title('%s - Gajim' % self.data_form_widget.title) - grid = self.xml.get_object('grid') - grid.attach(self.data_form_widget, 0, 0, 2, 1) - else: - if 'registered' in infos: - self.window.set_title(_('Edit %s') % service) - else: - self.window.set_title(_('Register to %s') % service) - self.data_form_widget = FakeDataForm(infos) - grid = self.xml.get_object('grid') - grid.attach(self.data_form_widget, 0, 0, 2, 1) - - self.xml.connect_signals(self) - self.window.show_all() - - def on_cancel_button_clicked(self, widget): - self.window.destroy() - - def on_ok_button_clicked(self, widget): - # send registration info to the core - if self.is_form: - form = self.data_form_widget.data_form - app.connections[self.account].register_agent(self.service, - form, True) # True is for is_form - else: - infos = self.data_form_widget.get_infos() - if 'instructions' in infos: - del infos['instructions'] - if 'registered' in infos: - del infos['registered'] - app.connections[self.account].register_agent(self.service, infos) - - self.window.destroy() - class GroupchatConfigWindow: def __init__(self, account, room_jid, form=None): diff --git a/gajim/data/gui/service_registration_window.ui b/gajim/data/gui/service_registration_window.ui deleted file mode 100644 index a7ff3e995..000000000 --- a/gajim/data/gui/service_registration_window.ui +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - True - False - gtk-apply - - - True - False - gtk-cancel - - - False - 6 - Register to - dialog - - - True - False - vertical - 6 - - - True - False - 6 - 6 - - - - - - - - - False - True - 0 - - - - - True - False - 12 - end - - - _Cancel - True - True - True - False - image2 - True - - - - False - False - 0 - - - - - _OK - True - True - True - True - False - image1 - True - - - - False - False - 1 - - - - - False - True - 1 - - - - - - diff --git a/gajim/disco.py b/gajim/disco.py index af0a96c0a..4fe38e86f 100644 --- a/gajim/disco.py +++ b/gajim/disco.py @@ -59,6 +59,7 @@ from gajim import groups from gajim import adhoc_commands from gajim import search_window from gajim import gui_menu_builder +from gajim.gtk import ServiceRegistration from gajim.common import app import nbxmpp @@ -1355,8 +1356,8 @@ class ToplevelAgentBrowser(AgentBrowser): return jid = model[iter_][0] if jid: - app.connections[self.account].request_register_agent_info(jid) - self.window.destroy(chain = True) + ServiceRegistration(self.account, jid) + self.window.destroy(chain=True) def on_join_button_clicked(self, widget): """ diff --git a/gajim/gtk/__init__.py b/gajim/gtk/__init__.py index e21fc3645..c62ff0cc3 100644 --- a/gajim/gtk/__init__.py +++ b/gajim/gtk/__init__.py @@ -66,3 +66,4 @@ from gajim.gtk.bookmarks import ManageBookmarksWindow from gajim.gtk.profile import ProfileWindow from gajim.gtk.features import FeaturesDialog from gajim.gtk.account_wizard import AccountCreationWizard +from gajim.gtk.service_registration import ServiceRegistration diff --git a/gajim/gtk/add_contact.py b/gajim/gtk/add_contact.py index f094f07e7..6609cbf6f 100644 --- a/gajim/gtk/add_contact.py +++ b/gajim/gtk/add_contact.py @@ -209,7 +209,8 @@ class AddNewContactWindow(Gtk.ApplicationWindow): model = self.protocol_jid_combobox.get_model() row = self.protocol_jid_combobox.get_active() jid = model[row][0] - app.connections[self.account].request_register_agent_info(jid) + from gajim.gtk import ServiceRegistration + ServiceRegistration(self.account, jid) def _on_key_press(self, widget, event): if event.keyval == Gdk.KEY_Escape: diff --git a/gajim/gtk/dialogs.py b/gajim/gtk/dialogs.py index ea3feb5bc..62b693ae6 100644 --- a/gajim/gtk/dialogs.py +++ b/gajim/gtk/dialogs.py @@ -13,10 +13,10 @@ # along with Gajim. If not, see . from gi.repository import Gtk -from gi.repository import Gdk from gajim.common import app from gajim.common import helpers +from gajim.common.modules import dataforms from gajim.gtk.util import get_builder from gajim.gtk.util import load_icon diff --git a/gajim/gtk/service_registration.py b/gajim/gtk/service_registration.py new file mode 100644 index 000000000..6db36b628 --- /dev/null +++ b/gajim/gtk/service_registration.py @@ -0,0 +1,198 @@ +# 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 . + +import logging +from enum import IntEnum + +from gi.repository import Gtk + +from gajim.common import app +from gajim.common.modules import dataforms + +log = logging.getLogger('gajim.gtk.registration') + + +class Page(IntEnum): + REQUEST = 0 + FORM = 1 + SENDING = 2 + SUCCESS = 3 + ERROR = 4 + + +class ServiceRegistration(Gtk.Assistant): + def __init__(self, account, agent): + Gtk.Assistant.__init__(self) + + self._con = app.connections[account] + self._agent = agent + self._account = account + self._data_form_widget = None + self._is_form = None + + self.set_application(app.app) + self.set_resizable(True) + self.set_position(Gtk.WindowPosition.CENTER) + + self.set_default_size(500, 300) + self.get_style_context().add_class('dialog-margin') + + request = RequestPage() + self.append_page(request) + self.set_page_type(request, Gtk.AssistantPageType.INTRO) + + form = FormPage() + self.append_page(form) + self.set_page_type(form, Gtk.AssistantPageType.INTRO) + self.set_page_complete(form, True) + + sending = SendingPage() + self.append_page(sending) + self.set_page_type(sending, Gtk.AssistantPageType.PROGRESS) + + success = SuccessfulPage() + self.append_page(success) + self.set_page_type(success, Gtk.AssistantPageType.SUMMARY) + self.set_page_complete(success, True) + + error = ErrorPage() + self.append_page(error) + self.set_page_type(error, Gtk.AssistantPageType.SUMMARY) + self.set_page_complete(error, True) + + self.connect('prepare', self._on_page_change) + self.connect('cancel', self._on_cancel) + self.connect('close', self._on_cancel) + + self.show_all() + + def _on_page_change(self, assistant, page): + if self.get_current_page() == Page.REQUEST: + self._con.get_module('Register').get_register_form( + self._agent, self._on_get_success, self._on_error) + elif self.get_current_page() == Page.SENDING: + self._register() + self.commit() + pass + + def _on_get_success(self, form, is_form): + log.info('Show Form page') + self._is_form = is_form + if is_form: + from gajim import dataforms_widget + dataform = dataforms.ExtendForm(node=form) + self._data_form_widget = dataforms_widget.DataFormWidget(dataform) + if self._data_form_widget.title: + self.set_title('%s - Gajim' % self._data_form_widget.title) + else: + if 'registered' in form: + self.set_title(_('Edit %s') % self._agent) + else: + self.set_title(_('Register to %s') % self._agent) + from gajim import config + self._data_form_widget = config.FakeDataForm(form) + + page = self.get_nth_page(Page.FORM) + page.pack_start(self._data_form_widget, True, True, 0) + self._data_form_widget.show() + self.set_current_page(Page.FORM) + + def _on_error(self, error_text): + log.info('Show Error page') + page = self.get_nth_page(Page.ERROR) + page.set_text(error_text) + self.set_current_page(Page.ERROR) + + def _on_cancel(self, widget): + self.destroy() + + def _register(self): + log.info('Show Sending page') + if self._is_form: + form = self._data_form_widget.data_form + else: + form = self._data_form_widget.get_infos() + if 'instructions' in form: + del form['instructions'] + if 'registered' in form: + del form['registered'] + + self._con.get_module('Register').register_agent( + self._agent, + form, + self._is_form, + self._on_register_success, + self._on_error) + + def _on_register_success(self): + log.info('Show Success page') + self.set_current_page(Page.SUCCESS) + + +class RequestPage(Gtk.Box): + def __init__(self): + super().__init__(orientation=Gtk.Orientation.VERTICAL) + self.set_spacing(18) + spinner = Gtk.Spinner() + self.pack_start(spinner, True, True, 0) + spinner.start() + + +class SendingPage(RequestPage): + def __init__(self): + super().__init__() + + +class FormPage(Gtk.Box): + def __init__(self): + super().__init__(orientation=Gtk.Orientation.VERTICAL) + + +class SuccessfulPage(Gtk.Box): + def __init__(self): + super().__init__(orientation=Gtk.Orientation.VERTICAL) + self.set_spacing(12) + self.set_homogeneous(True) + + icon = Gtk.Image.new_from_icon_name('object-select-symbolic', + Gtk.IconSize.DIALOG) + icon.get_style_context().add_class('success-color') + icon.set_valign(Gtk.Align.END) + label = Gtk.Label(label=_('Registration successful')) + label.get_style_context().add_class('bold16') + label.set_valign(Gtk.Align.START) + + self.add(icon) + self.add(label) + + +class ErrorPage(Gtk.Box): + def __init__(self): + super().__init__(orientation=Gtk.Orientation.VERTICAL) + self.set_spacing(12) + self.set_homogeneous(True) + + icon = Gtk.Image.new_from_icon_name('dialog-error-symbolic', + Gtk.IconSize.DIALOG) + icon.get_style_context().add_class('error-color') + icon.set_valign(Gtk.Align.END) + self._label = Gtk.Label() + self._label.get_style_context().add_class('bold16') + self._label.set_valign(Gtk.Align.START) + + self.add(icon) + self.add(self._label) + + def set_text(self, text): + self._label.set_text(text) diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py index d24bae907..f86bebbab 100644 --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -580,17 +580,6 @@ class Interface: 'unsubscribed', 'gajim-unsubscribed', event_type, obj.jid) - @staticmethod - def handle_event_register_agent_info(obj): - # ('REGISTER_AGENT_INFO', account, (agent, infos, is_form)) - # info in a dataform if is_form is True - if obj.is_form or 'instructions' in obj.config: - config.ServiceRegistrationWindow(obj.agent, obj.config, - obj.conn.name, obj.is_form) - else: - ErrorDialog(_('Contact with "%s" cannot be established') % \ - obj.agent, _('Check your connection or try again later.')) - def handle_event_gc_config(self, obj): #('GC_CONFIG', account, (jid, form_node)) config is a dict account = obj.conn.name @@ -1565,7 +1554,6 @@ class Interface: 'password-required': [self.handle_event_password_required], 'plain-connection': [self.handle_event_plain_connection], 'presence-received': [self.handle_event_presence], - 'register-agent-info-received': [self.handle_event_register_agent_info], 'roster-info': [self.handle_event_roster_info], 'roster-item-exchange-received': \ [self.handle_event_roster_item_exchange], diff --git a/gajim/roster_window.py b/gajim/roster_window.py index f53c9a1b7..928110cbd 100644 --- a/gajim/roster_window.py +++ b/gajim/roster_window.py @@ -71,6 +71,7 @@ from gajim.gtk import AddNewContactWindow from gajim.gtk import ManagePEPServicesWindow from gajim.gtk import ManageBookmarksWindow from gajim.gtk import AccountCreationWizard +from gajim.gtk import ServiceRegistration from gajim.common.const import AvatarSize @@ -2790,7 +2791,7 @@ class RosterWindow: """ When we want to modify the agent registration """ - app.connections[account].request_register_agent_info(contact.jid) + ServiceRegistration(account, contact.jid) def on_remove_agent(self, widget, list_): """