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

Commits:

2 changed files:

Changes:

  • nbxmpp/modules/omemo.py
    ... ... @@ -124,7 +124,7 @@ class OMEMO(BaseModule):
    124 124
     
    
    125 125
             raise_if_error(items)
    
    126 126
     
    
    127
    -        if items is None:
    
    127
    +        if not items:
    
    128 128
                 yield task.set_result(None)
    
    129 129
     
    
    130 130
             yield _parse_devicelist(items[0])
    
    ... ... @@ -164,7 +164,7 @@ class OMEMO(BaseModule):
    164 164
     
    
    165 165
             raise_if_error(items)
    
    166 166
     
    
    167
    -        if items is None:
    
    167
    +        if not items:
    
    168 168
                 yield task.set_result(None)
    
    169 169
     
    
    170 170
             yield _parse_bundle(items[0])
    

  • nbxmpp/modules/openpgp.py
    ... ... @@ -22,25 +22,25 @@ import string
    22 22
     from nbxmpp.namespaces import Namespace
    
    23 23
     from nbxmpp.protocol import NodeProcessed
    
    24 24
     from nbxmpp.protocol import Node
    
    25
    -from nbxmpp.protocol import isResultNode
    
    26 25
     from nbxmpp.protocol import StanzaMalformed
    
    27
    -from nbxmpp.util import call_on_response
    
    28
    -from nbxmpp.util import callback
    
    29 26
     from nbxmpp.util import b64decode
    
    30 27
     from nbxmpp.util import b64encode
    
    31
    -from nbxmpp.util import raise_error
    
    32 28
     from nbxmpp.structs import StanzaHandler
    
    33 29
     from nbxmpp.structs import PGPKeyMetadata
    
    34 30
     from nbxmpp.structs import PGPPublicKey
    
    31
    +from nbxmpp.errors import MalformedStanzaError
    
    32
    +from nbxmpp.task import iq_request_task
    
    35 33
     from nbxmpp.modules.date_and_time import parse_datetime
    
    36
    -from nbxmpp.modules.pubsub import get_pubsub_request
    
    37 34
     from nbxmpp.modules.base import BaseModule
    
    35
    +from nbxmpp.modules.util import finalize
    
    36
    +from nbxmpp.modules.util import raise_if_error
    
    38 37
     
    
    39 38
     
    
    40 39
     class OpenPGP(BaseModule):
    
    41 40
     
    
    42 41
         _depends = {
    
    43
    -        'publish': 'PubSub'
    
    42
    +        'publish': 'PubSub',
    
    43
    +        'request_items': 'PubSub',
    
    44 44
         }
    
    45 45
     
    
    46 46
         def __init__(self, client):
    
    ... ... @@ -107,8 +107,8 @@ class OpenPGP(BaseModule):
    107 107
                 return
    
    108 108
     
    
    109 109
             try:
    
    110
    -            data = self._parse_keylist(properties.jid, item)
    
    111
    -        except StanzaMalformed as error:
    
    110
    +            data = _parse_keylist(properties.jid, item)
    
    111
    +        except ValueError as error:
    
    112 112
                 self._log.warning(error)
    
    113 113
                 self._log.warning(stanza)
    
    114 114
                 raise NodeProcessed
    
    ... ... @@ -123,162 +123,137 @@ class OpenPGP(BaseModule):
    123 123
     
    
    124 124
             properties.pubsub_event = pubsub_event
    
    125 125
     
    
    126
    -    @staticmethod
    
    127
    -    def _parse_keylist(jid, item):
    
    128
    -        keylist_node = item.getTag('public-keys-list',
    
    129
    -                                   namespace=Namespace.OPENPGP)
    
    130
    -        if keylist_node is None:
    
    131
    -            raise StanzaMalformed('No public-keys-list node found')
    
    132
    -
    
    133
    -        metadata = keylist_node.getTags('pubkey-metadata')
    
    134
    -        if not metadata:
    
    135
    -            return None
    
    136
    -
    
    137
    -        data = []
    
    138
    -        for key in metadata:
    
    139
    -            fingerprint = key.getAttr('v4-fingerprint')
    
    140
    -            date = key.getAttr('date')
    
    141
    -            if fingerprint is None or date is None:
    
    142
    -                raise StanzaMalformed('Invalid metadata node')
    
    143
    -
    
    144
    -            timestamp = parse_datetime(date, epoch=True)
    
    145
    -            if timestamp is None:
    
    146
    -                raise StanzaMalformed('Invalid date timestamp: %s' % date)
    
    147
    -
    
    148
    -            data.append(PGPKeyMetadata(jid, fingerprint, timestamp))
    
    149
    -        return data
    
    150
    -
    
    151
    -    def set_keylist(self, keylist):
    
    152
    -        item = Node('public-keys-list', {'xmlns': Namespace.OPENPGP})
    
    153
    -        if keylist is not None:
    
    154
    -            for key in keylist:
    
    155
    -                date = time.strftime('%Y-%m-%dT%H:%M:%SZ',
    
    156
    -                                     time.gmtime(key.date))
    
    157
    -                attrs = {'v4-fingerprint': key.fingerprint,
    
    158
    -                         'date': date}
    
    159
    -                item.addChild('pubkey-metadata', attrs=attrs)
    
    126
    +    @iq_request_task
    
    127
    +    def set_keylist(self, keylist, public=True):
    
    128
    +        task = yield
    
    129
    +
    
    130
    +        access_model = 'open' if public else 'presence'
    
    131
    +
    
    132
    +        options = {
    
    133
    +            'pubsub#persist_items': 'true',
    
    134
    +            'pubsub#access_model': access_model,
    
    135
    +        }
    
    160 136
     
    
    161 137
             self._log.info('Set keylist: %s', keylist)
    
    162 138
     
    
    163
    -        self.publish(Namespace.OPENPGP_PK, item, id_='current')
    
    139
    +        result = yield self.publish(Namespace.OPENPGP_PK,
    
    140
    +                                    _make_keylist(keylist),
    
    141
    +                                    id_='current',
    
    142
    +                                    options=options,
    
    143
    +                                    force_node_options=True)
    
    144
    +
    
    145
    +        yield finalize(task, result)
    
    164 146
     
    
    165
    -    def set_public_key(self, key, fingerprint, date):
    
    166
    -        date = time.strftime(
    
    167
    -            '%Y-%m-%dT%H:%M:%SZ', time.gmtime(date))
    
    168
    -        item = Node('pubkey', attrs={'xmlns': Namespace.OPENPGP,
    
    169
    -                                     'date': date})
    
    170
    -        data = item.addChild('data')
    
    171
    -        data.addData(b64encode(key))
    
    172
    -        node = '%s:%s' % (Namespace.OPENPGP_PK, fingerprint)
    
    147
    +    @iq_request_task
    
    148
    +    def set_public_key(self, key, fingerprint, date, public=True):
    
    149
    +        task = yield
    
    173 150
     
    
    174 151
             self._log.info('Set public key')
    
    175 152
     
    
    176
    -        self.publish(node, item, id_='current')
    
    153
    +        access_model = 'open' if public else 'presence'
    
    177 154
     
    
    178
    -    @call_on_response('_public_key_received')
    
    179
    -    def request_public_key(self, jid, fingerprint):
    
    180
    -        self._log.info('Request public key from: %s %s', jid, fingerprint)
    
    181
    -        node = '%s:%s' % (Namespace.OPENPGP_PK, fingerprint)
    
    182
    -        return get_pubsub_request(jid, node, max_items=1)
    
    155
    +        options = {
    
    156
    +            'pubsub#persist_items': 'true',
    
    157
    +            'pubsub#access_model': access_model,
    
    158
    +        }
    
    183 159
     
    
    184
    -    @callback
    
    185
    -    def _public_key_received(self, stanza):
    
    186
    -        jid = stanza.getFrom().bare
    
    160
    +        result = yield self.publish(f'{Namespace.OPENPGP_PK}:{fingerprint}',
    
    161
    +                                    _make_public_key(key, date),
    
    162
    +                                    id_='current',
    
    163
    +                                    options=options,
    
    164
    +                                    force_node_options=True)
    
    187 165
     
    
    188
    -        if not isResultNode(stanza):
    
    189
    -            return raise_error(self._log.info, stanza)
    
    166
    +        yield finalize(task, result)
    
    190 167
     
    
    191
    -        pubsub_node = stanza.getTag('pubsub')
    
    192
    -        items_node = pubsub_node.getTag('items')
    
    193
    -        item = items_node.getTag('item')
    
    168
    +    @iq_request_task
    
    169
    +    def request_public_key(self, jid, fingerprint):
    
    170
    +        task = yield
    
    194 171
     
    
    195
    -        pub_key = item.getTag('pubkey', namespace=Namespace.OPENPGP)
    
    196
    -        if pub_key is None:
    
    197
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed',
    
    198
    -                               'PGP public key has no pubkey node')
    
    172
    +        self._log.info('Request public key from: %s %s', jid, fingerprint)
    
    199 173
     
    
    200
    -        date = parse_datetime(pub_key.getAttr('date'), epoch=True)
    
    174
    +        items = yield self.request_items(
    
    175
    +            f'{Namespace.OPENPGP_PK}:{fingerprint}',
    
    176
    +            max_items=1,
    
    177
    +            jid=jid)
    
    201 178
     
    
    202
    -        data = pub_key.getTag('data')
    
    203
    -        if data is None:
    
    204
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed',
    
    205
    -                               'PGP public key has no data node')
    
    179
    +        raise_if_error(items)
    
    180
    +
    
    181
    +        if not items:
    
    182
    +            yield task.set_result(None)
    
    206 183
     
    
    207 184
             try:
    
    208
    -            key = b64decode(data.getData(), return_type=bytes)
    
    209
    -        except Exception as error:
    
    210
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed',
    
    211
    -                               str(error))
    
    185
    +            key = _parse_public_key(jid, items[0])
    
    186
    +        except ValueError as error:
    
    187
    +            raise MalformedStanzaError(str(error), items)
    
    212 188
     
    
    213
    -        key = PGPPublicKey(jid, key, date)
    
    214
    -        self._log.info('Received public key: %s %s', key.jid, key.date)
    
    215
    -        return key
    
    189
    +        yield key
    
    190
    +
    
    191
    +    @iq_request_task
    
    192
    +    def request_keylist(self, jid=None):
    
    193
    +        task = yield
    
    216 194
     
    
    217
    -    @call_on_response('_keylist_received')
    
    218
    -    def request_keylist(self, jid):
    
    219 195
             self._log.info('Request keylist from: %s', jid)
    
    220
    -        return get_pubsub_request(jid, Namespace.OPENPGP_PK, max_items=1)
    
    221 196
     
    
    222
    -    @callback
    
    223
    -    def _keylist_received(self, stanza):
    
    224
    -        jid = stanza.getFrom().bare
    
    197
    +        items = yield self.request_items(
    
    198
    +            Namespace.OPENPGP_PK,
    
    199
    +            max_items=1,
    
    200
    +            jid=jid)
    
    225 201
     
    
    226
    -        if not isResultNode(stanza):
    
    227
    -            return raise_error(self._log.info, stanza)
    
    202
    +        raise_if_error(items)
    
    228 203
     
    
    229
    -        pubsub_node = stanza.getTag('pubsub')
    
    230
    -        items_node = pubsub_node.getTag('items')
    
    231
    -        item = items_node.getTag('item')
    
    204
    +        if not items:
    
    205
    +            yield task.set_result(None)
    
    232 206
     
    
    233 207
             try:
    
    234
    -            keylist = self._parse_keylist(jid, item)
    
    235
    -        except StanzaMalformed as error:
    
    236
    -            return raise_error(self._log.warning, stanza,
    
    237
    -                               'stanza-malformed', str(error))
    
    208
    +            keylist = _parse_keylist(jid, items[0])
    
    209
    +        except ValueError as error:
    
    210
    +            raise MalformedStanzaError(str(error), items)
    
    211
    +
    
    238 212
             self._log.info('Received keylist: %s', keylist)
    
    239
    -        return keylist
    
    213
    +        yield keylist
    
    240 214
     
    
    241
    -    @call_on_response('_secret_key_received')
    
    215
    +    @iq_request_task
    
    242 216
         def request_secret_key(self):
    
    243
    -        self._log.info('Request secret key')
    
    244
    -        jid = self._client.get_bound_jid().bare
    
    245
    -        return get_pubsub_request(jid, Namespace.OPENPGP_SK, max_items=1)
    
    217
    +        task = yield
    
    246 218
     
    
    247
    -    @callback
    
    248
    -    def _secret_key_received(self, stanza):
    
    249
    -        if not isResultNode(stanza):
    
    250
    -            return raise_error(self._log.info, stanza)
    
    219
    +        self._log.info('Request secret key')
    
    251 220
     
    
    252
    -        pubsub_node = stanza.getTag('pubsub')
    
    253
    -        items_node = pubsub_node.getTag('items')
    
    254
    -        item = items_node.getTag('item')
    
    221
    +        items = yield self.request_items(
    
    222
    +            Namespace.OPENPGP_SK,
    
    223
    +            max_items=1)
    
    255 224
     
    
    256
    -        sec_key = item.getTag('secretkey', namespace=Namespace.OPENPGP)
    
    257
    -        if sec_key is None:
    
    258
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed',
    
    259
    -                               'PGP secretkey node not found')
    
    225
    +        raise_if_error(items)
    
    260 226
     
    
    261
    -        data = sec_key.getData()
    
    262
    -        if not data:
    
    263
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed',
    
    264
    -                               'PGP secretkey has no data')
    
    227
    +        if not items:
    
    228
    +            yield task.set_result(None)
    
    265 229
     
    
    266 230
             try:
    
    267
    -            key = b64decode(data, return_type=bytes)
    
    268
    -        except Exception as error:
    
    269
    -            return raise_error(self._log.warning, stanza, 'stanza-malformed',
    
    270
    -                               str(error))
    
    271
    -        self._log.info('Received secret key')
    
    272
    -        return key
    
    231
    +            secret_key = _parse_secret_key(items[0])
    
    232
    +        except ValueError as error:
    
    233
    +            raise MalformedStanzaError(str(error), items)
    
    273 234
     
    
    235
    +        yield secret_key
    
    236
    +
    
    237
    +    @iq_request_task
    
    274 238
         def set_secret_key(self, secret_key):
    
    275
    -        item = Node('secretkey', {'xmlns': Namespace.OPENPGP})
    
    276
    -        if secret_key is not None:
    
    277
    -            item.setData(b64encode(secret_key))
    
    239
    +        task = yield
    
    240
    +
    
    241
    +        self._log.info('Set public key')
    
    242
    +
    
    243
    +        options = {
    
    244
    +            'pubsub#persist_items': 'true',
    
    245
    +            'pubsub#access_model': 'whitelist',
    
    246
    +        }
    
    278 247
     
    
    279 248
             self._log.info('Set secret key')
    
    280 249
     
    
    281
    -        self.publish(Namespace.OPENPGP_SK, item, id_='current')
    
    250
    +        result = yield self.publish(Namespace.OPENPGP_SK,
    
    251
    +                                    _make_secret_key(secret_key),
    
    252
    +                                    id_='current',
    
    253
    +                                    options=options,
    
    254
    +                                    force_node_options=True)
    
    255
    +
    
    256
    +        yield finalize(task, result)
    
    282 257
     
    
    283 258
     
    
    284 259
     def parse_signcrypt(stanza):
    
    ... ... @@ -374,3 +349,93 @@ def create_message_stanza(stanza, encrypted_payload, with_fallback_text):
    374 349
         if with_fallback_text:
    
    375 350
             stanza.setBody(
    
    376 351
                 '[This message is *encrypted* with OpenPGP (See :XEP:`0373`]')
    
    352
    +
    
    353
    +
    
    354
    +def _make_keylist(keylist):
    
    355
    +    item = Node('public-keys-list', {'xmlns': Namespace.OPENPGP})
    
    356
    +    if keylist is not None:
    
    357
    +        for key in keylist:
    
    358
    +            date = time.strftime('%Y-%m-%dT%H:%M:%SZ',
    
    359
    +                                 time.gmtime(key.date))
    
    360
    +            attrs = {'v4-fingerprint': key.fingerprint,
    
    361
    +                     'date': date}
    
    362
    +            item.addChild('pubkey-metadata', attrs=attrs)
    
    363
    +    return item
    
    364
    +
    
    365
    +
    
    366
    +def _make_public_key(key, date):
    
    367
    +    date = time.strftime(
    
    368
    +        '%Y-%m-%dT%H:%M:%SZ', time.gmtime(date))
    
    369
    +    item = Node('pubkey', attrs={'xmlns': Namespace.OPENPGP,
    
    370
    +                                 'date': date})
    
    371
    +    data = item.addChild('data')
    
    372
    +    data.addData(b64encode(key))
    
    373
    +    return item
    
    374
    +
    
    375
    +
    
    376
    +def _make_secret_key(secret_key):
    
    377
    +    item = Node('secretkey', {'xmlns': Namespace.OPENPGP})
    
    378
    +    if secret_key is not None:
    
    379
    +        item.setData(b64encode(secret_key))
    
    380
    +    return item
    
    381
    +
    
    382
    +
    
    383
    +def _parse_public_key(jid, item):
    
    384
    +    pub_key = item.getTag('pubkey', namespace=Namespace.OPENPGP)
    
    385
    +    if pub_key is None:
    
    386
    +        raise ValueError('pubkey node missing')
    
    387
    +
    
    388
    +    date = parse_datetime(pub_key.getAttr('date'), epoch=True)
    
    389
    +
    
    390
    +    data = pub_key.getTag('data')
    
    391
    +    if data is None:
    
    392
    +        raise ValueError('data node missing')
    
    393
    +
    
    394
    +    try:
    
    395
    +        key = b64decode(data.getData(), return_type=bytes)
    
    396
    +    except Exception as error:
    
    397
    +        raise ValueError(f'decoding error: {error}')
    
    398
    +
    
    399
    +    return PGPPublicKey(jid, key, date)
    
    400
    +
    
    401
    +
    
    402
    +def _parse_keylist(jid, item):
    
    403
    +    keylist_node = item.getTag('public-keys-list',
    
    404
    +                               namespace=Namespace.OPENPGP)
    
    405
    +    if keylist_node is None:
    
    406
    +        raise ValueError('public-keys-list node missing')
    
    407
    +
    
    408
    +    metadata = keylist_node.getTags('pubkey-metadata')
    
    409
    +    if not metadata:
    
    410
    +        return None
    
    411
    +
    
    412
    +    data = []
    
    413
    +    for key in metadata:
    
    414
    +        fingerprint = key.getAttr('v4-fingerprint')
    
    415
    +        date = key.getAttr('date')
    
    416
    +        if fingerprint is None or date is None:
    
    417
    +            raise ValueError('Invalid metadata node')
    
    418
    +
    
    419
    +        timestamp = parse_datetime(date, epoch=True)
    
    420
    +        if timestamp is None:
    
    421
    +            raise ValueError('Invalid date timestamp: %s' % date)
    
    422
    +
    
    423
    +        data.append(PGPKeyMetadata(jid, fingerprint, timestamp))
    
    424
    +    return data
    
    425
    +
    
    426
    +
    
    427
    +def _parse_secret_key(item):
    
    428
    +    sec_key = item.getTag('secretkey', namespace=Namespace.OPENPGP)
    
    429
    +    if sec_key is None:
    
    430
    +        raise ValueError('secretkey node missing')
    
    431
    +
    
    432
    +    data = sec_key.getData()
    
    433
    +    if not data:
    
    434
    +        raise ValueError('secretkey data missing')
    
    435
    +
    
    436
    +    try:
    
    437
    +        key = b64decode(data, return_type=bytes)
    
    438
    +    except Exception as error:
    
    439
    +        raise ValueError(f'decoding error: {error}')
    
    440
    +
    
    441
    +    return key