252 lines
8.6 KiB
Python
252 lines
8.6 KiB
Python
# 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
|
|
|
|
from typing import Any
|
|
from typing import List
|
|
from typing import Optional
|
|
|
|
import copy
|
|
|
|
import nbxmpp
|
|
from nbxmpp.util import is_error_result
|
|
from nbxmpp.structs import BookmarkData
|
|
from nbxmpp.const import BookmarkStoreType
|
|
from gi.repository import GLib
|
|
|
|
from gajim.common import app
|
|
from gajim.common.nec import NetworkEvent
|
|
from gajim.common.modules.base import BaseModule
|
|
from gajim.common.modules.util import event_node
|
|
|
|
|
|
class Bookmarks(BaseModule):
|
|
|
|
_nbxmpp_extends = 'Bookmarks'
|
|
_nbxmpp_methods = [
|
|
'request_bookmarks',
|
|
'store_bookmarks',
|
|
]
|
|
|
|
def __init__(self, con):
|
|
BaseModule.__init__(self, con)
|
|
self._register_pubsub_handler(self._bookmark_event_received)
|
|
self._conversion = False
|
|
self._bookmarks = []
|
|
self._join_timeouts = []
|
|
self._request_in_progress = False
|
|
|
|
@property
|
|
def conversion(self):
|
|
return self._conversion
|
|
|
|
@property
|
|
def bookmarks(self):
|
|
return self._bookmarks
|
|
|
|
@bookmarks.setter
|
|
def bookmarks(self, value):
|
|
self._bookmarks = value
|
|
|
|
@event_node(nbxmpp.NS_BOOKMARKS)
|
|
def _bookmark_event_received(self, _con, _stanza, properties):
|
|
bookmarks = properties.pubsub_event.data
|
|
|
|
if not properties.is_self_message:
|
|
self._log.warning('%s has an open access bookmarks node',
|
|
properties.jid)
|
|
return
|
|
|
|
if not self._pubsub_support() or not self.conversion:
|
|
return
|
|
|
|
if self._request_in_progress:
|
|
self._log.info('Ignore update, pubsub request in progress')
|
|
return
|
|
|
|
old_bookmarks = self._convert_to_set(self._bookmarks)
|
|
self._bookmarks = bookmarks
|
|
self._act_on_changed_bookmarks(old_bookmarks)
|
|
app.nec.push_incoming_event(
|
|
NetworkEvent('bookmarks-received', account=self._account))
|
|
|
|
def pass_disco(self, from_, _identities, features, _data, _node):
|
|
if nbxmpp.NS_BOOKMARK_CONVERSION not in features:
|
|
return
|
|
self._conversion = True
|
|
self._log.info('Discovered Bookmarks Conversion: %s', from_)
|
|
|
|
def _act_on_changed_bookmarks(self, old_bookmarks):
|
|
new_bookmarks = self._convert_to_set(self._bookmarks)
|
|
changed = new_bookmarks - old_bookmarks
|
|
if not changed:
|
|
return
|
|
|
|
join = [jid for jid, autojoin in changed if autojoin]
|
|
bookmarks = []
|
|
for jid in join:
|
|
self._log.info('Schedule autojoin in 10s for: %s', jid)
|
|
bookmarks.append(self.get_bookmark_from_jid(jid))
|
|
# If another client creates a MUC, the MUC is locked until the
|
|
# configuration is finished. Give the user some time to finish
|
|
# the configuration.
|
|
timeout_id = GLib.timeout_add_seconds(
|
|
10, self._join_with_timeout, bookmarks)
|
|
self._join_timeouts.append(timeout_id)
|
|
|
|
# TODO: leave mucs
|
|
# leave = [jid for jid, autojoin in changed if not autojoin]
|
|
|
|
@staticmethod
|
|
def _convert_to_set(bookmarks):
|
|
set_ = set()
|
|
for bookmark in bookmarks:
|
|
set_.add((bookmark.jid, bookmark.autojoin))
|
|
return set_
|
|
|
|
def get_bookmark_from_jid(self, jid):
|
|
for bookmark in self._bookmarks:
|
|
if bookmark.jid == jid:
|
|
return bookmark
|
|
|
|
def get_sorted_bookmarks(self, short_name=False):
|
|
# This returns a sorted by name copy of the bookmarks
|
|
sorted_bookmarks = []
|
|
for bookmark in self._bookmarks:
|
|
bookmark_copy = copy.deepcopy(bookmark)
|
|
if not bookmark_copy.name:
|
|
# No name was given for this bookmark
|
|
# Use the first part of JID instead
|
|
name = bookmark_copy.jid.split("@")[0]
|
|
bookmark_copy = bookmark_copy._replace(name=name)
|
|
|
|
if short_name:
|
|
name = bookmark_copy.name
|
|
name = (name[:42] + '..') if len(name) > 42 else name
|
|
bookmark_copy = bookmark_copy._replace(name=name)
|
|
|
|
sorted_bookmarks.append(bookmark_copy)
|
|
|
|
sorted_bookmarks.sort(key=lambda x: x.name.lower())
|
|
return sorted_bookmarks
|
|
|
|
def _pubsub_support(self) -> bool:
|
|
return (self._con.get_module('PEP').supported and
|
|
self._con.get_module('PubSub').publish_options)
|
|
|
|
def request_bookmarks(self):
|
|
if not app.account_is_connected(self._account):
|
|
return
|
|
|
|
self._request_in_progress = True
|
|
type_ = BookmarkStoreType.PRIVATE
|
|
if self._pubsub_support() and self.conversion:
|
|
type_ = BookmarkStoreType.PUBSUB
|
|
|
|
self._nbxmpp('Bookmarks').request_bookmarks(
|
|
type_, callback=self._bookmarks_received)
|
|
|
|
def _bookmarks_received(self, bookmarks):
|
|
if is_error_result(bookmarks):
|
|
self._log.info('Error: %s', bookmarks)
|
|
bookmarks = []
|
|
|
|
self._request_in_progress = False
|
|
self._bookmarks = bookmarks
|
|
self.auto_join_bookmarks()
|
|
app.nec.push_incoming_event(
|
|
NetworkEvent('bookmarks-received', account=self._account))
|
|
|
|
def store_bookmarks(self):
|
|
if not app.account_is_connected(self._account):
|
|
return
|
|
|
|
type_ = BookmarkStoreType.PRIVATE
|
|
if self._pubsub_support() and self.conversion:
|
|
type_ = BookmarkStoreType.PUBSUB
|
|
|
|
self._nbxmpp('Bookmarks').store_bookmarks(self._bookmarks, type_)
|
|
|
|
app.nec.push_incoming_event(
|
|
NetworkEvent('bookmarks-received', account=self._account))
|
|
|
|
def _join_with_timeout(self, bookmarks: List[Any]) -> None:
|
|
self._join_timeouts.pop(0)
|
|
self.auto_join_bookmarks(bookmarks)
|
|
|
|
def auto_join_bookmarks(self, bookmarks: Optional[List[Any]] = None) -> None:
|
|
if app.is_invisible(self._account):
|
|
return
|
|
|
|
if bookmarks is None:
|
|
bookmarks = self._bookmarks
|
|
|
|
for bookmark in bookmarks:
|
|
if bookmark.autojoin:
|
|
# Only join non-opened groupchats. Opened one are already
|
|
# auto-joined on re-connection
|
|
if bookmark.jid not in app.gc_connected[self._account]:
|
|
# we are not already connected
|
|
self._log.info('Autojoin Bookmark: %s', bookmark.jid)
|
|
minimize = app.config.get_per('rooms', bookmark.jid,
|
|
'minimize_on_autojoin', True)
|
|
app.interface.join_gc_room(
|
|
self._account, bookmark.jid, bookmark.nick,
|
|
bookmark.password, minimize=minimize)
|
|
|
|
def add_bookmark(self, name, jid, autojoin, password, nick):
|
|
bookmark = BookmarkData(jid=jid,
|
|
name=name,
|
|
autojoin=autojoin,
|
|
password=password,
|
|
nick=nick)
|
|
self._bookmarks.append(bookmark)
|
|
self.store_bookmarks()
|
|
|
|
def remove(self, jid: str, publish: bool = True) -> None:
|
|
bookmark = self.get_bookmark_from_jid(jid)
|
|
if bookmark is None:
|
|
return
|
|
self._bookmarks.remove(bookmark)
|
|
if publish:
|
|
self.store_bookmarks()
|
|
|
|
def get_name_from_bookmark(self, jid: str) -> str:
|
|
fallback = jid.split('@')[0]
|
|
bookmark = self.get_bookmark_from_jid(jid)
|
|
if bookmark is None:
|
|
return fallback
|
|
return bookmark.name or fallback
|
|
|
|
def is_bookmark(self, jid: str) -> bool:
|
|
return self.get_bookmark_from_jid(jid) is not None
|
|
|
|
def purge_pubsub_bookmarks(self) -> None:
|
|
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')
|
|
|
|
def _remove_timeouts(self):
|
|
for _id in self._join_timeouts:
|
|
GLib.source_remove(_id)
|
|
|
|
def cleanup(self):
|
|
self._remove_timeouts()
|
|
|
|
|
|
def get_instance(*args, **kwargs):
|
|
return Bookmarks(*args, **kwargs), 'Bookmarks'
|