Bladeren bron

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.
JC Brand 6 jaren geleden
bovenliggende
commit
38a232fd45

+ 5 - 0
CHANGES.md

@@ -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
 
 

+ 24 - 76
docs/source/configuration.rst

@@ -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``.
-They are :ref:`keepalive` and `prebind_url`_.
-
-``keepalive`` can be used keep the session alive without having to pass in
-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.
+In addition to setting ``authentication`` to ``prebind``, you'll also need to
+set the `prebind_url`_ and `bosh-service-url`_.
 
 
-Here's an example of Converse being initialized with these three options:
+Here's an example of Converse being initialized with these 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
-federated server), while setting it to `false` means that people not on your
+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
 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
-also specified a `prebind_url` URL, from where Converse can fetch the BOSH
+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
 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,36 +726,9 @@ 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
-with `prebind`_.
-
-
-.. _`keepalive`:
+This value may be provided together with a ``password`` instead of supplying a
+`credentials_url`_ when setting ``auto_login`` to ``true``.
 
 
-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`:
 
 
@@ -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
-description of time-format options available for `DayJS` you can check the
+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
 `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`:
 
 

+ 0 - 1
docs/source/other_frameworks.rst

@@ -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']
             });
             });

+ 3 - 6
docs/source/setup.rst

@@ -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:
-  `Overcoming cross-domain request restrictions`_
+  restrictions in the browser.
 
 
 * 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
-way of the :ref:`keepalive` setting.
+Once a user is logged in, the session will be kept alive across page loads.
 
 
 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
-:ref:`prebind_url` settings.
+You'll need to configure Converse with the :ref:`prebind` :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.

+ 0 - 37
spec/converse.js

@@ -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);

+ 143 - 0
src/headless/converse-bosh.js

@@ -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 ************************/
+    }
+});

+ 31 - 195
src/headless/converse-core.js

@@ -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
-     * option), we don't need to again send out a presence stanza, because
-     * it's as if "we never left" (see onConnectStatusChanged).
+     * When re-attaching to an existing session we don't need to again send out a presence stanza,
+     * because 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) {
-            if (_converse.authentication === _converse.PREBIND) {
-                _converse.attemptPreboundSession(reconnecting);
-            } else {
-                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 };
+        async login (jid, password, reconnecting) {
+            if (_converse.api.connection.isType('bosh')) {
+                if (reconnecting && _converse.prebind_url) {
+                    return _converse.startNewBOSHSession();
+                } else if (await _converse.restoreBOSHSession()) {
+                    return;
                 }
                 }
-                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

+ 1 - 1
src/headless/converse-smacks.js

@@ -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.

+ 1 - 0
src/headless/headless.js

@@ -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

+ 3 - 0
src/headless/utils/core.js

@@ -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();
 };
 };