123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- import isObject from "lodash-es/isObject";
- import log from "@converse/headless/log.js";
- import { _converse, api, converse } from "@converse/headless/core.js";
- import { getOpenPromise } from '@converse/openpromise';
- const { Strophe, $iq } = converse.env;
- export default {
- /**
- * The XEP-0030 service discovery API
- *
- * This API lets you discover information about entities on the
- * XMPP network.
- *
- * @namespace api.disco
- * @memberOf api
- */
- disco: {
- /**
- * @namespace api.disco.stream
- * @memberOf api.disco
- */
- stream: {
- /**
- * @method 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 api.waitUntil('streamFeaturesAdded');
- if (!name || !xmlns) {
- throw new Error("name and xmlns need to be provided when calling disco.stream.getFeature");
- }
- if (_converse.stream_features === undefined && !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;
- }
- return _converse.stream_features.findWhere({'name': name, 'xmlns': xmlns});
- }
- },
- /**
- * @namespace api.disco.own
- * @memberOf api.disco
- */
- own: {
- /**
- * @namespace api.disco.own.identities
- * @memberOf api.disco.own
- */
- identities: {
- /**
- * Lets you add new identities for this client (i.e. instance of Converse)
- * @method 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();
- */
- add (category, type, name, lang) {
- for (var i=0; i<_converse.disco._identities.length; i++) {
- if (_converse.disco._identities[i].category == category &&
- _converse.disco._identities[i].type == type &&
- _converse.disco._identities[i].name == name &&
- _converse.disco._identities[i].lang == lang) {
- return false;
- }
- }
- _converse.disco._identities.push({category: category, type: type, name: name, lang: lang});
- },
- /**
- * Clears all previously registered identities.
- * @method api.disco.own.identities.clear
- * @example _converse.api.disco.own.identities.clear();
- */
- clear () {
- _converse.disco._identities = []
- },
- /**
- * Returns all of the identities registered for this client
- * (i.e. instance of Converse).
- * @method api.disco.identities.get
- * @example const identities = api.disco.own.identities.get();
- */
- get () {
- return _converse.disco._identities;
- }
- },
- /**
- * @namespace api.disco.own.features
- * @memberOf api.disco.own
- */
- features: {
- /**
- * Lets you register new disco features for this client (i.e. instance of Converse)
- * @method 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");
- */
- add (name) {
- for (var i=0; i<_converse.disco._features.length; i++) {
- if (_converse.disco._features[i] == name) { return false; }
- }
- _converse.disco._features.push(name);
- },
- /**
- * Clears all previously registered features.
- * @method api.disco.own.features.clear
- * @example _converse.api.disco.own.features.clear();
- */
- clear () {
- _converse.disco._features = []
- },
- /**
- * Returns all of the features registered for this client (i.e. instance of Converse).
- * @method api.disco.own.features.get
- * @example const features = api.disco.own.features.get();
- */
- get () {
- return _converse.disco._features;
- }
- }
- },
- /**
- * Query for information about an XMPP entity
- *
- * @method 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) {
- 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 api.sendIQ(info);
- },
- /**
- * Query for items associated with an XMPP entity
- *
- * @method 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) {
- const attrs = {'xmlns': Strophe.NS.DISCO_ITEMS};
- if (node) {
- attrs.node = node;
- }
- return api.sendIQ(
- $iq({
- 'from': _converse.connection.jid,
- 'to':jid,
- 'type':'get'
- }).c('query', attrs)
- );
- },
- /**
- * Namespace for methods associated with disco entities
- *
- * @namespace api.disco.entities
- * @memberOf api.disco
- */
- entities: {
- /**
- * Get the corresponding `DiscoEntity` instance.
- *
- * @method 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 api.waitUntil('discoInitialized');
- if (!jid) {
- return _converse.disco_entities;
- }
- if (_converse.disco_entities === undefined && !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 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 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);
- }
- },
- /**
- * @namespace api.disco.features
- * @memberOf api.disco
- */
- features: {
- /**
- * Return a given feature of a disco entity
- *
- * @method 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
- * 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 api.waitUntil('discoInitialized');
- let entity = await api.disco.entities.get(jid, true);
- if (_converse.disco_entities === undefined && !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 = [...entity.items.map(i => i.hasFeature(feature)), entity.hasFeature(feature)];
- const result = await Promise.all(promises);
- return result.filter(isObject);
- }
- },
- /**
- * Used to determine whether an entity supports a given feature.
- *
- * @method 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`.
- * @example
- * if (await api.disco.supports(Strophe.NS.MAM, _converse.bare_jid)) {
- * // The feature is supported
- * } else {
- * // The feature is not supported
- * }
- */
- async supports (feature, jid) {
- const features = await api.disco.features.get(feature, jid) || [];
- return features.length > 0;
- },
- /**
- * Refresh the features, fields and identities associated with a
- * disco entity by refetching them from the server
- * @method api.disco.refresh
- * @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 api.disco.refresh('room@conference.example.org');
- */
- async refresh (jid) {
- if (!jid) {
- throw new TypeError('api.disco.refresh: You need to provide an entity JID');
- }
- await api.waitUntil('discoInitialized');
- let entity = await api.disco.entities.get(jid);
- if (entity) {
- entity.features.reset();
- entity.fields.reset();
- entity.identities.reset();
- if (!entity.waitUntilFeaturesDiscovered.isPending) {
- entity.waitUntilFeaturesDiscovered = getOpenPromise()
- }
- entity.queryInfo();
- } else {
- // Create it if it doesn't exist
- entity = await api.disco.entities.create(jid, {'ignore_cache': true});
- }
- return entity.waitUntilFeaturesDiscovered;
- },
- /**
- * @deprecated Use {@link api.disco.refresh} instead.
- * @method api.disco.refreshFeatures
- */
- refreshFeatures (jid) {
- return api.refresh(jid);
- },
- /**
- * Return all the features associated with a disco entity
- *
- * @method 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 api.disco.getFeatures('room@conference.example.org');
- */
- async getFeatures (jid) {
- if (!jid) {
- throw new TypeError('api.disco.getFeatures: You need to provide an entity JID');
- }
- await api.waitUntil('discoInitialized');
- let entity = await api.disco.entities.get(jid, true);
- entity = await entity.waitUntilFeaturesDiscovered;
- return entity.features;
- },
- /**
- * 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 api.disco.getFields
- * @param {string} jid The JID of the entity whose fields are returned.
- * @example
- * const fields = await api.disco.getFields('room@conference.example.org');
- */
- async getFields (jid) {
- if (!jid) {
- throw new TypeError('api.disco.getFields: You need to provide an entity JID');
- }
- await api.waitUntil('discoInitialized');
- let entity = await api.disco.entities.get(jid, true);
- entity = await entity.waitUntilFeaturesDiscovered;
- return entity.fields;
- },
- /**
- * 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 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
- * api.disco.getIdentity('pubsub', 'pep', _converse.bare_jid).then(
- * function (identity) {
- * if (identity) {
- * // The entity DOES have this identity
- * } else {
- * // The entity DOES NOT have this identity
- * }
- * }
- * ).catch(e => log.error(e));
- */
- async getIdentity (category, type, jid) {
- const e = await api.disco.entities.get(jid, true);
- if (e === undefined && !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);
- }
- }
- }
|