241 lines
6.2 KiB
JavaScript
241 lines
6.2 KiB
JavaScript
/**
|
||
* Entity Capabilities (XEP-0115)
|
||
*
|
||
* Depends on disco plugin.
|
||
*
|
||
* See: http://xmpp.org/extensions/xep-0115.html
|
||
*
|
||
* Authors:
|
||
* - Michael Weibel <michael.weibel@gmail.com>
|
||
*
|
||
* Copyright:
|
||
* - Michael Weibel <michael.weibel@gmail.com>
|
||
*/
|
||
|
||
Strophe.addConnectionPlugin('caps', {
|
||
/** Constant: HASH
|
||
* Hash used
|
||
*
|
||
* Currently only sha-1 is supported.
|
||
*/
|
||
HASH: 'sha-1',
|
||
/** Variable: node
|
||
* Client which is being used.
|
||
*
|
||
* Can be overwritten as soon as Strophe has been initialized.
|
||
*/
|
||
node: 'http://strophe.im/strophejs/',
|
||
/** PrivateVariable: _ver
|
||
* Own generated version string
|
||
*/
|
||
_ver: '',
|
||
/** PrivateVariable: _connection
|
||
* Strophe connection
|
||
*/
|
||
_connection: null,
|
||
/** PrivateVariable: _knownCapabilities
|
||
* A hashtable containing version-strings and their capabilities, serialized
|
||
* as string.
|
||
*
|
||
* TODO: Maybe those caps shouldn't be serialized.
|
||
*/
|
||
_knownCapabilities: {},
|
||
/** PrivateVariable: _jidVerIndex
|
||
* A hashtable containing jids and their versions for better lookup of capabilities.
|
||
*/
|
||
_jidVerIndex: {},
|
||
|
||
/** Function: init
|
||
* Initialize plugin:
|
||
* - Add caps namespace
|
||
* - Add caps feature to disco plugin
|
||
* - Add handler for caps stanzas
|
||
*
|
||
* Parameters:
|
||
* (Strophe.Connection) conn - Strophe connection
|
||
*/
|
||
init: function(conn) {
|
||
this._connection = conn;
|
||
|
||
Strophe.addNamespace('CAPS', 'http://jabber.org/protocol/caps');
|
||
|
||
if (!this._connection.disco) {
|
||
throw "Caps plugin requires the disco plugin to be installed.";
|
||
}
|
||
|
||
this._connection.disco.addFeature(Strophe.NS.CAPS);
|
||
this._connection.addHandler(this._delegateCapabilities.bind(this), Strophe.NS.CAPS);
|
||
},
|
||
|
||
/** Function: generateCapsAttrs
|
||
* Returns the attributes for generating the "c"-stanza containing the own version
|
||
*
|
||
* Returns:
|
||
* (Object) - attributes
|
||
*/
|
||
generateCapsAttrs: function() {
|
||
return {
|
||
'xmlns': Strophe.NS.CAPS,
|
||
'hash': this.HASH,
|
||
'node': this.node,
|
||
'ver': this.generateVer()
|
||
};
|
||
},
|
||
|
||
/** Function: generateVer
|
||
* Returns the base64 encoded version string (encoded itself with sha1)
|
||
*
|
||
* Returns:
|
||
* (String) - version
|
||
*/
|
||
generateVer: function() {
|
||
if (this._ver !== "") {
|
||
return this._ver;
|
||
}
|
||
|
||
var ver = "",
|
||
identities = this._connection.disco._identities.sort(this._sortIdentities),
|
||
identitiesLen = identities.length,
|
||
features = this._connection.disco._features.sort(),
|
||
featuresLen = features.length;
|
||
for(var i = 0; i < identitiesLen; i++) {
|
||
var curIdent = identities[i];
|
||
ver += curIdent.category + "/" + curIdent.type + "/" + curIdent.lang + "/" + curIdent.name + "<";
|
||
}
|
||
for(var i = 0; i < featuresLen; i++) {
|
||
ver += features[i] + '<';
|
||
}
|
||
|
||
this._ver = b64_sha1(ver);
|
||
return this._ver;
|
||
},
|
||
|
||
/** Function: getCapabilitiesByJid
|
||
* Returns serialized capabilities of a jid (if available).
|
||
* Otherwise null.
|
||
*
|
||
* Parameters:
|
||
* (String) jid - Jabber id
|
||
*
|
||
* Returns:
|
||
* (String|null) - capabilities, serialized; or null when not available.
|
||
*/
|
||
getCapabilitiesByJid: function(jid) {
|
||
if (this._jidVerIndex[jid]) {
|
||
return this._knownCapabilities[this._jidVerIndex[jid]];
|
||
}
|
||
return null;
|
||
},
|
||
|
||
/** PrivateFunction: _delegateCapabilities
|
||
* Checks if the version has already been saved.
|
||
* If yes: do nothing.
|
||
* If no: Request capabilities
|
||
*
|
||
* Parameters:
|
||
* (Strophe.Builder) stanza - Stanza
|
||
*
|
||
* Returns:
|
||
* (Boolean)
|
||
*/
|
||
_delegateCapabilities: function(stanza) {
|
||
var from = stanza.getAttribute('from'),
|
||
c = stanza.querySelector('c'),
|
||
ver = c.getAttribute('ver'),
|
||
node = c.getAttribute('node');
|
||
if (!this._knownCapabilities[ver]) {
|
||
return this._requestCapabilities(from, node, ver);
|
||
} else {
|
||
this._jidVerIndex[from] = ver;
|
||
}
|
||
if (!this._jidVerIndex[from] || !this._jidVerIndex[from] !== ver) {
|
||
this._jidVerIndex[from] = ver;
|
||
}
|
||
return true;
|
||
},
|
||
|
||
/** PrivateFunction: _requestCapabilities
|
||
* Requests capabilities from the one which sent the caps-info stanza.
|
||
* This is done using disco info.
|
||
*
|
||
* Additionally, it registers a handler for handling the reply.
|
||
*
|
||
* Parameters:
|
||
* (String) to - Destination jid
|
||
* (String) node - Node attribute of the caps-stanza
|
||
* (String) ver - Version of the caps-stanza
|
||
*
|
||
* Returns:
|
||
* (Boolean) - true
|
||
*/
|
||
_requestCapabilities: function(to, node, ver) {
|
||
if (to !== this._connection.jid) {
|
||
var id = this._connection.disco.info(to, node + '#' + ver);
|
||
this._connection.addHandler(this._handleDiscoInfoReply.bind(this), Strophe.NS.DISCO_INFO, 'iq', 'result', id, to);
|
||
}
|
||
return true;
|
||
},
|
||
|
||
/** PrivateFunction: _handleDiscoInfoReply
|
||
* Parses the disco info reply and adds the version & it's capabilities to the _knownCapabilities variable.
|
||
* Additionally, it adds the jid & the version to the _jidVerIndex variable for a better lookup.
|
||
*
|
||
* Parameters:
|
||
* (Strophe.Builder) stanza - Disco info stanza
|
||
*
|
||
* Returns:
|
||
* (Boolean) - false, to automatically remove the handler.
|
||
*/
|
||
_handleDiscoInfoReply: function(stanza) {
|
||
var query = stanza.querySelector('query'),
|
||
node = query.getAttribute('node').split('#'),
|
||
ver = node[1],
|
||
from = stanza.getAttribute('from');
|
||
if (!this._knownCapabilities[ver]) {
|
||
var childNodes = query.childNodes,
|
||
childNodesLen = childNodes.length;
|
||
this._knownCapabilities[ver] = [];
|
||
for(var i = 0; i < childNodesLen; i++) {
|
||
var node = childNodes[i];
|
||
this._knownCapabilities[ver].push({name: node.nodeName, attributes: node.attributes});
|
||
}
|
||
this._jidVerIndex[from] = ver;
|
||
} else if (!this._jidVerIndex[from] || !this._jidVerIndex[from] !== ver) {
|
||
this._jidVerIndex[from] = ver;
|
||
}
|
||
return false;
|
||
},
|
||
|
||
/** PrivateFunction: _sortIdentities
|
||
* Sorts two identities according the sorting requirements in XEP-0115.
|
||
*
|
||
* Parameters:
|
||
* (Object) a - Identity a
|
||
* (Object) b - Identity b
|
||
*
|
||
* Returns:
|
||
* (Integer) - 1, 0 or -1; according to which one's greater.
|
||
*/
|
||
_sortIdentities: function(a, b) {
|
||
if (a.category > b.category) {
|
||
return 1;
|
||
}
|
||
if (a.category < b.category) {
|
||
return -1;
|
||
}
|
||
if (a.type > b.type) {
|
||
return 1;
|
||
}
|
||
if (a.type < b.type) {
|
||
return -1;
|
||
}
|
||
if (a.lang > b.lang) {
|
||
return 1;
|
||
}
|
||
if (a.lang < b.lang) {
|
||
return -1;
|
||
}
|
||
return 0;
|
||
}
|
||
});
|