summaryrefslogtreecommitdiff
path: root/slixmpp/plugins/xep_0066/oob.py
blob: 233263a23d7d0ada07480ab9881fcaff68b1dbbc (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""
    Slixmpp: The Slick XMPP Library
    Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
    This file is part of Slixmpp.

    See the file LICENSE for copying permission.
"""

import logging

from slixmpp.stanza import Message, Presence, Iq
from slixmpp.exceptions import XMPPError
from slixmpp.xmlstream import register_stanza_plugin
from slixmpp.xmlstream.handler import Callback
from slixmpp.xmlstream.matcher import StanzaPath
from slixmpp.plugins import BasePlugin
from slixmpp.plugins.xep_0066 import stanza


log = logging.getLogger(__name__)


class XEP_0066(BasePlugin):

    """
    XEP-0066: Out of Band Data

    Out of Band Data is a basic method for transferring files between
    XMPP agents. The URL of the resource in question is sent to the receiving
    entity, which then downloads the resource before responding to the OOB
    request. OOB is also used as a generic means to transmit URLs in other
    stanzas to indicate where to find additional information.

    Also see <http://www.xmpp.org/extensions/xep-0066.html>.

    Events:
        oob_transfer -- Raised when a request to download a resource
                        has been received.

    Methods:
        send_oob -- Send a request to another entity to download a file
                    or other addressable resource.
    """

    name = 'xep_0066'
    description = 'XEP-0066: Out of Band Data'
    dependencies = {'xep_0030'}
    stanza = stanza

    def plugin_init(self):
        """Start the XEP-0066 plugin."""

        self.url_handlers = {'global': self._default_handler,
                             'jid': {}}

        register_stanza_plugin(Iq, stanza.OOBTransfer)
        register_stanza_plugin(Message, stanza.OOB)
        register_stanza_plugin(Presence, stanza.OOB)

        self.xmpp.register_handler(
                Callback('OOB Transfer',
                         StanzaPath('iq@type=set/oob_transfer'),
                         self._handle_transfer))

    def plugin_end(self):
        self.xmpp.remove_handler('OOB Transfer')
        self.xmpp['xep_0030'].del_feature(feature=stanza.OOBTransfer.namespace)
        self.xmpp['xep_0030'].del_feature(feature=stanza.OOB.namespace)

    def session_bind(self, jid):
        self.xmpp['xep_0030'].add_feature(stanza.OOBTransfer.namespace)
        self.xmpp['xep_0030'].add_feature(stanza.OOB.namespace)

    def register_url_handler(self, jid=None, handler=None):
        """
        Register a handler to process download requests, either for all
        JIDs or a single JID.

        Arguments:
            jid     -- If None, then set the handler as a global default.
            handler -- If None, then remove the existing handler for the
                       given JID, or reset the global handler if the JID
                       is None.
        """
        if jid is None:
            if handler is not None:
                self.url_handlers['global'] = handler
            else:
                self.url_handlers['global'] = self._default_handler
        else:
            if handler is not None:
                self.url_handlers['jid'][jid] = handler
            else:
                del self.url_handlers['jid'][jid]

    def send_oob(self, to, url, desc=None, ifrom=None, **iqargs):
        """
        Initiate a basic file transfer by sending the URL of
        a file or other resource.

        Arguments:
            url      -- The URL of the resource to transfer.
            desc     -- An optional human readable description of the item
                        that is to be transferred.
            ifrom    -- Specifiy the sender's JID.
            block    -- If true, block and wait for the stanzas' reply.
            timeout  -- The time in seconds to block while waiting for
                        a reply. If None, then wait indefinitely.
            callback -- Optional callback to execute when a reply is
                        received instead of blocking and waiting for
                        the reply.
        """
        iq = self.xmpp.Iq()
        iq['type'] = 'set'
        iq['to'] = to
        iq['from'] = ifrom
        iq['oob_transfer']['url'] = url
        iq['oob_transfer']['desc'] = desc
        return iq.send(**iqargs)

    def _run_url_handler(self, iq):
        """
        Execute the appropriate handler for a transfer request.

        Arguments:
            iq -- The Iq stanza containing the OOB transfer request.
        """
        if iq['to'] in self.url_handlers['jid']:
            return self.url_handlers['jid'][iq['to']](iq)
        else:
            if self.url_handlers['global']:
                self.url_handlers['global'](iq)
            else:
                raise XMPPError('service-unavailable')

    def _default_handler(self, iq):
        """
        As a safe default, don't actually download files.

        Register a new handler using self.register_url_handler to
        screen requests and download files.

        Arguments:
            iq -- The Iq stanza containing the OOB transfer request.
        """
        raise XMPPError('service-unavailable')

    def _handle_transfer(self, iq):
        """
        Handle receiving an out-of-band transfer request.

        Arguments:
            iq -- An Iq stanza containing an OOB transfer request.
        """
        log.debug('Received out-of-band data request for %s from %s:' % (
            iq['oob_transfer']['url'], iq['from']))
        self._run_url_handler(iq)
        iq.reply().send()