Philipp Hörist pushed to branch master at gajim / gajim
Commits:
-
2ca0ca38
by Philipp Hörist at 2018-07-01T22:35:56+02:00
17 changed files:
- gajim/common/connection.py
- gajim/common/connection_handlers.py
- gajim/common/connection_handlers_events.py
- gajim/common/const.py
- + gajim/common/modules/bookmarks.py
- + gajim/common/modules/pubsub.py
- + gajim/common/modules/user_avatar.py
- gajim/common/pep.py
- − gajim/common/pubsub.py
- gajim/config.py
- gajim/dialogs.py
- gajim/disco.py
- gajim/groupchat_control.py
- gajim/groups.py
- gajim/gui_interface.py
- gajim/gui_menu_builder.py
- gajim/roster_window.py
Changes:
| ... | ... | @@ -73,6 +73,9 @@ from gajim.common.modules.last_activity import LastActivity |
| 73 | 73 |
from gajim.common.modules.http_auth import HTTPAuth
|
| 74 | 74 |
from gajim.common.modules.vcard_temp import VCardTemp
|
| 75 | 75 |
from gajim.common.modules.vcard_avatars import VCardAvatars
|
| 76 |
+from gajim.common.modules.pubsub import PubSub
|
|
| 77 |
+from gajim.common.modules.bookmarks import Bookmarks
|
|
| 78 |
+from gajim.common.modules.user_avatar import UserAvatar
|
|
| 76 | 79 |
from gajim.common.connection_handlers import *
|
| 77 | 80 |
from gajim.common.contacts import GC_Contact
|
| 78 | 81 |
from gajim.gtkgui_helpers import get_action
|
| ... | ... | @@ -113,7 +116,6 @@ class CommonConnection: |
| 113 | 116 |
self.old_show = ''
|
| 114 | 117 |
self.priority = app.get_priority(name, 'offline')
|
| 115 | 118 |
self.time_to_reconnect = None
|
| 116 |
- self.bookmarks = []
|
|
| 117 | 119 |
|
| 118 | 120 |
self.blocked_list = []
|
| 119 | 121 |
self.blocked_contacts = []
|
| ... | ... | @@ -491,18 +493,6 @@ class CommonConnection: |
| 491 | 493 |
def account_changed(self, new_name):
|
| 492 | 494 |
self.name = new_name
|
| 493 | 495 |
|
| 494 |
- def get_bookmarks(self):
|
|
| 495 |
- """
|
|
| 496 |
- To be implemented by derived classes
|
|
| 497 |
- """
|
|
| 498 |
- raise NotImplementedError
|
|
| 499 |
- |
|
| 500 |
- def store_bookmarks(self):
|
|
| 501 |
- """
|
|
| 502 |
- To be implemented by derived classes
|
|
| 503 |
- """
|
|
| 504 |
- raise NotImplementedError
|
|
| 505 |
- |
|
| 506 | 496 |
def get_metacontacts(self):
|
| 507 | 497 |
"""
|
| 508 | 498 |
To be implemented by derived classes
|
| ... | ... | @@ -670,6 +660,9 @@ class Connection(CommonConnection, ConnectionHandlers): |
| 670 | 660 |
self.register_module('HTTPAuth', HTTPAuth, self)
|
| 671 | 661 |
self.register_module('VCardTemp', VCardTemp, self)
|
| 672 | 662 |
self.register_module('VCardAvatars', VCardAvatars, self)
|
| 663 |
+ self.register_module('PubSub', PubSub, self)
|
|
| 664 |
+ self.register_module('Bookmarks', Bookmarks, self)
|
|
| 665 |
+ self.register_module('UserAvatar', UserAvatar, self)
|
|
| 673 | 666 |
|
| 674 | 667 |
app.ged.register_event_handler('privacy-list-received', ged.CORE,
|
| 675 | 668 |
self._nec_privacy_list_received)
|
| ... | ... | @@ -1774,8 +1767,8 @@ class Connection(CommonConnection, ConnectionHandlers): |
| 1774 | 1767 |
# ask our VCard
|
| 1775 | 1768 |
self.get_module('VCardTemp').request_vcard()
|
| 1776 | 1769 |
|
| 1777 |
- # Get bookmarks from private namespace
|
|
| 1778 |
- self.get_bookmarks()
|
|
| 1770 |
+ # Get bookmarks
|
|
| 1771 |
+ self.get_module('Bookmarks').get_bookmarks()
|
|
| 1779 | 1772 |
|
| 1780 | 1773 |
# Get annotations
|
| 1781 | 1774 |
self.get_module('Annotations').get_annotations()
|
| ... | ... | @@ -1916,8 +1909,7 @@ class Connection(CommonConnection, ConnectionHandlers): |
| 1916 | 1909 |
self.pubsub_publish_options_supported = True
|
| 1917 | 1910 |
else:
|
| 1918 | 1911 |
# Remove stored bookmarks accessible to everyone.
|
| 1919 |
- self.send_pb_purge(our_jid, 'storage:bookmarks')
|
|
| 1920 |
- self.send_pb_delete(our_jid, 'storage:bookmarks')
|
|
| 1912 |
+ self.get_module('Bookmarks').purge_pubsub_bookmarks()
|
|
| 1921 | 1913 |
|
| 1922 | 1914 |
if obj.fjid == hostname:
|
| 1923 | 1915 |
if nbxmpp.NS_SECLABEL in obj.features:
|
| ... | ... | @@ -2255,99 +2247,6 @@ class Connection(CommonConnection, ConnectionHandlers): |
| 2255 | 2247 |
return True
|
| 2256 | 2248 |
return False
|
| 2257 | 2249 |
|
| 2258 |
- def _request_bookmarks_xml(self):
|
|
| 2259 |
- if not app.account_is_connected(self.name):
|
|
| 2260 |
- return
|
|
| 2261 |
- iq = nbxmpp.Iq(typ='get')
|
|
| 2262 |
- iq2 = iq.addChild(name='query', namespace=nbxmpp.NS_PRIVATE)
|
|
| 2263 |
- iq2.addChild(name='storage', namespace='storage:bookmarks')
|
|
| 2264 |
- self.connection.send(iq)
|
|
| 2265 |
- app.log('bookmarks').info('Request Bookmarks (PrivateStorage)')
|
|
| 2266 |
- |
|
| 2267 |
- def _check_bookmarks_received(self):
|
|
| 2268 |
- if not self.bookmarks:
|
|
| 2269 |
- self._request_bookmarks_xml()
|
|
| 2270 |
- |
|
| 2271 |
- def get_bookmarks(self, storage_type=None):
|
|
| 2272 |
- """
|
|
| 2273 |
- Get Bookmarks from storage or PubSub if supported as described in XEP
|
|
| 2274 |
- 0048
|
|
| 2275 |
- |
|
| 2276 |
- storage_type can be set to xml to force request to xml storage
|
|
| 2277 |
- """
|
|
| 2278 |
- if not app.account_is_connected(self.name):
|
|
| 2279 |
- return
|
|
| 2280 |
- |
|
| 2281 |
- if storage_type != 'xml':
|
|
| 2282 |
- if self.pep_supported and self.pubsub_publish_options_supported:
|
|
| 2283 |
- self.send_pb_retrieve('', 'storage:bookmarks')
|
|
| 2284 |
- app.log('bookmarks').info('Request Bookmarks (PubSub)')
|
|
| 2285 |
- # some server (ejabberd) are so slow to answer that we
|
|
| 2286 |
- # request via XML if we don't get answer in the next 30 seconds
|
|
| 2287 |
- app.idlequeue.set_alarm(self._check_bookmarks_received, 30)
|
|
| 2288 |
- return
|
|
| 2289 |
- |
|
| 2290 |
- self._request_bookmarks_xml()
|
|
| 2291 |
- |
|
| 2292 |
- def get_bookmarks_storage_node(self):
|
|
| 2293 |
- NS_GAJIM_BM = 'xmpp:gajim.org/bookmarks'
|
|
| 2294 |
- storage_node = nbxmpp.Node(
|
|
| 2295 |
- tag='storage', attrs={'xmlns': 'storage:bookmarks'})
|
|
| 2296 |
- for bm in self.bookmarks:
|
|
| 2297 |
- conf_node = storage_node.addChild(name="conference")
|
|
| 2298 |
- conf_node.setAttr('jid', bm['jid'])
|
|
| 2299 |
- conf_node.setAttr('autojoin', bm['autojoin'])
|
|
| 2300 |
- conf_node.setAttr('name', bm['name'])
|
|
| 2301 |
- conf_node.setTag(
|
|
| 2302 |
- 'minimize', namespace=NS_GAJIM_BM).setData(bm['minimize'])
|
|
| 2303 |
- # Only add optional elements if not empty
|
|
| 2304 |
- # Note: need to handle both None and '' as empty
|
|
| 2305 |
- # thus shouldn't use "is not None"
|
|
| 2306 |
- if bm.get('nick', None):
|
|
| 2307 |
- conf_node.setTagData('nick', bm['nick'])
|
|
| 2308 |
- if bm.get('password', None):
|
|
| 2309 |
- conf_node.setTagData('password', bm['password'])
|
|
| 2310 |
- if bm.get('print_status', None):
|
|
| 2311 |
- conf_node.setTag(
|
|
| 2312 |
- 'print_status',
|
|
| 2313 |
- namespace=NS_GAJIM_BM).setData(bm['print_status'])
|
|
| 2314 |
- return storage_node
|
|
| 2315 |
- |
|
| 2316 |
- @staticmethod
|
|
| 2317 |
- def get_bookmark_publish_options():
|
|
| 2318 |
- options = nbxmpp.Node(nbxmpp.NS_DATA + ' x',
|
|
| 2319 |
- attrs={'type': 'submit'})
|
|
| 2320 |
- f = options.addChild('field',
|
|
| 2321 |
- attrs={'var': 'FORM_TYPE', 'type': 'hidden'})
|
|
| 2322 |
- f.setTagData('value', nbxmpp.NS_PUBSUB_PUBLISH_OPTIONS)
|
|
| 2323 |
- f = options.addChild('field', attrs={'var': 'pubsub#access_model'})
|
|
| 2324 |
- f.setTagData('value', 'whitelist')
|
|
| 2325 |
- return options
|
|
| 2326 |
- |
|
| 2327 |
- def store_bookmarks(self, storage_type=None):
|
|
| 2328 |
- """
|
|
| 2329 |
- Send bookmarks to the storage namespace or PubSub if supported
|
|
| 2330 |
- |
|
| 2331 |
- storage_type can be set to 'pubsub' or 'xml' so store in only one method
|
|
| 2332 |
- else it will be stored on both
|
|
| 2333 |
- """
|
|
| 2334 |
- if not app.account_is_connected(self.name):
|
|
| 2335 |
- return
|
|
| 2336 |
- |
|
| 2337 |
- storage_node = self.get_bookmarks_storage_node()
|
|
| 2338 |
- |
|
| 2339 |
- if storage_type != 'xml':
|
|
| 2340 |
- if self.pep_supported and self.pubsub_publish_options_supported:
|
|
| 2341 |
- self.send_pb_publish(
|
|
| 2342 |
- '', 'storage:bookmarks', storage_node, 'current',
|
|
| 2343 |
- options=self.get_bookmark_publish_options())
|
|
| 2344 |
- app.log('bookmarks').info('Bookmarks published (PubSub)')
|
|
| 2345 |
- |
|
| 2346 |
- if storage_type != 'pubsub':
|
|
| 2347 |
- iq = nbxmpp.Iq('set', nbxmpp.NS_PRIVATE, payload=storage_node)
|
|
| 2348 |
- self.connection.send(iq)
|
|
| 2349 |
- app.log('bookmarks').info('Bookmarks published (PrivateStorage)')
|
|
| 2350 |
- |
|
| 2351 | 2250 |
def get_roster_delimiter(self):
|
| 2352 | 2251 |
"""
|
| 2353 | 2252 |
Get roster group delimiter from storage as described in XEP 0083
|
| ... | ... | @@ -2614,11 +2513,7 @@ class Connection(CommonConnection, ConnectionHandlers): |
| 2614 | 2513 |
destroy.setAttr('jid', jid)
|
| 2615 | 2514 |
self.connection.send(iq)
|
| 2616 | 2515 |
i = 0
|
| 2617 |
- for bm in self.bookmarks:
|
|
| 2618 |
- if bm['jid'] == room_jid:
|
|
| 2619 |
- del self.bookmarks[i]
|
|
| 2620 |
- break
|
|
| 2621 |
- i += 1
|
|
| 2516 |
+ self.get_module('Bookmarks').bookmarks.pop(jid, None)
|
|
| 2622 | 2517 |
self.store_bookmarks()
|
| 2623 | 2518 |
|
| 2624 | 2519 |
def send_gc_status(self, nick, jid, show, status, auto=False):
|
| ... | ... | @@ -50,7 +50,6 @@ from gajim.common import jingle_xtls |
| 50 | 50 |
from gajim.common import configpaths
|
| 51 | 51 |
from gajim.common.caps_cache import muc_caps_cache
|
| 52 | 52 |
from gajim.common.commands import ConnectionCommands
|
| 53 |
-from gajim.common.pubsub import ConnectionPubSub
|
|
| 54 | 53 |
from gajim.common.protocol.caps import ConnectionCaps
|
| 55 | 54 |
from gajim.common.protocol.bytestream import ConnectionSocks5Bytestream
|
| 56 | 55 |
from gajim.common.protocol.bytestream import ConnectionIBBytestream
|
| ... | ... | @@ -339,15 +338,15 @@ class ConnectionPEP(object): |
| 339 | 338 |
if message:
|
| 340 | 339 |
i = item.addChild('text')
|
| 341 | 340 |
i.addData(message)
|
| 342 |
- self._pubsub_connection.send_pb_publish('', nbxmpp.NS_ACTIVITY, item,
|
|
| 343 |
- '0')
|
|
| 341 |
+ self.get_module('PubSub').send_pb_publish(
|
|
| 342 |
+ '', nbxmpp.NS_ACTIVITY, item, '0')
|
|
| 344 | 343 |
|
| 345 | 344 |
def retract_activity(self):
|
| 346 | 345 |
if not self.pep_supported:
|
| 347 | 346 |
return
|
| 348 | 347 |
self.send_activity(None)
|
| 349 | 348 |
# not all client support new XEP, so we still retract
|
| 350 |
- self._pubsub_connection.send_pb_retract('', nbxmpp.NS_ACTIVITY, '0')
|
|
| 349 |
+ self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_ACTIVITY, '0')
|
|
| 351 | 350 |
|
| 352 | 351 |
def send_mood(self, mood, message=None):
|
| 353 | 352 |
if self.connected == 1:
|
| ... | ... | @@ -363,14 +362,14 @@ class ConnectionPEP(object): |
| 363 | 362 |
if message:
|
| 364 | 363 |
i = item.addChild('text')
|
| 365 | 364 |
i.addData(message)
|
| 366 |
- self._pubsub_connection.send_pb_publish('', nbxmpp.NS_MOOD, item, '0')
|
|
| 365 |
+ self.get_module('PubSub').send_pb_publish('', nbxmpp.NS_MOOD, item, '0')
|
|
| 367 | 366 |
|
| 368 | 367 |
def retract_mood(self):
|
| 369 | 368 |
if not self.pep_supported:
|
| 370 | 369 |
return
|
| 371 | 370 |
self.send_mood(None)
|
| 372 | 371 |
# not all client support new XEP, so we still retract
|
| 373 |
- self._pubsub_connection.send_pb_retract('', nbxmpp.NS_MOOD, '0')
|
|
| 372 |
+ self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_MOOD, '0')
|
|
| 374 | 373 |
|
| 375 | 374 |
def send_tune(self, artist='', title='', source='', track=0, length=0,
|
| 376 | 375 |
items=None):
|
| ... | ... | @@ -399,14 +398,14 @@ class ConnectionPEP(object): |
| 399 | 398 |
i.addData(length)
|
| 400 | 399 |
if items:
|
| 401 | 400 |
item.addChild(payload=items)
|
| 402 |
- self._pubsub_connection.send_pb_publish('', nbxmpp.NS_TUNE, item, '0')
|
|
| 401 |
+ self.get_module('PubSub').send_pb_publish('', nbxmpp.NS_TUNE, item, '0')
|
|
| 403 | 402 |
|
| 404 | 403 |
def retract_tune(self):
|
| 405 | 404 |
if not self.pep_supported:
|
| 406 | 405 |
return
|
| 407 | 406 |
self.send_tune(None)
|
| 408 | 407 |
# not all client support new XEP, so we still retract
|
| 409 |
- self._pubsub_connection.send_pb_retract('', nbxmpp.NS_TUNE, '0')
|
|
| 408 |
+ self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_TUNE, '0')
|
|
| 410 | 409 |
|
| 411 | 410 |
def send_nickname(self, nick):
|
| 412 | 411 |
if self.connected == 1:
|
| ... | ... | @@ -418,13 +417,13 @@ class ConnectionPEP(object): |
| 418 | 417 |
return
|
| 419 | 418 |
item = nbxmpp.Node('nick', {'xmlns': nbxmpp.NS_NICK})
|
| 420 | 419 |
item.addData(nick)
|
| 421 |
- self._pubsub_connection.send_pb_publish('', nbxmpp.NS_NICK, item, '0')
|
|
| 420 |
+ self.get_module('PubSub').send_pb_publish('', nbxmpp.NS_NICK, item, '0')
|
|
| 422 | 421 |
|
| 423 | 422 |
def retract_nickname(self):
|
| 424 | 423 |
if not self.pep_supported:
|
| 425 | 424 |
return
|
| 426 | 425 |
|
| 427 |
- self._pubsub_connection.send_pb_retract('', nbxmpp.NS_NICK, '0')
|
|
| 426 |
+ self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_NICK, '0')
|
|
| 428 | 427 |
|
| 429 | 428 |
def send_location(self, info):
|
| 430 | 429 |
if self.connected == 1:
|
| ... | ... | @@ -439,14 +438,14 @@ class ConnectionPEP(object): |
| 439 | 438 |
if info.get(field, None):
|
| 440 | 439 |
i = item.addChild(field)
|
| 441 | 440 |
i.addData(info[field])
|
| 442 |
- self._pubsub_connection.send_pb_publish('', nbxmpp.NS_LOCATION, item, '0')
|
|
| 441 |
+ self.get_module('PubSub').send_pb_publish('', nbxmpp.NS_LOCATION, item, '0')
|
|
| 443 | 442 |
|
| 444 | 443 |
def retract_location(self):
|
| 445 | 444 |
if not self.pep_supported:
|
| 446 | 445 |
return
|
| 447 | 446 |
self.send_location({})
|
| 448 | 447 |
# not all client support new XEP, so we still retract
|
| 449 |
- self._pubsub_connection.send_pb_retract('', nbxmpp.NS_LOCATION, '0')
|
|
| 448 |
+ self.get_module('PubSub').send_pb_retract('', nbxmpp.NS_LOCATION, '0')
|
|
| 450 | 449 |
|
| 451 | 450 |
# basic connection handlers used here and in zeroconf
|
| 452 | 451 |
class ConnectionHandlersBase:
|
| ... | ... | @@ -915,7 +914,7 @@ class ConnectionHandlersBase: |
| 915 | 914 |
|
| 916 | 915 |
class ConnectionHandlers(ConnectionArchive313,
|
| 917 | 916 |
ConnectionSocks5Bytestream, ConnectionDisco,
|
| 918 |
-ConnectionCommands, ConnectionPubSub, ConnectionPEP, ConnectionCaps,
|
|
| 917 |
+ConnectionCommands, ConnectionPEP, ConnectionCaps,
|
|
| 919 | 918 |
ConnectionHandlersBase, ConnectionJingle, ConnectionIBBytestream,
|
| 920 | 919 |
ConnectionHTTPUpload):
|
| 921 | 920 |
def __init__(self):
|
| ... | ... | @@ -923,7 +922,6 @@ ConnectionHTTPUpload): |
| 923 | 922 |
ConnectionSocks5Bytestream.__init__(self)
|
| 924 | 923 |
ConnectionIBBytestream.__init__(self)
|
| 925 | 924 |
ConnectionCommands.__init__(self)
|
| 926 |
- ConnectionPubSub.__init__(self)
|
|
| 927 | 925 |
ConnectionPEP.__init__(self, account=self.name, dispatcher=self,
|
| 928 | 926 |
pubsub_connection=self)
|
| 929 | 927 |
ConnectionHTTPUpload.__init__(self)
|
| ... | ... | @@ -949,8 +947,6 @@ ConnectionHTTPUpload): |
| 949 | 947 |
|
| 950 | 948 |
self.privacy_default_list = None
|
| 951 | 949 |
|
| 952 |
- app.nec.register_incoming_event(PrivateStorageBookmarksReceivedEvent)
|
|
| 953 |
- app.nec.register_incoming_event(BookmarksReceivedEvent)
|
|
| 954 | 950 |
app.nec.register_incoming_event(StreamConflictReceivedEvent)
|
| 955 | 951 |
app.nec.register_incoming_event(StreamOtherHostReceivedEvent)
|
| 956 | 952 |
app.nec.register_incoming_event(MessageReceivedEvent)
|
| ... | ... | @@ -961,8 +957,6 @@ ConnectionHTTPUpload): |
| 961 | 957 |
|
| 962 | 958 |
app.ged.register_event_handler('roster-set-received',
|
| 963 | 959 |
ged.CORE, self._nec_roster_set_received)
|
| 964 |
- app.ged.register_event_handler('private-storage-bookmarks-received',
|
|
| 965 |
- ged.CORE, self._nec_private_storate_bookmarks_received)
|
|
| 966 | 960 |
app.ged.register_event_handler('roster-received', ged.CORE,
|
| 967 | 961 |
self._nec_roster_received)
|
| 968 | 962 |
app.ged.register_event_handler('iq-error-received', ged.CORE,
|
| ... | ... | @@ -988,12 +982,9 @@ ConnectionHTTPUpload): |
| 988 | 982 |
ConnectionHandlersBase.cleanup(self)
|
| 989 | 983 |
ConnectionCaps.cleanup(self)
|
| 990 | 984 |
ConnectionArchive313.cleanup(self)
|
| 991 |
- ConnectionPubSub.cleanup(self)
|
|
| 992 | 985 |
ConnectionHTTPUpload.cleanup(self)
|
| 993 | 986 |
app.ged.remove_event_handler('roster-set-received',
|
| 994 | 987 |
ged.CORE, self._nec_roster_set_received)
|
| 995 |
- app.ged.remove_event_handler('private-storage-bookmarks-received',
|
|
| 996 |
- ged.CORE, self._nec_private_storate_bookmarks_received)
|
|
| 997 | 988 |
app.ged.remove_event_handler('roster-received', ged.CORE,
|
| 998 | 989 |
self._nec_roster_received)
|
| 999 | 990 |
app.ged.remove_event_handler('iq-error-received', ged.CORE,
|
| ... | ... | @@ -1144,28 +1135,6 @@ ConnectionHTTPUpload): |
| 1144 | 1135 |
conn=self, stanza=obj.stanza))
|
| 1145 | 1136 |
return True
|
| 1146 | 1137 |
|
| 1147 |
- def _nec_private_storate_bookmarks_received(self, obj):
|
|
| 1148 |
- if obj.conn.name != self.name:
|
|
| 1149 |
- return
|
|
| 1150 |
- app.log('bookmarks').info('Received Bookmarks (PrivateStorage)')
|
|
| 1151 |
- resend_to_pubsub = False
|
|
| 1152 |
- bm_jids = [b['jid'] for b in self.bookmarks]
|
|
| 1153 |
- for bm in obj.bookmarks:
|
|
| 1154 |
- if bm['jid'] not in bm_jids:
|
|
| 1155 |
- self.bookmarks.append(bm)
|
|
| 1156 |
- # We got a bookmark that was not in pubsub
|
|
| 1157 |
- resend_to_pubsub = True
|
|
| 1158 |
- if resend_to_pubsub:
|
|
| 1159 |
- self.store_bookmarks('pubsub')
|
|
| 1160 |
- |
|
| 1161 |
- def _PrivateCB(self, con, iq_obj):
|
|
| 1162 |
- """
|
|
| 1163 |
- Private Data (XEP 048 and 049)
|
|
| 1164 |
- """
|
|
| 1165 |
- log.debug('PrivateCB')
|
|
| 1166 |
- app.nec.push_incoming_event(PrivateStorageReceivedEvent(None,
|
|
| 1167 |
- conn=self, stanza=iq_obj))
|
|
| 1168 |
- |
|
| 1169 | 1138 |
def _SecLabelCB(self, con, iq_obj):
|
| 1170 | 1139 |
"""
|
| 1171 | 1140 |
Security Label callback, used for catalogues.
|
| ... | ... | @@ -1540,8 +1509,8 @@ ConnectionHTTPUpload): |
| 1540 | 1509 |
# ask our VCard
|
| 1541 | 1510 |
self.get_module('VCardTemp').request_vcard()
|
| 1542 | 1511 |
|
| 1543 |
- # Get bookmarks from private namespace
|
|
| 1544 |
- self.get_bookmarks()
|
|
| 1512 |
+ # Get bookmarks
|
|
| 1513 |
+ self.get_module('Bookmarks').get_bookmarks()
|
|
| 1545 | 1514 |
|
| 1546 | 1515 |
# Get annotations from private namespace
|
| 1547 | 1516 |
self.get_module('Annotations').get_annotations()
|
| ... | ... | @@ -1645,7 +1614,6 @@ ConnectionHTTPUpload): |
| 1645 | 1614 |
nbxmpp.NS_MUC_OWNER)
|
| 1646 | 1615 |
con.RegisterHandler('iq', self._MucAdminCB, 'result',
|
| 1647 | 1616 |
nbxmpp.NS_MUC_ADMIN)
|
| 1648 |
- con.RegisterHandler('iq', self._PrivateCB, 'result', nbxmpp.NS_PRIVATE)
|
|
| 1649 | 1617 |
con.RegisterHandler('iq', self._SecLabelCB, 'result',
|
| 1650 | 1618 |
nbxmpp.NS_SECLABEL_CATALOG)
|
| 1651 | 1619 |
con.RegisterHandler('iq', self._CommandExecuteCB, 'set',
|
| ... | ... | @@ -1657,8 +1625,6 @@ ConnectionHTTPUpload): |
| 1657 | 1625 |
con.RegisterHandler('iq', self._PrivacySetCB, 'set', nbxmpp.NS_PRIVACY)
|
| 1658 | 1626 |
con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM_1)
|
| 1659 | 1627 |
con.RegisterHandler('iq', self._ArchiveCB, ns=nbxmpp.NS_MAM_2)
|
| 1660 |
- con.RegisterHandler('iq', self._PubSubCB, 'result')
|
|
| 1661 |
- con.RegisterHandler('iq', self._PubSubErrorCB, 'error')
|
|
| 1662 | 1628 |
con.RegisterHandler('iq', self._JingleCB, 'result')
|
| 1663 | 1629 |
con.RegisterHandler('iq', self._JingleCB, 'error')
|
| 1664 | 1630 |
con.RegisterHandler('iq', self._JingleCB, 'set', nbxmpp.NS_JINGLE)
|
| ... | ... | @@ -309,118 +309,6 @@ class MucAdminReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): |
| 309 | 309 |
self.users_dict[jid]['reason'] = reason
|
| 310 | 310 |
return True
|
| 311 | 311 |
|
| 312 |
-class PrivateStorageReceivedEvent(nec.NetworkIncomingEvent):
|
|
| 313 |
- name = 'private-storage-received'
|
|
| 314 |
- base_network_events = []
|
|
| 315 |
- |
|
| 316 |
- def generate(self):
|
|
| 317 |
- query = self.stanza.getTag('query')
|
|
| 318 |
- self.storage_node = query.getTag('storage')
|
|
| 319 |
- if self.storage_node:
|
|
| 320 |
- self.namespace = self.storage_node.getNamespace()
|
|
| 321 |
- return True
|
|
| 322 |
- |
|
| 323 |
- |
|
| 324 |
-class BookmarksHelper:
|
|
| 325 |
- def parse_bookmarks(self):
|
|
| 326 |
- self.bookmarks = []
|
|
| 327 |
- NS_GAJIM_BM = 'xmpp:gajim.org/bookmarks'
|
|
| 328 |
- confs = self.storage_node.getTags('conference')
|
|
| 329 |
- for conf in confs:
|
|
| 330 |
- autojoin_val = conf.getAttr('autojoin')
|
|
| 331 |
- if not autojoin_val: # not there (it's optional)
|
|
| 332 |
- autojoin_val = False
|
|
| 333 |
- minimize_val = conf.getTag('minimize', namespace=NS_GAJIM_BM)
|
|
| 334 |
- if not minimize_val: # not there, try old Gajim behaviour
|
|
| 335 |
- minimize_val = conf.getAttr('minimize')
|
|
| 336 |
- if not minimize_val: # not there (it's optional)
|
|
| 337 |
- minimize_val = False
|
|
| 338 |
- else:
|
|
| 339 |
- minimize_val = minimize_val.getData()
|
|
| 340 |
- |
|
| 341 |
- print_status = conf.getTag('print_status', namespace=NS_GAJIM_BM)
|
|
| 342 |
- if not print_status: # not there, try old Gajim behaviour
|
|
| 343 |
- print_status = conf.getTagData('print_status')
|
|
| 344 |
- if not print_status: # not there, try old Gajim behaviour
|
|
| 345 |
- print_status = conf.getTagData('show_status')
|
|
| 346 |
- else:
|
|
| 347 |
- print_status = print_status.getData()
|
|
| 348 |
- |
|
| 349 |
- try:
|
|
| 350 |
- jid = helpers.parse_jid(conf.getAttr('jid'))
|
|
| 351 |
- except helpers.InvalidFormat:
|
|
| 352 |
- log.warning('Invalid JID: %s, ignoring it'
|
|
| 353 |
- % conf.getAttr('jid'))
|
|
| 354 |
- continue
|
|
| 355 |
- |
|
| 356 |
- bm = {'name': conf.getAttr('name'),
|
|
| 357 |
- 'jid': jid,
|
|
| 358 |
- 'autojoin': autojoin_val,
|
|
| 359 |
- 'minimize': minimize_val,
|
|
| 360 |
- 'password': conf.getTagData('password'),
|
|
| 361 |
- 'nick': conf.getTagData('nick'),
|
|
| 362 |
- 'print_status': print_status}
|
|
| 363 |
- |
|
| 364 |
- bm_jids = [b['jid'] for b in self.bookmarks]
|
|
| 365 |
- if bm['jid'] not in bm_jids:
|
|
| 366 |
- self.bookmarks.append(bm)
|
|
| 367 |
- |
|
| 368 |
-class PrivateStorageBookmarksReceivedEvent(nec.NetworkIncomingEvent,
|
|
| 369 |
-BookmarksHelper):
|
|
| 370 |
- name = 'private-storage-bookmarks-received'
|
|
| 371 |
- base_network_events = ['private-storage-received']
|
|
| 372 |
- |
|
| 373 |
- def generate(self):
|
|
| 374 |
- self.conn = self.base_event.conn
|
|
| 375 |
- self.storage_node = self.base_event.storage_node
|
|
| 376 |
- if self.base_event.namespace != nbxmpp.NS_BOOKMARKS:
|
|
| 377 |
- return
|
|
| 378 |
- self.parse_bookmarks()
|
|
| 379 |
- return True
|
|
| 380 |
- |
|
| 381 |
-class BookmarksReceivedEvent(nec.NetworkIncomingEvent):
|
|
| 382 |
- name = 'bookmarks-received'
|
|
| 383 |
- base_network_events = ['private-storage-bookmarks-received',
|
|
| 384 |
- 'pubsub-bookmarks-received']
|
|
| 385 |
- |
|
| 386 |
- def generate(self):
|
|
| 387 |
- self.conn = self.base_event.conn
|
|
| 388 |
- self.bookmarks = self.base_event.bookmarks
|
|
| 389 |
- return True
|
|
| 390 |
- |
|
| 391 |
-class PubsubReceivedEvent(nec.NetworkIncomingEvent):
|
|
| 392 |
- name = 'pubsub-received'
|
|
| 393 |
- base_network_events = []
|
|
| 394 |
- |
|
| 395 |
- def generate(self):
|
|
| 396 |
- self.jid = self.stanza.getFrom()
|
|
| 397 |
- self.pubsub_node = self.stanza.getTag('pubsub')
|
|
| 398 |
- if not self.pubsub_node:
|
|
| 399 |
- return
|
|
| 400 |
- self.items_node = self.pubsub_node.getTag('items')
|
|
| 401 |
- if not self.items_node:
|
|
| 402 |
- return
|
|
| 403 |
- return True
|
|
| 404 |
- |
|
| 405 |
-class PubsubBookmarksReceivedEvent(nec.NetworkIncomingEvent, BookmarksHelper):
|
|
| 406 |
- name = 'pubsub-bookmarks-received'
|
|
| 407 |
- base_network_events = ['pubsub-received']
|
|
| 408 |
- |
|
| 409 |
- def generate(self):
|
|
| 410 |
- self.conn = self.base_event.conn
|
|
| 411 |
- self.item_node = self.base_event.items_node.getTag('item')
|
|
| 412 |
- if not self.item_node:
|
|
| 413 |
- return
|
|
| 414 |
- children = self.item_node.getChildren()
|
|
| 415 |
- if not children:
|
|
| 416 |
- return
|
|
| 417 |
- self.storage_node = children[0]
|
|
| 418 |
- ns = self.storage_node.getNamespace()
|
|
| 419 |
- if ns != nbxmpp.NS_BOOKMARKS:
|
|
| 420 |
- return
|
|
| 421 |
- self.parse_bookmarks()
|
|
| 422 |
- return True
|
|
| 423 |
- |
|
| 424 | 312 |
class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
| 425 | 313 |
name = 'iq-error-received'
|
| 426 | 314 |
base_network_events = []
|
| ... | ... | @@ -119,6 +119,11 @@ class RequestAvatar(IntEnum): |
| 119 | 119 |
ROOM = 1
|
| 120 | 120 |
USER = 2
|
| 121 | 121 |
|
| 122 |
+@unique
|
|
| 123 |
+class BookmarkStorageType(IntEnum):
|
|
| 124 |
+ PRIVATE = 0
|
|
| 125 |
+ PUBSUB = 1
|
|
| 126 |
+ |
|
| 122 | 127 |
SSLError = {
|
| 123 | 128 |
2: _("Unable to get issuer certificate"),
|
| 124 | 129 |
3: _("Unable to get certificate CRL"),
|
| 1 |
+# This file is part of Gajim.
|
|
| 2 |
+#
|
|
| 3 |
+# Gajim is free software; you can redistribute it and/or modify
|
|
| 4 |
+# it under the terms of the GNU General Public License as published
|
|
| 5 |
+# by the Free Software Foundation; version 3 only.
|
|
| 6 |
+#
|
|
| 7 |
+# Gajim is distributed in the hope that it will be useful,
|
|
| 8 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| 9 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| 10 |
+# GNU General Public License for more details.
|
|
| 11 |
+#
|
|
| 12 |
+# You should have received a copy of the GNU General Public License
|
|
| 13 |
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
| 14 |
+ |
|
| 15 |
+# XEP-0048: Bookmarks
|
|
| 16 |
+ |
|
| 17 |
+import logging
|
|
| 18 |
+ |
|
| 19 |
+import nbxmpp
|
|
| 20 |
+ |
|
| 21 |
+from gajim.common import app
|
|
| 22 |
+from gajim.common import helpers
|
|
| 23 |
+from gajim.common.const import BookmarkStorageType
|
|
| 24 |
+from gajim.common.nec import NetworkIncomingEvent
|
|
| 25 |
+ |
|
| 26 |
+log = logging.getLogger('gajim.c.m.bookmarks')
|
|
| 27 |
+ |
|
| 28 |
+ |
|
| 29 |
+class Bookmarks:
|
|
| 30 |
+ def __init__(self, con):
|
|
| 31 |
+ self._con = con
|
|
| 32 |
+ self._account = con.name
|
|
| 33 |
+ self.bookmarks = {}
|
|
| 34 |
+ |
|
| 35 |
+ self.handlers = []
|
|
| 36 |
+ |
|
| 37 |
+ def _pubsub_support(self):
|
|
| 38 |
+ return (self._con.pep_supported and
|
|
| 39 |
+ self._con.pubsub_publish_options_supported)
|
|
| 40 |
+ |
|
| 41 |
+ def get_bookmarks(self, storage_type=None):
|
|
| 42 |
+ if not app.account_is_connected(self._account):
|
|
| 43 |
+ return
|
|
| 44 |
+ |
|
| 45 |
+ if storage_type in (None, BookmarkStorageType.PUBSUB):
|
|
| 46 |
+ if self._pubsub_support():
|
|
| 47 |
+ self._request_pubsub_bookmarks()
|
|
| 48 |
+ else:
|
|
| 49 |
+ # Fallback, request private storage
|
|
| 50 |
+ self._request_private_bookmarks()
|
|
| 51 |
+ else:
|
|
| 52 |
+ log.info('Request Bookmarks (PrivateStorage)')
|
|
| 53 |
+ self._request_private_bookmarks()
|
|
| 54 |
+ |
|
| 55 |
+ def _request_pubsub_bookmarks(self):
|
|
| 56 |
+ log.info('Request Bookmarks (PubSub)')
|
|
| 57 |
+ self._con.get_module('PubSub').send_pb_retrieve(
|
|
| 58 |
+ '', 'storage:bookmarks',
|
|
| 59 |
+ cb=self._pubsub_bookmarks_received)
|
|
| 60 |
+ |
|
| 61 |
+ def _pubsub_bookmarks_received(self, conn, stanza):
|
|
| 62 |
+ if not nbxmpp.isResultNode(stanza):
|
|
| 63 |
+ log.info('No pubsub bookmarks: %s', stanza.getError())
|
|
| 64 |
+ # Fallback, request private storage
|
|
| 65 |
+ self._request_private_bookmarks()
|
|
| 66 |
+ return
|
|
| 67 |
+ |
|
| 68 |
+ log.info('Received Bookmarks (PubSub)')
|
|
| 69 |
+ self._parse_bookmarks(stanza)
|
|
| 70 |
+ self._request_private_bookmarks()
|
|
| 71 |
+ |
|
| 72 |
+ def _request_private_bookmarks(self):
|
|
| 73 |
+ if not app.account_is_connected(self._account):
|
|
| 74 |
+ return
|
|
| 75 |
+ |
|
| 76 |
+ iq = nbxmpp.Iq(typ='get')
|
|
| 77 |
+ query = iq.addChild(name='query', namespace=nbxmpp.NS_PRIVATE)
|
|
| 78 |
+ query.addChild(name='storage', namespace='storage:bookmarks')
|
|
| 79 |
+ log.info('Request Bookmarks (PrivateStorage)')
|
|
| 80 |
+ self._con.connection.SendAndCallForResponse(
|
|
| 81 |
+ iq, self._private_bookmarks_received)
|
|
| 82 |
+ |
|
| 83 |
+ def _private_bookmarks_received(self, stanza):
|
|
| 84 |
+ if not nbxmpp.isResultNode(stanza):
|
|
| 85 |
+ log.info('No private bookmarks: %s', stanza.getError())
|
|
| 86 |
+ else:
|
|
| 87 |
+ log.info('Received Bookmarks (PrivateStorage)')
|
|
| 88 |
+ merged = self._parse_bookmarks(stanza, check_merge=True)
|
|
| 89 |
+ if merged:
|
|
| 90 |
+ log.info('Merge PrivateStorage with PubSub')
|
|
| 91 |
+ self.store_bookmarks(BookmarkStorageType.PUBSUB)
|
|
| 92 |
+ self.auto_join_bookmarks()
|
|
| 93 |
+ app.nec.push_incoming_event(BookmarksReceivedEvent(
|
|
| 94 |
+ None, account=self._account))
|
|
| 95 |
+ |
|
| 96 |
+ @staticmethod
|
|
| 97 |
+ def _get_storage_node(stanza):
|
|
| 98 |
+ node = stanza.getTag('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 99 |
+ if node is None:
|
|
| 100 |
+ node = stanza.getTag('event', namespace=nbxmpp.NS_PUBSUB_EVENT)
|
|
| 101 |
+ if node is None:
|
|
| 102 |
+ # Private Storage
|
|
| 103 |
+ query = stanza.getQuery()
|
|
| 104 |
+ if query is None:
|
|
| 105 |
+ return
|
|
| 106 |
+ storage = query.getTag('storage',
|
|
| 107 |
+ namespace=nbxmpp.NS_BOOKMARKS)
|
|
| 108 |
+ if storage is None:
|
|
| 109 |
+ return
|
|
| 110 |
+ return storage
|
|
| 111 |
+ |
|
| 112 |
+ items_node = node.getTag('items')
|
|
| 113 |
+ if items_node is None:
|
|
| 114 |
+ return
|
|
| 115 |
+ if items_node.getAttr('node') != nbxmpp.NS_BOOKMARKS:
|
|
| 116 |
+ return
|
|
| 117 |
+ |
|
| 118 |
+ item_node = items_node.getTag('item')
|
|
| 119 |
+ if item_node is None:
|
|
| 120 |
+ return
|
|
| 121 |
+ |
|
| 122 |
+ storage = item_node.getTag('storage', namespace=nbxmpp.NS_BOOKMARKS)
|
|
| 123 |
+ if storage is None:
|
|
| 124 |
+ return
|
|
| 125 |
+ return storage
|
|
| 126 |
+ |
|
| 127 |
+ def _parse_bookmarks(self, stanza, check_merge=False):
|
|
| 128 |
+ merged = False
|
|
| 129 |
+ storage = self._get_storage_node(stanza)
|
|
| 130 |
+ if storage is None:
|
|
| 131 |
+ return
|
|
| 132 |
+ |
|
| 133 |
+ NS_GAJIM_BM = 'xmpp:gajim.org/bookmarks'
|
|
| 134 |
+ confs = storage.getTags('conference')
|
|
| 135 |
+ for conf in confs:
|
|
| 136 |
+ autojoin_val = conf.getAttr('autojoin')
|
|
| 137 |
+ if not autojoin_val: # not there (it's optional)
|
|
| 138 |
+ autojoin_val = False
|
|
| 139 |
+ minimize_val = conf.getTag('minimize', namespace=NS_GAJIM_BM)
|
|
| 140 |
+ if not minimize_val: # not there, try old Gajim behaviour
|
|
| 141 |
+ minimize_val = conf.getAttr('minimize')
|
|
| 142 |
+ if not minimize_val: # not there (it's optional)
|
|
| 143 |
+ minimize_val = False
|
|
| 144 |
+ else:
|
|
| 145 |
+ minimize_val = minimize_val.getData()
|
|
| 146 |
+ |
|
| 147 |
+ print_status = conf.getTag('print_status', namespace=NS_GAJIM_BM)
|
|
| 148 |
+ if not print_status: # not there, try old Gajim behaviour
|
|
| 149 |
+ print_status = conf.getTagData('print_status')
|
|
| 150 |
+ if not print_status: # not there, try old Gajim behaviour
|
|
| 151 |
+ print_status = conf.getTagData('show_status')
|
|
| 152 |
+ else:
|
|
| 153 |
+ print_status = print_status.getData()
|
|
| 154 |
+ |
|
| 155 |
+ try:
|
|
| 156 |
+ jid = helpers.parse_jid(conf.getAttr('jid'))
|
|
| 157 |
+ except helpers.InvalidFormat:
|
|
| 158 |
+ log.warning('Invalid JID: %s, ignoring it'
|
|
| 159 |
+ % conf.getAttr('jid'))
|
|
| 160 |
+ continue
|
|
| 161 |
+ |
|
| 162 |
+ if check_merge:
|
|
| 163 |
+ if jid not in self.bookmarks:
|
|
| 164 |
+ merged = True
|
|
| 165 |
+ |
|
| 166 |
+ log.debug('Found Bookmark: %s', jid)
|
|
| 167 |
+ self.bookmarks[jid] = {
|
|
| 168 |
+ 'name': conf.getAttr('name'),
|
|
| 169 |
+ 'autojoin': autojoin_val,
|
|
| 170 |
+ 'minimize': minimize_val,
|
|
| 171 |
+ 'password': conf.getTagData('password'),
|
|
| 172 |
+ 'nick': conf.getTagData('nick'),
|
|
| 173 |
+ 'print_status': print_status}
|
|
| 174 |
+ |
|
| 175 |
+ return merged
|
|
| 176 |
+ |
|
| 177 |
+ def _build_storage_node(self):
|
|
| 178 |
+ NS_GAJIM_BM = 'xmpp:gajim.org/bookmarks'
|
|
| 179 |
+ storage_node = nbxmpp.Node(
|
|
| 180 |
+ tag='storage', attrs={'xmlns': 'storage:bookmarks'})
|
|
| 181 |
+ for jid, bm in self.bookmarks.items():
|
|
| 182 |
+ conf_node = storage_node.addChild(name="conference")
|
|
| 183 |
+ conf_node.setAttr('jid', jid)
|
|
| 184 |
+ conf_node.setAttr('autojoin', bm['autojoin'])
|
|
| 185 |
+ conf_node.setAttr('name', bm['name'])
|
|
| 186 |
+ conf_node.setTag(
|
|
| 187 |
+ 'minimize', namespace=NS_GAJIM_BM).setData(bm['minimize'])
|
|
| 188 |
+ # Only add optional elements if not empty
|
|
| 189 |
+ # Note: need to handle both None and '' as empty
|
|
| 190 |
+ # thus shouldn't use "is not None"
|
|
| 191 |
+ if bm.get('nick', None):
|
|
| 192 |
+ conf_node.setTagData('nick', bm['nick'])
|
|
| 193 |
+ if bm.get('password', None):
|
|
| 194 |
+ conf_node.setTagData('password', bm['password'])
|
|
| 195 |
+ if bm.get('print_status', None):
|
|
| 196 |
+ conf_node.setTag(
|
|
| 197 |
+ 'print_status',
|
|
| 198 |
+ namespace=NS_GAJIM_BM).setData(bm['print_status'])
|
|
| 199 |
+ return storage_node
|
|
| 200 |
+ |
|
| 201 |
+ @staticmethod
|
|
| 202 |
+ def get_bookmark_publish_options():
|
|
| 203 |
+ options = nbxmpp.Node(nbxmpp.NS_DATA + ' x',
|
|
| 204 |
+ attrs={'type': 'submit'})
|
|
| 205 |
+ f = options.addChild('field',
|
|
| 206 |
+ attrs={'var': 'FORM_TYPE', 'type': 'hidden'})
|
|
| 207 |
+ f.setTagData('value', nbxmpp.NS_PUBSUB_PUBLISH_OPTIONS)
|
|
| 208 |
+ f = options.addChild('field', attrs={'var': 'pubsub#access_model'})
|
|
| 209 |
+ f.setTagData('value', 'whitelist')
|
|
| 210 |
+ return options
|
|
| 211 |
+ |
|
| 212 |
+ def store_bookmarks(self, storage_type=None):
|
|
| 213 |
+ if not app.account_is_connected(self._account):
|
|
| 214 |
+ return
|
|
| 215 |
+ |
|
| 216 |
+ storage_node = self._build_storage_node()
|
|
| 217 |
+ |
|
| 218 |
+ if storage_type is None:
|
|
| 219 |
+ if self._pubsub_support():
|
|
| 220 |
+ self._pubsub_store(storage_node)
|
|
| 221 |
+ else:
|
|
| 222 |
+ self._private_store(storage_node)
|
|
| 223 |
+ elif storage_type == BookmarkStorageType.PUBSUB:
|
|
| 224 |
+ if self._pubsub_support():
|
|
| 225 |
+ self._pubsub_store(storage_node)
|
|
| 226 |
+ elif storage_type == BookmarkStorageType.PRIVATE:
|
|
| 227 |
+ self._private_store(storage_node)
|
|
| 228 |
+ |
|
| 229 |
+ def _pubsub_store(self, storage_node):
|
|
| 230 |
+ self._con.get_module('PubSub').send_pb_publish(
|
|
| 231 |
+ '', 'storage:bookmarks', storage_node, 'current',
|
|
| 232 |
+ options=self.get_bookmark_publish_options(),
|
|
| 233 |
+ cb=self._pubsub_store_result)
|
|
| 234 |
+ log.info('Publish Bookmarks (PubSub)')
|
|
| 235 |
+ |
|
| 236 |
+ def _private_store(self, storage_node):
|
|
| 237 |
+ iq = nbxmpp.Iq('set', nbxmpp.NS_PRIVATE, payload=storage_node)
|
|
| 238 |
+ log.info('Publish Bookmarks (PrivateStorage)')
|
|
| 239 |
+ self._con.connection.SendAndCallForResponse(
|
|
| 240 |
+ iq, self._private_store_result)
|
|
| 241 |
+ |
|
| 242 |
+ def _pubsub_store_result(self, conn, stanza):
|
|
| 243 |
+ if not nbxmpp.isResultNode(stanza):
|
|
| 244 |
+ log.error('Error: %s', stanza.getError())
|
|
| 245 |
+ return
|
|
| 246 |
+ |
|
| 247 |
+ def _private_store_result(self, stanza):
|
|
| 248 |
+ if not nbxmpp.isResultNode(stanza):
|
|
| 249 |
+ log.error('Error: %s', stanza.getError())
|
|
| 250 |
+ return
|
|
| 251 |
+ |
|
| 252 |
+ def auto_join_bookmarks(self):
|
|
| 253 |
+ if app.is_invisible(self._account):
|
|
| 254 |
+ return
|
|
| 255 |
+ for jid, bm in self.bookmarks.items():
|
|
| 256 |
+ if bm['autojoin'] in ('1', 'true'):
|
|
| 257 |
+ # Only join non-opened groupchats. Opened one are already
|
|
| 258 |
+ # auto-joined on re-connection
|
|
| 259 |
+ if jid not in app.gc_connected[self._account]:
|
|
| 260 |
+ # we are not already connected
|
|
| 261 |
+ minimize = bm['minimize'] in ('1', 'true')
|
|
| 262 |
+ app.interface.join_gc_room(
|
|
| 263 |
+ self._account, jid, bm['nick'],
|
|
| 264 |
+ bm['password'], minimize=minimize)
|
|
| 265 |
+ |
|
| 266 |
+ def add_bookmark(self, name, jid, autojoin,
|
|
| 267 |
+ minimize, password, nick):
|
|
| 268 |
+ self.bookmarks[jid] = {
|
|
| 269 |
+ 'name': name,
|
|
| 270 |
+ 'autojoin': autojoin,
|
|
| 271 |
+ 'minimize': minimize,
|
|
| 272 |
+ 'password': password,
|
|
| 273 |
+ 'nick': nick}
|
|
| 274 |
+ |
|
| 275 |
+ self.store_bookmarks()
|
|
| 276 |
+ app.nec.push_incoming_event(BookmarksReceivedEvent(
|
|
| 277 |
+ None, account=self._account))
|
|
| 278 |
+ |
|
| 279 |
+ def get_name_from_bookmark(self, jid):
|
|
| 280 |
+ try:
|
|
| 281 |
+ return self.bookmarks[jid]['name']
|
|
| 282 |
+ except KeyError:
|
|
| 283 |
+ return jid.split('@')[0]
|
|
| 284 |
+ |
|
| 285 |
+ def purge_pubsub_bookmarks(self):
|
|
| 286 |
+ log.info('Purge/Delete Bookmarks on PubSub, '
|
|
| 287 |
+ 'because publish options are not available')
|
|
| 288 |
+ self._con.get_module('PubSub').send_pb_purge('', 'storage:bookmarks')
|
|
| 289 |
+ self._con.get_module('PubSub').send_pb_delete('', 'storage:bookmarks')
|
|
| 290 |
+ |
|
| 291 |
+ |
|
| 292 |
+class BookmarksReceivedEvent(NetworkIncomingEvent):
|
|
| 293 |
+ name = 'bookmarks-received'
|
|
| 294 |
+ base_network_events = []
|
| 1 |
+# Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
|
|
| 2 |
+# Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
|
|
| 3 |
+# Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
|
| 4 |
+# Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
|
|
| 5 |
+# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
|
|
| 6 |
+#
|
|
| 7 |
+# This file is part of Gajim.
|
|
| 8 |
+#
|
|
| 9 |
+# Gajim is free software; you can redistribute it and/or modify
|
|
| 10 |
+# it under the terms of the GNU General Public License as published
|
|
| 11 |
+# by the Free Software Foundation; version 3 only.
|
|
| 12 |
+#
|
|
| 13 |
+# Gajim is distributed in the hope that it will be useful,
|
|
| 14 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| 15 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| 16 |
+# GNU General Public License for more details.
|
|
| 17 |
+#
|
|
| 18 |
+# You should have received a copy of the GNU General Public License
|
|
| 19 |
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
| 20 |
+ |
|
| 21 |
+# XEP-0060: Publish-Subscribe
|
|
| 22 |
+ |
|
| 23 |
+import logging
|
|
| 24 |
+ |
|
| 25 |
+import nbxmpp
|
|
| 26 |
+ |
|
| 27 |
+from gajim.common import app
|
|
| 28 |
+ |
|
| 29 |
+log = logging.getLogger('gajim.c.m.pubsub')
|
|
| 30 |
+ |
|
| 31 |
+ |
|
| 32 |
+class PubSub:
|
|
| 33 |
+ def __init__(self, con):
|
|
| 34 |
+ self._con = con
|
|
| 35 |
+ self._account = con.name
|
|
| 36 |
+ |
|
| 37 |
+ self.handlers = []
|
|
| 38 |
+ |
|
| 39 |
+ def send_pb_subscription_query(self, jid, cb, **kwargs):
|
|
| 40 |
+ if not app.account_is_connected(self._account):
|
|
| 41 |
+ return
|
|
| 42 |
+ |
|
| 43 |
+ query = nbxmpp.Iq('get', to=jid)
|
|
| 44 |
+ pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 45 |
+ pb.addChild('subscriptions')
|
|
| 46 |
+ |
|
| 47 |
+ self._con.connection.SendAndCallForResponse(query, cb, kwargs)
|
|
| 48 |
+ |
|
| 49 |
+ def send_pb_subscribe(self, jid, node, cb, **kwargs):
|
|
| 50 |
+ if not app.account_is_connected(self._account):
|
|
| 51 |
+ return
|
|
| 52 |
+ |
|
| 53 |
+ our_jid = app.get_jid_from_account(self._account)
|
|
| 54 |
+ query = nbxmpp.Iq('set', to=jid)
|
|
| 55 |
+ pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 56 |
+ pb.addChild('subscribe', {'node': node, 'jid': our_jid})
|
|
| 57 |
+ |
|
| 58 |
+ self._con.connection.SendAndCallForResponse(query, cb, kwargs)
|
|
| 59 |
+ |
|
| 60 |
+ def send_pb_unsubscribe(self, jid, node, cb, **kwargs):
|
|
| 61 |
+ if not app.account_is_connected(self._account):
|
|
| 62 |
+ return
|
|
| 63 |
+ |
|
| 64 |
+ our_jid = app.get_jid_from_account(self._account)
|
|
| 65 |
+ query = nbxmpp.Iq('set', to=jid)
|
|
| 66 |
+ pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 67 |
+ pb.addChild('unsubscribe', {'node': node, 'jid': our_jid})
|
|
| 68 |
+ |
|
| 69 |
+ self._con.connection.SendAndCallForResponse(query, cb, kwargs)
|
|
| 70 |
+ |
|
| 71 |
+ def send_pb_publish(self, jid, node, item,
|
|
| 72 |
+ id_=None, options=None, cb=None, **kwargs):
|
|
| 73 |
+ if not app.account_is_connected(self._account):
|
|
| 74 |
+ return
|
|
| 75 |
+ |
|
| 76 |
+ if cb is None:
|
|
| 77 |
+ cb = self._default_callback
|
|
| 78 |
+ |
|
| 79 |
+ query = nbxmpp.Iq('set', to=jid)
|
|
| 80 |
+ e = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 81 |
+ p = e.addChild('publish', {'node': node})
|
|
| 82 |
+ attrs = {}
|
|
| 83 |
+ if id_:
|
|
| 84 |
+ attrs = {'id': id_}
|
|
| 85 |
+ p.addChild('item', attrs, [item])
|
|
| 86 |
+ if options:
|
|
| 87 |
+ p = e.addChild('publish-options')
|
|
| 88 |
+ p.addChild(node=options)
|
|
| 89 |
+ |
|
| 90 |
+ self._con.connection.SendAndCallForResponse(query, cb, kwargs)
|
|
| 91 |
+ |
|
| 92 |
+ @staticmethod
|
|
| 93 |
+ def get_pb_retrieve_iq(jid, node, item_id=None):
|
|
| 94 |
+ """
|
|
| 95 |
+ Get IQ to query items from a node
|
|
| 96 |
+ """
|
|
| 97 |
+ query = nbxmpp.Iq('get', to=jid)
|
|
| 98 |
+ r = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 99 |
+ r = r.addChild('items', {'node': node})
|
|
| 100 |
+ if item_id is not None:
|
|
| 101 |
+ r.addChild('item', {'id': item_id})
|
|
| 102 |
+ return query
|
|
| 103 |
+ |
|
| 104 |
+ def send_pb_retrieve(self, jid, node, item_id=None, cb=None, **kwargs):
|
|
| 105 |
+ """
|
|
| 106 |
+ Get items from a node
|
|
| 107 |
+ """
|
|
| 108 |
+ if not app.account_is_connected(self._account):
|
|
| 109 |
+ return
|
|
| 110 |
+ |
|
| 111 |
+ if cb is None:
|
|
| 112 |
+ cb = self._default_callback
|
|
| 113 |
+ |
|
| 114 |
+ query = self.get_pb_retrieve_iq(jid, node, item_id)
|
|
| 115 |
+ |
|
| 116 |
+ self._con.connection.SendAndCallForResponse(query, cb, kwargs)
|
|
| 117 |
+ |
|
| 118 |
+ def send_pb_retract(self, jid, node, id_, cb=None, **kwargs):
|
|
| 119 |
+ """
|
|
| 120 |
+ Delete item from a node
|
|
| 121 |
+ """
|
|
| 122 |
+ if not app.account_is_connected(self._account):
|
|
| 123 |
+ return
|
|
| 124 |
+ |
|
| 125 |
+ if cb is None:
|
|
| 126 |
+ cb = self._default_callback
|
|
| 127 |
+ |
|
| 128 |
+ query = nbxmpp.Iq('set', to=jid)
|
|
| 129 |
+ r = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 130 |
+ r = r.addChild('retract', {'node': node, 'notify': '1'})
|
|
| 131 |
+ r = r.addChild('item', {'id': id_})
|
|
| 132 |
+ |
|
| 133 |
+ self._con.connection.SendAndCallForResponse(query, cb, kwargs)
|
|
| 134 |
+ |
|
| 135 |
+ def send_pb_purge(self, jid, node, cb=None, **kwargs):
|
|
| 136 |
+ """
|
|
| 137 |
+ Purge node: Remove all items
|
|
| 138 |
+ """
|
|
| 139 |
+ if not app.account_is_connected(self._account):
|
|
| 140 |
+ return
|
|
| 141 |
+ |
|
| 142 |
+ if cb is None:
|
|
| 143 |
+ cb = self._default_callback
|
|
| 144 |
+ |
|
| 145 |
+ query = nbxmpp.Iq('set', to=jid)
|
|
| 146 |
+ d = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
|
|
| 147 |
+ d = d.addChild('purge', {'node': node})
|
|
| 148 |
+ |
|
| 149 |
+ self._con.connection.SendAndCallForResponse(query, cb, kwargs)
|
|
| 150 |
+ |
|
| 151 |
+ def send_pb_delete(self, jid, node, on_ok=None, on_fail=None):
|
|
| 152 |
+ """
|
|
| 153 |
+ Delete node
|
|
| 154 |
+ """
|
|
| 155 |
+ if not app.account_is_connected(self._account):
|
|
| 156 |
+ return
|
|
| 157 |
+ query = nbxmpp.Iq('set', to=jid)
|
|
| 158 |
+ d = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
|
|
| 159 |
+ d = d.addChild('delete', {'node': node})
|
|
| 160 |
+ |
|
| 161 |
+ def response(con, resp, jid, node):
|
|
| 162 |
+ if resp.getType() == 'result' and on_ok:
|
|
| 163 |
+ on_ok(jid, node)
|
|
| 164 |
+ elif on_fail:
|
|
| 165 |
+ msg = resp.getErrorMsg()
|
|
| 166 |
+ on_fail(jid, node, msg)
|
|
| 167 |
+ |
|
| 168 |
+ self._con.connection.SendAndCallForResponse(
|
|
| 169 |
+ query, response, {'jid': jid, 'node': node})
|
|
| 170 |
+ |
|
| 171 |
+ def send_pb_create(self, jid, node, cb,
|
|
| 172 |
+ configure=False, configure_form=None):
|
|
| 173 |
+ """
|
|
| 174 |
+ Create a new node
|
|
| 175 |
+ """
|
|
| 176 |
+ if not app.account_is_connected(self._account):
|
|
| 177 |
+ return
|
|
| 178 |
+ query = nbxmpp.Iq('set', to=jid)
|
|
| 179 |
+ c = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 180 |
+ c = c.addChild('create', {'node': node})
|
|
| 181 |
+ if configure:
|
|
| 182 |
+ conf = c.addChild('configure')
|
|
| 183 |
+ if configure_form is not None:
|
|
| 184 |
+ conf.addChild(node=configure_form)
|
|
| 185 |
+ |
|
| 186 |
+ self._con.connection.SendAndCallForResponse(query, cb)
|
|
| 187 |
+ |
|
| 188 |
+ def send_pb_configure(self, jid, node, form, cb=None):
|
|
| 189 |
+ if not app.account_is_connected(self._account):
|
|
| 190 |
+ return
|
|
| 191 |
+ |
|
| 192 |
+ if cb is None:
|
|
| 193 |
+ cb = self._default_callback
|
|
| 194 |
+ |
|
| 195 |
+ query = nbxmpp.Iq('set', to=jid)
|
|
| 196 |
+ c = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
|
|
| 197 |
+ c = c.addChild('configure', {'node': node})
|
|
| 198 |
+ c.addChild(node=form)
|
|
| 199 |
+ |
|
| 200 |
+ self._con.connection.SendAndCallForResponse(query, cb)
|
|
| 201 |
+ |
|
| 202 |
+ def request_pb_configuration(self, jid, node, cb=None):
|
|
| 203 |
+ if not app.account_is_connected(self._account):
|
|
| 204 |
+ return
|
|
| 205 |
+ |
|
| 206 |
+ if cb is None:
|
|
| 207 |
+ cb = self._default_callback
|
|
| 208 |
+ |
|
| 209 |
+ query = nbxmpp.Iq('get', to=jid)
|
|
| 210 |
+ e = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
|
|
| 211 |
+ e = e.addChild('configure', {'node': node})
|
|
| 212 |
+ |
|
| 213 |
+ self._con.connection.SendAndCallForResponse(query, cb)
|
|
| 214 |
+ |
|
| 215 |
+ def _default_callback(self, conn, stanza, *args, **kwargs):
|
|
| 216 |
+ if not nbxmpp.isResultNode(stanza):
|
|
| 217 |
+ log.warning('Error: %s', stanza.getError())
|
| 1 |
+# This file is part of Gajim.
|
|
| 2 |
+#
|
|
| 3 |
+# Gajim is free software; you can redistribute it and/or modify
|
|
| 4 |
+# it under the terms of the GNU General Public License as published
|
|
| 5 |
+# by the Free Software Foundation; version 3 only.
|
|
| 6 |
+#
|
|
| 7 |
+# Gajim is distributed in the hope that it will be useful,
|
|
| 8 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| 9 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| 10 |
+# GNU General Public License for more details.
|
|
| 11 |
+#
|
|
| 12 |
+# You should have received a copy of the GNU General Public License
|
|
| 13 |
+# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
| 14 |
+ |
|
| 15 |
+# XEP-0084: User Avatar
|
|
| 16 |
+ |
|
| 17 |
+import logging
|
|
| 18 |
+import base64
|
|
| 19 |
+import binascii
|
|
| 20 |
+ |
|
| 21 |
+import nbxmpp
|
|
| 22 |
+ |
|
| 23 |
+from gajim.common import app
|
|
| 24 |
+from gajim.common.exceptions import StanzaMalformed
|
|
| 25 |
+ |
|
| 26 |
+log = logging.getLogger('gajim.c.m.user_avatar')
|
|
| 27 |
+ |
|
| 28 |
+ |
|
| 29 |
+class UserAvatar:
|
|
| 30 |
+ def __init__(self, con):
|
|
| 31 |
+ self._con = con
|
|
| 32 |
+ self._account = con.name
|
|
| 33 |
+ |
|
| 34 |
+ self.handlers = []
|
|
| 35 |
+ |
|
| 36 |
+ def get_pubsub_avatar(self, jid, item_id):
|
|
| 37 |
+ log.info('Request: %s %s', jid, item_id)
|
|
| 38 |
+ self._con.get_module('PubSub').send_pb_retrieve(
|
|
| 39 |
+ jid, 'urn:xmpp:avatar:data', item_id, self._avatar_received)
|
|
| 40 |
+ |
|
| 41 |
+ def _validate_avatar_node(self, stanza):
|
|
| 42 |
+ jid = stanza.getFrom()
|
|
| 43 |
+ if jid is None:
|
|
| 44 |
+ jid = self._con.get_own_jid().getStripped()
|
|
| 45 |
+ else:
|
|
| 46 |
+ jid = jid.getStripped()
|
|
| 47 |
+ |
|
| 48 |
+ if nbxmpp.isErrorNode(stanza):
|
|
| 49 |
+ raise StanzaMalformed(stanza.getErrorMsg())
|
|
| 50 |
+ |
|
| 51 |
+ pubsub_node = stanza.getTag('pubsub')
|
|
| 52 |
+ if pubsub_node is None:
|
|
| 53 |
+ raise StanzaMalformed('No pubsub node', stanza)
|
|
| 54 |
+ |
|
| 55 |
+ items_node = pubsub_node.getTag('items')
|
|
| 56 |
+ if items_node is None:
|
|
| 57 |
+ raise StanzaMalformed('No items node', stanza)
|
|
| 58 |
+ |
|
| 59 |
+ if items_node.getAttr('node') != 'urn:xmpp:avatar:data':
|
|
| 60 |
+ raise StanzaMalformed('Wrong namespace', stanza)
|
|
| 61 |
+ |
|
| 62 |
+ item = items_node.getTag('item')
|
|
| 63 |
+ if item is None:
|
|
| 64 |
+ raise StanzaMalformed('No item node', stanza)
|
|
| 65 |
+ |
|
| 66 |
+ sha = item.getAttr('id')
|
|
| 67 |
+ data_tag = item.getTag('data', namespace='urn:xmpp:avatar:data')
|
|
| 68 |
+ if sha is None or data_tag is None:
|
|
| 69 |
+ raise StanzaMalformed('No id attr or data node found', stanza)
|
|
| 70 |
+ |
|
| 71 |
+ data = data_tag.getData()
|
|
| 72 |
+ if data is None:
|
|
| 73 |
+ raise StanzaMalformed('Data node empty', stanza)
|
|
| 74 |
+ |
|
| 75 |
+ data = base64.b64decode(data.encode('utf-8'))
|
|
| 76 |
+ |
|
| 77 |
+ return jid, sha, data
|
|
| 78 |
+ |
|
| 79 |
+ def _avatar_received(self, conn, stanza):
|
|
| 80 |
+ try:
|
|
| 81 |
+ jid, sha, data = self._validate_avatar_node(stanza)
|
|
| 82 |
+ except (StanzaMalformed, binascii.Error) as error:
|
|
| 83 |
+ log.warning('Error: %s %s', stanza.getFrom(), error)
|
|
| 84 |
+ return
|
|
| 85 |
+ |
|
| 86 |
+ log.info('Received: %s %s', jid, sha)
|
|
| 87 |
+ app.interface.save_avatar(data)
|
|
| 88 |
+ |
|
| 89 |
+ if self._con.get_own_jid().bareMatch(jid):
|
|
| 90 |
+ app.config.set_per('accounts', self._account, 'avatar_sha', sha)
|
|
| 91 |
+ else:
|
|
| 92 |
+ own_jid = self._con.get_own_jid().getStripped()
|
|
| 93 |
+ app.logger.set_avatar_sha(own_jid, jid, sha)
|
|
| 94 |
+ app.contacts.set_avatar(self._account, jid, sha)
|
|
| 95 |
+ |
|
| 96 |
+ app.interface.update_avatar(self._account, jid)
|
| ... | ... | @@ -485,7 +485,8 @@ class AvatarNotificationPEP(AbstractPEP): |
| 485 | 485 |
for item in items.getTags('item'):
|
| 486 | 486 |
metadata = item.getTag('metadata')
|
| 487 | 487 |
if metadata is None:
|
| 488 |
- app.log('avatar').warning('Invalid avatar stanza:\n%s', items)
|
|
| 488 |
+ app.log('c.m.user_avatar').warning(
|
|
| 489 |
+ 'Invalid avatar stanza:\n%s', items)
|
|
| 489 | 490 |
break
|
| 490 | 491 |
info = item.getTag('metadata').getTag('info')
|
| 491 | 492 |
if info is not None:
|
| ... | ... | @@ -498,23 +499,22 @@ class AvatarNotificationPEP(AbstractPEP): |
| 498 | 499 |
con = app.connections[account]
|
| 499 | 500 |
if self.avatar is None:
|
| 500 | 501 |
# Remove avatar
|
| 501 |
- app.log('avatar').info('Remove (Pubsub): %s', jid)
|
|
| 502 |
+ app.log('c.m.user_avatar').info('Remove: %s', jid)
|
|
| 502 | 503 |
app.contacts.set_avatar(account, jid, None)
|
| 503 | 504 |
own_jid = con.get_own_jid().getStripped()
|
| 504 | 505 |
app.logger.set_avatar_sha(own_jid, jid, None)
|
| 505 | 506 |
app.interface.update_avatar(account, jid)
|
| 506 | 507 |
else:
|
| 507 | 508 |
sha = app.contacts.get_avatar_sha(account, jid)
|
| 508 |
- app.log('avatar').info(
|
|
| 509 |
- 'Update (Pubsub): %s %s', jid, self.avatar['id'])
|
|
| 509 |
+ app.log('c.m.user_avatar').info(
|
|
| 510 |
+ 'Update: %s %s', jid, self.avatar['id'])
|
|
| 510 | 511 |
if sha == self.avatar['id']:
|
| 511 |
- app.log('avatar').info(
|
|
| 512 |
- 'Avatar already known (Pubsub): %s %s',
|
|
| 512 |
+ app.log('c.m.user_avatar').info(
|
|
| 513 |
+ 'Avatar already known: %s %s',
|
|
| 513 | 514 |
jid, self.avatar['id'])
|
| 514 | 515 |
return
|
| 515 |
- app.log('avatar').info('Request (Pubsub): %s', jid)
|
|
| 516 |
- con.get_pubsub_avatar(jid, 'urn:xmpp:avatar:data',
|
|
| 517 |
- self.avatar['id'])
|
|
| 516 |
+ con.get_module('UserAvatar').get_pubsub_avatar(
|
|
| 517 |
+ jid, self.avatar['id'])
|
|
| 518 | 518 |
|
| 519 | 519 |
|
| 520 | 520 |
SUPPORTED_PERSONAL_USER_EVENTS = [
|
| 1 |
-# -*- coding:utf-8 -*-
|
|
| 2 |
-## src/common/pubsub.py
|
|
| 3 |
-##
|
|
| 4 |
-## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
|
|
| 5 |
-## Copyright (C) 2006-2014 Yann Leboulanger <asterix AT lagaule.org>
|
|
| 6 |
-## Copyright (C) 2007 Jean-Marie Traissard <jim AT lapin.org>
|
|
| 7 |
-## Copyright (C) 2008 Stephan Erb <steve-e AT h3c.de>
|
|
| 8 |
-##
|
|
| 9 |
-## This file is part of Gajim.
|
|
| 10 |
-##
|
|
| 11 |
-## Gajim is free software; you can redistribute it and/or modify
|
|
| 12 |
-## it under the terms of the GNU General Public License as published
|
|
| 13 |
-## by the Free Software Foundation; version 3 only.
|
|
| 14 |
-##
|
|
| 15 |
-## Gajim is distributed in the hope that it will be useful,
|
|
| 16 |
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
| 17 |
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
| 18 |
-## GNU General Public License for more details.
|
|
| 19 |
-##
|
|
| 20 |
-## You should have received a copy of the GNU General Public License
|
|
| 21 |
-## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
|
| 22 |
-##
|
|
| 23 |
- |
|
| 24 |
-import base64
|
|
| 25 |
-import binascii
|
|
| 26 |
- |
|
| 27 |
-import nbxmpp
|
|
| 28 |
-from gajim.common import app
|
|
| 29 |
-#TODO: Doesn't work
|
|
| 30 |
-#from common.connection_handlers import PEP_CONFIG
|
|
| 31 |
-PEP_CONFIG = 'pep_config'
|
|
| 32 |
-from gajim.common import ged
|
|
| 33 |
-from gajim.common.connection_handlers_events import PubsubReceivedEvent
|
|
| 34 |
-from gajim.common.connection_handlers_events import PubsubBookmarksReceivedEvent
|
|
| 35 |
-from gajim.common.exceptions import StanzaMalformed
|
|
| 36 |
- |
|
| 37 |
-import logging
|
|
| 38 |
-log = logging.getLogger('gajim.c.pubsub')
|
|
| 39 |
- |
|
| 40 |
-class ConnectionPubSub:
|
|
| 41 |
- def __init__(self):
|
|
| 42 |
- self.__callbacks = {}
|
|
| 43 |
- app.nec.register_incoming_event(PubsubBookmarksReceivedEvent)
|
|
| 44 |
- app.ged.register_event_handler('pubsub-bookmarks-received',
|
|
| 45 |
- ged.CORE, self._nec_pubsub_bookmarks_received)
|
|
| 46 |
- |
|
| 47 |
- def cleanup(self):
|
|
| 48 |
- app.ged.remove_event_handler('pubsub-bookmarks-received',
|
|
| 49 |
- ged.CORE, self._nec_pubsub_bookmarks_received)
|
|
| 50 |
- |
|
| 51 |
- def send_pb_subscription_query(self, jid, cb, *args, **kwargs):
|
|
| 52 |
- if not self.connection or self.connected < 2:
|
|
| 53 |
- return
|
|
| 54 |
- query = nbxmpp.Iq('get', to=jid)
|
|
| 55 |
- pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 56 |
- pb.addChild('subscriptions')
|
|
| 57 |
- |
|
| 58 |
- id_ = self.connection.send(query)
|
|
| 59 |
- |
|
| 60 |
- self.__callbacks[id_] = (cb, args, kwargs)
|
|
| 61 |
- |
|
| 62 |
- def send_pb_subscribe(self, jid, node, cb, *args, **kwargs):
|
|
| 63 |
- if not self.connection or self.connected < 2:
|
|
| 64 |
- return
|
|
| 65 |
- our_jid = app.get_jid_from_account(self.name)
|
|
| 66 |
- query = nbxmpp.Iq('set', to=jid)
|
|
| 67 |
- pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 68 |
- pb.addChild('subscribe', {'node': node, 'jid': our_jid})
|
|
| 69 |
- |
|
| 70 |
- id_ = self.connection.send(query)
|
|
| 71 |
- |
|
| 72 |
- self.__callbacks[id_] = (cb, args, kwargs)
|
|
| 73 |
- |
|
| 74 |
- def send_pb_unsubscribe(self, jid, node, cb, *args, **kwargs):
|
|
| 75 |
- if not self.connection or self.connected < 2:
|
|
| 76 |
- return
|
|
| 77 |
- our_jid = app.get_jid_from_account(self.name)
|
|
| 78 |
- query = nbxmpp.Iq('set', to=jid)
|
|
| 79 |
- pb = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 80 |
- pb.addChild('unsubscribe', {'node': node, 'jid': our_jid})
|
|
| 81 |
- |
|
| 82 |
- id_ = self.connection.send(query)
|
|
| 83 |
- |
|
| 84 |
- self.__callbacks[id_] = (cb, args, kwargs)
|
|
| 85 |
- |
|
| 86 |
- def send_pb_publish(self, jid, node, item, id_=None, options=None):
|
|
| 87 |
- """
|
|
| 88 |
- Publish item to a node
|
|
| 89 |
- """
|
|
| 90 |
- if not self.connection or self.connected < 2:
|
|
| 91 |
- return
|
|
| 92 |
- query = nbxmpp.Iq('set', to=jid)
|
|
| 93 |
- e = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 94 |
- p = e.addChild('publish', {'node': node})
|
|
| 95 |
- attrs = {}
|
|
| 96 |
- if id_:
|
|
| 97 |
- attrs = {'id': id_}
|
|
| 98 |
- p.addChild('item', attrs, [item])
|
|
| 99 |
- if options:
|
|
| 100 |
- p = e.addChild('publish-options')
|
|
| 101 |
- p.addChild(node=options)
|
|
| 102 |
- |
|
| 103 |
- self.connection.send(query)
|
|
| 104 |
- |
|
| 105 |
- @staticmethod
|
|
| 106 |
- def get_pb_retrieve_iq(jid, node, item_id=None):
|
|
| 107 |
- """
|
|
| 108 |
- Get IQ to query items from a node
|
|
| 109 |
- """
|
|
| 110 |
- query = nbxmpp.Iq('get', to=jid)
|
|
| 111 |
- r = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 112 |
- r = r.addChild('items', {'node': node})
|
|
| 113 |
- if item_id is not None:
|
|
| 114 |
- r.addChild('item', {'id': item_id})
|
|
| 115 |
- return query
|
|
| 116 |
- |
|
| 117 |
- def send_pb_retrieve(self, jid, node, item_id=None, cb=None, *args, **kwargs):
|
|
| 118 |
- """
|
|
| 119 |
- Get items from a node
|
|
| 120 |
- """
|
|
| 121 |
- if not self.connection or self.connected < 2:
|
|
| 122 |
- return
|
|
| 123 |
- query = self.get_pb_retrieve_iq(jid, node, item_id)
|
|
| 124 |
- id_ = self.connection.send(query)
|
|
| 125 |
- |
|
| 126 |
- if cb:
|
|
| 127 |
- self.__callbacks[id_] = (cb, args, kwargs)
|
|
| 128 |
- |
|
| 129 |
- def get_pubsub_avatar(self, jid, node, item_id):
|
|
| 130 |
- query = self.get_pb_retrieve_iq(jid, node, item_id)
|
|
| 131 |
- self.connection.SendAndCallForResponse(
|
|
| 132 |
- query, self._nec_pubsub_avatar_received, {'jid': jid})
|
|
| 133 |
- |
|
| 134 |
- def send_pb_retract(self, jid, node, id_):
|
|
| 135 |
- """
|
|
| 136 |
- Delete item from a node
|
|
| 137 |
- """
|
|
| 138 |
- if not self.connection or self.connected < 2:
|
|
| 139 |
- return
|
|
| 140 |
- query = nbxmpp.Iq('set', to=jid)
|
|
| 141 |
- r = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 142 |
- r = r.addChild('retract', {'node': node, 'notify': '1'})
|
|
| 143 |
- r = r.addChild('item', {'id': id_})
|
|
| 144 |
- |
|
| 145 |
- self.connection.send(query)
|
|
| 146 |
- |
|
| 147 |
- def send_pb_purge(self, jid, node):
|
|
| 148 |
- """
|
|
| 149 |
- Purge node: Remove all items
|
|
| 150 |
- """
|
|
| 151 |
- if not self.connection or self.connected < 2:
|
|
| 152 |
- return
|
|
| 153 |
- query = nbxmpp.Iq('set', to=jid)
|
|
| 154 |
- d = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
|
|
| 155 |
- d = d.addChild('purge', {'node': node})
|
|
| 156 |
- |
|
| 157 |
- self.connection.send(query)
|
|
| 158 |
- |
|
| 159 |
- def send_pb_delete(self, jid, node, on_ok=None, on_fail=None):
|
|
| 160 |
- """
|
|
| 161 |
- Delete node
|
|
| 162 |
- """
|
|
| 163 |
- if not self.connection or self.connected < 2:
|
|
| 164 |
- return
|
|
| 165 |
- query = nbxmpp.Iq('set', to=jid)
|
|
| 166 |
- d = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
|
|
| 167 |
- d = d.addChild('delete', {'node': node})
|
|
| 168 |
- |
|
| 169 |
- def response(con, resp, jid, node):
|
|
| 170 |
- if resp.getType() == 'result' and on_ok:
|
|
| 171 |
- on_ok(jid, node)
|
|
| 172 |
- elif on_fail:
|
|
| 173 |
- msg = resp.getErrorMsg()
|
|
| 174 |
- on_fail(jid, node, msg)
|
|
| 175 |
- |
|
| 176 |
- self.connection.SendAndCallForResponse(query, response, {'jid': jid,
|
|
| 177 |
- 'node': node})
|
|
| 178 |
- |
|
| 179 |
- def send_pb_create(self, jid, node, configure=False, configure_form=None):
|
|
| 180 |
- """
|
|
| 181 |
- Create a new node
|
|
| 182 |
- """
|
|
| 183 |
- if not self.connection or self.connected < 2:
|
|
| 184 |
- return
|
|
| 185 |
- query = nbxmpp.Iq('set', to=jid)
|
|
| 186 |
- c = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB)
|
|
| 187 |
- c = c.addChild('create', {'node': node})
|
|
| 188 |
- if configure:
|
|
| 189 |
- conf = c.addChild('configure')
|
|
| 190 |
- if configure_form is not None:
|
|
| 191 |
- conf.addChild(node=configure_form)
|
|
| 192 |
- |
|
| 193 |
- self.connection.send(query)
|
|
| 194 |
- |
|
| 195 |
- def send_pb_configure(self, jid, node, form):
|
|
| 196 |
- if not self.connection or self.connected < 2:
|
|
| 197 |
- return
|
|
| 198 |
- query = nbxmpp.Iq('set', to=jid)
|
|
| 199 |
- c = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
|
|
| 200 |
- c = c.addChild('configure', {'node': node})
|
|
| 201 |
- c.addChild(node=form)
|
|
| 202 |
- |
|
| 203 |
- self.connection.send(query)
|
|
| 204 |
- |
|
| 205 |
- def _PubSubCB(self, conn, stanza):
|
|
| 206 |
- log.debug('_PubsubCB')
|
|
| 207 |
- try:
|
|
| 208 |
- cb, args, kwargs = self.__callbacks.pop(stanza.getID())
|
|
| 209 |
- cb(conn, stanza, *args, **kwargs)
|
|
| 210 |
- except Exception:
|
|
| 211 |
- pass
|
|
| 212 |
- app.nec.push_incoming_event(PubsubReceivedEvent(None,
|
|
| 213 |
- conn=self, stanza=stanza))
|
|
| 214 |
- |
|
| 215 |
- def _nec_pubsub_bookmarks_received(self, obj):
|
|
| 216 |
- if obj.conn.name != self.name:
|
|
| 217 |
- return
|
|
| 218 |
- app.log('bookmarks').info('Received Bookmarks (PubSub)')
|
|
| 219 |
- bm_jids = [b['jid'] for b in self.bookmarks]
|
|
| 220 |
- for bm in obj.bookmarks:
|
|
| 221 |
- if bm['jid'] not in bm_jids:
|
|
| 222 |
- self.bookmarks.append(bm)
|
|
| 223 |
- # We got bookmarks from pubsub, now get those from xml to merge them
|
|
| 224 |
- self.get_bookmarks(storage_type='xml')
|
|
| 225 |
- |
|
| 226 |
- def _validate_avatar_node(self, stanza):
|
|
| 227 |
- jid = stanza.getFrom()
|
|
| 228 |
- if jid is None:
|
|
| 229 |
- jid = self.get_own_jid().getStripped()
|
|
| 230 |
- else:
|
|
| 231 |
- jid = jid.getStripped()
|
|
| 232 |
- |
|
| 233 |
- if nbxmpp.isErrorNode(stanza):
|
|
| 234 |
- raise StanzaMalformed(stanza.getErrorMsg())
|
|
| 235 |
- |
|
| 236 |
- pubsub_node = stanza.getTag('pubsub')
|
|
| 237 |
- if pubsub_node is None:
|
|
| 238 |
- raise StanzaMalformed('No pubsub node', stanza)
|
|
| 239 |
- |
|
| 240 |
- items_node = pubsub_node.getTag('items')
|
|
| 241 |
- if items_node is None:
|
|
| 242 |
- raise StanzaMalformed('No items node', stanza)
|
|
| 243 |
- |
|
| 244 |
- if items_node.getAttr('node') != 'urn:xmpp:avatar:data':
|
|
| 245 |
- raise StanzaMalformed('Wrong namespace', stanza)
|
|
| 246 |
- |
|
| 247 |
- item = items_node.getTag('item')
|
|
| 248 |
- if item is None:
|
|
| 249 |
- raise StanzaMalformed('No item node', stanza)
|
|
| 250 |
- |
|
| 251 |
- sha = item.getAttr('id')
|
|
| 252 |
- data_tag = item.getTag('data', namespace='urn:xmpp:avatar:data')
|
|
| 253 |
- if sha is None or data_tag is None:
|
|
| 254 |
- raise StanzaMalformed('No id attr or data node found', stanza)
|
|
| 255 |
- |
|
| 256 |
- data = data_tag.getData()
|
|
| 257 |
- if data is None:
|
|
| 258 |
- raise StanzaMalformed('Data node empty', stanza)
|
|
| 259 |
- |
|
| 260 |
- data = base64.b64decode(data.encode('utf-8'))
|
|
| 261 |
- |
|
| 262 |
- return jid, sha, data
|
|
| 263 |
- |
|
| 264 |
- def _nec_pubsub_avatar_received(self, conn, stanza, jid):
|
|
| 265 |
- try:
|
|
| 266 |
- jid, sha, data = self._validate_avatar_node(stanza)
|
|
| 267 |
- except (StanzaMalformed, binascii.Error) as error:
|
|
| 268 |
- app.log('avatar').warning(
|
|
| 269 |
- 'Error loading Avatar (Pubsub): %s %s', jid, error)
|
|
| 270 |
- return
|
|
| 271 |
- |
|
| 272 |
- app.log('avatar').info(
|
|
| 273 |
- 'Received Avatar (Pubsub): %s %s', jid, sha)
|
|
| 274 |
- app.interface.save_avatar(data)
|
|
| 275 |
- |
|
| 276 |
- if self.get_own_jid().bareMatch(jid):
|
|
| 277 |
- app.config.set_per('accounts', self.name, 'avatar_sha', sha)
|
|
| 278 |
- else:
|
|
| 279 |
- own_jid = self.get_own_jid().getStripped()
|
|
| 280 |
- app.logger.set_avatar_sha(own_jid, jid, sha)
|
|
| 281 |
- app.contacts.set_avatar(self.name, jid, sha)
|
|
| 282 |
- |
|
| 283 |
- app.interface.update_avatar(self.name, jid)
|
|
| 284 |
- |
|
| 285 |
- def _PubSubErrorCB(self, conn, stanza):
|
|
| 286 |
- log.debug('_PubsubErrorCB')
|
|
| 287 |
- pubsub = stanza.getTag('pubsub')
|
|
| 288 |
- if not pubsub:
|
|
| 289 |
- return
|
|
| 290 |
- items = pubsub.getTag('items')
|
|
| 291 |
- if not items:
|
|
| 292 |
- return
|
|
| 293 |
- if items.getAttr('node') == 'storage:bookmarks':
|
|
| 294 |
- # Receiving bookmarks from pubsub failed, so take them from xml
|
|
| 295 |
- self.get_bookmarks(storage_type='xml')
|
|
| 296 |
- |
|
| 297 |
- def request_pb_configuration(self, jid, node):
|
|
| 298 |
- if not self.connection or self.connected < 2:
|
|
| 299 |
- return
|
|
| 300 |
- query = nbxmpp.Iq('get', to=jid)
|
|
| 301 |
- e = query.addChild('pubsub', namespace=nbxmpp.NS_PUBSUB_OWNER)
|
|
| 302 |
- e = e.addChild('configure', {'node': node})
|
|
| 303 |
- id_ = self.connection.getAnID()
|
|
| 304 |
- query.setID(id_)
|
|
| 305 |
- self.awaiting_answers[id_] = (PEP_CONFIG,)
|
|
| 306 |
- self.connection.send(query)
|
| ... | ... | @@ -1900,11 +1900,13 @@ class ManageBookmarksWindow: |
| 1900 | 1900 |
iter_ = self.treestore.append(None, [None, account, None, None,
|
| 1901 | 1901 |
None, None, None, None])
|
| 1902 | 1902 |
|
| 1903 |
- for bookmark in app.connections[account].bookmarks:
|
|
| 1903 |
+ con = app.connections[account]
|
|
| 1904 |
+ bookmarks = con.get_module('Bookmarks').bookmarks
|
|
| 1905 |
+ for jid, bookmark in bookmarks.items():
|
|
| 1904 | 1906 |
if not bookmark['name']:
|
| 1905 | 1907 |
# No name was given for this bookmark.
|
| 1906 | 1908 |
# Use the first part of JID instead...
|
| 1907 |
- name = bookmark['jid'].split("@")[0]
|
|
| 1909 |
+ name = jid.split("@")[0]
|
|
| 1908 | 1910 |
bookmark['name'] = name
|
| 1909 | 1911 |
|
| 1910 | 1912 |
# make '1', '0', 'true', 'false' (or other) to True/False
|
| ... | ... | @@ -1920,7 +1922,7 @@ class ManageBookmarksWindow: |
| 1920 | 1922 |
self.treestore.append(iter_, [
|
| 1921 | 1923 |
account,
|
| 1922 | 1924 |
bookmark['name'],
|
| 1923 |
- bookmark['jid'],
|
|
| 1925 |
+ jid,
|
|
| 1924 | 1926 |
autojoin,
|
| 1925 | 1927 |
minimize,
|
| 1926 | 1928 |
bookmark['password'],
|
| ... | ... | @@ -2047,15 +2049,17 @@ class ManageBookmarksWindow: |
| 2047 | 2049 |
Parse the treestore data into our new bookmarks array, then send the new
|
| 2048 | 2050 |
bookmarks to the server.
|
| 2049 | 2051 |
"""
|
| 2052 |
+ |
|
| 2050 | 2053 |
(model, iter_) = self.selection.get_selected()
|
| 2051 | 2054 |
if iter_ and model.iter_parent(iter_):
|
| 2052 |
- #bookmark selected, check it
|
|
| 2055 |
+ # bookmark selected, check it
|
|
| 2053 | 2056 |
if not self.check_valid_bookmark():
|
| 2054 | 2057 |
return
|
| 2055 | 2058 |
|
| 2056 | 2059 |
for account in self.treestore:
|
| 2057 | 2060 |
acct = account[1]
|
| 2058 |
- app.connections[acct].bookmarks = []
|
|
| 2061 |
+ con = app.connections[acct]
|
|
| 2062 |
+ con.get_module('Bookmarks').bookmarks = {}
|
|
| 2059 | 2063 |
|
| 2060 | 2064 |
for bm in account.iterchildren():
|
| 2061 | 2065 |
# Convert True/False/None to '1' or '0'
|
| ... | ... | @@ -2067,13 +2071,17 @@ class ManageBookmarksWindow: |
| 2067 | 2071 |
nick = bm[6]
|
| 2068 | 2072 |
|
| 2069 | 2073 |
# create the bookmark-dict
|
| 2070 |
- bmdict = { 'name': name, 'jid': jid, 'autojoin': autojoin,
|
|
| 2071 |
- 'minimize': minimize, 'password': pw, 'nick': nick,
|
|
| 2074 |
+ bmdict = {
|
|
| 2075 |
+ 'name': name,
|
|
| 2076 |
+ 'autojoin': autojoin,
|
|
| 2077 |
+ 'minimize': minimize,
|
|
| 2078 |
+ 'password': pw,
|
|
| 2079 |
+ 'nick': nick,
|
|
| 2072 | 2080 |
'print_status': bm[7]}
|
| 2073 | 2081 |
|
| 2074 |
- app.connections[acct].bookmarks.append(bmdict)
|
|
| 2082 |
+ con.get_module('Bookmarks').bookmarks[jid] = bmdict
|
|
| 2075 | 2083 |
|
| 2076 |
- app.connections[acct].store_bookmarks()
|
|
| 2084 |
+ con.get_module('Bookmarks').store_bookmarks()
|
|
| 2077 | 2085 |
gui_menu_builder.build_bookmark_menu(acct)
|
| 2078 | 2086 |
self.window.destroy()
|
| 2079 | 2087 |
|
| ... | ... | @@ -2900,8 +2908,10 @@ class ManagePEPServicesWindow: |
| 2900 | 2908 |
model, iter_ = selection.get_selected()
|
| 2901 | 2909 |
node = model[iter_][0]
|
| 2902 | 2910 |
our_jid = app.get_jid_from_account(self.account)
|
| 2903 |
- app.connections[self.account].send_pb_delete(our_jid, node,
|
|
| 2904 |
- on_ok=self.node_removed, on_fail=self.node_not_removed)
|
|
| 2911 |
+ con = app.connections[self.account]
|
|
| 2912 |
+ con.get_module('PubSub').send_pb_delete(our_jid, node,
|
|
| 2913 |
+ on_ok=self.node_removed,
|
|
| 2914 |
+ on_fail=self.node_not_removed)
|
|
| 2905 | 2915 |
|
| 2906 | 2916 |
def on_configure_button_clicked(self, widget):
|
| 2907 | 2917 |
selection = self.treeview.get_selection()
|
| ... | ... | @@ -2910,13 +2920,15 @@ class ManagePEPServicesWindow: |
| 2910 | 2920 |
model, iter_ = selection.get_selected()
|
| 2911 | 2921 |
node = model[iter_][0]
|
| 2912 | 2922 |
our_jid = app.get_jid_from_account(self.account)
|
| 2913 |
- app.connections[self.account].request_pb_configuration(our_jid, node)
|
|
| 2923 |
+ con = app.connections[self.account]
|
|
| 2924 |
+ con.get_module('PubSub').request_pb_configuration(our_jid, node)
|
|
| 2914 | 2925 |
|
| 2915 | 2926 |
def _nec_pep_config_received(self, obj):
|
| 2916 | 2927 |
def on_ok(form, node):
|
| 2917 | 2928 |
form.type_ = 'submit'
|
| 2918 | 2929 |
our_jid = app.get_jid_from_account(self.account)
|
| 2919 |
- app.connections[self.account].send_pb_configure(our_jid, node, form)
|
|
| 2930 |
+ con = app.connections[self.account]
|
|
| 2931 |
+ con.get_module('PubSub').send_pb_configure(our_jid, node, form)
|
|
| 2920 | 2932 |
window = dialogs.DataFormWindow(obj.form, (on_ok, obj.node))
|
| 2921 | 2933 |
title = _('Configure %s') % obj.node
|
| 2922 | 2934 |
window.set_title(title)
|
| ... | ... | @@ -2520,7 +2520,8 @@ class JoinGroupchatWindow(Gtk.ApplicationWindow): |
| 2520 | 2520 |
self.autojoin_switch.set_sensitive(switch.get_active())
|
| 2521 | 2521 |
|
| 2522 | 2522 |
def _add_bookmark(self, account, nickname, password):
|
| 2523 |
- if not app.connections[account].private_storage_supported:
|
|
| 2523 |
+ con = app.connections[account]
|
|
| 2524 |
+ if not con.private_storage_supported:
|
|
| 2524 | 2525 |
return
|
| 2525 | 2526 |
|
| 2526 | 2527 |
add_bookmark = self.bookmark_switch.get_active()
|
| ... | ... | @@ -2531,8 +2532,8 @@ class JoinGroupchatWindow(Gtk.ApplicationWindow): |
| 2531 | 2532 |
|
| 2532 | 2533 |
# Add as bookmark, with autojoin and not minimized
|
| 2533 | 2534 |
name = app.get_nick_from_jid(self.room_jid)
|
| 2534 |
- app.interface.add_gc_bookmark(
|
|
| 2535 |
- account, name, self.room_jid, autojoin, 1, password, nickname)
|
|
| 2535 |
+ con.get_module('Bookmarks').add_bookmark(
|
|
| 2536 |
+ name, self.room_jid, autojoin, 1, password, nickname)
|
|
| 2536 | 2537 |
|
| 2537 | 2538 |
def _on_destroy(self, *args):
|
| 2538 | 2539 |
if self.minimal_mode == 0:
|
| ... | ... | @@ -2784,10 +2785,11 @@ class StartChatDialog(Gtk.ApplicationWindow): |
| 2784 | 2785 |
show_account = len(self.accounts) > 1
|
| 2785 | 2786 |
for account in self.accounts:
|
| 2786 | 2787 |
self.new_groupchat_rows[account] = None
|
| 2787 |
- bookmarks = app.connections[account].bookmarks
|
|
| 2788 |
+ con = app.connections[account]
|
|
| 2789 |
+ bookmarks = con.get_module('Bookmarks').bookmarks
|
|
| 2788 | 2790 |
groupchats = {}
|
| 2789 |
- for bookmark in bookmarks:
|
|
| 2790 |
- groupchats[bookmark['jid']] = bookmark['name']
|
|
| 2791 |
+ for jid, bookmark in bookmarks.items():
|
|
| 2792 |
+ groupchats[jid] = bookmark['name']
|
|
| 2791 | 2793 |
|
| 2792 | 2794 |
for jid in app.contacts.get_gc_list(account):
|
| 2793 | 2795 |
if jid in groupchats:
|
| ... | ... | @@ -1757,29 +1757,29 @@ class MucBrowser(AgentBrowser): |
| 1757 | 1757 |
self.join_button = None
|
| 1758 | 1758 |
|
| 1759 | 1759 |
def on_bookmark_button_clicked(self, *args):
|
| 1760 |
+ con = app.connections[self.account]
|
|
| 1760 | 1761 |
model, iter = self.window.services_treeview.get_selection().get_selected()
|
| 1761 | 1762 |
if not iter:
|
| 1762 | 1763 |
return
|
| 1763 | 1764 |
name = app.config.get_per('accounts', self.account, 'name')
|
| 1764 | 1765 |
room_jid = model[iter][0]
|
| 1765 | 1766 |
bm = {
|
| 1766 |
- 'name': room_jid.split('@')[0],
|
|
| 1767 |
- 'jid': room_jid,
|
|
| 1768 |
- 'autojoin': '0',
|
|
| 1769 |
- 'minimize': '0',
|
|
| 1770 |
- 'password': '',
|
|
| 1771 |
- 'nick': name
|
|
| 1767 |
+ 'name': room_jid.split('@')[0],
|
|
| 1768 |
+ 'autojoin': '0',
|
|
| 1769 |
+ 'minimize': '0',
|
|
| 1770 |
+ 'password': '',
|
|
| 1771 |
+ 'nick': name
|
|
| 1772 | 1772 |
}
|
| 1773 | 1773 |
|
| 1774 |
- for bookmark in app.connections[self.account].bookmarks:
|
|
| 1775 |
- if bookmark['jid'] == bm['jid']:
|
|
| 1776 |
- dialogs.ErrorDialog( _('Bookmark already set'),
|
|
| 1777 |
- _('Group Chat "%s" is already in your bookmarks.') % bm['jid'],
|
|
| 1774 |
+ if room_jid in con.get_module('Bookmarks').bookmarks:
|
|
| 1775 |
+ dialogs.ErrorDialog(
|
|
| 1776 |
+ _('Bookmark already set'),
|
|
| 1777 |
+ _('Group Chat "%s" is already in your bookmarks.') % room_jid,
|
|
| 1778 | 1778 |
transient_for=self.window.window)
|
| 1779 |
- return
|
|
| 1779 |
+ return
|
|
| 1780 | 1780 |
|
| 1781 |
- app.connections[self.account].bookmarks.append(bm)
|
|
| 1782 |
- app.connections[self.account].store_bookmarks()
|
|
| 1781 |
+ con.get_module('Bookmarks').bookmarks[room_jid] = bm
|
|
| 1782 |
+ con.get_module('Bookmarks').store_bookmarks()
|
|
| 1783 | 1783 |
|
| 1784 | 1784 |
gui_menu_builder.build_bookmark_menu(self.account)
|
| 1785 | 1785 |
|
| ... | ... | @@ -1951,8 +1951,9 @@ class DiscussionGroupsBrowser(AgentBrowser): |
| 1951 | 1951 |
self.subscribe_button = None
|
| 1952 | 1952 |
self.unsubscribe_button = None
|
| 1953 | 1953 |
|
| 1954 |
- app.connections[account].send_pb_subscription_query(jid,
|
|
| 1955 |
- self._on_pep_subscriptions)
|
|
| 1954 |
+ con = app.connections[account]
|
|
| 1955 |
+ con.get_module('PubSub').send_pb_subscription_query(
|
|
| 1956 |
+ jid, self._on_pep_subscriptions)
|
|
| 1956 | 1957 |
|
| 1957 | 1958 |
def _create_treemodel(self):
|
| 1958 | 1959 |
"""
|
| ... | ... | @@ -2122,10 +2123,11 @@ class DiscussionGroupsBrowser(AgentBrowser): |
| 2122 | 2123 |
model, iter_ = self.window.services_treeview.get_selection().get_selected()
|
| 2123 | 2124 |
if iter_ is None: return
|
| 2124 | 2125 |
|
| 2125 |
- groupnode = model.get_value(iter_, 1) # 1 = groupnode
|
|
| 2126 |
+ node = model.get_value(iter_, 1) # 1 = groupnode
|
|
| 2126 | 2127 |
|
| 2127 |
- app.connections[self.account].send_pb_subscribe(self.jid, groupnode,
|
|
| 2128 |
- self._on_pep_subscribe, groupnode)
|
|
| 2128 |
+ con = app.connections[self.account]
|
|
| 2129 |
+ con.get_module('PubSub').send_pb_subscribe(
|
|
| 2130 |
+ self.jid, node, self._on_pep_subscribe, groupnode=node)
|
|
| 2129 | 2131 |
|
| 2130 | 2132 |
def on_unsubscribe_button_clicked(self, widget):
|
| 2131 | 2133 |
"""
|
| ... | ... | @@ -2134,10 +2136,11 @@ class DiscussionGroupsBrowser(AgentBrowser): |
| 2134 | 2136 |
model, iter_ = self.window.services_treeview.get_selection().get_selected()
|
| 2135 | 2137 |
if iter_ is None: return
|
| 2136 | 2138 |
|
| 2137 |
- groupnode = model.get_value(iter_, 1) # 1 = groupnode
|
|
| 2139 |
+ node = model.get_value(iter_, 1) # 1 = groupnode
|
|
| 2138 | 2140 |
|
| 2139 |
- app.connections[self.account].send_pb_unsubscribe(self.jid, groupnode,
|
|
| 2140 |
- self._on_pep_unsubscribe, groupnode)
|
|
| 2141 |
+ con = app.connections[self.account]
|
|
| 2142 |
+ con.get_module('PubSub').send_pb_unsubscribe(
|
|
| 2143 |
+ self.jid, node, self._on_pep_unsubscribe, groupnode=node)
|
|
| 2141 | 2144 |
|
| 2142 | 2145 |
def _on_pep_subscriptions(self, conn, request):
|
| 2143 | 2146 |
"""
|
| ... | ... | @@ -2168,8 +2171,6 @@ class DiscussionGroupsBrowser(AgentBrowser): |
| 2168 | 2171 |
# we now know subscriptions, update button states
|
| 2169 | 2172 |
self.update_actions()
|
| 2170 | 2173 |
|
| 2171 |
- raise nbxmpp.NodeProcessed
|
|
| 2172 |
- |
|
| 2173 | 2174 |
def _on_pep_subscribe(self, conn, request, groupnode):
|
| 2174 | 2175 |
"""
|
| 2175 | 2176 |
We have just subscribed to a node. Update UI
|
| ... | ... | @@ -2184,8 +2185,6 @@ class DiscussionGroupsBrowser(AgentBrowser): |
| 2184 | 2185 |
|
| 2185 | 2186 |
self.update_actions()
|
| 2186 | 2187 |
|
| 2187 |
- raise nbxmpp.NodeProcessed
|
|
| 2188 |
- |
|
| 2189 | 2188 |
def _on_pep_unsubscribe(self, conn, request, groupnode):
|
| 2190 | 2189 |
"""
|
| 2191 | 2190 |
We have just unsubscribed from a node. Update UI
|
| ... | ... | @@ -2200,7 +2199,5 @@ class DiscussionGroupsBrowser(AgentBrowser): |
| 2200 | 2199 |
|
| 2201 | 2200 |
self.update_actions()
|
| 2202 | 2201 |
|
| 2203 |
- raise nbxmpp.NodeProcessed
|
|
| 2204 |
- |
|
| 2205 | 2202 |
# Fill the global agent type info dictionary
|
| 2206 | 2203 |
_agent_type_info = _gen_agent_type_info()
|
| ... | ... | @@ -348,13 +348,10 @@ class GroupchatControl(ChatControlBase): |
| 348 | 348 |
self.room_jid = self.contact.jid
|
| 349 | 349 |
self.nick = nick
|
| 350 | 350 |
self.new_nick = ''
|
| 351 |
- self.name = ''
|
|
| 352 |
- for bm in app.connections[self.account].bookmarks:
|
|
| 353 |
- if bm['jid'] == self.room_jid:
|
|
| 354 |
- self.name = bm['name']
|
|
| 355 |
- break
|
|
| 356 |
- if not self.name:
|
|
| 357 |
- self.name = self.room_jid.split('@')[0]
|
|
| 351 |
+ |
|
| 352 |
+ bm_module = app.connections[self.account].get_module('Bookmarks')
|
|
| 353 |
+ self.name = bm_module.get_name_from_bookmark(self.room_jid)
|
|
| 354 |
+ |
|
| 358 | 355 |
self.contact.name = self.name
|
| 359 | 356 |
|
| 360 | 357 |
self.widget_set_visible(self.xml.get_object('banner_eventbox'),
|
| ... | ... | @@ -589,13 +586,9 @@ class GroupchatControl(ChatControlBase): |
| 589 | 586 |
# Bookmarks
|
| 590 | 587 |
con = app.connections[self.account]
|
| 591 | 588 |
bookmark_support = con.bookmarks_available()
|
| 592 |
- not_bookmarked = True
|
|
| 593 |
- for bm in con.bookmarks:
|
|
| 594 |
- if bm['jid'] == self.room_jid:
|
|
| 595 |
- not_bookmarked = False
|
|
| 596 |
- break
|
|
| 589 |
+ bookmarked = self.room_jid in con.get_module('Bookmarks').bookmarks
|
|
| 597 | 590 |
win.lookup_action('bookmark-' + self.control_id).set_enabled(
|
| 598 |
- online and bookmark_support and not_bookmarked)
|
|
| 591 |
+ online and bookmark_support and not bookmarked)
|
|
| 599 | 592 |
|
| 600 | 593 |
# Request Voice
|
| 601 | 594 |
role = self.get_role(self.nick)
|
| ... | ... | @@ -712,9 +705,10 @@ class GroupchatControl(ChatControlBase): |
| 712 | 705 |
Bookmark the room, without autojoin and not minimized
|
| 713 | 706 |
"""
|
| 714 | 707 |
password = app.gc_passwords.get(self.room_jid, '')
|
| 715 |
- app.interface.add_gc_bookmark(
|
|
| 716 |
- self.account, self.name, self.room_jid,
|
|
| 717 |
- '0', '0', password, self.nick)
|
|
| 708 |
+ con = app.connections[self.account]
|
|
| 709 |
+ con.get_module('Bookmarks').add_bookmark(
|
|
| 710 |
+ self.name, self.room_jid,
|
|
| 711 |
+ '1', '1', password, self.nick)
|
|
| 718 | 712 |
self.update_actions()
|
| 719 | 713 |
|
| 720 | 714 |
def _on_request_voice(self, action, param):
|
| ... | ... | @@ -67,7 +67,9 @@ class GroupsPostWindow: |
| 67 | 67 |
item.addChild('content', {}, [buf.get_text(buf.get_start_iter(), buf.get_end_iter(), True)])
|
| 68 | 68 |
|
| 69 | 69 |
# publish it to node
|
| 70 |
- app.connections[self.account].send_pb_publish(self.servicejid, self.groupid, item, '0')
|
|
| 70 |
+ con = app.connections[self.account]
|
|
| 71 |
+ con.get_module('PubSub').send_pb_publish(
|
|
| 72 |
+ self.servicejid, self.groupid, item, '0')
|
|
| 71 | 73 |
|
| 72 | 74 |
# close the window
|
| 73 | 75 |
self.window.destroy()
|
| ... | ... | @@ -824,17 +824,7 @@ class Interface: |
| 824 | 824 |
self.instances[account]['sub_request'][obj.jid].destroy()
|
| 825 | 825 |
|
| 826 | 826 |
def handle_event_bookmarks(self, obj):
|
| 827 |
- # ('BOOKMARKS', account, [{name,jid,autojoin,password,nick}, {}])
|
|
| 828 |
- # We received a bookmark item from the server (JEP48)
|
|
| 829 |
- # Auto join GC windows if necessary
|
|
| 830 |
- |
|
| 831 |
- gui_menu_builder.build_bookmark_menu(obj.conn.name)
|
|
| 832 |
- invisible_show = app.SHOW_LIST.index('invisible')
|
|
| 833 |
- # do not autojoin if we are invisible
|
|
| 834 |
- if obj.conn.connected == invisible_show:
|
|
| 835 |
- return
|
|
| 836 |
- |
|
| 837 |
- GLib.idle_add(self.auto_join_bookmarks, obj.conn.name)
|
|
| 827 |
+ gui_menu_builder.build_bookmark_menu(obj.account)
|
|
| 838 | 828 |
|
| 839 | 829 |
def handle_event_file_send_error(self, account, array):
|
| 840 | 830 |
jid = array[0]
|
| ... | ... | @@ -1728,7 +1718,6 @@ class Interface: |
| 1728 | 1718 |
if isinstance(ctrl, ChatControlBase):
|
| 1729 | 1719 |
ctrl.scroll_to_end()
|
| 1730 | 1720 |
|
| 1731 |
- |
|
| 1732 | 1721 |
def join_gc_minimal(self, account, room_jid, password=None,
|
| 1733 | 1722 |
transient_for=None):
|
| 1734 | 1723 |
if account is not None:
|
| ... | ... | @@ -1738,9 +1727,10 @@ class Interface: |
| 1738 | 1727 |
app.interface.join_gc_room(account, room_jid, '', '')
|
| 1739 | 1728 |
return
|
| 1740 | 1729 |
|
| 1741 |
- for bookmark in app.connections[account].bookmarks:
|
|
| 1742 |
- if bookmark['jid'] != room_jid:
|
|
| 1743 |
- continue
|
|
| 1730 |
+ con = app.connections[account]
|
|
| 1731 |
+ bookmarks = con.get_module('Bookmarks').bookmarks
|
|
| 1732 |
+ bookmark = bookmarks.get(room_jid, None)
|
|
| 1733 |
+ if bookmark is not None:
|
|
| 1744 | 1734 |
app.interface.join_gc_room(
|
| 1745 | 1735 |
account, room_jid, bookmark['nick'], bookmark['password'])
|
| 1746 | 1736 |
return
|
| ... | ... | @@ -2469,51 +2459,6 @@ class Interface: |
| 2469 | 2459 |
return False
|
| 2470 | 2460 |
return True
|
| 2471 | 2461 |
|
| 2472 |
- def auto_join_bookmarks(self, account):
|
|
| 2473 |
- """
|
|
| 2474 |
- Autojoin bookmarked GCs that have 'auto join' on for this account
|
|
| 2475 |
- """
|
|
| 2476 |
- for bm in app.connections[account].bookmarks:
|
|
| 2477 |
- if bm['autojoin'] in ('1', 'true'):
|
|
| 2478 |
- jid = bm['jid']
|
|
| 2479 |
- # Only join non-opened groupchats. Opened one are already
|
|
| 2480 |
- # auto-joined on re-connection
|
|
| 2481 |
- if not jid in app.gc_connected[account]:
|
|
| 2482 |
- # we are not already connected
|
|
| 2483 |
- minimize = bm['minimize'] in ('1', 'true')
|
|
| 2484 |
- self.join_gc_room(account, jid, bm['nick'],
|
|
| 2485 |
- bm['password'], minimize = minimize)
|
|
| 2486 |
- |
|
| 2487 |
- def add_gc_bookmark(self, account, name, jid, autojoin, minimize, password,
|
|
| 2488 |
- nick):
|
|
| 2489 |
- """
|
|
| 2490 |
- Add a bookmark for this account, sorted in bookmark list
|
|
| 2491 |
- """
|
|
| 2492 |
- bm = {
|
|
| 2493 |
- 'name': name,
|
|
| 2494 |
- 'jid': jid,
|
|
| 2495 |
- 'autojoin': autojoin,
|
|
| 2496 |
- 'minimize': minimize,
|
|
| 2497 |
- 'password': password,
|
|
| 2498 |
- 'nick': nick
|
|
| 2499 |
- }
|
|
| 2500 |
- place_found = False
|
|
| 2501 |
- index = 0
|
|
| 2502 |
- # check for duplicate entry and respect alpha order
|
|
| 2503 |
- for bookmark in app.connections[account].bookmarks:
|
|
| 2504 |
- if bookmark['jid'] == bm['jid']:
|
|
| 2505 |
- return
|
|
| 2506 |
- if bookmark['name'] > bm['name']:
|
|
| 2507 |
- place_found = True
|
|
| 2508 |
- break
|
|
| 2509 |
- index += 1
|
|
| 2510 |
- if place_found:
|
|
| 2511 |
- app.connections[account].bookmarks.insert(index, bm)
|
|
| 2512 |
- else:
|
|
| 2513 |
- app.connections[account].bookmarks.append(bm)
|
|
| 2514 |
- app.connections[account].store_bookmarks()
|
|
| 2515 |
- gui_menu_builder.build_bookmark_menu(account)
|
|
| 2516 |
- |
|
| 2517 | 2462 |
# does JID exist only within a groupchat?
|
| 2518 | 2463 |
def is_pm_contact(self, fjid, account):
|
| 2519 | 2464 |
bare_jid = app.get_jid_without_resource(fjid)
|
| ... | ... | @@ -172,14 +172,15 @@ show_bookmarked=False, force_resource=False): |
| 172 | 172 |
rooms2 = [] # a list of (room_jid, account) tuple
|
| 173 | 173 |
r_jids = [] # list of room jids
|
| 174 | 174 |
for account in connected_accounts:
|
| 175 |
- for room in app.connections[account].bookmarks:
|
|
| 176 |
- r_jid = room['jid']
|
|
| 177 |
- if r_jid in r_jids:
|
|
| 175 |
+ con = app.connections[account]
|
|
| 176 |
+ boomarks = con.get_module('Bookmarks').bookmarks
|
|
| 177 |
+ for jid, bookmark in boomarks.items():
|
|
| 178 |
+ if jid in r_jids:
|
|
| 178 | 179 |
continue
|
| 179 |
- if r_jid not in app.gc_connected[account] or not \
|
|
| 180 |
- app.gc_connected[account][r_jid]:
|
|
| 181 |
- rooms2.append((r_jid, account))
|
|
| 182 |
- r_jids.append(r_jid)
|
|
| 180 |
+ if jid not in app.gc_connected[account] or not \
|
|
| 181 |
+ app.gc_connected[account][jid]:
|
|
| 182 |
+ rooms2.append((jid, account))
|
|
| 183 |
+ r_jids.append(jid)
|
|
| 183 | 184 |
|
| 184 | 185 |
if not rooms2:
|
| 185 | 186 |
return
|
| ... | ... | @@ -675,7 +676,9 @@ def get_groupchat_menu(control_id): |
| 675 | 676 |
|
| 676 | 677 |
|
| 677 | 678 |
def get_bookmarks_menu(account, rebuild=False):
|
| 678 |
- if not app.connections[account].bookmarks:
|
|
| 679 |
+ con = app.connections[account]
|
|
| 680 |
+ boomarks = con.get_module('Bookmarks').bookmarks
|
|
| 681 |
+ if not boomarks:
|
|
| 679 | 682 |
return None
|
| 680 | 683 |
menu = Gio.Menu()
|
| 681 | 684 |
|
| ... | ... | @@ -688,12 +691,12 @@ def get_bookmarks_menu(account, rebuild=False): |
| 688 | 691 |
|
| 689 | 692 |
# Build Bookmarks
|
| 690 | 693 |
section = Gio.Menu()
|
| 691 |
- for bookmark in app.connections[account].bookmarks:
|
|
| 694 |
+ for jid, bookmark in boomarks.items():
|
|
| 692 | 695 |
name = bookmark['name']
|
| 693 | 696 |
if not name:
|
| 694 | 697 |
# No name was given for this bookmark.
|
| 695 | 698 |
# Use the first part of JID instead...
|
| 696 |
- name = bookmark['jid'].split("@")[0]
|
|
| 699 |
+ name = jid.split("@")[0]
|
|
| 697 | 700 |
|
| 698 | 701 |
# Shorten long names
|
| 699 | 702 |
name = (name[:42] + '..') if len(name) > 42 else name
|
| ... | ... | @@ -703,7 +706,7 @@ def get_bookmarks_menu(account, rebuild=False): |
| 703 | 706 |
|
| 704 | 707 |
# Create Variant Dict
|
| 705 | 708 |
dict_ = {'account': GLib.Variant('s', account),
|
| 706 |
- 'jid': GLib.Variant('s', bookmark['jid'])}
|
|
| 709 |
+ 'jid': GLib.Variant('s', jid)}
|
|
| 707 | 710 |
if bookmark['nick']:
|
| 708 | 711 |
dict_['nick'] = GLib.Variant('s', bookmark['nick'])
|
| 709 | 712 |
if bookmark['password']:
|
| ... | ... | @@ -2154,7 +2154,8 @@ class RosterWindow: |
| 2154 | 2154 |
auto=auto)
|
| 2155 | 2155 |
if was_invisible and status != 'offline':
|
| 2156 | 2156 |
# We come back from invisible, join bookmarks
|
| 2157 |
- app.interface.auto_join_bookmarks(account)
|
|
| 2157 |
+ con = app.connections[account]
|
|
| 2158 |
+ con.get_module('Bookmarks').auto_join_bookmarks()
|
|
| 2158 | 2159 |
|
| 2159 | 2160 |
|
| 2160 | 2161 |
def chg_contact_status(self, contact, show, status, account):
|
| ... | ... | @@ -2712,9 +2713,9 @@ class RosterWindow: |
| 2712 | 2713 |
### FIXME: order callbacks in itself...
|
| 2713 | 2714 |
################################################################################
|
| 2714 | 2715 |
|
| 2715 |
- def on_bookmark_menuitem_activate(self, widget, account, bookmark):
|
|
| 2716 |
- app.interface.join_gc_room(account, bookmark['jid'], bookmark['nick'],
|
|
| 2717 |
- bookmark['password'])
|
|
| 2716 |
+ def on_bookmark_menuitem_activate(self, widget, account, jid, bookmark):
|
|
| 2717 |
+ app.interface.join_gc_room(
|
|
| 2718 |
+ account, jid, bookmark['nick'], bookmark['password'])
|
|
| 2718 | 2719 |
|
| 2719 | 2720 |
def on_info(self, widget, contact, account):
|
| 2720 | 2721 |
"""
|
| ... | ... | @@ -5439,16 +5440,17 @@ class RosterWindow: |
| 5439 | 5440 |
gc_sub_menu.append(item)
|
| 5440 | 5441 |
|
| 5441 | 5442 |
# User has at least one bookmark.
|
| 5442 |
- if app.connections[account].bookmarks:
|
|
| 5443 |
+ con = app.connections[account]
|
|
| 5444 |
+ if con.get_module('Bookmarks').bookmarks:
|
|
| 5443 | 5445 |
item = Gtk.SeparatorMenuItem.new()
|
| 5444 | 5446 |
gc_sub_menu.append(item)
|
| 5445 | 5447 |
|
| 5446 |
- for bookmark in app.connections[account].bookmarks:
|
|
| 5448 |
+ for jid, bookmark in con.get_module('Bookmarks').bookmarks.items():
|
|
| 5447 | 5449 |
name = bookmark['name']
|
| 5448 | 5450 |
if not name:
|
| 5449 | 5451 |
# No name was given for this bookmark.
|
| 5450 | 5452 |
# Use the first part of JID instead...
|
| 5451 |
- name = bookmark['jid'].split("@")[0]
|
|
| 5453 |
+ name = jid.split("@")[0]
|
|
| 5452 | 5454 |
|
| 5453 | 5455 |
# Shorten long names
|
| 5454 | 5456 |
name = (name[:42] + '..') if len(name) > 42 else name
|
| ... | ... | @@ -5456,8 +5458,9 @@ class RosterWindow: |
| 5456 | 5458 |
# Do not use underline.
|
| 5457 | 5459 |
item = Gtk.MenuItem.new_with_label(name)
|
| 5458 | 5460 |
item.set_use_underline(False)
|
| 5459 |
- item.connect('activate', self.on_bookmark_menuitem_activate,
|
|
| 5460 |
- account, bookmark)
|
|
| 5461 |
+ item.connect(
|
|
| 5462 |
+ 'activate', self.on_bookmark_menuitem_activate,
|
|
| 5463 |
+ account, jid, bookmark)
|
|
| 5461 | 5464 |
gc_sub_menu.append(item)
|
| 5462 | 5465 |
|
| 5463 | 5466 |
def show_appropriate_context_menu(self, event, iters):
|