diff options
Diffstat (limited to 'src/tabs/rostertab.py')
-rw-r--r-- | src/tabs/rostertab.py | 546 |
1 files changed, 393 insertions, 153 deletions
diff --git a/src/tabs/rostertab.py b/src/tabs/rostertab.py index 878e89ed..aaff7de3 100644 --- a/src/tabs/rostertab.py +++ b/src/tabs/rostertab.py @@ -5,15 +5,16 @@ rectangle shows the current contact info. This module also includes functions to match users in the roster. """ -from gettext import gettext as _ - import logging log = logging.getLogger(__name__) +import base64 import curses import difflib import os +import ssl from os import getenv, path +from functools import partial from . import Tab @@ -25,6 +26,7 @@ from contact import Contact, Resource from decorators import refresh_wrapper from roster import RosterGroup, roster from theming import get_theme, dump_tuple +from decorators import command_args_parser class RosterInfoTab(Tab): """ @@ -44,107 +46,315 @@ class RosterInfoTab(Tab): self.input = self.default_help_message self.state = 'normal' self.key_func['^I'] = self.completion - self.key_func[' '] = self.on_space self.key_func["/"] = self.on_slash - self.key_func["KEY_UP"] = self.move_cursor_up - self.key_func["KEY_DOWN"] = self.move_cursor_down - self.key_func["M-u"] = self.move_cursor_to_next_contact - self.key_func["M-y"] = self.move_cursor_to_prev_contact - self.key_func["M-U"] = self.move_cursor_to_next_group - self.key_func["M-Y"] = self.move_cursor_to_prev_group - self.key_func["M-[1;5B"] = self.move_cursor_to_next_group - self.key_func["M-[1;5A"] = self.move_cursor_to_prev_group - self.key_func["l"] = self.command_last_activity - self.key_func["o"] = self.toggle_offline_show - self.key_func["v"] = self.get_contact_version - self.key_func["i"] = self.show_contact_info - self.key_func["n"] = self.change_contact_name - self.key_func["s"] = self.start_search - self.key_func["S"] = self.start_search_slow - 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 his/here roster.'), - shortdesc=_('Deny an user your presence.'), - completion=self.completion_deny) - self.register_command('accept', self.command_accept, - usage=_('[jid]'), - desc=_('Allow the provided JID (or the selected contact in your roster), to see your presence.'), - shortdesc=_('Allow an user your presence.'), - completion=self.completion_deny) - self.register_command('add', self.command_add, - usage=_('<jid>'), - desc=_('Add the specified JID to your roster, ask him to allow you to see his presence, and allow him to see your presence.'), - shortdesc=_('Add an user to your roster.')) - self.register_command('name', self.command_name, - usage=_('<jid> <name>'), - shortdesc=_('Set the given JID\'s name.'), - completion=self.completion_name) - self.register_command('groupadd', self.command_groupadd, - usage=_('<jid> <group>'), - desc=_('Add the given JID to the given group.'), - shortdesc=_('Add an user to a group'), - completion=self.completion_groupadd) - self.register_command('groupmove', self.command_groupmove, - usage=_('<jid> <old group> <new group>'), - desc=_('Move the given JID from the old group to the new group.'), - shortdesc=_('Move an user to another group.'), - completion=self.completion_groupmove) - self.register_command('groupremove', self.command_groupremove, - usage=_('<jid> <group>'), - desc=_('Remove the given JID from the given group.'), - shortdesc=_('Remove an 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 wil unsubscribe you from its presence, cancel its subscription to yours, and remove the item from your roster.'), - shortdesc=_('Remove an user from your roster.'), - completion=self.completion_remove) + # disable most of the roster features when in anonymous mode + if not self.core.xmpp.anon: + self.key_func[' '] = self.on_space + self.key_func["KEY_UP"] = self.move_cursor_up + self.key_func["KEY_DOWN"] = self.move_cursor_down + self.key_func["M-u"] = self.move_cursor_to_next_contact + self.key_func["M-y"] = self.move_cursor_to_prev_contact + self.key_func["M-U"] = self.move_cursor_to_next_group + self.key_func["M-Y"] = self.move_cursor_to_prev_group + self.key_func["M-[1;5B"] = self.move_cursor_to_next_group + self.key_func["M-[1;5A"] = self.move_cursor_to_prev_group + self.key_func["l"] = self.command_last_activity + self.key_func["o"] = self.toggle_offline_show + self.key_func["v"] = self.get_contact_version + self.key_func["i"] = self.show_contact_info + self.key_func["s"] = self.start_search + 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 his/here roster.', + shortdesc='Deny an user your presence.', + completion=self.completion_deny) + self.register_command('accept', self.command_accept, + usage='[jid]', + desc='Allow the provided JID (or the selected contact ' + 'in your roster), to see your presence.', + shortdesc='Allow an user your presence.', + completion=self.completion_deny) + self.register_command('add', self.command_add, + usage='<jid>', + desc='Add the specified JID to your roster, ask him to' + ' allow you to see his presence, and allow him to' + ' see your presence.', + shortdesc='Add an user to your roster.') + self.register_command('name', self.command_name, + usage='<jid> [name]', + shortdesc='Set the given JID\'s name.', + completion=self.completion_name) + self.register_command('groupadd', self.command_groupadd, + usage='<jid> <group>', + desc='Add the given JID to the given group.', + shortdesc='Add an user to a group', + completion=self.completion_groupadd) + self.register_command('groupmove', self.command_groupmove, + usage='<jid> <old group> <new group>', + desc='Move the given JID from the old group to the new group.', + shortdesc='Move an user to another group.', + completion=self.completion_groupmove) + self.register_command('groupremove', self.command_groupremove, + usage='<jid> <group>', + desc='Remove the given JID from the given group.', + shortdesc='Remove an 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 an user from your roster.', + completion=self.completion_remove) + self.register_command('export', self.command_export, + usage='[/path/to/file]', + desc='Export your contacts into /path/to/file if ' + 'specified, or $HOME/poezio_contacts if not.', + shortdesc='Export your roster to a file.', + completion=partial(self.completion_file, 1)) + self.register_command('import', self.command_import, + usage='[/path/to/file]', + desc='Import your contacts from /path/to/file if ' + 'specified, or $HOME/poezio_contacts if not.', + shortdesc='Import your roster from a file.', + completion=partial(self.completion_file, 1)) + self.register_command('password', self.command_password, + usage='<password>', + shortdesc='Change your password') + self.register_command('reconnect', self.command_reconnect, - desc=_('Disconnect from the remote server if you are currently connected and then connect to it again.'), - shortdesc=_('Disconnect and reconnect to the server.')) + desc='Disconnect from the remote server if you are ' + 'currently connected and then connect to it again.', + shortdesc='Disconnect and reconnect to the server.') self.register_command('disconnect', self.command_disconnect, - desc=_('Disconnect from the remote server.'), - shortdesc=_('Disconnect from the server.')) - self.register_command('export', self.command_export, - usage=_('[/path/to/file]'), - desc=_('Export your contacts into /path/to/file if specified, or $HOME/poezio_contacts if not.'), - shortdesc=_('Export your roster to a file.'), - completion=self.completion_file) - self.register_command('import', self.command_import, - usage=_('[/path/to/file]'), - desc=_('Import your contacts from /path/to/file if specified, or $HOME/poezio_contacts if not.'), - shortdesc=_('Import your roster from a file.'), - completion=self.completion_file) + desc='Disconnect from the remote server.', + shortdesc='Disconnect from the server.') self.register_command('clear', self.command_clear, - shortdesc=_('Clear the info buffer.')) + shortdesc='Clear the info buffer.') self.register_command('last_activity', self.command_last_activity, - usage=_('<jid>'), - desc=_('Informs you of the last activity of a JID.'), - shortdesc=_('Get the activity of someone.'), + usage='<jid>', + desc='Informs you of the last activity of a JID.', + shortdesc='Get the activity of someone.', completion=self.core.completion_last_activity) - self.register_command('password', self.command_password, - usage='<password>', - shortdesc=_('Change your password')) self.resize() self.update_commands() self.update_keys() def check_blocking(self, features): - if 'urn:xmpp:blocking' in features: + if 'urn:xmpp:blocking' in features and not self.core.xmpp.anon: self.register_command('block', self.command_block, - usage=_('[jid]'), - shortdesc=_('Prevent a JID from talking to you.'), + usage='[jid]', + shortdesc='Prevent a JID from talking to you.', completion=self.completion_block) self.register_command('unblock', self.command_unblock, - usage=_('[jid]'), - shortdesc=_('Allow a JID to talk to you.'), + usage='[jid]', + shortdesc='Allow a JID to talk to you.', completion=self.completion_unblock) self.register_command('list_blocks', self.command_list_blocks, - shortdesc=_('Show the blocked contacts.')) + shortdesc='Show the blocked contacts.') self.core.xmpp.del_event_handler('session_start', self.check_blocking) self.core.xmpp.add_event_handler('blocked_message', self.on_blocked_message) + def check_saslexternal(self, features): + if 'urn:xmpp:saslcert:1' in features and not self.core.xmpp.anon: + self.register_command('certs', self.command_certs, + desc='List the fingerprints of certificates' + ' which can connect to your account.', + shortdesc='List allowed client certs.') + self.register_command('cert_add', self.command_cert_add, + desc='Add a client certificate to the authorized ones. ' + 'It must have an unique name and be contained in ' + 'a PEM file. [management] is a boolean indicating' + ' if a client connected using this certificate can' + ' manage the certificates itself.', + shortdesc='Add a client certificate.', + usage='<name> <certificate path> [management]', + completion=self.completion_cert_add) + self.register_command('cert_disable', self.command_cert_disable, + desc='Remove a certificate from the list ' + 'of allowed ones. Clients currently ' + 'using this certificate will not be ' + 'forcefully disconnected.', + shortdesc='Disable a certificate', + usage='<name>') + self.register_command('cert_revoke', self.command_cert_revoke, + desc='Remove a certificate from the list ' + 'of allowed ones. Clients currently ' + 'using this certificate will be ' + 'forcefully disconnected.', + shortdesc='Revoke a certificate', + usage='<name>') + self.register_command('cert_fetch', self.command_cert_fetch, + desc='Retrieve a certificate with its ' + 'name. It will be stored in <path>.', + shortdesc='Fetch a certificate', + usage='<name> <path>', + completion=self.completion_cert_fetch) + + @command_args_parser.ignored + 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) + + @command_args_parser.quoted(2, 1) + 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: + with open(args[1]) as fd: + crt = fd.read() + crt = crt.replace(ssl.PEM_FOOTER, '').replace(ssl.PEM_HEADER, '').replace(' ', '').replace('\n', '') + except Exception as e: + self.core.information('Unable to read the certificate: %s' % e, 'Error') + return + + if len(args) > 2: + management = args[2] + if management: + management = management.lower() + if management not in ('false', '0'): + management = True + else: + management = False + else: + management = False + else: + management = True + + self.core.xmpp.plugin['xep_0257'].add_cert(name, crt, callback=cb, + allow_management=management) + + def completion_cert_add(self, the_input): + """ + completion for /cert_add <name> <path> [management] + """ + text = the_input.get_text() + args = common.shell_split(text) + n = the_input.get_argument_position() + log.debug('%s %s %s', the_input.text, n, the_input.pos) + if n == 1: + return + elif n == 2: + return self.completion_file(2, the_input) + elif n == 3: + return the_input.new_completion(['true', 'false'], n) + + @command_args_parser.quoted(1) + 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) + + @command_args_parser.quoted(1) + 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) + + + @command_args_parser.quoted(2) + 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) + + def completion_cert_fetch(self, the_input): + """ + completion for /cert_fetch <name> <path> + """ + text = the_input.get_text() + args = common.shell_split(text) + n = the_input.get_argument_position() + log.debug('%s %s %s', the_input.text, n, the_input.pos) + if n == 1: + return + elif n == 2: + return self.completion_file(2, the_input) + def on_blocked_message(self, message): """ When we try to send a message to a blocked contact @@ -158,7 +368,8 @@ class RosterInfoTab(Tab): } tab.add_message(message) - def command_block(self, arg): + @command_args_parser.quoted(0, 1) + def command_block(self, args): """ /block [jid] """ @@ -169,8 +380,8 @@ class RosterInfoTab(Tab): return self.core.information('Contact blocked.', 'Info') item = self.roster_win.selected_row - if arg: - jid = safeJID(arg) + if args: + jid = safeJID(args[0]) elif isinstance(item, Contact): jid = item.bare_jid elif isinstance(item, Resource): @@ -185,7 +396,8 @@ class RosterInfoTab(Tab): jids = roster.jids() return the_input.new_completion(jids, 1, '', quotify=False) - def command_unblock(self, arg): + @command_args_parser.quoted(0, 1) + def command_unblock(self, args): """ /unblock [jid] """ @@ -196,8 +408,8 @@ class RosterInfoTab(Tab): return self.core.information('Contact unblocked.', 'Info') item = self.roster_win.selected_row - if arg: - jid = safeJID(arg) + if args: + jid = safeJID(args[0]) elif isinstance(item, Contact): jid = item.bare_jid elif isinstance(item, Resource): @@ -218,7 +430,8 @@ class RosterInfoTab(Tab): self.core.xmpp.plugin['xep_0191'].get_blocked(callback=on_result) return True - def command_list_blocks(self, arg=None): + @command_args_parser.ignored + def command_list_blocks(self): """ /list_blocks """ @@ -236,7 +449,8 @@ class RosterInfoTab(Tab): self.core.xmpp.plugin['xep_0191'].get_blocked(callback=callback) - def command_reconnect(self, args=None): + @command_args_parser.ignored + def command_reconnect(self): """ /reconnect """ @@ -245,19 +459,21 @@ class RosterInfoTab(Tab): else: self.core.xmpp.connect() - def command_disconnect(self, args=None): + @command_args_parser.ignored + def command_disconnect(self): """ /disconnect """ self.core.disconnect() - def command_last_activity(self, arg=None): + @command_args_parser.quoted(0, 1) + def command_last_activity(self, args): """ /activity [jid] """ item = self.roster_win.selected_row - if arg: - jid = arg + if args: + jid = args[0] elif isinstance(item, Contact): jid = item.bare_jid elif isinstance(item, Resource): @@ -311,31 +527,45 @@ class RosterInfoTab(Tab): not self.input.help_message: self.complete_commands(self.input) - def completion_file(self, the_input): + def completion_file(self, complete_number, the_input): """ - Completion for /import and /export + Generic quoted completion for files/paths + (use functools.partial to use directly as a completion + for a command) """ text = the_input.get_text() - args = text.split() - n = len(args) - if n == 1: - home = os.getenv('HOME') or '/' - return the_input.auto_completion([home, '/tmp'], '') - else: - the_path = text[text.index(' ')+1:] + args = common.shell_split(text) + n = the_input.get_argument_position() + if n == complete_number: + if args[n-1] == '' or len(args) < n+1: + home = os.getenv('HOME') or '/' + return the_input.new_completion([home, '/tmp'], n, quotify=True) + path_ = args[n] + if path.isdir(path_): + dir_ = path_ + base = '' + else: + dir_ = path.dirname(path_) + base = path.basename(path_) try: - names = os.listdir(the_path) - except: + names = os.listdir(dir_) + except OSError: names = [] + names_filtered = [name for name in names if name.startswith(base)] + if names_filtered: + names = names_filtered + if not names: + names = [path_] end_list = [] for name in names: - value = os.path.join(the_path, name) + value = os.path.join(dir_, name) if not name.startswith('.'): end_list.append(value) - return the_input.auto_completion(end_list, '') + return the_input.new_completion(end_list, n, quotify=True) - def command_clear(self, arg=''): + @command_args_parser.ignored + def command_clear(self): """ /clear """ @@ -344,7 +574,8 @@ class RosterInfoTab(Tab): self.core.information_win.rebuild_everything(self.core.information_buffer) self.refresh() - def command_password(self, arg): + @command_args_parser.quoted(1) + def command_password(self, args): """ /password <password> """ @@ -352,19 +583,18 @@ class RosterInfoTab(Tab): if iq['type'] == 'result': self.core.information('Password updated', 'Account') if config.get('password'): - config.silent_set('password', arg) + config.silent_set('password', args[0]) else: self.core.information('Unable to change the password', 'Account') - self.core.xmpp.plugin['xep_0077'].change_password(arg, callback=callback) - + self.core.xmpp.plugin['xep_0077'].change_password(args[0], callback=callback) - - def command_deny(self, arg): + @command_args_parser.quoted(0, 1) + def command_deny(self, args): """ /deny [jid] Denies a JID from our roster """ - if not arg: + if not args: item = self.roster_win.selected_row if isinstance(item, Contact): jid = item.bare_jid @@ -372,7 +602,7 @@ class RosterInfoTab(Tab): self.core.information('No subscription to deny') return else: - jid = safeJID(arg).bare + jid = safeJID(args[0]).bare if not jid in [jid for jid in roster.jids()]: self.core.information('No subscription to deny') return @@ -383,14 +613,15 @@ class RosterInfoTab(Tab): self.core.information('Subscription to %s was revoked' % jid, 'Roster') + @command_args_parser.quoted(1) def command_add(self, args): """ Add the specified JID to the roster, and set automatically accept the reverse subscription """ - jid = safeJID(safeJID(args.strip()).bare) + jid = safeJID(safeJID(args[0]).bare) if not jid: - self.core.information(_('No JID specified'), 'Error') + self.core.information('No JID specified', 'Error') return if jid in roster and roster[jid].subscription in ('to', 'both'): return self.core.information('Already subscribed.', 'Roster') @@ -398,7 +629,8 @@ class RosterInfoTab(Tab): roster.modified() self.core.information('%s was added to the roster' % jid, 'Roster') - def command_name(self, arg): + @command_args_parser.quoted(1, 1) + def command_name(self, args): """ Set a name for the specified JID in your roster """ @@ -406,15 +638,14 @@ class RosterInfoTab(Tab): if not iq: self.core.information('The name could not be set.', 'Error') log.debug('Error in /name:\n%s', iq) - args = common.shell_split(arg) - if not args: + if args is None: return self.core.command_help('name') jid = safeJID(args[0]).bare name = args[1] if len(args) == 2 else '' contact = roster[jid] if contact is None: - self.core.information(_('No such JID in roster'), 'Error') + self.core.information('No such JID in roster', 'Error') return groups = set(contact.groups) @@ -424,24 +655,24 @@ class RosterInfoTab(Tab): self.core.xmpp.update_roster(jid, name=name, groups=groups, subscription=subscription, callback=callback) + @command_args_parser.quoted(2) def command_groupadd(self, args): """ Add the specified JID to the specified group """ - args = common.shell_split(args) - if len(args) != 2: - return + if args is None: + return self.core.command_help('groupadd') jid = safeJID(args[0]).bare group = args[1] contact = roster[jid] if contact is None: - self.core.information(_('No such JID in roster'), 'Error') + self.core.information('No such JID in roster', 'Error') return new_groups = set(contact.groups) if group in new_groups: - self.core.information(_('JID already in group'), 'Error') + self.core.information('JID already in group', 'Error') return roster.modified() @@ -464,12 +695,12 @@ class RosterInfoTab(Tab): self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription, callback=callback) - def command_groupmove(self, arg): + @command_args_parser.quoted(3) + def command_groupmove(self, args): """ Remove the specified JID from the first specified group and add it to the second one """ - args = common.shell_split(arg) - if len(args) != 3: + if args is None: return self.core.command_help('groupmove') jid = safeJID(args[0]).bare group_from = args[1] @@ -477,7 +708,7 @@ class RosterInfoTab(Tab): contact = roster[jid] if not contact: - self.core.information(_('No such JID in roster'), 'Error') + self.core.information('No such JID in roster', 'Error') return new_groups = set(contact.groups) @@ -485,19 +716,19 @@ class RosterInfoTab(Tab): new_groups.remove('none') if group_to == 'none' or group_from == 'none': - self.core.information(_('"none" is not a group.'), 'Error') + self.core.information('"none" is not a group.', 'Error') return if group_from not in new_groups: - self.core.information(_('JID not in first group'), 'Error') + self.core.information('JID not in first group', 'Error') return if group_to in new_groups: - self.core.information(_('JID already in second group'), 'Error') + self.core.information('JID already in second group', 'Error') return if group_to == group_from: - self.core.information(_('The groups are the same.'), 'Error') + self.core.information('The groups are the same.', 'Error') return roster.modified() @@ -519,19 +750,20 @@ class RosterInfoTab(Tab): self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription, callback=callback) + @command_args_parser.quoted(2) def command_groupremove(self, args): """ Remove the specified JID from the specified group """ - args = common.shell_split(args) - if len(args) != 2: - return + if args is None: + return self.core.command_help('groupremove') + jid = safeJID(args[0]).bare group = args[1] contact = roster[jid] if contact is None: - self.core.information(_('No such JID in roster'), 'Error') + self.core.information('No such JID in roster', 'Error') return new_groups = set(contact.groups) @@ -540,7 +772,7 @@ class RosterInfoTab(Tab): except KeyError: pass if group not in new_groups: - self.core.information(_('JID not in group'), 'Error') + self.core.information('JID not in group', 'Error') return roster.modified() @@ -559,13 +791,14 @@ class RosterInfoTab(Tab): self.core.xmpp.update_roster(jid, name=name, groups=new_groups, subscription=subscription, callback=callback) + @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.strip(): - jid = safeJID(args.strip()).bare + if args: + jid = safeJID(args[0]).bare else: item = self.roster_win.selected_row if isinstance(item, Contact): @@ -576,12 +809,12 @@ class RosterInfoTab(Tab): roster.remove(jid) del roster[jid] - def command_import(self, arg): + @command_args_parser.quoted(0, 1) + def command_import(self, args): """ Import the contacts """ - args = common.shell_split(arg) - if len(args): + if args: if args[0].startswith('/'): filepath = args[0] else: @@ -603,12 +836,12 @@ class RosterInfoTab(Tab): self.command_add(jid.lstrip('\n')) self.core.information('Contacts imported from %s' % filepath, 'Info') - def command_export(self, arg): + @command_args_parser.quoted(0, 1) + def command_export(self, args): """ Export the contacts """ - args = common.shell_split(arg) - if len(args): + if args: if args[0].startswith('/'): filepath = args[0] else: @@ -697,11 +930,12 @@ class RosterInfoTab(Tab): if contact.pending_in) return the_input.new_completion(jids, 1, '', quotify=False) - def command_accept(self, arg): + @command_args_parser.quoted(0, 1) + def command_accept(self, args): """ Accept a JID from in roster. Authorize it AND subscribe to it """ - if not arg: + if not args: item = self.roster_win.selected_row if isinstance(item, Contact): jid = item.bare_jid @@ -709,7 +943,7 @@ class RosterInfoTab(Tab): self.core.information('No subscription to accept') return else: - jid = safeJID(arg).bare + jid = safeJID(args[0]).bare nodepart = safeJID(jid).user jid = safeJID(jid) # crappy transports putting resources inside the node part @@ -769,13 +1003,15 @@ class RosterInfoTab(Tab): success = config.silent_set(option, str(not value)) roster.modified() if not success: - self.core.information(_('Unable to write in the config file'), 'Error') + self.core.information('Unable to write in the config file', 'Error') return True def on_slash(self): """ '/' is pressed, we enter "input mode" """ + if isinstance(self.input, windows.YesNoInput): + return curses.curs_set(1) self.input = windows.CommandInput("", self.reset_help_message, self.execute_slash_command) self.input.resize(1, self.width, self.height-1, 0) @@ -951,6 +1187,8 @@ class RosterInfoTab(Tab): Start the search. The input should appear with a short instruction in it. """ + if isinstance(self.input, windows.YesNoInput): + return curses.curs_set(1) self.input = windows.CommandInput("[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter) self.input.resize(1, self.width, self.height-1, 0) @@ -961,6 +1199,8 @@ class RosterInfoTab(Tab): @refresh_wrapper.always def start_search_slow(self): + if isinstance(self.input, windows.YesNoInput): + return curses.curs_set(1) self.input = windows.CommandInput("[Search]", self.on_search_terminate, self.on_search_terminate, self.set_roster_filter_slow) self.input.resize(1, self.width, self.height-1, 0) |