Commit 8094cadb authored by Philipp Hörist's avatar Philipp Hörist

Refactor MUC module

- nbxmpp provides now most of the MUC code
parent c63e3263
Pipeline #2889 passed with stages
in 3 minutes and 44 seconds
......@@ -1482,12 +1482,12 @@ class ChatControl(ChatControlBase):
self._add_info_bar_message(markup, [b], file_props, Gtk.MessageType.ERROR)
def _on_accept_gc_invitation(self, widget, event):
if event.is_continued:
app.interface.join_gc_room(self.account, event.room_jid,
if event.continued:
app.interface.join_gc_room(self.account, str(event.muc),
app.nicks[self.account], event.password,
is_continued=True)
else:
app.interface.join_gc_minimal(self.account, event.room_jid)
app.interface.join_gc_minimal(self.account, str(event.muc))
app.events.remove_events(self.account, self.contact.jid, event=event)
......@@ -1495,14 +1495,14 @@ class ChatControl(ChatControlBase):
app.events.remove_events(self.account, self.contact.jid, event=event)
def _get_gc_invitation(self, event):
markup = '<b>%s:</b> %s' % (_('Groupchat Invitation'), event.room_jid)
markup = '<b>%s:</b> %s' % (_('Groupchat Invitation'), event.muc)
if event.reason:
markup += ' (%s)' % event.reason
b1 = Gtk.Button.new_with_mnemonic(_('_Join'))
b1.connect('clicked', self._on_accept_gc_invitation, event)
b2 = Gtk.Button(stock=Gtk.STOCK_CANCEL)
b2.connect('clicked', self._on_cancel_gc_invitation, event)
self._add_info_bar_message(markup, [b1, b2], (event.room_jid,
self._add_info_bar_message(markup, [b1, b2], (event.muc,
event.reason), Gtk.MessageType.QUESTION)
def on_event_added(self, event):
......@@ -1546,7 +1546,7 @@ class ChatControl(ChatControlBase):
removed = False
for ib_msg in self.info_bar_queue:
if ev.type_ == 'gc-invitation':
if ev.room_jid == ib_msg[2][0]:
if ev.muc == ib_msg[2][0]:
self.info_bar_queue.remove(ib_msg)
removed = True
else: # file-*
......
......@@ -480,6 +480,7 @@ def zeroconf_is_connected():
config.get_per('accounts', ZEROCONF_ACC_NAME, 'is_zeroconf')
def in_groupchat(account, room_jid):
room_jid = str(room_jid)
if room_jid not in gc_connected[account]:
return False
return gc_connected[account][room_jid]
......
......@@ -307,12 +307,12 @@ class LegacyContactsAPI:
return self_contact
def create_not_in_roster_contact(self, jid, account, resource='', name='',
keyID=''):
keyID='', groupchat=False):
# Use Account object if available
account = self._accounts.get(account, account)
return self.create_contact(jid=jid, account=account, resource=resource,
name=name, groups=[_('Not in Roster')], show='not in roster',
status='', sub='none', keyID=keyID)
status='', sub='none', keyID=keyID, groupchat=groupchat)
def copy_contact(self, contact):
return self.create_contact(contact.jid, contact.account,
......
......@@ -130,15 +130,10 @@ class UnsubscribedEvent(Event):
class GcInvitationtEvent(Event):
type_ = 'gc-invitation'
def __init__(self, room_jid, reason, password, is_continued, jid_from,
time_=None, show_in_roster=False, show_in_systray=True):
Event.__init__(self, time_, show_in_roster=show_in_roster,
show_in_systray=show_in_systray)
self.room_jid = room_jid
self.reason = reason
self.password = password
self.is_continued = is_continued
self.jid_from = jid_from
def __init__(self, event):
Event.__init__(self, None, show_in_roster=False, show_in_systray=True)
for key, value in vars(event).items():
setattr(self, key, value)
class FileRequestEvent(Event):
type_ = 'file-request'
......
......@@ -1462,6 +1462,15 @@ def load_json(path, key=None, default=None):
return json_dict
return json_dict.get(key, default)
def ignore_contact(account, jid):
jid = str(jid)
known_contact = app.contacts.get_contacts(account, jid)
ignore = app.config.get_per('accounts', account, 'ignore_unknown_contacts')
if ignore and not known_contact:
log.info('Ignore unknown contact %s', jid)
return True
return False
class AdditionalDataDict(collections.UserDict):
def __init__(self, initialdata=None):
collections.UserDict.__init__(self, initialdata)
......
......@@ -170,5 +170,29 @@ def parse_bob_data(stanza):
return filepath
def store_bob_data(bob_data):
if bob_data is None:
return
algo_hash = '%s+%s' % (bob_data.algo, bob_data.hash_)
filepath = Path(configpaths.get('BOB')) / algo_hash
if algo_hash in app.bob_cache or filepath.exists():
log.info('BoB data already cached')
return
if bob_data.max_age == 0:
app.bob_cache[algo_hash] = bob_data.data
else:
try:
with open(str(filepath), 'w+b') as file:
file.write(bob_data.data)
except Exception:
log.exception('Unable to save data')
return
log.info('BoB data stored: %s', algo_hash)
return filepath
def get_instance(*args, **kwargs):
return BitsOfBinary(*args, **kwargs), 'BitsOfBinary'
......@@ -212,31 +212,14 @@ class Message:
subject = event.stanza.getSubject()
groupchat = event.mtype == 'groupchat'
# XEP-0045: only a message that contains a <subject/> but no <body/>
# element shall be considered a subject change for MUC purposes.
muc_subject = subject and groupchat and not event.msgtxt
# Determine timestamps
if groupchat:
delay_entity_jid = event.jid
else:
delay_entity_jid = self._con.get_own_jid().getDomain()
if muc_subject:
# MUC Subjects can have a delay timestamp
# to indicate when the user has set the subject,
# the 'from' attr on these delays is the MUC server
# but we treat it as user timestamp
delay_jid = self._con.get_own_jid().getDomain()
timestamp = parse_delay(event.stanza, from_=delay_jid)
if timestamp is None:
timestamp = time.time()
user_timestamp = parse_delay(event.stanza, from_=delay_entity_jid)
else:
timestamp = parse_delay(event.stanza, from_=delay_entity_jid)
if timestamp is None:
timestamp = time.time()
user_timestamp = parse_delay(event.stanza,
not_from=[delay_entity_jid])
user_timestamp = parse_delay(event.stanza,
not_from=[delay_jid])
if user_timestamp is not None:
event.additional_data.set_value(
......@@ -271,11 +254,6 @@ class Message:
event.session, event.fjid, timestamp)
return
if muc_subject:
app.nec.push_incoming_event(NetworkEvent('gc-subject-received',
**vars(event)))
return
if groupchat:
if not event.msgtxt:
return
......
This diff is collapsed.
......@@ -1291,51 +1291,60 @@ class RosterItemExchangeWindow:
class InvitationReceivedDialog:
def __init__(self, account, room_jid, contact_fjid, password=None,
comment=None, is_continued=False):
self.room_jid = room_jid
def __init__(self, account, event):
self.account = account
self.password = password
self.is_continued = is_continued
self.contact_fjid = contact_fjid
self.room_jid = str(event.muc)
self.from_ = str(event.from_)
self.password = event.password
self.is_continued = event.continued
jid = app.get_jid_without_resource(contact_fjid)
if event.from_.bareMatch(event.muc):
contact_text = event.from_.getResource()
else:
contact = app.contacts.get_first_contact_from_jid(
account, event.from_.getBare())
if contact is None:
contact_text = str(event.from_)
else:
contact_text = contact.get_shown_name()
pritext = _('''You are invited to a groupchat''')
#Don't translate $Contact
if is_continued:
if self.is_continued:
sectext = _('$Contact has invited you to join a discussion')
else:
sectext = _('$Contact has invited you to group chat %(room_jid)s')\
% {'room_jid': room_jid}
contact = app.contacts.get_first_contact_from_jid(account, jid)
contact_text = contact and contact.name or jid
sectext = i18n.direction_mark + sectext.replace('$Contact',
contact_text)
% {'room_jid': self.room_jid}
sectext = sectext.replace('$Contact', contact_text)
if comment: # only if not None and not ''
comment = GLib.markup_escape_text(comment)
if event.reason:
comment = GLib.markup_escape_text(event.reason)
comment = _('Comment: %s') % comment
sectext += '\n\n%s' % comment
sectext += '\n\n' + _('Do you want to accept the invitation?')
def on_yes(checked, text):
def on_yes(_checked, _text):
if self.is_continued:
app.interface.join_gc_room(self.account, self.room_jid,
app.nicks[self.account], self.password,
is_continued=True)
app.interface.join_gc_room(self.account,
self.room_jid,
app.nicks[self.account],
self.password,
is_continued=True)
else:
app.interface.join_gc_minimal(
self.account, self.room_jid, password=self.password)
app.interface.join_gc_minimal(self.account,
self.room_jid,
password=self.password)
def on_no(text):
app.connections[account].get_module('MUC').decline(
self.room_jid, self.contact_fjid, text)
self.room_jid, self.from_, text)
dlg = YesNoDialog(pritext, sectext,
text_label=_('Reason (if you decline):'), on_response_yes=on_yes,
on_response_no=on_no)
dlg = YesNoDialog(pritext,
sectext,
text_label=_('Reason (if you decline):'),
on_response_yes=on_yes,
on_response_no=on_no)
dlg.set_title(_('Groupchat Invitation'))
class ProgressDialog:
......
......@@ -33,6 +33,7 @@ import logging
from enum import IntEnum, unique
import nbxmpp
from nbxmpp.const import StatusCode
from gi.repository import Gtk
from gi.repository import Gdk
......@@ -1080,7 +1081,7 @@ class GroupchatControl(ChatControlBase):
def _on_voice_approval(self, event):
if event.account != self.account:
return
if event.room_jid != self.room_jid:
if event.jid != self.room_jid:
return
SingleMessageWindow(self.account,
self.room_jid,
......@@ -1091,7 +1092,7 @@ class GroupchatControl(ChatControlBase):
def _on_captcha_challenge(self, event):
if event.account != self.account:
return
if event.room_jid != self.room_jid:
if event.jid != self.room_jid:
return
if self.form_widget:
......@@ -1419,11 +1420,11 @@ class GroupchatControl(ChatControlBase):
return
self.set_subject(event.subject)
text = _('%(nick)s has set the subject to %(subject)s') % {
'nick': event.resource, 'subject': event.subject}
'nick': event.nickname, 'subject': event.subject}
if event.delayed:
if event.user_timestamp:
date = time.strftime('%d-%m-%Y %H:%M:%S',
time.localtime(event.timestamp))
time.localtime(event.user_timestamp))
text = '%s - %s' % (text, date)
just_joined = self.join_time > time.time() - 10
......@@ -1440,35 +1441,31 @@ class GroupchatControl(ChatControlBase):
if event.account != self.account:
return
if event.room_jid != self.room_jid:
if event.jid != self.room_jid:
return
changes = []
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 event.status_codes:
if StatusCode.SHOWING_UNAVAILABLE in event.status_codes:
changes.append(_('Room now shows unavailable members'))
if '103' in event.status_codes:
if StatusCode.NOT_SHOWING_UNAVAILABLE in event.status_codes:
changes.append(_('Room now does not show unavailable members'))
if '104' in event.status_codes:
if StatusCode.CONFIG_NON_PRIVACY_RELATED 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 event.status_codes:
if StatusCode.CONFIG_ROOM_LOGGING 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 event.status_codes:
if StatusCode.CONFIG_NO_ROOM_LOGGING in event.status_codes:
changes.append(_('Room logging is now disabled'))
if '172' in event.status_codes:
if StatusCode.CONFIG_NON_ANONYMOUS in event.status_codes:
changes.append(_('Room is now non-anonymous'))
self.is_anonymous = False
if '173' in event.status_codes:
if StatusCode.CONFIG_SEMI_ANONYMOUS in event.status_codes:
changes.append(_('Room is now semi-anonymous'))
self.is_anonymous = True
if '174' in event.status_codes:
if StatusCode.CONFIG_FULL_ANONYMOUS in event.status_codes:
changes.append(_('Room is now fully anonymous'))
self.is_anonymous = True
......
......@@ -62,8 +62,7 @@ class GroupchatConfig(Gtk.ApplicationWindow):
con.get_module('MUC').get_affiliation(
self.jid,
affiliation,
self._on_affiliations_received,
self._on_affiliations_error)
callback=self._on_affiliations_received)
if form is not None:
self._ui.stack.set_visible_child_name('config')
......@@ -340,29 +339,28 @@ class GroupchatConfig(Gtk.ApplicationWindow):
con = app.connections[self.account]
con.get_module('MUC').set_affiliation(self.jid, diff_dict)
def _on_affiliations_error(self, affiliation, error):
log.info('Error while requesting %s affiliations: %s',
affiliation, error)
def _on_affiliations_received(self, _account, _room_jid,
affiliation, users):
def _on_affiliations_received(self, result):
if result.is_error:
log.info('Error while requesting %s affiliations: %s',
result.affiliation, result.error)
return
if affiliation == 'outcast':
if result.affiliation == 'outcast':
self._ui.stack.get_child_by_name('outcast').show()
for jid, attrs in users.items():
affiliation_edit, jid_edit = self._allowed_to_edit(affiliation)
if affiliation == 'outcast':
for jid, attrs in result.users.items():
affiliation_edit, jid_edit = self._allowed_to_edit(result.affiliation)
if result.affiliation == 'outcast':
reason = attrs.get('reason')
self._ui.outcast_store.append(
[jid,
reason,
None,
affiliation,
result.affiliation,
None,
affiliation_edit,
jid_edit])
self._affiliations[jid] = {'affiliation': affiliation,
self._affiliations[jid] = {'affiliation': result.affiliation,
'reason': reason}
else:
nick = attrs.get('nick')
......@@ -371,11 +369,11 @@ class GroupchatConfig(Gtk.ApplicationWindow):
[jid,
nick,
role,
affiliation,
_(affiliation.capitalize()),
result.affiliation,
_(result.affiliation.capitalize()),
affiliation_edit,
jid_edit])
self._affiliations[jid] = {'affiliation': affiliation,
self._affiliations[jid] = {'affiliation': result.affiliation,
'nick': nick}
if role is not None:
self._ui.role_column.set_visible(True)
......
......@@ -109,7 +109,7 @@ class Notification:
def _on_event_removed(self, event_list):
for event in event_list:
if event.type_ == 'gc-invitation':
self._withdraw('gc-invitation', event.account, event.room_jid)
self._withdraw('gc-invitation', event.account, event.muc)
if event.type_ in ('normal', 'printed_chat', 'chat',
'printed_pm', 'pm', 'printed_marked_gc_msg',
'printed_gc_msg'):
......
......@@ -609,39 +609,43 @@ class Interface:
else:
GroupchatConfig(account, obj.jid, 'owner', obj.dataform)
def handle_event_gc_decline(self, obj):
gc_control = self.msg_win_mgr.get_gc_control(obj.room_jid, obj.account)
def handle_event_gc_decline(self, event):
gc_control = self.msg_win_mgr.get_gc_control(str(event.muc),
event.account)
if gc_control:
if obj.reason:
if event.reason:
gc_control.print_conversation(
_('%(jid)s declined the invitation: %(reason)s') % {
'jid': obj.from_, 'reason': obj.reason},
'jid': event.from_, 'reason': event.reason},
graphics=False)
else:
gc_control.print_conversation(
_('%(jid)s declined the invitation') % {
'jid': obj.from_}, graphics=False)
def handle_event_gc_invitation(self, obj):
if helpers.allow_popup_window(obj.account) or not self.systray_enabled:
dialogs.InvitationReceivedDialog(
obj.account, obj.room_jid,
str(obj.from_), obj.password, obj.reason,
is_continued=obj.is_continued)
'jid': event.from_}, graphics=False)
def handle_event_gc_invitation(self, event):
if helpers.allow_popup_window(event.account) or not self.systray_enabled:
dialogs.InvitationReceivedDialog(event.account, event)
return
event = events.GcInvitationtEvent(
obj.room_jid, obj.reason,
obj.password, obj.is_continued, str(obj.from_))
self.add_event(obj.account, str(obj.from_), event)
from_ = str(event.from_)
muc = str(event.muc)
event_ = events.GcInvitationtEvent(event)
self.add_event(event.account, from_, event_)
if helpers.allow_showing_notification(obj.account):
if helpers.allow_showing_notification(event.account):
event_type = _('Groupchat Invitation')
text = _('You are invited to {room} by {user}').format(
room=obj.room_jid, user=str(obj.from_))
app.notification.popup(
event_type, str(obj.from_), obj.account, 'gc-invitation',
'gajim-gc_invitation', event_type, text, room_jid=obj.room_jid)
text = _('You are invited to {room} by {user}').format(room=muc,
user=from_)
app.notification.popup(event_type,
from_,
event.account,
'gc-invitation',
'gajim-gc_invitation',
event_type,
text,
room_jid=muc)
def forget_gpg_passphrase(self, keyid):
if keyid in self.gpg_passphrase:
......@@ -1523,7 +1527,9 @@ class Interface:
if app.contacts.get_contact_with_highest_priority(account, jid):
self.roster.draw_contact(jid, account)
else:
self.roster.add_to_not_in_the_roster(account, jid)
groupchat = event.type_ == 'gc-invitation'
self.roster.add_to_not_in_the_roster(
account, jid, groupchat=groupchat)
# Select the big brother contact in roster, it's visible because it has
# events.
......@@ -1621,8 +1627,7 @@ class Interface:
event = app.events.get_first_event(account, jid, type_)
if event is None:
return
dialogs.InvitationReceivedDialog(account, event.room_jid, jid,
event.password, event.reason, event.is_continued)
dialogs.InvitationReceivedDialog(account, event)
app.events.remove_events(account, jid, event)
self.roster.draw_contact(jid, account)
elif type_ == 'subscription_request':
......
......@@ -1032,14 +1032,16 @@ class RosterWindow:
self.draw_group(group, account)
# FIXME: integrate into add_contact()
def add_to_not_in_the_roster(self, account, jid, nick='', resource=''):
def add_to_not_in_the_roster(self, account, jid, nick='', resource='',
groupchat=False):
keyID = ''
attached_keys = app.config.get_per('accounts', account,
'attached_gpg_keys').split()
if jid in attached_keys:
keyID = attached_keys[attached_keys.index(jid) + 1]
contact = app.contacts.create_not_in_roster_contact(jid=jid,
account=account, resource=resource, name=nick, keyID=keyID)
contact = app.contacts.create_not_in_roster_contact(
jid=jid, account=account, resource=resource, name=nick,
keyID=keyID, groupchat=groupchat)
app.contacts.add_contact(account, contact)
self.add_contact(contact.jid, account)
return contact
......@@ -2002,9 +2004,7 @@ class RosterWindow:
return True
if event.type_ == 'gc-invitation':
dialogs.InvitationReceivedDialog(account, event.room_jid,
event.jid_from, event.password, event.reason,
is_continued=event.is_continued)
dialogs.InvitationReceivedDialog(account, event)
app.events.remove_events(account, jid, event)
return True
......@@ -2692,12 +2692,8 @@ class RosterWindow:
app.log('avatar').debug('Draw roster avatar: %s', obj.jid)
self.draw_avatar(obj.jid, obj.account)
def _nec_gc_subject_received(self, obj):
contact = app.contacts.get_contact_with_highest_priority(
obj.account, obj.jid)
if contact:
contact.status = obj.subject
self.draw_contact(obj.jid, obj.account)
def _nec_gc_subject_received(self, event):
self.draw_contact(event.jid, event.account)
def _nec_metacontacts_received(self, obj):
self.redraw_metacontacts(obj.conn.name)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment