diff --git a/plugins/otr.py b/plugins/otr.py index b3848d2d..23843788 100644 --- a/plugins/otr.py +++ b/plugins/otr.py @@ -184,7 +184,6 @@ and :term:`log` configuration parameters are tab-specific. from gettext import gettext as _ import logging -log = logging.getLogger(__name__) import os import html import curses @@ -194,10 +193,11 @@ import potr from potr.context import NotEncryptedError, UnencryptedMessage, ErrorReceived, NotOTRMessage,\ STATE_ENCRYPTED, STATE_PLAINTEXT, STATE_FINISHED, Context, Account, crypt +from slixmpp import JID, InvalidJID + from poezio import common from poezio import xdg from poezio import xhtml -from poezio.common import safeJID from poezio.config import config from poezio.plugin import BasePlugin from poezio.roster import roster @@ -207,6 +207,8 @@ from poezio.decorators import command_args_parser from poezio.core.structs import Completion from poezio.ui.types import InfoMessage, Message +log = logging.getLogger(__name__) + POLICY_FLAGS = { 'ALLOW_V1': False, 'ALLOW_V2': True, @@ -345,7 +347,7 @@ class PoezioContext(Context): self.xmpp = xmpp self.core = core self.flags = {} - self.trustName = safeJID(peer).bare + self.trustName = JID(peer).bare self.in_smp = False self.smp_own = False self.log = 0 @@ -375,7 +377,7 @@ class PoezioContext(Context): 'info': '\x19%s}' % dump_tuple(get_theme().COLOR_INFORMATION_TEXT), 'normal': '\x19%s}' % dump_tuple(get_theme().COLOR_NORMAL_TEXT), 'jid': self.peer, - 'bare_jid': safeJID(self.peer).bare + 'bare_jid': JID(self.peer).bare } tab = self.core.tabs.by_name(self.peer) @@ -461,8 +463,9 @@ class PoezioAccount(Account): if acc != self.name or proto != 'xmpp': continue - jid = safeJID(ctx).bare - if not jid: + try: + jid = JID(ctx).bare + except InvalidJID: continue self.setTrust(jid, fpr, trust) except: @@ -595,7 +598,7 @@ class Plugin(BasePlugin): """ Retrieve or create an OTR context """ - jid = safeJID(jid) + jid = JID(jid) if jid.full not in self.contexts: flags = POLICY_FLAGS.copy() require = self.config.get_by_tabname( @@ -806,9 +809,11 @@ class Plugin(BasePlugin): Find an OTR session from a bare JID. """ for ctx in self.contexts: - if safeJID( - ctx - ).bare == bare_jid and self.contexts[ctx].state == STATE_ENCRYPTED: + try: + jid = JID(ctx).bare + except InvalidJID: + continue + if jid == bare_jid and self.contexts[ctx].state == STATE_ENCRYPTED: return self.contexts[ctx] return None @@ -880,7 +885,11 @@ class Plugin(BasePlugin): Returns the text to display in the infobar (the OTR status) """ context = self.get_context(jid) - if safeJID(jid).bare == jid and context.state != STATE_ENCRYPTED: + try: + bare_jid = JID(jid).bare + except InvalidJID: + bare_jid = '' + if bare_jid == jid and context.state != STATE_ENCRYPTED: ctx = self.find_encrypted_context_with_matching(jid) if ctx: context = ctx diff --git a/plugins/qr.py b/plugins/qr.py index a6d325aa..735c3002 100755 --- a/plugins/qr.py +++ b/plugins/qr.py @@ -6,9 +6,10 @@ import qrcode from typing import Dict, Callable +from slixmpp import JID, InvalidJID + from poezio import windows from poezio.tabs import Tab -from poezio.common import safeJID from poezio.core.structs import Command from poezio.decorators import command_args_parser from poezio.plugin import BasePlugin @@ -170,7 +171,11 @@ class Plugin(BasePlugin): def command_invite(self, args): server = self.core.xmpp.boundjid.domain if len(args) > 0: - server = safeJID(args[0]) + try: + server = JID(args[0]) + except InvalidJID: + self.api.information(f'Invalid JID: {args[0]}', 'Error') + return session = { 'next' : self.on_next, 'error': self.core.handler.adhoc_error diff --git a/plugins/server_part.py b/plugins/server_part.py index f29b4099..cae2248e 100644 --- a/plugins/server_part.py +++ b/plugins/server_part.py @@ -16,10 +16,10 @@ Command """ +from slixmpp import JID, InvalidJID from poezio.plugin import BasePlugin from poezio.tabs import MucTab from poezio.decorators import command_args_parser -from poezio.common import safeJID from poezio.core.structs import Completion @@ -42,13 +42,15 @@ class Plugin(BasePlugin): jid = current_tab.jid.bare message = None elif len(args) == 1: - jid = safeJID(args[0]).domain - if not jid: + try: + jid = JID(args[0]).domain + except InvalidJID: return self.core.command_help('server_part') message = None else: - jid = safeJID(args[0]).domain - if not jid: + try: + jid = JID(args[0]).domain + except InvalidJID: return self.core.command_help('server_part') message = args[1] diff --git a/plugins/uptime.py b/plugins/uptime.py index eca89cc5..a55af970 100644 --- a/plugins/uptime.py +++ b/plugins/uptime.py @@ -12,7 +12,7 @@ Command Retrieve the uptime of the server of ``jid``. """ from poezio.plugin import BasePlugin -from poezio.common import parse_secs_to_str, safeJID +from poezio.common import parse_secs_to_str from slixmpp.xmlstream import ET from slixmpp import JID, InvalidJID from slixmpp.exceptions import IqError, IqTimeout diff --git a/poezio/common.py b/poezio/common.py index f80555ac..d4d09f9f 100644 --- a/poezio/common.py +++ b/poezio/common.py @@ -459,24 +459,6 @@ def format_gaming_string(infos: Dict[str, str]) -> str: return name -def safeJID(*args: Any, **kwargs: Any) -> JID: - """ - Construct a :py:class:`slixmpp.JID` object from a string. - - Used to avoid tracebacks during is stringprep fails - (fall back to a JID with an empty string). - """ - try: - return JID(*args, **kwargs) - except InvalidJID: - log.debug( - 'safeJID caught an invalidJID exception: %r, %r', - args, kwargs, - exc_info=True, - ) - return JID('') - - def unique_prefix_of(a: str, b: str) -> str: """ Return the unique prefix of `a` with `b`. diff --git a/poezio/core/completions.py b/poezio/core/completions.py index 9b218c9a..af71de66 100644 --- a/poezio/core/completions.py +++ b/poezio/core/completions.py @@ -2,25 +2,23 @@ Completions for the global commands """ import logging +import os +from functools import reduce +from pathlib import Path from typing import List, Optional -log = logging.getLogger(__name__) - -import os -from pathlib import Path -from functools import reduce - -from slixmpp import JID +from slixmpp import JID, InvalidJID from poezio import common from poezio import tabs from poezio import xdg -from poezio.common import safeJID from poezio.config import config from poezio.roster import roster from poezio.core.structs import POSSIBLE_SHOW, Completion +log = logging.getLogger(__name__) + class CompletionCore: def __init__(self, core): @@ -124,9 +122,12 @@ class CompletionCore: return False if len(args) == 1: args.append('') - jid = safeJID(args[1]) - if args[1].endswith('@') and not jid.user and not jid.server: - jid.user = args[1][:-1] + try: + jid = JID(args[1]) + except InvalidJID: + jid = JID('') + if args[1].endswith('@'): + jid.user = args[1][:-1] relevant_rooms = [] relevant_rooms.extend(sorted(self.core.pending_invites.keys())) @@ -149,7 +150,8 @@ class CompletionCore: for tab in self.core.get_tabs(tabs.MucTab): if tab.joined: serv_list.append( - '%s@%s' % (jid.user, safeJID(tab.name).host)) + '%s@%s' % (jid.user, tab.general_jid.server) + ) serv_list.extend(relevant_rooms) return Completion( the_input.new_completion, serv_list, 1, quotify=True) @@ -213,9 +215,8 @@ class CompletionCore: if len(args) == 1: args.append('') - jid = safeJID(args[1]) - - if jid.server and (jid.resource or jid.full.endswith('/')): + try: + jid = JID(args[1]) tab = self.core.tabs.by_name_and_class(jid.bare, tabs.MucTab) nicks = [tab.own_nick] if tab else [] default = os.environ.get('USER') if os.environ.get( @@ -230,6 +231,8 @@ class CompletionCore: jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks] return Completion( the_input.new_completion, jids_list, 1, quotify=True) + except InvalidJID: + pass muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)] muc_list.sort() muc_list.append('*') @@ -429,9 +432,8 @@ class CompletionCore: return False if len(args) == 1: args.append('') - jid = safeJID(args[1]) - - if jid.server and (jid.resource or jid.full.endswith('/')): + try: + jid = JID(args[1]) tab = self.core.tabs.by_name_and_class(jid.bare, tabs.MucTab) nicks = [tab.own_nick] if tab else [] default = os.environ.get('USER') if os.environ.get( @@ -446,6 +448,8 @@ class CompletionCore: jids_list = ['%s/%s' % (jid.bare, nick) for nick in nicks] return Completion( the_input.new_completion, jids_list, 1, quotify=True) + except InvalidJID: + pass muc_list = [tab.name for tab in self.core.get_tabs(tabs.MucTab)] muc_list.append('*') return Completion(the_input.new_completion, muc_list, 1, quotify=True) diff --git a/poezio/core/handlers.py b/poezio/core/handlers.py index 25639eed..980c5822 100644 --- a/poezio/core/handlers.py +++ b/poezio/core/handlers.py @@ -3,7 +3,6 @@ XMPP-related handlers for the Core class """ import logging -log = logging.getLogger(__name__) from typing import Optional @@ -30,7 +29,7 @@ from poezio import fixes from poezio import tabs from poezio import xhtml from poezio import multiuserchat as muc -from poezio.common import safeJID, get_error_message +from poezio.common import get_error_message from poezio.config import config, get_image_cache from poezio.core.structs import Status from poezio.contact import Resource @@ -58,6 +57,8 @@ try: except ImportError: PYGMENTS = False +log = logging.getLogger(__name__) + CERT_WARNING_TEXT = """ WARNING: CERTIFICATE FOR %s CHANGED @@ -249,7 +250,10 @@ class HandlerCore: """ Direct invitation received """ - room = safeJID(message['groupchat_invite']['jid']) + try: + room = JID(message['groupchat_invite']['jid']) + except InvalidJID: + return if room.bare in self.core.pending_invites: return diff --git a/poezio/multiuserchat.py b/poezio/multiuserchat.py index 4f63971a..466a3c08 100644 --- a/poezio/multiuserchat.py +++ b/poezio/multiuserchat.py @@ -19,7 +19,6 @@ from typing import ( TYPE_CHECKING, ) -from poezio.common import safeJID from slixmpp import ( JID, ClientXMPP, @@ -45,7 +44,7 @@ def change_show( """ Change our 'Show' """ - jid = safeJID(jid) + jid = JID(jid) pres = xmpp.make_presence(pto='%s/%s' % (jid, own_nick)) if show: # if show is None, don't put a tag. It means "available" pres['type'] = show @@ -66,7 +65,7 @@ def change_nick( """ xmpp = core.xmpp presence = xmpp.make_presence( - pshow=show, pstatus=status, pto=safeJID('%s/%s' % (jid, nick))) + pshow=show, pstatus=status, pto=JID('%s/%s' % (jid, nick))) core.events.trigger('changing_nick', presence) presence.send() @@ -122,7 +121,7 @@ def leave_groupchat( """ Leave the groupchat """ - jid = safeJID(jid) + jid = JID(jid) try: xmpp.plugin['xep_0045'].leave_muc(jid, own_nick, msg) except KeyError: diff --git a/poezio/roster.py b/poezio/roster.py index 86fc704c..4534dd30 100644 --- a/poezio/roster.py +++ b/poezio/roster.py @@ -8,7 +8,6 @@ Defines the Roster and RosterGroup classes """ import logging -log = logging.getLogger(__name__) from typing import List @@ -18,10 +17,10 @@ from poezio.roster_sorting import SORTING_METHODS, GROUP_SORTING_METHODS from os import path as p from datetime import datetime -from poezio.common import safeJID from slixmpp.exceptions import IqError, IqTimeout -from slixmpp import JID +from slixmpp import JID, InvalidJID +log = logging.getLogger(__name__) class Roster: """ @@ -77,7 +76,10 @@ class Roster: def __getitem__(self, key): """Get a Contact from his bare JID""" - key = safeJID(key).bare + try: + key = JID(key).bare + except InvalidJID: + return None if key in self.contacts and self.contacts[key] is not None: return self.contacts[key] if key in self.jids(): @@ -91,7 +93,10 @@ class Roster: def remove(self, jid): """Send a removal iq to the server""" - jid = safeJID(jid).bare + try: + jid = JID(jid).bare + except InvalidJID: + return if self.__node[jid]: try: self.__node[jid].send_presence(ptype='unavailable') @@ -101,7 +106,10 @@ class Roster: def __delitem__(self, jid): """Remove a contact from the roster view""" - jid = safeJID(jid).bare + try: + jid = JID(jid).bare + except InvalidJID: + return contact = self[jid] if not contact: return @@ -119,7 +127,10 @@ class Roster: def __contains__(self, key): """True if the bare jid is in the roster, false otherwise""" - return safeJID(key).bare in self.jids() + try: + return JID(key).bare in self.jids() + except InvalidJID: + return False @property def jid(self): diff --git a/poezio/tabs/bookmarkstab.py b/poezio/tabs/bookmarkstab.py index 4f4833fc..31902fa6 100644 --- a/poezio/tabs/bookmarkstab.py +++ b/poezio/tabs/bookmarkstab.py @@ -12,9 +12,8 @@ from poezio import windows from poezio.bookmarks import Bookmark, BookmarkList from poezio.core.structs import Command from poezio.tabs import Tab -from poezio.common import safeJID -from slixmpp import JID +from slixmpp import JID, InvalidJID log = logging.getLogger(__name__) @@ -82,10 +81,11 @@ class BookmarksTab(Tab): 'Duplicate bookmarks in list (saving aborted)', 'Error') return for bm in self.new_bookmarks: - if safeJID(bm.jid): + try: + JID(bm.jid) if not self.bookmarks[bm.jid]: self.bookmarks.append(bm) - else: + except InvalidJID: self.core.information( 'Invalid JID for bookmark: %s/%s' % (bm.jid, bm.nick), 'Error') diff --git a/poezio/tabs/conversationtab.py b/poezio/tabs/conversationtab.py index 2cc6aa2f..873bf0d3 100644 --- a/poezio/tabs/conversationtab.py +++ b/poezio/tabs/conversationtab.py @@ -11,17 +11,17 @@ There are two different instances of a ConversationTab: the time. """ -import asyncio import curses import logging from typing import Dict, Callable +from slixmpp import JID, InvalidJID + from poezio.tabs.basetabs import OneToOneTab, Tab from poezio import common from poezio import windows from poezio import xhtml -from poezio.common import safeJID from poezio.config import config from poezio.core.structs import Command from poezio.decorators import refresh_wrapper @@ -166,7 +166,13 @@ class ConversationTab(OneToOneTab): status = iq['last_activity']['status'] from_ = iq['from'] msg = '\x19%s}The last activity of %s was %s ago%s' - if not safeJID(from_).user: + user = '' + try: + user = JID(from_).user + except InvalidJID: + pass + + if not user: msg = '\x19%s}The uptime of %s is %s.' % ( dump_tuple(get_theme().COLOR_INFORMATION_TEXT), from_, common.parse_secs_to_str(seconds)) @@ -188,7 +194,10 @@ class ConversationTab(OneToOneTab): @command_args_parser.ignored def command_info(self): contact = roster[self.get_dest_jid()] - jid = safeJID(self.get_dest_jid()) + try: + jid = JID(self.get_dest_jid()) + except InvalidJID: + jid = JID('') if contact: if jid.resource: resource = contact[jid.full] @@ -300,7 +309,10 @@ class ConversationTab(OneToOneTab): def on_lose_focus(self): contact = roster[self.get_dest_jid()] - jid = safeJID(self.get_dest_jid()) + try: + jid = JID(self.get_dest_jid()) + except InvalidJID: + jid = JID('') if contact: if jid.resource: resource = contact[jid.full] @@ -321,7 +333,10 @@ class ConversationTab(OneToOneTab): def on_gain_focus(self): contact = roster[self.get_dest_jid()] - jid = safeJID(self.get_dest_jid()) + try: + jid = JID(self.get_dest_jid()) + except InvalidJID: + jid = JID('') if contact: if jid.resource: resource = contact[jid.full] diff --git a/poezio/tabs/rostertab.py b/poezio/tabs/rostertab.py index 5556cf77..6dd48726 100644 --- a/poezio/tabs/rostertab.py +++ b/poezio/tabs/rostertab.py @@ -16,11 +16,11 @@ from os import getenv, path from pathlib import Path from typing import Dict, Callable +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 @@ -531,7 +531,11 @@ class RosterInfoTab(Tab): """ 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] @@ -571,7 +575,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] @@ -614,7 +622,11 @@ class RosterInfoTab(Tab): """ 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] @@ -671,7 +683,11 @@ class RosterInfoTab(Tab): 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] @@ -1037,7 +1053,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() @@ -1119,8 +1135,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 @@ -1133,9 +1152,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 diff --git a/poezio/windows/bookmark_forms.py b/poezio/windows/bookmark_forms.py index b8a60763..fb8ad589 100644 --- a/poezio/windows/bookmark_forms.py +++ b/poezio/windows/bookmark_forms.py @@ -4,13 +4,13 @@ Windows used inthe bookmarkstab import curses from typing import List, Tuple, Optional -from poezio.windows import base_wins +from slixmpp import JID, InvalidJID + from poezio.windows.base_wins import Win from poezio.windows.inputs import Input from poezio.windows.data_forms import FieldInput, FieldInputMixin from poezio.theming import to_curses_attr, get_theme -from poezio.common import safeJID from poezio.bookmarks import Bookmark, BookmarkList @@ -33,14 +33,20 @@ class BookmarkJIDInput(FieldInput, Input): def __init__(self, field: Bookmark) -> None: FieldInput.__init__(self, field) Input.__init__(self) - jid = safeJID(field.jid) + try: + jid = JID(field.jid) + except InvalidJID: + jid = JID('') jid.resource = field.nick or None self.text = jid.full self.pos = len(self.text) self.color = get_theme().COLOR_NORMAL_TEXT def save(self) -> None: - jid = safeJID(self.get_text()) + try: + jid = JID(self.get_text()) + except InvalidJID: + jid = JID('') self._field.jid = jid.bare self._field.nick = jid.resource diff --git a/poezio/windows/info_wins.py b/poezio/windows/info_wins.py index 5278e3b8..23d28cc1 100644 --- a/poezio/windows/info_wins.py +++ b/poezio/windows/info_wins.py @@ -8,9 +8,9 @@ from __future__ import annotations from typing import Optional, Dict, TYPE_CHECKING, Any import logging -log = logging.getLogger(__name__) -from poezio.common import safeJID +from slixmpp import JID, InvalidJID + from poezio.config import config from poezio.windows.base_wins import Win @@ -22,6 +22,8 @@ if TYPE_CHECKING: from poezio.tabs import MucTab from poezio.windows import TextWin +log = logging.getLogger(__name__) + class InfoWin(Win): """ @@ -101,7 +103,10 @@ class PrivateInfoWin(InfoWin): to_curses_attr(get_theme().COLOR_INFORMATION_BAR)) def write_room_name(self, name): - jid = safeJID(name) + try: + jid = JID(name) + except InvalidJID: + jid = JID('') room_name, nick = jid.bare, jid.resource theme = get_theme() self.addstr(nick, to_curses_attr(theme.COLOR_PRIVATE_NAME)) @@ -158,7 +163,10 @@ class ConversationInfoWin(InfoWin): # from someone not in our roster. In this case, we display # only the maximum information from the message we can get. log.debug('Refresh: %s', self.__class__.__name__) - jid = safeJID(jid) + try: + jid = JID(jid) + except InvalidJID: + jid = JID('') if contact: if jid.resource: resource = contact[jid.full] @@ -363,7 +371,10 @@ class ConversationStatusMessageWin(InfoWin): def refresh(self, jid, contact): log.debug('Refresh: %s', self.__class__.__name__) - jid = safeJID(jid) + try: + jid = JID(jid) + except InvalidJID: + jid = JID('') if contact: if jid.resource: resource = contact[jid.full] diff --git a/test/test_common.py b/test/test_common.py index b6560fc9..3235632d 100644 --- a/test/test_common.py +++ b/test/test_common.py @@ -11,7 +11,7 @@ from poezio.common import (_datetime_tuple as datetime_tuple, get_utc_time, get_local_time, shell_split, _find_argument_quoted as find_argument_quoted, _find_argument_unquoted as find_argument_unquoted, parse_str_to_secs, - parse_secs_to_str, safeJID, unique_prefix_of) + parse_secs_to_str, unique_prefix_of) def test_utc_time(): delta = timedelta(seconds=-3600) @@ -60,10 +60,6 @@ def test_parse_secs_to_str(): with pytest.raises(TypeError): parse_secs_to_str('toto') -def test_safeJID(): - assert safeJID('toto@titi/tata') == JID('toto@titi/tata') - assert safeJID('toto@…') == JID('') - def test_unique_prefix_of__no_shared_prefix(): assert unique_prefix_of("a", "b") == "a" assert unique_prefix_of("foo", "bar") == "f"