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

Commits:

5 changed files:

Changes:

  • nbxmpp/const.py
    ... ... @@ -296,6 +296,26 @@ class AnonymityMode(Enum):
    296 296
         NONE = 'none'
    
    297 297
     
    
    298 298
     
    
    299
    +class AdHocStatus(Enum):
    
    300
    +    EXECUTING = 'executing'
    
    301
    +    COMPLETED = 'completed'
    
    302
    +    CANCELED = 'canceled'
    
    303
    +
    
    304
    +
    
    305
    +class AdHocAction(Enum):
    
    306
    +    EXECUTE = 'execute'
    
    307
    +    CANCEL = 'cancel'
    
    308
    +    PREV = 'prev'
    
    309
    +    NEXT = 'next'
    
    310
    +    COMPLETE = 'complete'
    
    311
    +
    
    312
    +
    
    313
    +class AdHocNoteType(Enum):
    
    314
    +    INFO = 'info'
    
    315
    +    WARN = 'warn'
    
    316
    +    ERROR = 'error'
    
    317
    +
    
    318
    +
    
    299 319
     MOODS = [
    
    300 320
         'afraid',
    
    301 321
         'amazed',
    

  • nbxmpp/dispatcher.py
    ... ... @@ -67,6 +67,7 @@ from nbxmpp.modules.omemo import OMEMO
    67 67
     from nbxmpp.modules.annotations import Annotations
    
    68 68
     from nbxmpp.modules.muclumbus import Muclumbus
    
    69 69
     from nbxmpp.modules.software_version import SoftwareVersion
    
    70
    +from nbxmpp.modules.adhoc import AdHoc
    
    70 71
     from nbxmpp.modules.misc import unwrap_carbon
    
    71 72
     from nbxmpp.modules.misc import unwrap_mam
    
    72 73
     from nbxmpp.util import get_properties_struct
    
    ... ... @@ -202,6 +203,7 @@ class XMPPDispatcher(PlugIn):
    202 203
             self._modules['Annotations'] = Annotations(self._owner)
    
    203 204
             self._modules['Muclumbus'] = Muclumbus(self._owner)
    
    204 205
             self._modules['SoftwareVersion'] = SoftwareVersion(self._owner)
    
    206
    +        self._modules['AdHoc'] = AdHoc(self._owner)
    
    205 207
     
    
    206 208
             for instance in self._modules.values():
    
    207 209
                 for handler in instance.handlers:
    

  • nbxmpp/modules/adhoc.py
    1
    +# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
    
    2
    +#
    
    3
    +# This file is part of nbxmpp.
    
    4
    +#
    
    5
    +# This program is free software; you can redistribute it and/or
    
    6
    +# modify it under the terms of the GNU General Public License
    
    7
    +# as published by the Free Software Foundation; either version 3
    
    8
    +# of the License, or (at your option) any later version.
    
    9
    +#
    
    10
    +# This program is distributed in the hope that it will be useful,
    
    11
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    12
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    13
    +# GNU General Public License for more details.
    
    14
    +#
    
    15
    +# You should have received a copy of the GNU General Public License
    
    16
    +# along with this program; If not, see <http://www.gnu.org/licenses/>.
    
    17
    +
    
    18
    +import logging
    
    19
    +
    
    20
    +from nbxmpp.protocol import NS_COMMANDS
    
    21
    +from nbxmpp.protocol import NS_DISCO_ITEMS
    
    22
    +from nbxmpp.protocol import NS_DATA
    
    23
    +from nbxmpp.protocol import Iq
    
    24
    +from nbxmpp.protocol import isResultNode
    
    25
    +from nbxmpp.protocol import Node
    
    26
    +from nbxmpp.util import call_on_response
    
    27
    +from nbxmpp.util import callback
    
    28
    +from nbxmpp.util import raise_error
    
    29
    +from nbxmpp.structs import AdHocCommand
    
    30
    +from nbxmpp.structs import AdHocCommandNote
    
    31
    +from nbxmpp.const import AdHocStatus
    
    32
    +from nbxmpp.const import AdHocAction
    
    33
    +from nbxmpp.const import AdHocNoteType
    
    34
    +from nbxmpp.modules.discovery import get_disco_request
    
    35
    +
    
    36
    +
    
    37
    +log = logging.getLogger('nbxmpp.m.adhoc')
    
    38
    +
    
    39
    +
    
    40
    +class AdHoc:
    
    41
    +    def __init__(self, client):
    
    42
    +        self._client = client
    
    43
    +        self.handlers = []
    
    44
    +
    
    45
    +    @call_on_response('_command_list_received')
    
    46
    +    def request_command_list(self, jid=None):
    
    47
    +        if jid is None:
    
    48
    +            jid = self._client.get_bound_jid().getBare()
    
    49
    +        return get_disco_request(NS_DISCO_ITEMS, jid, node=NS_COMMANDS)
    
    50
    +
    
    51
    +    @callback
    
    52
    +    def _command_list_received(self, stanza):
    
    53
    +        if not isResultNode(stanza):
    
    54
    +            return raise_error(log.info, stanza)
    
    55
    +
    
    56
    +        payload = stanza.getQueryPayload()
    
    57
    +        if not payload:
    
    58
    +            return raise_error(log.warning, stanza, 'stanza-malformed')
    
    59
    +
    
    60
    +        command_list = []
    
    61
    +        for item in payload:
    
    62
    +            if item.getName() != 'item':
    
    63
    +                continue
    
    64
    +            try:
    
    65
    +                command_list.append(AdHocCommand(**item.getAttrs()))
    
    66
    +            except Exception as error:
    
    67
    +                log.warning(error)
    
    68
    +                return raise_error(log.warning, stanza, 'stanza-malformed')
    
    69
    +
    
    70
    +        return command_list
    
    71
    +
    
    72
    +    @call_on_response('_command_result_received')
    
    73
    +    def execute_command(self, command, action=None, dataform=None):
    
    74
    +        if action is None:
    
    75
    +            action = AdHocAction.EXECUTE
    
    76
    +        attrs = {'node': command.node,
    
    77
    +                 'xmlns': NS_COMMANDS,
    
    78
    +                 'action': action.value}
    
    79
    +        if command.sessionid is not None:
    
    80
    +            attrs['sessionid'] = command.sessionid
    
    81
    +
    
    82
    +        command_node = Node('command', attrs=attrs)
    
    83
    +        if dataform is not None:
    
    84
    +            command_node.addChild(node=dataform)
    
    85
    +        iq = Iq('set', to=command.jid)
    
    86
    +        iq.addChild(node=command_node)
    
    87
    +        return iq
    
    88
    +
    
    89
    +    @callback
    
    90
    +    def _command_result_received(self, stanza):
    
    91
    +        if not isResultNode(stanza):
    
    92
    +            return raise_error(log.info, stanza)
    
    93
    +
    
    94
    +        command = stanza.getTag('command', namespace=NS_COMMANDS)
    
    95
    +        if command is None:
    
    96
    +            return raise_error(log.warning, stanza, 'stanza-malformed')
    
    97
    +
    
    98
    +        attrs = command.getAttrs()
    
    99
    +        notes = []
    
    100
    +        actions = []
    
    101
    +        try:
    
    102
    +            for note in command.getTags('note'):
    
    103
    +                type_ = note.getAttr('type')
    
    104
    +                if type_ is not None:
    
    105
    +                    type_ = AdHocNoteType(note.getAttr('type'))
    
    106
    +                notes.append(AdHocCommandNote(text=note.getData(),
    
    107
    +                                              type=type_))
    
    108
    +
    
    109
    +            actions_ = command.getTag('actions')
    
    110
    +            if actions_ is not None:
    
    111
    +                for action in actions_.getChildren():
    
    112
    +                    actions.append(AdHocAction(action.getName()))
    
    113
    +
    
    114
    +            return AdHocCommand(
    
    115
    +                jid=str(stanza.getFrom()),
    
    116
    +                name=None,
    
    117
    +                node=attrs['node'],
    
    118
    +                sessionid=attrs.get('sessionid'),
    
    119
    +                status=AdHocStatus(attrs['status']),
    
    120
    +                data=command.getTag('x', namespace=NS_DATA),
    
    121
    +                actions=actions,
    
    122
    +                notes=notes)
    
    123
    +        except Exception as error:
    
    124
    +            log.warning(error)
    
    125
    +            return raise_error(log.warning, stanza, 'stanza-malformed')

  • nbxmpp/modules/discovery.py
    1
    +# Copyright (C) 2019 Philipp Hörist <philipp AT hoerist.com>
    
    2
    +#
    
    3
    +# This file is part of nbxmpp.
    
    4
    +#
    
    5
    +# This program is free software; you can redistribute it and/or
    
    6
    +# modify it under the terms of the GNU General Public License
    
    7
    +# as published by the Free Software Foundation; either version 3
    
    8
    +# of the License, or (at your option) any later version.
    
    9
    +#
    
    10
    +# This program is distributed in the hope that it will be useful,
    
    11
    +# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    12
    +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    13
    +# GNU General Public License for more details.
    
    14
    +#
    
    15
    +# You should have received a copy of the GNU General Public License
    
    16
    +# along with this program; If not, see <http://www.gnu.org/licenses/>.
    
    17
    +
    
    18
    +import logging
    
    19
    +
    
    20
    +from nbxmpp.protocol import Iq
    
    21
    +
    
    22
    +
    
    23
    +log = logging.getLogger('nbxmpp.m.discovery')
    
    24
    +
    
    25
    +
    
    26
    +def get_disco_request(namespace, jid, node=None):
    
    27
    +    iq = Iq('get', to=jid, queryNS=namespace)
    
    28
    +    if node is not None:
    
    29
    +        iq.getQuery().setAttr('node', node)
    
    30
    +    return iq

  • nbxmpp/structs.py
    ... ... @@ -29,6 +29,7 @@ from nbxmpp.const import StatusCode
    29 29
     from nbxmpp.const import PresenceType
    
    30 30
     from nbxmpp.const import Error
    
    31 31
     from nbxmpp.const import LOCATION_DATA
    
    32
    +from nbxmpp.const import AdHocStatus
    
    32 33
     
    
    33 34
     StanzaHandler = namedtuple('StanzaHandler',
    
    34 35
                                'name callback typ ns xmlns system priority')
    
    ... ... @@ -106,6 +107,25 @@ MuclumbusItem = namedtuple('MuclumbusItem', 'jid name nusers description languag
    106 107
     
    
    107 108
     SoftwareVersionResult = namedtuple('SoftwareVersionResult', 'name version os')
    
    108 109
     
    
    110
    +AdHocCommandNote = namedtuple('AdHocCommandNote', 'text type')
    
    111
    +
    
    112
    +class AdHocCommand(namedtuple('AdHocCommand', 'jid node name sessionid status data actions notes')):
    
    113
    +
    
    114
    +    __slots__ = []
    
    115
    +
    
    116
    +    def __new__(cls, jid, node, name, sessionid=None, status=None,
    
    117
    +                data=None, actions=None, notes=None):
    
    118
    +        return super(AdHocCommand, cls).__new__(cls, jid, node, name, sessionid,
    
    119
    +                                                status, data, actions, notes)
    
    120
    +
    
    121
    +    @property
    
    122
    +    def is_completed(self):
    
    123
    +        return self.status == AdHocStatus.COMPLETED
    
    124
    +
    
    125
    +    @property
    
    126
    +    def is_canceled(self):
    
    127
    +        return self.status == AdHocStatus.CANCEL
    
    128
    +
    
    109 129
     
    
    110 130
     class OMEMOBundle(namedtuple('OMEMOBundle', 'spk spk_signature ik otpks')):
    
    111 131
         def pick_prekey(self):