xmpp.chapril.org-conversejs/src/headless/converse-disco.js

791 lines
37 KiB
JavaScript
Raw Normal View History

// Converse.js
// https://conversejs.org
//
2019-02-18 19:17:06 +01:00
// Copyright (c) 2013-2019, the Converse developers
// Licensed under the Mozilla Public License (MPLv2)
/**
* @module converse-disco
* @description
* Converse plugin which add support for XEP-0030: Service Discovery
*/
2018-10-23 03:41:38 +02:00
import converse from "./converse-core";
import log from "./log";
2018-10-23 03:41:38 +02:00
import sizzle from "sizzle";
const { Backbone, Strophe, $iq, utils, _ } = converse.env;
2018-10-23 03:41:38 +02:00
converse.plugins.add('converse-disco', {
2018-10-23 03:41:38 +02:00
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
const { _converse } = this;
2018-10-23 03:41:38 +02:00
// Promises exposed by this plugin
_converse.api.promises.add('discoInitialized');
_converse.api.promises.add('streamFeaturesAdded');
/**
* @class
* @namespace _converse.DiscoEntity
* @memberOf _converse
*/
2018-10-23 03:41:38 +02:00
_converse.DiscoEntity = Backbone.Model.extend({
/* A Disco Entity is a JID addressable entity that can be queried
* for features.
*
* See XEP-0030: https://xmpp.org/extensions/xep-0030.html
*/
idAttribute: 'jid',
initialize (attrs, options) {
2018-10-23 03:41:38 +02:00
this.waitUntilFeaturesDiscovered = utils.getResolveablePromise();
this.dataforms = new _converse.Collection();
let id = `converse.dataforms-${this.get('jid')}`;
this.dataforms.browserStorage = _converse.createStore(id, 'session');
this.features = new _converse.Collection();
id = `converse.features-${this.get('jid')}`;
this.features.browserStorage = _converse.createStore(id, 'session');
this.listenTo(this.features, 'add', this.onFeatureAdded)
this.fields = new _converse.Collection();
id = `converse.fields-${this.get('jid')}`;
this.fields.browserStorage = _converse.createStore(id, 'session');
this.listenTo(this.fields, 'add', this.onFieldAdded)
this.identities = new _converse.Collection();
id = `converse.identities-${this.get('jid')}`;
this.identities.browserStorage = _converse.createStore(id, 'session');
this.fetchFeatures(options);
2018-10-23 03:41:38 +02:00
this.items = new _converse.DiscoEntities();
id = `converse.disco-items-${this.get('jid')}`;
this.items.browserStorage = _converse.createStore(id, 'session');
2018-10-23 03:41:38 +02:00
this.items.fetch();
},
/**
* Returns a Promise which resolves with a map indicating
* whether a given identity is provided by this entity.
* @private
* @method _converse.DiscoEntity#getIdentity
* @param { String } category - The identity category
* @param { String } type - The identity type
*/
async getIdentity (category, type) {
await this.waitUntilFeaturesDiscovered;
return this.identities.findWhere({
'category': category,
'type': type
2018-10-23 03:41:38 +02:00
});
},
/**
* Returns a Promise which resolves with a map indicating
* whether a given feature is supported.
* @private
* @method _converse.DiscoEntity#hasFeature
* @param { String } feature - The feature that might be supported.
*/
async hasFeature (feature) {
await this.waitUntilFeaturesDiscovered
if (this.features.findWhere({'var': feature})) {
return this;
}
2018-10-23 03:41:38 +02:00
},
onFeatureAdded (feature) {
feature.entity = this;
2019-03-29 15:47:23 +01:00
/**
* Triggered when Converse has learned of a service provided by the XMPP server.
* See XEP-0030.
* @event _converse#serviceDiscovered
* @type { Backbone.Model }
* @example _converse.api.listen.on('featuresDiscovered', feature => { ... });
*/
_converse.api.trigger('serviceDiscovered', feature);
2018-10-23 03:41:38 +02:00
},
onFieldAdded (field) {
field.entity = this;
2019-03-29 15:47:23 +01:00
/**
* Triggered when Converse has learned of a disco extension field.
* See XEP-0030.
* @event _converse#discoExtensionFieldDiscovered
* @example _converse.api.listen.on('discoExtensionFieldDiscovered', () => { ... });
*/
_converse.api.trigger('discoExtensionFieldDiscovered', field);
2018-10-23 03:41:38 +02:00
},
async fetchFeatures (options) {
if (options.ignore_cache) {
2018-10-23 03:41:38 +02:00
this.queryInfo();
} else {
const store_id = this.features.browserStorage.name;
const result = await this.features.browserStorage.store.getItem(store_id);
if (result && result.length === 0 || result === null) {
this.queryInfo();
} else {
this.features.fetch({
add: true,
success: () => {
this.waitUntilFeaturesDiscovered.resolve(this);
this.trigger('featuresDiscovered');
}
});
this.identities.fetch({add: true});
}
2018-10-23 03:41:38 +02:00
}
},
2018-12-17 14:36:55 +01:00
async queryInfo () {
let stanza;
2018-12-17 14:36:55 +01:00
try {
stanza = await _converse.api.disco.info(this.get('jid'), null);
} catch (iq) {
log.error(iq);
this.waitUntilFeaturesDiscovered.resolve(this);
return;
2018-12-17 14:36:55 +01:00
}
this.onInfo(stanza);
2018-10-23 03:41:38 +02:00
},
2018-10-23 03:41:38 +02:00
onDiscoItems (stanza) {
sizzle(`query[xmlns="${Strophe.NS.DISCO_ITEMS}"] item`, stanza).forEach(item => {
2018-10-23 03:41:38 +02:00
if (item.getAttribute("node")) {
// XXX: Ignore nodes for now.
2018-10-23 03:41:38 +02:00
// See: https://xmpp.org/extensions/xep-0030.html#items-nodes
return;
}
2018-10-23 03:41:38 +02:00
const jid = item.getAttribute('jid');
2019-07-29 10:19:05 +02:00
if (this.items.get(jid) === undefined) {
2018-10-23 03:41:38 +02:00
const entity = _converse.disco_entities.get(jid);
if (entity) {
this.items.add(entity);
} else {
this.items.create({'jid': jid});
}
}
2018-10-23 03:41:38 +02:00
});
},
async queryForItems () {
2018-10-23 03:41:38 +02:00
if (_.isEmpty(this.identities.where({'category': 'server'}))) {
// Don't fetch features and items if this is not a
// server or a conference component.
return;
}
const stanza = await _converse.api.disco.items(this.get('jid'));
this.onDiscoItems(stanza);
2018-10-23 03:41:38 +02:00
},
onInfo (stanza) {
2019-05-23 14:26:20 +02:00
Array.from(stanza.querySelectorAll('identity')).forEach(identity => {
2018-10-23 03:41:38 +02:00
this.identities.create({
'category': identity.getAttribute('category'),
'type': identity.getAttribute('type'),
'name': identity.getAttribute('name')
});
2018-10-23 03:41:38 +02:00
});
sizzle(`x[type="result"][xmlns="${Strophe.NS.XFORM}"]`, stanza).forEach(form => {
2018-10-23 03:41:38 +02:00
const data = {};
sizzle('field', form).forEach(field => {
2018-10-23 03:41:38 +02:00
data[field.getAttribute('var')] = {
'value': _.get(field.querySelector('value'), 'textContent'),
2018-10-23 03:41:38 +02:00
'type': field.getAttribute('type')
};
});
2018-10-23 03:41:38 +02:00
this.dataforms.create(data);
});
2018-10-23 03:41:38 +02:00
if (stanza.querySelector(`feature[var="${Strophe.NS.DISCO_ITEMS}"]`)) {
this.queryForItems();
}
2018-10-23 03:41:38 +02:00
_.forEach(stanza.querySelectorAll('feature'), feature => {
this.features.create({
'var': feature.getAttribute('var'),
'from': stanza.getAttribute('from')
});
});
2018-10-23 03:41:38 +02:00
// XEP-0128 Service Discovery Extensions
sizzle('x[type="result"][xmlns="jabber:x:data"] field', stanza).forEach(field => {
2018-10-23 03:41:38 +02:00
this.fields.create({
'var': field.getAttribute('var'),
'value': _.get(field.querySelector('value'), 'textContent'),
'from': stanza.getAttribute('from')
});
});
2018-10-23 03:41:38 +02:00
this.waitUntilFeaturesDiscovered.resolve(this);
this.trigger('featuresDiscovered');
}
});
_converse.DiscoEntities = _converse.Collection.extend({
2018-10-23 03:41:38 +02:00
model: _converse.DiscoEntity,
fetchEntities () {
return new Promise((resolve, reject) => {
this.fetch({
add: true,
success: resolve,
error () {
reject (new Error("Could not fetch disco entities"));
}
});
2018-10-23 03:41:38 +02:00
});
}
});
function addClientFeatures () {
// See https://xmpp.org/registrar/disco-categories.html
2018-10-23 03:41:38 +02:00
_converse.api.disco.own.identities.add('client', 'web', 'Converse');
_converse.api.disco.own.features.add(Strophe.NS.CHATSTATES);
_converse.api.disco.own.features.add(Strophe.NS.DISCO_INFO);
_converse.api.disco.own.features.add(Strophe.NS.ROSTERX); // Limited support
if (_converse.message_carbons) {
_converse.api.disco.own.features.add(Strophe.NS.CARBONS);
}
2019-03-29 15:47:23 +01:00
/**
* Triggered in converse-disco once the core disco features of
* Converse have been added.
* @event _converse#addClientFeatures
* @example _converse.api.listen.on('addClientFeatures', () => { ... });
*/
_converse.api.trigger('addClientFeatures');
2018-10-23 03:41:38 +02:00
return this;
}
function initStreamFeatures () {
const bare_jid = Strophe.getBareJidFromJid(_converse.jid);
const id = `converse.stream-features-${bare_jid}`;
if (!_converse.stream_features || _converse.stream_features.browserStorage.name !== id) {
_converse.stream_features = new _converse.Collection();
_converse.stream_features.browserStorage = _converse.createStore(id, "session");
_converse.stream_features.fetch({
success (collection) {
if (collection.length === 0 && _converse.connection.features) {
Array.from(_converse.connection.features.childNodes)
.forEach(feature => {
_converse.stream_features.create({
'name': feature.nodeName,
'xmlns': feature.getAttribute('xmlns')
});
2018-10-23 03:41:38 +02:00
});
}
/**
* Triggered as soon as Converse has processed the stream features as advertised by
* the server. If you want to check whether a stream feature is supported before
* proceeding, then you'll first want to wait for this event.
* @event _converse#streamFeaturesAdded
* @example _converse.api.listen.on('streamFeaturesAdded', () => { ... });
*/
_converse.api.trigger('streamFeaturesAdded');
},
error (m, e) {
log.error(e);
2018-10-23 03:41:38 +02:00
}
});
}
2018-10-23 03:41:38 +02:00
}
const plugin = this;
plugin._identities = [];
plugin._features = [];
function onDiscoInfoRequest (stanza) {
const node = stanza.getElementsByTagName('query')[0].getAttribute('node');
const attrs = {xmlns: Strophe.NS.DISCO_INFO};
if (node) { attrs.node = node; }
const iqresult = $iq({'type': 'result', 'id': stanza.getAttribute('id')});
const from = stanza.getAttribute('from');
if (from !== null) {
iqresult.attrs({'to': from});
}
iqresult.c('query', attrs);
plugin._identities.forEach(identity => {
const attrs = {
'category': identity.category,
'type': identity.type
};
if (identity.name) {
attrs.name = identity.name;
}
if (identity.lang) {
attrs['xml:lang'] = identity.lang;
}
iqresult.c('identity', attrs).up();
});
2019-09-16 14:02:14 +02:00
plugin._features.forEach(feature => iqresult.c('feature', {'var': feature}).up());
_converse.api.send(iqresult.tree());
return true;
}
async function initializeDisco () {
2018-10-23 03:41:38 +02:00
addClientFeatures();
_converse.connection.addHandler(onDiscoInfoRequest, Strophe.NS.DISCO_INFO, 'iq', 'get', null, null);
2018-10-23 03:41:38 +02:00
_converse.disco_entities = new _converse.DiscoEntities();
const id = `converse.disco-entities-${_converse.bare_jid}`;
_converse.disco_entities.browserStorage = _converse.createStore(id, 'session');
const collection = await _converse.disco_entities.fetchEntities();
if (collection.length === 0 || !collection.get(_converse.domain)) {
// If we don't have an entity for our own XMPP server,
// create one.
_converse.disco_entities.create({'jid': _converse.domain});
}
2019-03-29 15:47:23 +01:00
/**
* Triggered once the `converse-disco` plugin has been initialized and the
* `_converse.disco_entities` collection will be available and populated with at
* least the service discovery features of the user's own server.
* @event _converse#discoInitialized
* @example _converse.api.listen.on('discoInitialized', () => { ... });
*/
_converse.api.trigger('discoInitialized');
2018-10-23 03:41:38 +02:00
}
/******************** Event Handlers ********************/
// Re-create promise upon reconnection
_converse.api.listen.on('userSessionInitialized', initStreamFeatures);
_converse.api.listen.on('beforeResourceBinding', initStreamFeatures);
2018-10-23 03:41:38 +02:00
_converse.api.listen.on('reconnected', initializeDisco);
_converse.api.listen.on('connected', initializeDisco);
_converse.api.listen.on('beforeTearDown', async () => {
_converse.api.promises.add('streamFeaturesAdded')
if (_converse.stream_features) {
await _converse.stream_features.clearSession();
delete _converse.stream_features;
}
});
_converse.api.listen.on('clearSession', () => {
if (_converse.shouldClearCache() && _converse.disco_entities) {
Array.from(_converse.disco_entities.models).forEach(e => e.features.clearSession());
Array.from(_converse.disco_entities.models).forEach(e => e.identities.clearSession());
Array.from(_converse.disco_entities.models).forEach(e => e.dataforms.clearSession());
Array.from(_converse.disco_entities.models).forEach(e => e.fields.clearSession());
_converse.disco_entities.clearSession();
delete _converse.disco_entities;
}
2018-10-23 03:41:38 +02:00
});
/************************ API ************************/
2019-04-29 09:07:15 +02:00
Object.assign(_converse.api, {
2018-10-23 03:41:38 +02:00
/**
* The XEP-0030 service discovery API
*
* This API lets you discover information about entities on the
* XMPP network.
*
* @namespace _converse.api.disco
* @memberOf _converse.api
*/
disco: {
2018-10-23 03:41:38 +02:00
/**
* @namespace _converse.api.disco.stream
* @memberOf _converse.api.disco
*/
stream: {
2018-10-23 03:41:38 +02:00
/**
* @method _converse.api.disco.stream.getFeature
* @param {String} name The feature name
* @param {String} xmlns The XML namespace
* @example _converse.api.disco.stream.getFeature('ver', 'urn:xmpp:features:rosterver')
*/
async getFeature (name, xmlns) {
await _converse.api.waitUntil('streamFeaturesAdded');
if (!name || !xmlns) {
2018-10-23 03:41:38 +02:00
throw new Error("name and xmlns need to be provided when calling disco.stream.getFeature");
}
if (_converse.stream_features === undefined && !_converse.api.connection.connected()) {
// Happens during tests when disco lookups happen asynchronously after teardown.
const msg = `Tried to get feature ${name} ${xmlns} but _converse.stream_features has been torn down`;
log.warn(msg);
return;
}
2018-10-23 03:41:38 +02:00
return _converse.stream_features.findWhere({'name': name, 'xmlns': xmlns});
}
2018-10-23 03:41:38 +02:00
},
2018-09-02 15:07:14 +02:00
/**
2018-10-23 03:41:38 +02:00
* @namespace _converse.api.disco.own
* @memberOf _converse.api.disco
*/
own: {
2018-09-02 15:07:14 +02:00
/**
2018-10-23 03:41:38 +02:00
* @namespace _converse.api.disco.own.identities
* @memberOf _converse.api.disco.own
2018-09-02 15:07:14 +02:00
*/
identities: {
2018-09-02 15:07:14 +02:00
/**
2018-10-23 03:41:38 +02:00
* Lets you add new identities for this client (i.e. instance of Converse)
* @method _converse.api.disco.own.identities.add
*
* @param {String} category - server, client, gateway, directory, etc.
* @param {String} type - phone, pc, web, etc.
* @param {String} name - "Converse"
* @param {String} lang - en, el, de, etc.
*
* @example _converse.api.disco.own.identities.clear();
2018-09-02 15:07:14 +02:00
*/
2018-10-23 03:41:38 +02:00
add (category, type, name, lang) {
for (var i=0; i<plugin._identities.length; i++) {
if (plugin._identities[i].category == category &&
plugin._identities[i].type == type &&
plugin._identities[i].name == name &&
plugin._identities[i].lang == lang) {
return false;
}
}
2018-10-23 03:41:38 +02:00
plugin._identities.push({category: category, type: type, name: name, lang: lang});
},
/**
* Clears all previously registered identities.
* @method _converse.api.disco.own.identities.clear
* @example _converse.api.disco.own.identities.clear();
*/
clear () {
plugin._identities = []
},
/**
* Returns all of the identities registered for this client
* (i.e. instance of Converse).
* @method _converse.api.disco.identities.get
* @example const identities = _converse.api.disco.own.identities.get();
*/
get () {
return plugin._identities;
}
},
/**
2018-10-23 03:41:38 +02:00
* @namespace _converse.api.disco.own.features
* @memberOf _converse.api.disco.own
*/
features: {
/**
2018-10-23 03:41:38 +02:00
* Lets you register new disco features for this client (i.e. instance of Converse)
* @method _converse.api.disco.own.features.add
* @param {String} name - e.g. http://jabber.org/protocol/caps
* @example _converse.api.disco.own.features.add("http://jabber.org/protocol/caps");
*/
2018-10-23 03:41:38 +02:00
add (name) {
for (var i=0; i<plugin._features.length; i++) {
if (plugin._features[i] == name) { return false; }
}
2018-10-23 03:41:38 +02:00
plugin._features.push(name);
},
2018-05-11 13:42:35 +02:00
/**
2018-10-23 03:41:38 +02:00
* Clears all previously registered features.
* @method _converse.api.disco.own.features.clear
* @example _converse.api.disco.own.features.clear();
2018-05-11 13:42:35 +02:00
*/
2018-10-23 03:41:38 +02:00
clear () {
plugin._features = []
},
/**
* Returns all of the features registered for this client (i.e. instance of Converse).
* @method _converse.api.disco.own.features.get
* @example const features = _converse.api.disco.own.features.get();
*/
get () {
return plugin._features;
}
2018-10-23 03:41:38 +02:00
}
},
2018-10-23 03:41:38 +02:00
/**
* Query for information about an XMPP entity
*
* @method _converse.api.disco.info
* @param {string} jid The Jabber ID of the entity to query
* @param {string} [node] A specific node identifier associated with the JID
* @returns {promise} Promise which resolves once we have a result from the server.
*/
info (jid, node) {
2018-10-23 03:41:38 +02:00
const attrs = {xmlns: Strophe.NS.DISCO_INFO};
if (node) {
attrs.node = node;
}
const info = $iq({
'from': _converse.connection.jid,
'to':jid,
'type':'get'
}).c('query', attrs);
return _converse.api.sendIQ(info);
},
/**
* Query for items associated with an XMPP entity
*
* @method _converse.api.disco.items
* @param {string} jid The Jabber ID of the entity to query for items
* @param {string} [node] A specific node identifier associated with the JID
* @returns {promise} Promise which resolves once we have a result from the server.
*/
items (jid, node) {
2018-10-23 03:41:38 +02:00
const attrs = {'xmlns': Strophe.NS.DISCO_ITEMS};
if (node) {
attrs.node = node;
}
return _converse.api.sendIQ(
$iq({
'from': _converse.connection.jid,
'to':jid,
'type':'get'
2018-10-23 03:41:38 +02:00
}).c('query', attrs)
);
},
2018-10-23 03:41:38 +02:00
/**
* Namespace for methods associated with disco entities
*
* @namespace _converse.api.disco.entities
* @memberOf _converse.api.disco
*/
entities: {
/**
* Get the corresponding `DiscoEntity` instance.
*
2018-10-23 03:41:38 +02:00
* @method _converse.api.disco.entities.get
* @param {string} jid The Jabber ID of the entity
* @param {boolean} [create] Whether the entity should be created if it doesn't exist.
* @example _converse.api.disco.entities.get(jid);
*/
async get (jid, create=false) {
await _converse.api.waitUntil('discoInitialized');
if (!jid) {
return _converse.disco_entities;
}
if (_converse.disco_entities === undefined && !_converse.api.connection.connected()) {
// Happens during tests when disco lookups happen asynchronously after teardown.
const msg = `Tried to look up entity ${jid} but _converse.disco_entities has been torn down`;
log.warn(msg);
return;
}
const entity = _converse.disco_entities.get(jid);
if (entity || !create) {
return entity;
}
return _converse.api.disco.entities.create(jid);
},
/**
* Create a new disco entity. It's identity and features
* will automatically be fetched from cache or from the
* XMPP server.
*
* Fetching from cache can be disabled by passing in
* `ignore_cache: true` in the options parameter.
*
* @method _converse.api.disco.entities.create
* @param {string} jid The Jabber ID of the entity
* @param {object} [options] Additional options
* @param {boolean} [options.ignore_cache]
* If true, fetch all features from the XMPP server instead of restoring them from cache
* @example _converse.api.disco.entities.create(jid, {'ignore_cache': true});
*/
create (jid, options) {
return _converse.disco_entities.create({'jid': jid}, options);
2018-10-23 03:41:38 +02:00
}
},
/**
* @namespace _converse.api.disco.features
* @memberOf _converse.api.disco
*/
features: {
/**
* Return a given feature of a disco entity
*
* @method _converse.api.disco.features.get
* @param {string} feature The feature that might be
* supported. In the XML stanza, this is the `var`
* attribute of the `<feature>` element. For
* example: `http://jabber.org/protocol/muc`
* @param {string} jid The JID of the entity
* (and its associated items) which should be queried
* @returns {promise} A promise which resolves with a list containing
* _converse.Entity instances representing the entity
* itself or those items associated with the entity if
* they support the given feature.
* @example
* _converse.api.disco.features.get(Strophe.NS.MAM, _converse.bare_jid);
*/
async get (feature, jid) {
if (!jid) {
throw new TypeError('You need to provide an entity JID');
}
await _converse.api.waitUntil('discoInitialized');
let entity = await _converse.api.disco.entities.get(jid, true);
if (_converse.disco_entities === undefined && !_converse.api.connection.connected()) {
// Happens during tests when disco lookups happen asynchronously after teardown.
const msg = `Tried to get feature ${feature} for ${jid} but _converse.disco_entities has been torn down`;
log.warn(msg);
return;
}
entity = await entity.waitUntilFeaturesDiscovered;
const promises = _.concat(
entity.items.map(item => item.hasFeature(feature)),
entity.hasFeature(feature)
);
const result = await Promise.all(promises);
return _.filter(result, _.isObject);
}
},
2018-10-23 03:41:38 +02:00
/**
* Used to determine whether an entity supports a given feature.
*
* @method _converse.api.disco.supports
* @param {string} feature The feature that might be
* supported. In the XML stanza, this is the `var`
* attribute of the `<feature>` element. For
* example: `http://jabber.org/protocol/muc`
* @param {string} jid The JID of the entity
* (and its associated items) which should be queried
* @returns {promise} A promise which resolves with `true` or `false`.
2018-10-23 03:41:38 +02:00
* @example
* if (await _converse.api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)) {
* // The feature is supported
* } else {
* // The feature is not supported
* }
2018-10-23 03:41:38 +02:00
*/
async supports (feature, jid) {
const features = await _converse.api.disco.features.get(feature, jid);
return features.length > 0;
2018-10-23 03:41:38 +02:00
},
2018-10-23 03:41:38 +02:00
/**
* Refresh the features (and fields and identities) associated with a
* disco entity by refetching them from the server
*
* @method _converse.api.disco.refreshFeatures
* @param {string} jid The JID of the entity whose features are refreshed.
* @returns {promise} A promise which resolves once the features have been refreshed
* @example
* await _converse.api.disco.refreshFeatures('room@conference.example.org');
*/
async refreshFeatures (jid) {
if (!jid) {
2018-10-23 03:41:38 +02:00
throw new TypeError('api.disco.refreshFeatures: You need to provide an entity JID');
}
await _converse.api.waitUntil('discoInitialized');
let entity = await _converse.api.disco.entities.get(jid);
if (entity) {
entity.features.reset();
entity.fields.reset();
entity.identities.reset();
if (!entity.waitUntilFeaturesDiscovered.isPending) {
entity.waitUntilFeaturesDiscovered = utils.getResolveablePromise()
}
entity.queryInfo();
} else {
// Create it if it doesn't exist
entity = await _converse.api.disco.entities.create(jid, {'ignore_cache': true});
}
return entity.waitUntilFeaturesDiscovered;
2018-10-23 03:41:38 +02:00
},
2018-10-23 03:41:38 +02:00
/**
* Return all the features associated with a disco entity
*
* @method _converse.api.disco.getFeatures
* @param {string} jid The JID of the entity whose features are returned.
* @returns {promise} A promise which resolves with the returned features
* @example
* const features = await _converse.api.disco.getFeatures('room@conference.example.org');
*/
async getFeatures (jid) {
if (!jid) {
2018-10-23 03:41:38 +02:00
throw new TypeError('api.disco.getFeatures: You need to provide an entity JID');
}
await _converse.api.waitUntil('discoInitialized');
let entity = await _converse.api.disco.entities.get(jid, true);
entity = await entity.waitUntilFeaturesDiscovered;
return entity.features;
2018-10-23 03:41:38 +02:00
},
2018-10-23 03:41:38 +02:00
/**
* Return all the service discovery extensions fields
* associated with an entity.
*
* See [XEP-0129: Service Discovery Extensions](https://xmpp.org/extensions/xep-0128.html)
*
* @method _converse.api.disco.getFields
* @param {string} jid The JID of the entity whose fields are returned.
* @example
* const fields = await _converse.api.disco.getFields('room@conference.example.org');
*/
async getFields (jid) {
if (!jid) {
2018-10-23 03:41:38 +02:00
throw new TypeError('api.disco.getFields: You need to provide an entity JID');
}
await _converse.api.waitUntil('discoInitialized');
let entity = await _converse.api.disco.entities.get(jid, true);
entity = await entity.waitUntilFeaturesDiscovered;
return entity.fields;
2018-10-23 03:41:38 +02:00
},
/**
* Get the identity (with the given category and type) for a given disco entity.
*
* For example, when determining support for PEP (personal eventing protocol), you
* want to know whether the user's own JID has an identity with
* `category='pubsub'` and `type='pep'` as explained in this section of
* XEP-0163: https://xmpp.org/extensions/xep-0163.html#support
*
* @method _converse.api.disco.getIdentity
* @param {string} The identity category.
* In the XML stanza, this is the `category`
* attribute of the `<identity>` element.
* For example: 'pubsub'
* @param {string} type The identity type.
* In the XML stanza, this is the `type`
* attribute of the `<identity>` element.
* For example: 'pep'
* @param {string} jid The JID of the entity which might have the identity
* @returns {promise} A promise which resolves with a map indicating
* whether an identity with a given type is provided by the entity.
* @example
* _converse.api.disco.getIdentity('pubsub', 'pep', _converse.bare_jid).then(
* function (identity) {
* if (identity) {
2018-10-23 03:41:38 +02:00
* // The entity DOES have this identity
* } else {
* // The entity DOES NOT have this identity
2018-10-23 03:41:38 +02:00
* }
* }
* ).catch(e => log.error(e));
2018-10-23 03:41:38 +02:00
*/
async getIdentity (category, type, jid) {
const e = await _converse.api.disco.entities.get(jid, true);
if (e === undefined && !_converse.api.connection.connected()) {
// Happens during tests when disco lookups happen asynchronously after teardown.
const msg = `Tried to look up category ${category} for ${jid} but _converse.disco_entities has been torn down`;
log.warn(msg);
return;
}
return e.getIdentity(category, type);
}
2018-10-23 03:41:38 +02:00
}
});
}
});