Move BOSH code into a plugin

- Remove the `keepalive` configuration setting. It is now always implicitly `true`.
- Remove the `expose_rid_and_sid` configuration setting.
- A `prebind_url` is now mandatory when setting `authentication` to `prebind`.
- It's no longer possible to pass in `rid` and `sid` values to `converse.initialize.
This commit is contained in:
JC Brand 2019-06-05 10:01:55 +02:00
parent 01fce55733
commit 38a232fd45
10 changed files with 211 additions and 316 deletions

View File

@ -1,6 +1,7 @@
# Changelog # Changelog
## 5.0.0 (Unreleased) ## 5.0.0 (Unreleased)
- BOSH support has been moved to a plugin.
- Support for XEP-0410 to check whether we're still present in a room - Support for XEP-0410 to check whether we're still present in a room
- Initial support for the [CredentialsContainer](https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer) web API - Initial support for the [CredentialsContainer](https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer) web API
- Allow for synchronous events. When a synchronous event is fired, Converse will - Allow for synchronous events. When a synchronous event is fired, Converse will
@ -46,6 +47,10 @@
- `_converse.api.disco.supports` now returns a Promise which resolves to a Boolean instead of an Array. - `_converse.api.disco.supports` now returns a Promise which resolves to a Boolean instead of an Array.
- The `forward_messages` config option (which was set to `false` by default) has been removed. - The `forward_messages` config option (which was set to `false` by default) has been removed.
Use [message_carbons](https://conversejs.org/docs/html/configuration.html#message-carbons) instead. Use [message_carbons](https://conversejs.org/docs/html/configuration.html#message-carbons) instead.
- Remove the `keepalive` configuration setting. It is now always implicitly `true`.
- Remove the `expose_rid_and_sid` configuration setting.
- A `prebind_url` is now mandatory when setting `authentication` to `prebind`.
It's no longer possible to pass in `rid` and `sid` values to `converse.initialize.
### API changes ### API changes

View File

@ -66,6 +66,8 @@ as soon as the page loads.
The server's domain is passed in via the `jid`_ setting. The server's domain is passed in via the `jid`_ setting.
.. _`prebind`:
prebind prebind
~~~~~~~ ~~~~~~~
@ -85,25 +87,15 @@ A JID (jabber ID), SID (session ID) and RID (Request ID).
Converse needs these tokens in order to attach to that same session. Converse needs these tokens in order to attach to that same session.
There are two complementary configuration settings to ``prebind``. In addition to setting ``authentication`` to ``prebind``, you'll also need to
They are :ref:`keepalive` and `prebind_url`_. set the `prebind_url`_ and `bosh-service-url`_.
``keepalive`` can be used keep the session alive without having to pass in Here's an example of Converse being initialized with these options:
new RID and SID tokens to ``converse.initialize`` every time you reload the page.
This removes the need to set up a new BOSH session every time a page loads.
You do however still need to supply the user's JID so that Converse can be
sure that the session it's resuming is for the right user.
`prebind_url`_ lets you specify a URL which Converse will call whenever a
new BOSH session needs to be set up.
Here's an example of Converse being initialized with these three options:
.. code-block:: javascript .. code-block:: javascript
converse.initialize({ converse.initialize({
bosh_service_url: 'https://bind.example.com', bosh_service_url: 'https://bind.example.com',
keepalive: true,
jid: 'me@example.com', jid: 'me@example.com',
authentication: 'prebind', authentication: 'prebind',
prebind_url: 'http://example.com/api/prebind', prebind_url: 'http://example.com/api/prebind',
@ -187,8 +179,8 @@ allow_non_roster_messaging
Determines whether you'll receive messages from users that are not in your Determines whether you'll receive messages from users that are not in your
roster. The XMPP specification allows for this (similar to email). roster. The XMPP specification allows for this (similar to email).
Setting this to `true` increases your chances of receiving spam (when using a Setting this to ``true`` increases your chances of receiving spam (when using a
federated server), while setting it to `false` means that people not on your federated server), while setting it to ``false`` means that people not on your
roster can't contact you unless one (or both) of you subscribe to one another's roster can't contact you unless one (or both) of you subscribe to one another's
presence (i.e. adding as a roster contact). presence (i.e. adding as a roster contact).
@ -321,13 +313,13 @@ auto_reconnect
Automatically reconnect to the XMPP server if the connection drops Automatically reconnect to the XMPP server if the connection drops
unexpectedly. unexpectedly.
This option works best when you have `authentication` set to `prebind` and have This option works best when you have ``authentication`` set to ``prebind`` and have
also specified a `prebind_url` URL, from where Converse can fetch the BOSH also specified a ``prebind_url`` URL, from where Converse can fetch the BOSH
tokens. In this case, Converse will automaticallly reconnect when the tokens. In this case, Converse will automaticallly reconnect when the
connection drops but also reestablish earlier lost connections (due to connection drops but also reestablish earlier lost connections (due to
network outages, closing your laptop etc.). network outages, closing your laptop etc.).
When `authentication` is set to `login`, then this option will only work when When ``authentication`` is set to `login`, then this option will only work when
the page hasn't been reloaded yet, because then the user's password has been the page hasn't been reloaded yet, because then the user's password has been
wiped from memory. This configuration can however still be useful when using wiped from memory. This configuration can however still be useful when using
Converse in desktop apps, for example those based on `CEF <https://bitbucket.org/chromiumembedded/cef>`_ Converse in desktop apps, for example those based on `CEF <https://bitbucket.org/chromiumembedded/cef>`_
@ -407,6 +399,7 @@ plugins from registering themselves under those names.
The core, and by default whitelisted, plugins are:: The core, and by default whitelisted, plugins are::
converse-bosh
converse-bookmarks converse-bookmarks
converse-chatboxes converse-chatboxes
converse-chatview converse-chatview
@ -427,8 +420,9 @@ The core, and by default whitelisted, plugins are::
converse-roomslist converse-roomslist
converse-rosterview converse-rosterview
converse-singleton converse-singleton
converse-smacks
converse-spoilers converse-spoilers
converse-vcard' converse-vcard
Example: Example:
@ -519,7 +513,7 @@ credentials_url
* Default: ``null`` * Default: ``null``
* Type: URL * Type: URL
This setting should be used in conjunction with ``authentication`` set to ``login`` and :ref:`keepalive` set to ``true``. This setting should be used in conjunction with ``authentication`` set to ``login``.
It allows you to specify a URL which Converse will call when it needs to get It allows you to specify a URL which Converse will call when it needs to get
the username and password (or authentication token) which Converse will use the username and password (or authentication token) which Converse will use
@ -644,18 +638,6 @@ Determines whether `XEP-0198 Stream Management <https://xmpp.org/extensions/xep-
support is turned on or not. support is turned on or not.
expose_rid_and_sid
------------------
* Default: ``false``
Allow the prebind tokens, RID (request ID) and SID (session ID), to be exposed
globally via the API. This allows other scripts served on the same page to use
these values.
*Beware*: a malicious script could use these tokens to assume your identity
and inject fake chat messages.
filter_by_resource filter_by_resource
------------------ ------------------
@ -744,37 +726,10 @@ The Jabber ID or "JID" of the current user. The JID uniquely identifies a user
on the XMPP network. It looks like an email address, but it's used for instant on the XMPP network. It looks like an email address, but it's used for instant
messaging instead. messaging instead.
This value needs to be provided when using the :ref:`keepalive` option together This value may be provided together with a ``password`` instead of supplying a
with `prebind`_. `credentials_url`_ when setting ``auto_login`` to ``true``.
.. _`keepalive`:
keepalive
---------
* Default: ``true``
Determines whether Converse will maintain the chat session across page
loads.
This setting should also be used in conjunction with ``authentication`` set to `prebind`_.
When using ``keepalive`` and ``prebind``, you will have to provide the `jid`_
of the current user to ensure that a cached session is only resumed if it
belongs to the current user.
See also:
* :ref:`session-support`
.. note::
Currently the "keepalive" setting only works with BOSH and not with
websockets. This is because XMPP over websocket does not use the same
session token as with BOSH. A possible solution for this is to implement
`XEP-0198 <https://xmpp.org/extensions/xep-0198.html>`_, specifically
with regards to "stream resumption".
.. _`locales`: .. _`locales`:
locales locales
@ -818,7 +773,7 @@ injection attack could be attempted.
The variable being interpolated via the curly braces is ``locale``, which is The variable being interpolated via the curly braces is ``locale``, which is
the value passed in to the `i18n`_ setting, or the browser's locale or the the value passed in to the `i18n`_ setting, or the browser's locale or the
default local or `en` (resolved in that order). default local or ``en`` (resolved in that order).
From version 3.3.0, Converse no longer bundles all translations into its From version 3.3.0, Converse no longer bundles all translations into its
final build file. Instead, only the relevant translations are fetched at final build file. Instead, only the relevant translations are fetched at
@ -899,7 +854,7 @@ message_archiving_timeout
The amount of time (in milliseconds) to wait when requesting archived messages The amount of time (in milliseconds) to wait when requesting archived messages
from the XMPP server. from the XMPP server.
Used in conjunction with `message_archiving` and in context of `XEP-0313: Message Archive Management <https://xmpp.org/extensions/xep-0313.html>`_. Used in conjunction with ``message_archiving`` and in context of `XEP-0313: Message Archive Management <https://xmpp.org/extensions/xep-0313.html>`_.
message_carbons message_carbons
--------------- ---------------
@ -1037,7 +992,7 @@ The nickname will be included in presence requests to other users and will also
be used as the default nickname when entering MUC chatrooms. be used as the default nickname when entering MUC chatrooms.
This value will have first preference ahead of other nickname sources, such as This value will have first preference ahead of other nickname sources, such as
the VCard `nickname` value. the VCard ``nickname`` value.
notify_all_room_messages notify_all_room_messages
@ -1141,7 +1096,7 @@ prebind_url
See also: :ref:`session-support` See also: :ref:`session-support`
This setting should be used in conjunction with ``authentication`` set to `prebind` and :ref:`keepalive` set to ``true``. This setting should be used in conjunction with ``authentication`` set to `prebind`.
It allows you to specify a URL which Converse will call when it needs to get It allows you to specify a URL which Converse will call when it needs to get
the RID and SID (Request ID and Session ID) tokens of a BOSH connection, which the RID and SID (Request ID and Session ID) tokens of a BOSH connection, which
@ -1322,7 +1277,7 @@ If set to ``true``, notifications will be shown in the following cases:
* the browser is not visible nor focused and a private message is received. * the browser is not visible nor focused and a private message is received.
* the browser is not visible nor focused and a groupchat message is received which mentions you. * the browser is not visible nor focused and a groupchat message is received which mentions you.
* `auto_subscribe` is set to `false` and a new contact request is received. * ``auto_subscribe`` is set to ``false`` and a new contact request is received.
If set to ``all``, notifications will be shown even if the above conditions are If set to ``all``, notifications will be shown even if the above conditions are
not fulfilled. not fulfilled.
@ -1457,7 +1412,7 @@ synchronize_availability
Valid options: ``true``, ``false``, ``a resource name``. Valid options: ``true``, ``false``, ``a resource name``.
This option lets you synchronize your chat status (`online`, `busy`, `away`) with other chat clients. In other words, This option lets you synchronize your chat status (`online`, `busy`, `away`) with other chat clients. In other words,
if you change your status to `busy` in a different chat client, your status will change to `busy` in Converse as well. if you change your status to ``busy`` in a different chat client, your status will change to ``busy`` in Converse as well.
If set to ``true``, Converse will synchronize with all other clients you are logged in with. If set to ``true``, Converse will synchronize with all other clients you are logged in with.
@ -1519,8 +1474,8 @@ time_format
Examples: ``HH:mm``, ``hh:mm``, ``hh:mm a``. Examples: ``HH:mm``, ``hh:mm``, ``hh:mm a``.
This option makes the time format for the time shown, for each message, configurable. Converse uses `DayJS <https://github.com/iamkun/dayjs>`_ This option makes the time format for the time shown, for each message, configurable. Converse uses `DayJS <https://github.com/iamkun/dayjs>`_
for showing time. This option allows the configuration of the format in which `DayJS` will display the time for the messages. For detailed for showing time. This option allows the configuration of the format in which ``DayJS`` will display the time for the messages. For detailed
description of time-format options available for `DayJS` you can check the description of time-format options available for ``DayJS`` you can check the
`default formatting options <https://github.com/iamkun/dayjs/blob/dev/docs/en/API-reference.md#displaying>`_ and the `default formatting options <https://github.com/iamkun/dayjs/blob/dev/docs/en/API-reference.md#displaying>`_ and the
`advanced options <https://github.com/iamkun/dayjs/blob/master/docs/en/Plugin.md#advancedformat>`_. `advanced options <https://github.com/iamkun/dayjs/blob/master/docs/en/Plugin.md#advancedformat>`_.
@ -1577,13 +1532,6 @@ techniques for bidirectional HTTP (such as `BOSH <https://en.wikipedia.org/wiki/
Please refer to your XMPP server's documentation on how to enable websocket Please refer to your XMPP server's documentation on how to enable websocket
support. support.
.. note::
Please note that not older browsers do not support websockets. For older
browsers you'll want to specify a BOSH URL. See the :ref:`bosh-service-url`
configuration setting).
.. note::
Converse does not yet support "keepalive" with websockets.
.. _`view_mode`: .. _`view_mode`:

View File

@ -116,7 +116,6 @@ your components, for example:
'auto_reconnect': true, 'auto_reconnect': true,
'bosh_service_url': bosh_url, 'bosh_service_url': bosh_url,
'jid': bare_jid, 'jid': bare_jid,
'keepalive': true,
'credentials_url': credentials_url, 'credentials_url': credentials_url,
'whitelisted_plugins': ['conversejs-angular-service'] 'whitelisted_plugins': ['conversejs-angular-service']
}); });

View File

@ -29,8 +29,7 @@ The diagram below shows a fairly common setup for a website or intranet:
* It communicates with the XMPP server via BOSH or websocket which is usually * It communicates with the XMPP server via BOSH or websocket which is usually
reverse-proxied by a web-server in order to overcome cross-site scripting reverse-proxied by a web-server in order to overcome cross-site scripting
restrictions in the browser. For more info on that, read the section: restrictions in the browser.
`Overcoming cross-domain request restrictions`_
* Optionally the XMPP server is configured to use a SQL database for storing * Optionally the XMPP server is configured to use a SQL database for storing
archived chat messages. archived chat messages.
@ -293,8 +292,7 @@ Single Session Support
It's possible to enable shared sessions whereby users already It's possible to enable shared sessions whereby users already
logged in to your website will also automatically be logged in on the XMPP server, logged in to your website will also automatically be logged in on the XMPP server,
Once a user is logged in, the session will be kept alive across page loads by Once a user is logged in, the session will be kept alive across page loads.
way of the :ref:`keepalive` setting.
There are a few ways to let your users be automatically authenticated to an There are a few ways to let your users be automatically authenticated to an
XMPP server once they've logged in to your site. XMPP server once they've logged in to your site.
@ -364,8 +362,7 @@ page load). Each page load is a new request which requires a new unique RID.
The best way to achieve this is to simply increment the RID with each page The best way to achieve this is to simply increment the RID with each page
load. load.
You'll need to configure Converse with the ``prebind``, :ref:`keepalive` and You'll need to configure Converse with the :ref:`prebind` :ref:`prebind_url` settings.
:ref:`prebind_url` settings.
Please read the documentation on those settings for a fuller picture of what Please read the documentation on those settings for a fuller picture of what
needs to be done. needs to be done.

View File

@ -23,37 +23,6 @@
_converse.connection = connection; _converse.connection = connection;
done(); done();
})); }));
describe("with prebind", function () {
it("needs a jid when also using keepalive", mock.initConverse([], null, {'auto_login': false}, (done, _converse) => {
const authentication = _converse.authentication;
const jid = _converse.jid;
delete _converse.jid;
_converse.keepalive = true;
_converse.authentication = "prebind";
expect(_converse.api.user.login.bind(_converse)).toThrow(
new Error(
"restoreBOSHSession: tried to restore a \"keepalive\" session "+
"but we don't have the JID for the user!"));
_converse.authentication= authentication;
_converse.jid = jid;
_converse.keepalive = false;
done();
}));
it("needs jid, rid and sid values when not using keepalive", mock.initConverse((done, _converse) => {
const jid = _converse.jid;
delete _converse.jid;
_converse.keepalive = false;
_converse.authentication = "prebind";
expect(_converse.api.user.login.bind(_converse)).toThrow(
new Error("attemptPreboundSession: If you use prebind and not keepalive, then you MUST supply JID, RID and SID values or a prebind_url."));
_converse.bosh_service_url = undefined;
_converse.jid = jid;
done();
}));
});
}); });
describe("A chat state indication", function () { describe("A chat state indication", function () {
@ -219,9 +188,6 @@
test_utils.createContacts(_converse, 'current'); test_utils.createContacts(_converse, 'current');
const old_connection = _converse.connection; const old_connection = _converse.connection;
_converse.connection._proto.rid = '1234'; _converse.connection._proto.rid = '1234';
_converse.expose_rid_and_sid = false;
expect(_converse.api.tokens.get('rid')).toBe(null);
_converse.expose_rid_and_sid = true;
expect(_converse.api.tokens.get('rid')).toBe('1234'); expect(_converse.api.tokens.get('rid')).toBe('1234');
_converse.connection = undefined; _converse.connection = undefined;
expect(_converse.api.tokens.get('rid')).toBe(null); expect(_converse.api.tokens.get('rid')).toBe(null);
@ -234,9 +200,6 @@
test_utils.createContacts(_converse, 'current'); test_utils.createContacts(_converse, 'current');
const old_connection = _converse.connection; const old_connection = _converse.connection;
_converse.connection._proto.sid = '1234'; _converse.connection._proto.sid = '1234';
_converse.expose_rid_and_sid = false;
expect(_converse.api.tokens.get('sid')).toBe(null);
_converse.expose_rid_and_sid = true;
expect(_converse.api.tokens.get('sid')).toBe('1234'); expect(_converse.api.tokens.get('sid')).toBe('1234');
_converse.connection = undefined; _converse.connection = undefined;
expect(_converse.api.tokens.get('sid')).toBe(null); expect(_converse.api.tokens.get('sid')).toBe(null);

View File

@ -0,0 +1,143 @@
// Converse.js
// http://conversejs.org
//
// Copyright (c) The Converse.js developers
// Licensed under the Mozilla Public License (MPLv2)
/* This is a Converse.js plugin which add support for XEP-0206: XMPP Over BOSH */
import BrowserStorage from "backbone.browserStorage";
import converse from "./converse-core";
const { Backbone, Strophe, _ } = converse.env;
const u = converse.env.utils;
converse.plugins.add('converse-bosh', {
initialize () {
const { _converse } = this;
_converse.api.settings.update({
bosh_service_url: undefined,
prebind_url: null
});
async function initBOSHSession () {
const id = 'converse.bosh-session';
if (!_converse.bosh_session) {
_converse.bosh_session = new Backbone.Model({id});
_converse.bosh_session.browserStorage = new BrowserStorage.session(id);
await new Promise(resolve => _converse.bosh_session.fetch({'success': resolve, 'error': resolve}));
}
if (_converse.jid && _converse.bosh_session.get('jid') === _converse.jid) {
_converse.bosh_session.clear({'silent': true });
_converse.bosh_session.save({'jid': _converse.jid, id});
}
return _converse.bosh_session;
}
_converse.startNewBOSHSession = function () {
if (!_converse.prebind_url) {
throw new Error(
"attemptPreboundSession: If you use prebind then you MUST supply a prebind_url");
}
const xhr = new XMLHttpRequest();
xhr.open('GET', _converse.prebind_url, true);
xhr.setRequestHeader('Accept', 'application/json, text/javascript');
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 400) {
const data = JSON.parse(xhr.responseText);
_converse.connection.attach(
data.jid,
data.sid,
data.rid,
_converse.onConnectStatusChanged
);
} else {
xhr.onerror();
}
};
xhr.onerror = function () {
delete _converse.connection;
/**
* Triggered when fetching prebind tokens failed
* @event _converse#noResumeableBOSHSession
* @type { _converse }
* @example _converse.api.listen.on('noResumeableBOSHSession', _converse => { ... });
*/
_converse.api.trigger('noResumeableBOSHSession', _converse);
};
xhr.send();
}
_converse.restoreBOSHSession = async function () {
if (!_converse.api.connection.isType('bosh')) {
return false;
}
const jid = (await initBOSHSession()).get('jid');
if (jid) {
try {
_converse.connection.restore(jid, _converse.onConnectStatusChanged);
return true;
} catch (e) {
_converse.log(
"Could not restore session for jid: "+
jid+" Error message: "+e.message, Strophe.LogLevel.WARN);
_converse.clearSession(); // We want to clear presences (see #555)
return false;
}
}
return false;
}
/************************ BEGIN Event Handlers ************************/
_converse.api.listen.on('clearSession', () => {
if (!_.isUndefined(_converse.bosh_session)) {
_converse.bosh_session.destroy();
delete _converse.bosh_session;
}
});
_converse.api.listen.on('setUserJID', () => {
if (!_.isUndefined(_converse.bosh_session)) {
_converse.bosh_session.save({'jid': _converse.jid});
}
});
/************************ END Event Handlers ************************/
/************************ BEGIN API ************************/
Object.assign(_converse.api, {
/**
* This namespace lets you access the BOSH tokens
*
* @namespace _converse.api.tokens
* @memberOf _converse.api
*/
tokens: {
/**
* @method _converse.api.tokens.get
* @param {string} [id] The type of token to return ('rid' or 'sid').
* @returns 'string' A token, either the RID or SID token depending on what's asked for.
* @example _converse.api.tokens.get('rid');
*/
get (id) {
if (_.isUndefined(_converse.connection)) {
return null;
}
if (id.toLowerCase() === 'rid') {
return _converse.connection.rid || _converse.connection._proto.rid;
} else if (id.toLowerCase() === 'sid') {
return _converse.connection.sid || _converse.connection._proto.sid;
}
}
}
});
/************************ end api ************************/
}
});

View File

@ -75,6 +75,7 @@ const BOSH_WAIT = 59;
// the other plugins are whitelisted in src/converse.js // the other plugins are whitelisted in src/converse.js
const CORE_PLUGINS = [ const CORE_PLUGINS = [
'converse-bookmarks', 'converse-bookmarks',
'converse-bosh',
'converse-caps', 'converse-caps',
'converse-chatboxes', 'converse-chatboxes',
'converse-disco', 'converse-disco',
@ -205,18 +206,15 @@ _converse.default_settings = {
auto_reconnect: true, auto_reconnect: true,
auto_xa: 0, // Seconds after which user status is set to 'xa' auto_xa: 0, // Seconds after which user status is set to 'xa'
blacklisted_plugins: [], blacklisted_plugins: [],
bosh_service_url: undefined,
connection_options: {}, connection_options: {},
credentials_url: null, // URL from where login credentials can be fetched credentials_url: null, // URL from where login credentials can be fetched
csi_waiting_time: 0, // Support for XEP-0352. Seconds before client is considered idle and CSI is sent out. csi_waiting_time: 0, // Support for XEP-0352. Seconds before client is considered idle and CSI is sent out.
debug: false, debug: false,
default_state: 'online', default_state: 'online',
expose_rid_and_sid: false,
geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g, geouri_regex: /https:\/\/www.openstreetmap.org\/.*#map=[0-9]+\/([\-0-9.]+)\/([\-0-9.]+)\S*/g,
geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2', geouri_replacement: 'https://www.openstreetmap.org/?mlat=$1&mlon=$2#map=18/$1/$2',
idle_presence_timeout: 300, // Seconds after which an idle presence is sent idle_presence_timeout: 300, // Seconds after which an idle presence is sent
jid: undefined, jid: undefined,
keepalive: true,
locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json', locales_url: 'locale/{{{locale}}}/LC_MESSAGES/converse.json',
locales: [ locales: [
'af', 'ar', 'bg', 'ca', 'cs', 'de', 'eo', 'es', 'eu', 'en', 'fr', 'gl', 'af', 'ar', 'bg', 'ca', 'cs', 'de', 'eo', 'es', 'eu', 'en', 'fr', 'gl',
@ -226,7 +224,6 @@ _converse.default_settings = {
message_carbons: true, message_carbons: true,
nickname: undefined, nickname: undefined,
password: undefined, password: undefined,
prebind_url: null,
priority: 0, priority: 0,
rid: undefined, rid: undefined,
root: window.document, root: window.document,
@ -443,32 +440,14 @@ const debouncedReconnect = _.debounce(reconnect, 2000);
function clearSession () { function clearSession () {
if (!_.isUndefined(_converse.bosh_session)) {
_converse.bosh_session.destroy();
delete _converse.bosh_session;
}
if (!_.isUndefined(_converse.session)) { if (!_.isUndefined(_converse.session)) {
_converse.session.destroy(); _converse.session.destroy();
delete _converse.session; delete _converse.session;
} }
// TODO: Refactor so that we don't clear // TODO: Refactor so that we don't clear
if (!_converse.config.get('trusted') || isTestEnv()) { if (!_converse.config.get('trusted') || isTestEnv()) {
window.localStorage.clear(); window.localStorage.clear();
window.sessionStorage.clear(); window.sessionStorage.clear();
} else {
if (!_.isUndefined(_converse.bosh_session)) {
_converse.bosh_session.destroy();
delete _converse.bosh_session;
}
if (!_.isUndefined(_converse.session)) {
_converse.session.destroy();
delete _converse.session;
}
_.get(_converse, 'bosh_session.browserStorage', {
_clear: _.noop
})._clear();
_.get(_converse, 'session.browserStorage', { _clear: _.noop })._clear();
} }
/** /**
* Triggered once the session information has been cleared, * Triggered once the session information has been cleared,
@ -479,6 +458,7 @@ function clearSession () {
_converse.api.trigger('clearSession'); _converse.api.trigger('clearSession');
} }
_converse.initConnection = function () { _converse.initConnection = function () {
/* Creates a new Strophe.Connection instance if we don't already have one. /* Creates a new Strophe.Connection instance if we don't already have one.
*/ */
@ -494,7 +474,11 @@ _converse.initConnection = function () {
} else if (_converse.bosh_service_url) { } else if (_converse.bosh_service_url) {
_converse.connection = new Strophe.Connection( _converse.connection = new Strophe.Connection(
_converse.bosh_service_url, _converse.bosh_service_url,
Object.assign(_converse.default_connection_options, _converse.connection_options, {'keepalive': _converse.keepalive}) Object.assign(
_converse.default_connection_options,
_converse.connection_options,
{'keepalive': true}
)
); );
} else { } else {
throw new Error("initConnection: this browser does not support websockets and bosh_service_url wasn't specified."); throw new Error("initConnection: this browser does not support websockets and bosh_service_url wasn't specified.");
@ -510,29 +494,6 @@ _converse.initConnection = function () {
_converse.api.trigger('connectionInitialized'); _converse.api.trigger('connectionInitialized');
}; };
async function initBOSHSession () {
const id = 'converse.bosh-session';
_converse.bosh_session = new Backbone.Model({id});
_converse.bosh_session.browserStorage = new BrowserStorage.session(id);
try {
await new Promise((success, error) => _converse.bosh_session.fetch({ success, error }));
if (_converse.jid && !u.isSameBareJID(_converse.bosh_session.get('jid'), _converse.jid)) {
_converse.bosh_session.clear({ silent: true });
_converse.bosh_session.save({ jid: _converse.jid, id });
}
} catch (e) {
if (_converse.jid) {
_converse.bosh_session.save({ jid: _converse.jid });
}
}
/**
* Triggered once the session has been initialized. The session is a
* persistent object which stores session information in the browser storage.
* @event _converse#BOSHSessionInitialized
* @memberOf _converse
*/
_converse.api.trigger('BOSHSessionInitialized');
}
async function initUserSession (jid) { async function initUserSession (jid) {
const bare_jid = Strophe.getBareJidFromJid(jid); const bare_jid = Strophe.getBareJidFromJid(jid);
@ -567,6 +528,11 @@ function setUserJID (jid) {
'resource': _converse.resource, 'resource': _converse.resource,
'domain': _converse.domain 'domain': _converse.domain
}); });
/**
* Triggered whenever the user's JID has been updated
* @event _converse#setUserJID
*/
_converse.api.trigger('setUserJID');
} }
@ -604,11 +570,10 @@ function setUpXMLLogging () {
} }
async function finishInitialization () { function finishInitialization () {
initClientConfig(); initClientConfig();
initPlugins(); initPlugins();
_converse.initConnection(); _converse.initConnection();
await initBOSHSession();
_converse.api.user.login(); _converse.api.user.login();
_converse.registerGlobalEventHandlers(); _converse.registerGlobalEventHandlers();
if (!Backbone.history.started) { if (!Backbone.history.started) {
@ -735,9 +700,8 @@ _converse.initialize = async function (settings, callback) {
/* When reloading the page: /* When reloading the page:
* For new sessions, we need to send out a presence stanza to notify * For new sessions, we need to send out a presence stanza to notify
* the server/network that we're online. * the server/network that we're online.
* When re-attaching to an existing session (e.g. via the keepalive * When re-attaching to an existing session we don't need to again send out a presence stanza,
* option), we don't need to again send out a presence stanza, because * because it's as if "we never left" (see onConnectStatusChanged).
* it's as if "we never left" (see onConnectStatusChanged).
* https://github.com/jcbrand/converse.js/issues/521 * https://github.com/jcbrand/converse.js/issues/521
*/ */
this.send_initial_presence = true; this.send_initial_presence = true;
@ -1243,106 +1207,8 @@ _converse.initialize = async function (settings, callback) {
}); });
this.startNewBOSHSession = function () {
const xhr = new XMLHttpRequest();
xhr.open('GET', _converse.prebind_url, true);
xhr.setRequestHeader('Accept', 'application/json, text/javascript');
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 400) {
const data = JSON.parse(xhr.responseText);
_converse.connection.attach(
data.jid,
data.sid,
data.rid,
_converse.onConnectStatusChanged
);
} else {
xhr.onerror();
}
};
xhr.onerror = function () {
delete _converse.connection;
/**
* Triggered when keepalive=true but there aren't any stored prebind tokens.
* @event _converse#noResumeableSession
* @type { _converse }
* @example _converse.api.listen.on('noResumeableSession', _converse => { ... });
*/
_converse.api.trigger('noResumeableSession', this);
};
xhr.send();
};
this.restoreBOSHSession = function (jid_is_required) {
if (!_converse.api.connection.isType('bosh')) {
return false;
}
/* Tries to restore a cached BOSH session. */
const jid = _converse.bosh_session.get('jid');
if (!jid) {
const msg = "restoreBOSHSession: tried to restore a \"keepalive\" session "+
"but we don't have the JID for the user!";
if (jid_is_required) {
throw new Error(msg);
} else {
_converse.log(msg);
return false;
}
} else {
try {
this.connection.restore(jid, this.onConnectStatusChanged);
return true;
} catch (e) {
_converse.log(
"Could not restore session for jid: "+
jid+" Error message: "+e.message, Strophe.LogLevel.WARN);
clearSession(); // We want to clear presences (see #555)
return false;
}
}
};
this.attemptPreboundSession = function (reconnecting) {
/* Handle session resumption or initialization when prebind is
* being used.
*/
if (!reconnecting) {
if (this.keepalive && this.restoreBOSHSession(true)) {
return;
}
// No keepalive, or session resumption has failed.
if (this.jid && this.sid && this.rid) {
return this.connection.attach(
this.jid,
this.sid,
this.rid,
this.onConnectStatusChanged
);
}
}
if (this.prebind_url) {
return this.startNewBOSHSession();
} else {
throw new Error(
"attemptPreboundSession: If you use prebind and not keepalive, "+
"then you MUST supply JID, RID and SID values or a prebind_url.");
}
};
this.attemptNonPreboundSession = async function (credentials, reconnecting) { this.attemptNonPreboundSession = async function (credentials, reconnecting) {
/* Handle session resumption or initialization when prebind is not being used.
*
* Two potential options exist and are handled in this method:
* 1. keepalive
* 2. auto_login
*/
if (!reconnecting && this.keepalive && this.restoreBOSHSession()) {
return;
}
if (credentials) { if (credentials) {
// When credentials are passed in, they override prebinding
// or credentials fetching via HTTP
this.autoLogin(credentials); this.autoLogin(credentials);
} else if (this.auto_login) { } else if (this.auto_login) {
if (this.credentials_url) { if (this.credentials_url) {
@ -1577,28 +1443,25 @@ _converse.api = {
* @param {string} [jid] * @param {string} [jid]
* @param {string} [password] * @param {string} [password]
* @param {boolean} [reconnecting] * @param {boolean} [reconnecting]
* @example
* converse.plugins.add('myplugin', {
* initialize: function () {
* this._converse.api.user.login('romeo@montague.lit', 'secret');
* }
* });
*/ */
login (jid, password, reconnecting) { async login (jid, password, reconnecting) {
if (_converse.authentication === _converse.PREBIND) { if (_converse.api.connection.isType('bosh')) {
_converse.attemptPreboundSession(reconnecting); if (reconnecting && _converse.prebind_url) {
} else { return _converse.startNewBOSHSession();
let credentials; } else if (await _converse.restoreBOSHSession()) {
if (jid && password) { return;
credentials = { jid: jid, password: password };
} else if (u.isValidJID(_converse.jid) && _converse.password) {
credentials = { jid: _converse.jid, password: _converse.password };
} }
if (credentials && credentials.jid) {
setUserJID(credentials.jid);
}
_converse.attemptNonPreboundSession(credentials, reconnecting);
} }
let credentials;
if (jid && password) {
credentials = { jid: jid, password: password };
} else if (u.isValidJID(_converse.jid) && _converse.password) {
credentials = { jid: _converse.jid, password: _converse.password };
}
if (credentials && credentials.jid) {
setUserJID(credentials.jid);
}
_converse.attemptNonPreboundSession(credentials, reconnecting);
}, },
/** /**
@ -1810,31 +1673,6 @@ _converse.api = {
} }
}, },
/**
* This namespace lets you access the BOSH tokens
*
* @namespace _converse.api.tokens
* @memberOf _converse.api
*/
tokens: {
/**
* @method _converse.api.tokens.get
* @param {string} [id] The type of token to return ('rid' or 'sid').
* @returns 'string' A token, either the RID or SID token depending on what's asked for.
* @example _converse.api.tokens.get('rid');
*/
get (id) {
if (!_converse.expose_rid_and_sid || _.isUndefined(_converse.connection)) {
return null;
}
if (id.toLowerCase() === 'rid') {
return _converse.connection.rid || _converse.connection._proto.rid;
} else if (id.toLowerCase() === 'sid') {
return _converse.connection.sid || _converse.connection._proto.sid;
}
}
},
/** /**
* Converse emits events to which you can subscribe to. * Converse emits events to which you can subscribe to.
* *
@ -1986,9 +1824,7 @@ const converse = {
* bosh_service_url: 'https://bind.example.com', * bosh_service_url: 'https://bind.example.com',
* hide_muc_server: false, * hide_muc_server: false,
* i18n: locales['en'], * i18n: locales['en'],
* keepalive: true,
* play_sounds: true, * play_sounds: true,
* prebind: false,
* show_controlbox_by_default: true, * show_controlbox_by_default: true,
* debug: false, * debug: false,
* roster_groups: true * roster_groups: true

View File

@ -192,7 +192,7 @@ converse.plugins.add('converse-smacks', {
_converse.connection.addHandler(sendAck, Strophe.NS.SM, 'r'); _converse.connection.addHandler(sendAck, Strophe.NS.SM, 'r');
_converse.connection.addHandler(handleAck, Strophe.NS.SM, 'a'); _converse.connection.addHandler(handleAck, Strophe.NS.SM, 'a');
if (_converse.connection._proto instanceof Strophe.Bosh && if (_converse.api.connection.isType('bosh') &&
_converse.connfeedback.get('connection_status') === Strophe.Status.ATTACHED) { _converse.connfeedback.get('connection_status') === Strophe.Status.ATTACHED) {
// No need to continue further when we have an existing BOSH session, // No need to continue further when we have an existing BOSH session,
// since our existing session still exists server-side. // since our existing session still exists server-side.

View File

@ -3,6 +3,7 @@
* Any of the following components may be removed if they're not needed. * Any of the following components may be removed if they're not needed.
*/ */
import "./converse-bookmarks"; // XEP-0199 XMPP Ping import "./converse-bookmarks"; // XEP-0199 XMPP Ping
import "./converse-bosh"; // XEP-0115 Entity Capabilities
import "./converse-caps"; // XEP-0115 Entity Capabilities import "./converse-caps"; // XEP-0115 Entity Capabilities
import "./converse-chatboxes"; // Backbone Collection and Models for chat boxes import "./converse-chatboxes"; // Backbone Collection and Models for chat boxes
import "./converse-disco"; // XEP-0030 Service discovery import "./converse-disco"; // XEP-0030 Service discovery

View File

@ -87,6 +87,9 @@ u.isValidMUCJID = function (jid) {
}; };
u.isSameBareJID = function (jid1, jid2) { u.isSameBareJID = function (jid1, jid2) {
if (!_.isString(jid1) || !_.isString(jid2)) {
return false;
}
return Strophe.getBareJidFromJid(jid1).toLowerCase() === return Strophe.getBareJidFromJid(jid1).toLowerCase() ===
Strophe.getBareJidFromJid(jid2).toLowerCase(); Strophe.getBareJidFromJid(jid2).toLowerCase();
}; };