Refactor Pubsub/Bookmarks/UserAvatar into own modules

This commit is contained in:
Philipp Hörist 2018-07-01 02:16:33 +02:00
parent 858e472ef4
commit 2ca0ca38a5
17 changed files with 746 additions and 733 deletions

View File

@ -73,6 +73,9 @@ from gajim.common.modules.last_activity import LastActivity
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.modules.pubsub import PubSub
from gajim.common.modules.bookmarks import Bookmarks
from gajim.common.modules.user_avatar import UserAvatar
from gajim.common.connection_handlers import *
from gajim.common.contacts import GC_Contact
from gajim.gtkgui_helpers import get_action
@ -113,7 +116,6 @@ class CommonConnection:
self.old_show = ''
self.priority = app.get_priority(name, 'offline')
self.time_to_reconnect = None
self.bookmarks = []
self.blocked_list = []
self.blocked_contacts = []
@ -491,18 +493,6 @@ class CommonConnection:
def account_changed(self, new_name):
self.name = new_name
def get_bookmarks(self):
"""
To be implemented by derived classes
"""
raise NotImplementedError
def store_bookmarks(self):
"""
To be implemented by derived classes
"""
raise NotImplementedError
def get_metacontacts(self):
"""
To be implemented by derived classes
@ -670,6 +660,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.register_module('HTTPAuth', HTTPAuth, self)
self.register_module('VCardTemp', VCardTemp, self)
self.register_module('VCardAvatars', VCardAvatars, self)
self.register_module('PubSub', PubSub, self)
self.register_module('Bookmarks', Bookmarks, self)
self.register_module('UserAvatar', UserAvatar, self)
app.ged.register_event_handler('privacy-list-received', ged.CORE,
self._nec_privacy_list_received)
@ -1774,8 +1767,8 @@ class Connection(CommonConnection, ConnectionHandlers):
# ask our VCard
self.get_module('VCardTemp').request_vcard()
# Get bookmarks from private namespace
self.get_bookmarks()
# Get bookmarks
self.get_module('Bookmarks').get_bookmarks()
# Get annotations
self.get_module('Annotations').get_annotations()
@ -1916,8 +1909,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.pubsub_publish_options_supported = True
else:
# Remove stored bookmarks accessible to everyone.
self.send_pb_purge(our_jid, 'storage:bookmarks')
self.send_pb_delete(our_jid, 'storage:bookmarks')
self.get_module('Bookmarks').purge_pubsub_bookmarks()
if obj.fjid == hostname:
if nbxmpp.NS_SECLABEL in obj.features:
@ -2255,99 +2247,6 @@ class Connection(CommonConnection, ConnectionHandlers):
return True
return False
def _request_bookmarks_xml(self):
if not app.account_is_connected(self.name):
return
iq = nbxmpp.Iq(typ='get')
iq2 = iq.addChild(name='query', namespace=nbxmpp.NS_PRIVATE)
iq2.addChild(name='storage', namespace='storage:bookmarks')
self.connection.send(iq)
app.log('bookmarks').info('Request Bookmarks (PrivateStorage)')
def _check_bookmarks_received(self):
if not self.bookmarks:
self._request_bookmarks_xml()
def get_bookmarks(self, storage_type=None):
"""
Get Bookmarks from storage or PubSub if supported as described in XEP
0048
storage_type can be set to xml to force request to xml storage
"""
if not app.account_is_connected(self.name):
return
if storage_type != 'xml':
if self.pep_supported and self.pubsub_publish_options_supported:
self.send_pb_retrieve('', 'storage:bookmarks')
app.log('bookmarks').info('Request Bookmarks (PubSub)')
# some server (ejabberd) are so slow to answer that we
# request via XML if we don't get answer in the next 30 seconds
app.idlequeue.set_alarm(self._check_bookmarks_received, 30)
return
self._request_bookmarks_xml()
def get_bookmarks_storage_node(self):
NS_GAJIM_BM = 'xmpp:gajim.org/bookmarks'
storage_node = nbxmpp.Node(
tag='storage', attrs={'xmlns': 'storage:bookmarks'})
for bm in self.bookmarks:
conf_node = storage_node.addChild(name="conference")
conf_node.setAttr('jid', bm['jid'])
conf_node.setAttr('autojoin', bm['autojoin'])
conf_node.setAttr('name', bm['name'])
conf_node.setTag(
'minimize', namespace=NS_GAJIM_BM).setData(bm['minimize'])
# Only add optional elements if not empty
# Note: need to handle both None and '' as empty
# thus shouldn't use "is not None"
if bm.get('nick', None):
conf_node.setTagData('nick', bm['nick'])
if bm.get('password', None):
conf_node.setTagData('password', bm['password'])
if bm.get('print_status', None):
conf_node.setTag(
'print_status',
namespace=NS_GAJIM_BM).setData(bm['print_status'])
return storage_node
@staticmethod
def get_bookmark_publish_options():
options = nbxmpp.Node(nbxmpp.NS_DATA + ' x',
attrs={'type': 'submit'})
f = options.addChild('field',
attrs={'var': 'FORM_TYPE', 'type': 'hidden'})
f.setTagData('value', nbxmpp.NS_PUBSUB_PUBLISH_OPTIONS)
f = options.addChild('field', attrs={'var': 'pubsub#access_model'})
f.setTagData('value', 'whitelist')
return options
def store_bookmarks(self, storage_type=None):
"""
Send bookmarks to the storage namespace or PubSub if supported
storage_type can be set to 'pubsub' or 'xml' so store in only one method
else it will be stored on both
"""
if not app.account_is_connected(self.name):
return
storage_node = self.get_bookmarks_storage_node()
if storage_type != 'xml':
if self.pep_supported and self.pubsub_publish_options_supported:
self.send_pb_publish(
'', 'storage:bookmarks', storage_node, 'current',
options=self.get_bookmark_publish_options())
app.log('bookmarks').info('Bookmarks published (PubSub)')
if storage_type != 'pubsub':
iq = nbxmpp.Iq('set', nbxmpp.NS_PRIVATE, payload=storage_node)
self.connection.send(iq)
app.log('bookmarks').info('Bookmarks published (PrivateStorage)')
def get_roster_delimiter(self):
"""
Get roster group delimiter from storage as described in XEP 0083
@ -2614,11 +2513,7 @@ class Connection(CommonConnection, ConnectionHandlers):
destroy.setAttr('jid', jid)
self.connection.send(iq)
i = 0
for bm in self.bookmarks:
if bm['jid'] == room_jid:
del self.bookmarks[i]
break
i += 1
self.get_module('Bookmarks').bookmarks.pop(jid, None)
self.store_bookmarks()
def send_gc_status(self, nick, jid, show, status, auto=False):

View File

@ -50,7 +50,6 @@ from gajim.common import jingle_xtls
from gajim.common import configpaths
from gajim.common.caps_cache import muc_caps_cache
from gajim.common.commands import ConnectionCommands
from gajim.common.pubsub import ConnectionPubSub
from gajim.common.protocol.caps import ConnectionCaps
from gajim.common.protocol.bytestream import ConnectionSocks5Bytestream
from gajim.common.protocol.bytestream import ConnectionIBBytestream
@ -339,15 +338,15 @@ class ConnectionPEP(object):
if message:
i = item.addChild('text')
i.addData(message)
self._pubsub_connection.send_pb_publish('', nbxmpp.NS_ACTIVITY, item,
'0')
self.get_module('PubSub').send_pb_publish(
'', nbxmpp.NS_ACTIVITY, item, '0')
def retract_activity(self):
if not self.pep_supported:
return
self.send_activity(None)
# not all client support new XEP, so we still retract
self._pubsub_connection.send_pb_retract('', nbxmpp.NS_ACTIVITY, '0')
self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_ACTIVITY, '0')
def send_mood(self, mood, message=None):
if self.connected == 1:
@ -363,14 +362,14 @@ class ConnectionPEP(object):
if message:
i = item.addChild('text')
i.addData(message)
self._pubsub_connection.send_pb_publish('', nbxmpp.NS_MOOD, item, '0')
self.get_module('PubSub').send_pb_publish('', nbxmpp.NS_MOOD, item, '0')
def retract_mood(self):
if not self.pep_supported:
return
self.send_mood(None)
# not all client support new XEP, so we still retract
self._pubsub_connection.send_pb_retract('', nbxmpp.NS_MOOD, '0')
self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_MOOD, '0')
def send_tune(self, artist='', title='', source='', track=0, length=0,
items=None):
@ -399,14 +398,14 @@ class ConnectionPEP(object):
i.addData(length)
if items:
item.addChild(payload=items)
self._pubsub_connection.send_pb_publish('', nbxmpp.NS_TUNE, item, '0')
self.get_module('PubSub').send_pb_publish('', nbxmpp.NS_TUNE, item, '0')
def retract_tune(self):
if not self.pep_supported:
return
self.send_tune(None)
# not all client support new XEP, so we still retract
self._pubsub_connection.send_pb_retract('', nbxmpp.NS_TUNE, '0')
self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_TUNE, '0')
def send_nickname(self, nick):
if self.connected == 1:
@ -418,13 +417,13 @@ class ConnectionPEP(object):
return
item = nbxmpp.Node('nick', {'xmlns': nbxmpp.NS_NICK})
item.addData(nick)
self._pubsub_connection.send_pb_publish('', nbxmpp.NS_NICK, item, '0')
self.get_module('PubSub').send_pb_publish('', nbxmpp.NS_NICK, item, '0')
def retract_nickname(self):
if not self.pep_supported:
return
self._pubsub_connection.send_pb_retract('', nbxmpp.NS_NICK, '0')
self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_NICK, '0')
def send_location(self, info):
if self.connected == 1:
@ -439,14 +438,14 @@ class ConnectionPEP(object):
if info.get(field, None):
i = item.addChild(field)
i.addData(info[field])
self._pubsub_connection.send_pb_publish('', nbxmpp.NS_LOCATION, item, '0')
self.get_module('PubSub').send_pb_publish('', nbxmpp.NS_LOCATION, item, '0')
def retract_location(self):
if not self.pep_supported:
return
self.send_location({})
# not all client support new XEP, so we still retract
self._pubsub_connection.send_pb_retract('', nbxmpp.NS_LOCATION, '0')
self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_LOCATION, '0')
# basic connection handlers used here and in zeroconf
class ConnectionHandlersBase:
@ -915,7 +914,7 @@ class ConnectionHandlersBase:
class ConnectionHandlers(ConnectionArchive313,
ConnectionSocks5Bytestream, ConnectionDisco,
ConnectionCommands, ConnectionPubSub, ConnectionPEP, ConnectionCaps,
ConnectionCommands, ConnectionPEP, ConnectionCaps,
ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream,
ConnectionHTTPUpload):
def __init__(self):
@ -923,7 +922,6 @@ ConnectionHTTPUpload):
ConnectionSocks5Bytestream.__init__(self)
ConnectionIBBytestream.__init__(self)
ConnectionCommands.__init__(self)
ConnectionPubSub.__init__(self)
ConnectionPEP.__init__(self, account=self.name, dispatcher=self,
pubsub_connection=self)
ConnectionHTTPUpload.__init__(self)
@ -949,8 +947,6 @@ ConnectionHTTPUpload):
self.privacy_default_list = None
app.nec.register_incoming_event(PrivateStorageBookmarksReceivedEvent)
app.nec.register_incoming_event(BookmarksReceivedEvent)
app.nec.register_incoming_event(StreamConflictReceivedEvent)
app.nec.register_incoming_event(StreamOtherHostReceivedEvent)
app.nec.register_incoming_event(MessageReceivedEvent)
@ -961,8 +957,6 @@ ConnectionHTTPUpload):
app.ged.register_event_handler('roster-set-received',
ged.CORE, self._nec_roster_set_received)
app.ged.register_event_handler('private-storage-bookmarks-received',
ged.CORE, self._nec_private_storate_bookmarks_received)
app.ged.register_event_handler('roster-received', ged.CORE,
self._nec_roster_received)
app.ged.register_event_handler('iq-error-received', ged.CORE,
@ -988,12 +982,9 @@ ConnectionHTTPUpload):
ConnectionHandlersBase.cleanup(self)
ConnectionCaps.cleanup(self)
ConnectionArchive313.cleanup(self)
ConnectionPubSub.cleanup(self)
ConnectionHTTPUpload.cleanup(self)
app.ged.remove_event_handler('roster-set-received',
ged.CORE, self._nec_roster_set_received)
app.ged.remove_event_handler('private-storage-bookmarks-received',
ged.CORE, self._nec_private_storate_bookmarks_received)
app.ged.remove_event_handler('roster-received', ged.CORE,
self._nec_roster_received)
app.ged.remove_event_handler('iq-error-received', ged.CORE,
@ -1144,28 +1135,6 @@ ConnectionHTTPUpload):
conn=self, stanza=obj.stanza))
return True
def _nec_private_storate_bookmarks_received(self, obj):
if obj.conn.name != self.name:
return
app.log('bookmarks').info('Received Bookmarks (PrivateStorage)')
resend_to_pubsub = False
bm_jids = [b['jid'] for b in self.bookmarks]
for bm in obj.bookmarks:
if bm['jid'] not in bm_jids:
self.bookmarks.append(bm)
# We got a bookmark that was not in pubsub
resend_to_pubsub = True
if resend_to_pubsub:
self.store_bookmarks('pubsub')
def _PrivateCB(self, con, iq_obj):
"""
Private Data (XEP 048 and 049)
"""
log.debug('PrivateCB')
app.nec.push_incoming_event(PrivateStorageReceivedEvent(None,
conn=self, stanza=iq_obj))
def _SecLabelCB(self, con, iq_obj):
"""
Security Label callback, used for catalogues.
@ -1540,8 +1509,8 @@ ConnectionHTTPUpload):
# ask our VCard
self.get_module('VCardTemp').request_vcard()
# Get bookmarks from private namespace
self.get_bookmarks()
# Get bookmarks
self.get_module('Bookmarks').get_bookmarks()
# Get annotations from private namespace
self.get_module('Annotations').get_annotations()
@ -1645,7 +1614,6 @@ ConnectionHTTPUpload):
nbxmpp.NS_MUC_OWNER)
con.RegisterHandler('iq', self._MucAdminCB, 'result',
nbxmpp.NS_MUC_ADMIN)
con.RegisterHandler('iq', self._PrivateCB, 'result', nbxmpp.NS_PRIVATE)
con.RegisterHandler('iq', self._SecLabelCB, 'result',
nbxmpp.NS_SECLABEL_CATALOG)
con.RegisterHandler('iq', self._CommandExecuteCB, 'set',
@ -1657,8 +1625,6 @@ ConnectionHTTPUpload):
con.RegisterHandler('iq', self._PrivacySetCB, 'set', nbxmpp.NS_PRIVACY)
con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM_1)
con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM_2)
con.RegisterHandler('iq', self._PubSubCB, 'result')
con.RegisterHandler('iq', self._PubSubErrorCB, 'error')
con.RegisterHandler('iq', self._JingleCB, 'result')
con.RegisterHandler('iq', self._JingleCB, 'error')
con.RegisterHandler('iq', self._JingleCB, 'set', nbxmpp.NS_JINGLE)

View File

@ -309,118 +309,6 @@ class MucAdminReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
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 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 IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'iq-error-received'
base_network_events = []

View File

@ -119,6 +119,11 @@ class RequestAvatar(IntEnum):
ROOM = 1
USER = 2
@unique
class BookmarkStorageType(IntEnum):
PRIVATE = 0
PUBSUB = 1
SSLError = {
2: _("Unable to get issuer certificate"),
3: _("Unable to get certificate CRL"),

View File

@ -0,0 +1,294 @@
# 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-0048: Bookmarks
import logging
import nbxmpp
from gajim.common import app
from gajim.common import helpers
from gajim.common.const import BookmarkStorageType
from gajim.common.nec import NetworkIncomingEvent
log = logging.getLogger('gajim.c.m.bookmarks')
class Bookmarks:
def __init__(self, con):
self._con = con
self._account = con.name
self.bookmarks = {}
self.handlers = []
def _pubsub_support(self):
return (self._con.pep_supported and
self._con.pubsub_publish_options_supported)
def get_bookmarks(self, storage_type=None):
if not app.account_is_connected(self._account):
return
if storage_type in (None, BookmarkStorageType.PUBSUB):
if self._pubsub_support():
self._request_pubsub_bookmarks()
else:
# Fallback, request private storage
self._request_private_bookmarks()
else:
log.info('Request Bookmarks (PrivateStorage)')
self._request_private_bookmarks()
def _request_pubsub_bookmarks(self):
log.info('Request Bookmarks (PubSub)')
self._con.get_module('PubSub').send_pb_retrieve(
'', 'storage:bookmarks',
cb=self._pubsub_bookmarks_received)
def _pubsub_bookmarks_received(self, conn, stanza):
if not nbxmpp.isResultNode(stanza):
log.info('No pubsub bookmarks: %s', stanza.getError())
# Fallback, request private storage
self._request_private_bookmarks()
return
log.info('Received Bookmarks (PubSub)')
self._parse_bookmarks(stanza)
self._request_private_bookmarks()
def _request_private_bookmarks(self):
if not app.account_is_connected(self._account):
return
iq = nbxmpp.Iq(typ='get')
query = iq.addChild(name='query', namespace=nbxmpp.NS_PRIVATE)
query.addChild(name='storage', namespace='storage:bookmarks')
log.info('Request Bookmarks (PrivateStorage)')
self._con.connection.SendAndCallForResponse(
iq, self._private_bookmarks_received)
def _private_bookmarks_received(self, stanza):
if not nbxmpp.isResultNode(stanza):
log.info('No private bookmarks: %s', stanza.getError())
else:
log.info('Received Bookmarks (PrivateStorage)')
merged = self._parse_bookmarks(stanza, check_merge=True)
if merged:
log.info('Merge PrivateStorage with PubSub')
self.store_bookmarks(BookmarkStorageType.PUBSUB)
self.auto_join_bookmarks()
app.nec.push_incoming_event(BookmarksReceivedEvent(
None, account=self._account))
@staticmethod
def _get_storage_node(stanza):
node = stanza.getTag('pubsub', namespace=nbxmpp.NS_PUBSUB)
if node is None:
node = stanza.getTag('event', namespace=nbxmpp.NS_PUBSUB_EVENT)
if node is None:
# Private Storage
query = stanza.getQuery()
if query is None:
return
storage = query.getTag('storage',
namespace=nbxmpp.NS_BOOKMARKS)
if storage is None:
return
return storage
items_node = node.getTag('items')
if items_node is None:
return
if items_node.getAttr('node') != nbxmpp.NS_BOOKMARKS:
return
item_node = items_node.getTag('item')
if item_node is None:
return
storage = item_node.getTag('storage', namespace=nbxmpp.NS_BOOKMARKS)
if storage is None:
return
return storage
def _parse_bookmarks(self, stanza, check_merge=False):
merged = False
storage = self._get_storage_node(stanza)
if storage is None:
return
NS_GAJIM_BM = 'xmpp:gajim.org/bookmarks'
confs = storage.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
if check_merge:
if jid not in self.bookmarks:
merged = True
log.debug('Found Bookmark: %s', jid)
self.bookmarks[jid] = {
'name': conf.getAttr('name'),
'autojoin': autojoin_val,
'minimize': minimize_val,
'password': conf.getTagData('password'),
'nick': conf.getTagData('nick'),
'print_status': print_status}
return merged
def _build_storage_node(self):
NS_GAJIM_BM = 'xmpp:gajim.org/bookmarks'
storage_node = nbxmpp.Node(
tag='storage', attrs={'xmlns': 'storage:bookmarks'})
for jid, bm in self.bookmarks.items():
conf_node = storage_node.addChild(name="conference")
conf_node.setAttr('jid', jid)
conf_node.setAttr('autojoin', bm['autojoin'])
conf_node.setAttr('name', bm['name'])
conf_node.setTag(
'minimize', namespace=NS_GAJIM_BM).setData(bm['minimize'])
# Only add optional elements if not empty
# Note: need to handle both None and '' as empty
# thus shouldn't use "is not None"
if bm.get('nick', None):
conf_node.setTagData('nick', bm['nick'])
if bm.get('password', None):
conf_node.setTagData('password', bm['password'])
if bm.get('print_status', None):
conf_node.setTag(
'print_status',
namespace=NS_GAJIM_BM).setData(bm['print_status'])
return storage_node
@staticmethod
def get_bookmark_publish_options():
options = nbxmpp.Node(nbxmpp.NS_DATA + ' x',
attrs={'type': 'submit'})
f = options.addChild('field',
attrs={'var': 'FORM_TYPE', 'type': 'hidden'})
f.setTagData('value', nbxmpp.NS_PUBSUB_PUBLISH_OPTIONS)
f = options.addChild('field', attrs={'var': 'pubsub#access_model'})
f.setTagData('value', 'whitelist')
return options
def store_bookmarks(self, storage_type=None):
if not app.account_is_connected(self._account):
return
storage_node = self._build_storage_node()
if storage_type is None:
if self._pubsub_support():
self._pubsub_store(storage_node)
else:
self._private_store(storage_node)
elif storage_type == BookmarkStorageType.PUBSUB:
if self._pubsub_support():
self._pubsub_store(storage_node)
elif storage_type == BookmarkStorageType.PRIVATE:
self._private_store(storage_node)
def _pubsub_store(self, storage_node):
self._con.get_module('PubSub').send_pb_publish(
'', 'storage:bookmarks', storage_node, 'current',
options=self.get_bookmark_publish_options(),
cb=self._pubsub_store_result)
log.info('Publish Bookmarks (PubSub)')
def _private_store(self, storage_node):
iq = nbxmpp.Iq('set', nbxmpp.NS_PRIVATE, payload=storage_node)
log.info('Publish Bookmarks (PrivateStorage)')
self._con.connection.SendAndCallForResponse(
iq, self._private_store_result)
def _pubsub_store_result(self, conn, stanza):
if not nbxmpp.isResultNode(stanza):
log.error('Error: %s', stanza.getError())
return
def _private_store_result(self, stanza):
if not nbxmpp.isResultNode(stanza):
log.error('Error: %s', stanza.getError())
return
def auto_join_bookmarks(self):
if app.is_invisible(self._account):
return
for jid, bm in self.bookmarks.items():
if bm['autojoin'] in ('1', 'true'):
# Only join non-opened groupchats. Opened one are already
# auto-joined on re-connection
if jid not in app.gc_connected[self._account]:
# we are not already connected
minimize = bm['minimize'] in ('1', 'true')
app.interface.join_gc_room(
self._account, jid, bm['nick'],
bm['password'], minimize=minimize)
def add_bookmark(self, name, jid, autojoin,
minimize, password, nick):
self.bookmarks[jid] = {
'name': name,
'autojoin': autojoin,
'minimize': minimize,
'password': password,
'nick': nick}
self.store_bookmarks()
app.nec.push_incoming_event(BookmarksReceivedEvent(
None, account=self._account))
def get_name_from_bookmark(self, jid):
try:
return self.bookmarks[jid]['name']
except KeyError:
return jid.split('@')[0]
def purge_pubsub_bookmarks(self):
log.info('Purge/Delete Bookmarks on PubSub, '
'because publish options are not available')
self._con.get_module('PubSub').send_pb_purge('', 'storage:bookmarks')
self._con.get_module('PubSub').send_pb_delete('', 'storage:bookmarks')
class BookmarksReceivedEvent(NetworkIncomingEvent):
name = 'bookmarks-received'
base_network_events = []

View File

@ -0,0 +1,217 @@
# Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
# Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
# Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
# Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# 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-0060: Publish-Subscribe
import logging
import nbxmpp
from gajim.common import app
log = logging.getLogger('gajim.c.m.pubsub')
class PubSub:
def __init__(self, con):
self._con = con
self._account = con.name
self.handlers = []
def send_pb_subscription_query(self, jid, cb, **kwargs):
if not app.account_is_connected(self._account):
return
query = nbxmpp.Iq('get', to=jid)
pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
pb.addChild('subscriptions')
self._con.connection.SendAndCallForResponse(query, cb, kwargs)
def send_pb_subscribe(self, jid, node, cb, **kwargs):
if not app.account_is_connected(self._account):
return
our_jid = app.get_jid_from_account(self._account)
query = nbxmpp.Iq('set', to=jid)
pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
pb.addChild('subscribe', {'node': node, 'jid': our_jid})
self._con.connection.SendAndCallForResponse(query, cb, kwargs)
def send_pb_unsubscribe(self, jid, node, cb, **kwargs):
if not app.account_is_connected(self._account):
return
our_jid = app.get_jid_from_account(self._account)
query = nbxmpp.Iq('set', to=jid)
pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
pb.addChild('unsubscribe', {'node': node, 'jid': our_jid})
self._con.connection.SendAndCallForResponse(query, cb, kwargs)
def send_pb_publish(self, jid, node, item,
id_=None, options=None, cb=None, **kwargs):
if not app.account_is_connected(self._account):
return
if cb is None:
cb = self._default_callback
query = nbxmpp.Iq('set', to=jid)
e = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
p = e.addChild('publish', {'node': node})
attrs = {}
if id_:
attrs = {'id': id_}
p.addChild('item', attrs, [item])
if options:
p = e.addChild('publish-options')
p.addChild(node=options)
self._con.connection.SendAndCallForResponse(query, cb, kwargs)
@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, **kwargs):
"""
Get items from a node
"""
if not app.account_is_connected(self._account):
return
if cb is None:
cb = self._default_callback
query = self.get_pb_retrieve_iq(jid, node, item_id)
self._con.connection.SendAndCallForResponse(query, cb, kwargs)
def send_pb_retract(self, jid, node, id_, cb=None, **kwargs):
"""
Delete item from a node
"""
if not app.account_is_connected(self._account):
return
if cb is None:
cb = self._default_callback
query = nbxmpp.Iq('set', to=jid)
r = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
r = r.addChild('retract', {'node': node, 'notify': '1'})
r = r.addChild('item', {'id': id_})
self._con.connection.SendAndCallForResponse(query, cb, kwargs)
def send_pb_purge(self, jid, node, cb=None, **kwargs):
"""
Purge node: Remove all items
"""
if not app.account_is_connected(self._account):
return
if cb is None:
cb = self._default_callback
query = nbxmpp.Iq('set', to=jid)
d = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
d = d.addChild('purge', {'node': node})
self._con.connection.SendAndCallForResponse(query, cb, kwargs)
def send_pb_delete(self, jid, node, on_ok=None, on_fail=None):
"""
Delete node
"""
if not app.account_is_connected(self._account):
return
query = nbxmpp.Iq('set', to=jid)
d = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
d = d.addChild('delete', {'node': node})
def response(con, resp, jid, node):
if resp.getType() == 'result' and on_ok:
on_ok(jid, node)
elif on_fail:
msg = resp.getErrorMsg()
on_fail(jid, node, msg)
self._con.connection.SendAndCallForResponse(
query, response, {'jid': jid, 'node': node})
def send_pb_create(self, jid, node, cb,
configure=False, configure_form=None):
"""
Create a new node
"""
if not app.account_is_connected(self._account):
return
query = nbxmpp.Iq('set', to=jid)
c = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
c = c.addChild('create', {'node': node})
if configure:
conf = c.addChild('configure')
if configure_form is not None:
conf.addChild(node=configure_form)
self._con.connection.SendAndCallForResponse(query, cb)
def send_pb_configure(self, jid, node, form, cb=None):
if not app.account_is_connected(self._account):
return
if cb is None:
cb = self._default_callback
query = nbxmpp.Iq('set', to=jid)
c = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
c = c.addChild('configure', {'node': node})
c.addChild(node=form)
self._con.connection.SendAndCallForResponse(query, cb)
def request_pb_configuration(self, jid, node, cb=None):
if not app.account_is_connected(self._account):
return
if cb is None:
cb = self._default_callback
query = nbxmpp.Iq('get', to=jid)
e = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
e = e.addChild('configure', {'node': node})
self._con.connection.SendAndCallForResponse(query, cb)
def _default_callback(self, conn, stanza, *args, **kwargs):
if not nbxmpp.isResultNode(stanza):
log.warning('Error: %s', stanza.getError())

View File

@ -0,0 +1,96 @@
# 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-0084: User Avatar
import logging
import base64
import binascii
import nbxmpp
from gajim.common import app
from gajim.common.exceptions import StanzaMalformed
log = logging.getLogger('gajim.c.m.user_avatar')
class UserAvatar:
def __init__(self, con):
self._con = con
self._account = con.name
self.handlers = []
def get_pubsub_avatar(self, jid, item_id):
log.info('Request: %s %s', jid, item_id)
self._con.get_module('PubSub').send_pb_retrieve(
jid, 'urn:xmpp:avatar:data', item_id, self._avatar_received)
def _validate_avatar_node(self, stanza):
jid = stanza.getFrom()
if jid is None:
jid = self._con.get_own_jid().getStripped()
else:
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 _avatar_received(self, conn, stanza):
try:
jid, sha, data = self._validate_avatar_node(stanza)
except (StanzaMalformed, binascii.Error) as error:
log.warning('Error: %s %s', stanza.getFrom(), error)
return
log.info('Received: %s %s', jid, sha)
app.interface.save_avatar(data)
if self._con.get_own_jid().bareMatch(jid):
app.config.set_per('accounts', self._account, 'avatar_sha', sha)
else:
own_jid = self._con.get_own_jid().getStripped()
app.logger.set_avatar_sha(own_jid, jid, sha)
app.contacts.set_avatar(self._account, jid, sha)
app.interface.update_avatar(self._account, jid)

View File

@ -485,7 +485,8 @@ class AvatarNotificationPEP(AbstractPEP):
for item in items.getTags('item'):
metadata = item.getTag('metadata')
if metadata is None:
app.log('avatar').warning('Invalid avatar stanza:\n%s', items)
app.log('c.m.user_avatar').warning(
'Invalid avatar stanza:\n%s', items)
break
info = item.getTag('metadata').getTag('info')
if info is not None:
@ -498,23 +499,22 @@ class AvatarNotificationPEP(AbstractPEP):
con = app.connections[account]
if self.avatar is None:
# Remove avatar
app.log('avatar').info('Remove (Pubsub): %s', jid)
app.log('c.m.user_avatar').info('Remove: %s', jid)
app.contacts.set_avatar(account, jid, None)
own_jid = con.get_own_jid().getStripped()
app.logger.set_avatar_sha(own_jid, jid, None)
app.interface.update_avatar(account, jid)
else:
sha = app.contacts.get_avatar_sha(account, jid)
app.log('avatar').info(
'Update (Pubsub): %s %s', jid, self.avatar['id'])
app.log('c.m.user_avatar').info(
'Update: %s %s', jid, self.avatar['id'])
if sha == self.avatar['id']:
app.log('avatar').info(
'Avatar already known (Pubsub): %s %s',
app.log('c.m.user_avatar').info(
'Avatar already known: %s %s',
jid, self.avatar['id'])
return
app.log('avatar').info('Request (Pubsub): %s', jid)
con.get_pubsub_avatar(jid, 'urn:xmpp:avatar:data',
self.avatar['id'])
con.get_module('UserAvatar').get_pubsub_avatar(
jid, self.avatar['id'])
SUPPORTED_PERSONAL_USER_EVENTS = [

View File

@ -1,306 +0,0 @@
# -*- coding:utf-8 -*-
## src/common/pubsub.py
##
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
## Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
##
## 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/>.
##
import base64
import binascii
import nbxmpp
from gajim.common import app
#TODO: Doesn't work
#from common.connection_handlers import PEP_CONFIG
PEP_CONFIG = 'pep_config'
from gajim.common import ged
from gajim.common.connection_handlers_events import PubsubReceivedEvent
from gajim.common.connection_handlers_events import PubsubBookmarksReceivedEvent
from gajim.common.exceptions import StanzaMalformed
import logging
log = logging.getLogger('gajim.c.pubsub')
class ConnectionPubSub:
def __init__(self):
self.__callbacks = {}
app.nec.register_incoming_event(PubsubBookmarksReceivedEvent)
app.ged.register_event_handler('pubsub-bookmarks-received',
ged.CORE, self._nec_pubsub_bookmarks_received)
def cleanup(self):
app.ged.remove_event_handler('pubsub-bookmarks-received',
ged.CORE, self._nec_pubsub_bookmarks_received)
def send_pb_subscription_query(self, jid, cb, *args, **kwargs):
if not self.connection or self.connected < 2:
return
query = nbxmpp.Iq('get', to=jid)
pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
pb.addChild('subscriptions')
id_ = self.connection.send(query)
self.__callbacks[id_] = (cb, args, kwargs)
def send_pb_subscribe(self, jid, node, cb, *args, **kwargs):
if not self.connection or self.connected < 2:
return
our_jid = app.get_jid_from_account(self.name)
query = nbxmpp.Iq('set', to=jid)
pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
pb.addChild('subscribe', {'node': node, 'jid': our_jid})
id_ = self.connection.send(query)
self.__callbacks[id_] = (cb, args, kwargs)
def send_pb_unsubscribe(self, jid, node, cb, *args, **kwargs):
if not self.connection or self.connected < 2:
return
our_jid = app.get_jid_from_account(self.name)
query = nbxmpp.Iq('set', to=jid)
pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
pb.addChild('unsubscribe', {'node': node, 'jid': our_jid})
id_ = self.connection.send(query)
self.__callbacks[id_] = (cb, args, kwargs)
def send_pb_publish(self, jid, node, item, id_=None, options=None):
"""
Publish item to a node
"""
if not self.connection or self.connected < 2:
return
query = nbxmpp.Iq('set', to=jid)
e = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
p = e.addChild('publish', {'node': node})
attrs = {}
if id_:
attrs = {'id': id_}
p.addChild('item', attrs, [item])
if options:
p = e.addChild('publish-options')
p.addChild(node=options)
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):
"""
Get items from a node
"""
if not self.connection or self.connected < 2:
return
query = self.get_pb_retrieve_iq(jid, node, item_id)
id_ = self.connection.send(query)
if cb:
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_):
"""
Delete item from a node
"""
if not self.connection or self.connected < 2:
return
query = nbxmpp.Iq('set', to=jid)
r = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
r = r.addChild('retract', {'node': node, 'notify': '1'})
r = r.addChild('item', {'id': id_})
self.connection.send(query)
def send_pb_purge(self, jid, node):
"""
Purge node: Remove all items
"""
if not self.connection or self.connected < 2:
return
query = nbxmpp.Iq('set', to=jid)
d = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
d = d.addChild('purge', {'node': node})
self.connection.send(query)
def send_pb_delete(self, jid, node, on_ok=None, on_fail=None):
"""
Delete node
"""
if not self.connection or self.connected < 2:
return
query = nbxmpp.Iq('set', to=jid)
d = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
d = d.addChild('delete', {'node': node})
def response(con, resp, jid, node):
if resp.getType() == 'result' and on_ok:
on_ok(jid, node)
elif on_fail:
msg = resp.getErrorMsg()
on_fail(jid, node, msg)
self.connection.SendAndCallForResponse(query, response, {'jid': jid,
'node': node})
def send_pb_create(self, jid, node, configure=False, configure_form=None):
"""
Create a new node
"""
if not self.connection or self.connected < 2:
return
query = nbxmpp.Iq('set', to=jid)
c = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
c = c.addChild('create', {'node': node})
if configure:
conf = c.addChild('configure')
if configure_form is not None:
conf.addChild(node=configure_form)
self.connection.send(query)
def send_pb_configure(self, jid, node, form):
if not self.connection or self.connected < 2:
return
query = nbxmpp.Iq('set', to=jid)
c = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
c = c.addChild('configure', {'node': node})
c.addChild(node=form)
self.connection.send(query)
def _PubSubCB(self, conn, stanza):
log.debug('_PubsubCB')
try:
cb, args, kwargs = self.__callbacks.pop(stanza.getID())
cb(conn, stanza, *args, **kwargs)
except Exception:
pass
app.nec.push_incoming_event(PubsubReceivedEvent(None,
conn=self, stanza=stanza))
def _nec_pubsub_bookmarks_received(self, obj):
if obj.conn.name != self.name:
return
app.log('bookmarks').info('Received Bookmarks (PubSub)')
bm_jids = [b['jid'] for b in self.bookmarks]
for bm in obj.bookmarks:
if bm['jid'] not in bm_jids:
self.bookmarks.append(bm)
# We got bookmarks from pubsub, now get those from xml to merge them
self.get_bookmarks(storage_type='xml')
def _validate_avatar_node(self, stanza):
jid = stanza.getFrom()
if jid is None:
jid = self.get_own_jid().getStripped()
else:
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(
'Received Avatar (Pubsub): %s %s', jid, sha)
app.interface.save_avatar(data)
if self.get_own_jid().bareMatch(jid):
app.config.set_per('accounts', self.name, 'avatar_sha', sha)
else:
own_jid = self.get_own_jid().getStripped()
app.logger.set_avatar_sha(own_jid, jid, sha)
app.contacts.set_avatar(self.name, jid, sha)
app.interface.update_avatar(self.name, jid)
def _PubSubErrorCB(self, conn, stanza):
log.debug('_PubsubErrorCB')
pubsub = stanza.getTag('pubsub')
if not pubsub:
return
items = pubsub.getTag('items')
if not items:
return
if items.getAttr('node') == 'storage:bookmarks':
# Receiving bookmarks from pubsub failed, so take them from xml
self.get_bookmarks(storage_type='xml')
def request_pb_configuration(self, jid, node):
if not self.connection or self.connected < 2:
return
query = nbxmpp.Iq('get', to=jid)
e = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
e = e.addChild('configure', {'node': node})
id_ = self.connection.getAnID()
query.setID(id_)
self.awaiting_answers[id_] = (PEP_CONFIG,)
self.connection.send(query)

View File

@ -1900,11 +1900,13 @@ class ManageBookmarksWindow:
iter_ = self.treestore.append(None, [None, account, None, None,
None, None, None, None])
for bookmark in app.connections[account].bookmarks:
con = app.connections[account]
bookmarks = con.get_module('Bookmarks').bookmarks
for jid, bookmark in bookmarks.items():
if not bookmark['name']:
# No name was given for this bookmark.
# Use the first part of JID instead...
name = bookmark['jid'].split("@")[0]
name = jid.split("@")[0]
bookmark['name'] = name
# make '1', '0', 'true', 'false' (or other) to True/False
@ -1920,7 +1922,7 @@ class ManageBookmarksWindow:
self.treestore.append(iter_, [
account,
bookmark['name'],
bookmark['jid'],
jid,
autojoin,
minimize,
bookmark['password'],
@ -2047,15 +2049,17 @@ class ManageBookmarksWindow:
Parse the treestore data into our new bookmarks array, then send the new
bookmarks to the server.
"""
(model, iter_) = self.selection.get_selected()
if iter_ and model.iter_parent(iter_):
#bookmark selected, check it
# bookmark selected, check it
if not self.check_valid_bookmark():
return
for account in self.treestore:
acct = account[1]
app.connections[acct].bookmarks = []
con = app.connections[acct]
con.get_module('Bookmarks').bookmarks = {}
for bm in account.iterchildren():
# Convert True/False/None to '1' or '0'
@ -2067,13 +2071,17 @@ class ManageBookmarksWindow:
nick = bm[6]
# create the bookmark-dict
bmdict = { 'name': name, 'jid': jid, 'autojoin': autojoin,
'minimize': minimize, 'password': pw, 'nick': nick,
bmdict = {
'name': name,
'autojoin': autojoin,
'minimize': minimize,
'password': pw,
'nick': nick,
'print_status': bm[7]}
app.connections[acct].bookmarks.append(bmdict)
con.get_module('Bookmarks').bookmarks[jid] = bmdict
app.connections[acct].store_bookmarks()
con.get_module('Bookmarks').store_bookmarks()
gui_menu_builder.build_bookmark_menu(acct)
self.window.destroy()
@ -2900,8 +2908,10 @@ class ManagePEPServicesWindow:
model, iter_ = selection.get_selected()
node = model[iter_][0]
our_jid = app.get_jid_from_account(self.account)
app.connections[self.account].send_pb_delete(our_jid, node,
on_ok=self.node_removed, on_fail=self.node_not_removed)
con = app.connections[self.account]
con.get_module('PubSub').send_pb_delete(our_jid, node,
on_ok=self.node_removed,
on_fail=self.node_not_removed)
def on_configure_button_clicked(self, widget):
selection = self.treeview.get_selection()
@ -2910,13 +2920,15 @@ class ManagePEPServicesWindow:
model, iter_ = selection.get_selected()
node = model[iter_][0]
our_jid = app.get_jid_from_account(self.account)
app.connections[self.account].request_pb_configuration(our_jid, node)
con = app.connections[self.account]
con.get_module('PubSub').request_pb_configuration(our_jid, node)
def _nec_pep_config_received(self, obj):
def on_ok(form, node):
form.type_ = 'submit'
our_jid = app.get_jid_from_account(self.account)
app.connections[self.account].send_pb_configure(our_jid, node, form)
con = app.connections[self.account]
con.get_module('PubSub').send_pb_configure(our_jid, node, form)
window = dialogs.DataFormWindow(obj.form, (on_ok, obj.node))
title = _('Configure %s') % obj.node
window.set_title(title)

View File

@ -2520,7 +2520,8 @@ class JoinGroupchatWindow(Gtk.ApplicationWindow):
self.autojoin_switch.set_sensitive(switch.get_active())
def _add_bookmark(self, account, nickname, password):
if not app.connections[account].private_storage_supported:
con = app.connections[account]
if not con.private_storage_supported:
return
add_bookmark = self.bookmark_switch.get_active()
@ -2531,8 +2532,8 @@ class JoinGroupchatWindow(Gtk.ApplicationWindow):
# Add as bookmark, with autojoin and not minimized
name = app.get_nick_from_jid(self.room_jid)
app.interface.add_gc_bookmark(
account, name, self.room_jid, autojoin, 1, password, nickname)
con.get_module('Bookmarks').add_bookmark(
name, self.room_jid, autojoin, 1, password, nickname)
def _on_destroy(self, *args):
if self.minimal_mode == 0:
@ -2784,10 +2785,11 @@ class StartChatDialog(Gtk.ApplicationWindow):
show_account = len(self.accounts) > 1
for account in self.accounts:
self.new_groupchat_rows[account] = None
bookmarks = app.connections[account].bookmarks
con = app.connections[account]
bookmarks = con.get_module('Bookmarks').bookmarks
groupchats = {}
for bookmark in bookmarks:
groupchats[bookmark['jid']] = bookmark['name']
for jid, bookmark in bookmarks.items():
groupchats[jid] = bookmark['name']
for jid in app.contacts.get_gc_list(account):
if jid in groupchats:

View File

@ -1757,29 +1757,29 @@ class MucBrowser(AgentBrowser):
self.join_button = None
def on_bookmark_button_clicked(self, *args):
con = app.connections[self.account]
model, iter = self.window.services_treeview.get_selection().get_selected()
if not iter:
return
name = app.config.get_per('accounts', self.account, 'name')
room_jid = model[iter][0]
bm = {
'name': room_jid.split('@')[0],
'jid': room_jid,
'autojoin': '0',
'minimize': '0',
'password': '',
'nick': name
'name': room_jid.split('@')[0],
'autojoin': '0',
'minimize': '0',
'password': '',
'nick': name
}
for bookmark in app.connections[self.account].bookmarks:
if bookmark['jid'] == bm['jid']:
dialogs.ErrorDialog( _('Bookmark already set'),
_('Group Chat "%s" is already in your bookmarks.') % bm['jid'],
if room_jid in con.get_module('Bookmarks').bookmarks:
dialogs.ErrorDialog(
_('Bookmark already set'),
_('Group Chat "%s" is already in your bookmarks.') % room_jid,
transient_for=self.window.window)
return
return
app.connections[self.account].bookmarks.append(bm)
app.connections[self.account].store_bookmarks()
con.get_module('Bookmarks').bookmarks[room_jid] = bm
con.get_module('Bookmarks').store_bookmarks()
gui_menu_builder.build_bookmark_menu(self.account)
@ -1951,8 +1951,9 @@ class DiscussionGroupsBrowser(AgentBrowser):
self.subscribe_button = None
self.unsubscribe_button = None
app.connections[account].send_pb_subscription_query(jid,
self._on_pep_subscriptions)
con = app.connections[account]
con.get_module('PubSub').send_pb_subscription_query(
jid, self._on_pep_subscriptions)
def _create_treemodel(self):
"""
@ -2122,10 +2123,11 @@ class DiscussionGroupsBrowser(AgentBrowser):
model, iter_ = self.window.services_treeview.get_selection().get_selected()
if iter_ is None: return
groupnode = model.get_value(iter_, 1) # 1 = groupnode
node = model.get_value(iter_, 1) # 1 = groupnode
app.connections[self.account].send_pb_subscribe(self.jid, groupnode,
self._on_pep_subscribe, groupnode)
con = app.connections[self.account]
con.get_module('PubSub').send_pb_subscribe(
self.jid, node, self._on_pep_subscribe, groupnode=node)
def on_unsubscribe_button_clicked(self, widget):
"""
@ -2134,10 +2136,11 @@ class DiscussionGroupsBrowser(AgentBrowser):
model, iter_ = self.window.services_treeview.get_selection().get_selected()
if iter_ is None: return
groupnode = model.get_value(iter_, 1) # 1 = groupnode
node = model.get_value(iter_, 1) # 1 = groupnode
app.connections[self.account].send_pb_unsubscribe(self.jid, groupnode,
self._on_pep_unsubscribe, groupnode)
con = app.connections[self.account]
con.get_module('PubSub').send_pb_unsubscribe(
self.jid, node, self._on_pep_unsubscribe, groupnode=node)
def _on_pep_subscriptions(self, conn, request):
"""
@ -2168,8 +2171,6 @@ class DiscussionGroupsBrowser(AgentBrowser):
# we now know subscriptions, update button states
self.update_actions()
raise nbxmpp.NodeProcessed
def _on_pep_subscribe(self, conn, request, groupnode):
"""
We have just subscribed to a node. Update UI
@ -2184,8 +2185,6 @@ class DiscussionGroupsBrowser(AgentBrowser):
self.update_actions()
raise nbxmpp.NodeProcessed
def _on_pep_unsubscribe(self, conn, request, groupnode):
"""
We have just unsubscribed from a node. Update UI
@ -2200,7 +2199,5 @@ class DiscussionGroupsBrowser(AgentBrowser):
self.update_actions()
raise nbxmpp.NodeProcessed
# Fill the global agent type info dictionary
_agent_type_info = _gen_agent_type_info()

View File

@ -348,13 +348,10 @@ class GroupchatControl(ChatControlBase):
self.room_jid = self.contact.jid
self.nick = nick
self.new_nick = ''
self.name = ''
for bm in app.connections[self.account].bookmarks:
if bm['jid'] == self.room_jid:
self.name = bm['name']
break
if not self.name:
self.name = self.room_jid.split('@')[0]
bm_module = app.connections[self.account].get_module('Bookmarks')
self.name = bm_module.get_name_from_bookmark(self.room_jid)
self.contact.name = self.name
self.widget_set_visible(self.xml.get_object('banner_eventbox'),
@ -589,13 +586,9 @@ class GroupchatControl(ChatControlBase):
# Bookmarks
con = app.connections[self.account]
bookmark_support = con.bookmarks_available()
not_bookmarked = True
for bm in con.bookmarks:
if bm['jid'] == self.room_jid:
not_bookmarked = False
break
bookmarked = self.room_jid in con.get_module('Bookmarks').bookmarks
win.lookup_action('bookmark-' + self.control_id).set_enabled(
online and bookmark_support and not_bookmarked)
online and bookmark_support and not bookmarked)
# Request Voice
role = self.get_role(self.nick)
@ -712,9 +705,10 @@ class GroupchatControl(ChatControlBase):
Bookmark the room, without autojoin and not minimized
"""
password = app.gc_passwords.get(self.room_jid, '')
app.interface.add_gc_bookmark(
self.account, self.name, self.room_jid,
'0', '0', password, self.nick)
con = app.connections[self.account]
con.get_module('Bookmarks').add_bookmark(
self.name, self.room_jid,
'1', '1', password, self.nick)
self.update_actions()
def _on_request_voice(self, action, param):

View File

@ -67,7 +67,9 @@ class GroupsPostWindow:
item.addChild('content', {}, [buf.get_text(buf.get_start_iter(), buf.get_end_iter(), True)])
# publish it to node
app.connections[self.account].send_pb_publish(self.servicejid, self.groupid, item, '0')
con = app.connections[self.account]
con.get_module('PubSub').send_pb_publish(
self.servicejid, self.groupid, item, '0')
# close the window
self.window.destroy()

View File

@ -824,17 +824,7 @@ class Interface:
self.instances[account]['sub_request'][obj.jid].destroy()
def handle_event_bookmarks(self, obj):
# ('BOOKMARKS', account, [{name,jid,autojoin,password,nick}, {}])
# We received a bookmark item from the server (JEP48)
# Auto join GC windows if necessary
gui_menu_builder.build_bookmark_menu(obj.conn.name)
invisible_show = app.SHOW_LIST.index('invisible')
# do not autojoin if we are invisible
if obj.conn.connected == invisible_show:
return
GLib.idle_add(self.auto_join_bookmarks, obj.conn.name)
gui_menu_builder.build_bookmark_menu(obj.account)
def handle_event_file_send_error(self, account, array):
jid = array[0]
@ -1728,7 +1718,6 @@ class Interface:
if isinstance(ctrl, ChatControlBase):
ctrl.scroll_to_end()
def join_gc_minimal(self, account, room_jid, password=None,
transient_for=None):
if account is not None:
@ -1738,9 +1727,10 @@ class Interface:
app.interface.join_gc_room(account, room_jid, '', '')
return
for bookmark in app.connections[account].bookmarks:
if bookmark['jid'] != room_jid:
continue
con = app.connections[account]
bookmarks = con.get_module('Bookmarks').bookmarks
bookmark = bookmarks.get(room_jid, None)
if bookmark is not None:
app.interface.join_gc_room(
account, room_jid, bookmark['nick'], bookmark['password'])
return
@ -2469,51 +2459,6 @@ class Interface:
return False
return True
def auto_join_bookmarks(self, account):
"""
Autojoin bookmarked GCs that have 'auto join' on for this account
"""
for bm in app.connections[account].bookmarks:
if bm['autojoin'] in ('1', 'true'):
jid = bm['jid']
# Only join non-opened groupchats. Opened one are already
# auto-joined on re-connection
if not jid in app.gc_connected[account]:
# we are not already connected
minimize = bm['minimize'] in ('1', 'true')
self.join_gc_room(account, jid, bm['nick'],
bm['password'], minimize = minimize)
def add_gc_bookmark(self, account, name, jid, autojoin, minimize, password,
nick):
"""
Add a bookmark for this account, sorted in bookmark list
"""
bm = {
'name': name,
'jid': jid,
'autojoin': autojoin,
'minimize': minimize,
'password': password,
'nick': nick
}
place_found = False
index = 0
# check for duplicate entry and respect alpha order
for bookmark in app.connections[account].bookmarks:
if bookmark['jid'] == bm['jid']:
return
if bookmark['name'] > bm['name']:
place_found = True
break
index += 1
if place_found:
app.connections[account].bookmarks.insert(index, bm)
else:
app.connections[account].bookmarks.append(bm)
app.connections[account].store_bookmarks()
gui_menu_builder.build_bookmark_menu(account)
# does JID exist only within a groupchat?
def is_pm_contact(self, fjid, account):
bare_jid = app.get_jid_without_resource(fjid)

View File

@ -172,14 +172,15 @@ show_bookmarked=False, force_resource=False):
rooms2 = [] # a list of (room_jid, account) tuple
r_jids = [] # list of room jids
for account in connected_accounts:
for room in app.connections[account].bookmarks:
r_jid = room['jid']
if r_jid in r_jids:
con = app.connections[account]
boomarks = con.get_module('Bookmarks').bookmarks
for jid, bookmark in boomarks.items():
if jid in r_jids:
continue
if r_jid not in app.gc_connected[account] or not \
app.gc_connected[account][r_jid]:
rooms2.append((r_jid, account))
r_jids.append(r_jid)
if jid not in app.gc_connected[account] or not \
app.gc_connected[account][jid]:
rooms2.append((jid, account))
r_jids.append(jid)
if not rooms2:
return
@ -675,7 +676,9 @@ def get_groupchat_menu(control_id):
def get_bookmarks_menu(account, rebuild=False):
if not app.connections[account].bookmarks:
con = app.connections[account]
boomarks = con.get_module('Bookmarks').bookmarks
if not boomarks:
return None
menu = Gio.Menu()
@ -688,12 +691,12 @@ def get_bookmarks_menu(account, rebuild=False):
# Build Bookmarks
section = Gio.Menu()
for bookmark in app.connections[account].bookmarks:
for jid, bookmark in boomarks.items():
name = bookmark['name']
if not name:
# No name was given for this bookmark.
# Use the first part of JID instead...
name = bookmark['jid'].split("@")[0]
name = jid.split("@")[0]
# Shorten long names
name = (name[:42] + '..') if len(name) > 42 else name
@ -703,7 +706,7 @@ def get_bookmarks_menu(account, rebuild=False):
# Create Variant Dict
dict_ = {'account': GLib.Variant('s', account),
'jid': GLib.Variant('s', bookmark['jid'])}
'jid': GLib.Variant('s', jid)}
if bookmark['nick']:
dict_['nick'] = GLib.Variant('s', bookmark['nick'])
if bookmark['password']:

View File

@ -2154,7 +2154,8 @@ class RosterWindow:
auto=auto)
if was_invisible and status != 'offline':
# We come back from invisible, join bookmarks
app.interface.auto_join_bookmarks(account)
con = app.connections[account]
con.get_module('Bookmarks').auto_join_bookmarks()
def chg_contact_status(self, contact, show, status, account):
@ -2712,9 +2713,9 @@ class RosterWindow:
### FIXME: order callbacks in itself...
################################################################################
def on_bookmark_menuitem_activate(self, widget, account, bookmark):
app.interface.join_gc_room(account, bookmark['jid'], bookmark['nick'],
bookmark['password'])
def on_bookmark_menuitem_activate(self, widget, account, jid, bookmark):
app.interface.join_gc_room(
account, jid, bookmark['nick'], bookmark['password'])
def on_info(self, widget, contact, account):
"""
@ -5439,16 +5440,17 @@ class RosterWindow:
gc_sub_menu.append(item)
# User has at least one bookmark.
if app.connections[account].bookmarks:
con = app.connections[account]
if con.get_module('Bookmarks').bookmarks:
item = Gtk.SeparatorMenuItem.new()
gc_sub_menu.append(item)
for bookmark in app.connections[account].bookmarks:
for jid, bookmark in con.get_module('Bookmarks').bookmarks.items():
name = bookmark['name']
if not name:
# No name was given for this bookmark.
# Use the first part of JID instead...
name = bookmark['jid'].split("@")[0]
name = jid.split("@")[0]
# Shorten long names
name = (name[:42] + '..') if len(name) > 42 else name
@ -5456,8 +5458,9 @@ class RosterWindow:
# Do not use underline.
item = Gtk.MenuItem.new_with_label(name)
item.set_use_underline(False)
item.connect('activate', self.on_bookmark_menuitem_activate,
account, bookmark)
item.connect(
'activate', self.on_bookmark_menuitem_activate,
account, jid, bookmark)
gc_sub_menu.append(item)
def show_appropriate_context_menu(self, event, iters):