Changeset 68:2c6d1f895583


Ignore:
Timestamp:
Jul 17, 2009, 3:19:40 PM (12 years ago)
Author:
Ralph Meijer <ralphm@…>
Branch:
default
Convert:
svn:b33ecbfc-034c-dc11-8662-000475d9059e/trunk@186
Message:

Implement a better presence protocol handler PresenceProtocol?.

This handler is not client-only, like PresenceClientProtocol?, and accepts
sender arguments to all methods for outgoing presence. The *Received
methods now expect a single argument that is a parsed representation of the
presence stanza.

Also included is a simple XMPP server example that connects via the
server-to-server protocol, accepting presence requests and echoing incoming
messages for all potential addresses at a domain.

Files:
1 added
3 edited

Legend:

Unmodified
Added
Removed
  • wokkel/generic.py

    r57 r68  
    11# -*- test-case-name: wokkel.test.test_generic -*-
    22#
    3 # Copyright (c) 2003-2008 Ralph Meijer
     3# Copyright (c) 2003-2009 Ralph Meijer
    44# See LICENSE for details.
    55
     
    1111
    1212from twisted.internet import defer, protocol
     13from twisted.python import reflect
    1314from twisted.words.protocols.jabber import error, jid, xmlstream
    1415from twisted.words.protocols.jabber.xmlstream import toResponse
     
    174175    """
    175176
    176     sender = None
    177     recipient = None
     177    stanzaKind = None
     178    stanzaID = None
    178179    stanzaType = None
     180
     181    def __init__(self, recipient=None, sender=None):
     182        self.recipient = recipient
     183        self.sender = sender
     184
    179185
    180186    @classmethod
     
    186192
    187193    def parseElement(self, element):
    188         self.sender = jid.internJID(element['from'])
    189194        if element.hasAttribute('from'):
    190195            self.sender = jid.internJID(element['from'])
     
    192197            self.recipient = jid.internJID(element['to'])
    193198        self.stanzaType = element.getAttribute('type')
     199        self.stanzaID = element.getAttribute('id')
     200
     201        # Save element
     202        stripNamespace(element)
     203        self.element = element
     204
     205        # accumulate all childHandlers in the class hierarchy of Class
     206        handlers = {}
     207        reflect.accumulateClassDict(self.__class__, 'childParsers', handlers)
     208
     209        for child in element.elements():
     210            try:
     211                handler = handlers[child.uri, child.name]
     212            except KeyError:
     213                pass
     214            else:
     215                getattr(self, handler)(child)
     216
     217
     218    def toElement(self):
     219        element = domish.Element((None, self.stanzaKind))
     220        if self.sender is not None:
     221            element['from'] = self.sender.full()
     222        if self.recipient is not None:
     223            element['to'] = self.recipient.full()
     224        if self.stanzaType:
     225            element['type'] = self.stanzaType
     226        if self.stanzaID:
     227            element['id'] = self.stanzaID
     228        return element
     229
     230
     231
     232class ErrorStanza(Stanza):
     233
     234    def parseElement(self, element):
     235        Stanza.parseElement(self, element)
     236        self.exception = error.exceptionFromStanza(element)
    194237
    195238
  • wokkel/test/test_xmppim.py

    r28 r68  
    1 # Copyright (c) 2003-2008 Ralph Meijer
     1# Copyright (c) 2003-2009 Ralph Meijer
    22# See LICENSE for details
    33
     
    66"""
    77
     8from twisted.internet import defer
    89from twisted.trial import unittest
    910from twisted.words.protocols.jabber.jid import JID
    1011from twisted.words.protocols.jabber.xmlstream import toResponse
    11 from twisted.words.xish import domish
     12from twisted.words.xish import domish, utility
    1213
    1314from wokkel import xmppim
     15from wokkel.generic import ErrorStanza, parseXml
    1416from wokkel.test.helpers import XmlStreamStub
    1517
     18NS_XML = 'http://www.w3.org/XML/1998/namespace'
    1619NS_ROSTER = 'jabber:iq:roster'
    1720
     
    7679
    7780
     81
     82class AvailabilityPresenceTest(unittest.TestCase):
     83
     84    def test_fromElement(self):
     85        xml = """<presence from='user@example.org' to='user@example.com'>
     86                   <show>chat</show>
     87                   <status>Let's chat!</status>
     88                   <priority>50</priority>
     89                 </presence>
     90              """
     91
     92        presence = xmppim.AvailabilityPresence.fromElement(parseXml(xml))
     93        self.assertEquals(JID('user@example.org'), presence.sender)
     94        self.assertEquals(JID('user@example.com'), presence.recipient)
     95        self.assertTrue(presence.available)
     96        self.assertEquals('chat', presence.show)
     97        self.assertEquals({None: "Let's chat!"}, presence.statuses)
     98        self.assertEquals(50, presence.priority)
     99
     100
     101class PresenceProtocolTest(unittest.TestCase):
     102    """
     103    Tests for L{xmppim.PresenceProtocol}
     104    """
     105
     106    def setUp(self):
     107        self.output = []
     108        self.protocol = xmppim.PresenceProtocol()
     109        self.protocol.parent = self
     110        self.protocol.xmlstream = utility.EventDispatcher()
     111        self.protocol.connectionInitialized()
     112
     113
     114    def send(self, obj):
     115        self.output.append(obj)
     116
     117
     118    def test_errorReceived(self):
     119        """
     120        Incoming presence stanzas are parsed and dispatched.
     121        """
     122        xml = """<presence type="error"/>"""
     123
     124        def errorReceived(error):
     125            xmppim.PresenceProtocol.errorReceived(self.protocol, error)
     126            try:
     127                self.assertIsInstance(error, ErrorStanza)
     128            except:
     129                d.errback()
     130            else:
     131                d.callback(None)
     132
     133        d = defer.Deferred()
     134        self.protocol.errorReceived = errorReceived
     135        self.protocol.xmlstream.dispatch(parseXml(xml))
     136        return d
     137
     138
     139    def test_availableReceived(self):
     140        """
     141        Incoming presence stanzas are parsed and dispatched.
     142        """
     143        xml = """<presence/>"""
     144
     145        def availableReceived(presence):
     146            xmppim.PresenceProtocol.availableReceived(self.protocol, presence)
     147            try:
     148                self.assertIsInstance(presence, xmppim.AvailabilityPresence)
     149            except:
     150                d.errback()
     151            else:
     152                d.callback(None)
     153
     154        d = defer.Deferred()
     155        self.protocol.availableReceived = availableReceived
     156        self.protocol.xmlstream.dispatch(parseXml(xml))
     157        return d
     158
     159
     160    def test_unavailableReceived(self):
     161        """
     162        Incoming presence stanzas are parsed and dispatched.
     163        """
     164        xml = """<presence type='unavailable'/>"""
     165
     166        def unavailableReceived(presence):
     167            xmppim.PresenceProtocol.unavailableReceived(self.protocol, presence)
     168            try:
     169                self.assertIsInstance(presence, xmppim.AvailabilityPresence)
     170            except:
     171                d.errback()
     172            else:
     173                d.callback(None)
     174
     175        d = defer.Deferred()
     176        self.protocol.unavailableReceived = unavailableReceived
     177        self.protocol.xmlstream.dispatch(parseXml(xml))
     178        return d
     179
     180
     181    def test_subscribeReceived(self):
     182        """
     183        Incoming presence stanzas are parsed and dispatched.
     184        """
     185        xml = """<presence type='subscribe'/>"""
     186
     187        def subscribeReceived(presence):
     188            xmppim.PresenceProtocol.subscribeReceived(self.protocol, presence)
     189            try:
     190                self.assertIsInstance(presence, xmppim.SubscriptionPresence)
     191            except:
     192                d.errback()
     193            else:
     194                d.callback(None)
     195
     196        d = defer.Deferred()
     197        self.protocol.subscribeReceived = subscribeReceived
     198        self.protocol.xmlstream.dispatch(parseXml(xml))
     199        return d
     200
     201
     202    def test_unsubscribeReceived(self):
     203        """
     204        Incoming presence stanzas are parsed and dispatched.
     205        """
     206        xml = """<presence type='unsubscribe'/>"""
     207
     208        def unsubscribeReceived(presence):
     209            xmppim.PresenceProtocol.unsubscribeReceived(self.protocol, presence)
     210            try:
     211                self.assertIsInstance(presence, xmppim.SubscriptionPresence)
     212            except:
     213                d.errback()
     214            else:
     215                d.callback(None)
     216
     217        d = defer.Deferred()
     218        self.protocol.unsubscribeReceived = unsubscribeReceived
     219        self.protocol.xmlstream.dispatch(parseXml(xml))
     220        return d
     221
     222
     223    def test_subscribedReceived(self):
     224        """
     225        Incoming presence stanzas are parsed and dispatched.
     226        """
     227        xml = """<presence type='subscribed'/>"""
     228
     229        def subscribedReceived(presence):
     230            xmppim.PresenceProtocol.subscribedReceived(self.protocol, presence)
     231            try:
     232                self.assertIsInstance(presence, xmppim.SubscriptionPresence)
     233            except:
     234                d.errback()
     235            else:
     236                d.callback(None)
     237
     238        d = defer.Deferred()
     239        self.protocol.subscribedReceived = subscribedReceived
     240        self.protocol.xmlstream.dispatch(parseXml(xml))
     241        return d
     242
     243
     244    def test_unsubscribedReceived(self):
     245        """
     246        Incoming presence stanzas are parsed and dispatched.
     247        """
     248        xml = """<presence type='unsubscribed'/>"""
     249
     250        def unsubscribedReceived(presence):
     251            xmppim.PresenceProtocol.unsubscribedReceived(self.protocol,
     252                                                         presence)
     253            try:
     254                self.assertIsInstance(presence, xmppim.SubscriptionPresence)
     255            except:
     256                d.errback()
     257            else:
     258                d.callback(None)
     259
     260        d = defer.Deferred()
     261        self.protocol.unsubscribedReceived = unsubscribedReceived
     262        self.protocol.xmlstream.dispatch(parseXml(xml))
     263        return d
     264
     265
     266    def test_probeReceived(self):
     267        """
     268        Incoming presence stanzas are parsed and dispatched.
     269        """
     270        xml = """<presence type='probe'/>"""
     271
     272        def probeReceived(presence):
     273            xmppim.PresenceProtocol.probeReceived(self.protocol, presence)
     274            try:
     275                self.assertIsInstance(presence, xmppim.ProbePresence)
     276            except:
     277                d.errback()
     278            else:
     279                d.callback(None)
     280
     281        d = defer.Deferred()
     282        self.protocol.probeReceived = probeReceived
     283        self.protocol.xmlstream.dispatch(parseXml(xml))
     284        return d
     285
     286    def test_available(self):
     287        """
     288        It should be possible to pass a sender address.
     289        """
     290        self.protocol.available(JID('user@example.com'),
     291                                show=u'chat',
     292                                status=u'Talk to me!',
     293                                priority=50)
     294        element = self.output[-1]
     295        self.assertEquals("user@example.com", element.getAttribute('to'))
     296        self.assertIdentical(None, element.getAttribute('type'))
     297        self.assertEquals(u'chat', unicode(element.show))
     298        self.assertEquals(u'Talk to me!', unicode(element.status))
     299        self.assertEquals(u'50', unicode(element.priority))
     300
     301    def test_availableLanguages(self):
     302        """
     303        It should be possible to pass a sender address.
     304        """
     305        self.protocol.available(JID('user@example.com'),
     306                                show=u'chat',
     307                                statuses={None: u'Talk to me!',
     308                                          'nl': u'Praat met me!'},
     309                                priority=50)
     310        element = self.output[-1]
     311        self.assertEquals("user@example.com", element.getAttribute('to'))
     312        self.assertIdentical(None, element.getAttribute('type'))
     313        self.assertEquals(u'chat', unicode(element.show))
     314
     315        statuses = {}
     316        for status in element.elements():
     317            if status.name == 'status':
     318                lang = status.getAttribute((NS_XML, 'lang'))
     319                statuses[lang] = unicode(status)
     320
     321        self.assertIn(None, statuses)
     322        self.assertEquals(u'Talk to me!', statuses[None])
     323        self.assertIn('nl', statuses)
     324        self.assertEquals(u'Praat met me!', statuses['nl'])
     325        self.assertEquals(u'50', unicode(element.priority))
     326
     327
     328    def test_availableSender(self):
     329        """
     330        It should be possible to pass a sender address.
     331        """
     332        self.protocol.available(JID('user@example.com'),
     333                                sender=JID('user@example.org'))
     334        element = self.output[-1]
     335        self.assertEquals("user@example.org", element.getAttribute('from'))
     336
     337
     338    def test_unavailableDirected(self):
     339        """
     340        Test sending of directed unavailable presence broadcast.
     341        """
     342
     343        self.protocol.unavailable(JID('user@example.com'))
     344        element = self.output[-1]
     345        self.assertEquals("presence", element.name)
     346        self.assertEquals(None, element.uri)
     347        self.assertEquals("user@example.com", element.getAttribute('to'))
     348        self.assertEquals("unavailable", element.getAttribute('type'))
     349
     350    def test_unavailableWithStatus(self):
     351        """
     352        Test sending of directed unavailable presence broadcast with status.
     353        """
     354
     355        self.protocol.unavailable(JID('user@example.com'),
     356                                  {None: 'Disconnected'})
     357        element = self.output[-1]
     358        self.assertEquals("presence", element.name)
     359        self.assertEquals(None, element.uri)
     360        self.assertEquals("user@example.com", element.getAttribute('to'))
     361        self.assertEquals("unavailable", element.getAttribute('type'))
     362        self.assertEquals("Disconnected", unicode(element.status))
     363
     364
     365    def test_unavailableBroadcast(self):
     366        """
     367        Test sending of unavailable presence broadcast.
     368        """
     369
     370        self.protocol.unavailable(None)
     371        element = self.output[-1]
     372        self.assertEquals("presence", element.name)
     373        self.assertEquals(None, element.uri)
     374        self.assertEquals(None, element.getAttribute('to'))
     375        self.assertEquals("unavailable", element.getAttribute('type'))
     376
     377
     378    def test_unavailableBroadcastNoRecipientParameter(self):
     379        """
     380        Test sending of unavailable presence broadcast by not passing entity.
     381        """
     382
     383        self.protocol.unavailable()
     384        element = self.output[-1]
     385        self.assertEquals("presence", element.name)
     386        self.assertEquals(None, element.uri)
     387        self.assertEquals(None, element.getAttribute('to'))
     388        self.assertEquals("unavailable", element.getAttribute('type'))
     389
     390
     391    def test_unavailableSender(self):
     392        """
     393        It should be possible to pass a sender address.
     394        """
     395        self.protocol.unavailable(JID('user@example.com'),
     396                                  sender=JID('user@example.org'))
     397        element = self.output[-1]
     398        self.assertEquals("user@example.org", element.getAttribute('from'))
     399
     400
     401    def test_subscribeSender(self):
     402        """
     403        It should be possible to pass a sender address.
     404        """
     405        self.protocol.subscribe(JID('user@example.com'),
     406                                sender=JID('user@example.org'))
     407        element = self.output[-1]
     408        self.assertEquals("user@example.org", element.getAttribute('from'))
     409
     410
     411    def test_unsubscribeSender(self):
     412        """
     413        It should be possible to pass a sender address.
     414        """
     415        self.protocol.unsubscribe(JID('user@example.com'),
     416                                  sender=JID('user@example.org'))
     417        element = self.output[-1]
     418        self.assertEquals("user@example.org", element.getAttribute('from'))
     419
     420
     421    def test_subscribedSender(self):
     422        """
     423        It should be possible to pass a sender address.
     424        """
     425        self.protocol.subscribed(JID('user@example.com'),
     426                                 sender=JID('user@example.org'))
     427        element = self.output[-1]
     428        self.assertEquals("user@example.org", element.getAttribute('from'))
     429
     430
     431    def test_unsubscribedSender(self):
     432        """
     433        It should be possible to pass a sender address.
     434        """
     435        self.protocol.unsubscribed(JID('user@example.com'),
     436                                   sender=JID('user@example.org'))
     437        element = self.output[-1]
     438        self.assertEquals("user@example.org", element.getAttribute('from'))
     439
     440
     441    def test_probeSender(self):
     442        """
     443        It should be possible to pass a sender address.
     444        """
     445        self.protocol.probe(JID('user@example.com'),
     446                            sender=JID('user@example.org'))
     447        element = self.output[-1]
     448        self.assertEquals("user@example.org", element.getAttribute('from'))
     449
     450
     451
    78452class RosterClientProtocolTest(unittest.TestCase):
    79453    """
  • wokkel/xmppim.py

    r63 r68  
    11# -*- test-case-name: wokkel.test.test_xmppim -*-
    22#
    3 # Copyright (c) 2003-2008 Ralph Meijer
     3# Copyright (c) 2003-2009 Ralph Meijer
    44# See LICENSE for details.
    55
     
    1717
    1818from wokkel.compat import IQ
     19from wokkel.generic import ErrorStanza, Stanza
    1920from wokkel.subprotocols import XMPPHandler
    2021
     
    114115    def _onPresenceUnsubscribe(self, presence):
    115116        self.unsubscribeReceived(JID(presence["from"]))
     117
    116118
    117119    def availableReceived(self, entity, show=None, statuses=None, priority=0):
     
    246248        """
    247249        self.send(Presence(to=entity, type='unsubscribed'))
     250
     251
     252
     253class BasePresence(Stanza):
     254    """
     255    Stanza of kind presence.
     256    """
     257    stanzaKind = 'presence'
     258
     259
     260
     261class AvailabilityPresence(BasePresence):
     262    """
     263    Presence.
     264
     265    This represents availability presence (as opposed to
     266    L{SubscriptionPresence}).
     267
     268    @ivar available: The availability being communicated.
     269    @type available: C{bool}
     270    @ivar show: More specific availability. Can be one of C{'chat'}, C{'away'},
     271                C{'xa'}, C{'dnd'} or C{None}.
     272    @type show: C{str} or C{NoneType}
     273    @ivar statuses: Natural language texts to detail the (un)availability.
     274                    These are represented as a mapping from language code
     275                    (C{str} or C{None}) to the corresponding text (C{unicode}).
     276                    If the key is C{None}, the associated text is in the
     277                    default language.
     278    @type statuses: C{dict}
     279    @ivar priority: Priority level for this resource. Must be between -128 and
     280                    127. Defaults to 0.
     281    @type priority: C{int}
     282    """
     283
     284    childParsers = {(None, 'show'): '_childParser_show',
     285                     (None, 'status'): '_childParser_status',
     286                     (None, 'priority'): '_childParser_priority'}
     287
     288    def __init__(self, recipient=None, sender=None, available=True,
     289                       show=None, status=None, statuses=None, priority=0):
     290        BasePresence.__init__(self, recipient=recipient, sender=sender)
     291        self.available = available
     292        self.show = show
     293        self.statuses = statuses or {}
     294        if status:
     295            self.statuses[None] = status
     296        self.priority = priority
     297
     298
     299    def _childParser_show(self, element):
     300        show = unicode(element)
     301        if show in ('chat', 'away', 'xa', 'dnd'):
     302            self.show = show
     303
     304
     305    def _childParser_status(self, element):
     306        lang = element.getAttribute((NS_XML, 'lang'), None)
     307        text = unicode(element)
     308        self.statuses[lang] = text
     309
     310
     311    def _childParser_priority(self, element):
     312        try:
     313            self.priority = int(unicode(element))
     314        except ValueError:
     315            pass
     316
     317
     318    def parseElement(self, element):
     319        BasePresence.parseElement(self, element)
     320
     321        if self.stanzaType == 'unavailable':
     322            self.available = False
     323
     324
     325    def toElement(self):
     326        if not self.available:
     327            self.stanzaType = 'unavailable'
     328
     329        presence = BasePresence.toElement(self)
     330
     331        if self.available:
     332            if self.show in ('chat', 'away', 'xa', 'dnd'):
     333                presence.addElement('show', content=self.show)
     334            if self.priority != 0:
     335                presence.addElement('priority', content=unicode(self.priority))
     336
     337        for lang, text in self.statuses.iteritems():
     338            status = presence.addElement('status', content=text)
     339            if lang:
     340                status[(NS_XML, 'lang')] = lang
     341
     342        return presence
     343
     344
     345
     346class SubscriptionPresence(BasePresence):
     347    """
     348    Presence subscription request or response.
     349
     350    This kind of presence is used to represent requests for presence
     351    subscription and their replies.
     352
     353    Based on L{BasePresence} and {Stanza}, it just uses the L{stanzaType}
     354    attribute to represent the type of subscription presence. This can be
     355    one of C{'subscribe'}, C{'unsubscribe'}, C{'subscribed'} and
     356    C{'unsubscribed'}.
     357    """
     358
     359
     360
     361class ProbePresence(BasePresence):
     362    """
     363    Presence probe request.
     364    """
     365
     366    stanzaType = 'probe'
     367
     368
     369
     370class PresenceProtocol(XMPPHandler):
     371    """
     372    XMPP Presence protocol.
     373
     374    @cvar presenceTypeParserMap: Maps presence stanza types to their respective
     375        stanza parser classes (derived from L{Stanza}).
     376    @type presenceTypeParserMap: C{dict}
     377    """
     378
     379    presenceTypeParserMap = {
     380                'error': ErrorStanza,
     381                'available': AvailabilityPresence,
     382                'unavailable': AvailabilityPresence,
     383                'subscribe': SubscriptionPresence,
     384                'unsubscribe': SubscriptionPresence,
     385                'subscribed': SubscriptionPresence,
     386                'unsubscribed': SubscriptionPresence,
     387                'probe': ProbePresence,
     388        }
     389
     390    def connectionInitialized(self):
     391        self.xmlstream.addObserver("/presence", self._onPresence)
     392
     393
     394    def _onPresence(self, element):
     395        stanza = Stanza.fromElement(element)
     396
     397        presenceType = stanza.stanzaType or 'available'
     398
     399        try:
     400            parser = self.presenceTypeParserMap[presenceType]
     401        except KeyError:
     402            return
     403
     404        presence = parser.fromElement(element)
     405
     406        try:
     407            handler = getattr(self, '%sReceived' % presenceType)
     408        except AttributeError:
     409            return
     410        else:
     411            handler(presence)
     412
     413
     414    def errorReceived(self, presence):
     415        """
     416        Error presence was received.
     417        """
     418        pass
     419
     420
     421    def availableReceived(self, presence):
     422        """
     423        Available presence was received.
     424        """
     425        pass
     426
     427
     428    def unavailableReceived(self, presence):
     429        """
     430        Unavailable presence was received.
     431        """
     432        pass
     433
     434
     435    def subscribedReceived(self, presence):
     436        """
     437        Subscription approval confirmation was received.
     438        """
     439        pass
     440
     441
     442    def unsubscribedReceived(self, presence):
     443        """
     444        Unsubscription confirmation was received.
     445        """
     446        pass
     447
     448
     449    def subscribeReceived(self, presence):
     450        """
     451        Subscription request was received.
     452        """
     453        pass
     454
     455
     456    def unsubscribeReceived(self, presence):
     457        """
     458        Unsubscription request was received.
     459        """
     460        pass
     461
     462
     463    def probeReceived(self, presence):
     464        """
     465        Probe presence was received.
     466        """
     467        pass
     468
     469
     470    def available(self, recipient=None, show=None, statuses=None, priority=0,
     471                        status=None, sender=None):
     472        """
     473        Send available presence.
     474
     475        @param recipient: Optional Recipient to which the presence should be
     476            sent.
     477        @type recipient: {JID}
     478
     479        @param show: Optional detailed presence information. One of C{'away'},
     480            C{'xa'}, C{'chat'}, C{'dnd'}.
     481        @type show: C{str}
     482
     483        @param statuses: Mapping of natural language descriptions of the
     484           availability status, keyed by the language descriptor. A status
     485           without a language specified, is keyed with C{None}.
     486        @type statuses: C{dict}
     487
     488        @param priority: priority level of the resource.
     489        @type priority: C{int}
     490        """
     491        presence = AvailabilityPresence(recipient=recipient, sender=sender,
     492                                        show=show, statuses=statuses,
     493                                        status=status, priority=priority)
     494        self.send(presence.toElement())
     495
     496
     497    def unavailable(self, recipient=None, statuses=None, sender=None):
     498        """
     499        Send unavailable presence.
     500
     501        @param recipient: Optional entity to which the presence should be sent.
     502        @type recipient: {JID}
     503
     504        @param statuses: dictionary of natural language descriptions of the
     505            availability status, keyed by the language descriptor. A status
     506            without a language specified, is keyed with C{None}.
     507        @type statuses: C{dict}
     508        """
     509        presence = AvailabilityPresence(recipient=recipient, sender=sender,
     510                                        available=False, statuses=statuses)
     511        self.send(presence.toElement())
     512
     513
     514    def subscribe(self, recipient, sender=None):
     515        """
     516        Send subscription request
     517
     518        @param recipient: Entity to subscribe to.
     519        @type recipient: {JID}
     520        """
     521        presence = SubscriptionPresence(recipient=recipient, sender=sender)
     522        presence.stanzaType = 'subscribe'
     523        self.send(presence.toElement())
     524
     525
     526    def unsubscribe(self, recipient, sender=None):
     527        """
     528        Send unsubscription request
     529
     530        @param recipient: Entity to unsubscribe from.
     531        @type recipient: {JID}
     532        """
     533        presence = SubscriptionPresence(recipient=recipient, sender=sender)
     534        presence.stanzaType = 'unsubscribe'
     535        self.send(presence.toElement())
     536
     537
     538    def subscribed(self, recipient, sender=None):
     539        """
     540        Send subscription confirmation.
     541
     542        @param recipient: Entity that subscribed.
     543        @type recipient: {JID}
     544        """
     545        presence = SubscriptionPresence(recipient=recipient, sender=sender)
     546        presence.stanzaType = 'subscribed'
     547        self.send(presence.toElement())
     548
     549
     550    def unsubscribed(self, recipient, sender=None):
     551        """
     552        Send unsubscription confirmation.
     553
     554        @param recipient: Entity that unsubscribed.
     555        @type recipient: {JID}
     556        """
     557        presence = SubscriptionPresence(recipient=recipient, sender=sender)
     558        presence.stanzaType = 'unsubscribed'
     559        self.send(presence.toElement())
     560
     561
     562    def probe(self, recipient, sender=None):
     563        """
     564        Send presence probe.
     565
     566        @param recipient: Entity to be probed.
     567        @type recipient: {JID}
     568        """
     569        presence = ProbePresence(recipient=recipient, sender=sender)
     570        self.send(presence.toElement())
     571
    248572
    249573
Note: See TracChangeset for help on using the changeset viewer.