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

221 lines
9.7 KiB
JavaScript
Raw Normal View History

// Converse.js (A browser based XMPP chat client)
// http://conversejs.org
//
// Copyright (c) 2012-2017, Jan-Carel Brand <jc@opkode.com>
// Licensed under the Mozilla Public License (MPLv2)
//
/* This is a Converse.js plugin which add support for XEP-0030: Service Discovery */
/*global Backbone, define, window, document */
(function (root, factory) {
define(["converse-core", "sizzle", "strophe.disco"], factory);
}(this, function (converse, sizzle) {
const { Backbone, Promise, Strophe, b64_sha1, _ } = converse.env;
converse.plugins.add('converse-disco', {
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
const { _converse } = this;
function onDiscoItems (stanza) {
_.each(stanza.querySelectorAll('query item'), (item) => {
if (item.getAttribute("node")) {
// XXX: ignore nodes for now.
// See: https://xmpp.org/extensions/xep-0030.html#items-nodes
return;
}
const jid = item.getAttribute('jid');
const entities = _converse.disco_entities;
if (_.isUndefined(entities.get(jid))) {
entities.create({'jid': jid});
}
});
}
// Promises exposed by this plugin
_converse.api.promises.add('discoInitialized');
_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 () {
this.features = new Backbone.Collection();
this.features.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.features-${this.get('jid')}`)
);
this.features.on('add', this.onFeatureAdded);
this.identities = new Backbone.Collection();
this.identities.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.identities-${this.get('jid')}`)
);
this.fetchFeatures();
},
onFeatureAdded (feature) {
_converse.emit('serviceDiscovered', feature);
},
fetchFeatures () {
if (this.features.browserStorage.records.length === 0) {
this.queryInfo();
} else {
this.features.fetch({add: true});
this.identities.fetch({add: true});
}
},
queryInfo () {
_converse.connection.disco.info(this.get('jid'), null, this.onInfo.bind(this));
},
queryForItems () {
if (_.isEmpty(this.identities.where({'category': 'server'}))) {
// Don't fetch features and items if this is not a
// server or a conference component.
return;
}
_converse.connection.disco.items(this.get('jid'), null, onDiscoItems);
},
onInfo (stanza) {
_.forEach(stanza.querySelectorAll('identity'), (identity) => {
this.identities.create({
'category': identity.getAttribute('category'),
'type': stanza.getAttribute('type'),
'name': stanza.getAttribute('name')
});
});
if (stanza.querySelector('feature[var="'+Strophe.NS.DISCO_ITEMS+'"]')) {
this.queryForItems();
}
_.forEach(stanza.querySelectorAll('feature'), (feature) => {
this.features.create({
'var': feature.getAttribute('var'),
'from': stanza.getAttribute('from')
});
});
this.trigger('featuresDiscovered');
}
});
_converse.DiscoEntities = Backbone.Collection.extend({
model: _converse.DiscoEntity,
initialize () {
this.browserStorage = new Backbone.BrowserStorage[_converse.storage](
b64_sha1(`converse.disco-entities-${_converse.bare_jid}`)
);
this.fetchEntities().then(
_.partial(_converse.emit, 'discoInitialized'),
_.partial(_converse.emit, 'discoInitialized')
).catch(_.partial(_converse.log, _, Strophe.LogLevel.FATAL));
},
fetchEntities () {
return new Promise((resolve, reject) => {
this.fetch({
add: true,
success: function (collection) {
if (collection.length === 0 || !collection.get(_converse.domain)) {
this.create({'jid': _converse.domain});
}
resolve();
}.bind(this),
error () {
reject (new Error("Could not fetch disco entities"));
}
});
});
}
});
function addClientFeatures () {
/* The strophe.disco.js plugin keeps a list of features which
* it will advertise to any #info queries made to it.
*
* See: http://xmpp.org/extensions/xep-0030.html#info
*/
// See http://xmpp.org/registrar/disco-categories.html
_converse.connection.disco.addIdentity('client', 'web', 'Converse.js');
_converse.connection.disco.addFeature(Strophe.NS.BOSH);
_converse.connection.disco.addFeature(Strophe.NS.CHATSTATES);
_converse.connection.disco.addFeature(Strophe.NS.DISCO_INFO);
_converse.connection.disco.addFeature(Strophe.NS.ROSTERX); // Limited support
if (_converse.message_carbons) {
_converse.connection.disco.addFeature(Strophe.NS.CARBONS);
}
_converse.emit('addClientFeatures');
return this;
}
function initializeDisco () {
addClientFeatures();
_converse.disco_entities = new _converse.DiscoEntities();
}
_converse.api.listen.on('reconnected', initializeDisco);
_converse.api.listen.on('connected', initializeDisco);
_converse.api.listen.on('beforeTearDown', () => {
if (_converse.disco_entities) {
_converse.disco_entities.each((entity) => {
entity.features.reset();
entity.features.browserStorage._clear();
});
_converse.disco_entities.reset();
_converse.disco_entities.browserStorage._clear();
}
});
/* We extend the default converse.js API to add methods specific to service discovery */
_.extend(_converse.api, {
'disco': {
'supports' (entity_jid, feature) {
/* Returns a Promise which returns a boolean indicating
* whether the feature is supported or by the given
* entity or not.
*
* Parameters:
* (String) entity_jid - The JID of the entity which might support the feature.
* (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'
*/
return _converse.api.waitUntil('discoInitialized').then(() =>
new Promise((resolve, reject) => {
function fulfillPromise (entity) {
if (entity.features.findWhere({'var': feature})) {
resolve(true);
} else {
resolve(false);
}
}
let entity = _converse.disco_entities.get(entity_jid);
if (_.isUndefined(entity)) {
entity = _converse.disco_entities.create({'jid': entity_jid});
entity.on('featuresDiscovered', _.partial(fulfillPromise, entity));
} else {
fulfillPromise(entity);
}
})
);
}
}
});
}
});
}));