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.http_auth import HTTPAuth
from gajim.common.modules.vcard_temp import VCardTemp from gajim.common.modules.vcard_temp import VCardTemp
from gajim.common.modules.vcard_avatars import VCardAvatars 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.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
@ -113,7 +116,6 @@ class CommonConnection:
self.old_show = '' self.old_show = ''
self.priority = app.get_priority(name, 'offline') self.priority = app.get_priority(name, 'offline')
self.time_to_reconnect = None self.time_to_reconnect = None
self.bookmarks = []
self.blocked_list = [] self.blocked_list = []
self.blocked_contacts = [] self.blocked_contacts = []
@ -491,18 +493,6 @@ class CommonConnection:
def account_changed(self, new_name): def account_changed(self, new_name):
self.name = 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): def get_metacontacts(self):
""" """
To be implemented by derived classes To be implemented by derived classes
@ -670,6 +660,9 @@ class Connection(CommonConnection, ConnectionHandlers):
self.register_module('HTTPAuth', HTTPAuth, self) self.register_module('HTTPAuth', HTTPAuth, self)
self.register_module('VCardTemp', VCardTemp, self) self.register_module('VCardTemp', VCardTemp, self)
self.register_module('VCardAvatars', VCardAvatars, 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, app.ged.register_event_handler('privacy-list-received', ged.CORE,
self._nec_privacy_list_received) self._nec_privacy_list_received)
@ -1774,8 +1767,8 @@ class Connection(CommonConnection, ConnectionHandlers):
# ask our VCard # ask our VCard
self.get_module('VCardTemp').request_vcard() self.get_module('VCardTemp').request_vcard()
# Get bookmarks from private namespace # Get bookmarks
self.get_bookmarks() self.get_module('Bookmarks').get_bookmarks()
# Get annotations # Get annotations
self.get_module('Annotations').get_annotations() self.get_module('Annotations').get_annotations()
@ -1916,8 +1909,7 @@ class Connection(CommonConnection, ConnectionHandlers):
self.pubsub_publish_options_supported = True self.pubsub_publish_options_supported = True
else: else:
# Remove stored bookmarks accessible to everyone. # Remove stored bookmarks accessible to everyone.
self.send_pb_purge(our_jid, 'storage:bookmarks') self.get_module('Bookmarks').purge_pubsub_bookmarks()
self.send_pb_delete(our_jid, 'storage:bookmarks')
if obj.fjid == hostname: if obj.fjid == hostname:
if nbxmpp.NS_SECLABEL in obj.features: if nbxmpp.NS_SECLABEL in obj.features:
@ -2255,99 +2247,6 @@ class Connection(CommonConnection, ConnectionHandlers):
return True return True
return False 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): def get_roster_delimiter(self):
""" """
Get roster group delimiter from storage as described in XEP 0083 Get roster group delimiter from storage as described in XEP 0083
@ -2614,11 +2513,7 @@ class Connection(CommonConnection, ConnectionHandlers):
destroy.setAttr('jid', jid) destroy.setAttr('jid', jid)
self.connection.send(iq) self.connection.send(iq)
i = 0 i = 0
for bm in self.bookmarks: self.get_module('Bookmarks').bookmarks.pop(jid, None)
if bm['jid'] == room_jid:
del self.bookmarks[i]
break
i += 1
self.store_bookmarks() self.store_bookmarks()
def send_gc_status(self, nick, jid, show, status, auto=False): 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 import configpaths
from gajim.common.caps_cache import muc_caps_cache from gajim.common.caps_cache import muc_caps_cache
from gajim.common.commands import ConnectionCommands from gajim.common.commands import ConnectionCommands
from gajim.common.pubsub import ConnectionPubSub
from gajim.common.protocol.caps import ConnectionCaps from gajim.common.protocol.caps import ConnectionCaps
from gajim.common.protocol.bytestream import ConnectionSocks5Bytestream from gajim.common.protocol.bytestream import ConnectionSocks5Bytestream
from gajim.common.protocol.bytestream import ConnectionIBBytestream from gajim.common.protocol.bytestream import ConnectionIBBytestream
@ -339,15 +338,15 @@ class ConnectionPEP(object):
if message: if message:
i = item.addChild('text') i = item.addChild('text')
i.addData(message) i.addData(message)
self._pubsub_connection.send_pb_publish('', nbxmpp.NS_ACTIVITY, item, self.get_module('PubSub').send_pb_publish(
'0') '', nbxmpp.NS_ACTIVITY, item, '0')
def retract_activity(self): def retract_activity(self):
if not self.pep_supported: if not self.pep_supported:
return return
self.send_activity(None) self.send_activity(None)
# not all client support new XEP, so we still retract # 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): def send_mood(self, mood, message=None):
if self.connected == 1: if self.connected == 1:
@ -363,14 +362,14 @@ class ConnectionPEP(object):
if message: if message:
i = item.addChild('text') i = item.addChild('text')
i.addData(message) 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): def retract_mood(self):
if not self.pep_supported: if not self.pep_supported:
return return
self.send_mood(None) self.send_mood(None)
# not all client support new XEP, so we still retract # 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, def send_tune(self, artist='', title='', source='', track=0, length=0,
items=None): items=None):
@ -399,14 +398,14 @@ class ConnectionPEP(object):
i.addData(length) i.addData(length)
if items: if items:
item.addChild(payload=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): def retract_tune(self):
if not self.pep_supported: if not self.pep_supported:
return return
self.send_tune(None) self.send_tune(None)
# not all client support new XEP, so we still retract # 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): def send_nickname(self, nick):
if self.connected == 1: if self.connected == 1:
@ -418,13 +417,13 @@ class ConnectionPEP(object):
return return
item = nbxmpp.Node('nick', {'xmlns': nbxmpp.NS_NICK}) item = nbxmpp.Node('nick', {'xmlns': nbxmpp.NS_NICK})
item.addData(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): def retract_nickname(self):
if not self.pep_supported: if not self.pep_supported:
return 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): def send_location(self, info):
if self.connected == 1: if self.connected == 1:
@ -439,14 +438,14 @@ class ConnectionPEP(object):
if info.get(field, None): if info.get(field, None):
i = item.addChild(field) i = item.addChild(field)
i.addData(info[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): def retract_location(self):
if not self.pep_supported: if not self.pep_supported:
return return
self.send_location({}) self.send_location({})
# not all client support new XEP, so we still retract # 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 # basic connection handlers used here and in zeroconf
class ConnectionHandlersBase: class ConnectionHandlersBase:
@ -915,7 +914,7 @@ class ConnectionHandlersBase:
class ConnectionHandlers(ConnectionArchive313, class ConnectionHandlers(ConnectionArchive313,
ConnectionSocks5Bytestream, ConnectionDisco, ConnectionSocks5Bytestream, ConnectionDisco,
ConnectionCommands, ConnectionPubSub, ConnectionPEP, ConnectionCaps, ConnectionCommands, ConnectionPEP, ConnectionCaps,
ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream, ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream,
ConnectionHTTPUpload): ConnectionHTTPUpload):
def __init__(self): def __init__(self):
@ -923,7 +922,6 @@ ConnectionHTTPUpload):
ConnectionSocks5Bytestream.__init__(self) ConnectionSocks5Bytestream.__init__(self)
ConnectionIBBytestream.__init__(self) ConnectionIBBytestream.__init__(self)
ConnectionCommands.__init__(self) ConnectionCommands.__init__(self)
ConnectionPubSub.__init__(self)
ConnectionPEP.__init__(self, account=self.name, dispatcher=self, ConnectionPEP.__init__(self, account=self.name, dispatcher=self,
pubsub_connection=self) pubsub_connection=self)
ConnectionHTTPUpload.__init__(self) ConnectionHTTPUpload.__init__(self)
@ -949,8 +947,6 @@ ConnectionHTTPUpload):
self.privacy_default_list = None 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(StreamConflictReceivedEvent)
app.nec.register_incoming_event(StreamOtherHostReceivedEvent) app.nec.register_incoming_event(StreamOtherHostReceivedEvent)
app.nec.register_incoming_event(MessageReceivedEvent) app.nec.register_incoming_event(MessageReceivedEvent)
@ -961,8 +957,6 @@ ConnectionHTTPUpload):
app.ged.register_event_handler('roster-set-received', app.ged.register_event_handler('roster-set-received',
ged.CORE, self._nec_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, app.ged.register_event_handler('roster-received', ged.CORE,
self._nec_roster_received) self._nec_roster_received)
app.ged.register_event_handler('iq-error-received', ged.CORE, app.ged.register_event_handler('iq-error-received', ged.CORE,
@ -988,12 +982,9 @@ ConnectionHTTPUpload):
ConnectionHandlersBase.cleanup(self) ConnectionHandlersBase.cleanup(self)
ConnectionCaps.cleanup(self) ConnectionCaps.cleanup(self)
ConnectionArchive313.cleanup(self) ConnectionArchive313.cleanup(self)
ConnectionPubSub.cleanup(self)
ConnectionHTTPUpload.cleanup(self) ConnectionHTTPUpload.cleanup(self)
app.ged.remove_event_handler('roster-set-received', app.ged.remove_event_handler('roster-set-received',
ged.CORE, self._nec_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, app.ged.remove_event_handler('roster-received', ged.CORE,
self._nec_roster_received) self._nec_roster_received)
app.ged.remove_event_handler('iq-error-received', ged.CORE, app.ged.remove_event_handler('iq-error-received', ged.CORE,
@ -1144,28 +1135,6 @@ ConnectionHTTPUpload):
conn=self, stanza=obj.stanza)) conn=self, stanza=obj.stanza))
return True 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): def _SecLabelCB(self, con, iq_obj):
""" """
Security Label callback, used for catalogues. Security Label callback, used for catalogues.
@ -1540,8 +1509,8 @@ ConnectionHTTPUpload):
# ask our VCard # ask our VCard
self.get_module('VCardTemp').request_vcard() self.get_module('VCardTemp').request_vcard()
# Get bookmarks from private namespace # Get bookmarks
self.get_bookmarks() self.get_module('Bookmarks').get_bookmarks()
# Get annotations from private namespace # Get annotations from private namespace
self.get_module('Annotations').get_annotations() self.get_module('Annotations').get_annotations()
@ -1645,7 +1614,6 @@ ConnectionHTTPUpload):
nbxmpp.NS_MUC_OWNER) nbxmpp.NS_MUC_OWNER)
con.RegisterHandler('iq', self._MucAdminCB, 'result', con.RegisterHandler('iq', self._MucAdminCB, 'result',
nbxmpp.NS_MUC_ADMIN) nbxmpp.NS_MUC_ADMIN)
con.RegisterHandler('iq', self._PrivateCB, 'result', nbxmpp.NS_PRIVATE)
con.RegisterHandler('iq', self._SecLabelCB, 'result', con.RegisterHandler('iq', self._SecLabelCB, 'result',
nbxmpp.NS_SECLABEL_CATALOG) nbxmpp.NS_SECLABEL_CATALOG)
con.RegisterHandler('iq', self._CommandExecuteCB, 'set', con.RegisterHandler('iq', self._CommandExecuteCB, 'set',
@ -1657,8 +1625,6 @@ ConnectionHTTPUpload):
con.RegisterHandler('iq', self._PrivacySetCB, 'set', nbxmpp.NS_PRIVACY) 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_1)
con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM_2) 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, 'result')
con.RegisterHandler('iq', self._JingleCB, 'error') con.RegisterHandler('iq', self._JingleCB, 'error')
con.RegisterHandler('iq', self._JingleCB, 'set', nbxmpp.NS_JINGLE) 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 self.users_dict[jid]['reason'] = reason
return True 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): class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
name = 'iq-error-received' name = 'iq-error-received'
base_network_events = [] base_network_events = []

View File

@ -119,6 +119,11 @@ class RequestAvatar(IntEnum):
ROOM = 1 ROOM = 1
USER = 2 USER = 2
@unique
class BookmarkStorageType(IntEnum):
PRIVATE = 0
PUBSUB = 1
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"),

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'): for item in items.getTags('item'):
metadata = item.getTag('metadata') metadata = item.getTag('metadata')
if metadata is None: 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 break
info = item.getTag('metadata').getTag('info') info = item.getTag('metadata').getTag('info')
if info is not None: if info is not None:
@ -498,23 +499,22 @@ class AvatarNotificationPEP(AbstractPEP):
con = app.connections[account] con = app.connections[account]
if self.avatar is None: if self.avatar is None:
# Remove avatar # 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) app.contacts.set_avatar(account, jid, None)
own_jid = con.get_own_jid().getStripped() own_jid = con.get_own_jid().getStripped()
app.logger.set_avatar_sha(own_jid, jid, None) app.logger.set_avatar_sha(own_jid, jid, None)
app.interface.update_avatar(account, jid) app.interface.update_avatar(account, jid)
else: else:
sha = app.contacts.get_avatar_sha(account, jid) sha = app.contacts.get_avatar_sha(account, jid)
app.log('avatar').info( app.log('c.m.user_avatar').info(
'Update (Pubsub): %s %s', jid, self.avatar['id']) 'Update: %s %s', jid, self.avatar['id'])
if sha == self.avatar['id']: if sha == self.avatar['id']:
app.log('avatar').info( app.log('c.m.user_avatar').info(
'Avatar already known (Pubsub): %s %s', 'Avatar already known: %s %s',
jid, self.avatar['id']) jid, self.avatar['id'])
return return
app.log('avatar').info('Request (Pubsub): %s', jid) con.get_module('UserAvatar').get_pubsub_avatar(
con.get_pubsub_avatar(jid, 'urn:xmpp:avatar:data', jid, self.avatar['id'])
self.avatar['id'])
SUPPORTED_PERSONAL_USER_EVENTS = [ 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, iter_ = self.treestore.append(None, [None, account, None, None,
None, None, 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']: if not bookmark['name']:
# No name was given for this bookmark. # No name was given for this bookmark.
# Use the first part of JID instead... # Use the first part of JID instead...
name = bookmark['jid'].split("@")[0] name = jid.split("@")[0]
bookmark['name'] = name bookmark['name'] = name
# make '1', '0', 'true', 'false' (or other) to True/False # make '1', '0', 'true', 'false' (or other) to True/False
@ -1920,7 +1922,7 @@ class ManageBookmarksWindow:
self.treestore.append(iter_, [ self.treestore.append(iter_, [
account, account,
bookmark['name'], bookmark['name'],
bookmark['jid'], jid,
autojoin, autojoin,
minimize, minimize,
bookmark['password'], bookmark['password'],
@ -2047,15 +2049,17 @@ class ManageBookmarksWindow:
Parse the treestore data into our new bookmarks array, then send the new Parse the treestore data into our new bookmarks array, then send the new
bookmarks to the server. bookmarks to the server.
""" """
(model, iter_) = self.selection.get_selected() (model, iter_) = self.selection.get_selected()
if iter_ and model.iter_parent(iter_): if iter_ and model.iter_parent(iter_):
#bookmark selected, check it # bookmark selected, check it
if not self.check_valid_bookmark(): if not self.check_valid_bookmark():
return return
for account in self.treestore: for account in self.treestore:
acct = account[1] acct = account[1]
app.connections[acct].bookmarks = [] con = app.connections[acct]
con.get_module('Bookmarks').bookmarks = {}
for bm in account.iterchildren(): for bm in account.iterchildren():
# Convert True/False/None to '1' or '0' # Convert True/False/None to '1' or '0'
@ -2067,13 +2071,17 @@ class ManageBookmarksWindow:
nick = bm[6] nick = bm[6]
# create the bookmark-dict # create the bookmark-dict
bmdict = { 'name': name, 'jid': jid, 'autojoin': autojoin, bmdict = {
'minimize': minimize, 'password': pw, 'nick': nick, 'name': name,
'autojoin': autojoin,
'minimize': minimize,
'password': pw,
'nick': nick,
'print_status': bm[7]} '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) gui_menu_builder.build_bookmark_menu(acct)
self.window.destroy() self.window.destroy()
@ -2900,8 +2908,10 @@ class ManagePEPServicesWindow:
model, iter_ = selection.get_selected() model, iter_ = selection.get_selected()
node = model[iter_][0] node = model[iter_][0]
our_jid = app.get_jid_from_account(self.account) our_jid = app.get_jid_from_account(self.account)
app.connections[self.account].send_pb_delete(our_jid, node, con = app.connections[self.account]
on_ok=self.node_removed, on_fail=self.node_not_removed) 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): def on_configure_button_clicked(self, widget):
selection = self.treeview.get_selection() selection = self.treeview.get_selection()
@ -2910,13 +2920,15 @@ class ManagePEPServicesWindow:
model, iter_ = selection.get_selected() model, iter_ = selection.get_selected()
node = model[iter_][0] node = model[iter_][0]
our_jid = app.get_jid_from_account(self.account) 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 _nec_pep_config_received(self, obj):
def on_ok(form, node): def on_ok(form, node):
form.type_ = 'submit' form.type_ = 'submit'
our_jid = app.get_jid_from_account(self.account) 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)) window = dialogs.DataFormWindow(obj.form, (on_ok, obj.node))
title = _('Configure %s') % obj.node title = _('Configure %s') % obj.node
window.set_title(title) window.set_title(title)

View File

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

View File

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

View File

@ -348,13 +348,10 @@ class GroupchatControl(ChatControlBase):
self.room_jid = self.contact.jid self.room_jid = self.contact.jid
self.nick = nick self.nick = nick
self.new_nick = '' self.new_nick = ''
self.name = ''
for bm in app.connections[self.account].bookmarks: bm_module = app.connections[self.account].get_module('Bookmarks')
if bm['jid'] == self.room_jid: self.name = bm_module.get_name_from_bookmark(self.room_jid)
self.name = bm['name']
break
if not self.name:
self.name = self.room_jid.split('@')[0]
self.contact.name = self.name self.contact.name = self.name
self.widget_set_visible(self.xml.get_object('banner_eventbox'), self.widget_set_visible(self.xml.get_object('banner_eventbox'),
@ -589,13 +586,9 @@ class GroupchatControl(ChatControlBase):
# Bookmarks # Bookmarks
con = app.connections[self.account] con = app.connections[self.account]
bookmark_support = con.bookmarks_available() bookmark_support = con.bookmarks_available()
not_bookmarked = True bookmarked = self.room_jid in con.get_module('Bookmarks').bookmarks
for bm in con.bookmarks:
if bm['jid'] == self.room_jid:
not_bookmarked = False
break
win.lookup_action('bookmark-' + self.control_id).set_enabled( 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 # Request Voice
role = self.get_role(self.nick) role = self.get_role(self.nick)
@ -712,9 +705,10 @@ class GroupchatControl(ChatControlBase):
Bookmark the room, without autojoin and not minimized Bookmark the room, without autojoin and not minimized
""" """
password = app.gc_passwords.get(self.room_jid, '') password = app.gc_passwords.get(self.room_jid, '')
app.interface.add_gc_bookmark( con = app.connections[self.account]
self.account, self.name, self.room_jid, con.get_module('Bookmarks').add_bookmark(
'0', '0', password, self.nick) self.name, self.room_jid,
'1', '1', password, self.nick)
self.update_actions() self.update_actions()
def _on_request_voice(self, action, param): 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)]) item.addChild('content', {}, [buf.get_text(buf.get_start_iter(), buf.get_end_iter(), True)])
# publish it to node # 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 # close the window
self.window.destroy() self.window.destroy()

View File

@ -824,17 +824,7 @@ class Interface:
self.instances[account]['sub_request'][obj.jid].destroy() self.instances[account]['sub_request'][obj.jid].destroy()
def handle_event_bookmarks(self, obj): def handle_event_bookmarks(self, obj):
# ('BOOKMARKS', account, [{name,jid,autojoin,password,nick}, {}]) gui_menu_builder.build_bookmark_menu(obj.account)
# 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)
def handle_event_file_send_error(self, account, array): def handle_event_file_send_error(self, account, array):
jid = array[0] jid = array[0]
@ -1728,7 +1718,6 @@ class Interface:
if isinstance(ctrl, ChatControlBase): if isinstance(ctrl, ChatControlBase):
ctrl.scroll_to_end() ctrl.scroll_to_end()
def join_gc_minimal(self, account, room_jid, password=None, def join_gc_minimal(self, account, room_jid, password=None,
transient_for=None): transient_for=None):
if account is not None: if account is not None:
@ -1738,9 +1727,10 @@ class Interface:
app.interface.join_gc_room(account, room_jid, '', '') app.interface.join_gc_room(account, room_jid, '', '')
return return
for bookmark in app.connections[account].bookmarks: con = app.connections[account]
if bookmark['jid'] != room_jid: bookmarks = con.get_module('Bookmarks').bookmarks
continue bookmark = bookmarks.get(room_jid, None)
if bookmark is not None:
app.interface.join_gc_room( app.interface.join_gc_room(
account, room_jid, bookmark['nick'], bookmark['password']) account, room_jid, bookmark['nick'], bookmark['password'])
return return
@ -2469,51 +2459,6 @@ class Interface:
return False return False
return True 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? # does JID exist only within a groupchat?
def is_pm_contact(self, fjid, account): def is_pm_contact(self, fjid, account):
bare_jid = app.get_jid_without_resource(fjid) 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 rooms2 = [] # a list of (room_jid, account) tuple
r_jids = [] # list of room jids r_jids = [] # list of room jids
for account in connected_accounts: for account in connected_accounts:
for room in app.connections[account].bookmarks: con = app.connections[account]
r_jid = room['jid'] boomarks = con.get_module('Bookmarks').bookmarks
if r_jid in r_jids: for jid, bookmark in boomarks.items():
if jid in r_jids:
continue continue
if r_jid not in app.gc_connected[account] or not \ if jid not in app.gc_connected[account] or not \
app.gc_connected[account][r_jid]: app.gc_connected[account][jid]:
rooms2.append((r_jid, account)) rooms2.append((jid, account))
r_jids.append(r_jid) r_jids.append(jid)
if not rooms2: if not rooms2:
return return
@ -675,7 +676,9 @@ def get_groupchat_menu(control_id):
def get_bookmarks_menu(account, rebuild=False): 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 return None
menu = Gio.Menu() menu = Gio.Menu()
@ -688,12 +691,12 @@ def get_bookmarks_menu(account, rebuild=False):
# Build Bookmarks # Build Bookmarks
section = Gio.Menu() section = Gio.Menu()
for bookmark in app.connections[account].bookmarks: for jid, bookmark in boomarks.items():
name = bookmark['name'] name = bookmark['name']
if not name: if not name:
# No name was given for this bookmark. # No name was given for this bookmark.
# Use the first part of JID instead... # Use the first part of JID instead...
name = bookmark['jid'].split("@")[0] name = jid.split("@")[0]
# Shorten long names # Shorten long names
name = (name[:42] + '..') if len(name) > 42 else name name = (name[:42] + '..') if len(name) > 42 else name
@ -703,7 +706,7 @@ def get_bookmarks_menu(account, rebuild=False):
# Create Variant Dict # Create Variant Dict
dict_ = {'account': GLib.Variant('s', account), dict_ = {'account': GLib.Variant('s', account),
'jid': GLib.Variant('s', bookmark['jid'])} 'jid': GLib.Variant('s', jid)}
if bookmark['nick']: if bookmark['nick']:
dict_['nick'] = GLib.Variant('s', bookmark['nick']) dict_['nick'] = GLib.Variant('s', bookmark['nick'])
if bookmark['password']: if bookmark['password']:

View File

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