Welcome to mirror list, hosted at ThFree Co, Russian Federation.

bind.py « nbxmpp - dev.gajim.org/gajim/python-nbxmpp.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 207fd87ff4983ca5d8998eb582aa9250010f86bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
# Copyright (C)           Dimitur Kirov <dkirov AT gmail.com>
# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
#
# This file is part of nbxmpp.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; If not, see <http://www.gnu.org/licenses/>.

import logging

from nbxmpp.plugin import PlugIn
from nbxmpp.protocol import NS_BIND
from nbxmpp.protocol import NS_SESSION
from nbxmpp.protocol import NS_STREAMS
from nbxmpp.protocol import NS_STREAM_MGMT
from nbxmpp.protocol import Node
from nbxmpp.protocol import isResultNode
from nbxmpp.protocol import Protocol
from nbxmpp.const import Realm
from nbxmpp.const import Event

log = logging.getLogger('nbxmpp.bind')


class NonBlockingBind(PlugIn):
    """
    Bind some JID to the current connection to allow router know of our
    location. Must be plugged after successful SASL auth
    """

    def __init__(self):
        PlugIn.__init__(self)
        self._session_required = False

    def plugin(self, _owner):
        self._owner.RegisterHandler(
            'features', self._on_features, xmlns=NS_STREAMS)

        feats = self._owner.Dispatcher.Stream.features
        if feats is not None:
            if feats.getTag('bind', namespace=NS_BIND) is not None:
                # We already received the features
                self._on_features(None, feats)

    def _on_features(self, _con, feats):
        """
        Determine if server supports resource binding and set some internal
        attributes accordingly.
        """
        if not feats or not feats.getTag('bind', namespace=NS_BIND):
            return

        session = feats.getTag('session', namespace=NS_SESSION)
        if session is not None:
            if session.getTag('optional') is None:
                self._session_required = True

        self._bind()

    def plugout(self):
        """
        Remove Bind handler from owner's dispatcher. Used internally
        """
        self._owner.UnregisterHandler(
            'features', self._on_features, xmlns=NS_STREAMS)

    def _bind(self):
        """
        Perform binding. Use provided resource name or random (if not provided).
        """
        log.info('Send bind')
        resource = []
        if self._owner._Resource:
            resource = [Node('resource', payload=[self._owner._Resource])]

        payload = Node('bind', attrs={'xmlns': NS_BIND}, payload=resource)
        node = Protocol('iq', typ='set', payload=[payload])

        self._owner.Dispatcher.SendAndCallForResponse(node, func=self._on_bind)

    def _on_bind(self, _client, stanza):
        if isResultNode(stanza):
            bind = stanza.getTag('bind')
            if bind is not None:
                jid = bind.getTagData('jid')
                log.info('Successfully bound %s', jid)
                self._owner.set_bound_jid(jid)

                if not self._session_required:
                    # Server don't want us to initialize a session
                    log.info('No session required')
                    self._on_bind_successful()
                else:
                    node = Node('session', attrs={'xmlns':NS_SESSION})
                    iq = Protocol('iq', typ='set', payload=[node])
                    self._owner.SendAndCallForResponse(
                        iq, func=self._on_session)
                return
        if stanza:
            log.error('Binding failed: %s.', stanza.getTag('error'))
        else:
            log.error('Binding failed: timeout expired')
        self._owner.Connection.start_disconnect()
        self._owner.Dispatcher.Event(Realm.CONNECTING, Event.BIND_FAILED)
        self.PlugOut()

    def _on_session(self, _client, stanza):
        if isResultNode(stanza):
            log.info('Successfully started session')
            self._on_bind_successful()
        else:
            log.error('Session open failed')
            self._owner.Connection.start_disconnect()
            self._owner.Dispatcher.Event(Realm.CONNECTING, Event.SESSION_FAILED)
            self.PlugOut()

    def _on_bind_successful(self):
        feats = self._owner.Dispatcher.Stream.features
        if feats.getTag('sm', namespace=NS_STREAM_MGMT):
            self._owner.Smacks.send_enable()
        self._owner.Dispatcher.Event(Realm.CONNECTING, Event.CONNECTION_ACTIVE)
        self.PlugOut()