Philipp Hörist pushed to branch master at gajim / python-nbxmpp

Commits:

30 changed files:

Changes:

  • nbxmpp/client.py
    ... ... @@ -313,7 +313,7 @@ class Client(Observable):
    313 313
             return self._jid
    
    314 314
     
    
    315 315
         def _set_bound_jid(self, jid):
    
    316
    -        self._jid = JID(jid)
    
    316
    +        self._jid = JID.from_string(jid)
    
    317 317
     
    
    318 318
         @property
    
    319 319
         def has_error(self):
    

  • nbxmpp/dispatcher.py
    ... ... @@ -362,7 +362,7 @@ class StanzaDispatcher(Observable):
    362 362
     
    
    363 363
             if name == 'iq':
    
    364 364
                 if stanza.getFrom() is None and own_jid is not None:
    
    365
    -                stanza.setFrom(own_jid.getBare())
    
    365
    +                stanza.setFrom(own_jid.bare)
    
    366 366
     
    
    367 367
             if name == 'message':
    
    368 368
                 # https://tools.ietf.org/html/rfc6120#section-8.1.1.1
    
    ... ... @@ -373,13 +373,14 @@ class StanzaDispatcher(Observable):
    373 373
                 to = stanza.getTo()
    
    374 374
                 if to is None:
    
    375 375
                     stanza.setTo(own_jid)
    
    376
    -            elif not to.bareMatch(own_jid):
    
    376
    +
    
    377
    +            elif not to.bare_match(own_jid):
    
    377 378
                     self._log.warning('Message addressed to someone else: %s',
    
    378 379
                                       stanza)
    
    379 380
                     return
    
    380 381
     
    
    381 382
                 if stanza.getFrom() is None:
    
    382
    -                stanza.setFrom(own_jid.getBare())
    
    383
    +                stanza.setFrom(own_jid.bare)
    
    383 384
     
    
    384 385
                 # Unwrap carbon
    
    385 386
                 try:
    

  • nbxmpp/examples/client.py
    ... ... @@ -80,8 +80,8 @@ class TestClient(Gtk.Window):
    80 80
     
    
    81 81
         def _create_client(self):
    
    82 82
             self._client = Client(log_context='TEST')
    
    83
    -        self._client.set_domain(self.address.getDomain())
    
    84
    -        self._client.set_username(self.address.getNode())
    
    83
    +        self._client.set_domain(self.address.domain)
    
    84
    +        self._client.set_username(self.address.localpart)
    
    85 85
             self._client.set_resource('test')
    
    86 86
     
    
    87 87
             proxy_ip = self._builder.proxy_ip.get_text()
    
    ... ... @@ -117,7 +117,7 @@ class TestClient(Gtk.Window):
    117 117
     
    
    118 118
         @property
    
    119 119
         def address(self):
    
    120
    -        return JID(self._builder.address.get_text())
    
    120
    +        return JID.from_string(self._builder.address.get_text())
    
    121 121
     
    
    122 122
         @property
    
    123 123
         def xml_box(self):
    

  • nbxmpp/modules/activity.py
    ... ... @@ -91,6 +91,6 @@ class Activity(BaseModule):
    91 91
                 if data.text:
    
    92 92
                     item.addChild('text', payload=data.text)
    
    93 93
     
    
    94
    -        jid = self._client.get_bound_jid().getBare()
    
    94
    +        jid = self._client.get_bound_jid().bare
    
    95 95
             self._client.get_module('PubSub').publish(
    
    96 96
                 jid, Namespace.ACTIVITY, item, id_='current')

  • nbxmpp/modules/adhoc.py
    ... ... @@ -41,7 +41,7 @@ class AdHoc(BaseModule):
    41 41
         @call_on_response('_command_list_received')
    
    42 42
         def request_command_list(self, jid=None):
    
    43 43
             if jid is None:
    
    44
    -            jid = self._client.get_bound_jid().getBare()
    
    44
    +            jid = self._client.get_bound_jid().bare
    
    45 45
             return get_disco_request(Namespace.DISCO_ITEMS,
    
    46 46
                                      jid,
    
    47 47
                                      node=Namespace.COMMANDS)
    

  • nbxmpp/modules/annotations.py
    ... ... @@ -38,7 +38,7 @@ class Annotations(BaseModule):
    38 38
     
    
    39 39
         @property
    
    40 40
         def domain(self):
    
    41
    -        return self._client.get_bound_jid().getDomain()
    
    41
    +        return self._client.get_bound_jid().domain
    
    42 42
     
    
    43 43
         @call_on_response('_annotations_received')
    
    44 44
         def request_annotations(self):
    
    ... ... @@ -59,7 +59,7 @@ class Annotations(BaseModule):
    59 59
             notes = []
    
    60 60
             for note in storage.getTags('note'):
    
    61 61
                 try:
    
    62
    -                jid = JID(note.getAttr('jid'))
    
    62
    +                jid = JID.from_string(note.getAttr('jid'))
    
    63 63
                 except Exception as error:
    
    64 64
                     self._log.warning('Invalid JID: %s, %s',
    
    65 65
                                       note.getAttr('jid'), error)
    

  • nbxmpp/modules/bookmarks.py
    ... ... @@ -131,7 +131,7 @@ class Bookmarks(BaseModule):
    131 131
             confs = storage.getTags('conference')
    
    132 132
             for conf in confs:
    
    133 133
                 try:
    
    134
    -                jid = JID(conf.getAttr('jid'))
    
    134
    +                jid = JID.from_string(conf.getAttr('jid'))
    
    135 135
                 except Exception as error:
    
    136 136
                     self._log.warning('Invalid JID: "%s", %s',
    
    137 137
                                       conf.getAttr('jid'),
    
    ... ... @@ -161,7 +161,7 @@ class Bookmarks(BaseModule):
    161 161
                 return None
    
    162 162
     
    
    163 163
             try:
    
    164
    -            jid = JID(item.getAttr('id'))
    
    164
    +            jid = JID.from_string(item.getAttr('id'))
    
    165 165
             except Exception as error:
    
    166 166
                 self._log.warning('Invalid JID: "%s", %s',
    
    167 167
                                   item.getAttr('id'),
    
    ... ... @@ -209,7 +209,7 @@ class Bookmarks(BaseModule):
    209 209
     
    
    210 210
         @call_on_response('_bookmarks_received')
    
    211 211
         def request_bookmarks(self, type_):
    
    212
    -        jid = self._client.get_bound_jid().getBare()
    
    212
    +        jid = self._client.get_bound_jid().bare
    
    213 213
             if type_ == BookmarkStoreType.PUBSUB_BOOKMARK_2:
    
    214 214
                 self._log.info('Request bookmarks 2 (PubSub)')
    
    215 215
                 request = get_pubsub_request(jid, Namespace.BOOKMARKS_2)
    
    ... ... @@ -272,7 +272,7 @@ class Bookmarks(BaseModule):
    272 272
     
    
    273 273
             from_ = stanza.getFrom()
    
    274 274
             if from_ is None:
    
    275
    -            from_ = self._client.get_bound_jid().getBare()
    
    275
    +            from_ = self._client.get_bound_jid().bare
    
    276 276
             self._log.info('Received bookmarks from: %s', from_)
    
    277 277
             for bookmark in bookmarks:
    
    278 278
                 self._log.info(bookmark)
    
    ... ... @@ -315,14 +315,14 @@ class Bookmarks(BaseModule):
    315 315
     
    
    316 316
         def retract_bookmark(self, bookmark_jid):
    
    317 317
             self._log.info('Retract Bookmark: %s', bookmark_jid)
    
    318
    -        jid = self._client.get_bound_jid().getBare()
    
    318
    +        jid = self._client.get_bound_jid().bare
    
    319 319
             self._client.get_module('PubSub').retract(jid,
    
    320 320
                                                       Namespace.BOOKMARKS_2,
    
    321 321
                                                       str(bookmark_jid))
    
    322 322
     
    
    323 323
         def _store_bookmark_1(self, bookmarks):
    
    324 324
             self._log.info('Store Bookmarks 1 (PubSub)')
    
    325
    -        jid = self._client.get_bound_jid().getBare()
    
    325
    +        jid = self._client.get_bound_jid().bare
    
    326 326
             self._bookmark_1_queue = bookmarks
    
    327 327
             item = self._build_storage_node(bookmarks)
    
    328 328
             options = get_publish_options(BOOKMARK_1_OPTIONS)
    
    ... ... @@ -341,7 +341,7 @@ class Bookmarks(BaseModule):
    341 341
                 return
    
    342 342
     
    
    343 343
             self._log.info('Store Bookmarks 2 (PubSub)')
    
    344
    -        jid = self._client.get_bound_jid().getBare()
    
    344
    +        jid = self._client.get_bound_jid().bare
    
    345 345
             for bookmark in bookmarks:
    
    346 346
                 self._bookmark_2_queue[bookmark.jid] = bookmark
    
    347 347
                 item = self._build_conference_node(bookmark)
    
    ... ... @@ -367,7 +367,7 @@ class Bookmarks(BaseModule):
    367 367
                     return
    
    368 368
     
    
    369 369
                 self._node_configuration_in_progress = True
    
    370
    -            jid = self._client.get_bound_jid().getBare()
    
    370
    +            jid = self._client.get_bound_jid().bare
    
    371 371
                 self._client.get_module('PubSub').get_node_configuration(
    
    372 372
                     jid,
    
    373 373
                     node,
    

  • nbxmpp/modules/dataforms.py
    ... ... @@ -424,7 +424,7 @@ class JidSingleField(ListSingleField):
    424 424
         def is_valid(self):
    
    425 425
             if self.value:
    
    426 426
                 try:
    
    427
    -                JID(self.value)
    
    427
    +                JID.from_string(self.value)
    
    428 428
                     return True, ''
    
    429 429
                 except Exception as error:
    
    430 430
                     return False, error
    
    ... ... @@ -479,7 +479,7 @@ class JidMultiField(ListMultiField):
    479 479
             if self.values:
    
    480 480
                 for value in self.values:
    
    481 481
                     try:
    
    482
    -                    JID(value)
    
    482
    +                    JID.from_string(value)
    
    483 483
                     except Exception as error:
    
    484 484
                         return False, error
    
    485 485
                 return True, ''
    

  • nbxmpp/modules/delay.py
    ... ... @@ -47,18 +47,18 @@ class Delay(BaseModule):
    47 47
                 # to indicate when the user has set the subject,
    
    48 48
                 # the 'from' attr on these delays is the MUC server
    
    49 49
                 # but we treat it as user timestamp
    
    50
    -            jids = [properties.jid.getBare(),
    
    51
    -                    properties.jid.getDomain()]
    
    50
    +            jids = [properties.jid.bare,
    
    51
    +                    properties.jid.domain]
    
    52 52
     
    
    53 53
                 properties.user_timestamp = parse_delay(stanza, from_=jids)
    
    54 54
     
    
    55 55
             else:
    
    56 56
                 if properties.from_muc:
    
    57 57
                     # Some servers use the MUC JID, others the domain
    
    58
    -                jids = [properties.jid.getBare(),
    
    59
    -                        properties.jid.getDomain()]
    
    58
    +                jids = [properties.jid.bare,
    
    59
    +                        properties.jid.domain]
    
    60 60
                 else:
    
    61
    -                jids = [self._client.get_bound_jid().getDomain()]
    
    61
    +                jids = [self._client.get_bound_jid().domain]
    
    62 62
     
    
    63 63
                 server_delay = parse_delay(stanza, from_=jids)
    
    64 64
                 if server_delay is not None:
    

  • nbxmpp/modules/location.py
    ... ... @@ -70,6 +70,6 @@ class Location(BaseModule):
    70 70
                     if value is not None:
    
    71 71
                         item.addChild(tag, payload=value)
    
    72 72
     
    
    73
    -        jid = self._client.get_bound_jid().getBare()
    
    73
    +        jid = self._client.get_bound_jid().bare
    
    74 74
             self._client.get_module('PubSub').publish(
    
    75 75
                 jid, Namespace.LOCATION, item, id_='current')

  • nbxmpp/modules/mam.py
    ... ... @@ -183,7 +183,7 @@ class MAM(BaseModule):
    183 183
                     continue
    
    184 184
     
    
    185 185
                 try:
    
    186
    -                jid = JID(jid)
    
    186
    +                jid = JID.from_string(jid)
    
    187 187
                 except Exception:
    
    188 188
                     self._log.warning('Invalid jid found in preferences: %s',
    
    189 189
                                       jid)
    

  • nbxmpp/modules/message.py
    ... ... @@ -48,7 +48,7 @@ class BaseMessage(BaseModule):
    48 48
     
    
    49 49
             elif properties.is_mam_message and not properties.type.is_groupchat:
    
    50 50
                 own_jid = self._client.get_bound_jid()
    
    51
    -            if own_jid.bareMatch(stanza.getFrom()):
    
    51
    +            if own_jid.bare_match(stanza.getFrom()):
    
    52 52
                     properties.jid = stanza.getTo()
    
    53 53
                 else:
    
    54 54
                     properties.jid = stanza.getFrom()
    
    ... ... @@ -106,4 +106,4 @@ class BaseMessage(BaseModule):
    106 106
         def _parse_self_message(stanza, properties):
    
    107 107
             if properties.type.is_groupchat:
    
    108 108
                 return False
    
    109
    -        return stanza.getFrom().bareMatch(stanza.getTo())
    109
    +        return stanza.getFrom().bare_match(stanza.getTo())

  • nbxmpp/modules/misc.py
    ... ... @@ -38,7 +38,7 @@ def unwrap_carbon(stanza, own_jid):
    38 38
                 return stanza, None
    
    39 39
     
    
    40 40
         # Carbon must be from our bare jid
    
    41
    -    if not stanza.getFrom() == own_jid.getBare():
    
    41
    +    if stanza.getFrom() != own_jid.new_as_bare():
    
    42 42
             raise InvalidFrom('Invalid from: %s' % stanza.getAttr('from'))
    
    43 43
     
    
    44 44
         forwarded = carbon.getTag('forwarded', namespace=Namespace.FORWARD)
    
    ... ... @@ -49,14 +49,14 @@ def unwrap_carbon(stanza, own_jid):
    49 49
         # Fill missing to/from
    
    50 50
         to = message.getTo()
    
    51 51
         if to is None:
    
    52
    -        message.setTo(own_jid.getBare())
    
    52
    +        message.setTo(own_jid.bare)
    
    53 53
     
    
    54 54
         frm = message.getFrom()
    
    55 55
         if frm is None:
    
    56
    -        message.setFrom(own_jid.getBare())
    
    56
    +        message.setFrom(own_jid.bare)
    
    57 57
     
    
    58 58
         if type_ == 'received':
    
    59
    -        if message.getFrom().bareMatch(own_jid):
    
    59
    +        if message.getFrom().bare_match(own_jid):
    
    60 60
                 # Drop 'received' Carbons from ourself, we already
    
    61 61
                 # got the message with the 'sent' Carbon or via the
    
    62 62
                 # message itself
    
    ... ... @@ -95,11 +95,11 @@ def unwrap_mam(stanza, own_jid):
    95 95
         # Fill missing to/from
    
    96 96
         to = message.getTo()
    
    97 97
         if to is None:
    
    98
    -        message.setTo(own_jid.getBare())
    
    98
    +        message.setTo(own_jid.bare)
    
    99 99
     
    
    100 100
         frm = message.getFrom()
    
    101 101
         if frm is None:
    
    102
    -        message.setFrom(own_jid.getBare())
    
    102
    +        message.setFrom(own_jid.bare)
    
    103 103
     
    
    104 104
         # Timestamp parsing
    
    105 105
         # Most servers dont set the 'from' attr, so we cant check for it
    

  • nbxmpp/modules/mood.py
    ... ... @@ -80,6 +80,6 @@ class Mood(BaseModule):
    80 80
                 if data.text:
    
    81 81
                     item.addChild('text', payload=data.text)
    
    82 82
     
    
    83
    -        jid = self._client.get_bound_jid().getBare()
    
    83
    +        jid = self._client.get_bound_jid().bare
    
    84 84
             self._client.get_module('PubSub').publish(
    
    85 85
                 jid, Namespace.MOOD, item, id_='current')

  • nbxmpp/modules/muc.py
    ... ... @@ -92,24 +92,22 @@ class MUC(BaseModule):
    92 92
             if muc is None:
    
    93 93
                 return
    
    94 94
             properties.from_muc = True
    
    95
    -        properties.muc_jid = properties.jid.copy()
    
    96
    -        properties.muc_jid.setBare()
    
    97
    -        properties.muc_nickname = properties.jid.getResource()
    
    95
    +        properties.muc_jid = properties.jid.new_as_bare()
    
    96
    +        properties.muc_nickname = properties.jid.resource
    
    98 97
     
    
    99 98
         def _process_muc_user_presence(self, _client, stanza, properties):
    
    100 99
             muc_user = stanza.getTag('x', namespace=Namespace.MUC_USER)
    
    101 100
             if muc_user is None:
    
    102 101
                 return
    
    103 102
             properties.from_muc = True
    
    104
    -        properties.muc_jid = properties.jid.copy()
    
    105
    -        properties.muc_jid.setBare()
    
    103
    +        properties.muc_jid = properties.jid.new_as_bare()
    
    106 104
     
    
    107 105
             destroy = muc_user.getTag('destroy')
    
    108 106
             if destroy is not None:
    
    109 107
                 alternate = destroy.getAttr('jid')
    
    110 108
                 if alternate is not None:
    
    111 109
                     try:
    
    112
    -                    alternate = JID(alternate)
    
    110
    +                    alternate = JID.from_string(alternate)
    
    113 111
                     except Exception as error:
    
    114 112
                         self._log.warning('Invalid alternate JID provided: %s',
    
    115 113
                                           error)
    
    ... ... @@ -121,7 +119,7 @@ class MUC(BaseModule):
    121 119
                     password=muc_user.getTagData('password'))
    
    122 120
                 return
    
    123 121
     
    
    124
    -        properties.muc_nickname = properties.jid.getResource()
    
    122
    +        properties.muc_nickname = properties.jid.resource
    
    125 123
     
    
    126 124
             # https://xmpp.org/extensions/xep-0045.html#registrar-statuscodes
    
    127 125
             message_status_codes = [
    
    ... ... @@ -170,9 +168,8 @@ class MUC(BaseModule):
    170 168
     
    
    171 169
         def _process_groupchat_message(self, _client, stanza, properties):
    
    172 170
             properties.from_muc = True
    
    173
    -        properties.muc_jid = properties.jid.copy()
    
    174
    -        properties.muc_jid.setBare()
    
    175
    -        properties.muc_nickname = properties.jid.getResource() or None
    
    171
    +        properties.muc_jid = properties.jid.new_as_bare()
    
    172
    +        properties.muc_nickname = properties.jid.resource
    
    176 173
     
    
    177 174
             muc_user = stanza.getTag('x', namespace=Namespace.MUC_USER)
    
    178 175
             if muc_user is not None:
    
    ... ... @@ -188,7 +185,7 @@ class MUC(BaseModule):
    188 185
             if addresses is not None:
    
    189 186
                 address = addresses.getTag('address', attrs={'type': 'ofrom'})
    
    190 187
                 if address is not None:
    
    191
    -                properties.muc_ofrom = JID(address.getAttr('jid'))
    
    188
    +                properties.muc_ofrom = JID.from_string(address.getAttr('jid'))
    
    192 189
     
    
    193 190
         def _process_message(self, _client, stanza, properties):
    
    194 191
             muc_user = stanza.getTag('x', namespace=Namespace.MUC_USER)
    
    ... ... @@ -206,10 +203,9 @@ class MUC(BaseModule):
    206 203
                 return
    
    207 204
     
    
    208 205
             properties.from_muc = True
    
    209
    -        properties.muc_jid = properties.jid.copy()
    
    210
    -        properties.muc_jid.setBare()
    
    206
    +        properties.muc_jid = properties.jid.new_as_bare()
    
    211 207
     
    
    212
    -        if not properties.jid.isBare:
    
    208
    +        if not properties.jid.is_bare:
    
    213 209
                 return
    
    214 210
     
    
    215 211
             # MUC Config change
    
    ... ... @@ -253,7 +249,7 @@ class MUC(BaseModule):
    253 249
                 return
    
    254 250
     
    
    255 251
             data = {}
    
    256
    -        data['muc'] = JID(direct.getAttr('jid'))
    
    252
    +        data['muc'] = JID.from_string(direct.getAttr('jid'))
    
    257 253
             data['from_'] = properties.jid
    
    258 254
             data['reason'] = direct.getAttr('reason')
    
    259 255
             data['password'] = direct.getAttr('password')
    
    ... ... @@ -272,15 +268,14 @@ class MUC(BaseModule):
    272 268
                 return
    
    273 269
     
    
    274 270
             properties.from_muc = True
    
    275
    -        properties.muc_jid = properties.jid.copy()
    
    276
    -        properties.muc_jid.setBare()
    
    271
    +        properties.muc_jid = properties.jid.new_as_bare()
    
    277 272
     
    
    278 273
             data = {}
    
    279 274
     
    
    280 275
             invite = muc_user.getTag('invite')
    
    281 276
             if invite is not None:
    
    282
    -            data['muc'] = JID(properties.jid.getBare())
    
    283
    -            data['from_'] = JID(invite.getAttr('from'))
    
    277
    +            data['muc'] = properties.jid.new_as_bare()
    
    278
    +            data['from_'] = JID.from_string(invite.getAttr('from'))
    
    284 279
                 data['reason'] = invite.getTagData('reason')
    
    285 280
                 data['password'] = muc_user.getTagData('password')
    
    286 281
                 data['type'] = InviteType.MEDIATED
    
    ... ... @@ -296,8 +291,8 @@ class MUC(BaseModule):
    296 291
     
    
    297 292
             decline = muc_user.getTag('decline')
    
    298 293
             if decline is not None:
    
    299
    -            data['muc'] = JID(properties.jid.getBare())
    
    300
    -            data['from_'] = JID(decline.getAttr('from'))
    
    294
    +            data['muc'] = properties.jid.new_as_bare()
    
    295
    +            data['from_'] = JID.from_string(decline.getAttr('from'))
    
    301 296
                 data['reason'] = decline.getTagData('reason')
    
    302 297
                 properties.muc_decline = DeclineData(**data)
    
    303 298
                 return
    
    ... ... @@ -317,7 +312,7 @@ class MUC(BaseModule):
    317 312
             nick = data_form['muc#roomnick'].value
    
    318 313
     
    
    319 314
             try:
    
    320
    -            jid = JID(data_form['muc#jid'].value)
    
    315
    +            jid = JID.from_string(data_form['muc#jid'].value)
    
    321 316
             except Exception:
    
    322 317
                 self._log.warning('Invalid JID on voice request')
    
    323 318
                 self._log.warning(stanza)
    
    ... ... @@ -327,8 +322,7 @@ class MUC(BaseModule):
    327 322
                                                     nick=nick,
    
    328 323
                                                     form=data_form)
    
    329 324
             properties.from_muc = True
    
    330
    -        properties.muc_jid = properties.jid.copy()
    
    331
    -        properties.muc_jid.setBare()
    
    325
    +        properties.muc_jid = properties.jid.new_as_bare()
    
    332 326
     
    
    333 327
         def approve_voice_request(self, muc_jid, voice_request):
    
    334 328
             form = voice_request.form
    
    ... ... @@ -354,7 +348,7 @@ class MUC(BaseModule):
    354 348
             users_dict = {}
    
    355 349
             for item in items:
    
    356 350
                 try:
    
    357
    -                jid = JID(item.getAttr('jid'))
    
    351
    +                jid = JID.from_string(item.getAttr('jid'))
    
    358 352
                 except Exception as error:
    
    359 353
                     self._log.warning('Invalid JID: %s, %s',
    
    360 354
                                       item.getAttr('jid'), error)
    
    ... ... @@ -573,7 +567,7 @@ class MUC(BaseModule):
    573 567
             jid = item_dict.get('jid')
    
    574 568
             if jid is not None:
    
    575 569
                 try:
    
    576
    -                jid = JID(jid)
    
    570
    +                jid = JID.from_string(jid)
    
    577 571
                 except InvalidJid as error:
    
    578 572
                     raise StanzaMalformed('invalid jid %s, %s' % (jid, error))
    
    579 573
     
    

  • nbxmpp/modules/nickname.py
    ... ... @@ -87,6 +87,6 @@ class Nickname(BaseModule):
    87 87
             item = Node('nick', {'xmlns': Namespace.NICK})
    
    88 88
             if nickname is not None:
    
    89 89
                 item.addData(nickname)
    
    90
    -        jid = self._client.get_bound_jid().getBare()
    
    90
    +        jid = self._client.get_bound_jid().bare
    
    91 91
             self._client.get_module('PubSub').publish(
    
    92 92
                 jid, Namespace.NICK, item, id_='current')

  • nbxmpp/modules/omemo.py
    ... ... @@ -192,14 +192,14 @@ class OMEMO(BaseModule):
    192 192
                 item.addChild('device').setAttr('id', device)
    
    193 193
     
    
    194 194
             self._log.info('Set devicelist: %s', devicelist)
    
    195
    -        jid = self._client.get_bound_jid().getBare()
    
    195
    +        jid = self._client.get_bound_jid().bare
    
    196 196
             self._client.get_module('PubSub').publish(
    
    197 197
                 jid, Namespace.OMEMO_TEMP_DL, item, id_='current')
    
    198 198
     
    
    199 199
         @call_on_response('_devicelist_received')
    
    200 200
         def request_devicelist(self, jid=None):
    
    201 201
             if jid is None:
    
    202
    -            jid = self._client.get_bound_jid().getBare()
    
    202
    +            jid = self._client.get_bound_jid().bare
    
    203 203
             self._log.info('Request devicelist from: %s', jid)
    
    204 204
             return get_pubsub_request(jid, Namespace.OMEMO_TEMP_DL, max_items=1)
    
    205 205
     
    
    ... ... @@ -225,7 +225,7 @@ class OMEMO(BaseModule):
    225 225
             self._log.info('Set bundle')
    
    226 226
     
    
    227 227
             node = '%s:%s' % (Namespace.OMEMO_TEMP_BUNDLE, device_id)
    
    228
    -        jid = self._client.get_bound_jid().getBare()
    
    228
    +        jid = self._client.get_bound_jid().bare
    
    229 229
             self._client.get_module('PubSub').publish(
    
    230 230
                 jid, node, item, id_='current')
    
    231 231
     
    

  • nbxmpp/modules/openpgp.py
    ... ... @@ -155,7 +155,7 @@ class OpenPGP(BaseModule):
    155 155
                     item.addChild('pubkey-metadata', attrs=attrs)
    
    156 156
     
    
    157 157
             self._log.info('Set keylist: %s', keylist)
    
    158
    -        jid = self._client.get_bound_jid().getBare()
    
    158
    +        jid = self._client.get_bound_jid().bare
    
    159 159
             self._client.get_module('PubSub').publish(
    
    160 160
                 jid, Namespace.OPENPGP_PK, item, id_='current')
    
    161 161
     
    
    ... ... @@ -169,7 +169,7 @@ class OpenPGP(BaseModule):
    169 169
             node = '%s:%s' % (Namespace.OPENPGP_PK, fingerprint)
    
    170 170
     
    
    171 171
             self._log.info('Set public key')
    
    172
    -        jid = self._client.get_bound_jid().getBare()
    
    172
    +        jid = self._client.get_bound_jid().bare
    
    173 173
             self._client.get_module('PubSub').publish(
    
    174 174
                 jid, node, item, id_='current')
    
    175 175
     
    
    ... ... @@ -181,7 +181,7 @@ class OpenPGP(BaseModule):
    181 181
     
    
    182 182
         @callback
    
    183 183
         def _public_key_received(self, stanza):
    
    184
    -        jid = JID(stanza.getFrom().getBare())
    
    184
    +        jid = stanza.getFrom().bare
    
    185 185
     
    
    186 186
             if not isResultNode(stanza):
    
    187 187
                 return raise_error(self._log.info, stanza)
    
    ... ... @@ -219,7 +219,7 @@ class OpenPGP(BaseModule):
    219 219
     
    
    220 220
         @callback
    
    221 221
         def _keylist_received(self, stanza):
    
    222
    -        jid = JID(stanza.getFrom().getBare())
    
    222
    +        jid = stanza.getFrom().bare
    
    223 223
     
    
    224 224
             if not isResultNode(stanza):
    
    225 225
                 return raise_error(self._log.info, stanza)
    
    ... ... @@ -239,7 +239,7 @@ class OpenPGP(BaseModule):
    239 239
         @call_on_response('_secret_key_received')
    
    240 240
         def request_secret_key(self):
    
    241 241
             self._log.info('Request secret key')
    
    242
    -        jid = self._client.get_bound_jid().getBare()
    
    242
    +        jid = self._client.get_bound_jid().bare
    
    243 243
             return get_pubsub_request(jid, Namespace.OPENPGP_SK, max_items=1)
    
    244 244
     
    
    245 245
         @callback
    
    ... ... @@ -275,7 +275,7 @@ class OpenPGP(BaseModule):
    275 275
                 item.setData(b64encode(secret_key))
    
    276 276
     
    
    277 277
             self._log.info('Set secret key')
    
    278
    -        jid = self._client.get_bound_jid().getBare()
    
    278
    +        jid = self._client.get_bound_jid().bare
    
    279 279
             self._client.get_module('PubSub').publish(
    
    280 280
                 jid, Namespace.OPENPGP_SK, item, id_='current')
    
    281 281
     
    
    ... ... @@ -338,7 +338,7 @@ def create_signcrypt_node(stanza, not_encrypted_nodes):
    338 338
                 stanza.delChild(node)
    
    339 339
     
    
    340 340
         signcrypt = Node('signcrypt', attrs={'xmlns': Namespace.OPENPGP})
    
    341
    -    signcrypt.addChild('to', attrs={'jid': stanza.getTo().getBare()})
    
    341
    +    signcrypt.addChild('to', attrs={'jid': str(stanza.getTo().bare)})
    
    342 342
     
    
    343 343
         timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
    
    344 344
         signcrypt.addChild('time', attrs={'stamp': timestamp})
    

  • nbxmpp/modules/presence.py
    ... ... @@ -49,7 +49,7 @@ class BasePresence(BaseModule):
    49 49
     
    
    50 50
             own_jid = self._client.get_bound_jid()
    
    51 51
             properties.self_presence = own_jid == properties.jid
    
    52
    -        properties.self_bare = properties.jid.bareMatch(own_jid)
    
    52
    +        properties.self_bare = properties.jid.bare_match(own_jid)
    
    53 53
     
    
    54 54
         def _parse_priority(self, stanza):
    
    55 55
             priority = stanza.getPriority()
    

  • nbxmpp/modules/receipts.py
    ... ... @@ -68,10 +68,10 @@ def build_receipt(stanza):
    68 68
     
    
    69 69
         is_muc_pm = isMucPM(stanza)
    
    70 70
     
    
    71
    -    jid = stanza.getFrom().copy()
    
    71
    +    jid = stanza.getFrom()
    
    72 72
         typ = stanza.getType()
    
    73 73
         if typ == 'groupchat' or not is_muc_pm:
    
    74
    -        jid.setBare()
    
    74
    +        jid = jid.new_as_bare()
    
    75 75
     
    
    76 76
         message = Message(to=jid, typ=typ)
    
    77 77
         if is_muc_pm:
    

  • nbxmpp/modules/register.py
    ... ... @@ -168,8 +168,8 @@ class Register(BaseModule):
    168 168
     
    
    169 169
         @call_on_response('_on_password_change')
    
    170 170
         def change_password(self, password):
    
    171
    -        domain = self._client.get_bound_jid().getDomain()
    
    172
    -        username = self._client.get_bound_jid().getNode()
    
    171
    +        domain = self._client.get_bound_jid().domain
    
    172
    +        username = self._client.get_bound_jid().localpart
    
    173 173
             iq = Iq('set', Namespace.REGISTER, to=domain)
    
    174 174
             query = iq.getQuery()
    
    175 175
             query.setTagData('username', username)
    
    ... ... @@ -192,7 +192,7 @@ class Register(BaseModule):
    192 192
     
    
    193 193
         @call_on_response('_default_response')
    
    194 194
         def change_password_with_form(self, form):
    
    195
    -        domain = self._client.get_bound_jid().getDomain()
    
    195
    +        domain = self._client.get_bound_jid().domain
    
    196 196
             iq = Iq('set', Namespace.REGISTER, to=domain)
    
    197 197
             iq.setQueryPayload(form)
    
    198 198
             return iq
    

  • nbxmpp/modules/tune.py
    ... ... @@ -70,6 +70,6 @@ class Tune(BaseModule):
    70 70
                     if value is not None:
    
    71 71
                         item.addChild(tag, payload=value)
    
    72 72
     
    
    73
    -        jid = self._client.get_bound_jid().getBare()
    
    73
    +        jid = self._client.get_bound_jid().bare
    
    74 74
             self._client.get_module('PubSub').publish(
    
    75 75
                 jid, Namespace.TUNE, item, id_='current')

  • nbxmpp/modules/user_avatar.py
    ... ... @@ -88,7 +88,7 @@ class UserAvatar(BaseModule):
    88 88
         def _avatar_data_received(self, stanza):
    
    89 89
             jid = stanza.getFrom()
    
    90 90
             if jid is None:
    
    91
    -            jid = JID(self._client.get_bound_jid().getBare())
    
    91
    +            jid = self._client.get_bound_jid().bare
    
    92 92
     
    
    93 93
             if not isResultNode(stanza):
    
    94 94
                 return raise_error(self._log.warning, stanza)
    

  • nbxmpp/old_dispatcher.py
    ... ... @@ -561,7 +561,7 @@ class XMPPDispatcher(PlugIn):
    561 561
     
    
    562 562
             if name == 'iq':
    
    563 563
                 if stanza.getFrom() is None and own_jid is not None:
    
    564
    -                stanza.setFrom(own_jid.getBare())
    
    564
    +                stanza.setFrom(own_jid.bare)
    
    565 565
     
    
    566 566
             if name == 'message':
    
    567 567
                 # https://tools.ietf.org/html/rfc6120#section-8.1.1.1
    
    ... ... @@ -572,12 +572,13 @@ class XMPPDispatcher(PlugIn):
    572 572
                 to = stanza.getTo()
    
    573 573
                 if to is None:
    
    574 574
                     stanza.setTo(own_jid)
    
    575
    -            elif not to.bareMatch(own_jid):
    
    575
    +
    
    576
    +            elif not to.bare_match(own_jid):
    
    576 577
                     log.warning('Message addressed to someone else: %s', stanza)
    
    577 578
                     return
    
    578 579
     
    
    579 580
                 if stanza.getFrom() is None:
    
    580
    -                stanza.setFrom(own_jid.getBare())
    
    581
    +                stanza.setFrom(own_jid.bare)
    
    581 582
     
    
    582 583
                 # Unwrap carbon
    
    583 584
                 try:
    

  • nbxmpp/protocol.py
    ... ... @@ -21,7 +21,9 @@ sub- stanzas) handling routines
    21 21
     import time
    
    22 22
     import hashlib
    
    23 23
     import functools
    
    24
    +import warnings
    
    24 25
     from base64 import b64encode
    
    26
    +from collections import namedtuple
    
    25 27
     
    
    26 28
     from gi.repository import GLib
    
    27 29
     
    
    ... ... @@ -439,6 +441,7 @@ _status_conditions = {
    439 441
     }
    
    440 442
     
    
    441 443
     _localpart_disallowed_chars = set('"&\'/:<>@')
    
    444
    +_localpart_escape_chars = ' "&\'/:<>@'
    
    442 445
     
    
    443 446
     
    
    444 447
     STREAM_NOT_AUTHORIZED = 'urn:ietf:params:xml:ns:xmpp-streams not-authorized'
    
    ... ... @@ -659,33 +662,11 @@ stream_exceptions = {'bad-format': BadFormat,
    659 662
                          'xml-not-well-formed': XMLNotWellFormed}
    
    660 663
     
    
    661 664
     
    
    662
    -@functools.lru_cache(maxsize=None)
    
    663
    -def parse_jid(jid):
    
    664
    -    # https://tools.ietf.org/html/rfc7622#section-3.2
    
    665
    -
    
    666
    -    # Remove any portion from the first '/' character to the end of the
    
    667
    -    # string (if there is a '/' character present).
    
    668
    -
    
    669
    -    # Remove any portion from the beginning of the string to the first
    
    670
    -    # '@' character (if there is an '@' character present).
    
    671
    -
    
    672
    -    if jid.find('/') != -1:
    
    673
    -        rest, resourcepart = jid.split('/', 1)
    
    674
    -        resourcepart = validate_resourcepart(resourcepart)
    
    675
    -    else:
    
    676
    -        rest, resourcepart = jid, ''
    
    677
    -
    
    678
    -    if rest.find('@') != -1:
    
    679
    -        localpart, domainpart = rest.split('@', 1)
    
    680
    -        localpart = validate_localpart(localpart)
    
    681
    -    else:
    
    682
    -        localpart, domainpart = '', rest
    
    665
    +def deprecation_warning(message):
    
    666
    +    warnings.warn(message, DeprecationWarning)
    
    683 667
     
    
    684
    -    domainpart = validate_domainpart(domainpart)
    
    685
    -    return localpart, domainpart, resourcepart
    
    686 668
     
    
    687
    -
    
    688
    -@functools.lru_cache(maxsize=32768)
    
    669
    +@functools.lru_cache(maxsize=None)
    
    689 670
     def validate_localpart(localpart):
    
    690 671
         if not localpart or len(localpart.encode()) > 1023:
    
    691 672
             raise LocalpartByteLimit
    
    ... ... @@ -700,7 +681,7 @@ def validate_localpart(localpart):
    700 681
             raise LocalpartNotAllowedChar
    
    701 682
     
    
    702 683
     
    
    703
    -@functools.lru_cache(maxsize=32768)
    
    684
    +@functools.lru_cache(maxsize=None)
    
    704 685
     def validate_resourcepart(resourcepart):
    
    705 686
         if not resourcepart or len(resourcepart.encode()) > 1023:
    
    706 687
             raise ResourcepartByteLimit
    
    ... ... @@ -712,7 +693,7 @@ def validate_resourcepart(resourcepart):
    712 693
             raise ResourcepartNotAllowedChar
    
    713 694
     
    
    714 695
     
    
    715
    -@functools.lru_cache(maxsize=4096)
    
    696
    +@functools.lru_cache(maxsize=None)
    
    716 697
     def validate_domainpart(domainpart):
    
    717 698
         if not domainpart:
    
    718 699
             raise DomainpartByteLimit
    
    ... ... @@ -729,167 +710,265 @@ def validate_domainpart(domainpart):
    729 710
             domainpart = domainpart[:-1]
    
    730 711
     
    
    731 712
         try:
    
    732
    -        idna.encode(domainpart, uts46=True)
    
    713
    +        idna_encode(domainpart)
    
    733 714
         except Exception:
    
    734 715
             raise DomainpartNotAllowedChar
    
    735 716
     
    
    736 717
         return domainpart
    
    737 718
     
    
    738 719
     
    
    739
    -class JID:
    
    740
    -    """
    
    741
    -    JID can be built from string, modified, compared, serialised into string
    
    742
    -    """
    
    720
    +@functools.lru_cache(maxsize=None)
    
    721
    +def idna_encode(domain):
    
    722
    +    return idna.encode(domain, uts46=True).decode()
    
    743 723
     
    
    744
    -    def __init__(self, jid=None, node='', domain='', resource=''):
    
    745
    -        """
    
    746
    -        JID can be specified as string (jid argument) or as separate parts
    
    747 724
     
    
    748
    -        Examples:
    
    749
    -        JID('node@domain/resource')
    
    750
    -        JID(node='node',domain='domain.org')
    
    751
    -        """
    
    752
    -        if not jid and not domain:
    
    753
    -            raise ValueError('JID must contain at least domain name')
    
    725
    +@functools.lru_cache(maxsize=None)
    
    726
    +def escape_localpart(localpart):
    
    727
    +    # https://xmpp.org/extensions/xep-0106.html#bizrules-algorithm
    
    728
    +    #
    
    729
    +    # If there are any instances of character sequences that correspond
    
    730
    +    # to escapings of the disallowed characters
    
    731
    +    # (e.g., the character sequence "\27") or the escaping character
    
    732
    +    # (i.e., the character sequence "\5c") in the source address,
    
    733
    +    # the leading backslash character MUST be escaped to the character
    
    734
    +    # sequence "\5c"
    
    735
    +
    
    736
    +    for char in '\\' + _localpart_escape_chars:
    
    737
    +        seq = "\\{:02x}".format(ord(char))
    
    738
    +        localpart = localpart.replace(seq, "\\5c{:02x}".format(ord(char)))
    
    739
    +
    
    740
    +    # Escape all other chars
    
    741
    +    for char in _localpart_escape_chars:
    
    742
    +        localpart = localpart.replace(char, "\\{:02x}".format(ord(char)))
    
    743
    +
    
    744
    +    return localpart
    
    745
    +
    
    746
    +
    
    747
    +@functools.lru_cache(maxsize=None)
    
    748
    +def unescape_localpart(localpart):
    
    749
    +    if localpart.startswith('\\20') or localpart.endswith('\\20'):
    
    750
    +        # Escaped JIDs are not allowed to start or end with \20
    
    751
    +        # so this localpart must be already unescaped
    
    752
    +        return localpart
    
    753
    +
    
    754
    +    for char in _localpart_escape_chars:
    
    755
    +        seq = "\\{:02x}".format(ord(char))
    
    756
    +        localpart = localpart.replace(seq, char)
    
    757
    +
    
    758
    +    for char in _localpart_escape_chars + "\\":
    
    759
    +        seq = "\\5c{:02x}".format(ord(char))
    
    760
    +        localpart = localpart.replace(seq, "\\{:02x}".format(ord(char)))
    
    761
    +
    
    762
    +    return localpart
    
    763
    +
    
    764
    +
    
    765
    +class JID(namedtuple('JID',
    
    766
    +                     ['jid', 'localpart', 'domain', 'resource'])):
    
    767
    +
    
    768
    +    __slots__ = []
    
    769
    +
    
    770
    +    def __new__(cls, jid=None, localpart=None, domain=None, resource=None):
    
    771
    +        if jid is not None:
    
    772
    +            deprecation_warning('JID(jid) is deprecated, use from_string()')
    
    773
    +            return JID.from_string(str(jid))
    
    774
    +
    
    775
    +        if localpart is not None:
    
    776
    +            localpart = validate_localpart(localpart)
    
    777
    +
    
    778
    +        domain = validate_domainpart(domain)
    
    779
    +
    
    780
    +        if resource is not None:
    
    781
    +            resource = validate_resourcepart(resource)
    
    782
    +
    
    783
    +        return super().__new__(cls, None, localpart, domain, resource)
    
    754 784
     
    
    755
    -        if isinstance(jid, JID):
    
    756
    -            self.node, self.domain = jid.node, jid.domain
    
    757
    -            self.resource = jid.resource
    
    758
    -        elif domain:
    
    759
    -            self.node, self.domain, self.resource = node, domain, resource
    
    785
    +    @classmethod
    
    786
    +    @functools.lru_cache(maxsize=None)
    
    787
    +    def from_string(cls, jid_string):
    
    788
    +        # https://tools.ietf.org/html/rfc7622#section-3.2
    
    789
    +
    
    790
    +        # Remove any portion from the first '/' character to the end of the
    
    791
    +        # string (if there is a '/' character present).
    
    792
    +
    
    793
    +        # Remove any portion from the beginning of the string to the first
    
    794
    +        # '@' character (if there is an '@' character present).
    
    795
    +
    
    796
    +        if jid_string.find('/') != -1:
    
    797
    +            rest, resourcepart = jid_string.split('/', 1)
    
    760 798
             else:
    
    761
    -            self.node, self.domain, self.resource = parse_jid(jid)
    
    799
    +            rest, resourcepart = jid_string, None
    
    762 800
     
    
    763
    -    def copy(self):
    
    764
    -        return JID(self)
    
    801
    +        if rest.find('@') != -1:
    
    802
    +            localpart, domainpart = rest.split('@', 1)
    
    803
    +        else:
    
    804
    +            localpart, domainpart = None, rest
    
    765 805
     
    
    766
    -    def getNode(self):
    
    767
    -        """
    
    768
    -        Return the node part of the JID
    
    769
    -        """
    
    770
    -        return self.node
    
    806
    +        return cls(jid=None,
    
    807
    +                   localpart=localpart,
    
    808
    +                   domain=domainpart,
    
    809
    +                   resource=resourcepart)
    
    771 810
     
    
    772
    -    def setNode(self, node):
    
    773
    -        """
    
    774
    -        Set the node part of the JID to new value. Specify None to remove
    
    775
    -        the node part
    
    776
    -        """
    
    777
    -        self.node = node.lower()
    
    811
    +    @classmethod
    
    812
    +    @functools.lru_cache(maxsize=None)
    
    813
    +    def from_user_input(cls, user_input, escaped=False):
    
    814
    +        # Use this if we want JIDs to be escaped according to XEP-0106
    
    815
    +        # The standard JID parsing cannot be applied because user_input is
    
    816
    +        # not a valid JID.
    
    778 817
     
    
    779
    -    def getDomain(self):
    
    780
    -        """
    
    781
    -        Return the domain part of the JID
    
    782
    -        """
    
    783
    -        return self.domain
    
    818
    +        # Only user_input which after escaping result in a bare JID can be
    
    819
    +        # successfully parsed.
    
    784 820
     
    
    785
    -    def setDomain(self, domain):
    
    786
    -        """
    
    787
    -        Set the domain part of the JID to new value
    
    788
    -        """
    
    789
    -        self.domain = domain.lower()
    
    821
    +        # The assumpution is user_input is a bare JID so we start with an
    
    822
    +        # rsplit on @ because we assume there is no resource, so the char @
    
    823
    +        # in the localpart can later be escaped.
    
    790 824
     
    
    791
    -    def getResource(self):
    
    792
    -        """
    
    793
    -        Return the resource part of the JID
    
    794
    -        """
    
    795
    -        return self.resource
    
    825
    +        if escaped:
    
    826
    +            # for convenience
    
    827
    +            return cls.from_string(user_input)
    
    796 828
     
    
    797
    -    def setResource(self, resource):
    
    798
    -        """
    
    799
    -        Set the resource part of the JID to new value.
    
    800
    -        Specify None to remove the resource part
    
    801
    -        """
    
    802
    -        self.resource = resource
    
    829
    +        if '@' in user_input:
    
    830
    +            localpart, domainpart = user_input.rsplit('@', 1)
    
    831
    +            if localpart.startswith(' ') or localpart.endswith(' '):
    
    832
    +                raise LocalpartNotAllowedChar
    
    803 833
     
    
    804
    -    def getStripped(self):
    
    805
    -        """
    
    806
    -        Return the bare representation of JID. I.e. string value w/o resource
    
    807
    -        """
    
    808
    -        if not self.node:
    
    809
    -            return self.domain
    
    810
    -        return '%s@%s' % (self.node, self.domain)
    
    834
    +            localpart = escape_localpart(localpart)
    
    811 835
     
    
    812
    -    def getBare(self):
    
    813
    -        """
    
    814
    -        Return the bare representation of JID. I.e. string value w/o resource
    
    815
    -        """
    
    816
    -        if not self.node:
    
    817
    -            return self.domain
    
    818
    -        return '%s@%s' % (self.node, self.domain)
    
    836
    +        else:
    
    837
    +            localpart = None
    
    838
    +            domainpart = user_input
    
    819 839
     
    
    820
    -    def setBare(self):
    
    821
    -        """
    
    822
    -        Deletes the resource which makes the JID bare
    
    823
    -        """
    
    824
    -        self.resource = ''
    
    840
    +        return cls(jid=None,
    
    841
    +                   localpart=localpart,
    
    842
    +                   domain=domainpart,
    
    843
    +                   resource=None)
    
    825 844
     
    
    826
    -    @property
    
    827
    -    def isBare(self):
    
    828
    -        return self.node and self.domain and not self.resource
    
    845
    +    def __str__(self):
    
    846
    +        if self.localpart:
    
    847
    +            jid = f'{self.localpart}@{self.domain}'
    
    848
    +        else:
    
    849
    +            jid = self.domain
    
    829 850
     
    
    830
    -    @property
    
    831
    -    def isDomain(self):
    
    832
    -        return self.domain and not self.node and not self.resource
    
    851
    +        if self.resource is not None:
    
    852
    +            return f'{jid}/{self.resource}'
    
    853
    +        return jid
    
    833 854
     
    
    834
    -    @property
    
    835
    -    def isFull(self):
    
    836
    -        return bool(self.domain and self.node and self.resource)
    
    855
    +    def __hash__(self):
    
    856
    +        return hash(str(self))
    
    837 857
     
    
    838 858
         def __eq__(self, other):
    
    839
    -        """
    
    840
    -        Compare the JID to another instance or to string for equality
    
    841
    -        """
    
    842
    -        if not isinstance(other, JID):
    
    859
    +        if isinstance(other, str):
    
    860
    +            deprecation_warning('comparing string with JID is deprected')
    
    843 861
                 try:
    
    844
    -                other = JID(other)
    
    862
    +                return JID.from_string(other) == self
    
    845 863
                 except Exception:
    
    846 864
                     return False
    
    847
    -
    
    848
    -        return (self.node == other.node and
    
    849
    -                self.domain == other.domain and
    
    850
    -                self.resource == other.resource)
    
    865
    +        return super().__eq__(other)
    
    851 866
     
    
    852 867
         def __ne__(self, other):
    
    853
    -        """
    
    854
    -        Compare the JID to another instance or to string for non-equality
    
    855
    -        """
    
    856 868
             return not self.__eq__(other)
    
    857 869
     
    
    858
    -    def bareMatch(self, other):
    
    859
    -        """
    
    860
    -        Compare the node and domain parts of the JID's for equality
    
    861
    -        """
    
    862
    -        if not isinstance(other, JID):
    
    863
    -            try:
    
    864
    -                other = JID(other)
    
    865
    -            except Exception:
    
    866
    -                return False
    
    870
    +    def domain_to_ascii(self):
    
    871
    +        return idna_encode(self.domain)
    
    867 872
     
    
    868
    -        return (self.node == other.node and
    
    869
    -                self.domain == other.domain)
    
    873
    +    @property
    
    874
    +    def bare(self):
    
    875
    +        if self.localpart is not None:
    
    876
    +            return f'{self.localpart}@{self.domain}'
    
    877
    +        return self.domain
    
    870 878
     
    
    871
    -    def __str__(self):
    
    872
    -        """
    
    873
    -        Serialise JID into string
    
    874
    -        """
    
    875
    -        if self.node:
    
    876
    -            jid = '%s@%s' % (self.node, self.domain)
    
    879
    +    @property
    
    880
    +    def is_bare(self):
    
    881
    +        return self.resource is None
    
    882
    +
    
    883
    +    def new_as_bare(self):
    
    884
    +        if self.resource is None:
    
    885
    +            return self
    
    886
    +        return self._replace(resource=None)
    
    887
    +
    
    888
    +    def bare_match(self, other):
    
    889
    +        if isinstance(other, str):
    
    890
    +            other = JID.from_string(other)
    
    891
    +        return self.bare == other.bare
    
    892
    +
    
    893
    +    @property
    
    894
    +    def is_domain(self):
    
    895
    +        return self.localpart is None and self.resource is None
    
    896
    +
    
    897
    +    @property
    
    898
    +    def is_full(self):
    
    899
    +        return (self.localpart is not None and
    
    900
    +                self.domain is not None and
    
    901
    +                self.resource is not None)
    
    902
    +
    
    903
    +    def new_with(self, **kwargs):
    
    904
    +        return self._replace(**kwargs)
    
    905
    +
    
    906
    +    def to_user_string(self, show_punycode=True):
    
    907
    +        domain = self.domain_to_ascii()
    
    908
    +        if domain.startswith('xn--') and show_punycode:
    
    909
    +            domain_encoded = f' ({domain})'
    
    877 910
             else:
    
    878
    -            jid = self.domain
    
    911
    +            domain_encoded = ''
    
    879 912
     
    
    880
    -        if self.resource:
    
    881
    -            return '%s/%s' % (jid, self.resource)
    
    882
    -        return jid
    
    913
    +        if self.localpart is None:
    
    914
    +            return f'{self}{domain_encoded}'
    
    883 915
     
    
    884
    -    def __hash__(self):
    
    885
    -        """
    
    886
    -        Produce hash of the JID, Allows to use JID objects as keys of the
    
    887
    -        dictionary
    
    888
    -        """
    
    889
    -        return hash(str(self))
    
    916
    +        localpart = unescape_localpart(self.localpart)
    
    917
    +
    
    918
    +        if self.resource is None:
    
    919
    +            return f'{localpart}@{self.domain}{domain_encoded}'
    
    920
    +        return f'{localpart}@{self.domain}/{self.resource}{domain_encoded}'
    
    921
    +
    
    922
    +    def bareMatch(self, other):
    
    923
    +        deprecation_warning('bareMatch() is deprected use bare_match()')
    
    924
    +        return self.bare_match(other)
    
    925
    +
    
    926
    +    @property
    
    927
    +    def isBare(self):
    
    928
    +        deprecation_warning('isBare() is deprected use '
    
    929
    +                            'the attribute is_bare')
    
    930
    +        return self.is_bare
    
    931
    +
    
    932
    +    @property
    
    933
    +    def isDomain(self):
    
    934
    +        deprecation_warning('isDomain() is deprected use '
    
    935
    +                            'the attribute is_domain')
    
    936
    +        return self.is_domain
    
    890 937
     
    
    891
    -    def __repr__(self):
    
    892
    -        return str(self)
    
    938
    +    @property
    
    939
    +    def isFull(self):
    
    940
    +        deprecation_warning('isFull() is deprected use '
    
    941
    +                            'the attribute is_full')
    
    942
    +        return self.is_full
    
    943
    +
    
    944
    +    def copy(self):
    
    945
    +        deprecation_warning('copy() is not needed, JID is immutable')
    
    946
    +        return self
    
    947
    +
    
    948
    +    def getNode(self):
    
    949
    +        deprecation_warning('getNode() is deprected use '
    
    950
    +                            'the attribute localpart')
    
    951
    +        return self.localpart
    
    952
    +
    
    953
    +    def getDomain(self):
    
    954
    +        deprecation_warning('getDomain() is deprected use '
    
    955
    +                            'the attribute domain')
    
    956
    +        return self.domain
    
    957
    +
    
    958
    +    def getResource(self):
    
    959
    +        deprecation_warning('getResource() is deprected use '
    
    960
    +                            'the attribute resource')
    
    961
    +        return self.resource
    
    962
    +
    
    963
    +    def getStripped(self):
    
    964
    +        deprecation_warning('getStripped() is deprected use '
    
    965
    +                            'the attribute bare')
    
    966
    +        return self.bare
    
    967
    +
    
    968
    +    def getBare(self):
    
    969
    +        deprecation_warning('getBare() is deprected use '
    
    970
    +                            'the attribute bare')
    
    971
    +        return self.bare
    
    893 972
     
    
    894 973
     
    
    895 974
     class StreamErrorNode(Node):
    
    ... ... @@ -1035,7 +1114,7 @@ class Protocol(Node):
    1035 1114
             """
    
    1036 1115
             attr = self.getAttr('jid')
    
    1037 1116
             if attr:
    
    1038
    -            return JID(attr)
    
    1117
    +            return JID.from_string(attr)
    
    1039 1118
             return attr
    
    1040 1119
     
    
    1041 1120
         def getID(self):
    
    ... ... @@ -1048,7 +1127,9 @@ class Protocol(Node):
    1048 1127
             """
    
    1049 1128
             Set the value of the 'to' attribute
    
    1050 1129
             """
    
    1051
    -        self.setAttr('to', JID(val))
    
    1130
    +        if not isinstance(val, JID):
    
    1131
    +            val = JID.from_string(val)
    
    1132
    +        self.setAttr('to', val)
    
    1052 1133
     
    
    1053 1134
         def getType(self):
    
    1054 1135
             """
    
    ... ... @@ -1060,7 +1141,9 @@ class Protocol(Node):
    1060 1141
             """
    
    1061 1142
             Set the value of the 'from' attribute
    
    1062 1143
             """
    
    1063
    -        self.setAttr('from', JID(val))
    
    1144
    +        if not isinstance(val, JID):
    
    1145
    +            val = JID.from_string(val)
    
    1146
    +        self.setAttr('from', val)
    
    1064 1147
     
    
    1065 1148
         def setType(self, val):
    
    1066 1149
             """
    
    ... ... @@ -1202,7 +1285,8 @@ class Protocol(Node):
    1202 1285
             Set the item 'item' to the value 'val'
    
    1203 1286
             """
    
    1204 1287
             if item in ['to', 'from']:
    
    1205
    -            val = JID(val)
    
    1288
    +            if not isinstance(val, JID):
    
    1289
    +                val = JID.from_string(val)
    
    1206 1290
             return self.setAttr(item, val)
    
    1207 1291
     
    
    1208 1292
     
    

  • nbxmpp/structs.py
    ... ... @@ -250,7 +250,7 @@ class DiscoInfo(namedtuple('DiscoInfo', 'stanza identities features dataforms ti
    250 250
                 return self.muc_identity_name
    
    251 251
     
    
    252 252
             if self.jid is not None:
    
    253
    -            return self.jid.getNode()
    
    253
    +            return self.jid.localpart
    
    254 254
             return None
    
    255 255
     
    
    256 256
         @property
    
    ... ... @@ -931,9 +931,7 @@ class PresenceProperties:
    931 931
         def new_jid(self):
    
    932 932
             if not self.is_nickname_changed:
    
    933 933
                 raise ValueError('This is not a nickname change')
    
    934
    -        jid = JID(self.jid)
    
    935
    -        jid.setResource(self.muc_user.nick)
    
    936
    -        return jid
    
    934
    +        return self.jid.new_with(resource=self.muc_user.nick)
    
    937 935
     
    
    938 936
         @property
    
    939 937
         def is_kicked(self):
    

  • test/lib/util.py
    ... ... @@ -14,7 +14,7 @@ class StanzaHandlerTest(unittest.TestCase):
    14 14
             self.client.is_websocket = False
    
    15 15
             self.dispatcher = StanzaDispatcher(self.client)
    
    16 16
     
    
    17
    -        self.client.get_bound_jid.return_value = JID('test@test.test')
    
    17
    +        self.client.get_bound_jid.return_value = JID.from_string('test@test.test')
    
    18 18
     
    
    19 19
             self.dispatcher.reset_parser()
    
    20 20
             self.dispatcher.process_data(STREAM_START)

  • test/unit/test_bookmarks.py
    ... ... @@ -13,12 +13,12 @@ class BookmarkTest(StanzaHandlerTest):
    13 13
             def _on_message(_con, _stanza, properties):
    
    14 14
     
    
    15 15
                 data = [
    
    16
    -                BookmarkData(jid=JID('theplay@conference.shakespeare.lit'),
    
    16
    +                BookmarkData(jid=JID.from_string('theplay@conference.shakespeare.lit'),
    
    17 17
                                  name='The Play\'s the Thing',
    
    18 18
                                  autojoin=True,
    
    19 19
                                  password='pass',
    
    20 20
                                  nick='JC'),
    
    21
    -                BookmarkData(jid=JID('second@conference.shakespeare.lit'),
    
    21
    +                BookmarkData(jid=JID.from_string('second@conference.shakespeare.lit'),
    
    22 22
                                  name='Second room',
    
    23 23
                                  autojoin=False,
    
    24 24
                                  password=None,
    
    ... ... @@ -71,7 +71,7 @@ class BookmarkTest(StanzaHandlerTest):
    71 71
         def test_bookmark_2_parsing(self):
    
    72 72
             def _on_message(_con, _stanza, properties):
    
    73 73
     
    
    74
    -            data = BookmarkData(jid=JID('theplay@conference.shakespeare.lit'),
    
    74
    +            data = BookmarkData(jid=JID.from_string('theplay@conference.shakespeare.lit'),
    
    75 75
                                     name='The Play\'s the Thing',
    
    76 76
                                     autojoin=True,
    
    77 77
                                     password=None,
    

  • test/unit/test_error_parsing.py
    1 1
     import unittest
    
    2 2
     
    
    3 3
     from nbxmpp.protocol import Iq
    
    4
    +from nbxmpp.protocol import JID
    
    4 5
     from nbxmpp.util import error_factory
    
    5 6
     
    
    6 7
     
    
    ... ... @@ -29,4 +30,4 @@ class TestErrorParsing(unittest.TestCase):
    29 30
             self.assertEqual(error.get_text('de'), 'File zu groß. Erlaubt sind 20000 bytes')
    
    30 31
             self.assertEqual(error.type, 'modify')
    
    31 32
             self.assertEqual(error.id, 'step_03')
    
    32
    -        self.assertEqual(error.jid, 'upload.montague.tld')
    33
    +        self.assertEqual(error.jid, JID.from_string('upload.montague.tld'))

  • test/unit/test_jid_parsing.py
    ... ... @@ -30,7 +30,7 @@ class JIDParsing(unittest.TestCase):
    30 30
             ]
    
    31 31
     
    
    32 32
             for jid in tests:
    
    33
    -            JID(jid)
    
    33
    +            JID.from_string(jid)
    
    34 34
     
    
    35 35
         def test_invalid_jids(self):
    
    36 36
             tests = [
    
    ... ... @@ -48,7 +48,7 @@ class JIDParsing(unittest.TestCase):
    48 48
     
    
    49 49
             for jid, exception in tests:
    
    50 50
                 with self.assertRaises(exception):
    
    51
    -                JID(jid)
    
    51
    +                JID.from_string(jid)
    
    52 52
     
    
    53 53
         def test_ip_literals(self):
    
    54 54
             tests = [
    
    ... ... @@ -57,7 +57,7 @@ class JIDParsing(unittest.TestCase):
    57 57
             ]
    
    58 58
     
    
    59 59
             for jid in tests:
    
    60
    -            JID(jid)
    
    60
    +            JID.from_string(jid)
    
    61 61
     
    
    62 62
         def test_jid_equality(self):
    
    63 63
             tests = [
    
    ... ... @@ -67,4 +67,112 @@ class JIDParsing(unittest.TestCase):
    67 67
             ]
    
    68 68
     
    
    69 69
             for jid in tests:
    
    70
    -            self.assertTrue(JID(jid) == jid)
    70
    +            self.assertTrue(JID.from_string(jid) == JID.from_string(jid))
    
    71
    +
    
    72
    +    def test_jid_escaping(self):
    
    73
    +        # (user input, escaped)
    
    74
    +        tests = [
    
    75
    +            (r'space cadet@example.com',
    
    76
    +             r'space\20cadet@example.com'),
    
    77
    +
    
    78
    +            (r'call me "ishmael"@example.com',
    
    79
    +             r'call\20me\20\22ishmael\22@example.com'),
    
    80
    +
    
    81
    +            (r'at&t guy@example.com',
    
    82
    +             r'at\26t\20guy@example.com'),
    
    83
    +
    
    84
    +            ('d\'artagnan@example.com',
    
    85
    +             r'd\27artagnan@example.com'),
    
    86
    +
    
    87
    +            (r'/.fanboy@example.com',
    
    88
    +             r'\2f.fanboy@example.com'),
    
    89
    +
    
    90
    +            (r'::foo::@example.com',
    
    91
    +             r'\3a\3afoo\3a\3a@example.com'),
    
    92
    +
    
    93
    +            (r'<foo>@example.com',
    
    94
    +             r'\3cfoo\3e@example.com'),
    
    95
    +
    
    96
    +            (r'user@host@example.com',
    
    97
    +             r'user\40host@example.com'),
    
    98
    +
    
    99
    +            (r'c:\net@example.com',
    
    100
    +             r'c\3a\net@example.com'),
    
    101
    +
    
    102
    +            (r'c:\\net@example.com',
    
    103
    +             r'c\3a\\net@example.com'),
    
    104
    +
    
    105
    +            (r'c:\cool stuff@example.com',
    
    106
    +             r'c\3a\cool\20stuff@example.com'),
    
    107
    +
    
    108
    +            (r'c:\5commas@example.com',
    
    109
    +             r'c\3a\5c5commas@example.com'),
    
    110
    +
    
    111
    +            (r'call me\20@example.com',
    
    112
    +             r'call\20me\5c20@example.com'),
    
    113
    +        ]
    
    114
    +
    
    115
    +        tests2 = [
    
    116
    +            'juliet@example.com',
    
    117
    +            'juliet@example.com',
    
    118
    +            'juliet@example.com',
    
    119
    +            'juliet@example.com',
    
    120
    +            'fussball@example.com',
    
    121
    +            'fu\U000000DFball@example.com',
    
    122
    +            '\U000003C0@example.com',
    
    123
    +            '\U000003A3@example.com',
    
    124
    +            '\U000003C3@example.com',
    
    125
    +            '\U000003C2@example.com',
    
    126
    +            'example.com',
    
    127
    +        ]
    
    128
    +
    
    129
    +        test3 = [
    
    130
    +            '\\20callme\\20@example.com',
    
    131
    +            '\\20callme@example.com',
    
    132
    +            'callme\\20@example.com',
    
    133
    +        ]
    
    134
    +
    
    135
    +        test4 = [
    
    136
    +            ('call\\20me@example.com', 'call me@example.com',)
    
    137
    +        ]
    
    138
    +
    
    139
    +        fail_tests = [
    
    140
    +            r'c:\5commas@example.com/asd',
    
    141
    +            r'juliet@example.com/test'
    
    142
    +        ]
    
    143
    +
    
    144
    +        for user_input, escaped in tests:
    
    145
    +            # Parse user input and escape it
    
    146
    +            jid = JID.from_user_input(user_input)
    
    147
    +
    
    148
    +            self.assertTrue(jid.domain == 'example.com')
    
    149
    +            self.assertTrue(str(jid) == escaped)
    
    150
    +            self.assertTrue(jid.to_user_string() == user_input)
    
    151
    +
    
    152
    +            # We must fail on invalid JIDs
    
    153
    +            with self.assertRaises(Exception):
    
    154
    +                JID.from_string(user_input)
    
    155
    +
    
    156
    +            # Parse escaped JIDs
    
    157
    +            jid = JID.from_string(escaped)
    
    158
    +            self.assertTrue(str(jid) == escaped)
    
    159
    +            self.assertTrue(jid.domain == 'example.com')
    
    160
    +
    
    161
    +        for jid in tests2:
    
    162
    +            # show that from_string() and from_user_input() produce the same
    
    163
    +            # result for valid bare JIDs
    
    164
    +            self.assertTrue(JID.from_string(jid) == JID.from_user_input(jid))
    
    165
    +
    
    166
    +        for jid in test3:
    
    167
    +            # JIDs starting or ending with \20 are not escaped
    
    168
    +            self.assertTrue(JID.from_string(jid).to_user_string() == jid)
    
    169
    +
    
    170
    +        for user_input, user_string in test4:
    
    171
    +            # Test escaped keyword argument
    
    172
    +            self.assertTrue(JID.from_user_input(user_input, escaped=True) != JID.from_user_input(user_input))
    
    173
    +            self.assertTrue(JID.from_user_input(user_input, escaped=True).to_user_string() == user_string)
    
    174
    +
    
    175
    +        for user_input in fail_tests:
    
    176
    +            # from_user_input does only support bare jids
    
    177
    +            with self.assertRaises(Exception):
    
    178
    +                JID.from_user_input(user_input)