Refactor getting avatars from pubsub

- use SendAndCallForResponse instead of triggering an Event
- validate iq result even more, it didnt print iq errors before
This commit is contained in:
Philipp Hörist 2018-02-08 21:05:15 +01:00
parent 7b1bdc5591
commit 7479bd8bd6
4 changed files with 82 additions and 67 deletions

View File

@ -24,8 +24,6 @@
from calendar import timegm from calendar import timegm
import datetime import datetime
import hashlib import hashlib
import binascii
import base64
import hmac import hmac
import logging import logging
import sys import sys
@ -604,48 +602,6 @@ class PubsubBookmarksReceivedEvent(nec.NetworkIncomingEvent, BookmarksHelper):
self.parse_bookmarks() self.parse_bookmarks()
return True return True
class PubsubAvatarReceivedEvent(nec.NetworkIncomingEvent):
name = 'pubsub-avatar-received'
base_network_events = ['pubsub-received']
def __init__(self, name, base_event):
'''
Pre-Generated attributes on self:
:conn: Connection instance
:jid: The from jid
:pubsub_node: The 'pubsub' node
:items_node: The 'items' node
'''
self._set_base_event_vars_as_attributes(base_event)
def generate(self):
if self.items_node.getAttr('node') != 'urn:xmpp:avatar:data':
return
item = self.items_node.getTag('item')
if not item:
log.warning('Received malformed avatar data via pubsub')
log.debug(self.stanza)
return
self.sha = item.getAttr('id')
data_tag = item.getTag('data', namespace='urn:xmpp:avatar:data')
if self.sha is None or data_tag is None:
log.warning('Received malformed avatar data via pubsub')
log.debug(self.stanza)
return
self.data = data_tag.getData()
if self.data is None:
log.warning('Received malformed avatar data via pubsub')
log.debug(self.stanza)
return
try:
self.data = base64.b64decode(self.data.encode('utf-8'))
except binascii.Error as err:
log.debug('Received malformed avatar data via pubsub: %s' % err)
return
return True
class SearchFormReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): class SearchFormReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'search-form-received' name = 'search-form-received'
base_network_events = [] base_network_events = []

View File

@ -147,3 +147,13 @@ class PluginsystemError(Exception):
def __str__(self): def __str__(self):
return self.text return self.text
class StanzaMalformed(Exception):
"""
Malfromed Stanza
"""
def __init__(self, message, stanza=''):
Exception.__init__(self, message, stanza)
self._msg = '{}\n{}'.format(message, stanza)
def __str__(self):
return self._msg

View File

@ -509,8 +509,8 @@ class AvatarNotificationPEP(AbstractPEP):
jid, self.avatar['id']) jid, self.avatar['id'])
return return
app.log('avatar').info('Request (Pubsub): %s', jid) app.log('avatar').info('Request (Pubsub): %s', jid)
con.send_pb_retrieve(jid, 'urn:xmpp:avatar:data', con.get_pubsub_avatar(jid, 'urn:xmpp:avatar:data',
self.avatar['id']) self.avatar['id'])
SUPPORTED_PERSONAL_USER_EVENTS = [ SUPPORTED_PERSONAL_USER_EVENTS = [

View File

@ -21,16 +21,18 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
import base64
import binascii
import nbxmpp import nbxmpp
from gajim.common import app from gajim.common import app
#TODO: Doesn't work #TODO: Doesn't work
#from common.connection_handlers import PEP_CONFIG #from common.connection_handlers import PEP_CONFIG
PEP_CONFIG = 'pep_config' PEP_CONFIG = 'pep_config'
from gajim.common import ged from gajim.common import ged
from gajim.common.nec import NetworkEvent
from gajim.common.connection_handlers_events import PubsubReceivedEvent from gajim.common.connection_handlers_events import PubsubReceivedEvent
from gajim.common.connection_handlers_events import PubsubBookmarksReceivedEvent from gajim.common.connection_handlers_events import PubsubBookmarksReceivedEvent
from gajim.common.connection_handlers_events import PubsubAvatarReceivedEvent from gajim.common.exceptions import StanzaMalformed
import logging import logging
log = logging.getLogger('gajim.c.pubsub') log = logging.getLogger('gajim.c.pubsub')
@ -39,11 +41,8 @@ class ConnectionPubSub:
def __init__(self): def __init__(self):
self.__callbacks = {} self.__callbacks = {}
app.nec.register_incoming_event(PubsubBookmarksReceivedEvent) app.nec.register_incoming_event(PubsubBookmarksReceivedEvent)
app.nec.register_incoming_event(PubsubAvatarReceivedEvent)
app.ged.register_event_handler('pubsub-bookmarks-received', app.ged.register_event_handler('pubsub-bookmarks-received',
ged.CORE, self._nec_pubsub_bookmarks_received) ged.CORE, self._nec_pubsub_bookmarks_received)
app.ged.register_event_handler('pubsub-avatar-received',
ged.CORE, self._nec_pubsub_avatar_received)
def cleanup(self): def cleanup(self):
app.ged.remove_event_handler('pubsub-bookmarks-received', app.ged.remove_event_handler('pubsub-bookmarks-received',
@ -103,22 +102,35 @@ class ConnectionPubSub:
self.connection.send(query) self.connection.send(query)
@staticmethod
def get_pb_retrieve_iq(jid, node, item_id=None):
"""
Get IQ to query items from a node
"""
query = nbxmpp.Iq('get', to=jid)
r = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
r = r.addChild('items', {'node': node})
if item_id is not None:
r.addChild('item', {'id': item_id})
return query
def send_pb_retrieve(self, jid, node, item_id=None, cb=None, *args, **kwargs): def send_pb_retrieve(self, jid, node, item_id=None, cb=None, *args, **kwargs):
""" """
Get items from a node Get items from a node
""" """
if not self.connection or self.connected < 2: if not self.connection or self.connected < 2:
return return
query = nbxmpp.Iq('get', to=jid) query = self.get_pb_retrieve_iq(jid, node, item_id)
r = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
r = r.addChild('items', {'node': node})
if item_id is not None:
r.addChild('item', {'id': item_id})
id_ = self.connection.send(query) id_ = self.connection.send(query)
if cb: if cb:
self.__callbacks[id_] = (cb, args, kwargs) self.__callbacks[id_] = (cb, args, kwargs)
def get_pubsub_avatar(self, jid, node, item_id):
query = self.get_pb_retrieve_iq(jid, node, item_id)
self.connection.SendAndCallForResponse(
query, self._nec_pubsub_avatar_received, {'jid': jid})
def send_pb_retract(self, jid, node, id_): def send_pb_retract(self, jid, node, id_):
""" """
Delete item from a node Delete item from a node
@ -211,25 +223,62 @@ class ConnectionPubSub:
# We got bookmarks from pubsub, now get those from xml to merge them # We got bookmarks from pubsub, now get those from xml to merge them
self.get_bookmarks(storage_type='xml') self.get_bookmarks(storage_type='xml')
def _nec_pubsub_avatar_received(self, obj): def _validate_avatar_node(self, stanza):
if obj.conn.name != self.name: jid = stanza.getFrom()
return if jid is None:
if obj.jid is None:
jid = self.get_own_jid().getStripped() jid = self.get_own_jid().getStripped()
else: else:
jid = obj.jid.getStripped() jid = jid.getStripped()
if nbxmpp.isErrorNode(stanza):
raise StanzaMalformed(stanza.getErrorMsg())
pubsub_node = stanza.getTag('pubsub')
if pubsub_node is None:
raise StanzaMalformed('No pubsub node', stanza)
items_node = pubsub_node.getTag('items')
if items_node is None:
raise StanzaMalformed('No items node', stanza)
if items_node.getAttr('node') != 'urn:xmpp:avatar:data':
raise StanzaMalformed('Wrong namespace', stanza)
item = items_node.getTag('item')
if item is None:
raise StanzaMalformed('No item node', stanza)
sha = item.getAttr('id')
data_tag = item.getTag('data', namespace='urn:xmpp:avatar:data')
if sha is None or data_tag is None:
raise StanzaMalformed('No id attr or data node found', stanza)
data = data_tag.getData()
if data is None:
raise StanzaMalformed('Data node empty', stanza)
data = base64.b64decode(data.encode('utf-8'))
return jid, sha, data
def _nec_pubsub_avatar_received(self, conn, stanza, jid):
try:
jid, sha, data = self._validate_avatar_node(stanza)
except (StanzaMalformed, binascii.Error) as error:
app.log('avatar').warning(
'Error loading Avatar (Pubsub): %s %s', jid, error)
return
app.log('avatar').info( app.log('avatar').info(
'Received Avatar (Pubsub): %s %s', jid, obj.sha) 'Received Avatar (Pubsub): %s %s', jid, sha)
app.interface.save_avatar(obj.data) app.interface.save_avatar(data)
if self.get_own_jid().bareMatch(jid): if self.get_own_jid().bareMatch(jid):
app.config.set_per('accounts', self.name, 'avatar_sha', obj.sha) app.config.set_per('accounts', self.name, 'avatar_sha', sha)
else: else:
own_jid = self.get_own_jid().getStripped() own_jid = self.get_own_jid().getStripped()
app.logger.set_avatar_sha(own_jid, jid, obj.sha) app.logger.set_avatar_sha(own_jid, jid, sha)
app.contacts.set_avatar(self.name, jid, obj.sha) app.contacts.set_avatar(self.name, jid, sha)
app.interface.update_avatar(self.name, jid) app.interface.update_avatar(self.name, jid)