def info(self, requestor, target, nodeIdentifier): """ Gather data for a disco info request. @param requestor: The entity that sent the request. @type requestor: L{JID<twisted.words.protocols.jabber.jid.JID>} @param target: The entity the request was sent to. @type target: L{JID<twisted.words.protocols.jabber.jid.JID>} @param nodeIdentifier: The optional node being queried, or C{''}. @type nodeIdentifier: C{unicode} @return: Deferred with the gathered results from sibling handlers. @rtype: L{defer.Deferred} """ xmpp_manager = self.parent.manager if target.host not in xmpp_manager.domains | xmpp_manager.muc_domains: return defer.fail(StanzaError('service-unavailable')) elements = [disco.DiscoFeature(disco.NS_DISCO_INFO), disco.DiscoFeature(disco.NS_DISCO_ITEMS), disco.DiscoFeature('http://sylkserver.com')] if target.host in xmpp_manager.muc_domains: elements.append(disco.DiscoIdentity('conference', 'text', 'SylkServer Chat Service')) elements.append(disco.DiscoFeature('http://jabber.org/protocol/muc')) elements.append(disco.DiscoFeature('urn:ietf:rfc:3264')) elements.append(disco.DiscoFeature('urn:xmpp:coin')) elements.append(disco.DiscoFeature(jingle.NS_JINGLE)) elements.append(disco.DiscoFeature(jingle.NS_JINGLE_APPS_RTP)) elements.append(disco.DiscoFeature(jingle.NS_JINGLE_APPS_RTP_AUDIO)) #elements.append(disco.DiscoFeature(jingle.NS_JINGLE_APPS_RTP_VIDEO)) elements.append(disco.DiscoFeature(jingle.NS_JINGLE_ICE_UDP_TRANSPORT)) elements.append(disco.DiscoFeature(jingle.NS_JINGLE_RAW_UDP_TRANSPORT)) if target.user: # We can't say much more here, because the actual conference may end up on a different server elements.append(disco.DiscoFeature('muc_temporary')) elements.append(disco.DiscoFeature('muc_unmoderated')) else: elements.append(disco.DiscoFeature(ping.NS_PING)) if not target.user: elements.append(disco.DiscoIdentity('gateway', 'simple', 'SylkServer')) elements.append(disco.DiscoIdentity('server', 'im', 'SylkServer')) else: elements.append(disco.DiscoIdentity('client', 'pc')) elements.append(disco.DiscoFeature('http://jabber.org/protocol/caps')) elements.append(disco.DiscoFeature('http://jabber.org/protocol/chatstates')) elements.append(disco.DiscoFeature('urn:ietf:rfc:3264')) elements.append(disco.DiscoFeature('urn:xmpp:coin')) elements.append(disco.DiscoFeature(jingle.NS_JINGLE)) elements.append(disco.DiscoFeature(jingle.NS_JINGLE_APPS_RTP)) elements.append(disco.DiscoFeature(jingle.NS_JINGLE_APPS_RTP_AUDIO)) #elements.append(disco.DiscoFeature(jingle.NS_JINGLE_APPS_RTP_VIDEO)) elements.append(disco.DiscoFeature(jingle.NS_JINGLE_ICE_UDP_TRANSPORT)) elements.append(disco.DiscoFeature(jingle.NS_JINGLE_RAW_UDP_TRANSPORT)) return defer.succeed(elements)
def test_discoServerSupport(self): """Disco support from client to server. """ test_srv = 'shakespeare.lit' def cb(query): # check namespace self.failUnless(query.uri == disco.NS_INFO, 'Wrong namespace') d = self.protocol.disco(test_srv) d.addCallback(cb) iq = self.stub.output[-1] # send back a response response = toResponse(iq, 'result') response.addElement('query', disco.NS_INFO) # need to add information to response response.query.addChild(disco.DiscoFeature(muc.NS_MUC)) response.query.addChild( disco.DiscoIdentity(category='conference', name='Macbeth Chat Service', type='text')) self.stub.send(response) return d
def test_infoNotDeferred(self): """ C{info} should gather disco info from sibling handlers. """ discoItems = [ disco.DiscoIdentity('dummy', 'generic', 'Generic Dummy Entity'), disco.DiscoFeature('jabber:iq:version') ] class DiscoResponder(XMPPHandler): implements(disco.IDisco) def getDiscoInfo(self, requestor, target, nodeIdentifier): if not nodeIdentifier: return discoItems else: return [] def cb(result): self.assertEquals(discoItems, result) self.service.parent = [self.service, DiscoResponder()] d = self.service.info(JID('*****@*****.**'), JID('example.com'), '') d.addCallback(cb) return d
def test_init(self): """ Test initialization with a category, type and name. """ identity = disco.DiscoIdentity(u'conference', u'text', u'The chatroom') self.assertEqual(u'conference', identity.category) self.assertEqual(u'text', identity.type) self.assertEqual(u'The chatroom', identity.name)
def getDiscoInfo(self, requestor, target, nodeIdentifier): if not nodeIdentifier: return defer.succeed([ disco.DiscoIdentity('dummy', 'generic', 'Generic Dummy Entity'), disco.DiscoFeature('jabber:iq:version') ]) else: return defer.succeed([])
def info(requestor, target, nodeIdentifier): self.assertEqual(JID('*****@*****.**'), requestor) return defer.succeed([ disco.DiscoIdentity('dummy', 'generic', 'Generic Dummy Entity'), disco.DiscoFeature('jabber:iq:version') ])
def getDiscoInfo(self, requestor, target, node): info = set() if node: info.add(disco.DiscoIdentity('automation', 'command-node')) info.add(disco.DiscoFeature('http://jabber.org/protocol/commands')) else: info.add(disco.DiscoFeature(NS_CMD)) return defer.succeed(info)
def getDiscoInfo(self, requestor, target, nodeIdentifier=''): def toInfo(nodeInfo): if not nodeInfo: return (nodeType, metaData) = nodeInfo['type'], nodeInfo['meta-data'] info.append(disco.DiscoIdentity('pubsub', nodeType)) if metaData: form = data_form.Form(formType="result", formNamespace=NS_PUBSUB_META_DATA) form.addField( data_form.Field( var='pubsub#node_type', value=nodeType, label='The type of node (collection or leaf)')) for metaDatum in metaData: form.addField(data_form.Field.fromDict(metaDatum)) info.append(form) return info = [] request = PubSubRequest('discoInfo') if self.resource is not None: resource = self.resource.locateResource(request) identity = resource.discoIdentity features = resource.features getInfo = resource.getInfo else: category = self.discoIdentity['category'] idType = self.discoIdentity['type'] name = self.discoIdentity['name'] identity = disco.DiscoIdentity(category, idType, name) features = self.pubSubFeatures getInfo = self.getNodeInfo if not nodeIdentifier: info.append(identity) info.append(disco.DiscoFeature(disco.NS_DISCO_ITEMS)) info.extend([ disco.DiscoFeature("%s#%s" % (NS_PUBSUB, feature)) for feature in features ]) d = defer.maybeDeferred(getInfo, requestor, target, nodeIdentifier or '') d.addCallback(toInfo) d.addErrback(log.err) d.addCallback(lambda _: info) return d
def test_toElementWithoutName(self): """ Test proper rendering to a DOM representation without a name. The returned element should be properly named and have C{conference}, C{type} attributes, no C{name} attribute. """ identity = disco.DiscoIdentity(u'conference', u'text') element = identity.toElement() self.assertEqual(NS_DISCO_INFO, element.uri) self.assertEqual(u'identity', element.name) self.assertEqual(u'conference', element.getAttribute(u'category')) self.assertEqual(u'text', element.getAttribute(u'type')) self.assertFalse(element.hasAttribute(u'name'))
def test_toElement(self): """ Test proper rendering to a DOM representation. The returned element should be properly named and have C{conference}, C{type}, and C{name} attributes. """ identity = disco.DiscoIdentity(u'conference', u'text', u'The chatroom') element = identity.toElement() self.assertEqual(NS_DISCO_INFO, element.uri) self.assertEqual(u'identity', element.name) self.assertEqual(u'conference', element.getAttribute(u'category')) self.assertEqual(u'text', element.getAttribute(u'type')) self.assertEqual(u'The chatroom', element.getAttribute(u'name'))
def getDiscoInfo(self, requestor, target, nodeIdentifier): if not nodeIdentifier: return defer.succeed([ disco.DiscoIdentity('sms', 'generic', 'SMS77.de Gateway'), disco.DiscoFeature('jabber:iq:version'), disco.DiscoFeature('jabber:iq:register'), #disco.DiscoFeature('jabber:iq:search'), disco.DiscoFeature('jabber:iq:gateway'), disco.DiscoFeature('vcard-temp'), disco.DiscoFeature('http://jabber.org/protocol/disco#info'), disco.DiscoFeature('http://jabber.org/protocol/disco#items') ]) else: return defer.succeed([])
def getDiscoInfo(self, req, target, ni): group, nick = resolveGroup(target) if group in self.groupset and not ni: gr = self.groupset.get(group) if nick: if XMPPChannel.isMember(req.full(), gr): # TODO raise error.StanzaError('service-unavailable') else: raise error.StanzaError('bad-request') else: di = [disco.DiscoIdentity(u'conference', u'text', name=gr.name)] for f in gr.getDiscoFeatures(): di.append(f) return di else: # TODO raise error.StanzaError('not-implemented')
def toInfo(nodeInfo): if not nodeInfo: return (nodeType, metaData) = nodeInfo['type'], nodeInfo['meta-data'] info.append(disco.DiscoIdentity('pubsub', nodeType)) if metaData: form = data_form.Form(formType="result", formNamespace=NS_PUBSUB_META_DATA) form.addField( data_form.Field( var='pubsub#node_type', value=nodeType, label='The type of node (collection or leaf)' ) ) for metaDatum in metaData: form.addField(data_form.Field.fromDict(metaDatum)) info.append(form.toElement())
def test_toElementChildren(self): """ Test C{toElement} creates a DOM with proper childs. """ info = disco.DiscoInfo() info.append(disco.DiscoFeature(u'jabber:iq:register')) info.append(disco.DiscoIdentity(u'conference', u'text')) info.append(data_form.Form(u'result')) element = info.toElement() featureElements = domish.generateElementsQNamed( element.children, u'feature', NS_DISCO_INFO) self.assertEqual(1, len(list(featureElements))) identityElements = domish.generateElementsQNamed( element.children, u'identity', NS_DISCO_INFO) self.assertEqual(1, len(list(identityElements))) extensionElements = domish.generateElementsQNamed( element.children, u'x', data_form.NS_X_DATA) self.assertEqual(1, len(list(extensionElements)))
def getDiscoInfo(self, requestor, target, nodeIdentifier): info = [] if not nodeIdentifier: info.append(disco.DiscoIdentity(**self.discoIdentity)) info.append(disco.DiscoFeature(disco.NS_ITEMS)) info.extend([disco.DiscoFeature("%s#%s" % (NS_PUBSUB, feature)) for feature in self.pubSubFeatures]) def toInfo(nodeInfo): if not nodeInfo: return (nodeType, metaData) = nodeInfo['type'], nodeInfo['meta-data'] info.append(disco.DiscoIdentity('pubsub', nodeType)) if metaData: form = data_form.Form(formType="result", formNamespace=NS_PUBSUB_META_DATA) form.addField( data_form.Field( var='pubsub#node_type', value=nodeType, label='The type of node (collection or leaf)' ) ) for metaDatum in metaData: form.addField(data_form.Field.fromDict(metaDatum)) info.append(form.toElement()) d = self.getNodeInfo(requestor, target, nodeIdentifier or '') d.addCallback(toInfo) d.addBoth(lambda result: info) return d
class PubSubResource(object): implements(IPubSubResource) features = [] discoIdentity = disco.DiscoIdentity('pubsub', 'service', 'Publish-Subscribe Service') def locateResource(self, request): return self def getInfo(self, requestor, service, nodeIdentifier): return defer.succeed(None) def getNodes(self, requestor, service, nodeIdentifier): return defer.succeed([]) def getConfigurationOptions(self): return {} def publish(self, request): return defer.fail(Unsupported('publish')) def subscribe(self, request): return defer.fail(Unsupported('subscribe')) def unsubscribe(self, request): return defer.fail(Unsupported('subscribe')) def subscriptions(self, request): return defer.fail(Unsupported('retrieve-subscriptions')) def affiliations(self, request): return defer.fail(Unsupported('retrieve-affiliations')) def create(self, request): return defer.fail(Unsupported('create-nodes')) def default(self, request): return defer.fail(Unsupported('retrieve-default')) def configureGet(self, request): return defer.fail(Unsupported('config-node')) def configureSet(self, request): return defer.fail(Unsupported('config-node')) def items(self, request): return defer.fail(Unsupported('retrieve-items')) def retract(self, request): return defer.fail(Unsupported('retract-items')) def purge(self, request): return defer.fail(Unsupported('purge-nodes')) def delete(self, request): return defer.fail(Unsupported('delete-nodes')) def affiliationsGet(self, request): return defer.fail(Unsupported('modify-affiliations')) def affiliationsSet(self, request): return defer.fail(Unsupported('modify-affiliations')) def subscriptionsGet(self, request): return defer.fail(Unsupported('manage-subscriptions')) def subscriptionsSet(self, request): return defer.fail(Unsupported('manage-subscriptions'))
class PubSubResourceFromBackend(PubSubResource): """ Adapts a backend to an xmpp publish-subscribe service. """ features = [ "config-node", "create-nodes", "delete-any", "delete-nodes", "item-ids", "meta-data", "publish", "purge-nodes", "retract-items", "retrieve-affiliations", "retrieve-default", "retrieve-items", "retrieve-subscriptions", "subscribe", ] discoIdentity = disco.DiscoIdentity('pubsub', 'service', 'Idavoll publish-subscribe service') pubsubService = None _errorMap = { error.NodeNotFound: ('item-not-found', None, None), error.NodeExists: ('conflict', None, None), error.Forbidden: ('forbidden', None, None), error.ItemForbidden: ('bad-request', 'item-forbidden', None), error.ItemRequired: ('bad-request', 'item-required', None), error.NoInstantNodes: ('not-acceptable', 'unsupported', 'instant-nodes'), error.NotSubscribed: ('unexpected-request', 'not-subscribed', None), error.InvalidConfigurationOption: ('not-acceptable', None, None), error.InvalidConfigurationValue: ('not-acceptable', None, None), error.NodeNotPersistent: ('feature-not-implemented', 'unsupported', 'persistent-node'), error.NoRootNode: ('bad-request', None, None), error.NoCollections: ('feature-not-implemented', 'unsupported', 'collections'), error.NoPublishing: ('feature-not-implemented', 'unsupported', 'publish'), } def __init__(self, backend): PubSubResource.__init__(self) self.backend = backend self.hideNodes = False self.backend.registerNotifier(self._notify) self.backend.registerPreDelete(self._preDelete) if self.backend.supportsInstantNodes(): self.features.append("instant-nodes") if self.backend.supportsOutcastAffiliation(): self.features.append("outcast-affiliation") if self.backend.supportsPersistentItems(): self.features.append("persistent-items") if self.backend.supportsPublisherAffiliation(): self.features.append("publisher-affiliation") def _notify(self, data): items = data['items'] nodeIdentifier = data['nodeIdentifier'] if 'subscription' not in data: d = self.backend.getNotifications(nodeIdentifier, items) else: subscription = data['subscription'] d = defer.succeed([(subscription.subscriber, [subscription], items)]) d.addCallback(lambda notifications: self.pubsubService.notifyPublish( self.serviceJID, nodeIdentifier, notifications)) def _preDelete(self, data): nodeIdentifier = data['nodeIdentifier'] redirectURI = data.get('redirectURI', None) d = self.backend.getSubscribers(nodeIdentifier) d.addCallback(lambda subscribers: self.pubsubService.notifyDelete( self.serviceJID, nodeIdentifier, subscribers, redirectURI)) return d def _mapErrors(self, failure): e = failure.trap(*self._errorMap.keys()) condition, pubsubCondition, feature = self._errorMap[e] msg = failure.value.msg if pubsubCondition: exc = PubSubError(condition, pubsubCondition, feature, msg) else: exc = StanzaError(condition, text=msg) raise exc def getInfo(self, requestor, service, nodeIdentifier): info = {} def saveType(result): info['type'] = result return nodeIdentifier def saveMetaData(result): info['meta-data'] = result return info def trapNotFound(failure): failure.trap(error.NodeNotFound) return info d = defer.succeed(nodeIdentifier) d.addCallback(self.backend.getNodeType) d.addCallback(saveType) d.addCallback(self.backend.getNodeMetaData) d.addCallback(saveMetaData) d.addErrback(trapNotFound) d.addErrback(self._mapErrors) return d def getNodes(self, requestor, service, nodeIdentifier): if service.resource: return defer.succeed([]) d = self.backend.getNodes() return d.addErrback(self._mapErrors) def getConfigurationOptions(self): return self.backend.nodeOptions def publish(self, request): d = self.backend.publish(request.nodeIdentifier, request.items, request.sender) return d.addErrback(self._mapErrors) def subscribe(self, request): d = self.backend.subscribe(request.nodeIdentifier, request.subscriber, request.sender) return d.addErrback(self._mapErrors) def unsubscribe(self, request): d = self.backend.unsubscribe(request.nodeIdentifier, request.subscriber, request.sender) return d.addErrback(self._mapErrors) def subscriptions(self, request): d = self.backend.getSubscriptions(request.sender) return d.addErrback(self._mapErrors) def affiliations(self, request): d = self.backend.getAffiliations(request.sender) return d.addErrback(self._mapErrors) def create(self, request): d = self.backend.createNode(request.nodeIdentifier, request.sender) return d.addErrback(self._mapErrors) def default(self, request): d = self.backend.getDefaultConfiguration(request.nodeType) return d.addErrback(self._mapErrors) def configureGet(self, request): d = self.backend.getNodeConfiguration(request.nodeIdentifier) return d.addErrback(self._mapErrors) def configureSet(self, request): d = self.backend.setNodeConfiguration(request.nodeIdentifier, request.options, request.sender) return d.addErrback(self._mapErrors) def items(self, request): d = self.backend.getItems(request.nodeIdentifier, request.sender, request.maxItems, request.itemIdentifiers) return d.addErrback(self._mapErrors) def retract(self, request): d = self.backend.retractItem(request.nodeIdentifier, request.itemIdentifiers, request.sender) return d.addErrback(self._mapErrors) def purge(self, request): d = self.backend.purgeNode(request.nodeIdentifier, request.sender) return d.addErrback(self._mapErrors) def delete(self, request): d = self.backend.deleteNode(request.nodeIdentifier, request.sender) return d.addErrback(self._mapErrors)