Refactor VCard code into own modules
This commit is contained in:
parent
71a82b5c3e
commit
8b800f4646
|
@ -397,6 +397,9 @@ def account_is_connected(account):
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def is_invisible(account):
|
||||||
|
return SHOW_LIST[connections[account].connected] == 'invisible'
|
||||||
|
|
||||||
def account_is_disconnected(account):
|
def account_is_disconnected(account):
|
||||||
return not account_is_connected(account)
|
return not account_is_connected(account)
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,8 @@ from gajim.common.modules.annotations import Annotations
|
||||||
from gajim.common.modules.roster_item_exchange import RosterItemExchange
|
from gajim.common.modules.roster_item_exchange import RosterItemExchange
|
||||||
from gajim.common.modules.last_activity import LastActivity
|
from gajim.common.modules.last_activity import LastActivity
|
||||||
from gajim.common.modules.http_auth import HTTPAuth
|
from gajim.common.modules.http_auth import HTTPAuth
|
||||||
|
from gajim.common.modules.vcard_temp import VCardTemp
|
||||||
|
from gajim.common.modules.vcard_avatars import VCardAvatars
|
||||||
from gajim.common.connection_handlers import *
|
from gajim.common.connection_handlers import *
|
||||||
from gajim.common.contacts import GC_Contact
|
from gajim.common.contacts import GC_Contact
|
||||||
from gajim.gtkgui_helpers import get_action
|
from gajim.gtkgui_helpers import get_action
|
||||||
|
@ -666,6 +668,8 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
self.register_module('RosterItemExchange', RosterItemExchange, self)
|
self.register_module('RosterItemExchange', RosterItemExchange, self)
|
||||||
self.register_module('LastActivity', LastActivity, self)
|
self.register_module('LastActivity', LastActivity, self)
|
||||||
self.register_module('HTTPAuth', HTTPAuth, self)
|
self.register_module('HTTPAuth', HTTPAuth, self)
|
||||||
|
self.register_module('VCardTemp', VCardTemp, self)
|
||||||
|
self.register_module('VCardAvatars', VCardAvatars, self)
|
||||||
|
|
||||||
app.ged.register_event_handler('privacy-list-received', ged.CORE,
|
app.ged.register_event_handler('privacy-list-received', ged.CORE,
|
||||||
self._nec_privacy_list_received)
|
self._nec_privacy_list_received)
|
||||||
|
@ -756,7 +760,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
self.connected = 0
|
self.connected = 0
|
||||||
self.time_to_reconnect = None
|
self.time_to_reconnect = None
|
||||||
self.privacy_rules_supported = False
|
self.privacy_rules_supported = False
|
||||||
self.avatar_presence_sent = False
|
self.get_module('VCardAvatars').avatar_advertised = False
|
||||||
if on_purpose:
|
if on_purpose:
|
||||||
self.sm = Smacks(self)
|
self.sm = Smacks(self)
|
||||||
if self.connection:
|
if self.connection:
|
||||||
|
@ -1768,7 +1772,7 @@ class Connection(CommonConnection, ConnectionHandlers):
|
||||||
show='invisible'))
|
show='invisible'))
|
||||||
if initial:
|
if initial:
|
||||||
# ask our VCard
|
# ask our VCard
|
||||||
self.request_vcard(self._on_own_avatar_received)
|
self.get_module('VCardTemp').request_vcard()
|
||||||
|
|
||||||
# Get bookmarks from private namespace
|
# Get bookmarks from private namespace
|
||||||
self.get_bookmarks()
|
self.get_bookmarks()
|
||||||
|
|
|
@ -283,388 +283,6 @@ class ConnectionDisco:
|
||||||
app.nec.push_incoming_event(AgentInfoReceivedEvent(None, conn=self,
|
app.nec.push_incoming_event(AgentInfoReceivedEvent(None, conn=self,
|
||||||
stanza=iq_obj))
|
stanza=iq_obj))
|
||||||
|
|
||||||
class ConnectionVcard:
|
|
||||||
def __init__(self):
|
|
||||||
self.own_vcard = None
|
|
||||||
self.room_jids = []
|
|
||||||
self.avatar_presence_sent = False
|
|
||||||
self._requested_shas = {}
|
|
||||||
|
|
||||||
app.ged.register_event_handler('presence-received', ged.GUI2,
|
|
||||||
self._vcard_presence_received)
|
|
||||||
app.ged.register_event_handler('gc-presence-received', ged.GUI2,
|
|
||||||
self._vcard_gc_presence_received)
|
|
||||||
app.ged.register_event_handler('room-avatar-received', ged.GUI2,
|
|
||||||
self._vcard_presence_received)
|
|
||||||
|
|
||||||
def _vcard_presence_received(self, obj):
|
|
||||||
if obj.conn.name != self.name:
|
|
||||||
return
|
|
||||||
|
|
||||||
if obj.avatar_sha is None:
|
|
||||||
# No Avatar is advertised
|
|
||||||
return
|
|
||||||
|
|
||||||
room_avatar = False
|
|
||||||
if isinstance(obj, RoomAvatarReceivedEvent):
|
|
||||||
room_avatar = True
|
|
||||||
|
|
||||||
if self.get_own_jid().bareMatch(obj.jid):
|
|
||||||
app.log('avatar').info('Update (vCard): %s %s',
|
|
||||||
obj.jid, obj.avatar_sha)
|
|
||||||
current_sha = app.config.get_per(
|
|
||||||
'accounts', self.name, 'avatar_sha')
|
|
||||||
if obj.avatar_sha != current_sha:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Request (vCard): %s', obj.jid)
|
|
||||||
self.request_vcard(self._on_own_avatar_received)
|
|
||||||
else:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Avatar already known (vCard): %s %s',
|
|
||||||
obj.jid, obj.avatar_sha)
|
|
||||||
return
|
|
||||||
|
|
||||||
if obj.avatar_sha == '':
|
|
||||||
# Empty <photo/> tag, means no avatar is advertised
|
|
||||||
app.log('avatar').info(
|
|
||||||
'%s has no avatar published (vCard)', obj.jid)
|
|
||||||
|
|
||||||
# Remove avatar
|
|
||||||
app.log('avatar').debug('Remove: %s', obj.jid)
|
|
||||||
app.contacts.set_avatar(self.name, obj.jid, None)
|
|
||||||
own_jid = self.get_own_jid().getStripped()
|
|
||||||
if not room_avatar:
|
|
||||||
app.logger.set_avatar_sha(own_jid, obj.jid, None)
|
|
||||||
app.interface.update_avatar(
|
|
||||||
self.name, obj.jid, room_avatar=room_avatar)
|
|
||||||
else:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Update (vCard): %s %s', obj.jid, obj.avatar_sha)
|
|
||||||
current_sha = app.contacts.get_avatar_sha(self.name, obj.jid)
|
|
||||||
|
|
||||||
if obj.avatar_sha == current_sha:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Avatar already known (vCard): %s %s',
|
|
||||||
obj.jid, obj.avatar_sha)
|
|
||||||
return
|
|
||||||
|
|
||||||
if room_avatar:
|
|
||||||
# We dont save the room avatar hash in our DB, so check
|
|
||||||
# if we previously downloaded it
|
|
||||||
if app.interface.avatar_exists(obj.avatar_sha):
|
|
||||||
app.contacts.set_avatar(self.name, obj.jid, obj.avatar_sha)
|
|
||||||
app.interface.update_avatar(
|
|
||||||
self.name, obj.jid, room_avatar=room_avatar)
|
|
||||||
elif obj.jid not in self._requested_shas:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Request (vCard): %s', obj.jid)
|
|
||||||
self._requested_shas[obj.jid] = obj.avatar_sha
|
|
||||||
self.request_vcard(self._on_room_avatar_received, obj.jid)
|
|
||||||
return
|
|
||||||
|
|
||||||
if obj.jid not in self._requested_shas:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Request (vCard): %s', obj.jid)
|
|
||||||
self._requested_shas[obj.jid] = obj.avatar_sha
|
|
||||||
self.request_vcard(self._on_avatar_received, obj.jid)
|
|
||||||
|
|
||||||
|
|
||||||
def _vcard_gc_presence_received(self, obj):
|
|
||||||
if obj.conn.name != self.name:
|
|
||||||
return
|
|
||||||
|
|
||||||
server = app.get_server_from_jid(obj.room_jid)
|
|
||||||
if server.startswith('irc') or obj.avatar_sha is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if obj.show == 'offline':
|
|
||||||
return
|
|
||||||
|
|
||||||
gc_contact = app.contacts.get_gc_contact(
|
|
||||||
self.name, obj.room_jid, obj.nick)
|
|
||||||
|
|
||||||
if gc_contact is None:
|
|
||||||
app.log('avatar').error('no gc contact found: %s', obj.nick)
|
|
||||||
return
|
|
||||||
|
|
||||||
if obj.avatar_sha == '':
|
|
||||||
# Empty <photo/> tag, means no avatar is advertised, remove avatar
|
|
||||||
app.log('avatar').info(
|
|
||||||
'%s has no avatar published (vCard)', obj.nick)
|
|
||||||
app.log('avatar').debug('Remove: %s', obj.nick)
|
|
||||||
gc_contact.avatar_sha = None
|
|
||||||
app.interface.update_avatar(contact=gc_contact)
|
|
||||||
else:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Update (vCard): %s %s', obj.nick, obj.avatar_sha)
|
|
||||||
path = os.path.join(configpaths.get('AVATAR'), obj.avatar_sha)
|
|
||||||
if not os.path.isfile(path):
|
|
||||||
if obj.fjid not in self._requested_shas:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Request (vCard): %s', obj.nick)
|
|
||||||
self._requested_shas[obj.fjid] = obj.avatar_sha
|
|
||||||
obj.conn.request_vcard(
|
|
||||||
self._on_avatar_received, obj.fjid, room=True)
|
|
||||||
return
|
|
||||||
|
|
||||||
if gc_contact.avatar_sha != obj.avatar_sha:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'%s changed his Avatar (vCard): %s',
|
|
||||||
obj.nick, obj.avatar_sha)
|
|
||||||
gc_contact.avatar_sha = obj.avatar_sha
|
|
||||||
app.interface.update_avatar(contact=gc_contact)
|
|
||||||
else:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Avatar already known (vCard): %s', obj.nick)
|
|
||||||
|
|
||||||
def send_avatar_presence(self):
|
|
||||||
show = helpers.get_xmpp_show(app.SHOW_LIST[self.connected])
|
|
||||||
p = nbxmpp.Presence(typ=None, priority=self.priority,
|
|
||||||
show=show, status=self.status)
|
|
||||||
p = self.add_sha(p)
|
|
||||||
self.connection.send(p)
|
|
||||||
app.interface.update_avatar(self.name, self.get_own_jid().getStripped())
|
|
||||||
|
|
||||||
def _node_to_dict(self, node):
|
|
||||||
dict_ = {}
|
|
||||||
for info in node.getChildren():
|
|
||||||
name = info.getName()
|
|
||||||
if name in ('ADR', 'TEL', 'EMAIL'): # we can have several
|
|
||||||
dict_.setdefault(name, [])
|
|
||||||
entry = {}
|
|
||||||
for c in info.getChildren():
|
|
||||||
entry[c.getName()] = c.getData()
|
|
||||||
dict_[name].append(entry)
|
|
||||||
elif info.getChildren() == []:
|
|
||||||
dict_[name] = info.getData()
|
|
||||||
else:
|
|
||||||
dict_[name] = {}
|
|
||||||
for c in info.getChildren():
|
|
||||||
dict_[name][c.getName()] = c.getData()
|
|
||||||
return dict_
|
|
||||||
|
|
||||||
def request_vcard(self, callback, jid=None, room=False):
|
|
||||||
"""
|
|
||||||
Request the VCARD
|
|
||||||
"""
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
|
|
||||||
if room:
|
|
||||||
room_jid = app.get_room_from_fjid(jid)
|
|
||||||
if room_jid not in self.room_jids:
|
|
||||||
self.room_jids.append(room_jid)
|
|
||||||
|
|
||||||
iq = nbxmpp.Iq(typ='get')
|
|
||||||
if jid:
|
|
||||||
iq.setTo(jid)
|
|
||||||
iq.setQuery('vCard').setNamespace(nbxmpp.NS_VCARD)
|
|
||||||
|
|
||||||
self.connection.SendAndCallForResponse(
|
|
||||||
iq, self._parse_vcard, {'callback': callback})
|
|
||||||
|
|
||||||
def send_vcard(self, vcard, sha):
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
iq = nbxmpp.Iq(typ='set')
|
|
||||||
iq2 = iq.setTag(nbxmpp.NS_VCARD + ' vCard')
|
|
||||||
for i in vcard:
|
|
||||||
if i == 'jid':
|
|
||||||
continue
|
|
||||||
if isinstance(vcard[i], dict):
|
|
||||||
iq3 = iq2.addChild(i)
|
|
||||||
for j in vcard[i]:
|
|
||||||
iq3.addChild(j).setData(vcard[i][j])
|
|
||||||
elif isinstance(vcard[i], list):
|
|
||||||
for j in vcard[i]:
|
|
||||||
iq3 = iq2.addChild(i)
|
|
||||||
for k in j:
|
|
||||||
iq3.addChild(k).setData(j[k])
|
|
||||||
else:
|
|
||||||
iq2.addChild(i).setData(vcard[i])
|
|
||||||
|
|
||||||
self.connection.SendAndCallForResponse(
|
|
||||||
iq, self._avatar_publish_result, {'sha': sha})
|
|
||||||
|
|
||||||
def upload_room_avatar(self, room_jid, data):
|
|
||||||
iq = nbxmpp.Iq(typ='set', to=room_jid)
|
|
||||||
vcard = iq.addChild('vCard', namespace=nbxmpp.NS_VCARD)
|
|
||||||
photo = vcard.addChild('PHOTO')
|
|
||||||
photo.addChild('TYPE', payload='image/png')
|
|
||||||
photo.addChild('BINVAL', payload=data)
|
|
||||||
|
|
||||||
self.connection.SendAndCallForResponse(
|
|
||||||
iq, self._upload_room_avatar_result)
|
|
||||||
|
|
||||||
def _upload_room_avatar_result(self, stanza):
|
|
||||||
if not nbxmpp.isResultNode(stanza):
|
|
||||||
reason = stanza.getErrorMsg() or stanza.getError()
|
|
||||||
app.nec.push_incoming_event(InformationEvent(
|
|
||||||
None, dialog_name='avatar-upload-error', args=reason))
|
|
||||||
|
|
||||||
def _avatar_publish_result(self, con, stanza, sha):
|
|
||||||
if stanza.getType() == 'result':
|
|
||||||
current_sha = app.config.get_per(
|
|
||||||
'accounts', self.name, 'avatar_sha')
|
|
||||||
if (current_sha != sha and
|
|
||||||
app.SHOW_LIST[self.connected] != 'invisible'):
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
return
|
|
||||||
app.config.set_per(
|
|
||||||
'accounts', self.name, 'avatar_sha', sha or '')
|
|
||||||
own_jid = self.get_own_jid().getStripped()
|
|
||||||
app.contacts.set_avatar(self.name, own_jid, sha)
|
|
||||||
self.send_avatar_presence()
|
|
||||||
app.log('avatar').info('%s: Published: %s', self.name, sha)
|
|
||||||
app.nec.push_incoming_event(
|
|
||||||
VcardPublishedEvent(None, conn=self))
|
|
||||||
|
|
||||||
elif stanza.getType() == 'error':
|
|
||||||
app.nec.push_incoming_event(
|
|
||||||
VcardNotPublishedEvent(None, conn=self))
|
|
||||||
|
|
||||||
def _get_vcard_photo(self, vcard, jid):
|
|
||||||
try:
|
|
||||||
photo = vcard['PHOTO']['BINVAL']
|
|
||||||
except (KeyError, AttributeError, TypeError):
|
|
||||||
avatar_sha = None
|
|
||||||
photo_decoded = None
|
|
||||||
else:
|
|
||||||
if photo == '':
|
|
||||||
avatar_sha = None
|
|
||||||
photo_decoded = None
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
photo_decoded = base64.b64decode(photo.encode('utf-8'))
|
|
||||||
except binascii.Error as error:
|
|
||||||
app.log('avatar').warning('Invalid avatar for %s: %s',
|
|
||||||
jid, error)
|
|
||||||
return None, None
|
|
||||||
avatar_sha = hashlib.sha1(photo_decoded).hexdigest()
|
|
||||||
|
|
||||||
return avatar_sha, photo_decoded
|
|
||||||
|
|
||||||
def _parse_vcard(self, con, stanza, callback):
|
|
||||||
frm_jid = stanza.getFrom()
|
|
||||||
room = False
|
|
||||||
if frm_jid is None:
|
|
||||||
frm_jid = self.get_own_jid()
|
|
||||||
elif frm_jid.getStripped() in self.room_jids:
|
|
||||||
room = True
|
|
||||||
|
|
||||||
resource = frm_jid.getResource()
|
|
||||||
jid = frm_jid.getStripped()
|
|
||||||
|
|
||||||
stanza_error = stanza.getError()
|
|
||||||
if stanza_error in ('service-unavailable', 'item-not-found',
|
|
||||||
'not-allowed'):
|
|
||||||
app.log('avatar').info('vCard not available: %s %s',
|
|
||||||
frm_jid, stanza_error)
|
|
||||||
callback(jid, resource, room, {})
|
|
||||||
return
|
|
||||||
|
|
||||||
vcard_node = stanza.getTag('vCard', namespace=nbxmpp.NS_VCARD)
|
|
||||||
if vcard_node is None:
|
|
||||||
app.log('avatar').info('vCard not available: %s', frm_jid)
|
|
||||||
app.log('avatar').debug(stanza)
|
|
||||||
return
|
|
||||||
vcard = self._node_to_dict(vcard_node)
|
|
||||||
|
|
||||||
if self.get_own_jid().bareMatch(jid):
|
|
||||||
if 'NICKNAME' in vcard:
|
|
||||||
app.nicks[self.name] = vcard['NICKNAME']
|
|
||||||
elif 'FN' in vcard:
|
|
||||||
app.nicks[self.name] = vcard['FN']
|
|
||||||
|
|
||||||
app.nec.push_incoming_event(
|
|
||||||
VcardReceivedEvent(None, conn=self, vcard_dict=vcard))
|
|
||||||
|
|
||||||
callback(jid, resource, room, vcard)
|
|
||||||
|
|
||||||
def _on_own_avatar_received(self, jid, resource, room, vcard):
|
|
||||||
|
|
||||||
avatar_sha, photo_decoded = self._get_vcard_photo(vcard, jid)
|
|
||||||
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Received own (vCard): %s', avatar_sha)
|
|
||||||
|
|
||||||
self.own_vcard = vcard
|
|
||||||
if avatar_sha is None:
|
|
||||||
app.log('avatar').info('No avatar found (vCard)')
|
|
||||||
app.config.set_per('accounts', self.name, 'avatar_sha', '')
|
|
||||||
self.send_avatar_presence()
|
|
||||||
return
|
|
||||||
|
|
||||||
current_sha = app.config.get_per('accounts', self.name, 'avatar_sha')
|
|
||||||
if current_sha == avatar_sha:
|
|
||||||
path = os.path.join(configpaths.get('AVATAR'), current_sha)
|
|
||||||
if not os.path.isfile(path):
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Caching (vCard): %s', current_sha)
|
|
||||||
app.interface.save_avatar(photo_decoded)
|
|
||||||
if self.avatar_presence_sent:
|
|
||||||
app.log('avatar').debug('Avatar already advertised')
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
app.interface.save_avatar(photo_decoded)
|
|
||||||
|
|
||||||
app.config.set_per('accounts', self.name, 'avatar_sha', avatar_sha)
|
|
||||||
if app.SHOW_LIST[self.connected] == 'invisible':
|
|
||||||
app.log('avatar').info(
|
|
||||||
'We are invisible, not publishing avatar')
|
|
||||||
return
|
|
||||||
|
|
||||||
self.send_avatar_presence()
|
|
||||||
self.avatar_presence_sent = True
|
|
||||||
|
|
||||||
def _on_room_avatar_received(self, jid, resource, room, vcard):
|
|
||||||
avatar_sha, photo_decoded = self._get_vcard_photo(vcard, jid)
|
|
||||||
expected_avatar_sha = self._requested_shas[jid]
|
|
||||||
if expected_avatar_sha != avatar_sha:
|
|
||||||
app.log('avatar').warning(
|
|
||||||
'Avatar mismatch (vCard): %s %s', jid, avatar_sha)
|
|
||||||
return
|
|
||||||
|
|
||||||
app.interface.save_avatar(photo_decoded)
|
|
||||||
self._requested_shas.pop(jid)
|
|
||||||
|
|
||||||
app.log('avatar').info('Received (vCard): %s %s', jid, avatar_sha)
|
|
||||||
app.contacts.set_avatar(self.name, jid, avatar_sha)
|
|
||||||
app.interface.update_avatar(self.name, jid, room_avatar=True)
|
|
||||||
|
|
||||||
def _on_avatar_received(self, jid, resource, room, vcard):
|
|
||||||
"""
|
|
||||||
Called when we receive a vCard Parse the vCard and trigger Events
|
|
||||||
"""
|
|
||||||
request_jid = jid
|
|
||||||
if room:
|
|
||||||
request_jid = '%s/%s' % (jid, resource)
|
|
||||||
|
|
||||||
avatar_sha, photo_decoded = self._get_vcard_photo(vcard, request_jid)
|
|
||||||
expected_avatar_sha = self._requested_shas[request_jid]
|
|
||||||
if expected_avatar_sha != avatar_sha:
|
|
||||||
app.log('avatar').warning(
|
|
||||||
'Avatar mismatch (vCard): %s %s', request_jid, avatar_sha)
|
|
||||||
return
|
|
||||||
|
|
||||||
app.interface.save_avatar(photo_decoded)
|
|
||||||
self._requested_shas.pop(request_jid)
|
|
||||||
|
|
||||||
# Received vCard from a contact
|
|
||||||
if room:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'Received (vCard): %s %s', resource, avatar_sha)
|
|
||||||
contact = app.contacts.get_gc_contact(self.name, jid, resource)
|
|
||||||
if contact is not None:
|
|
||||||
contact.avatar_sha = avatar_sha
|
|
||||||
app.interface.update_avatar(contact=contact)
|
|
||||||
else:
|
|
||||||
app.log('avatar').info('Received (vCard): %s %s', jid, avatar_sha)
|
|
||||||
own_jid = self.get_own_jid().getStripped()
|
|
||||||
app.logger.set_avatar_sha(own_jid, jid, avatar_sha)
|
|
||||||
app.contacts.set_avatar(self.name, jid, avatar_sha)
|
|
||||||
app.interface.update_avatar(self.name, jid)
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectionPEP(object):
|
class ConnectionPEP(object):
|
||||||
|
|
||||||
|
@ -1296,13 +914,12 @@ class ConnectionHandlersBase:
|
||||||
return sess
|
return sess
|
||||||
|
|
||||||
class ConnectionHandlers(ConnectionArchive313,
|
class ConnectionHandlers(ConnectionArchive313,
|
||||||
ConnectionVcard, ConnectionSocks5Bytestream, ConnectionDisco,
|
ConnectionSocks5Bytestream, ConnectionDisco,
|
||||||
ConnectionCommands, ConnectionPubSub, ConnectionPEP, ConnectionCaps,
|
ConnectionCommands, ConnectionPubSub, ConnectionPEP, ConnectionCaps,
|
||||||
ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream,
|
ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream,
|
||||||
ConnectionHTTPUpload):
|
ConnectionHTTPUpload):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
ConnectionArchive313.__init__(self)
|
ConnectionArchive313.__init__(self)
|
||||||
ConnectionVcard.__init__(self)
|
|
||||||
ConnectionSocks5Bytestream.__init__(self)
|
ConnectionSocks5Bytestream.__init__(self)
|
||||||
ConnectionIBBytestream.__init__(self)
|
ConnectionIBBytestream.__init__(self)
|
||||||
ConnectionCommands.__init__(self)
|
ConnectionCommands.__init__(self)
|
||||||
|
@ -1398,11 +1015,7 @@ ConnectionHTTPUpload):
|
||||||
app.ged.remove_event_handler('blocking', ged.CORE, self._nec_blocking)
|
app.ged.remove_event_handler('blocking', ged.CORE, self._nec_blocking)
|
||||||
|
|
||||||
def add_sha(self, p, send_caps=True):
|
def add_sha(self, p, send_caps=True):
|
||||||
c = p.setTag('x', namespace=nbxmpp.NS_VCARD_UPDATE)
|
p = self.get_module('VCardAvatars').add_update_node(p)
|
||||||
sha = app.config.get_per('accounts', self.name, 'avatar_sha')
|
|
||||||
app.log('avatar').info(
|
|
||||||
'%s: Send avatar presence: %s', self.name, sha or 'empty')
|
|
||||||
c.setTagData('photo', sha)
|
|
||||||
if send_caps:
|
if send_caps:
|
||||||
return self._add_caps(p)
|
return self._add_caps(p)
|
||||||
return p
|
return p
|
||||||
|
@ -1925,7 +1538,7 @@ ConnectionHTTPUpload):
|
||||||
show=show))
|
show=show))
|
||||||
if self.vcard_supported:
|
if self.vcard_supported:
|
||||||
# ask our VCard
|
# ask our VCard
|
||||||
self.request_vcard(self._on_own_avatar_received)
|
self.get_module('VCardTemp').request_vcard()
|
||||||
|
|
||||||
# Get bookmarks from private namespace
|
# Get bookmarks from private namespace
|
||||||
self.get_bookmarks()
|
self.get_bookmarks()
|
||||||
|
|
|
@ -550,16 +550,6 @@ PresenceHelperEvent):
|
||||||
tim = helpers.datetime_tuple(time_str)
|
tim = helpers.datetime_tuple(time_str)
|
||||||
self.idle_time = timegm(tim)
|
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')
|
xtags = self.stanza.getTags('x')
|
||||||
for x in xtags:
|
for x in xtags:
|
||||||
namespace = x.getNamespace()
|
namespace = x.getNamespace()
|
||||||
|
@ -567,9 +557,6 @@ PresenceHelperEvent):
|
||||||
self.is_gc = True
|
self.is_gc = True
|
||||||
elif namespace == nbxmpp.NS_SIGNED:
|
elif namespace == nbxmpp.NS_SIGNED:
|
||||||
sig_tag = x
|
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:
|
elif namespace == nbxmpp.NS_DELAY and not self.timestamp:
|
||||||
# XEP-0091
|
# XEP-0091
|
||||||
self._generate_timestamp(self.stanza.timestamp)
|
self._generate_timestamp(self.stanza.timestamp)
|
||||||
|
@ -1770,14 +1757,6 @@ class ConnectionTypeEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'connection-type'
|
name = 'connection-type'
|
||||||
base_network_events = []
|
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):
|
class StanzaReceivedEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'stanza-received'
|
name = 'stanza-received'
|
||||||
base_network_events = []
|
base_network_events = []
|
||||||
|
@ -1967,13 +1946,6 @@ class NonAnonymousServerErrorEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'non-anonymous-server-error'
|
name = 'non-anonymous-server-error'
|
||||||
base_network_events = []
|
base_network_events = []
|
||||||
|
|
||||||
class VcardReceivedEvent(nec.NetworkIncomingEvent):
|
|
||||||
name = 'vcard-received'
|
|
||||||
base_network_events = []
|
|
||||||
|
|
||||||
def generate(self):
|
|
||||||
return True
|
|
||||||
|
|
||||||
class UpdateGCAvatarEvent(nec.NetworkIncomingEvent):
|
class UpdateGCAvatarEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'update-gc-avatar'
|
name = 'update-gc-avatar'
|
||||||
base_network_events = []
|
base_network_events = []
|
||||||
|
@ -1995,19 +1967,6 @@ class UpdateRoomAvatarEvent(nec.NetworkIncomingEvent):
|
||||||
def generate(self):
|
def generate(self):
|
||||||
return True
|
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:
|
|
||||||
app.log('avatar').info(
|
|
||||||
'%s has no avatar published (vCard)', self.jid)
|
|
||||||
return
|
|
||||||
self.avatar_sha = vcard.getTagData('photo')
|
|
||||||
return True
|
|
||||||
|
|
||||||
class PEPConfigReceivedEvent(nec.NetworkIncomingEvent):
|
class PEPConfigReceivedEvent(nec.NetworkIncomingEvent):
|
||||||
name = 'pep-config-received'
|
name = 'pep-config-received'
|
||||||
base_network_events = []
|
base_network_events = []
|
||||||
|
|
|
@ -113,6 +113,12 @@ class IdleState(IntEnum):
|
||||||
AWAY = 2
|
AWAY = 2
|
||||||
AWAKE = 3
|
AWAKE = 3
|
||||||
|
|
||||||
|
@unique
|
||||||
|
class RequestAvatar(IntEnum):
|
||||||
|
SELF = 0
|
||||||
|
ROOM = 1
|
||||||
|
USER = 2
|
||||||
|
|
||||||
SSLError = {
|
SSLError = {
|
||||||
2: _("Unable to get issuer certificate"),
|
2: _("Unable to get issuer certificate"),
|
||||||
3: _("Unable to get certificate CRL"),
|
3: _("Unable to get certificate CRL"),
|
||||||
|
|
|
@ -1398,15 +1398,7 @@ def get_subscription_request_msg(account=None):
|
||||||
s = _('I would like to add you to my contact list.')
|
s = _('I would like to add you to my contact list.')
|
||||||
if account:
|
if account:
|
||||||
s = _('Hello, I am $name.') + ' ' + s
|
s = _('Hello, I am $name.') + ' ' + s
|
||||||
our_jid = app.get_jid_from_account(account)
|
name = app.connections[account].get_module('VCardTemp').get_vard_name()
|
||||||
vcard = app.connections[account].own_vcard
|
|
||||||
name = ''
|
|
||||||
if vcard:
|
|
||||||
if 'N' in vcard:
|
|
||||||
if 'GIVEN' in vcard['N'] and 'FAMILY' in vcard['N']:
|
|
||||||
name = vcard['N']['GIVEN'] + ' ' + vcard['N']['FAMILY']
|
|
||||||
if not name and 'FN' in vcard:
|
|
||||||
name = vcard['FN']
|
|
||||||
nick = app.nicks[account]
|
nick = app.nicks[account]
|
||||||
if name and nick:
|
if name and nick:
|
||||||
name += ' (%s)' % nick
|
name += ' (%s)' % nick
|
||||||
|
|
|
@ -0,0 +1,192 @@
|
||||||
|
# This file is part of Gajim.
|
||||||
|
#
|
||||||
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation; version 3 only.
|
||||||
|
#
|
||||||
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# XEP-0153: vCard-Based Avatars
|
||||||
|
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
|
||||||
|
from gajim.common import app
|
||||||
|
from gajim.common import helpers
|
||||||
|
from gajim.common import configpaths
|
||||||
|
from gajim.common.const import RequestAvatar
|
||||||
|
|
||||||
|
log = logging.getLogger('gajim.c.m.vcard.avatars')
|
||||||
|
|
||||||
|
|
||||||
|
class VCardAvatars:
|
||||||
|
def __init__(self, con):
|
||||||
|
self._con = con
|
||||||
|
self._account = con.name
|
||||||
|
self._requested_shas = []
|
||||||
|
|
||||||
|
self.handlers = [
|
||||||
|
('presence', self._presence_received, '', nbxmpp.NS_VCARD_UPDATE),
|
||||||
|
]
|
||||||
|
|
||||||
|
self.avatar_advertised = False
|
||||||
|
|
||||||
|
def _presence_received(self, con, stanza):
|
||||||
|
update = stanza.getTag('x', namespace=nbxmpp.NS_VCARD_UPDATE)
|
||||||
|
if update is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
jid = stanza.getFrom()
|
||||||
|
|
||||||
|
avatar_sha = update.getTagData('photo')
|
||||||
|
if avatar_sha is None:
|
||||||
|
log.info('%s is not ready to promote an avatar', jid)
|
||||||
|
# Empty update element, ignore
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._con.get_own_jid().bareMatch(jid):
|
||||||
|
if self._con.get_own_jid() == jid:
|
||||||
|
# Reflection of our own presence
|
||||||
|
return
|
||||||
|
self._self_update_received(jid, avatar_sha)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if presence is from a MUC service
|
||||||
|
contact = app.contacts.get_groupchat_contact(self._account, str(jid))
|
||||||
|
if contact is not None:
|
||||||
|
self._update_received(jid, avatar_sha)
|
||||||
|
elif stanza.getTag('x', namespace=nbxmpp.NS_MUC_USER):
|
||||||
|
show = stanza.getShow()
|
||||||
|
type_ = stanza.getType()
|
||||||
|
self._gc_update_received(jid, avatar_sha, show, type_)
|
||||||
|
else:
|
||||||
|
self._update_received(jid, avatar_sha)
|
||||||
|
|
||||||
|
def _self_update_received(self, jid, avatar_sha):
|
||||||
|
jid = jid.getStripped()
|
||||||
|
full_jid = jid
|
||||||
|
if avatar_sha == '':
|
||||||
|
# Empty <photo/> tag, means no avatar is advertised
|
||||||
|
log.info('%s has no avatar published', full_jid)
|
||||||
|
return
|
||||||
|
|
||||||
|
log.info('Update: %s %s', jid, avatar_sha)
|
||||||
|
current_sha = app.config.get_per(
|
||||||
|
'accounts', self._account, 'avatar_sha')
|
||||||
|
|
||||||
|
if avatar_sha != current_sha:
|
||||||
|
log.info('Request : %s', jid)
|
||||||
|
self._con.get_module('VCardTemp').request_vcard(RequestAvatar.SELF)
|
||||||
|
else:
|
||||||
|
log.info('Avatar already known: %s %s',
|
||||||
|
jid, avatar_sha)
|
||||||
|
|
||||||
|
def _update_received(self, jid, avatar_sha, room=False):
|
||||||
|
jid = jid.getStripped()
|
||||||
|
full_jid = jid
|
||||||
|
if avatar_sha == '':
|
||||||
|
# Empty <photo/> tag, means no avatar is advertised
|
||||||
|
log.info('%s has no avatar published', full_jid)
|
||||||
|
|
||||||
|
# Remove avatar
|
||||||
|
log.debug('Remove: %s', jid)
|
||||||
|
app.contacts.set_avatar(self._account, jid, None)
|
||||||
|
acc_jid = self._con.get_own_jid().getStripped()
|
||||||
|
if not room:
|
||||||
|
app.logger.set_avatar_sha(acc_jid, jid, None)
|
||||||
|
app.interface.update_avatar(
|
||||||
|
self._account, jid, room_avatar=room)
|
||||||
|
else:
|
||||||
|
log.info('Update: %s %s', full_jid, avatar_sha)
|
||||||
|
current_sha = app.contacts.get_avatar_sha(self._account, jid)
|
||||||
|
|
||||||
|
if avatar_sha == current_sha:
|
||||||
|
log.info('Avatar already known: %s %s', jid, avatar_sha)
|
||||||
|
return
|
||||||
|
|
||||||
|
if room:
|
||||||
|
# We dont save the room avatar hash in our DB, so check
|
||||||
|
# if we previously downloaded it
|
||||||
|
if app.interface.avatar_exists(avatar_sha):
|
||||||
|
app.contacts.set_avatar(self._account, jid, avatar_sha)
|
||||||
|
app.interface.update_avatar(
|
||||||
|
self._account, jid, room_avatar=room)
|
||||||
|
return
|
||||||
|
|
||||||
|
if avatar_sha not in self._requested_shas:
|
||||||
|
self._requested_shas.append(avatar_sha)
|
||||||
|
if room:
|
||||||
|
self._con.get_module('VCardTemp').request_vcard(
|
||||||
|
RequestAvatar.ROOM, jid, sha=avatar_sha)
|
||||||
|
else:
|
||||||
|
self._con.get_module('VCardTemp').request_vcard(
|
||||||
|
RequestAvatar.USER, jid, sha=avatar_sha)
|
||||||
|
|
||||||
|
def _gc_update_received(self, jid, avatar_sha, show, type_):
|
||||||
|
if show == 'offline' or type_ == 'unavailable':
|
||||||
|
return
|
||||||
|
|
||||||
|
nick = jid.getResource()
|
||||||
|
|
||||||
|
gc_contact = app.contacts.get_gc_contact(
|
||||||
|
self._account, jid.getStripped(), nick)
|
||||||
|
|
||||||
|
if gc_contact is None:
|
||||||
|
log.error('no gc contact found: %s', nick)
|
||||||
|
return
|
||||||
|
|
||||||
|
if avatar_sha == '':
|
||||||
|
# Empty <photo/> tag, means no avatar is advertised, remove avatar
|
||||||
|
log.info('%s has no avatar published', nick)
|
||||||
|
log.debug('Remove: %s', nick)
|
||||||
|
gc_contact.avatar_sha = None
|
||||||
|
app.interface.update_avatar(contact=gc_contact)
|
||||||
|
else:
|
||||||
|
log.info('Update: %s %s', nick, avatar_sha)
|
||||||
|
path = os.path.join(configpaths.get('AVATAR'), avatar_sha)
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
if avatar_sha not in self._requested_shas:
|
||||||
|
app.log('avatar').info('Request: %s', nick)
|
||||||
|
self._requested_shas.append(avatar_sha)
|
||||||
|
self._con.get_module('VCardTemp').request_vcard(
|
||||||
|
RequestAvatar.USER, str(jid),
|
||||||
|
room=True, sha=avatar_sha)
|
||||||
|
return
|
||||||
|
|
||||||
|
if gc_contact.avatar_sha != avatar_sha:
|
||||||
|
log.info('%s changed his Avatar: %s', nick, avatar_sha)
|
||||||
|
gc_contact.avatar_sha = avatar_sha
|
||||||
|
app.interface.update_avatar(contact=gc_contact)
|
||||||
|
else:
|
||||||
|
log.info('Avatar already known: %s', nick)
|
||||||
|
|
||||||
|
def send_avatar_presence(self, force=False):
|
||||||
|
if self.avatar_advertised and not force:
|
||||||
|
log.debug('Avatar already advertised')
|
||||||
|
return
|
||||||
|
show = helpers.get_xmpp_show(app.SHOW_LIST[self._con.connected])
|
||||||
|
pres = nbxmpp.Presence(typ=None, priority=self._con.priority,
|
||||||
|
show=show, status=self._con.status)
|
||||||
|
pres = self._con.add_sha(pres)
|
||||||
|
self._con.connection.send(pres)
|
||||||
|
self.avatar_advertised = True
|
||||||
|
app.interface.update_avatar(self._account,
|
||||||
|
self._con.get_own_jid().getStripped())
|
||||||
|
|
||||||
|
def add_update_node(self, node):
|
||||||
|
update = node.setTag('x', namespace=nbxmpp.NS_VCARD_UPDATE)
|
||||||
|
if self._con.get_module('VCardTemp').own_vcard_received:
|
||||||
|
sha = app.config.get_per('accounts', self._account, 'avatar_sha')
|
||||||
|
own_jid = self._con.get_own_jid()
|
||||||
|
log.info('Send avatar presence to: %s %s',
|
||||||
|
node.getTo() or own_jid, sha or 'no sha advertised')
|
||||||
|
update.setTagData('photo', sha)
|
||||||
|
return node
|
|
@ -0,0 +1,308 @@
|
||||||
|
# This file is part of Gajim.
|
||||||
|
#
|
||||||
|
# Gajim is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published
|
||||||
|
# by the Free Software Foundation; version 3 only.
|
||||||
|
#
|
||||||
|
# Gajim is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# XEP-0054: vcard-temp
|
||||||
|
|
||||||
|
import os
|
||||||
|
import hashlib
|
||||||
|
import binascii
|
||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
|
||||||
|
import nbxmpp
|
||||||
|
|
||||||
|
from gajim.common import app
|
||||||
|
from gajim.common import configpaths
|
||||||
|
from gajim.common.const import RequestAvatar
|
||||||
|
from gajim.common.nec import NetworkIncomingEvent
|
||||||
|
from gajim.common.connection_handlers_events import InformationEvent
|
||||||
|
|
||||||
|
log = logging.getLogger('gajim.c.m.vcard')
|
||||||
|
|
||||||
|
|
||||||
|
class VCardTemp:
|
||||||
|
def __init__(self, con):
|
||||||
|
self._con = con
|
||||||
|
self._account = con.name
|
||||||
|
|
||||||
|
self.handlers = []
|
||||||
|
|
||||||
|
self._own_vcard = None
|
||||||
|
self.own_vcard_received = False
|
||||||
|
self.room_jids = []
|
||||||
|
|
||||||
|
def _node_to_dict(self, node):
|
||||||
|
dict_ = {}
|
||||||
|
for info in node.getChildren():
|
||||||
|
name = info.getName()
|
||||||
|
if name in ('ADR', 'TEL', 'EMAIL'): # we can have several
|
||||||
|
dict_.setdefault(name, [])
|
||||||
|
entry = {}
|
||||||
|
for c in info.getChildren():
|
||||||
|
entry[c.getName()] = c.getData()
|
||||||
|
dict_[name].append(entry)
|
||||||
|
elif info.getChildren() == []:
|
||||||
|
dict_[name] = info.getData()
|
||||||
|
else:
|
||||||
|
dict_[name] = {}
|
||||||
|
for c in info.getChildren():
|
||||||
|
dict_[name][c.getName()] = c.getData()
|
||||||
|
return dict_
|
||||||
|
|
||||||
|
def request_vcard(self, callback=RequestAvatar.SELF, jid=None,
|
||||||
|
room=False, sha=None):
|
||||||
|
if not app.account_is_connected(self._account):
|
||||||
|
return
|
||||||
|
|
||||||
|
if isinstance(callback, RequestAvatar):
|
||||||
|
if callback == RequestAvatar.SELF:
|
||||||
|
callback = self._on_own_avatar_received
|
||||||
|
elif callback == RequestAvatar.ROOM:
|
||||||
|
callback = self._on_room_avatar_received
|
||||||
|
elif callback == RequestAvatar.USER:
|
||||||
|
callback = self._on_avatar_received
|
||||||
|
|
||||||
|
if room:
|
||||||
|
room_jid = app.get_room_from_fjid(jid)
|
||||||
|
if room_jid not in self.room_jids:
|
||||||
|
self.room_jids.append(room_jid)
|
||||||
|
|
||||||
|
iq = nbxmpp.Iq(typ='get')
|
||||||
|
if jid:
|
||||||
|
iq.setTo(jid)
|
||||||
|
iq.setQuery('vCard').setNamespace(nbxmpp.NS_VCARD)
|
||||||
|
|
||||||
|
own_jid = self._con.get_own_jid().getStripped()
|
||||||
|
log.info('Request: %s, expected sha: %s', jid or own_jid, sha)
|
||||||
|
|
||||||
|
self._con.connection.SendAndCallForResponse(
|
||||||
|
iq, self._parse_vcard, {'callback': callback, 'expected_sha': sha})
|
||||||
|
|
||||||
|
def send_vcard(self, vcard, sha):
|
||||||
|
if not app.account_is_connected(self._account):
|
||||||
|
return
|
||||||
|
|
||||||
|
iq = nbxmpp.Iq(typ='set')
|
||||||
|
iq2 = iq.setTag(nbxmpp.NS_VCARD + ' vCard')
|
||||||
|
for i in vcard:
|
||||||
|
if i == 'jid':
|
||||||
|
continue
|
||||||
|
if isinstance(vcard[i], dict):
|
||||||
|
iq3 = iq2.addChild(i)
|
||||||
|
for j in vcard[i]:
|
||||||
|
iq3.addChild(j).setData(vcard[i][j])
|
||||||
|
elif isinstance(vcard[i], list):
|
||||||
|
for j in vcard[i]:
|
||||||
|
iq3 = iq2.addChild(i)
|
||||||
|
for k in j:
|
||||||
|
iq3.addChild(k).setData(j[k])
|
||||||
|
else:
|
||||||
|
iq2.addChild(i).setData(vcard[i])
|
||||||
|
|
||||||
|
log.info('Upload avatar: %s %s', self._account, sha)
|
||||||
|
|
||||||
|
self._con.connection.SendAndCallForResponse(
|
||||||
|
iq, self._avatar_publish_result, {'sha': sha})
|
||||||
|
|
||||||
|
def upload_room_avatar(self, room_jid, data):
|
||||||
|
iq = nbxmpp.Iq(typ='set', to=room_jid)
|
||||||
|
vcard = iq.addChild('vCard', namespace=nbxmpp.NS_VCARD)
|
||||||
|
photo = vcard.addChild('PHOTO')
|
||||||
|
photo.addChild('TYPE', payload='image/png')
|
||||||
|
photo.addChild('BINVAL', payload=data)
|
||||||
|
|
||||||
|
log.info('Upload avatar: %s %s', room_jid)
|
||||||
|
self._con.connection.SendAndCallForResponse(
|
||||||
|
iq, self._upload_room_avatar_result)
|
||||||
|
|
||||||
|
def _upload_room_avatar_result(self, stanza):
|
||||||
|
if not nbxmpp.isResultNode(stanza):
|
||||||
|
reason = stanza.getErrorMsg() or stanza.getError()
|
||||||
|
app.nec.push_incoming_event(InformationEvent(
|
||||||
|
None, dialog_name='avatar-upload-error', args=reason))
|
||||||
|
|
||||||
|
def _avatar_publish_result(self, con, stanza, sha):
|
||||||
|
if stanza.getType() == 'result':
|
||||||
|
current_sha = app.config.get_per(
|
||||||
|
'accounts', self._account, 'avatar_sha')
|
||||||
|
if (current_sha != sha and not app.is_invisible(self._account)):
|
||||||
|
if not app.account_is_connected(self._account):
|
||||||
|
return
|
||||||
|
app.config.set_per(
|
||||||
|
'accounts', self._account, 'avatar_sha', sha or '')
|
||||||
|
own_jid = self._con.get_own_jid().getStripped()
|
||||||
|
app.contacts.set_avatar(self._account, own_jid, sha)
|
||||||
|
self._con.get_module('VCardAvatars').send_avatar_presence(
|
||||||
|
force=True)
|
||||||
|
log.info('%s: Published: %s', self._account, sha)
|
||||||
|
app.nec.push_incoming_event(
|
||||||
|
VcardPublishedEvent(None, conn=self._con))
|
||||||
|
|
||||||
|
elif stanza.getType() == 'error':
|
||||||
|
app.nec.push_incoming_event(
|
||||||
|
VcardNotPublishedEvent(None, conn=self._con))
|
||||||
|
|
||||||
|
def _get_vcard_photo(self, vcard, jid):
|
||||||
|
try:
|
||||||
|
photo = vcard['PHOTO']['BINVAL']
|
||||||
|
except (KeyError, AttributeError, TypeError):
|
||||||
|
avatar_sha = None
|
||||||
|
photo_decoded = None
|
||||||
|
else:
|
||||||
|
if photo == '':
|
||||||
|
avatar_sha = None
|
||||||
|
photo_decoded = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
photo_decoded = base64.b64decode(photo.encode('utf-8'))
|
||||||
|
except binascii.Error as error:
|
||||||
|
log.warning('Invalid avatar for %s: %s', jid, error)
|
||||||
|
return None, None
|
||||||
|
avatar_sha = hashlib.sha1(photo_decoded).hexdigest()
|
||||||
|
|
||||||
|
return avatar_sha, photo_decoded
|
||||||
|
|
||||||
|
def _parse_vcard(self, con, stanza, callback, expected_sha):
|
||||||
|
frm_jid = stanza.getFrom()
|
||||||
|
room = False
|
||||||
|
if frm_jid is None:
|
||||||
|
frm_jid = self._con.get_own_jid()
|
||||||
|
elif frm_jid.getStripped() in self.room_jids:
|
||||||
|
room = True
|
||||||
|
|
||||||
|
resource = frm_jid.getResource()
|
||||||
|
jid = frm_jid.getStripped()
|
||||||
|
|
||||||
|
stanza_error = stanza.getError()
|
||||||
|
if stanza_error in ('service-unavailable', 'item-not-found',
|
||||||
|
'not-allowed'):
|
||||||
|
log.info('vCard not available: %s %s', frm_jid, stanza_error)
|
||||||
|
callback(jid, resource, room, {}, expected_sha)
|
||||||
|
return
|
||||||
|
|
||||||
|
vcard_node = stanza.getTag('vCard', namespace=nbxmpp.NS_VCARD)
|
||||||
|
if vcard_node is None:
|
||||||
|
log.info('vCard not available: %s', frm_jid)
|
||||||
|
log.debug(stanza)
|
||||||
|
return
|
||||||
|
vcard = self._node_to_dict(vcard_node)
|
||||||
|
|
||||||
|
if self._con.get_own_jid().bareMatch(jid):
|
||||||
|
if 'NICKNAME' in vcard:
|
||||||
|
app.nicks[self._account] = vcard['NICKNAME']
|
||||||
|
elif 'FN' in vcard:
|
||||||
|
app.nicks[self._account] = vcard['FN']
|
||||||
|
|
||||||
|
app.nec.push_incoming_event(
|
||||||
|
VcardReceivedEvent(None, conn=self._con, vcard_dict=vcard))
|
||||||
|
|
||||||
|
callback(jid, resource, room, vcard, expected_sha)
|
||||||
|
|
||||||
|
def _on_own_avatar_received(self, jid, resource, room, vcard, *args):
|
||||||
|
avatar_sha, photo_decoded = self._get_vcard_photo(vcard, jid)
|
||||||
|
|
||||||
|
log.info('Received own vcard, avatar sha is: %s', avatar_sha)
|
||||||
|
|
||||||
|
self._own_vcard = vcard
|
||||||
|
self.own_vcard_received = True
|
||||||
|
if avatar_sha is None:
|
||||||
|
log.info('No avatar found')
|
||||||
|
app.config.set_per('accounts', self._account, 'avatar_sha', '')
|
||||||
|
self._con.get_module('VCardAvatars').send_avatar_presence(force=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
current_sha = app.config.get_per('accounts', self._account, 'avatar_sha')
|
||||||
|
if current_sha == avatar_sha:
|
||||||
|
path = os.path.join(configpaths.get('AVATAR'), current_sha)
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
log.info('Caching: %s', current_sha)
|
||||||
|
app.interface.save_avatar(photo_decoded)
|
||||||
|
self._con.get_module('VCardAvatars').send_avatar_presence()
|
||||||
|
else:
|
||||||
|
app.interface.save_avatar(photo_decoded)
|
||||||
|
|
||||||
|
app.config.set_per('accounts', self._account, 'avatar_sha', avatar_sha)
|
||||||
|
if app.is_invisible(self._account):
|
||||||
|
log.info('We are invisible, not advertising avatar')
|
||||||
|
return
|
||||||
|
|
||||||
|
self._con.get_module('VCardAvatars').send_avatar_presence(force=True)
|
||||||
|
|
||||||
|
def _on_room_avatar_received(self, jid, resource, room, vcard,
|
||||||
|
expected_sha):
|
||||||
|
avatar_sha, photo_decoded = self._get_vcard_photo(vcard, jid)
|
||||||
|
if expected_sha != avatar_sha:
|
||||||
|
log.warning('Avatar mismatch: %s %s', jid, avatar_sha)
|
||||||
|
return
|
||||||
|
|
||||||
|
app.interface.save_avatar(photo_decoded)
|
||||||
|
|
||||||
|
log.info('Received: %s %s', jid, avatar_sha)
|
||||||
|
app.contacts.set_avatar(self._account, jid, avatar_sha)
|
||||||
|
app.interface.update_avatar(self._account, jid, room_avatar=True)
|
||||||
|
|
||||||
|
def _on_avatar_received(self, jid, resource, room, vcard, expected_sha):
|
||||||
|
request_jid = jid
|
||||||
|
if room:
|
||||||
|
request_jid = '%s/%s' % (jid, resource)
|
||||||
|
|
||||||
|
avatar_sha, photo_decoded = self._get_vcard_photo(vcard, request_jid)
|
||||||
|
if expected_sha != avatar_sha:
|
||||||
|
log.warning('Received: avatar mismatch: %s %s',
|
||||||
|
request_jid, avatar_sha)
|
||||||
|
return
|
||||||
|
|
||||||
|
app.interface.save_avatar(photo_decoded)
|
||||||
|
|
||||||
|
# Received vCard from a contact
|
||||||
|
if room:
|
||||||
|
log.info('Received: %s %s', resource, avatar_sha)
|
||||||
|
contact = app.contacts.get_gc_contact(self._account, jid, resource)
|
||||||
|
if contact is not None:
|
||||||
|
contact.avatar_sha = avatar_sha
|
||||||
|
app.interface.update_avatar(contact=contact)
|
||||||
|
else:
|
||||||
|
log.info('Received: %s %s', jid, avatar_sha)
|
||||||
|
own_jid = self._con.get_own_jid().getStripped()
|
||||||
|
app.logger.set_avatar_sha(own_jid, jid, avatar_sha)
|
||||||
|
app.contacts.set_avatar(self._account, jid, avatar_sha)
|
||||||
|
app.interface.update_avatar(self._account, jid)
|
||||||
|
|
||||||
|
def get_vard_name(self):
|
||||||
|
name = ''
|
||||||
|
vcard = self._own_vcard
|
||||||
|
if not vcard:
|
||||||
|
return name
|
||||||
|
|
||||||
|
if 'N' in vcard:
|
||||||
|
if 'GIVEN' in vcard['N'] and 'FAMILY' in vcard['N']:
|
||||||
|
name = vcard['N']['GIVEN'] + ' ' + vcard['N']['FAMILY']
|
||||||
|
if not name and 'FN' in vcard:
|
||||||
|
name = vcard['FN']
|
||||||
|
return name
|
||||||
|
|
||||||
|
|
||||||
|
class VcardPublishedEvent(NetworkIncomingEvent):
|
||||||
|
name = 'vcard-published'
|
||||||
|
base_network_events = []
|
||||||
|
|
||||||
|
|
||||||
|
class VcardNotPublishedEvent(NetworkIncomingEvent):
|
||||||
|
name = 'vcard-not-published'
|
||||||
|
base_network_events = []
|
||||||
|
|
||||||
|
|
||||||
|
class VcardReceivedEvent(NetworkIncomingEvent):
|
||||||
|
name = 'vcard-received'
|
||||||
|
base_network_events = []
|
|
@ -40,19 +40,13 @@ AGENT_REMOVED = 'agent_removed'
|
||||||
|
|
||||||
from gajim.common import connection_handlers
|
from gajim.common import connection_handlers
|
||||||
|
|
||||||
class ConnectionVcard(connection_handlers.ConnectionVcard):
|
class ConnectionVcard:
|
||||||
def add_sha(self, p, *args):
|
def add_sha(self, p, *args):
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def add_caps(self, p):
|
def add_caps(self, p):
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def request_vcard(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def send_vcard(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ConnectionHandlersZeroconf(ConnectionVcard,
|
class ConnectionHandlersZeroconf(ConnectionVcard,
|
||||||
ConnectionSocks5BytestreamZeroconf, ConnectionCommands,
|
ConnectionSocks5BytestreamZeroconf, ConnectionCommands,
|
||||||
|
|
|
@ -762,8 +762,8 @@ class GroupchatControl(ChatControlBase):
|
||||||
|
|
||||||
publish = app.interface.get_avatar(sha, publish=True)
|
publish = app.interface.get_avatar(sha, publish=True)
|
||||||
avatar = base64.b64encode(publish).decode('utf-8')
|
avatar = base64.b64encode(publish).decode('utf-8')
|
||||||
|
con = app.connections[self.account]
|
||||||
app.connections[self.account].upload_room_avatar(
|
con.get_module('VCardTemp').upload_room_avatar(
|
||||||
self.room_jid, avatar)
|
self.room_jid, avatar)
|
||||||
|
|
||||||
AvatarChooserDialog(_on_accept,
|
AvatarChooserDialog(_on_accept,
|
||||||
|
|
|
@ -77,7 +77,7 @@ class ProfileWindow:
|
||||||
self._nec_vcard_not_published)
|
self._nec_vcard_not_published)
|
||||||
self.window.show_all()
|
self.window.show_all()
|
||||||
self.xml.get_object('ok_button').grab_focus()
|
self.xml.get_object('ok_button').grab_focus()
|
||||||
app.connections[account].request_vcard(
|
app.connections[account].get_module('VCardTemp').request_vcard(
|
||||||
self._nec_vcard_received, self.jid)
|
self._nec_vcard_received, self.jid)
|
||||||
|
|
||||||
def on_information_notebook_switch_page(self, widget, page, page_num):
|
def on_information_notebook_switch_page(self, widget, page, page_num):
|
||||||
|
@ -261,7 +261,7 @@ class ProfileWindow:
|
||||||
self.progressbar.set_fraction(0)
|
self.progressbar.set_fraction(0)
|
||||||
self.update_progressbar_timeout_id = None
|
self.update_progressbar_timeout_id = None
|
||||||
|
|
||||||
def _nec_vcard_received(self, jid, resource, room, vcard_):
|
def _nec_vcard_received(self, jid, resource, room, vcard_, *args):
|
||||||
self.set_values(vcard_)
|
self.set_values(vcard_)
|
||||||
|
|
||||||
def add_to_vcard(self, vcard_, entry, txt):
|
def add_to_vcard(self, vcard_, entry, txt):
|
||||||
|
@ -339,7 +339,8 @@ class ProfileWindow:
|
||||||
app.connections[self.account].retract_nickname()
|
app.connections[self.account].retract_nickname()
|
||||||
nick = app.config.get_per('accounts', self.account, 'name')
|
nick = app.config.get_per('accounts', self.account, 'name')
|
||||||
app.nicks[self.account] = nick
|
app.nicks[self.account] = nick
|
||||||
app.connections[self.account].send_vcard(vcard_, sha)
|
app.connections[self.account].get_module('VCardTemp').send_vcard(
|
||||||
|
vcard_, sha)
|
||||||
self.message_id = self.statusbar.push(self.context_id,
|
self.message_id = self.statusbar.push(self.context_id,
|
||||||
_('Sending profile…'))
|
_('Sending profile…'))
|
||||||
self.progressbar.show()
|
self.progressbar.show()
|
||||||
|
|
|
@ -840,10 +840,12 @@ class SignalObject(dbus.service.Object):
|
||||||
if avatar_mime_type:
|
if avatar_mime_type:
|
||||||
vcard['PHOTO']['TYPE'] = avatar_mime_type
|
vcard['PHOTO']['TYPE'] = avatar_mime_type
|
||||||
if account:
|
if account:
|
||||||
app.connections[account].send_vcard(vcard, sha)
|
app.connections[account].get_module('VCardTemp').send_vcard(
|
||||||
|
vcard, sha)
|
||||||
else:
|
else:
|
||||||
for acc in app.connections:
|
for acc in app.connections:
|
||||||
app.connections[acc].send_vcard(vcard, sha)
|
app.connections[acc].get_module('VCardTemp').send_vcard(
|
||||||
|
vcard, sha)
|
||||||
|
|
||||||
@dbus.service.method(INTERFACE, in_signature='ssss', out_signature='')
|
@dbus.service.method(INTERFACE, in_signature='ssss', out_signature='')
|
||||||
def join_room(self, room_jid, nick, password, account):
|
def join_room(self, room_jid, nick, password, account):
|
||||||
|
|
|
@ -268,7 +268,7 @@ class VcardWindow:
|
||||||
widget.set_text('')
|
widget.set_text('')
|
||||||
self.xml.get_object('DESC_textview').get_buffer().set_text('')
|
self.xml.get_object('DESC_textview').get_buffer().set_text('')
|
||||||
|
|
||||||
def _nec_vcard_received(self, jid, resource, room, vcard):
|
def _nec_vcard_received(self, jid, resource, room, vcard, *args):
|
||||||
self.clear_values()
|
self.clear_values()
|
||||||
self._set_values(vcard, jid)
|
self._set_values(vcard, jid)
|
||||||
|
|
||||||
|
@ -477,10 +477,13 @@ class VcardWindow:
|
||||||
self.fill_status_label()
|
self.fill_status_label()
|
||||||
|
|
||||||
if self.gc_contact:
|
if self.gc_contact:
|
||||||
con.request_vcard(self._nec_vcard_received,
|
con.get_module('VCardTemp').request_vcard(
|
||||||
self.gc_contact.get_full_jid(), room=True)
|
self._nec_vcard_received,
|
||||||
|
self.gc_contact.get_full_jid(),
|
||||||
|
room=True)
|
||||||
else:
|
else:
|
||||||
con.request_vcard(self._nec_vcard_received, self.contact.jid)
|
con.get_module('VCardTemp').request_vcard(
|
||||||
|
self._nec_vcard_received, self.contact.jid)
|
||||||
|
|
||||||
def on_close_button_clicked(self, widget):
|
def on_close_button_clicked(self, widget):
|
||||||
self.window.destroy()
|
self.window.destroy()
|
||||||
|
|
|
@ -47,9 +47,6 @@ class MockConnection(Mock, ConnectionHandlers):
|
||||||
|
|
||||||
app.connections[account] = self
|
app.connections[account] = self
|
||||||
|
|
||||||
def request_vcard(self, *args):
|
|
||||||
pass
|
|
||||||
|
|
||||||
class MockWindow(Mock):
|
class MockWindow(Mock):
|
||||||
def __init__(self, *args):
|
def __init__(self, *args):
|
||||||
Mock.__init__(self, *args)
|
Mock.__init__(self, *args)
|
||||||
|
|
Loading…
Reference in New Issue