gajim-plural/gajim/common/modules/muc.py

286 lines
10 KiB
Python
Raw Normal View History

2018-07-16 23:22:33 +02:00
# 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-0045: Multi-User Chat
# XEP-0249: Direct MUC Invitations
import time
2018-07-16 23:22:33 +02:00
import logging
import nbxmpp
from nbxmpp.const import InviteType
from nbxmpp.structs import StanzaHandler
2018-07-16 23:22:33 +02:00
2018-07-21 12:21:48 +02:00
from gajim.common import i18n
2018-07-16 23:22:33 +02:00
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.modules.bits_of_binary import store_bob_data
2018-07-16 23:22:33 +02:00
log = logging.getLogger('gajim.c.m.muc')
class MUC:
def __init__(self, con):
self._con = con
self._account = con.name
self.handlers = [
StanzaHandler(name='message',
callback=self._on_subject_change,
typ='groupchat',
priority=49),
StanzaHandler(name='message',
callback=self._on_config_change,
ns=nbxmpp.NS_MUC_USER,
priority=49),
StanzaHandler(name='message',
callback=self._on_invite_or_decline,
typ='normal',
ns=nbxmpp.NS_MUC_USER,
priority=49),
StanzaHandler(name='message',
callback=self._on_invite_or_decline,
ns=nbxmpp.NS_CONFERENCE,
priority=49),
StanzaHandler(name='message',
callback=self._on_captcha_challenge,
ns=nbxmpp.NS_CAPTCHA,
priority=49),
StanzaHandler(name='message',
callback=self._on_voice_request,
ns=nbxmpp.NS_DATA,
priority=49)
2018-07-16 23:22:33 +02:00
]
self._nbmxpp_methods = [
'get_affiliation',
'set_role',
'set_affiliation',
'set_config',
'set_subject',
'cancel_config',
'send_captcha',
'decline',
'request_voice'
'destroy',
]
def __getattr__(self, key):
if key in self._nbmxpp_methods:
if not app.account_is_connected(self._account):
log.warning('Account %s not connected, cant use %s',
self._account, key)
return
module = self._con.connection.get_module('MUC')
return getattr(module, key)
2018-09-11 22:25:55 +02:00
def pass_disco(self, from_, identities, features, _data, _node):
for identity in identities:
if identity.get('category') != 'conference':
continue
if identity.get('type') != 'text':
continue
if nbxmpp.NS_MUC in features:
log.info('Discovered MUC: %s', from_)
# TODO: make this nicer
self._con.muc_jid['jabber'] = from_
raise nbxmpp.NodeProcessed
def send_muc_join_presence(self, *args, room_jid=None, password=None,
rejoin=False, **kwargs):
if not app.account_is_connected(self._account):
return
presence = self._con.get_module('Presence').get_presence(
*args, **kwargs)
2018-10-14 21:42:51 +02:00
muc_x = presence.setTag(nbxmpp.NS_MUC + ' x')
if room_jid is not None:
2018-10-14 21:42:51 +02:00
self._add_history_query(muc_x, room_jid, rejoin)
if password is not None:
2018-10-14 21:42:51 +02:00
muc_x.setTagData('password', password)
log.debug('Send MUC join presence:\n%s', presence)
self._con.connection.send(presence)
2018-10-14 21:42:51 +02:00
def _add_history_query(self, muc_x, room_jid, rejoin):
last_date = app.logger.get_room_last_message_time(
self._account, room_jid)
if not last_date:
last_date = 0
if muc_caps_cache.has_mam(room_jid):
# The room is MAM capable dont get MUC History
2018-10-14 21:42:51 +02:00
muc_x.setTag('history', {'maxchars': '0'})
else:
# Request MUC History (not MAM)
tags = {}
timeout = app.config.get_per('rooms', room_jid,
'muc_restore_timeout')
if timeout is None or timeout == -2:
timeout = app.config.get('muc_restore_timeout')
if last_date == 0 and timeout >= 0:
last_date = time.time() - timeout * 60
elif not rejoin and timeout >= 0:
last_date = max(last_date, time.time() - timeout * 60)
last_date = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime(
last_date))
tags['since'] = last_date
nb = app.config.get_per('rooms', room_jid, 'muc_restore_lines')
if nb is None or nb == -2:
nb = app.config.get('muc_restore_lines')
if nb >= 0:
tags['maxstanzas'] = nb
if tags:
2018-10-14 21:42:51 +02:00
muc_x.setTag('history', tags)
def _on_subject_change(self, _con, _stanza, properties):
if not properties.is_muc_subject:
2018-07-21 12:21:48 +02:00
return
jid = properties.jid.getBare()
contact = app.contacts.get_groupchat_contact(self._account, jid)
2018-12-19 22:55:52 +01:00
if contact is None:
return
contact.status = properties.subject
2018-12-19 22:55:52 +01:00
app.nec.push_incoming_event(
NetworkEvent('gc-subject-received',
2018-12-19 22:55:52 +01:00
account=self._account,
jid=jid,
subject=properties.subject,
nickname=properties.muc_nickname,
user_timestamp=properties.user_timestamp))
2018-12-19 22:55:52 +01:00
raise nbxmpp.NodeProcessed
def _on_voice_request(self, _con, _stanza, properties):
if not properties.is_voice_request:
2018-12-19 11:01:09 +01:00
return
jid = str(properties.jid)
contact = app.contacts.get_groupchat_contact(self._account, jid)
2018-12-19 11:01:09 +01:00
if contact is None:
return
app.nec.push_incoming_event(
NetworkEvent('voice-approval',
2018-12-19 11:01:09 +01:00
account=self._account,
jid=jid,
form=properties.voice_request.form))
2018-12-19 11:01:09 +01:00
raise nbxmpp.NodeProcessed
def _on_captcha_challenge(self, _con, _stanza, properties):
if not properties.is_captcha_challenge:
return
contact = app.contacts.get_groupchat_contact(self._account,
str(properties.jid))
if contact is None:
return
log.info('Captcha challenge received from %s', properties.jid)
store_bob_data(properties.captcha.bob_data)
app.nec.push_incoming_event(
NetworkEvent('captcha-challenge',
account=self._account,
jid=properties.jid,
form=properties.captcha.form))
raise nbxmpp.NodeProcessed
def _on_config_change(self, _con, _stanza, properties):
if not properties.is_muc_config_change:
return
log.info('Received config change: %s %s',
properties.jid, properties.muc_status_codes)
app.nec.push_incoming_event(
NetworkEvent('gc-config-changed-received',
account=self._account,
jid=properties.jid,
status_codes=properties.muc_status_codes))
raise nbxmpp.NodeProcessed
def _on_invite_or_decline(self, _con, _stanza, properties):
if properties.muc_decline is not None:
data = properties.muc_decline
if helpers.ignore_contact(self._account, data.from_):
raise nbxmpp.NodeProcessed
2018-07-16 23:22:33 +02:00
log.info('Invite declined from: %s, reason: %s',
data.from_, data.reason)
2018-07-16 23:22:33 +02:00
app.nec.push_incoming_event(
NetworkEvent('gc-decline-received',
account=self._account,
**data._asdict()))
2018-07-16 23:22:33 +02:00
raise nbxmpp.NodeProcessed
if properties.muc_invite is not None:
data = properties.muc_invite
if helpers.ignore_contact(self._account, data.from_):
raise nbxmpp.NodeProcessed
2018-07-16 23:22:33 +02:00
log.info('Invite from: %s, to: %s', data.from_, data.muc)
2018-07-16 23:22:33 +02:00
if app.in_groupchat(self._account, data.muc):
2018-07-16 23:22:33 +02:00
# We are already in groupchat. Ignore invitation
log.info('We are already in this room')
raise nbxmpp.NodeProcessed
app.nec.push_incoming_event(
NetworkEvent('gc-invitation-received',
account=self._account,
**data._asdict()))
2018-07-16 23:22:33 +02:00
raise nbxmpp.NodeProcessed
def invite(self, room, to, reason=None, continue_=False):
if not app.account_is_connected(self._account):
return
type_ = InviteType.MEDIATED
2018-07-16 23:22:33 +02:00
contact = app.contacts.get_contact_from_full_jid(self._account, to)
if contact and contact.supports(nbxmpp.NS_CONFERENCE):
type_ = InviteType.DIRECT
2018-07-16 23:22:33 +02:00
password = app.gc_passwords.get(room, None)
self._con.connection.get_module('MUC').invite(
room, to, reason, password, continue_, type_)
2018-07-16 23:22:33 +02:00
def request_config(self, room_jid):
2018-07-16 23:22:33 +02:00
if not app.account_is_connected(self._account):
return
self._con.connection.get_module('MUC').request_config(
room_jid, i18n.LANG, callback=self._config_received)
2018-07-16 23:22:33 +02:00
def _config_received(self, result):
if result.is_error:
return
2018-07-16 23:22:33 +02:00
app.nec.push_incoming_event(NetworkEvent(
'muc-owner-received',
conn=self._con,
dataform=result.form,
jid=result.jid))
2018-07-21 12:21:48 +02:00
2018-07-16 23:22:33 +02:00
def get_instance(*args, **kwargs):
return MUC(*args, **kwargs), 'MUC'