summaryrefslogtreecommitdiff
path: root/poezio/tabs/rostertab.py
diff options
context:
space:
mode:
Diffstat (limited to 'poezio/tabs/rostertab.py')
-rw-r--r--poezio/tabs/rostertab.py459
1 files changed, 180 insertions, 279 deletions
diff --git a/poezio/tabs/rostertab.py b/poezio/tabs/rostertab.py
index 6f43cca1..18334c20 100644
--- a/poezio/tabs/rostertab.py
+++ b/poezio/tabs/rostertab.py
@@ -14,44 +14,36 @@ import ssl
from functools import partial
from os import getenv, path
from pathlib import Path
-from typing import Dict, Callable
+from typing import Dict, Callable, Union
+
+from slixmpp import JID, InvalidJID
+from slixmpp.exceptions import IqError, IqTimeout
-from poezio import common
from poezio import windows
-from poezio.common import safeJID, shell_split
+from poezio.common import shell_split
from poezio.config import config
from poezio.contact import Contact, Resource
from poezio.decorators import refresh_wrapper
from poezio.roster import RosterGroup, roster
from poezio.theming import get_theme, dump_tuple
-from poezio.decorators import command_args_parser
+from poezio.decorators import command_args_parser, deny_anonymous
from poezio.core.structs import Command, Completion
from poezio.tabs import Tab
+from poezio.ui.types import InfoMessage
log = logging.getLogger(__name__)
-def deny_anonymous(func: Callable) -> Callable:
- def wrap(self: 'RosterInfoTab', *args, **kwargs):
- if self.core.xmpp.anon:
- return self.core.information(
- 'This command is not available for anonymous accounts.',
- 'Info'
- )
- return func(self, *args, **kwargs)
- return wrap
-
-
class RosterInfoTab(Tab):
"""
A tab, split in two, containing the roster and infos
"""
- plugin_commands = {} # type: Dict[str, Command]
- plugin_keys = {} # type: Dict[str, Callable]
+ plugin_commands: Dict[str, Command] = {}
+ plugin_keys: Dict[str, Callable] = {}
def __init__(self, core):
Tab.__init__(self, core)
- self.name = "Roster"
+ self._name = "Roster"
self.v_separator = windows.VerticalSeparator()
self.information_win = windows.TextWin()
self.core.information_buffer.add_window(self.information_win)
@@ -83,15 +75,6 @@ class RosterInfoTab(Tab):
self.key_func["S"] = self.start_search_slow
self.key_func["n"] = self.change_contact_name
self.register_command(
- 'deny',
- self.command_deny,
- usage='[jid]',
- desc='Deny your presence to the provided JID (or the '
- 'selected contact in your roster), who is asking'
- 'you to be in their roster.',
- shortdesc='Deny a user your presence.',
- completion=self.completion_deny)
- self.register_command(
'name',
self.command_name,
usage='<jid> [name]',
@@ -119,16 +102,6 @@ class RosterInfoTab(Tab):
shortdesc='Remove a user from a group.',
completion=self.completion_groupremove)
self.register_command(
- 'remove',
- self.command_remove,
- usage='[jid]',
- desc='Remove the specified JID from your roster. This '
- 'will unsubscribe you from its presence, cancel '
- 'its subscription to yours, and remove the item '
- 'from your roster.',
- shortdesc='Remove a user from your roster.',
- completion=self.completion_remove)
- self.register_command(
'export',
self.command_export,
usage='[/path/to/file]',
@@ -226,50 +199,40 @@ class RosterInfoTab(Tab):
completion=self.completion_cert_fetch)
@property
- def selected_row(self):
+ def selected_row(self) -> Union[Contact, Resource]:
return self.roster_win.get_selected_row()
@command_args_parser.ignored
- def command_certs(self):
+ async def command_certs(self):
"""
/certs
"""
-
- def cb(iq):
- if iq['type'] == 'error':
- self.core.information(
- 'Unable to retrieve the certificate list.', 'Error')
- return
- certs = []
- for item in iq['sasl_certs']['items']:
- users = '\n'.join(item['users'])
- certs.append((item['name'], users))
-
- if not certs:
- return self.core.information('No certificates found', 'Info')
- msg = 'Certificates:\n'
- msg += '\n'.join(
- ((' %s%s' % (item[0] + (': ' if item[1] else ''), item[1]))
- for item in certs))
- self.core.information(msg, 'Info')
-
- self.core.xmpp.plugin['xep_0257'].get_certs(callback=cb, timeout=3)
+ try:
+ iq = await self.core.xmpp.plugin['xep_0257'].get_certs(timeout=3)
+ except (IqError, IqTimeout):
+ self.core.information(
+ 'Unable to retrieve the certificate list.', 'Error')
+ return
+ certs = []
+ for item in iq['sasl_certs']['items']:
+ users = '\n'.join(item['users'])
+ certs.append((item['name'], users))
+
+ if not certs:
+ return self.core.information('No certificates found', 'Info')
+ msg = 'Certificates:\n'
+ msg += '\n'.join(
+ ((' %s%s' % (item[0] + (': ' if item[1] else ''), item[1]))
+ for item in certs))
+ self.core.information(msg, 'Info')
@command_args_parser.quoted(2, 1)
- def command_cert_add(self, args):
+ async def command_cert_add(self, args):
"""
/cert_add <name> <certfile> [cert-management]
"""
if not args or len(args) < 2:
return self.core.command.help('cert_add')
-
- def cb(iq):
- if iq['type'] == 'error':
- self.core.information('Unable to add the certificate.',
- 'Error')
- else:
- self.core.information('Certificate added.', 'Info')
-
name = args[0]
try:
@@ -295,8 +258,17 @@ class RosterInfoTab(Tab):
else:
management = True
- self.core.xmpp.plugin['xep_0257'].add_cert(
- name, crt, callback=cb, allow_management=management)
+ try:
+ await self.core.xmpp.plugin['xep_0257'].add_cert(
+ name,
+ crt,
+ allow_management=management
+ )
+ self.core.information('Certificate added.', 'Info')
+ except (IqError, IqTimeout):
+ self.core.information('Unable to add the certificate.',
+ 'Error')
+
def completion_cert_add(self, the_input):
"""
@@ -312,76 +284,62 @@ class RosterInfoTab(Tab):
return Completion(the_input.new_completion, ['true', 'false'], n)
@command_args_parser.quoted(1)
- def command_cert_disable(self, args):
+ async def command_cert_disable(self, args):
"""
/cert_disable <name>
"""
if not args:
return self.core.command.help('cert_disable')
-
- def cb(iq):
- if iq['type'] == 'error':
- self.core.information('Unable to disable the certificate.',
- 'Error')
- else:
- self.core.information('Certificate disabled.', 'Info')
-
name = args[0]
-
- self.core.xmpp.plugin['xep_0257'].disable_cert(name, callback=cb)
+ try:
+ await self.core.xmpp.plugin['xep_0257'].disable_cert(name)
+ self.core.information('Certificate disabled.', 'Info')
+ except (IqError, IqTimeout):
+ self.core.information('Unable to disable the certificate.',
+ 'Error')
@command_args_parser.quoted(1)
- def command_cert_revoke(self, args):
+ async def command_cert_revoke(self, args):
"""
/cert_revoke <name>
"""
if not args:
return self.core.command.help('cert_revoke')
-
- def cb(iq):
- if iq['type'] == 'error':
- self.core.information('Unable to revoke the certificate.',
- 'Error')
- else:
- self.core.information('Certificate revoked.', 'Info')
-
name = args[0]
-
- self.core.xmpp.plugin['xep_0257'].revoke_cert(name, callback=cb)
+ try:
+ await self.core.xmpp.plugin['xep_0257'].revoke_cert(name)
+ self.core.information('Certificate revoked.', 'Info')
+ except (IqError, IqTimeout):
+ self.core.information('Unable to revoke the certificate.',
+ 'Error')
@command_args_parser.quoted(2)
- def command_cert_fetch(self, args):
+ async def command_cert_fetch(self, args):
"""
/cert_fetch <name> <path>
"""
if not args or len(args) < 2:
return self.core.command.help('cert_fetch')
-
- def cb(iq):
- if iq['type'] == 'error':
- self.core.information('Unable to fetch the certificate.',
- 'Error')
- return
-
- cert = None
- for item in iq['sasl_certs']['items']:
- if item['name'] == name:
- cert = base64.b64decode(item['x509cert'])
- break
-
- if not cert:
- return self.core.information('Certificate not found.', 'Info')
-
- cert = ssl.DER_cert_to_PEM_cert(cert)
- with open(path, 'w') as fd:
- fd.write(cert)
-
- self.core.information('File stored at %s' % path, 'Info')
-
name = args[0]
path = args[1]
- self.core.xmpp.plugin['xep_0257'].get_certs(callback=cb)
+ try:
+ iq = await self.core.xmpp.plugin['xep_0257'].get_certs()
+ except (IqError, IqTimeout):
+ self.core.information('Unable to fetch the certificate.',
+ 'Error')
+ return
+ cert = None
+ for item in iq['sasl_certs']['items']:
+ if item['name'] == name:
+ cert = base64.b64decode(item['x509cert'])
+ break
+ if not cert:
+ return self.core.information('Certificate not found.', 'Info')
+ cert = ssl.DER_cert_to_PEM_cert(cert)
+ with open(path, 'w') as fd:
+ fd.write(cert)
+ self.core.information('File stored at %s' % path, 'Info')
def completion_cert_fetch(self, the_input):
"""
@@ -402,32 +360,30 @@ class RosterInfoTab(Tab):
if not tab:
log.debug('Received message from nonexistent tab: %s',
message['from'])
- message = '\x19%(info_col)s}Cannot send message to %(jid)s: contact blocked' % {
+ message = 'Cannot send message to %(jid)s: contact blocked' % {
'info_col': dump_tuple(get_theme().COLOR_INFORMATION_TEXT),
'jid': message['from'],
}
- tab.add_message(message)
+ tab.add_message(InfoMessage(message))
@command_args_parser.ignored
- def command_list_blocks(self):
+ async def command_list_blocks(self):
"""
/list_blocks
"""
-
- def callback(iq):
- if iq['type'] == 'error':
- return self.core.information(
- 'Could not retrieve the blocklist.', 'Error')
- s = 'List of blocked JIDs:\n'
- items = (str(item) for item in iq['blocklist']['items'])
- jids = '\n'.join(items)
- if jids:
- s += jids
- else:
- s = 'No blocked JIDs.'
- self.core.information(s, 'Info')
-
- self.core.xmpp.plugin['xep_0191'].get_blocked(callback=callback)
+ try:
+ iq = await self.core.xmpp.plugin['xep_0191'].get_blocked()
+ except (IqError, IqTimeout) as iq:
+ return self.core.information(
+ 'Could not retrieve the blocklist.', 'Error')
+ s = 'List of blocked JIDs:\n'
+ items = (str(item) for item in iq['blocklist']['items'])
+ jids = '\n'.join(items)
+ if jids:
+ s += jids
+ else:
+ s = 'No blocked JIDs.'
+ self.core.information(s, 'Info')
@command_args_parser.ignored
def command_disconnect(self):
@@ -478,7 +434,9 @@ class RosterInfoTab(Tab):
roster_width)
self.information_win.resize(
self.height - 1 - tab_win_height - contact_win_h, info_width,
- 0, roster_width + 1, self.core.information_buffer)
+ 0, roster_width + 1, self.core.information_buffer,
+ force=self.ui_config_changed)
+ self.ui_config_changed = False
if display_contact_win:
y = self.height - tab_win_height - contact_win_h - 1
avatar_width = contact_win_h * 2
@@ -552,64 +510,34 @@ class RosterInfoTab(Tab):
@deny_anonymous
@command_args_parser.quoted(1)
- def command_password(self, args):
+ async def command_password(self, args):
"""
/password <password>
"""
-
- def callback(iq):
- if iq['type'] == 'result':
- self.core.information('Password updated', 'Account')
- if config.get('password'):
- config.silent_set('password', args[0])
- else:
- self.core.information('Unable to change the password',
- 'Account')
-
- self.core.xmpp.plugin['xep_0077'].change_password(
- args[0], callback=callback)
-
- @deny_anonymous
- @command_args_parser.quoted(0, 1)
- def command_deny(self, args):
- """
- /deny [jid]
- Denies a JID from our roster
- """
- if not args:
- item = self.roster_win.selected_row
- if isinstance(item, Contact):
- jid = item.bare_jid
- else:
- self.core.information('No subscription to deny', 'Warning')
- return
- else:
- jid = safeJID(args[0]).bare
- if jid not in [jid for jid in roster.jids()]:
- self.core.information('No subscription to deny', 'Warning')
- return
-
- contact = roster[jid]
- if contact:
- contact.unauthorize()
- self.core.information('Subscription to %s was revoked' % jid,
- 'Roster')
+ try:
+ await self.core.xmpp.plugin['xep_0077'].change_password(
+ args[0]
+ )
+ self.core.information('Password updated', 'Account')
+ if config.getstr('password'):
+ config.silent_set('password', args[0])
+ except (IqError, IqTimeout):
+ self.core.information('Unable to change the password',
+ 'Account')
@deny_anonymous
@command_args_parser.quoted(1, 1)
- def command_name(self, args):
+ async def command_name(self, args):
"""
Set a name for the specified JID in your roster
"""
-
- def callback(iq):
- if not iq:
- self.core.information('The name could not be set.', 'Error')
- log.debug('Error in /name:\n%s', iq)
-
if args is None:
return self.core.command.help('name')
- jid = safeJID(args[0]).bare
+ try:
+ jid = JID(args[0]).bare
+ except InvalidJID:
+ self.core.information(f'Invalid JID: {args[0]}', 'Error')
+ return
name = args[1] if len(args) == 2 else ''
contact = roster[jid]
@@ -621,16 +549,19 @@ class RosterInfoTab(Tab):
if 'none' in groups:
groups.remove('none')
subscription = contact.subscription
- self.core.xmpp.update_roster(
- jid,
- name=name,
- groups=groups,
- subscription=subscription,
- callback=callback)
+ try:
+ await self.core.xmpp.update_roster(
+ jid,
+ name=name,
+ groups=groups,
+ subscription=subscription
+ )
+ except (IqError, IqTimeout):
+ self.core.information('The name could not be set.', 'Error')
@deny_anonymous
@command_args_parser.quoted(1, 1)
- def command_groupadd(self, args):
+ async def command_groupadd(self, args):
"""
Add the specified JID to the specified group
"""
@@ -646,7 +577,11 @@ class RosterInfoTab(Tab):
else:
return self.core.command.help('groupadd')
else:
- jid = safeJID(args[0]).bare
+ try:
+ jid = JID(args[0]).bare
+ except InvalidJID:
+ self.core.information(f'Invalid JID: {args[0]}', 'Error')
+ return
group = args[1]
contact = roster[jid]
@@ -669,29 +604,31 @@ class RosterInfoTab(Tab):
name = contact.name
subscription = contact.subscription
- def callback(iq):
- if iq:
- roster.update_contact_groups(jid)
- else:
- self.core.information('The group could not be set.', 'Error')
- log.debug('Error in groupadd:\n%s', iq)
- self.core.xmpp.update_roster(
- jid,
- name=name,
- groups=new_groups,
- subscription=subscription,
- callback=callback)
+ try:
+ await self.core.xmpp.update_roster(
+ jid,
+ name=name,
+ groups=new_groups,
+ subscription=subscription,
+ )
+ roster.update_contact_groups(jid)
+ except (IqError, IqTimeout):
+ self.core.information('The group could not be set.', 'Error')
@deny_anonymous
@command_args_parser.quoted(3)
- def command_groupmove(self, args):
+ async def command_groupmove(self, args):
"""
Remove the specified JID from the first specified group and add it to the second one
"""
if args is None:
return self.core.command.help('groupmove')
- jid = safeJID(args[0]).bare
+ try:
+ jid = JID(args[0]).bare
+ except InvalidJID:
+ self.core.information(f'Invalid JID: {args[0]}', 'Error')
+ return
group_from = args[1]
group_to = args[2]
@@ -728,31 +665,31 @@ class RosterInfoTab(Tab):
new_groups.remove(group_from)
name = contact.name
subscription = contact.subscription
-
- def callback(iq):
- if iq:
- roster.update_contact_groups(contact)
- else:
- self.core.information('The group could not be set', 'Error')
- log.debug('Error in groupmove:\n%s', iq)
-
- self.core.xmpp.update_roster(
- jid,
- name=name,
- groups=new_groups,
- subscription=subscription,
- callback=callback)
+ try:
+ await self.core.xmpp.update_roster(
+ jid,
+ name=name,
+ groups=new_groups,
+ subscription=subscription,
+ )
+ roster.update_contact_groups(contact)
+ except (IqError, IqTimeout):
+ self.core.information('The group could not be set', 'Error')
@deny_anonymous
@command_args_parser.quoted(2)
- def command_groupremove(self, args):
+ async def command_groupremove(self, args):
"""
Remove the specified JID from the specified group
"""
if args is None:
return self.core.command.help('groupremove')
- jid = safeJID(args[0]).bare
+ try:
+ jid = JID(args[0]).bare
+ except InvalidJID:
+ self.core.information(f'Invalid JID: {args[0]}', 'Error')
+ return
group = args[1]
contact = roster[jid]
@@ -774,39 +711,16 @@ class RosterInfoTab(Tab):
new_groups.remove(group)
name = contact.name
subscription = contact.subscription
-
- def callback(iq):
- if iq:
- roster.update_contact_groups(jid)
- else:
- self.core.information('The group could not be set')
- log.debug('Error in groupremove:\n%s', iq)
-
- self.core.xmpp.update_roster(
- jid,
- name=name,
- groups=new_groups,
- subscription=subscription,
- callback=callback)
-
- @deny_anonymous
- @command_args_parser.quoted(0, 1)
- def command_remove(self, args):
- """
- Remove the specified JID from the roster. i.e.: unsubscribe
- from its presence, and cancel its subscription to our.
- """
- if args:
- jid = safeJID(args[0]).bare
- else:
- item = self.roster_win.selected_row
- if isinstance(item, Contact):
- jid = item.bare_jid
- else:
- self.core.information('No roster item to remove', 'Error')
- return
- roster.remove(jid)
- del roster[jid]
+ try:
+ self.core.xmpp.update_roster(
+ jid,
+ name=name,
+ groups=new_groups,
+ subscription=subscription,
+ )
+ roster.update_contact_groups(jid)
+ except (IqError, IqTimeout):
+ self.core.information('The group could not be set')
@deny_anonymous
@command_args_parser.quoted(0, 1)
@@ -932,16 +846,6 @@ class RosterInfoTab(Tab):
the_input.new_completion, groups, n, '', quotify=True)
return False
- def completion_deny(self, the_input):
- """
- Complete the first argument from the list of the
- contact with ask=='subscribe'
- """
- jids = sorted(
- str(contact.bare_jid) for contact in roster.contacts.values()
- if contact.pending_in)
- return Completion(the_input.new_completion, jids, 1, '', quotify=False)
-
def refresh(self):
if self.need_resize:
self.resize()
@@ -982,7 +886,7 @@ class RosterInfoTab(Tab):
Show or hide offline contacts
"""
option = 'roster_show_offline'
- value = config.get(option)
+ value = config.getbool(option)
success = config.silent_set(option, str(not value))
roster.modified()
if not success:
@@ -1126,15 +1030,6 @@ class RosterInfoTab(Tab):
'%s connected resource%s' % (len(cont), ''
if len(cont) == 1 else 's'))
acc.append('Current status: %s' % res.status)
- if cont.tune:
- acc.append('Tune: %s' % common.format_tune_string(cont.tune))
- if cont.mood:
- acc.append('Mood: %s' % cont.mood)
- if cont.activity:
- acc.append('Activity: %s' % cont.activity)
- if cont.gaming:
- acc.append(
- 'Game: %s' % (common.format_gaming_string(cont.gaming)))
msg = '\n'.join(acc)
elif isinstance(selected_row, Resource):
res = selected_row
@@ -1160,7 +1055,7 @@ class RosterInfoTab(Tab):
if isinstance(selected_row, Contact):
jid = selected_row.bare_jid
elif isinstance(selected_row, Resource):
- jid = safeJID(selected_row.jid).bare
+ jid = JID(selected_row.jid).bare
else:
return
self.on_slash()
@@ -1242,8 +1137,11 @@ def jid_and_name_match(contact, txt):
if not txt:
return True
txt = txt.lower()
- if txt in safeJID(contact.bare_jid).bare.lower():
- return True
+ try:
+ if txt in JID(contact.bare_jid).bare.lower():
+ return True
+ except InvalidJID:
+ pass
if txt in contact.name.lower():
return True
return False
@@ -1256,9 +1154,12 @@ def jid_and_name_match_slow(contact, txt):
"""
if not txt:
return True # Everything matches when search is empty
- user = safeJID(contact.bare_jid).bare
- if diffmatch(txt, user):
- return True
+ try:
+ user = JID(contact.bare_jid).bare
+ if diffmatch(txt, user):
+ return True
+ except InvalidJID:
+ pass
if contact.name and diffmatch(txt, contact.name):
return True
return False