gajim-plural/gajim/common/connection_handlers_events.py

3053 lines
110 KiB
Python

# -*- coding:utf-8 -*-
## src/common/connection_handlers_events.py
##
## Copyright (C) 2010-2014 Yann Leboulanger <asterix AT lagaule.org>
##
## 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/>.
##
# pylint: disable=no-init
# pylint: disable=attribute-defined-outside-init
from calendar import timegm
import datetime
import hashlib
import hmac
import logging
import sys
import os
from time import time as time_time
import nbxmpp
from nbxmpp.protocol import NS_CHATSTATES
from gajim.common import atom
from gajim.common import nec
from gajim.common import helpers
from gajim.common import app
from gajim.common import i18n
from gajim.common import dataforms
from gajim.common.zeroconf.zeroconf import Constant
from gajim.common.logger import KindConstant
from gajim.common.pep import SUPPORTED_PERSONAL_USER_EVENTS
from gajim.common.jingle_transport import JingleTransportSocks5
from gajim.common.file_props import FilesProp
from gajim.common.nec import NetworkEvent
if app.HAVE_PYOPENSSL:
import OpenSSL.crypto
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):
if check_fake_jid and hasattr(self, 'id_') and \
self.id_ in self.conn.groupchat_jids:
self.fjid = self.conn.groupchat_jids[self.id_]
del self.conn.groupchat_jids[self.id_]
else:
self.fjid = helpers.get_full_jid_from_iq(self.stanza)
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
def get_id(self):
self.id_ = self.stanza.getID()
def get_gc_control(self):
self.gc_control = app.interface.msg_win_mgr.get_gc_control(self.jid,
self.conn.name)
# If gc_control is missing - it may be minimized. Try to get it
# from there. If it's not there - then it's missing anyway and
# will remain set to None.
if not self.gc_control:
minimized = app.interface.minimized_controls[self.conn.name]
self.gc_control = minimized.get(self.jid)
def _generate_timestamp(self, tag):
# Make sure we use only int/float Epoch time
if tag is None:
self.timestamp = time_time()
return
try:
tim = helpers.datetime_tuple(tag)
self.timestamp = timegm(tim)
except Exception:
log.error('wrong timestamp, ignoring it: ' + tag)
self.timestamp = time_time()
def get_chatstate(self):
"""
Extract chatstate from a <message/> stanza
Requires self.stanza and self.msgtxt
"""
self.chatstate = None
# chatstates - look for chatstate tags in a message if not delayed
delayed = self.stanza.getTag('x', namespace=nbxmpp.NS_DELAY) is not None
if not delayed:
children = self.stanza.getChildren()
for child in children:
if child.getNamespace() == NS_CHATSTATES:
self.chatstate = child.getName()
break
def get_oob_data(self, stanza):
oob_node = stanza.getTag('x', namespace=nbxmpp.NS_X_OOB)
if oob_node is not None:
if 'gajim' not in self.additional_data:
self.additional_data['gajim'] = {}
oob_url = oob_node.getTagData('url')
if oob_url is not None:
self.additional_data['gajim']['oob_url'] = oob_url
oob_desc = oob_node.getTagData('desc')
if oob_desc is not None:
self.additional_data['gajim']['oob_desc'] = oob_desc
def get_stanza_id(self, stanza, query=False):
if query:
# On a MAM query the stanza-id is maybe not set, so
# get the id of the stanza
return stanza.getAttr('id')
stanza_id, by = stanza.getStanzaIDAttrs()
if by is None:
# We can not verify who set this stanza-id, ignore it.
return
if stanza.getType() == 'groupchat':
if stanza.getFrom().bareMatch(by):
# by attribute must match the server
return stanza_id
elif self.conn.get_own_jid().bareMatch(by):
# by attribute must match the server
return stanza_id
return
@staticmethod
def get_forwarded_message(stanza):
forwarded = stanza.getTag('forwarded',
namespace=nbxmpp.NS_FORWARD,
protocol=True)
if forwarded is not None:
return forwarded.getTag('message', protocol=True)
def _is_self_message(self, message):
if self.self_message is not None:
return self.self_message
own_jid = self.conn.get_own_jid()
frm = message.getFrom()
to = message.getTo()
# If 'to' is not set we assume own jid
self.self_message = frm.bareMatch(to or own_jid)
return self.self_message
def _is_muc_pm(self, message):
if self.muc_pm is not None:
return self.muc_pm
self.muc_pm = False
muc_user = message.getTag('x', namespace=nbxmpp.NS_MUC_USER)
if muc_user is not None:
self.muc_pm = muc_user.getChildren() == []
return self.muc_pm
class HttpAuthReceivedEvent(nec.NetworkIncomingEvent):
name = 'http-auth-received'
base_network_events = []
def generate(self):
self.opt = app.config.get_per('accounts', self.conn.name, 'http_auth')
self.iq_id = self.stanza.getTagAttr('confirm', 'id')
self.method = self.stanza.getTagAttr('confirm', 'method')
self.url = self.stanza.getTagAttr('confirm', 'url')
# In case it's a message with a body
self.msg = self.stanza.getTagData('body')
return True
class VersionResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'version-result-received'
base_network_events = []
def generate(self):
self.get_id()
self.get_jid_resource(check_fake_jid=True)
if self.id_ in self.conn.version_ids:
self.conn.version_ids.remove(self.id_)
self.client_info = ''
self.os_info = ''
if self.stanza.getType() == 'error':
return True
qp = self.stanza.getTag('query')
if qp.getTag('name'):
self.client_info += qp.getTag('name').getData()
if qp.getTag('version'):
self.client_info += ' ' + qp.getTag('version').getData()
if qp.getTag('os'):
self.os_info += qp.getTag('os').getData()
return True
class TimeResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'time-result-received'
base_network_events = []
def generate(self):
self.get_id()
self.get_jid_resource(check_fake_jid=True)
if self.id_ in self.conn.entity_time_ids:
self.conn.entity_time_ids.remove(self.id_)
self.time_info = ''
if self.stanza.getType() == 'error':
return True
qp = self.stanza.getTag('time')
if not qp:
# wrong answer
return
tzo = qp.getTag('tzo').getData()
if tzo.lower() == 'z':
tzo = '0:0'
try:
tzoh, tzom = tzo.split(':')
except Exception as e:
# wrong tzo
return
utc_time = qp.getTag('utc').getData()
ZERO = datetime.timedelta(0)
class UTC(datetime.tzinfo):
def utcoffset(self, dt):
return ZERO
def tzname(self, dt):
return "UTC"
def dst(self, dt):
return ZERO
class contact_tz(datetime.tzinfo):
def utcoffset(self, dt):
return datetime.timedelta(hours=int(tzoh), minutes=int(tzom))
def tzname(self, dt):
return "remote timezone"
def dst(self, dt):
return ZERO
if utc_time[-1:] == 'Z':
# Remove the trailing 'Z'
utc_time = utc_time[:-1]
elif utc_time[-6:] == "+00:00":
# Remove the trailing "+00:00"
utc_time = utc_time[:-6]
else:
log.info("Wrong timezone defintion: %s" % utc_time)
return
try:
t = datetime.datetime.strptime(utc_time, '%Y-%m-%dT%H:%M:%S')
except ValueError:
try:
t = datetime.datetime.strptime(utc_time,
'%Y-%m-%dT%H:%M:%S.%f')
except ValueError as e:
log.info('Wrong time format: %s' % str(e))
return
t = t.replace(tzinfo=UTC())
self.time_info = t.astimezone(contact_tz()).strftime('%c')
return True
class RosterItemExchangeEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'roster-item-exchange-received'
base_network_events = []
def generate(self):
self.get_id()
self.get_jid_resource()
self.exchange_items_list = {}
items_list = self.stanza.getTag('x').getChildren()
if not items_list:
return
self.action = items_list[0].getAttr('action')
if self.action is None:
self.action = 'add'
for item in self.stanza.getTag('x', namespace=nbxmpp.NS_ROSTERX).\
getChildren():
try:
jid = helpers.parse_jid(item.getAttr('jid'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it' % item.getAttr('jid'))
continue
name = item.getAttr('name')
contact = app.contacts.get_contact(self.conn.name, jid)
groups = []
same_groups = True
for group in item.getTags('group'):
groups.append(group.getData())
# check that all suggested groups are in the groups we have for
# this contact
if not contact or group not in contact.groups:
same_groups = False
if contact:
# check that all groups we have for this contact are in the
# suggested groups
for group in contact.groups:
if group not in groups:
same_groups = False
if contact.sub in ('both', 'to') and same_groups:
continue
self.exchange_items_list[jid] = []
self.exchange_items_list[jid].append(name)
self.exchange_items_list[jid].append(groups)
if self.exchange_items_list:
return True
class VersionRequestEvent(nec.NetworkIncomingEvent):
name = 'version-request-received'
base_network_events = []
class LastRequestEvent(nec.NetworkIncomingEvent):
name = 'last-request-received'
base_network_events = []
class TimeRequestEvent(nec.NetworkIncomingEvent):
name = 'time-request-received'
base_network_events = []
class TimeRevisedRequestEvent(nec.NetworkIncomingEvent):
name = 'time-revised-request-received'
base_network_events = []
class RosterReceivedEvent(nec.NetworkIncomingEvent):
name = 'roster-received'
base_network_events = []
def generate(self):
if hasattr(self, 'xmpp_roster'):
self.version = self.xmpp_roster.version
self.received_from_server = self.xmpp_roster.received_from_server
self.roster = {}
raw_roster = self.xmpp_roster.getRaw()
our_jid = app.get_jid_from_account(self.conn.name)
for jid in raw_roster:
try:
j = helpers.parse_jid(jid)
except Exception:
print(_('JID %s is not RFC compliant. It will not be added '
'to your roster. Use roster management tools such as '
'http://jru.jabberstudio.org/ to remove it') % jid,
file=sys.stderr)
else:
infos = raw_roster[jid]
if jid != our_jid and (not infos['subscription'] or \
infos['subscription'] == 'none') and (not infos['ask'] or \
infos['ask'] == 'none') and not infos['name'] and \
not infos['groups']:
# remove this useless item, it won't be shown in roster
# anyway
self.conn.connection.getRoster().delItem(jid)
elif jid != our_jid: # don't add our jid
self.roster[j] = raw_roster[jid]
self.roster[j]['avatar_sha'] = None
else:
# Roster comes from DB
self.received_from_server = False
self.version = app.config.get_per('accounts', self.conn.name,
'roster_version')
self.roster = app.logger.get_roster(app.get_jid_from_account(
self.conn.name))
if not self.roster:
app.config.set_per(
'accounts', self.conn.name, 'roster_version', '')
return True
class RosterSetReceivedEvent(nec.NetworkIncomingEvent):
name = 'roster-set-received'
base_network_events = []
def generate(self):
frm = helpers.get_jid_from_iq(self.stanza)
our_jid = app.get_jid_from_account(self.conn.name)
if frm and frm != our_jid and frm != app.get_server_from_jid(our_jid):
return
self.version = self.stanza.getTagAttr('query', 'ver')
self.items = {}
for item in self.stanza.getTag('query').getChildren():
try:
jid = helpers.parse_jid(item.getAttr('jid'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it' % item.getAttr('jid'))
continue
name = item.getAttr('name')
sub = item.getAttr('subscription')
ask = item.getAttr('ask')
groups = []
for group in item.getTags('group'):
groups.append(group.getData())
self.items[jid] = {'name': name, 'sub': sub, 'ask': ask,
'groups': groups}
if len(self.items) > 1:
reply = nbxmpp.Iq(typ='error', attrs={'id': self.stanza.getID()},
to=self.stanza.getFrom(), frm=self.stanza.getTo(), xmlns=None)
self.conn.connection.send(reply)
return
if self.conn.connection and self.conn.connected > 1:
reply = nbxmpp.Iq(typ='result', attrs={'id': self.stanza.getID()},
to=self.stanza.getFrom(), frm=self.stanza.getTo(), xmlns=None)
self.conn.connection.send(reply)
return True
class RosterInfoEvent(nec.NetworkIncomingEvent):
name = 'roster-info'
base_network_events = []
def init(self):
self.avatar_sha = None
class MucOwnerReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'muc-owner-received'
base_network_events = []
def generate(self):
self.get_jid_resource()
qp = self.stanza.getQueryPayload()
self.form_node = None
for q in qp:
if q.getNamespace() == nbxmpp.NS_DATA:
self.form_node = q
self.dataform = dataforms.ExtendForm(node=self.form_node)
return True
class MucAdminReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'muc-admin-received'
base_network_events = []
def generate(self):
self.get_jid_resource()
items = self.stanza.getTag('query',
namespace=nbxmpp.NS_MUC_ADMIN).getTags('item')
self.users_dict = {}
for item in items:
if item.has_attr('jid') and item.has_attr('affiliation'):
try:
jid = helpers.parse_jid(item.getAttr('jid'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it' % \
item.getAttr('jid'))
continue
affiliation = item.getAttr('affiliation')
self.users_dict[jid] = {'affiliation': affiliation}
if item.has_attr('nick'):
self.users_dict[jid]['nick'] = item.getAttr('nick')
if item.has_attr('role'):
self.users_dict[jid]['role'] = item.getAttr('role')
reason = item.getTagData('reason')
if reason:
self.users_dict[jid]['reason'] = reason
return True
class PrivateStorageReceivedEvent(nec.NetworkIncomingEvent):
name = 'private-storage-received'
base_network_events = []
def generate(self):
query = self.stanza.getTag('query')
self.storage_node = query.getTag('storage')
if self.storage_node:
self.namespace = self.storage_node.getNamespace()
return True
class BookmarksHelper:
def parse_bookmarks(self):
self.bookmarks = []
NS_GAJIM_BM = 'xmpp:gajim.org/bookmarks'
confs = self.storage_node.getTags('conference')
for conf in confs:
autojoin_val = conf.getAttr('autojoin')
if not autojoin_val: # not there (it's optional)
autojoin_val = False
minimize_val = conf.getTag('minimize', namespace=NS_GAJIM_BM)
if not minimize_val: # not there, try old Gajim behaviour
minimize_val = conf.getAttr('minimize')
if not minimize_val: # not there (it's optional)
minimize_val = False
else:
minimize_val = minimize_val.getData()
print_status = conf.getTag('print_status', namespace=NS_GAJIM_BM)
if not print_status: # not there, try old Gajim behaviour
print_status = conf.getTagData('print_status')
if not print_status: # not there, try old Gajim behaviour
print_status = conf.getTagData('show_status')
else:
print_status = print_status.getData()
try:
jid = helpers.parse_jid(conf.getAttr('jid'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it'
% conf.getAttr('jid'))
continue
bm = {'name': conf.getAttr('name'),
'jid': jid,
'autojoin': autojoin_val,
'minimize': minimize_val,
'password': conf.getTagData('password'),
'nick': conf.getTagData('nick'),
'print_status': print_status}
bm_jids = [b['jid'] for b in self.bookmarks]
if bm['jid'] not in bm_jids:
self.bookmarks.append(bm)
class PrivateStorageBookmarksReceivedEvent(nec.NetworkIncomingEvent,
BookmarksHelper):
name = 'private-storage-bookmarks-received'
base_network_events = ['private-storage-received']
def generate(self):
self.conn = self.base_event.conn
self.storage_node = self.base_event.storage_node
if self.base_event.namespace != nbxmpp.NS_BOOKMARKS:
return
self.parse_bookmarks()
return True
class BookmarksReceivedEvent(nec.NetworkIncomingEvent):
name = 'bookmarks-received'
base_network_events = ['private-storage-bookmarks-received',
'pubsub-bookmarks-received']
def generate(self):
self.conn = self.base_event.conn
self.bookmarks = self.base_event.bookmarks
return True
class PrivateStorageRosternotesReceivedEvent(nec.NetworkIncomingEvent):
name = 'private-storage-rosternotes-received'
base_network_events = ['private-storage-received']
def generate(self):
self.conn = self.base_event.conn
if self.base_event.namespace != nbxmpp.NS_ROSTERNOTES:
return
notes = self.base_event.storage_node.getTags('note')
self.annotations = {}
for note in notes:
try:
jid = helpers.parse_jid(note.getAttr('jid'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it' % note.getAttr('jid'))
continue
annotation = note.getData()
self.annotations[jid] = annotation
if self.annotations:
return True
class RosternotesReceivedEvent(nec.NetworkIncomingEvent):
name = 'rosternotes-received'
base_network_events = ['private-storage-rosternotes-received']
def generate(self):
self.conn = self.base_event.conn
self.annotations = self.base_event.annotations
return True
class PubsubReceivedEvent(nec.NetworkIncomingEvent):
name = 'pubsub-received'
base_network_events = []
def generate(self):
self.jid = self.stanza.getFrom()
self.pubsub_node = self.stanza.getTag('pubsub')
if not self.pubsub_node:
return
self.items_node = self.pubsub_node.getTag('items')
if not self.items_node:
return
return True
class PubsubBookmarksReceivedEvent(nec.NetworkIncomingEvent, BookmarksHelper):
name = 'pubsub-bookmarks-received'
base_network_events = ['pubsub-received']
def generate(self):
self.conn = self.base_event.conn
self.item_node = self.base_event.items_node.getTag('item')
if not self.item_node:
return
children = self.item_node.getChildren()
if not children:
return
self.storage_node = children[0]
ns = self.storage_node.getNamespace()
if ns != nbxmpp.NS_BOOKMARKS:
return
self.parse_bookmarks()
return True
class SearchFormReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'search-form-received'
base_network_events = []
def generate(self):
self.get_jid_resource()
self.data = None
self.is_dataform = False
tag = self.stanza.getTag('query', namespace=nbxmpp.NS_SEARCH)
if not tag:
return True
self.data = tag.getTag('x', namespace=nbxmpp.NS_DATA)
if self.data:
self.is_dataform = True
return True
self.data = {}
for i in self.stanza.getQueryPayload():
self.data[i.getName()] = i.getData()
return True
class SearchResultReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'search-result-received'
base_network_events = []
def generate(self):
self.get_jid_resource()
self.data = None
self.is_dataform = False
tag = self.stanza.getTag('query', namespace=nbxmpp.NS_SEARCH)
if not tag:
return True
self.data = tag.getTag('x', namespace=nbxmpp.NS_DATA)
if self.data:
self.is_dataform = True
return True
self.data = []
for item in tag.getTags('item'):
# We also show attributes. jid is there
f = item.attrs
for i in item.getPayload():
f[i.getName()] = i.getData()
self.data.append(f)
return True
class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'iq-error-received'
base_network_events = []
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 PingReceivedEvent(nec.NetworkIncomingEvent):
name = 'ping-received'
base_network_events = []
class StreamReceivedEvent(nec.NetworkIncomingEvent):
name = 'stream-received'
base_network_events = []
class StreamConflictReceivedEvent(nec.NetworkIncomingEvent):
name = 'stream-conflict-received'
base_network_events = ['stream-received']
def generate(self):
if self.base_event.stanza.getTag('conflict'):
self.conn = self.base_event.conn
return True
class StreamOtherHostReceivedEvent(nec.NetworkIncomingEvent):
name = 'stream-other-host-received'
base_network_events = ['stream-received']
def generate(self):
self.conn = self.base_event.conn
self.stanza = self.base_event.stanza
other_host = self.stanza.getTag('see-other-host')
if other_host and self.conn._current_type in ('ssl', 'tls'):
host = other_host.getData()
if ':' in host:
host_l = host.split(':', 1)
h = host_l[0]
p = host_l[1]
else:
h = host
p = 5222
if h.startswith('[') and h.endswith(']'):
h = h[1:-1]
self.redirected = {'host': h, 'port': p}
return True
class PresenceHelperEvent:
def _generate_show(self):
self.show = self.stanza.getShow()
if self.show not in ('chat', 'away', 'xa', 'dnd'):
self.show = '' # We ignore unknown show
if not self.ptype and not self.show:
self.show = 'online'
elif self.ptype == 'unavailable':
self.show = 'offline'
def _generate_ptype(self):
self.ptype = self.stanza.getType()
if self.ptype == 'available':
self.ptype = None
rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed',
'unsubscribe', 'unsubscribed')
if self.ptype and not self.ptype in rfc_types:
self.ptype = None
class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent,
PresenceHelperEvent):
name = 'presence-received'
base_network_events = ['raw-pres-received']
def _generate_keyID(self, sig_tag):
self.keyID = ''
if sig_tag and self.conn.USE_GPG and self.ptype != 'error':
# error presences contain our own signature
# verify
sig_msg = sig_tag.getData()
self.keyID = self.conn.gpg.verify(self.status, sig_msg)
self.keyID = helpers.prepare_and_validate_gpg_keyID(self.conn.name,
self.jid,
self.keyID)
def _generate_prio(self):
self.prio = self.stanza.getPriority()
try:
self.prio = int(self.prio)
except Exception:
self.prio = 0
def generate(self):
self.conn = self.base_event.conn
self.stanza = self.base_event.stanza
self.need_add_in_roster = False
self.need_redraw = False
self.popup = False # Do we want to open chat window ?
if not self.conn or self.conn.connected < 2:
log.debug('account is no more connected')
return
self._generate_ptype()
try:
self.get_jid_resource()
except Exception:
log.warning('Invalid JID: %s, ignoring it' % self.stanza.getFrom())
return
jid_list = app.contacts.get_jid_list(self.conn.name)
self.timestamp = None
self.get_id()
self.is_gc = False # is it a GC presence ?
sig_tag = None
self.avatar_sha = None
# XEP-0172 User Nickname
self.user_nick = self.stanza.getTagData('nick') or ''
self.contact_nickname = None
self.transport_auto_auth = False
# XEP-0203
delay_tag = self.stanza.getTag('delay', namespace=nbxmpp.NS_DELAY2)
if delay_tag:
self._generate_timestamp(self.stanza.timestamp)
# XEP-0319
self.idle_time = None
idle_tag = self.stanza.getTag('idle', namespace=nbxmpp.NS_IDLE)
if idle_tag:
time_str = idle_tag.getAttr('since')
tim = helpers.datetime_tuple(time_str)
self.idle_time = timegm(tim)
# Check if presence is from the room itself, used when the room
# sends a avatar hash
contact = app.contacts.get_groupchat_contact(self.conn.name, self.fjid)
if contact:
app.nec.push_incoming_event(
RoomAvatarReceivedEvent(
None, conn=self.conn, stanza=self.stanza,
contact=contact, jid=self.jid))
return
xtags = self.stanza.getTags('x')
for x in xtags:
namespace = x.getNamespace()
if namespace == nbxmpp.NS_MUC_USER:
self.is_gc = True
elif namespace == nbxmpp.NS_SIGNED:
sig_tag = x
elif namespace == nbxmpp.NS_VCARD_UPDATE:
self.avatar_sha = x.getTagData('photo')
self.contact_nickname = x.getTagData('nickname')
elif namespace == nbxmpp.NS_DELAY and not self.timestamp:
# XEP-0091
self._generate_timestamp(self.stanza.timestamp)
elif namespace == 'http://delx.cjb.net/protocol/roster-subsync':
# see http://trac.gajim.org/ticket/326
agent = app.get_server_from_jid(self.jid)
if self.conn.connection.getRoster().getItem(agent):
# to be sure it's a transport contact
self.transport_auto_auth = True
if not self.is_gc and self.id_ and self.id_.startswith('gajim_muc_') \
and self.ptype == 'error':
# Error presences may not include sent stanza, so we don't detect
# it's a muc presence. So detect it by ID
h = hmac.new(self.conn.secret_hmac, self.jid.encode('utf-8'),
hashlib.md5).hexdigest()[:6]
if self.id_.split('_')[-1] == h:
self.is_gc = True
self.status = self.stanza.getStatus() or ''
self._generate_show()
self._generate_prio()
self._generate_keyID(sig_tag)
self.errcode = self.stanza.getErrorCode()
self.errmsg = self.stanza.getErrorMsg()
if self.is_gc:
app.nec.push_incoming_event(
GcPresenceReceivedEvent(
None, conn=self.conn, stanza=self.stanza,
presence_obj=self))
return
if self.ptype == 'subscribe':
app.nec.push_incoming_event(SubscribePresenceReceivedEvent(None,
conn=self.conn, stanza=self.stanza, presence_obj=self))
elif self.ptype == 'subscribed':
# BE CAREFUL: no con.updateRosterItem() in a callback
app.nec.push_incoming_event(SubscribedPresenceReceivedEvent(None,
conn=self.conn, stanza=self.stanza, presence_obj=self))
elif self.ptype == 'unsubscribe':
log.debug(_('unsubscribe request from %s') % self.jid)
elif self.ptype == 'unsubscribed':
app.nec.push_incoming_event(UnsubscribedPresenceReceivedEvent(
None, conn=self.conn, stanza=self.stanza, presence_obj=self))
elif self.ptype == 'error':
return
if not self.ptype or self.ptype == 'unavailable':
our_jid = app.get_jid_from_account(self.conn.name)
if self.jid == our_jid and self.resource == self.conn.server_resource:
# We got our own presence
app.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
show=self.show))
elif self.jid in jid_list or self.jid == our_jid:
return True
class ZeroconfPresenceReceivedEvent(nec.NetworkIncomingEvent):
name = 'presence-received'
base_network_events = []
def generate(self):
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
self.resource = 'local'
self.prio = 0
self.keyID = None
self.timestamp = 0
self.contact_nickname = None
self.avatar_sha = None
self.need_add_in_roster = False
self.need_redraw = False
if self.show == 'offline':
self.ptype = 'unavailable'
else:
self.ptype = None
self.is_gc = False
self.user_nick = ''
self.transport_auto_auth = False
self.errcode = None
self.errmsg = ''
self.popup = False # Do we want to open chat window ?
return True
class GcPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'gc-presence-received'
base_network_events = []
def generate(self):
self.ptype = self.presence_obj.ptype
self.fjid = self.presence_obj.fjid
self.jid = self.presence_obj.jid
self.room_jid = self.presence_obj.jid
self.nick = self.presence_obj.resource
self.show = self.presence_obj.show
self.status = self.presence_obj.status
self.avatar_sha = self.presence_obj.avatar_sha
self.errcode = self.presence_obj.errcode
self.errmsg = self.presence_obj.errmsg
self.errcon = self.stanza.getError()
self.get_gc_control()
self.gc_contact = app.contacts.get_gc_contact(self.conn.name,
self.room_jid, self.nick)
if self.ptype == 'error':
return True
if self.ptype and self.ptype != 'unavailable':
return
if app.config.get('log_contact_status_changes') and \
app.config.should_log(self.conn.name, self.room_jid):
if self.gc_contact:
jid = self.gc_contact.jid
else:
jid = self.stanza.getJid()
st = self.status
if jid:
# we know real jid, save it in db
st += ' (%s)' % jid
show = app.logger.convert_show_values_to_db_api_values(self.show)
if show is not None:
fjid = nbxmpp.JID(self.fjid)
app.logger.insert_into_logs(self.conn.name,
fjid.getStripped(),
time_time(),
KindConstant.GCSTATUS,
contact_name=fjid.getResource(),
message=st,
show=show)
# NOTE: if it's a gc presence, don't ask vcard here.
# We may ask it to real jid in gui part.
self.status_code = []
ns_muc_user_x = self.stanza.getTag('x', namespace=nbxmpp.NS_MUC_USER)
if ns_muc_user_x:
destroy = ns_muc_user_x.getTag('destroy')
else:
destroy = None
if ns_muc_user_x and destroy:
# Room has been destroyed. see
# http://www.xmpp.org/extensions/xep-0045.html#destroyroom
self.reason = _('Room has been destroyed')
r = destroy.getTagData('reason')
if r:
self.reason += ' (%s)' % r
if destroy.getAttr('jid'):
try:
jid = helpers.parse_jid(destroy.getAttr('jid'))
self.reason += '\n' + \
_('You can join this room instead: %s') % jid
except helpers.InvalidFormat:
pass
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.role = self.stanza.getRole()
self.affiliation = self.stanza.getAffiliation()
self.real_jid = self.stanza.getJid()
self.actor = self.stanza.getActor()
self.new_nick = self.stanza.getNewNick()
return True
class SubscribePresenceReceivedEvent(nec.NetworkIncomingEvent):
name = 'subscribe-presence-received'
base_network_events = []
def generate(self):
self.jid = self.presence_obj.jid
self.fjid = self.presence_obj.fjid
self.status = self.presence_obj.status
self.transport_auto_auth = self.presence_obj.transport_auto_auth
self.user_nick = self.presence_obj.user_nick
return True
class SubscribedPresenceReceivedEvent(nec.NetworkIncomingEvent):
name = 'subscribed-presence-received'
base_network_events = []
def generate(self):
self.jid = self.presence_obj.jid
self.resource = self.presence_obj.resource
return True
class UnsubscribedPresenceReceivedEvent(nec.NetworkIncomingEvent):
name = 'unsubscribed-presence-received'
base_network_events = []
def generate(self):
self.jid = self.presence_obj.jid
return True
class OurShowEvent(nec.NetworkIncomingEvent):
name = 'our-show'
base_network_events = []
class BeforeChangeShowEvent(nec.NetworkIncomingEvent):
name = 'before-change-show'
base_network_events = []
class MamMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'mam-message-received'
base_network_events = ['raw-mam-message-received']
def __init__(self, name, base_event):
'''
Pre-Generated attributes on self:
:conn: Connection instance
:stanza: Complete stanza Node
:forwarded: Forwarded Node
:result: Result Node
'''
self._set_base_event_vars_as_attributes(base_event)
self.additional_data = {}
self.encrypted = False
self.groupchat = False
self.nick = None
self.self_message = None
self.muc_pm = None
def generate(self):
account = self.conn.name
archive_jid = self.stanza.getFrom()
own_jid = self.conn.get_own_jid()
if archive_jid and not archive_jid.bareMatch(own_jid):
# MAM Message not from our Archive
return False
self.msg_ = self.forwarded.getTag('message', protocol=True)
if self.msg_.getType() == 'groupchat':
return False
# use stanza-id as unique-id
self.unique_id, origin_id = self.get_unique_id()
self.message_id = self.msg_.getID()
# Check for duplicates
if app.logger.find_stanza_id(account,
own_jid.getStripped(),
self.unique_id, origin_id):
return
self.msgtxt = self.msg_.getTagData('body')
frm = self.msg_.getFrom()
# Some servers dont set the 'to' attribute when
# we send a message to ourself
to = self.msg_.getTo()
if to is None:
to = own_jid
if frm.bareMatch(own_jid):
self.with_ = to
self.kind = KindConstant.CHAT_MSG_SENT
else:
self.with_ = frm
self.kind = KindConstant.CHAT_MSG_RECV
delay = self.forwarded.getTagAttr(
'delay', 'stamp', namespace=nbxmpp.NS_DELAY2)
if delay is None:
log.error('Received MAM message without timestamp')
log.error(self.stanza)
return
self.timestamp = helpers.parse_datetime(
delay, check_utc=True, epoch=True)
if self.timestamp is None:
log.error('Received MAM message with invalid timestamp: %s', delay)
log.error(self.stanza)
return
# Save timestamp added by the user
user_delay = self.msg_.getTagAttr(
'delay', 'stamp', namespace=nbxmpp.NS_DELAY2)
if user_delay is not None:
self.user_timestamp = helpers.parse_datetime(
user_delay, check_utc=True, epoch=True)
if self.user_timestamp is None:
log.warning('Received MAM message with '
'invalid user timestamp: %s', user_delay)
log.warning(self.stanza)
log.debug('Received mam-message: unique id: %s', self.unique_id)
return True
def get_unique_id(self):
stanza_id = self.get_stanza_id(self.result, query=True)
if self._is_self_message(self.msg_) or self._is_muc_pm(self.msg_):
origin_id = self.msg_.getOriginID()
return stanza_id, origin_id
if self.conn.get_own_jid().bareMatch(self.msg_.getFrom()):
# message we sent
origin_id = self.msg_.getOriginID()
return stanza_id, origin_id
# A message we received
return stanza_id, None
class MamGcMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'mam-gc-message-received'
base_network_events = ['raw-mam-message-received']
def __init__(self, name, base_event):
'''
Pre-Generated attributes on self:
:conn: Connection instance
:stanza: Complete stanza Node
:forwarded: Forwarded Node
:result: Result Node
:muc_pm: True, if this is a MUC PM
propagated to MamDecryptedMessageReceivedEvent
'''
self._set_base_event_vars_as_attributes(base_event)
self.additional_data = {}
self.encrypted = False
self.groupchat = True
self.kind = KindConstant.GC_MSG
def generate(self):
account = self.conn.name
self.msg_ = self.forwarded.getTag('message', protocol=True)
if self.msg_.getType() != 'groupchat':
return False
try:
self.room_jid = self.stanza.getFrom().getStripped()
except AttributeError:
log.warning('Received GC MAM message '
'without from attribute\n%s', self.stanza)
return False
self.unique_id = self.get_stanza_id(self.result, query=True)
self.message_id = self.msg_.getID()
# Check for duplicates
if app.logger.find_stanza_id(account,
self.room_jid,
self.unique_id,
groupchat=True):
return
self.msgtxt = self.msg_.getTagData('body')
self.with_ = self.msg_.getFrom().getStripped()
self.nick = self.msg_.getFrom().getResource()
# Get the real jid if we have it
self.real_jid = None
muc_user = self.msg_.getTag('x', namespace=nbxmpp.NS_MUC_USER)
if muc_user is not None:
self.real_jid = muc_user.getTagAttr('item', 'jid')
delay = self.forwarded.getTagAttr(
'delay', 'stamp', namespace=nbxmpp.NS_DELAY2)
if delay is None:
log.error('Received MAM message without timestamp')
log.error(self.stanza)
return
self.timestamp = helpers.parse_datetime(
delay, check_utc=True, epoch=True)
if self.timestamp is None:
log.error('Received MAM message with invalid timestamp: %s', delay)
log.error(self.stanza)
return
# Save timestamp added by the user
user_delay = self.msg_.getTagAttr(
'delay', 'stamp', namespace=nbxmpp.NS_DELAY2)
if user_delay is not None:
self.user_timestamp = helpers.parse_datetime(
user_delay, check_utc=True, epoch=True)
if self.user_timestamp is None:
log.warning('Received MAM message with '
'invalid user timestamp: %s', user_delay)
log.warning(self.stanza)
log.debug('Received mam-gc-message: unique id: %s', self.unique_id)
return True
class MamDecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'mam-decrypted-message-received'
base_network_events = []
def generate(self):
self.correct_id = None
if not self.msgtxt:
# For example Chatstates, Receipts, Chatmarkers
log.debug('Received MAM message without text')
return
replace = self.msg_.getTag('replace', namespace=nbxmpp.NS_CORRECT)
if replace is not None:
self.correct_id = replace.getAttr('id')
self.get_oob_data(self.msg_)
if self.groupchat:
return True
if not self.muc_pm:
# muc_pm = False, means only there was no muc#user namespace
# This could still be a muc pm, we check the database if we
# know this jid. If not we disco it.
self.muc_pm = app.logger.jid_is_room_jid(self.with_.getStripped())
if self.muc_pm is None:
# Check if this event is triggered after a disco, so we dont
# run into an endless loop
if hasattr(self, 'disco'):
log.error('JID not known even after sucessful disco')
log.error(self.with_.getStripped())
return
# we don't know this JID, we need to disco it.
server = self.with_.getDomain()
if server not in self.conn.mam_awaiting_disco_result:
self.conn.mam_awaiting_disco_result[server] = [self]
self.conn.discoverInfo(server)
else:
self.conn.mam_awaiting_disco_result[server].append(self)
return
if self.muc_pm:
self.with_ = str(self.with_)
else:
self.with_ = self.with_.getStripped()
return True
class MessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'message-received'
base_network_events = ['raw-message-received']
def init(self):
self.additional_data = {}
def generate(self):
self.conn = self.base_event.conn
self.stanza = self.base_event.stanza
self.get_id()
self.forwarded = False
self.sent = False
self.encrypted = False
self.self_message = None
self.muc_pm = None
account = self.conn.name
if self.stanza.getFrom() == self.conn.get_own_jid(warn=True):
# Drop messages sent from our own full jid
# It can happen that when we sent message to our own bare jid
# that the server routes that message back to us
log.info('Received message from self: %s, message is dropped',
self.stanza.getFrom())
return
# check if the message is a roster item exchange (XEP-0144)
if self.stanza.getTag('x', namespace=nbxmpp.NS_ROSTERX):
app.nec.push_incoming_event(RosterItemExchangeEvent(None,
conn=self.conn, stanza=self.stanza))
return
# check if the message is a XEP-0070 confirmation request
if self.stanza.getTag('confirm', namespace=nbxmpp.NS_HTTP_AUTH):
app.nec.push_incoming_event(HttpAuthReceivedEvent(None,
conn=self.conn, stanza=self.stanza))
return
try:
self.get_jid_resource()
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it',
self.stanza.getFrom())
return
# Check for duplicates
self.unique_id = self.get_unique_id()
# Check groupchat messages for duplicates,
# We do this because of MUC History messages
type_ = self.stanza.getType()
if type_ == 'groupchat' or self.self_message or self.muc_pm:
if type_ == 'groupchat':
archive_jid = self.stanza.getFrom().getStripped()
else:
archive_jid = self.conn.get_own_jid().getStripped()
if app.logger.find_stanza_id(account,
archive_jid,
self.unique_id,
groupchat=type_ == 'groupchat'):
return
address_tag = self.stanza.getTag('addresses',
namespace=nbxmpp.NS_ADDRESS)
# Be sure it comes from one of our resource, else ignore address element
if address_tag and self.jid == app.get_jid_from_account(account):
address = address_tag.getTag('address', attrs={'type': 'ofrom'})
if address:
try:
self.fjid = helpers.parse_jid(address.getAttr('jid'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it',
address.getAttr('jid'))
return
self.jid = app.get_jid_without_resource(self.fjid)
carbon_marker = self.stanza.getTag('sent', namespace=nbxmpp.NS_CARBONS)
if not carbon_marker:
carbon_marker = self.stanza.getTag('received',
namespace=nbxmpp.NS_CARBONS)
# Be sure it comes from one of our resource, else ignore forward element
if carbon_marker and self.jid == app.get_jid_from_account(account):
forward_tag = carbon_marker.getTag('forwarded',
namespace=nbxmpp.NS_FORWARD)
if forward_tag:
msg = forward_tag.getTag('message')
self.stanza = nbxmpp.Message(node=msg)
self.get_id()
if carbon_marker.getName() == 'sent':
to = self.stanza.getTo()
frm = self.stanza.getFrom()
if not frm:
frm = app.get_jid_from_account(account)
self.stanza.setTo(frm)
if not to:
to = app.get_jid_from_account(account)
self.stanza.setFrom(to)
self.sent = True
elif carbon_marker.getName() == 'received':
full_frm = str(self.stanza.getFrom())
frm = app.get_jid_without_resource(full_frm)
if frm == app.get_jid_from_account(account):
# Drop 'received' Carbons from ourself, we already
# got the message with the 'sent' Carbon or via the
# message itself
log.info(
'Drop "received"-Carbon from ourself: %s'
% full_frm)
return
try:
self.get_jid_resource()
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it',
self.stanza.getFrom())
return
self.forwarded = True
result = self.stanza.getTag('result', protocol=True)
if result and result.getNamespace() in (nbxmpp.NS_MAM_1,
nbxmpp.NS_MAM_2):
if result.getAttr('queryid') not in self.conn.mam_query_ids:
log.warning('Invalid MAM Message: unknown query id')
log.debug(self.stanza)
return
forwarded = result.getTag('forwarded',
namespace=nbxmpp.NS_FORWARD,
protocol=True)
if not forwarded:
log.warning('Invalid MAM Message: no forwarded child')
return
app.nec.push_incoming_event(
NetworkEvent('raw-mam-message-received',
conn=self.conn,
stanza=self.stanza,
forwarded=forwarded,
result=result))
return
# Mediated invitation?
muc_user = self.stanza.getTag('x', namespace=nbxmpp.NS_MUC_USER)
if muc_user:
if muc_user.getTag('decline'):
app.nec.push_incoming_event(
GcDeclineReceivedEvent(
None, conn=self.conn,
room_jid=self.fjid, stanza=muc_user))
return
if muc_user.getTag('invite'):
app.nec.push_incoming_event(
GcInvitationReceivedEvent(
None, conn=self.conn, jid_from=self.fjid,
mediated=True, stanza=muc_user))
return
else:
# Direct invitation?
direct = self.stanza.getTag(
'x', namespace=nbxmpp.NS_CONFERENCE)
if direct:
app.nec.push_incoming_event(
GcInvitationReceivedEvent(
None, conn=self.conn, jid_from=self.fjid,
mediated=False, stanza=direct))
return
self.thread_id = self.stanza.getThread()
self.mtype = self.stanza.getType()
if not self.mtype or self.mtype not in ('chat', 'groupchat', 'error'):
self.mtype = 'normal'
self.msgtxt = self.stanza.getBody()
self.get_gc_control()
if self.gc_control and self.jid == self.fjid:
if self.mtype == 'error':
self.msgtxt = _('error while sending %(message)s ( %(error)s )'\
) % {'message': self.msgtxt,
'error': self.stanza.getErrorMsg()}
if self.stanza.getTag('html'):
self.stanza.delChild('html')
# message from a gc without a resource
self.mtype = 'groupchat'
self.session = None
if self.mtype != 'groupchat':
if app.interface.is_pm_contact(self.fjid, account) and \
self.mtype == 'error':
self.session = self.conn.find_session(self.fjid, self.thread_id)
if not self.session:
self.session = self.conn.get_latest_session(self.fjid)
if not self.session:
self.session = self.conn.make_new_session(self.fjid,
self.thread_id,
type_='pm')
else:
self.session = self.conn.get_or_create_session(self.fjid,
self.thread_id)
if self.thread_id and not self.session.received_thread_id:
self.session.received_thread_id = True
self.session.last_receive = time_time()
self._generate_timestamp(self.stanza.timestamp)
return True
def get_unique_id(self):
'''
Messages to self:
Messages to self are stored multiple times in MAM so we cant use
stanza-id to deduplicate. We use origin-id instead. Its not perfect
but there is no better way for now.
We drop "received"-Carbons of Message to self, so we dont have to
parse origin-id in that case.
MUC PMs:
MUC PMs are also stored multiple times, we also depend on origin-id
for now.
'''
if self.stanza.getType() == 'groupchat':
# TODO: Disco the MUC check if 'urn:xmpp:mam:2' is announced
return self.get_stanza_id(self.stanza)
elif self.stanza.getType() != 'chat':
return
# Messages we receive live
if self.conn.archiving_namespace != nbxmpp.NS_MAM_2:
# Only mam:2 ensures valid stanza-id
return
# Sent Carbon
sent_carbon = self.stanza.getTag('sent',
namespace=nbxmpp.NS_CARBONS,
protocol=True)
if sent_carbon is not None:
message = self.get_forwarded_message(sent_carbon)
if self._is_self_message(message) or self._is_muc_pm(message):
return message.getOriginID()
return self.get_stanza_id(message)
# Received Carbon
received_carbon = self.stanza.getTag('received',
namespace=nbxmpp.NS_CARBONS,
protocol=True)
if received_carbon is not None:
message = self.get_forwarded_message(received_carbon)
if self._is_muc_pm(message):
return message.getOriginID()
return self.get_stanza_id(message)
# Normal Message
if self._is_self_message(self.stanza) or self._is_muc_pm(self.stanza):
return self.stanza.getOriginID()
return self.get_stanza_id(self.stanza)
class ZeroconfMessageReceivedEvent(MessageReceivedEvent):
name = 'message-received'
base_network_events = []
def get_jid_resource(self):
self.fjid =self.stanza.getFrom()
if self.fjid is None:
for key in self.conn.connection.zeroconf.contacts:
if self.ip == self.conn.connection.zeroconf.contacts[key][
Constant.ADDRESS]:
self.fjid = key
break
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
def generate(self):
self.base_event = nec.NetworkIncomingEvent(None, conn=self.conn,
stanza=self.stanza)
return super(ZeroconfMessageReceivedEvent, self).generate()
class GcInvitationReceivedEvent(nec.NetworkIncomingEvent):
name = 'gc-invitation-received'
base_network_events = []
def generate(self):
account = self.conn.name
if not self.mediated:
# direct invitation
try:
self.room_jid = helpers.parse_jid(self.stanza.getAttr('jid'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it',
self.stanza.getAttr('jid'))
return
self.reason = self.stanza.getAttr('reason')
self.password = self.stanza.getAttr('password')
self.is_continued = False
self.is_continued = self.stanza.getAttr('continue') == 'true'
else:
self.invite = self.stanza.getTag('invite')
self.room_jid = self.jid_from
try:
self.jid_from = helpers.parse_jid(self.invite.getAttr('from'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it',
self.invite.getAttr('from'))
return
self.reason = self.invite.getTagData('reason')
self.password = self.stanza.getTagData('password')
self.is_continued = self.stanza.getTag('continue') is not None
if self.room_jid in app.gc_connected[account] and \
app.gc_connected[account][self.room_jid]:
# We are already in groupchat. Ignore invitation
return
jid = app.get_jid_without_resource(self.jid_from)
ignore = app.config.get_per(
'accounts', account, 'ignore_unknown_contacts')
if ignore and not app.contacts.get_contacts(account, jid):
return
return True
class GcDeclineReceivedEvent(nec.NetworkIncomingEvent):
name = 'gc-decline-received'
base_network_events = []
def generate(self):
account = self.conn.name
decline = self.stanza.getTag('decline')
try:
self.jid_from = helpers.parse_jid(decline.getAttr('from'))
except helpers.InvalidFormat:
log.warning('Invalid JID: %s, ignoring it',
decline.getAttr('from'))
return
jid = app.get_jid_without_resource(self.jid_from)
ignore = app.config.get_per(
'accounts', account, 'ignore_unknown_contacts')
if ignore and not app.contacts.get_contacts(account, jid):
return
self.reason = decline.getTagData('reason')
return True
class DecryptedMessageReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'decrypted-message-received'
base_network_events = []
def generate(self):
self.stanza = self.msg_obj.stanza
self.additional_data = self.msg_obj.additional_data
self.id_ = self.msg_obj.id_
self.unique_id = self.msg_obj.unique_id
self.jid = self.msg_obj.jid
self.fjid = self.msg_obj.fjid
self.resource = self.msg_obj.resource
self.mtype = self.msg_obj.mtype
self.thread_id = self.msg_obj.thread_id
self.msgtxt = self.msg_obj.msgtxt
self.gc_control = self.msg_obj.gc_control
self.session = self.msg_obj.session
self.timestamp = self.msg_obj.timestamp
self.encrypted = self.msg_obj.encrypted
self.forwarded = self.msg_obj.forwarded
self.sent = self.msg_obj.sent
self.conn = self.msg_obj.conn
self.muc_pm = self.msg_obj.muc_pm
self.popup = False
self.msg_log_id = None # id in log database
self.attention = False # XEP-0224
self.correct_id = None # XEP-0308
self.msghash = None
self.receipt_request_tag = self.stanza.getTag('request',
namespace=nbxmpp.NS_RECEIPTS)
self.receipt_received_tag = self.stanza.getTag('received',
namespace=nbxmpp.NS_RECEIPTS)
self.subject = self.stanza.getSubject()
self.displaymarking = None
self.seclabel = self.stanza.getTag('securitylabel',
namespace=nbxmpp.NS_SECLABEL)
if self.seclabel:
self.displaymarking = self.seclabel.getTag('displaymarking')
if self.stanza.getTag('attention', namespace=nbxmpp.NS_ATTENTION):
delayed = self.stanza.getTag('x', namespace=nbxmpp.NS_DELAY) is not\
None
if not delayed:
self.attention = True
self.form_node = self.stanza.getTag('x', namespace=nbxmpp.NS_DATA)
if app.config.get('ignore_incoming_xhtml'):
self.xhtml = None
else:
self.xhtml = self.stanza.getXHTML()
# XEP-0172 User Nickname
self.user_nick = self.stanza.getTagData('nick') or ''
self.get_chatstate()
self.get_oob_data(self.stanza)
replace = self.stanza.getTag('replace', namespace=nbxmpp.NS_CORRECT)
if replace:
self.correct_id = replace.getAttr('id')
return True
class ChatstateReceivedEvent(nec.NetworkIncomingEvent):
name = 'chatstate-received'
base_network_events = []
def generate(self):
self.stanza = self.msg_obj.stanza
self.jid = self.msg_obj.jid
self.fjid = self.msg_obj.fjid
self.resource = self.msg_obj.resource
self.chatstate = self.msg_obj.chatstate
return True
class GcMessageReceivedEvent(nec.NetworkIncomingEvent):
name = 'gc-message-received'
base_network_events = []
def generate(self):
self.stanza = self.msg_obj.stanza
if not hasattr(self.msg_obj, 'additional_data'):
self.additional_data = {}
else:
self.additional_data = self.msg_obj.additional_data
self.id_ = self.msg_obj.stanza.getID()
self.unique_id = self.msg_obj.unique_id
self.fjid = self.msg_obj.fjid
self.msgtxt = self.msg_obj.msgtxt
self.jid = self.msg_obj.jid
self.room_jid = self.msg_obj.jid
self.nickname = self.msg_obj.resource
self.timestamp = self.msg_obj.timestamp
self.xhtml_msgtxt = self.stanza.getXHTML()
self.encrypted = self.msg_obj.encrypted
self.correct_id = None # XEP-0308
if app.config.get('ignore_incoming_xhtml'):
self.xhtml_msgtxt = None
if self.msg_obj.resource:
# message from someone
self.nick = self.msg_obj.resource
else:
# message from server
self.nick = ''
self.has_timestamp = bool(self.stanza.timestamp)
self.subject = self.stanza.getSubject()
if self.subject is not None:
app.nec.push_incoming_event(GcSubjectReceivedEvent(None,
conn=self.conn, msg_event=self))
return
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:
return True
return
self.displaymarking = None
seclabel = self.stanza.getTag('securitylabel')
if seclabel and seclabel.getNamespace() == nbxmpp.NS_SECLABEL:
# Ignore message from room in which we are not
self.displaymarking = seclabel.getTag('displaymarking')
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_bob_data(uri_data, self.fjid,
self.conn._dispatch_gc_msg_with_captcha,
[self.stanza, self.msg_obj], 0)
return
replace = self.stanza.getTag('replace', namespace=nbxmpp.NS_CORRECT)
if replace:
self.correct_id = replace.getAttr('id')
return True
class GcSubjectReceivedEvent(nec.NetworkIncomingEvent):
name = 'gc-subject-received'
base_network_events = []
def generate(self):
self.conn = self.msg_event.conn
self.stanza = self.msg_event.stanza
self.room_jid = self.msg_event.room_jid
self.nickname = self.msg_event.nickname
self.fjid = self.msg_event.fjid
self.subject = self.msg_event.subject
self.msgtxt = self.msg_event.msgtxt
self.has_timestamp = self.msg_event.has_timestamp
return True
class GcConfigChangedReceivedEvent(nec.NetworkIncomingEvent):
name = 'gc-config-changed-received'
base_network_events = []
def generate(self):
self.conn = self.msg_event.conn
self.stanza = self.msg_event.stanza
self.room_jid = self.msg_event.room_jid
self.status_code = self.msg_event.status_code
return True
class MessageSentEvent(nec.NetworkIncomingEvent):
name = 'message-sent'
base_network_events = []
def generate(self):
if not self.automatic_message:
self.conn.sent_message_ids.append(self.stanza_id)
# only record the last 20000 message ids (should be about 1MB [36 byte per uuid]
# and about 24 hours if you send out a message every 5 seconds)
self.conn.sent_message_ids = self.conn.sent_message_ids[-20000:]
return True
class MessageNotSentEvent(nec.NetworkIncomingEvent):
name = 'message-not-sent'
base_network_events = []
class MessageErrorEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'message-error'
base_network_events = []
def generate(self):
self.get_id()
#only alert for errors of explicitly sent messages (see https://trac.gajim.org/ticket/8222)
if self.id_ in self.conn.sent_message_ids:
self.conn.sent_message_ids.remove(self.id_)
return True
return False
class AnonymousAuthEvent(nec.NetworkIncomingEvent):
name = 'anonymous-auth'
base_network_events = []
class JingleRequestReceivedEvent(nec.NetworkIncomingEvent):
name = 'jingle-request-received'
base_network_events = []
def generate(self):
self.fjid = self.jingle_session.peerjid
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
self.sid = self.jingle_session.sid
return True
class JingleConnectedReceivedEvent(nec.NetworkIncomingEvent):
name = 'jingle-connected-received'
base_network_events = []
def generate(self):
self.fjid = self.jingle_session.peerjid
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
self.sid = self.jingle_session.sid
return True
class JingleDisconnectedReceivedEvent(nec.NetworkIncomingEvent):
name = 'jingle-disconnected-received'
base_network_events = []
def generate(self):
self.fjid = self.jingle_session.peerjid
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
self.sid = self.jingle_session.sid
return True
class JingleTransferCancelledEvent(nec.NetworkIncomingEvent):
name = 'jingleFT-cancelled-received'
base_network_events = []
def generate(self):
self.fjid = self.jingle_session.peerjid
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
self.sid = self.jingle_session.sid
return True
class JingleErrorReceivedEvent(nec.NetworkIncomingEvent):
name = 'jingle-error-received'
base_network_events = []
def generate(self):
self.fjid = self.jingle_session.peerjid
self.jid, self.resource = app.get_room_and_nick_from_fjid(self.fjid)
self.sid = self.jingle_session.sid
return True
class ArchivingReceivedEvent(nec.NetworkIncomingEvent):
name = 'archiving-received'
base_network_events = []
def generate(self):
self.type_ = self.stanza.getType()
if self.type_ not in ('result', 'set', 'error'):
return
return True
class ArchivingErrorReceivedEvent(nec.NetworkIncomingEvent):
name = 'archiving-error-received'
base_network_events = ['archiving-received']
def generate(self):
self.conn = self.base_event.conn
self.stanza = self.base_event.stanza
self.type_ = self.base_event.type_
if self.type_ == 'error':
self.error_msg = self.stanza.getErrorMsg()
return True
class ArchivingCountReceived(nec.NetworkIncomingEvent):
name = 'archiving-count-received'
base_network_events = []
def generate(self):
return True
class ArchivingIntervalFinished(nec.NetworkIncomingEvent):
name = 'archiving-interval-finished'
base_network_events = []
def generate(self):
return True
class ArchivingQueryID(nec.NetworkIncomingEvent):
name = 'archiving-query-id'
base_network_events = []
def generate(self):
return True
class Archiving313PreferencesChangedReceivedEvent(nec.NetworkIncomingEvent):
name = 'archiving-313-preferences-changed-received'
base_network_events = ['archiving-received']
def generate(self):
self.conn = self.base_event.conn
self.stanza = self.base_event.stanza
self.type_ = self.base_event.type_
self.items = []
self.default = None
self.id = self.stanza.getID()
self.answer = None
prefs = self.stanza.getTag('prefs')
if self.type_ != 'result' or not prefs:
return
self.default = prefs.getAttr('default')
for item in prefs.getTag('always').getTags('jid'):
self.items.append((item.getData(), 'Always'))
for item in prefs.getTag('never').getTags('jid'):
self.items.append((item.getData(), 'Never'))
return True
class AccountCreatedEvent(nec.NetworkIncomingEvent):
name = 'account-created'
base_network_events = []
class AccountNotCreatedEvent(nec.NetworkIncomingEvent):
name = 'account-not-created'
base_network_events = []
class NewAccountConnectedEvent(nec.NetworkIncomingEvent):
name = 'new-account-connected'
base_network_events = []
def generate(self):
try:
self.errnum = self.conn.connection.Connection.ssl_errnum
except AttributeError:
self.errnum = 0 # we don't have an errnum
self.ssl_msg = ''
if self.errnum > 0:
from gajim.common.connection import ssl_error
self.ssl_msg = ssl_error.get(self.errnum,
_('Unknown SSL error: %d') % self.errnum)
self.ssl_cert = ''
self.ssl_fingerprint_sha1 = ''
self.ssl_fingerprint_sha256 = ''
if self.conn.connection.Connection.ssl_certificate:
cert = self.conn.connection.Connection.ssl_certificate
self.ssl_cert = OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert).decode('utf-8')
self.ssl_fingerprint_sha1 = cert.digest('sha1').decode('utf-8')
self.ssl_fingerprint_sha256 = cert.digest('sha256').decode('utf-8')
return True
class NewAccountNotConnectedEvent(nec.NetworkIncomingEvent):
name = 'new-account-not-connected'
base_network_events = []
class ConnectionTypeEvent(nec.NetworkIncomingEvent):
name = 'connection-type'
base_network_events = []
class VcardPublishedEvent(nec.NetworkIncomingEvent):
name = 'vcard-published'
base_network_events = []
class VcardNotPublishedEvent(nec.NetworkIncomingEvent):
name = 'vcard-not-published'
base_network_events = []
class StanzaReceivedEvent(nec.NetworkIncomingEvent):
name = 'stanza-received'
base_network_events = []
def init(self):
self.additional_data = {}
def generate(self):
return True
class StanzaSentEvent(nec.NetworkIncomingEvent):
name = 'stanza-sent'
base_network_events = []
def init(self):
self.additional_data = {}
class AgentRemovedEvent(nec.NetworkIncomingEvent):
name = 'agent-removed'
base_network_events = []
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'
base_network_events = []
def generate(self):
self.account = self.conn.name
self.use_gpg_agent = app.config.get('use_gpg_agent')
self.keyID = app.config.get_per('accounts', self.conn.name, 'keyid')
return True
class ConnectionLostEvent(nec.NetworkIncomingEvent):
name = 'connection-lost'
base_network_events = []
def generate(self):
app.nec.push_incoming_event(OurShowEvent(None, conn=self.conn,
show='offline'))
return True
class PingSentEvent(nec.NetworkIncomingEvent):
name = 'ping-sent'
base_network_events = []
class PingReplyEvent(nec.NetworkIncomingEvent):
name = 'ping-reply'
base_network_events = []
class PingErrorEvent(nec.NetworkIncomingEvent):
name = 'ping-error'
base_network_events = []
class CapsPresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent,
PresenceHelperEvent):
name = 'caps-presence-received'
base_network_events = ['raw-pres-received']
def _extract_caps_from_presence(self):
caps_tag = self.stanza.getTag('c', namespace=nbxmpp.NS_CAPS)
if caps_tag:
self.hash_method = caps_tag['hash']
self.node = caps_tag['node']
self.caps_hash = caps_tag['ver']
else:
self.hash_method = self.node = self.caps_hash = None
def generate(self):
self.conn = self.base_event.conn
self.stanza = self.base_event.stanza
try:
self.get_jid_resource()
except Exception:
return
self._generate_ptype()
self._generate_show()
self._extract_caps_from_presence()
return True
class CapsDiscoReceivedEvent(nec.NetworkIncomingEvent):
name = 'caps-disco-received'
base_network_events = []
class CapsReceivedEvent(nec.NetworkIncomingEvent):
name = 'caps-received'
base_network_events = ['caps-presence-received', 'caps-disco-received']
def generate(self):
self.conn = self.base_event.conn
self.fjid = self.base_event.fjid
self.jid = self.base_event.jid
self.resource = self.base_event.resource
self.client_caps = self.base_event.client_caps
return True
class GPGTrustKeyEvent(nec.NetworkIncomingEvent):
name = 'gpg-trust-key'
base_network_events = []
class GPGPasswordRequiredEvent(nec.NetworkIncomingEvent):
name = 'gpg-password-required'
base_network_events = []
def generate(self):
self.keyid = app.config.get_per('accounts', self.conn.name, 'keyid')
return True
class PEPReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'pep-received'
base_network_events = []
def generate(self):
if not self.stanza.getTag('event'):
return
if self.stanza.getTag('error'):
log.debug('PEPReceivedEvent received error stanza. Ignoring')
return
try:
self.get_jid_resource()
except Exception:
return
self.event_tag = self.stanza.getTag('event')
for pep_class in SUPPORTED_PERSONAL_USER_EVENTS:
pep = pep_class.get_tag_as_PEP(self.fjid, self.conn.name,
self.event_tag)
if pep:
self.pep_type = pep.type_
return True
items = self.event_tag.getTag('items')
if items:
# for each entry in feed (there shouldn't be more than one, but to
# be sure...
for item in items.getTags('item'):
entry = item.getTag('entry', namespace=nbxmpp.NS_ATOM)
if entry:
app.nec.push_incoming_event(AtomEntryReceived(None,
conn=self.conn, node=entry))
raise nbxmpp.NodeProcessed
class AtomEntryReceived(nec.NetworkIncomingEvent):
name = 'atom-entry-received'
base_network_events = []
def generate(self):
self.atom_entry = atom.OldEntry(node=self.node)
return True
class PlainConnectionEvent(nec.NetworkIncomingEvent):
name = 'plain-connection'
base_network_events = []
class InsecurePasswordEvent(nec.NetworkIncomingEvent):
name = 'insecure-password'
base_network_events = []
class InsecureSSLConnectionEvent(nec.NetworkIncomingEvent):
name = 'insecure-ssl-connection'
base_network_events = []
class SSLErrorEvent(nec.NetworkIncomingEvent):
name = 'ssl-error'
base_network_events = []
class FingerprintErrorEvent(nec.NetworkIncomingEvent):
name = 'fingerprint-error'
base_network_events = []
class UniqueRoomIdSupportedEvent(nec.NetworkIncomingEvent):
name = 'unique-room-id-supported'
base_network_events = []
class UniqueRoomIdNotSupportedEvent(nec.NetworkIncomingEvent):
name = 'unique-room-id-not-supported'
base_network_events = []
class PrivacyListsReceivedEvent(nec.NetworkIncomingEvent):
name = 'privacy-lists-received'
base_network_events = []
class PrivacyListReceivedEvent(nec.NetworkIncomingEvent):
name = 'privacy-list-received'
base_network_events = []
class PrivacyListRemovedEvent(nec.NetworkIncomingEvent):
name = 'privacy-list-removed'
base_network_events = []
class PrivacyListActiveDefaultEvent(nec.NetworkIncomingEvent):
name = 'privacy-list-active-default'
base_network_events = []
class NonAnonymousServerErrorEvent(nec.NetworkIncomingEvent):
name = 'non-anonymous-server-error'
base_network_events = []
class VcardReceivedEvent(nec.NetworkIncomingEvent):
name = 'vcard-received'
base_network_events = []
def generate(self):
return True
class UpdateGCAvatarEvent(nec.NetworkIncomingEvent):
name = 'update-gc-avatar'
base_network_events = []
def generate(self):
return True
class UpdateRosterAvatarEvent(nec.NetworkIncomingEvent):
name = 'update-roster-avatar'
base_network_events = []
def generate(self):
return True
class UpdateRoomAvatarEvent(nec.NetworkIncomingEvent):
name = 'update-room-avatar'
base_network_events = []
def generate(self):
return True
class RoomAvatarReceivedEvent(nec.NetworkIncomingEvent):
name = 'room-avatar-received'
base_network_events = []
def generate(self):
vcard = self.stanza.getTag('x', namespace=nbxmpp.NS_VCARD_UPDATE)
if vcard is None:
log.warning('Invalid room self presence:\n%s', self.stanza)
return
self.avatar_sha = vcard.getTagData('photo')
return True
class PEPConfigReceivedEvent(nec.NetworkIncomingEvent):
name = 'pep-config-received'
base_network_events = []
class MetacontactsReceivedEvent(nec.NetworkIncomingEvent):
name = 'metacontacts-received'
base_network_events = []
def generate(self):
# Metacontact tags
# http://www.xmpp.org/extensions/xep-0209.html
self.meta_list = {}
query = self.stanza.getTag('query')
storage = query.getTag('storage')
metas = storage.getTags('meta')
for meta in metas:
try:
jid = helpers.parse_jid(meta.getAttr('jid'))
except helpers.InvalidFormat:
continue
tag = meta.getAttr('tag')
data = {'jid': jid}
order = meta.getAttr('order')
try:
order = int(order)
except Exception:
order = 0
if order is not None:
data['order'] = order
if tag in self.meta_list:
self.meta_list[tag].append(data)
else:
self.meta_list[tag] = [data]
return True
class ZeroconfNameConflictEvent(nec.NetworkIncomingEvent):
name = 'zeroconf-name-conflict'
base_network_events = []
class PasswordRequiredEvent(nec.NetworkIncomingEvent):
name = 'password-required'
base_network_events = []
class Oauth2CredentialsRequiredEvent(nec.NetworkIncomingEvent):
name = 'oauth2-credentials-required'
base_network_events = []
class FailedDecryptEvent(nec.NetworkIncomingEvent):
name = 'failed-decrypt'
base_network_events = []
def generate(self):
self.conn = self.msg_obj.conn
self.fjid = self.msg_obj.fjid
self.timestamp = self.msg_obj.timestamp
self.session = self.msg_obj.session
return True
class SignedInEvent(nec.NetworkIncomingEvent):
name = 'signed-in'
base_network_events = []
class RegisterAgentInfoReceivedEvent(nec.NetworkIncomingEvent):
name = 'register-agent-info-received'
base_network_events = []
class AgentItemsReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'agent-items-received'
base_network_events = []
def generate(self):
q = self.stanza.getTag('query')
self.node = q.getAttr('node')
if not self.node:
self.node = ''
qp = self.stanza.getQueryPayload()
self.items = []
if not qp:
qp = []
for i in qp:
# CDATA payload is not processed, only nodes
if not isinstance(i, nbxmpp.simplexml.Node):
continue
attr = {}
for key in i.getAttrs():
attr[key] = i.getAttrs()[key]
if 'jid' not in attr:
continue
try:
attr['jid'] = helpers.parse_jid(attr['jid'])
except helpers.InvalidFormat:
# jid is not conform
continue
self.items.append(attr)
self.get_jid_resource()
hostname = app.config.get_per('accounts', self.conn.name, 'hostname')
self.get_id()
if self.id_ in self.conn.disco_items_ids:
self.conn.disco_items_ids.remove(self.id_)
if self.fjid == hostname and self.id_[:6] == 'Gajim_':
for item in self.items:
self.conn.discoverInfo(item['jid'], id_prefix='Gajim_')
else:
return True
class AgentItemsErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'agent-items-error-received'
base_network_events = []
def generate(self):
self.get_jid_resource()
self.get_id()
if self.id_ in self.conn.disco_items_ids:
self.conn.disco_items_ids.remove(self.id_)
return True
class AgentInfoReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'agent-info-received'
base_network_events = []
def generate(self):
self.get_id()
if self.id_ in self.conn.disco_info_ids:
self.conn.disco_info_ids.remove(self.id_)
if self.id_ is None:
log.warning('Invalid IQ received without an ID. '
'Ignoring it: %s', self.stanza)
return
# According to XEP-0030:
# For identity: category, type is mandatory, name is optional.
# For feature: var is mandatory
self.identities, self.features, self.data, self.node = self.parse_stanza(self.stanza)
if not self.identities:
# ejabberd doesn't send identities when we browse online users
# see http://www.jabber.ru/bugzilla/show_bug.cgi?id=225
self.identities = [{'category': 'server', 'type': 'im',
'name': self.node}]
self.get_jid_resource()
return True
@classmethod
def parse_stanza(cls, stanza):
identities, features, data, node = [], [], [], None
q = stanza.getTag('query')
node = q.getAttr('node')
if not node:
node = ''
qc = stanza.getQueryChildren()
if not qc:
qc = []
for i in qc:
if i.getName() == 'identity':
attr = {}
for key in i.getAttrs().keys():
attr[key] = i.getAttr(key)
identities.append(attr)
elif i.getName() == 'feature':
var = i.getAttr('var')
if var:
features.append(var)
elif i.getName() == 'x' and i.getNamespace() == nbxmpp.NS_DATA:
data.append(nbxmpp.DataForm(node=i))
return identities, features, data, node
class AgentInfoErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'agent-info-error-received'
base_network_events = []
def generate(self):
self.get_jid_resource()
self.get_id()
if self.id_ in self.conn.disco_info_ids:
self.conn.disco_info_ids.remove(self.id_)
return True
class FileRequestReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'file-request-received'
base_network_events = []
def init(self):
self.jingle_content = None
self.FT_content = None
def generate(self):
self.get_id()
self.fjid = self.conn._ft_get_from(self.stanza)
self.jid = app.get_jid_without_resource(self.fjid)
if self.jingle_content:
secu = self.jingle_content.getTag('security')
self.FT_content.use_security = bool(secu)
if secu:
fingerprint = secu.getTag('fingerprint')
if fingerprint:
self.FT_content.x509_fingerprint = fingerprint.getData()
if not self.FT_content.transport:
self.FT_content.transport = JingleTransportSocks5()
self.FT_content.transport.set_our_jid(
self.FT_content.session.ourjid)
self.FT_content.transport.set_connection(
self.FT_content.session.connection)
sid = self.stanza.getTag('jingle').getAttr('sid')
self.file_props = FilesProp.getNewFileProp(self.conn.name, sid)
self.file_props.transport_sid = self.FT_content.transport.sid
self.FT_content.file_props = self.file_props
self.FT_content.transport.set_file_props(self.file_props)
self.file_props.streamhosts.extend(
self.FT_content.transport.remote_candidates)
for host in self.file_props.streamhosts:
host['initiator'] = self.FT_content.session.initiator
host['target'] = self.FT_content.session.responder
self.file_props.session_type = 'jingle'
self.file_props.stream_methods = nbxmpp.NS_BYTESTREAM
desc = self.jingle_content.getTag('description')
if self.jingle_content.getAttr('creator') == 'initiator':
file_tag = desc.getTag('file')
self.file_props.sender = self.fjid
self.file_props.receiver = self.conn._ft_get_our_jid()
else:
file_tag = desc.getTag('file')
h = file_tag.getTag('hash')
h = h.getData() if h else None
n = file_tag.getTag('name')
n = n.getData() if n else None
pjid = app.get_jid_without_resource(self.fjid)
file_info = self.conn.get_file_info(pjid, hash_=h,
name=n,account=self.conn.name)
self.file_props.file_name = file_info['file-name']
self.file_props.sender = self.conn._ft_get_our_jid()
self.file_props.receiver = self.fjid
self.file_props.type_ = 's'
for child in file_tag.getChildren():
name = child.getName()
val = child.getData()
if val is None:
continue
if name == 'name':
self.file_props.name = val
if name == 'size':
self.file_props.size = int(val)
if name == 'hash':
self.file_props.algo = child.getAttr('algo')
self.file_props.hash_ = val
if name == 'date':
self.file_props.date = val
else:
si = self.stanza.getTag('si')
self.file_props = FilesProp.getNewFileProp(self.conn.name,
si.getAttr('id'))
self.file_props.transport_sid = self.file_props.sid
profile = si.getAttr('profile')
if profile != nbxmpp.NS_FILE:
self.conn.send_file_rejection(self.file_props, code='400',
typ='profile')
raise nbxmpp.NodeProcessed
feature_tag = si.getTag('feature', namespace=nbxmpp.NS_FEATURE)
if not feature_tag:
return
form_tag = feature_tag.getTag('x', namespace=nbxmpp.NS_DATA)
if not form_tag:
return
self.dataform = dataforms.ExtendForm(node=form_tag)
for f in self.dataform.iter_fields():
if f.var == 'stream-method' and f.type_ == 'list-single':
values = [o[1] for o in f.options]
self.file_props.stream_methods = ' '.join(values)
if nbxmpp.NS_BYTESTREAM in values or \
nbxmpp.NS_IBB in values:
break
else:
self.conn.send_file_rejection(self.file_props, code='400',
typ='stream')
raise nbxmpp.NodeProcessed
file_tag = si.getTag('file')
for name, val in file_tag.getAttrs().items():
if val is None:
continue
if name == 'name':
self.file_props.name = val
if name == 'size':
self.file_props.size = int(val)
mime_type = si.getAttr('mime-type')
if mime_type is not None:
self.file_props.mime_type = mime_type
self.file_props.sender = self.fjid
self.file_props.receiver = self.conn._ft_get_our_jid()
self.file_props.request_id = self.id_
file_desc_tag = file_tag.getTag('desc')
if file_desc_tag is not None:
self.file_props.desc = file_desc_tag.getData()
self.file_props.transfered_size = []
return True
class FileRequestErrorEvent(nec.NetworkIncomingEvent):
name = 'file-request-error'
base_network_events = []
def generate(self):
self.jid = app.get_jid_without_resource(self.jid)
return True
class FileTransferCompletedEvent(nec.NetworkIncomingEvent):
name = 'file-transfer-completed'
base_network_events = []
def generate(self):
jid = str(self.file_props.receiver)
self.jid = app.get_jid_without_resource(jid)
return True
class GatewayPromptReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'gateway-prompt-received'
base_network_events = []
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', 'gc-message-received',
'presence-received']
def detect_type(self):
if self.base_event.name == 'decrypted-message-received':
self.notif_type = 'msg'
if self.base_event.name == 'gc-message-received':
self.notif_type = 'gc-msg'
if self.base_event.name == 'presence-received':
self.notif_type = 'pres'
def get_focused(self):
self.control_focused = False
if self.control:
parent_win = self.control.parent_win
if parent_win and self.control == parent_win.get_active_control() \
and parent_win.window.get_property('has-toplevel-focus'):
self.control_focused = True
def handle_incoming_msg_event(self, msg_obj):
# don't alert for carbon copied messages from ourselves
if msg_obj.sent:
return
if not msg_obj.msgtxt:
return
self.jid = msg_obj.jid
if msg_obj.session:
self.control = msg_obj.session.control
else:
self.control = None
self.get_focused()
# This event has already been added to event list
if not self.control and len(app.events.get_events(self.conn.name, \
self.jid, [msg_obj.mtype])) <= 1:
self.first_unread = True
if msg_obj.mtype == 'pm':
nick = msg_obj.resource
else:
nick = app.get_name_from_jid(self.conn.name, self.jid)
if self.first_unread:
self.sound_event = 'first_message_received'
elif self.control_focused:
self.sound_event = 'next_message_received_focused'
else:
self.sound_event = 'next_message_received_unfocused'
if app.config.get('notification_preview_message'):
self.popup_text = msg_obj.msgtxt
if self.popup_text and (self.popup_text.startswith('/me ') or \
self.popup_text.startswith('/me\n')):
self.popup_text = '* ' + nick + self.popup_text[3:]
else:
# We don't want message preview, do_preview = False
self.popup_text = ''
if msg_obj.mtype == 'normal': # single message
self.popup_msg_type = 'normal'
self.popup_event_type = _('New Single Message')
self.popup_image = 'gajim-single_msg_recv'
self.popup_title = _('New Single Message from %(nickname)s') % \
{'nickname': nick}
elif msg_obj.mtype == 'pm':
self.popup_msg_type = 'pm'
self.popup_event_type = _('New Private Message')
self.popup_image = 'gajim-priv_msg_recv'
self.popup_title = _('New Private Message from group chat %s') % \
msg_obj.jid
if self.popup_text:
self.popup_text = _('%(nickname)s: %(message)s') % \
{'nickname': nick, 'message': self.popup_text}
else:
self.popup_text = _('Messaged by %(nickname)s') % \
{'nickname': nick}
else: # chat message
self.popup_msg_type = 'chat'
self.popup_event_type = _('New Message')
self.popup_image = 'gajim-chat_msg_recv'
self.popup_title = _('New Message from %(nickname)s') % \
{'nickname': nick}
if app.config.get('notify_on_new_message'):
if self.first_unread or (app.config.get('autopopup_chat_opened') \
and not self.control_focused):
if app.config.get('autopopupaway'):
# always show notification
self.do_popup = True
if app.connections[self.conn.name].connected in (2, 3):
# we're online or chat
self.do_popup = True
if msg_obj.attention and not app.config.get(
'ignore_incoming_attention'):
self.popup_timeout = 0
self.do_popup = True
else:
self.popup_timeout = app.config.get('notification_timeout')
if msg_obj.attention and not app.config.get(
'ignore_incoming_attention') and app.config.get_per('soundevents',
'attention_received', 'enabled'):
self.sound_event = 'attention_received'
self.do_sound = True
elif self.first_unread and helpers.allow_sound_notification(
self.conn.name, 'first_message_received'):
self.do_sound = True
elif not self.first_unread and self.control_focused and \
helpers.allow_sound_notification(self.conn.name,
'next_message_received_focused'):
self.do_sound = True
elif not self.first_unread and not self.control_focused and \
helpers.allow_sound_notification(self.conn.name,
'next_message_received_unfocused'):
self.do_sound = True
def handle_incoming_gc_msg_event(self, msg_obj):
if not msg_obj.msg_obj.gc_control:
# we got a message from a room we're not in? ignore it
return
self.jid = msg_obj.jid
sound = msg_obj.msg_obj.gc_control.highlighting_for_message(
msg_obj.msgtxt, msg_obj.timestamp)[1]
if msg_obj.nickname != msg_obj.msg_obj.gc_control.nick:
self.do_sound = True
if sound == 'received':
self.sound_event = 'muc_message_received'
elif sound == 'highlight':
self.sound_event = 'muc_message_highlight'
else:
self.do_sound = False
else:
self.do_sound = False
self.do_popup = False
def get_path_to_generic_or_avatar(self, generic, jid=None, suffix=None):
"""
Choose between avatar image and default image
Returns full path to the avatar image if it exists, otherwise returns full
path to the image. generic must be with extension and suffix without
"""
if jid:
# we want an avatar
puny_jid = helpers.sanitize_filename(jid)
path_to_file = os.path.join(app.AVATAR_PATH, puny_jid) + suffix
path_to_local_file = path_to_file + '_local'
for extension in ('.png', '.jpeg'):
path_to_local_file_full = path_to_local_file + extension
if os.path.exists(path_to_local_file_full):
return path_to_local_file_full
for extension in ('.png', '.jpeg'):
path_to_file_full = path_to_file + extension
if os.path.exists(path_to_file_full):
return path_to_file_full
return os.path.abspath(generic)
def handle_incoming_pres_event(self, pres_obj):
if app.jid_is_transport(pres_obj.jid):
return True
account = pres_obj.conn.name
self.jid = pres_obj.jid
resource = pres_obj.resource or ''
# It isn't an agent
for c in pres_obj.contact_list:
if c.resource == resource:
# we look for other connected resources
continue
if c.show not in ('offline', 'error'):
return True
# no other resource is connected, let's look in metacontacts
family = app.contacts.get_metacontacts_family(account, self.jid)
for info in family:
acct_ = info['account']
jid_ = info['jid']
c_ = app.contacts.get_contact_with_highest_priority(acct_, jid_)
if not c_:
continue
if c_.jid == self.jid:
continue
if c_.show not in ('offline', 'error'):
return True
if pres_obj.old_show < 2 and pres_obj.new_show > 1:
event = 'contact_connected'
show_image = 'online.png'
suffix = '_notif_size_colored'
server = app.get_server_from_jid(self.jid)
account_server = account + '/' + server
block_transport = False
if account_server in app.block_signed_in_notifications and \
app.block_signed_in_notifications[account_server]:
block_transport = True
if helpers.allow_showing_notification(account, 'notify_on_signin') \
and not app.block_signed_in_notifications[account] and \
not block_transport:
self.do_popup = True
if app.config.get_per('soundevents', 'contact_connected',
'enabled') and not app.block_signed_in_notifications[account] and\
not block_transport and helpers.allow_sound_notification(account,
'contact_connected'):
self.sound_event = event
self.do_sound = True
elif pres_obj.old_show > 1 and pres_obj.new_show < 2:
event = 'contact_disconnected'
show_image = 'offline.png'
suffix = '_notif_size_bw'
if helpers.allow_showing_notification(account, 'notify_on_signout'):
self.do_popup = True
if app.config.get_per('soundevents', 'contact_disconnected',
'enabled') and helpers.allow_sound_notification(account, event):
self.sound_event = event
self.do_sound = True
# Status change (not connected/disconnected or error (<1))
elif pres_obj.new_show > 1:
event = 'status_change'
# FIXME: we don't always 'online.png', but we first need 48x48 for
# all status
show_image = 'online.png'
suffix = '_notif_size_colored'
else:
return True
transport_name = app.get_transport_name_from_jid(self.jid)
img_path = None
if transport_name:
img_path = os.path.join(helpers.get_transport_path(
transport_name), '48x48', show_image)
if not img_path or not os.path.isfile(img_path):
iconset = app.config.get('iconset')
img_path = os.path.join(helpers.get_iconset_path(iconset),
'48x48', show_image)
self.popup_image_path = self.get_path_to_generic_or_avatar(img_path,
jid=self.jid, suffix=suffix)
self.popup_timeout = app.config.get('notification_timeout')
nick = i18n.direction_mark + app.get_name_from_jid(account, self.jid)
if event == 'status_change':
self.popup_title = _('%(nick)s Changed Status') % \
{'nick': nick}
self.popup_text = _('%(nick)s is now %(status)s') % \
{'nick': nick, 'status': helpers.get_uf_show(pres_obj.show)}
if pres_obj.status:
self.popup_text = self.popup_text + " : " + pres_obj.status
self.popup_event_type = _('Contact Changed Status')
elif event == 'contact_connected':
self.popup_title = _('%(nickname)s Signed In') % {'nickname': nick}
self.popup_text = ''
if pres_obj.status:
self.popup_text = pres_obj.status
self.popup_event_type = _('Contact Signed In')
elif event == 'contact_disconnected':
self.popup_title = _('%(nickname)s Signed Out') % {'nickname': nick}
self.popup_text = ''
if pres_obj.status:
self.popup_text = pres_obj.status
self.popup_event_type = _('Contact Signed Out')
def generate(self):
# what's needed to compute output
self.conn = self.base_event.conn
self.jid = ''
self.control = None
self.control_focused = False
self.first_unread = False
# For output
self.do_sound = False
self.sound_file = ''
self.sound_event = '' # gajim sound played if not sound_file is set
self.show_popup = False
self.do_popup = False
self.popup_title = ''
self.popup_text = ''
self.popup_event_type = ''
self.popup_msg_type = ''
self.popup_image = ''
self.popup_image_path = ''
self.popup_timeout = -1
self.do_command = False
self.command = ''
self.show_in_notification_area = False
self.show_in_roster = False
self.detect_type()
if self.notif_type == 'msg':
self.handle_incoming_msg_event(self.base_event)
elif self.notif_type == 'gc-msg':
self.handle_incoming_gc_msg_event(self.base_event)
elif self.notif_type == 'pres':
self.handle_incoming_pres_event(self.base_event)
return True
class MessageOutgoingEvent(nec.NetworkOutgoingEvent):
name = 'message-outgoing'
base_network_events = []
def init(self):
self.additional_data = {}
self.message = None
self.keyID = None
self.type_ = 'chat'
self.kind = None
self.timestamp = None
self.subject = ''
self.chatstate = None
self.stanza_id = None
self.resource = None
self.user_nick = None
self.xhtml = None
self.label = None
self.session = None
self.forward_from = None
self.form_node = None
self.delayed = None
self.callback = None
self.callback_args = []
self.now = False
self.is_loggable = True
self.control = None
self.attention = False
self.correct_id = None
self.automatic_message = True
self.encryption = ''
self.encrypted = False
def get_full_jid(self):
if self.resource:
return self.jid + '/' + self.resource
if self.session:
return self.session.get_to()
return self.jid
def generate(self):
if self.type_ == 'chat':
self.kind = KindConstant.CHAT_MSG_SENT
else:
self.kind = KindConstant.SINGLE_MSG_SENT
return True
class StanzaMessageOutgoingEvent(nec.NetworkOutgoingEvent):
name='stanza-message-outgoing'
base_network_events = []
def generate(self):
return True
class GcStanzaMessageOutgoingEvent(nec.NetworkOutgoingEvent):
name='gc-stanza-message-outgoing'
base_network_events = []
def generate(self):
return True
class GcMessageOutgoingEvent(nec.NetworkOutgoingEvent):
name = 'gc-message-outgoing'
base_network_events = []
def init(self):
self.additional_data = {}
self.message = ''
self.chatstate = None
self.xhtml = None
self.stanza_id = None
self.label = None
self.callback = None
self.callback_args = []
self.is_loggable = True
self.control = None
self.correct_id = None
self.automatic_message = True
def generate(self):
return True
class ClientCertPassphraseEvent(nec.NetworkIncomingEvent):
name = 'client-cert-passphrase'
base_network_events = []
class InformationEvent(nec.NetworkIncomingEvent):
name = 'information'
base_network_events = []
def init(self):
self.args = None
self.kwargs = {}
self.dialog_name = None
self.popup = True
def generate(self):
if self.args is None:
self.args = ()
else:
self.args = (self.args,)
return True
class BlockingEvent(nec.NetworkIncomingEvent):
name = 'blocking'
base_network_events = []
def init(self):
self.blocklist = []
self.blocked_jids = []
self.unblocked_jids = []
self.unblock_all = False
def generate(self):
block_list = self.stanza.getTag(
'blocklist', namespace=nbxmpp.NS_BLOCKING)
if block_list is not None:
for item in block_list.getTags('item'):
self.blocklist.append(item.getAttr('jid'))
app.log('blocking').info(
'Blocklist Received: %s', self.blocklist)
return True
block_tag = self.stanza.getTag('block', namespace=nbxmpp.NS_BLOCKING)
if block_tag is not None:
for item in block_tag.getTags('item'):
self.blocked_jids.append(item.getAttr('jid'))
app.log('blocking').info(
'Blocking Push - blocked JIDs: %s', self.blocked_jids)
unblock_tag = self.stanza.getTag(
'unblock', namespace=nbxmpp.NS_BLOCKING)
if unblock_tag is not None:
if not unblock_tag.getTags('item'):
self.unblock_all = True
app.log('blocking').info('Blocking Push - unblocked all')
return True
for item in unblock_tag.getTags('item'):
self.unblocked_jids.append(item.getAttr('jid'))
app.log('blocking').info(
'Blocking Push - unblocked JIDs: %s', self.unblocked_jids)
return True
class HTTPUploadStartEvent(nec.NetworkIncomingEvent):
name = 'httpupload-start'
base_network_events = []
def generate(self):
return True
class HTTPUploadProgressEvent(nec.NetworkIncomingEvent):
name = 'httpupload-progress'
base_network_events = []
def generate(self):
return True