Philipp Hörist pushed to branch master at gajim / gajim
Commits: 7286f428 by Philipp Hörist at 2018-12-18T21:41:06Z Add feature-discovered event
This lets us move more GUI code out of the common module
- - - - - ced387e7 by Philipp Hörist at 2018-12-18T21:49:07Z Remove legacy code
- - - - - de4cd80b by Philipp Hörist at 2018-12-18T21:52:29Z Make namedtuple local
- - - - - 99cfb8f6 by Philipp Hörist at 2018-12-18T23:11:24Z Cleanup Gajims disco features
- - - - - b935ce70 by Philipp Hörist at 2018-12-19T01:18:12Z Add more log output when calling handlers
- - - - - 1fec6681 by Philipp Hörist at 2018-12-19T22:00:09Z Move MUC config change handling into MUC module
- Remove status conditions code, since the XEP made a namespace bump to :1 and is now backwards compatible its useless - XEP-0045 mentions status code 100 can be in messages but there is no case given where this would ever happen, so its removed from message handling
- - - - - e9f4c505 by Philipp Hörist at 2018-12-19T22:00:50Z Refactor MUC captcha challenge code
- - - - - 5f555193 by Philipp Hörist at 2018-12-19T22:00:56Z Send active instead of gone as last chatstate
- Also attempt to fix #9446
- - - - - ce683995 by Philipp Hörist at 2018-12-19T22:00:56Z Catch mediated invites only if type is normal
https://xmpp.org/extensions/xep-0045.html#invite-mediated
- - - - - 56d2c6bc by Philipp Hörist at 2018-12-19T22:24:38Z Raise nbxmpp version
- - - - - 8b5adb06 by Philipp Hörist at 2018-12-19T22:24:39Z Allow setting handler priority
- - - - - c614ac69 by Philipp Hörist at 2018-12-19T22:24:44Z Refactor voice approval
- - - - - 1f975df8 by Philipp Hörist at 2018-12-19T22:24:49Z Remove incorrect comment
- - - - -
19 changed files:
- gajim/application.py - gajim/common/app.py - gajim/common/configpaths.py - gajim/common/connection.py - gajim/common/connection_handlers.py - gajim/common/connection_handlers_events.py - gajim/common/ged.py - gajim/common/helpers.py - gajim/common/modules/chatstates.py - gajim/common/modules/mam.py - gajim/common/modules/message.py - gajim/common/modules/muc.py - gajim/common/modules/privacylists.py - gajim/common/modules/vcard_temp.py - gajim/gajim.py - gajim/groupchat_control.py - gajim/gtk/single_message.py - gajim/remote_control.py - setup.cfg
Changes:
===================================== gajim/application.py ===================================== @@ -38,10 +38,14 @@ from datetime import datetime from pathlib import Path from urllib.parse import unquote
-from gi.repository import GLib, Gio, Gtk +import nbxmpp +from gi.repository import Gio +from gi.repository import GLib +from gi.repository import Gtk
import gajim from gajim.common import app +from gajim.common import ged from gajim.common import configpaths from gajim.common import logging_helpers from gajim.common import exceptions @@ -227,6 +231,10 @@ class GajimApplication(Gtk.Application): from gajim import gui_menu_builder gui_menu_builder.build_accounts_menu()
+ app.ged.register_event_handler('feature-discovered', + ged.CORE, + self._on_feature_discovered) + def _open_uris(self, uris): for uri in uris: app.log('uri_handler').info('open %s', uri) @@ -498,3 +506,14 @@ class GajimApplication(Gtk.Application): elif new_state and state == 'online': # We go online self.lookup_action(account + action_name).set_enabled(True) + + def _on_feature_discovered(self, event): + if event.feature == nbxmpp.NS_VCARD: + action = '%s-profile' % event.account + self.lookup_action(action).set_enabled(True) + elif event.feature in (nbxmpp.NS_MAM_1, nbxmpp.NS_MAM_2): + action = '%s-archive' % event.account + self.lookup_action(action).set_enabled(True) + elif event.feature == nbxmpp.NS_PRIVACY: + action = '%s-privacylists' % event.account + self.lookup_action(action).set_enabled(True)
===================================== gajim/common/app.py ===================================== @@ -67,13 +67,6 @@ plugin_manager = None # Plugins Manager
logger = cast(LoggerT, None)
-# For backwards compatibility needed -# some plugins use that -gajimpaths = configpaths.gajimpaths - - -RecentGroupchat = namedtuple('RecentGroupchat', ['room', 'server', 'nickname']) - css_config = None
os_info = None # used to cache os information @@ -149,17 +142,43 @@ socks5queue = None gupnp_igd = None
gajim_identity = {'type': 'pc', 'category': 'client', 'name': 'Gajim'} -gajim_common_features = [nbxmpp.NS_BYTESTREAM, nbxmpp.NS_SI, nbxmpp.NS_FILE, - nbxmpp.NS_MUC, nbxmpp.NS_MUC_USER, nbxmpp.NS_MUC_ADMIN, nbxmpp.NS_MUC_OWNER, - nbxmpp.NS_MUC_CONFIG, nbxmpp.NS_COMMANDS, nbxmpp.NS_DISCO_INFO, 'ipv6', - 'jabber:iq:gateway', nbxmpp.NS_LAST, nbxmpp.NS_PRIVACY, nbxmpp.NS_PRIVATE, - nbxmpp.NS_REGISTER, nbxmpp.NS_VERSION, nbxmpp.NS_DATA, nbxmpp.NS_ENCRYPTED, - 'msglog', 'sslc2s', 'stringprep', nbxmpp.NS_PING, nbxmpp.NS_TIME_REVISED, - nbxmpp.NS_SSN, nbxmpp.NS_MOOD, nbxmpp.NS_ACTIVITY, nbxmpp.NS_NICK, - nbxmpp.NS_ROSTERX, nbxmpp.NS_SECLABEL, nbxmpp.NS_HASHES_2, - nbxmpp.NS_HASHES_MD5, nbxmpp.NS_HASHES_SHA1, nbxmpp.NS_HASHES_SHA256, - nbxmpp.NS_HASHES_SHA512, nbxmpp.NS_CONFERENCE, nbxmpp.NS_CORRECT, - nbxmpp.NS_EME, 'urn:xmpp:avatar:metadata+notify', 'storage:bookmarks+notify'] + +gajim_common_features = [ + nbxmpp.NS_BYTESTREAM, + nbxmpp.NS_SI, + nbxmpp.NS_FILE, + nbxmpp.NS_MUC, + nbxmpp.NS_COMMANDS, + nbxmpp.NS_DISCO_INFO, + nbxmpp.NS_LAST, + nbxmpp.NS_DATA, + nbxmpp.NS_ENCRYPTED, + nbxmpp.NS_PING, + nbxmpp.NS_TIME_REVISED, + nbxmpp.NS_VERSION, + nbxmpp.NS_ROSTERX, + nbxmpp.NS_SECLABEL, + nbxmpp.NS_CONFERENCE, + nbxmpp.NS_CORRECT, + nbxmpp.NS_EME, + nbxmpp.NS_HASHES, + nbxmpp.NS_HASHES_2, + nbxmpp.NS_HASHES_MD5, + nbxmpp.NS_HASHES_SHA1, + nbxmpp.NS_HASHES_SHA256, + nbxmpp.NS_HASHES_SHA512, + nbxmpp.NS_HASHES_SHA3_256, + nbxmpp.NS_HASHES_SHA3_512, + nbxmpp.NS_HASHES_BLAKE2B_256, + nbxmpp.NS_HASHES_BLAKE2B_512, + nbxmpp.NS_JINGLE, + nbxmpp.NS_JINGLE_FILE_TRANSFER_5, + nbxmpp.NS_JINGLE_XTLS, + nbxmpp.NS_JINGLE_BYTESTREAM, + nbxmpp.NS_JINGLE_IBB, + nbxmpp.NS_AVATAR_METADATA + '+notify', + nbxmpp.NS_BOOKMARKS + '+notify', +]
# Optional features gajim supports per account gajim_optional_features = {} # type: Dict[str, List[str]] @@ -580,6 +599,9 @@ def get_recent_groupchats(account): recent_groupchats = config.get_per( 'accounts', account, 'recent_groupchats').split()
+ RecentGroupchat = namedtuple('RecentGroupchat', + ['room', 'server', 'nickname']) + recent_list = [] for groupchat in recent_groupchats: jid = nbxmpp.JID(groupchat)
===================================== gajim/common/configpaths.py ===================================== @@ -241,7 +241,3 @@ class ConfigPaths:
_paths = ConfigPaths() - -# For backwards compatibility needed -# some plugins use that -gajimpaths = _paths
===================================== gajim/common/connection.py ===================================== @@ -1684,14 +1684,6 @@ class Connection(CommonConnection, ConnectionHandlers): show=show, caps=ptype != 'unavailable')
- def send_captcha(self, jid, form_node): - if not app.account_is_connected(self.name): - return - iq = nbxmpp.Iq(typ='set', to=jid) - captcha = iq.addChild(name='captcha', namespace=nbxmpp.NS_CAPTCHA) - captcha.addChild(node=form_node) - self.connection.send(iq) - def check_unique_room_id_support(self, server, instance): if not app.account_is_connected(self.name): return
===================================== gajim/common/connection_handlers.py ===================================== @@ -585,12 +585,18 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, nbxmpp.NS_PUBKEY_PUBKEY)
for handler in modules.get_handlers(self): - con.RegisterHandler(*handler) + if len(handler) == 5: + name, func, typ, ns, priority = handler + con.RegisterHandler(name, func, typ, ns, priority=priority) + else: + con.RegisterHandler(*handler) self.handlers_registered = True
def _unregister_handlers(self): if not self.connection: return for handler in modules.get_handlers(self): + if len(handler) > 4: + handler = handler[:4] self.connection.UnregisterHandler(*handler) self.handlers_registered = False
===================================== gajim/common/connection_handlers_events.py ===================================== @@ -40,27 +40,6 @@ from gajim.common.file_props import FilesProp
log = logging.getLogger('gajim.c.connection_handlers_events')
-CONDITION_TO_CODE = { - 'realjid-public': 100, - 'affiliation-changed': 101, - 'unavailable-shown': 102, - 'unavailable-not-shown': 103, - 'configuration-changed': 104, - 'self-presence': 110, - 'logging-enabled': 170, - 'logging-disabled': 171, - 'non-anonymous': 172, - 'semi-anonymous': 173, - 'fully-anonymous': 174, - 'room-created': 201, - 'nick-assigned': 210, - 'banned': 301, - 'new-nick': 303, - 'kicked': 307, - 'removed-affiliation': 321, - 'removed-membership': 322, - 'removed-shutdown': 332, -}
class HelperEvent: def get_jid_resource(self, check_fake_jid=False): @@ -378,14 +357,7 @@ class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): self.status_code = ['destroyed'] else: self.reason = self.stanza.getReason() - conditions = self.stanza.getStatusConditions() - if conditions: - self.status_code = [] - for condition in conditions: - if condition in CONDITION_TO_CODE: - self.status_code.append(CONDITION_TO_CODE[condition]) - else: - self.status_code = self.stanza.getStatusCode() + self.status_code = self.stanza.getStatusCode()
self.role = self.stanza.getRole() self.affiliation = self.stanza.getAffiliation() @@ -429,60 +401,12 @@ class GcMessageReceivedEvent(nec.NetworkIncomingEvent): # message from server self.nick = ''
- conditions = self.stanza.getStatusConditions() - if conditions: - self.status_code = [] - for condition in conditions: - if condition in CONDITION_TO_CODE: - self.status_code.append(CONDITION_TO_CODE[condition]) - else: - self.status_code = self.stanza.getStatusCode() - if not self.stanza.getTag('body'): # no <body> - # It could be a config change. See - # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify - if self.stanza.getTag('x'): - if self.status_code != []: - app.nec.push_incoming_event(GcConfigChangedReceivedEvent( - None, conn=self.conn, msg_event=self)) - if self.msg_obj.form_node: - # It could be a voice request. See - # http://www.xmpp.org/extensions/xep-0045.html#voiceapprove - from gajim.gtk.single_message import SingleMessageWindow - SingleMessageWindow( - self.conn.name, self.fjid, - action='receive', from_whom=self.fjid, - subject='', message='', resource='', session=None, - form_node=self.msg_obj.form_node) return
from gajim.common.modules.security_labels import parse_securitylabel self.displaymarking = parse_securitylabel(self.stanza)
- self.captcha_form = None - captcha_tag = self.stanza.getTag('captcha', namespace=nbxmpp.NS_CAPTCHA) - if captcha_tag: - self.captcha_form = captcha_tag.getTag('x', - namespace=nbxmpp.NS_DATA) - for field in self.captcha_form.getTags('field'): - for media in field.getTags('media'): - for uri in media.getTags('uri'): - uri_data = uri.getData() - if uri_data.startswith('cid:'): - uri_data = uri_data[4:] - found = False - for data in self.stanza.getTags('data', - namespace=nbxmpp.NS_BOB): - if data.getAttr('cid') == uri_data: - uri.setData(data.getData()) - found = True - if not found: - self.conn.get_module('BitsOfBinary').get_bob_data( - uri_data, self.fjid, - self.conn._dispatch_gc_msg_with_captcha, - [self.stanza, self.msg_obj], 0) - return - from gajim.common.modules.misc import parse_correction self.correct_id = parse_correction(self.stanza)
===================================== gajim/common/ged.py ===================================== @@ -24,6 +24,7 @@ Global Events Dispatcher module.
import logging import traceback +import inspect
from nbxmpp import NodeProcessed
@@ -84,13 +85,19 @@ class GlobalEventsDispatcher: Error: %s''', handler, priority, event_name, error)
def raise_event(self, event_name, *args, **kwargs): - log.debug('%s Args: %s', event_name, str(args)) + log.debug('Raise event: %s', event_name) if event_name in self.handlers: node_processed = False # Iterate over a copy of the handlers list, so while iterating # the original handlers list can be modified for _priority, handler in list(self.handlers[event_name]): try: + if inspect.ismethod(handler): + log.debug('Call handler %s on %s', + handler.__name__, + handler.__self__) + else: + log.debug('Call handler %s', handler.__name__) if handler(*args, **kwargs): return True except NodeProcessed:
===================================== gajim/common/helpers.py ===================================== @@ -1161,56 +1161,47 @@ def prepare_and_validate_gpg_keyID(account, jid, keyID): return keyID
def update_optional_features(account=None): - if account: + if account is not None: accounts = [account] else: - accounts = [a for a in app.connections] - for a in accounts: - app.gajim_optional_features[a] = [] - if app.config.get_per('accounts', a, 'subscribe_mood'): - app.gajim_optional_features[a].append(nbxmpp.NS_MOOD + '+notify') - if app.config.get_per('accounts', a, 'subscribe_activity'): - app.gajim_optional_features[a].append(nbxmpp.NS_ACTIVITY + \ - '+notify') - if app.config.get_per('accounts', a, 'publish_tune'): - app.gajim_optional_features[a].append(nbxmpp.NS_TUNE) - if app.config.get_per('accounts', a, 'publish_location'): - app.gajim_optional_features[a].append(nbxmpp.NS_LOCATION) - if app.config.get_per('accounts', a, 'subscribe_tune'): - app.gajim_optional_features[a].append(nbxmpp.NS_TUNE + '+notify') - if app.config.get_per('accounts', a, 'subscribe_nick'): - app.gajim_optional_features[a].append(nbxmpp.NS_NICK + '+notify') - if app.config.get_per('accounts', a, 'subscribe_location'): - app.gajim_optional_features[a].append(nbxmpp.NS_LOCATION + \ - '+notify') + accounts = app.connections.keys() + + for account_ in accounts: + features = [] + app.gajim_optional_features[account_] = features + if app.config.get_per('accounts', account_, 'subscribe_mood'): + features.append(nbxmpp.NS_MOOD + '+notify') + if app.config.get_per('accounts', account_, 'subscribe_activity'): + features.append(nbxmpp.NS_ACTIVITY + '+notify') + if app.config.get_per('accounts', account_, 'subscribe_tune'): + features.append(nbxmpp.NS_TUNE + '+notify') + if app.config.get_per('accounts', account_, 'subscribe_nick'): + features.append(nbxmpp.NS_NICK + '+notify') + if app.config.get_per('accounts', account_, 'subscribe_location'): + features.append(nbxmpp.NS_LOCATION + '+notify') if app.config.get('outgoing_chat_state_notifactions') != 'disabled': - app.gajim_optional_features[a].append(nbxmpp.NS_CHATSTATES) + features.append(nbxmpp.NS_CHATSTATES) if not app.config.get('ignore_incoming_xhtml'): - app.gajim_optional_features[a].append(nbxmpp.NS_XHTML_IM) - if app.config.get_per('accounts', a, 'answer_receipts'): - app.gajim_optional_features[a].append(nbxmpp.NS_RECEIPTS) - app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE) + features.append(nbxmpp.NS_XHTML_IM) + if app.config.get_per('accounts', account_, 'answer_receipts'): + features.append(nbxmpp.NS_RECEIPTS) if app.is_installed('FARSTREAM'): - app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP) - app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP_AUDIO) - app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_RTP_VIDEO) - app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_ICE_UDP) - app.gajim_optional_features[a].append( - nbxmpp.NS_JINGLE_FILE_TRANSFER_5) - app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_XTLS) - app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_BYTESTREAM) - app.gajim_optional_features[a].append(nbxmpp.NS_JINGLE_IBB) + features.append(nbxmpp.NS_JINGLE_RTP) + features.append(nbxmpp.NS_JINGLE_RTP_AUDIO) + features.append(nbxmpp.NS_JINGLE_RTP_VIDEO) + features.append(nbxmpp.NS_JINGLE_ICE_UDP)
# Give plugins the possibility to add their features - app.plugin_manager.extension_point('update_caps', a) + app.plugin_manager.extension_point('update_caps', account_)
- app.caps_hash[a] = caps_cache.compute_caps_hash([app.gajim_identity], - app.gajim_common_features + app.gajim_optional_features[a]) + app.caps_hash[account_] = caps_cache.compute_caps_hash( + [app.gajim_identity], + app.gajim_common_features + features) # re-send presence with new hash - connected = app.connections[a].connected + connected = app.connections[account_].connected if connected > 1 and app.SHOW_LIST[connected] != 'invisible': - app.connections[a].change_status(app.SHOW_LIST[connected], - app.connections[a].status) + app.connections[account_].change_status( + app.SHOW_LIST[connected], app.connections[account_].status)
def jid_is_blocked(account, jid): con = app.connections[account]
===================================== gajim/common/modules/chatstates.py ===================================== @@ -171,6 +171,11 @@ class Chatstate: if contact is not None: contact = contact.as_contact() else: + # Contact not found, maybe we left the group chat + # or the contact was removed from the roster + log.info( + 'Contact %s not found, reset chatstate', jid) + self._chatstates.pop(jid, None) self._last_mouse_activity.pop(jid, None) self._last_keyboard_activity.pop(jid, None) continue @@ -206,14 +211,21 @@ class Chatstate: current_state = self._chatstates.get(contact.jid) setting = app.config.get('outgoing_chat_state_notifications') if setting == 'disabled': - # Send a last 'gone' state after user disabled chatstates + # Send a last 'active' state after user disabled chatstates if current_state is not None: - log.info('Send: %-10s - %s', State.GONE, contact.jid) - app.nec.push_outgoing_event( - MessageOutgoingEvent(None, - account=self._account, - jid=contact.jid, - chatstate=str(State.GONE))) + log.info('Send: %-10s - %s', State.ACTIVE, contact.jid) + + event_attrs = {'account': self._account, + 'jid': contact.jid, + 'chatstate': str(State.ACTIVE)} + + if contact.is_groupchat(): + app.nec.push_outgoing_event( + GcMessageOutgoingEvent(None, **event_attrs)) + else: + app.nec.push_outgoing_event( + MessageOutgoingEvent(None, **event_attrs)) + self._chatstates.pop(contact.jid, None) self._last_mouse_activity.pop(contact.jid, None) self._last_keyboard_activity.pop(contact.jid, None)
===================================== gajim/common/modules/mam.py ===================================== @@ -21,6 +21,7 @@ from datetime import datetime, timedelta import nbxmpp
from gajim.common import app +from gajim.common.nec import NetworkEvent from gajim.common.nec import NetworkIncomingEvent from gajim.common.const import ArchiveState from gajim.common.const import KindConstant @@ -65,9 +66,11 @@ class MAM:
self.available = True log.info('Discovered MAM %s: %s', self.archiving_namespace, from_) - # TODO: Move this GUI code out - action = app.app.lookup_action('%s-archive' % self._account) - action.set_enabled(True) + + app.nec.push_incoming_event( + NetworkEvent('feature-discovered', + account=self._account, + feature=self.archiving_namespace))
def _from_valid_archive(self, stanza, message, groupchat): if groupchat:
===================================== gajim/common/modules/message.py ===================================== @@ -28,7 +28,6 @@ from gajim.common.helpers import AdditionalDataDict from gajim.common.modules.security_labels import parse_securitylabel from gajim.common.modules.user_nickname import parse_nickname from gajim.common.modules.carbons import parse_carbon -from gajim.common.modules.bits_of_binary import parse_bob_data from gajim.common.modules.misc import parse_delay from gajim.common.modules.misc import parse_eme from gajim.common.modules.misc import parse_correction @@ -58,7 +57,8 @@ class Message: nbxmpp.NS_MAM_1, nbxmpp.NS_MAM_2, nbxmpp.NS_CONFERENCE, - nbxmpp.NS_IBB]) + nbxmpp.NS_IBB, + nbxmpp.NS_CAPTCHA,])
def _message_received(self, _con, stanza): # https://tools.ietf.org/html/rfc6120#section-8.1.1.1 @@ -259,8 +259,6 @@ class Message: event.additional_data.set_value( 'gajim', 'user_timestamp', user_timestamp)
- parse_bob_data(event.stanza) - event_attr = { 'popup': False, 'msg_log_id': None,
===================================== gajim/common/modules/muc.py ===================================== @@ -22,11 +22,13 @@ import weakref import nbxmpp
from gajim.common import i18n -from gajim.common.modules import dataforms from gajim.common import app from gajim.common import helpers from gajim.common.caps_cache import muc_caps_cache +from gajim.common.nec import NetworkEvent from gajim.common.nec import NetworkIncomingEvent +from gajim.common.modules import dataforms +from gajim.common.modules.bits_of_binary import parse_bob_data
log = logging.getLogger('gajim.c.m.muc')
@@ -37,8 +39,11 @@ class MUC: self._account = con.name
self.handlers = [ - ('message', self._mediated_invite, '', nbxmpp.NS_MUC_USER), + ('message', self._on_config_change, '', nbxmpp.NS_MUC_USER), + ('message', self._mediated_invite, 'normal', nbxmpp.NS_MUC_USER), ('message', self._direct_invite, '', nbxmpp.NS_CONFERENCE), + ('message', self._on_captcha_challenge, '', nbxmpp.NS_CAPTCHA), + ('message', self._on_voice_request, '', nbxmpp.NS_DATA, 45), ]
def pass_disco(self, from_, identities, features, _data, _node): @@ -110,6 +115,62 @@ class MUC: log.info('Set subject for %s', room_jid) self._con.connection.send(message)
+ def _on_voice_request(self, _con, stanza): + data_form = stanza.getTag('x', namespace=nbxmpp.NS_DATA) + if data_form is None: + return + + if stanza.getBody(): + return + + room_jid = str(stanza.getFrom()) + contact = app.contacts.get_groupchat_contact(self._account, room_jid) + if contact is None: + return + + data_form = dataforms.extend_form(data_form) + try: + if data_form['FORM_TYPE'].value != nbxmpp.NS_MUC_REQUEST: + return + except KeyError: + return + + app.nec.push_incoming_event( + NetworkEvent('voice-approval', + account=self._account, + room_jid=room_jid, + form=data_form)) + raise nbxmpp.NodeProcessed + + def _on_captcha_challenge(self, _con, stanza): + captcha = stanza.getTag('captcha', namespace=nbxmpp.NS_CAPTCHA) + if captcha is None: + return + + parse_bob_data(stanza) + room_jid = str(stanza.getFrom()) + contact = app.contacts.get_groupchat_contact(self._account, room_jid) + if contact is None: + return + + log.info('Captcha challenge received from %s', room_jid) + data_form = captcha.getTag('x', namespace=nbxmpp.NS_DATA) + data_form = dataforms.extend_form(node=data_form) + app.nec.push_incoming_event( + NetworkEvent('captcha-challenge', + account=self._account, + room_jid=room_jid, + form=data_form)) + raise nbxmpp.NodeProcessed + + def send_captcha(self, room_jid, form_node): + if not app.account_is_connected(self._account): + return + iq = nbxmpp.Iq(typ='set', to=room_jid) + captcha = iq.addChild(name='captcha', namespace=nbxmpp.NS_CAPTCHA) + captcha.addChild(node=form_node) + self._con.connection.send(iq) + def request_config(self, room_jid): if not app.account_is_connected(self._account): return @@ -154,6 +215,41 @@ class MUC: self._con.connection.SendAndCallForResponse( iq, self._default_response, {})
+ def _on_config_change(self, _con, stanza): + muc_user = stanza.getTag('x', namespace=nbxmpp.NS_MUC_USER) + if muc_user is None: + return + + if stanza.getBody(): + return + + room_list = app.contacts.get_gc_list(self._account) + room_jid = str(stanza.getFrom()) + if room_jid not in room_list: + # Message not from a group chat + return + + # https://xmpp.org/extensions/xep-0045.html#registrar-statuscodes + change_codes = ['100', '102', '103', '104', + '170', '171', '172', '173', '174'] + + codes = set() + for status in muc_user.getTags('status'): + code = status.getAttr('code') + if code in change_codes: + codes.add(code) + + if not codes: + return + + log.info('Received config change: %s', codes) + app.nec.push_incoming_event( + NetworkEvent('gc-config-changed-received', + account=self._account, + room_jid=room_jid, + status_codes=codes)) + raise nbxmpp.NodeProcessed + def destroy(self, room_jid, reason='', jid=''): if not app.account_is_connected(self._account): return @@ -272,6 +368,9 @@ class MUC: if stanza.getType() == 'error': return
+ if stanza.getBody(): + return + decline = muc_user.getTag('decline') if decline is not None:
===================================== gajim/common/modules/privacylists.py ===================================== @@ -20,6 +20,7 @@ import nbxmpp
from gajim.common import app from gajim.common import helpers +from gajim.common.nec import NetworkEvent from gajim.common.nec import NetworkIncomingEvent from gajim.common.connection_handlers_events import InformationEvent
@@ -51,9 +52,11 @@ class PrivacyLists:
self.supported = True log.info('Discovered XEP-0016: Privacy Lists: %s', from_) - # TODO: Move this GUI code out - action = app.app.lookup_action('%s-privacylists' % self._account) - action.set_enabled(True) + + app.nec.push_incoming_event( + NetworkEvent('feature-discovered', + account=self._account, + feature=nbxmpp.NS_PRIVACY))
def _list_push_received(self, _con, stanza): result = stanza.buildReply('result')
===================================== gajim/common/modules/vcard_temp.py ===================================== @@ -23,6 +23,7 @@ import nbxmpp
from gajim.common import app from gajim.common.const import RequestAvatar +from gajim.common.nec import NetworkEvent from gajim.common.nec import NetworkIncomingEvent from gajim.common.connection_handlers_events import InformationEvent
@@ -47,9 +48,10 @@ class VCardTemp:
self.supported = True log.info('Discovered vcard-temp: %s', from_) - # TODO: Move this GUI code out - action = app.app.lookup_action('%s-profile' % self._account) - action.set_enabled(True) + + app.nec.push_incoming_event(NetworkEvent('feature-discovered', + account=self._account, + feature=nbxmpp.NS_VCARD))
@staticmethod def _node_to_dict(node):
===================================== gajim/gajim.py ===================================== @@ -24,10 +24,9 @@ from ctypes import CDLL, byref, create_string_buffer from ctypes.util import find_library from distutils.version import LooseVersion as V
-# Install _() in namespace from gajim.common import i18n
-_MIN_NBXMPP_VER = "0.9.90" +_MIN_NBXMPP_VER = "0.9.90.2" _MIN_GTK_VER = "3.22.0"
===================================== gajim/groupchat_control.py ===================================== @@ -44,7 +44,6 @@ from gajim import gtkgui_helpers from gajim import gui_menu_builder from gajim import message_control from gajim import vcard -from gajim import dataforms_widget
from gajim.common.const import AvatarSize from gajim.common.caps_cache import muc_caps_cache @@ -53,7 +52,6 @@ from gajim.common import app from gajim.common import helpers from gajim.common.helpers import launch_browser_mailer from gajim.common.helpers import AdditionalDataDict -from gajim.common.modules import dataforms from gajim.common import ged from gajim.common import i18n from gajim.common.i18n import _ @@ -72,11 +70,13 @@ from gajim.gtk.dialogs import ConfirmationDialogCheck from gajim.gtk.dialogs import DoubleInputDialog from gajim.gtk.dialogs import InputDialog from gajim.gtk.dialogs import ChangeNickDialog +from gajim.gtk.single_message import SingleMessageWindow from gajim.gtk.filechoosers import AvatarChooserDialog from gajim.gtk.add_contact import AddNewContactWindow from gajim.gtk.tooltips import GCTooltip from gajim.gtk.groupchat_config import GroupchatConfig from gajim.gtk.adhoc_commands import CommandWindow +from gajim.gtk.dataform import DataFormWidget from gajim.gtk.util import NickCompletionGenerator from gajim.gtk.util import get_icon_name from gajim.gtk.util import get_affiliation_surface @@ -330,6 +330,10 @@ class GroupchatControl(ChatControlBase): self._nec_decrypted_message_received) app.ged.register_event_handler('gc-stanza-message-outgoing', ged.OUT_POSTCORE, self._message_sent) + app.ged.register_event_handler('captcha-challenge', ged.GUI1, + self._on_captcha_challenge) + app.ged.register_event_handler('voice-approval', ged.GUI1, + self._on_voice_approval) self.is_connected = False # disable win, we are not connected yet ChatControlBase.got_disconnected(self) @@ -1073,6 +1077,59 @@ class GroupchatControl(ChatControlBase): return self._update_banner_state_image()
+ def _on_voice_approval(self, event): + if event.account != self.account: + return + if event.room_jid != self.room_jid: + return + SingleMessageWindow(self.account, + self.room_jid, + action='receive', + from_whom=self.room_jid, + form_node=event.form) + + def _on_captcha_challenge(self, event): + if event.account != self.account: + return + if event.room_jid != self.room_jid: + return + + if self.form_widget: + self.form_widget.hide() + self.form_widget.destroy() + self.btn_box.destroy() + + self.form_widget = DataFormWidget(event.form) + + def on_send_dataform_clicked(widget): + if not self.form_widget: + return + form_node = self.form_widget.get_submit_form() + con = app.connections[self.account] + con.get_module('MUC').send_captcha(self.room_jid, form_node) + self.form_widget.hide() + self.form_widget.destroy() + self.btn_box.destroy() + self.form_widget = None + del self.btn_box + + # self.form_widget.connect('validated', on_send_dataform_clicked) + self.form_widget.show_all() + vbox = self.xml.get_object('gc_textviews_vbox') + vbox.pack_start(self.form_widget, False, True, 0) + + valid_button = Gtk.Button(stock=Gtk.STOCK_OK) + valid_button.connect('clicked', on_send_dataform_clicked) + self.btn_box = Gtk.HButtonBox() + self.btn_box.set_layout(Gtk.ButtonBoxStyle.END) + self.btn_box.pack_start(valid_button, True, True, 0) + self.btn_box.show_all() + vbox.pack_start(self.btn_box, False, False, 0) + if self.parent_win: + self.parent_win.redraw_tab(self, 'attention') + else: + self.attention_flag = True + def _nec_mam_decrypted_message_received(self, obj): if obj.conn.name != self.account: return @@ -1090,45 +1147,7 @@ class GroupchatControl(ChatControlBase): def _nec_gc_message_received(self, obj): if obj.room_jid != self.room_jid or obj.conn.name != self.account: return - if obj.captcha_form: - if self.form_widget: - self.form_widget.hide() - self.form_widget.destroy() - self.btn_box.destroy() - dataform = dataforms.extend_form(node=obj.captcha_form) - self.form_widget = dataforms_widget.DataFormWidget(dataform) - - def on_send_dataform_clicked(widget): - if not self.form_widget: - return - form_node = self.form_widget.data_form.get_purged() - form_node.type_ = 'submit' - obj.conn.send_captcha(self.room_jid, form_node) - self.form_widget.hide() - self.form_widget.destroy() - self.btn_box.destroy() - self.form_widget = None - del self.btn_box - - self.form_widget.connect('validated', on_send_dataform_clicked) - self.form_widget.show_all() - vbox = self.xml.get_object('gc_textviews_vbox') - vbox.pack_start(self.form_widget, False, True, 0) - - valid_button = Gtk.Button(stock=Gtk.STOCK_OK) - valid_button.connect('clicked', on_send_dataform_clicked) - self.btn_box = Gtk.HButtonBox() - self.btn_box.set_layout(Gtk.ButtonBoxStyle.END) - self.btn_box.pack_start(valid_button, True, True, 0) - self.btn_box.show_all() - vbox.pack_start(self.btn_box, False, False, 0) - if self.parent_win: - self.parent_win.redraw_tab(self, 'attention') - else: - self.attention_flag = True - if '100' in obj.status_code: - # Room is not anonymous - self.is_anonymous = False + if not obj.nick: # message from server self.print_conversation( @@ -1416,40 +1435,40 @@ class GroupchatControl(ChatControlBase): else: self.subject_button.show()
- def _nec_gc_config_changed_received(self, obj): - # statuscode is a list + def _nec_gc_config_changed_received(self, event): # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify - # http://www.xmpp.org/extensions/xep-0045.html#registrar-statuscodes... - # -init - if obj.room_jid != self.room_jid or obj.conn.name != self.account: + if event.account != self.account: + return + + if event.room_jid != self.room_jid: return
changes = [] - if '100' in obj.status_code: + if '100' in event.status_codes: # Can be a presence (see chg_contact_status in groupchat_control.py) changes.append(_('Any occupant is allowed to see your full JID')) self.is_anonymous = False - if '102' in obj.status_code: + if '102' in event.status_codes: changes.append(_('Room now shows unavailable members')) - if '103' in obj.status_code: + if '103' in event.status_codes: changes.append(_('Room now does not show unavailable members')) - if '104' in obj.status_code: + if '104' in event.status_codes: changes.append(_('A setting not related to privacy has been ' 'changed')) app.connections[self.account].get_module('Discovery').disco_muc( self.room_jid, self.update_actions, update=True) - if '170' in obj.status_code: + if '170' in event.status_codes: # Can be a presence (see chg_contact_status in groupchat_control.py) changes.append(_('Room logging is now enabled')) - if '171' in obj.status_code: + if '171' in event.status_codes: changes.append(_('Room logging is now disabled')) - if '172' in obj.status_code: + if '172' in event.status_codes: changes.append(_('Room is now non-anonymous')) self.is_anonymous = False - if '173' in obj.status_code: + if '173' in event.status_codes: changes.append(_('Room is now semi-anonymous')) self.is_anonymous = True - if '174' in obj.status_code: + if '174' in event.status_codes: changes.append(_('Room is now fully anonymous')) self.is_anonymous = True
@@ -2172,6 +2191,10 @@ class GroupchatControl(ChatControlBase): ged.GUI1, self._nec_mam_decrypted_message_received) app.ged.remove_event_handler('gc-stanza-message-outgoing', ged.OUT_POSTCORE, self._message_sent) + app.ged.remove_event_handler('captcha-challenge', ged.GUI1, + self._on_captcha_challenge) + app.ged.remove_event_handler('voice-approval', ged.GUI1, + self._on_voice_approval)
if self.is_connected: app.connections[self.account].send_gc_status(self.nick,
===================================== gajim/gtk/single_message.py ===================================== @@ -20,12 +20,11 @@ from gi.repository import GLib from gajim.common import app from gajim.common import helpers from gajim.common.i18n import _ -from gajim.common.modules import dataforms from gajim.common.connection_handlers_events import MessageOutgoingEvent
-from gajim.dataforms_widget import DataFormWidget from gajim.conversation_textview import ConversationTextview
+from gajim.gtk.dataform import DataFormWidget from gajim.gtk.dialogs import ErrorDialog from gajim.gtk.dialogs import AspellDictError from gajim.gtk.util import get_builder @@ -83,9 +82,7 @@ class SingleMessageWindow: parent_box = self.xml.get_object('conversation_scrolledwindow').\ get_parent() if form_node: - dataform = dataforms.extend_form(node=form_node) - dataform.type_ = 'submit' - self.form_widget = DataFormWidget(dataform) + self.form_widget = DataFormWidget(form_node) self.form_widget.show_all() parent_box.add(self.form_widget) parent_box.child_set_property(self.form_widget, 'position', @@ -282,7 +279,7 @@ class SingleMessageWindow: message = self.message_tv_buffer.get_text(begin, end, True)
if self.form_widget: - form_node = self.form_widget.data_form + form_node = self.form_widget.get_submit_form() else: form_node = None
===================================== gajim/remote_control.py ===================================== @@ -385,8 +385,8 @@ class GajimRemote(Server): # event has not been handled at GUI level return self.raise_signal('GCMessage', (obj.conn.name, [obj.fjid, obj.msgtxt, - obj.timestamp, obj.delayed, obj.xhtml_msgtxt, obj.status_code, - obj.displaymarking, obj.captcha_form, obj.needs_highlight])) + obj.timestamp, obj.delayed, obj.xhtml_msgtxt, + obj.displaymarking, obj.needs_highlight]))
def on_our_status(self, obj): self.raise_signal('AccountPresence', (obj.show, obj.conn.name))
===================================== setup.cfg ===================================== @@ -22,7 +22,7 @@ test_suite = test install_requires = cssutils>=1.0.2 keyring - nbxmpp>=0.9.90 + nbxmpp>=0.9.90.2 precis-i18n>=1.0.0 pyOpenSSL>=0.12
View it on GitLab: https://dev.gajim.org/gajim/gajim/compare/89d994093393d3ec010facfab6b3fc4802...