quickblox 2.17.2-logger → 2.17.3-logger

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/package.json +1 -2
  2. package/quickblox.js +36560 -38627
  3. package/quickblox.min.js +1 -1
  4. package/src/libs/strophe/strophe.common.js +6657 -0
  5. package/src/libs/strophe/strophe.esm.js +6649 -0
  6. package/src/libs/strophe/strophe.umd.js +6862 -0
  7. package/src/libs/strophe/strophe.umd.min.js +1 -0
  8. package/src/modules/chat/qbChat.js +9 -0
  9. package/src/modules/webrtc/qbWebRTCSignalingProcessor.js +2 -1
  10. package/src/modules/webrtc/qbWebRTCSignalingProvider.js +2 -2
  11. package/src/qbConfig.js +1 -1
  12. package/src/qbStrophe.js +2 -1
  13. package/strophejs-1.4.0/.eslintrc.json +264 -0
  14. package/strophejs-1.4.0/.gitattributes +1 -0
  15. package/strophejs-1.4.0/CHANGELOG.md +250 -0
  16. package/strophejs-1.4.0/LICENSE.txt +19 -0
  17. package/strophejs-1.4.0/Makefile +92 -0
  18. package/strophejs-1.4.0/README.md +45 -0
  19. package/strophejs-1.4.0/RELEASE_CHECKLIST.md +16 -0
  20. package/strophejs-1.4.0/contrib/discojs/README.txt +42 -0
  21. package/strophejs-1.4.0/contrib/discojs/css/disco.css +16 -0
  22. package/strophejs-1.4.0/contrib/discojs/index.html +47 -0
  23. package/strophejs-1.4.0/contrib/discojs/punjab.tac +18 -0
  24. package/strophejs-1.4.0/contrib/discojs/scripts/basic.js +102 -0
  25. package/strophejs-1.4.0/contrib/discojs/scripts/disco.js +60 -0
  26. package/strophejs-1.4.0/docs.css +797 -0
  27. package/strophejs-1.4.0/examples/amd.html +21 -0
  28. package/strophejs-1.4.0/examples/attach/README +37 -0
  29. package/strophejs-1.4.0/examples/attach/__init__.py +0 -0
  30. package/strophejs-1.4.0/examples/attach/attacher/__init__.py +0 -0
  31. package/strophejs-1.4.0/examples/attach/attacher/views.py +18 -0
  32. package/strophejs-1.4.0/examples/attach/boshclient.py +158 -0
  33. package/strophejs-1.4.0/examples/attach/manage.py +11 -0
  34. package/strophejs-1.4.0/examples/attach/settings.py +85 -0
  35. package/strophejs-1.4.0/examples/attach/templates/attacher/index.html +88 -0
  36. package/strophejs-1.4.0/examples/attach/urls.py +19 -0
  37. package/strophejs-1.4.0/examples/basic.html +23 -0
  38. package/strophejs-1.4.0/examples/basic.js +73 -0
  39. package/strophejs-1.4.0/examples/echobot.html +25 -0
  40. package/strophejs-1.4.0/examples/echobot.js +79 -0
  41. package/strophejs-1.4.0/examples/main.js +59 -0
  42. package/strophejs-1.4.0/examples/prebind.html +39 -0
  43. package/strophejs-1.4.0/examples/prebind.js +103 -0
  44. package/strophejs-1.4.0/examples/restore.html +24 -0
  45. package/strophejs-1.4.0/examples/restore.js +71 -0
  46. package/strophejs-1.4.0/package-lock.json +8631 -0
  47. package/strophejs-1.4.0/package.json +84 -0
  48. package/strophejs-1.4.0/rollup.config.js +76 -0
  49. package/strophejs-1.4.0/src/bosh.js +916 -0
  50. package/strophejs-1.4.0/src/core.js +3530 -0
  51. package/strophejs-1.4.0/src/md5.js +204 -0
  52. package/strophejs-1.4.0/src/sha1.js +172 -0
  53. package/strophejs-1.4.0/src/shared-connection-worker.js +114 -0
  54. package/strophejs-1.4.0/src/shims.js +123 -0
  55. package/strophejs-1.4.0/src/strophe.js +14 -0
  56. package/strophejs-1.4.0/src/utils.js +63 -0
  57. package/strophejs-1.4.0/src/websocket.js +557 -0
  58. package/strophejs-1.4.0/src/worker-websocket.js +150 -0
  59. package/strophejs-1.4.0/tests/index.html +21 -0
  60. package/strophejs-1.4.0/tests/main.js +49 -0
  61. package/strophejs-1.4.0/tests/tests.js +929 -0
  62. package/strophejs-1.6.1/.eslintrc.json +264 -0
  63. package/strophejs-1.6.1/.gitattributes +1 -0
  64. package/strophejs-1.6.1/.nvmrc +1 -0
  65. package/strophejs-1.6.1/CHANGELOG.md +288 -0
  66. package/strophejs-1.6.1/LICENSE.txt +19 -0
  67. package/strophejs-1.6.1/Makefile +92 -0
  68. package/strophejs-1.6.1/README.md +46 -0
  69. package/strophejs-1.6.1/RELEASE_CHECKLIST.md +18 -0
  70. package/strophejs-1.6.1/babel.config.json +10 -0
  71. package/strophejs-1.6.1/contrib/discojs/README.txt +42 -0
  72. package/strophejs-1.6.1/contrib/discojs/css/disco.css +16 -0
  73. package/strophejs-1.6.1/contrib/discojs/index.html +47 -0
  74. package/strophejs-1.6.1/contrib/discojs/punjab.tac +18 -0
  75. package/strophejs-1.6.1/contrib/discojs/scripts/basic.js +102 -0
  76. package/strophejs-1.6.1/contrib/discojs/scripts/disco.js +60 -0
  77. package/strophejs-1.6.1/docs.css +797 -0
  78. package/strophejs-1.6.1/examples/amd.html +21 -0
  79. package/strophejs-1.6.1/examples/attach/README +37 -0
  80. package/strophejs-1.6.1/examples/attach/__init__.py +0 -0
  81. package/strophejs-1.6.1/examples/attach/attacher/__init__.py +0 -0
  82. package/strophejs-1.6.1/examples/attach/attacher/views.py +18 -0
  83. package/strophejs-1.6.1/examples/attach/boshclient.py +158 -0
  84. package/strophejs-1.6.1/examples/attach/manage.py +11 -0
  85. package/strophejs-1.6.1/examples/attach/settings.py +85 -0
  86. package/strophejs-1.6.1/examples/attach/templates/attacher/index.html +88 -0
  87. package/strophejs-1.6.1/examples/attach/urls.py +19 -0
  88. package/strophejs-1.6.1/examples/basic.html +23 -0
  89. package/strophejs-1.6.1/examples/basic.js +73 -0
  90. package/strophejs-1.6.1/examples/echobot.html +25 -0
  91. package/strophejs-1.6.1/examples/echobot.js +79 -0
  92. package/strophejs-1.6.1/examples/main.js +59 -0
  93. package/strophejs-1.6.1/examples/prebind.html +39 -0
  94. package/strophejs-1.6.1/examples/prebind.js +103 -0
  95. package/strophejs-1.6.1/examples/restore.html +24 -0
  96. package/strophejs-1.6.1/examples/restore.js +71 -0
  97. package/strophejs-1.6.1/package-lock.json +18461 -0
  98. package/strophejs-1.6.1/package.json +87 -0
  99. package/strophejs-1.6.1/rollup.config.js +70 -0
  100. package/strophejs-1.6.1/src/bosh.js +916 -0
  101. package/strophejs-1.6.1/src/builder.js +239 -0
  102. package/strophejs-1.6.1/src/constants.js +155 -0
  103. package/strophejs-1.6.1/src/core.js +2377 -0
  104. package/strophejs-1.6.1/src/sasl-anon.js +17 -0
  105. package/strophejs-1.6.1/src/sasl-external.js +27 -0
  106. package/strophejs-1.6.1/src/sasl-oauthbearer.js +30 -0
  107. package/strophejs-1.6.1/src/sasl-plain.js +32 -0
  108. package/strophejs-1.6.1/src/sasl-sha1.js +24 -0
  109. package/strophejs-1.6.1/src/sasl-sha256.js +24 -0
  110. package/strophejs-1.6.1/src/sasl-sha384.js +24 -0
  111. package/strophejs-1.6.1/src/sasl-sha512.js +24 -0
  112. package/strophejs-1.6.1/src/sasl-xoauth2.js +27 -0
  113. package/strophejs-1.6.1/src/sasl.js +143 -0
  114. package/strophejs-1.6.1/src/scram.js +182 -0
  115. package/strophejs-1.6.1/src/shared-connection-worker.js +114 -0
  116. package/strophejs-1.6.1/src/shims.js +122 -0
  117. package/strophejs-1.6.1/src/strophe.js +15 -0
  118. package/strophejs-1.6.1/src/utils.js +626 -0
  119. package/strophejs-1.6.1/src/websocket.js +556 -0
  120. package/strophejs-1.6.1/src/worker-websocket.js +149 -0
  121. package/strophejs-1.6.1/tests.js +993 -0
@@ -0,0 +1,3530 @@
1
+ /*
2
+ This program is distributed under the terms of the MIT license.
3
+ Please see the LICENSE file for details.
4
+
5
+ Copyright 2006-2018, OGG, LLC
6
+ */
7
+ /*global define, document, sessionStorage, setTimeout, clearTimeout, ActiveXObject, DOMParser, btoa, atob */
8
+
9
+ import * as shims from './shims';
10
+ import MD5 from './md5';
11
+ import SHA1 from './sha1';
12
+ import utils from './utils';
13
+ import { atob, btoa } from 'abab'
14
+
15
+ /** Function: $build
16
+ * Create a Strophe.Builder.
17
+ * This is an alias for 'new Strophe.Builder(name, attrs)'.
18
+ *
19
+ * Parameters:
20
+ * (String) name - The root element name.
21
+ * (Object) attrs - The attributes for the root element in object notation.
22
+ *
23
+ * Returns:
24
+ * A new Strophe.Builder object.
25
+ */
26
+ export function $build(name, attrs) {
27
+ return new Strophe.Builder(name, attrs);
28
+ }
29
+
30
+ /** Function: $msg
31
+ * Create a Strophe.Builder with a <message/> element as the root.
32
+ *
33
+ * Parameters:
34
+ * (Object) attrs - The <message/> element attributes in object notation.
35
+ *
36
+ * Returns:
37
+ * A new Strophe.Builder object.
38
+ */
39
+ export function $msg(attrs) {
40
+ return new Strophe.Builder("message", attrs);
41
+ }
42
+
43
+ /** Function: $iq
44
+ * Create a Strophe.Builder with an <iq/> element as the root.
45
+ *
46
+ * Parameters:
47
+ * (Object) attrs - The <iq/> element attributes in object notation.
48
+ *
49
+ * Returns:
50
+ * A new Strophe.Builder object.
51
+ */
52
+ export function $iq(attrs) {
53
+ return new Strophe.Builder("iq", attrs);
54
+ }
55
+
56
+ /** Function: $pres
57
+ * Create a Strophe.Builder with a <presence/> element as the root.
58
+ *
59
+ * Parameters:
60
+ * (Object) attrs - The <presence/> element attributes in object notation.
61
+ *
62
+ * Returns:
63
+ * A new Strophe.Builder object.
64
+ */
65
+ export function $pres(attrs) {
66
+ return new Strophe.Builder("presence", attrs);
67
+ }
68
+
69
+ /** Class: Strophe
70
+ * An object container for all Strophe library functions.
71
+ *
72
+ * This class is just a container for all the objects and constants
73
+ * used in the library. It is not meant to be instantiated, but to
74
+ * provide a namespace for library objects, constants, and functions.
75
+ */
76
+ export const Strophe = {
77
+ /** Constant: VERSION */
78
+ VERSION: "1.4.0",
79
+
80
+ /** Constants: XMPP Namespace Constants
81
+ * Common namespace constants from the XMPP RFCs and XEPs.
82
+ *
83
+ * NS.HTTPBIND - HTTP BIND namespace from XEP 124.
84
+ * NS.BOSH - BOSH namespace from XEP 206.
85
+ * NS.CLIENT - Main XMPP client namespace.
86
+ * NS.AUTH - Legacy authentication namespace.
87
+ * NS.ROSTER - Roster operations namespace.
88
+ * NS.PROFILE - Profile namespace.
89
+ * NS.DISCO_INFO - Service discovery info namespace from XEP 30.
90
+ * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30.
91
+ * NS.MUC - Multi-User Chat namespace from XEP 45.
92
+ * NS.SASL - XMPP SASL namespace from RFC 3920.
93
+ * NS.STREAM - XMPP Streams namespace from RFC 3920.
94
+ * NS.BIND - XMPP Binding namespace from RFC 3920 and RFC 6120.
95
+ * NS.SESSION - XMPP Session namespace from RFC 3920.
96
+ * NS.XHTML_IM - XHTML-IM namespace from XEP 71.
97
+ * NS.XHTML - XHTML body namespace from XEP 71.
98
+ */
99
+ NS: {
100
+ HTTPBIND: "http://jabber.org/protocol/httpbind",
101
+ BOSH: "urn:xmpp:xbosh",
102
+ CLIENT: "jabber:client",
103
+ AUTH: "jabber:iq:auth",
104
+ ROSTER: "jabber:iq:roster",
105
+ PROFILE: "jabber:iq:profile",
106
+ DISCO_INFO: "http://jabber.org/protocol/disco#info",
107
+ DISCO_ITEMS: "http://jabber.org/protocol/disco#items",
108
+ MUC: "http://jabber.org/protocol/muc",
109
+ SASL: "urn:ietf:params:xml:ns:xmpp-sasl",
110
+ STREAM: "http://etherx.jabber.org/streams",
111
+ FRAMING: "urn:ietf:params:xml:ns:xmpp-framing",
112
+ BIND: "urn:ietf:params:xml:ns:xmpp-bind",
113
+ SESSION: "urn:ietf:params:xml:ns:xmpp-session",
114
+ VERSION: "jabber:iq:version",
115
+ STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas",
116
+ XHTML_IM: "http://jabber.org/protocol/xhtml-im",
117
+ XHTML: "http://www.w3.org/1999/xhtml"
118
+ },
119
+
120
+ /** Constants: XHTML_IM Namespace
121
+ * contains allowed tags, tag attributes, and css properties.
122
+ * Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset.
123
+ * See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended
124
+ * allowed tags and their attributes.
125
+ */
126
+ XHTML: {
127
+ tags: ['a','blockquote','br','cite','em','img','li','ol','p','span','strong','ul','body'],
128
+ attributes: {
129
+ 'a': ['href'],
130
+ 'blockquote': ['style'],
131
+ 'br': [],
132
+ 'cite': ['style'],
133
+ 'em': [],
134
+ 'img': ['src', 'alt', 'style', 'height', 'width'],
135
+ 'li': ['style'],
136
+ 'ol': ['style'],
137
+ 'p': ['style'],
138
+ 'span': ['style'],
139
+ 'strong': [],
140
+ 'ul': ['style'],
141
+ 'body': []
142
+ },
143
+ css: ['background-color','color','font-family','font-size','font-style','font-weight','margin-left','margin-right','text-align','text-decoration'],
144
+ /** Function: XHTML.validTag
145
+ *
146
+ * Utility method to determine whether a tag is allowed
147
+ * in the XHTML_IM namespace.
148
+ *
149
+ * XHTML tag names are case sensitive and must be lower case.
150
+ */
151
+ validTag (tag) {
152
+ for (let i=0; i<Strophe.XHTML.tags.length; i++) {
153
+ if (tag === Strophe.XHTML.tags[i]) {
154
+ return true;
155
+ }
156
+ }
157
+ return false;
158
+ },
159
+ /** Function: XHTML.validAttribute
160
+ *
161
+ * Utility method to determine whether an attribute is allowed
162
+ * as recommended per XEP-0071
163
+ *
164
+ * XHTML attribute names are case sensitive and must be lower case.
165
+ */
166
+ validAttribute (tag, attribute) {
167
+ if (typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) {
168
+ for (let i=0; i<Strophe.XHTML.attributes[tag].length; i++) {
169
+ if (attribute === Strophe.XHTML.attributes[tag][i]) {
170
+ return true;
171
+ }
172
+ }
173
+ }
174
+ return false;
175
+ },
176
+ validCSS (style) {
177
+ for (let i=0; i<Strophe.XHTML.css.length; i++) {
178
+ if (style === Strophe.XHTML.css[i]) {
179
+ return true;
180
+ }
181
+ }
182
+ return false;
183
+ }
184
+ },
185
+
186
+ /** Constants: Connection Status Constants
187
+ * Connection status constants for use by the connection handler
188
+ * callback.
189
+ *
190
+ * Status.ERROR - An error has occurred
191
+ * Status.CONNECTING - The connection is currently being made
192
+ * Status.CONNFAIL - The connection attempt failed
193
+ * Status.AUTHENTICATING - The connection is authenticating
194
+ * Status.AUTHFAIL - The authentication attempt failed
195
+ * Status.CONNECTED - The connection has succeeded
196
+ * Status.DISCONNECTED - The connection has been terminated
197
+ * Status.DISCONNECTING - The connection is currently being terminated
198
+ * Status.ATTACHED - The connection has been attached
199
+ * Status.REDIRECT - The connection has been redirected
200
+ * Status.CONNTIMEOUT - The connection has timed out
201
+ */
202
+ Status: {
203
+ ERROR: 0,
204
+ CONNECTING: 1,
205
+ CONNFAIL: 2,
206
+ AUTHENTICATING: 3,
207
+ AUTHFAIL: 4,
208
+ CONNECTED: 5,
209
+ DISCONNECTED: 6,
210
+ DISCONNECTING: 7,
211
+ ATTACHED: 8,
212
+ REDIRECT: 9,
213
+ CONNTIMEOUT: 10,
214
+ BINDREQUIRED: 11,
215
+ ATTACHFAIL: 12
216
+ },
217
+
218
+ ErrorCondition: {
219
+ BAD_FORMAT: "bad-format",
220
+ CONFLICT: "conflict",
221
+ MISSING_JID_NODE: "x-strophe-bad-non-anon-jid",
222
+ NO_AUTH_MECH: "no-auth-mech",
223
+ UNKNOWN_REASON: "unknown",
224
+ },
225
+
226
+ /** Constants: Log Level Constants
227
+ * Logging level indicators.
228
+ *
229
+ * LogLevel.DEBUG - Debug output
230
+ * LogLevel.INFO - Informational output
231
+ * LogLevel.WARN - Warnings
232
+ * LogLevel.ERROR - Errors
233
+ * LogLevel.FATAL - Fatal errors
234
+ */
235
+ LogLevel: {
236
+ DEBUG: 0,
237
+ INFO: 1,
238
+ WARN: 2,
239
+ ERROR: 3,
240
+ FATAL: 4
241
+ },
242
+
243
+ /** PrivateConstants: DOM Element Type Constants
244
+ * DOM element types.
245
+ *
246
+ * ElementType.NORMAL - Normal element.
247
+ * ElementType.TEXT - Text data element.
248
+ * ElementType.FRAGMENT - XHTML fragment element.
249
+ */
250
+ ElementType: {
251
+ NORMAL: 1,
252
+ TEXT: 3,
253
+ CDATA: 4,
254
+ FRAGMENT: 11
255
+ },
256
+
257
+ /** PrivateConstants: Timeout Values
258
+ * Timeout values for error states. These values are in seconds.
259
+ * These should not be changed unless you know exactly what you are
260
+ * doing.
261
+ *
262
+ * TIMEOUT - Timeout multiplier. A waiting request will be considered
263
+ * failed after Math.floor(TIMEOUT * wait) seconds have elapsed.
264
+ * This defaults to 1.1, and with default wait, 66 seconds.
265
+ * SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where
266
+ * Strophe can detect early failure, it will consider the request
267
+ * failed if it doesn't return after
268
+ * Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed.
269
+ * This defaults to 0.1, and with default wait, 6 seconds.
270
+ */
271
+ TIMEOUT: 1.1,
272
+ SECONDARY_TIMEOUT: 0.1,
273
+
274
+ /** Function: addNamespace
275
+ * This function is used to extend the current namespaces in
276
+ * Strophe.NS. It takes a key and a value with the key being the
277
+ * name of the new namespace, with its actual value.
278
+ * For example:
279
+ * Strophe.addNamespace('PUBSUB', "http://jabber.org/protocol/pubsub");
280
+ *
281
+ * Parameters:
282
+ * (String) name - The name under which the namespace will be
283
+ * referenced under Strophe.NS
284
+ * (String) value - The actual namespace.
285
+ */
286
+ addNamespace (name, value) {
287
+ Strophe.NS[name] = value;
288
+ },
289
+
290
+ /** Function: forEachChild
291
+ * Map a function over some or all child elements of a given element.
292
+ *
293
+ * This is a small convenience function for mapping a function over
294
+ * some or all of the children of an element. If elemName is null, all
295
+ * children will be passed to the function, otherwise only children
296
+ * whose tag names match elemName will be passed.
297
+ *
298
+ * Parameters:
299
+ * (XMLElement) elem - The element to operate on.
300
+ * (String) elemName - The child element tag name filter.
301
+ * (Function) func - The function to apply to each child. This
302
+ * function should take a single argument, a DOM element.
303
+ */
304
+ forEachChild (elem, elemName, func) {
305
+ for (let i=0; i<elem.childNodes.length; i++) {
306
+ const childNode = elem.childNodes[i];
307
+ if (childNode.nodeType === Strophe.ElementType.NORMAL &&
308
+ (!elemName || this.isTagEqual(childNode, elemName))) {
309
+ func(childNode);
310
+ }
311
+ }
312
+ },
313
+
314
+ /** Function: isTagEqual
315
+ * Compare an element's tag name with a string.
316
+ *
317
+ * This function is case sensitive.
318
+ *
319
+ * Parameters:
320
+ * (XMLElement) el - A DOM element.
321
+ * (String) name - The element name.
322
+ *
323
+ * Returns:
324
+ * true if the element's tag name matches _el_, and false
325
+ * otherwise.
326
+ */
327
+ isTagEqual (el, name) {
328
+ return el.tagName === name;
329
+ },
330
+
331
+ /** PrivateVariable: _xmlGenerator
332
+ * _Private_ variable that caches a DOM document to
333
+ * generate elements.
334
+ */
335
+ _xmlGenerator: null,
336
+
337
+ /** Function: xmlGenerator
338
+ * Get the DOM document to generate elements.
339
+ *
340
+ * Returns:
341
+ * The currently used DOM document.
342
+ */
343
+ xmlGenerator () {
344
+ if (!Strophe._xmlGenerator) {
345
+ Strophe._xmlGenerator = shims.getDummyXMLDOMDocument()
346
+ }
347
+ return Strophe._xmlGenerator;
348
+ },
349
+
350
+ /** Function: xmlElement
351
+ * Create an XML DOM element.
352
+ *
353
+ * This function creates an XML DOM element correctly across all
354
+ * implementations. Note that these are not HTML DOM elements, which
355
+ * aren't appropriate for XMPP stanzas.
356
+ *
357
+ * Parameters:
358
+ * (String) name - The name for the element.
359
+ * (Array|Object) attrs - An optional array or object containing
360
+ * key/value pairs to use as element attributes. The object should
361
+ * be in the format {'key': 'value'} or {key: 'value'}. The array
362
+ * should have the format [['key1', 'value1'], ['key2', 'value2']].
363
+ * (String) text - The text child data for the element.
364
+ *
365
+ * Returns:
366
+ * A new XML DOM element.
367
+ */
368
+ xmlElement (name) {
369
+ if (!name) { return null; }
370
+
371
+ const node = Strophe.xmlGenerator().createElement(name);
372
+ // FIXME: this should throw errors if args are the wrong type or
373
+ // there are more than two optional args
374
+ for (let a=1; a<arguments.length; a++) {
375
+ const arg = arguments[a];
376
+ if (!arg) { continue; }
377
+ if (typeof(arg) === "string" ||
378
+ typeof(arg) === "number") {
379
+ node.appendChild(Strophe.xmlTextNode(arg));
380
+ } else if (typeof(arg) === "object" &&
381
+ typeof(arg.sort) === "function") {
382
+ for (let i=0; i<arg.length; i++) {
383
+ const attr = arg[i];
384
+ if (typeof(attr) === "object" &&
385
+ typeof(attr.sort) === "function" &&
386
+ attr[1] !== undefined &&
387
+ attr[1] !== null) {
388
+ node.setAttribute(attr[0], attr[1]);
389
+ }
390
+ }
391
+ } else if (typeof(arg) === "object") {
392
+ for (const k in arg) {
393
+ if (Object.prototype.hasOwnProperty.call(arg, k) && arg[k] !== undefined && arg[k] !== null) {
394
+ node.setAttribute(k, arg[k]);
395
+ }
396
+ }
397
+ }
398
+ }
399
+ return node;
400
+ },
401
+
402
+ /* Function: xmlescape
403
+ * Excapes invalid xml characters.
404
+ *
405
+ * Parameters:
406
+ * (String) text - text to escape.
407
+ *
408
+ * Returns:
409
+ * Escaped text.
410
+ */
411
+ xmlescape (text) {
412
+ text = text.replace(/\&/g, "&amp;");
413
+ text = text.replace(/</g, "&lt;");
414
+ text = text.replace(/>/g, "&gt;");
415
+ text = text.replace(/'/g, "&apos;");
416
+ text = text.replace(/"/g, "&quot;");
417
+ return text;
418
+ },
419
+
420
+ /* Function: xmlunescape
421
+ * Unexcapes invalid xml characters.
422
+ *
423
+ * Parameters:
424
+ * (String) text - text to unescape.
425
+ *
426
+ * Returns:
427
+ * Unescaped text.
428
+ */
429
+ xmlunescape (text) {
430
+ text = text.replace(/\&amp;/g, "&");
431
+ text = text.replace(/&lt;/g, "<");
432
+ text = text.replace(/&gt;/g, ">");
433
+ text = text.replace(/&apos;/g, "'");
434
+ text = text.replace(/&quot;/g, "\"");
435
+ return text;
436
+ },
437
+
438
+ /** Function: xmlTextNode
439
+ * Creates an XML DOM text node.
440
+ *
441
+ * Provides a cross implementation version of document.createTextNode.
442
+ *
443
+ * Parameters:
444
+ * (String) text - The content of the text node.
445
+ *
446
+ * Returns:
447
+ * A new XML DOM text node.
448
+ */
449
+ xmlTextNode (text) {
450
+ return Strophe.xmlGenerator().createTextNode(text);
451
+ },
452
+
453
+ /** Function: xmlHtmlNode
454
+ * Creates an XML DOM html node.
455
+ *
456
+ * Parameters:
457
+ * (String) html - The content of the html node.
458
+ *
459
+ * Returns:
460
+ * A new XML DOM text node.
461
+ */
462
+ xmlHtmlNode (html) {
463
+ let node;
464
+ //ensure text is escaped
465
+ if (shims.DOMParser) {
466
+ const parser = new shims.DOMParser();
467
+ node = parser.parseFromString(html, "text/xml");
468
+ } else {
469
+ node = new ActiveXObject("Microsoft.XMLDOM");
470
+ node.async="false";
471
+ node.loadXML(html);
472
+ }
473
+ return node;
474
+ },
475
+
476
+ /** Function: getText
477
+ * Get the concatenation of all text children of an element.
478
+ *
479
+ * Parameters:
480
+ * (XMLElement) elem - A DOM element.
481
+ *
482
+ * Returns:
483
+ * A String with the concatenated text of all text element children.
484
+ */
485
+ getText (elem) {
486
+ if (!elem) { return null; }
487
+
488
+ let str = "";
489
+ if (elem.childNodes.length === 0 && elem.nodeType === Strophe.ElementType.TEXT) {
490
+ str += elem.nodeValue;
491
+ }
492
+ for (let i=0; i<elem.childNodes.length; i++) {
493
+ if (elem.childNodes[i].nodeType === Strophe.ElementType.TEXT) {
494
+ str += elem.childNodes[i].nodeValue;
495
+ }
496
+ }
497
+ return Strophe.xmlescape(str);
498
+ },
499
+
500
+ /** Function: copyElement
501
+ * Copy an XML DOM element.
502
+ *
503
+ * This function copies a DOM element and all its descendants and returns
504
+ * the new copy.
505
+ *
506
+ * Parameters:
507
+ * (XMLElement) elem - A DOM element.
508
+ *
509
+ * Returns:
510
+ * A new, copied DOM element tree.
511
+ */
512
+ copyElement (elem) {
513
+ let el;
514
+ if (elem.nodeType === Strophe.ElementType.NORMAL) {
515
+ el = Strophe.xmlElement(elem.tagName);
516
+
517
+ for (let i=0; i<elem.attributes.length; i++) {
518
+ el.setAttribute(elem.attributes[i].nodeName,
519
+ elem.attributes[i].value);
520
+ }
521
+
522
+ for (let i=0; i<elem.childNodes.length; i++) {
523
+ el.appendChild(Strophe.copyElement(elem.childNodes[i]));
524
+ }
525
+ } else if (elem.nodeType === Strophe.ElementType.TEXT) {
526
+ el = Strophe.xmlGenerator().createTextNode(elem.nodeValue);
527
+ }
528
+ return el;
529
+ },
530
+
531
+
532
+ /** Function: createHtml
533
+ * Copy an HTML DOM element into an XML DOM.
534
+ *
535
+ * This function copies a DOM element and all its descendants and returns
536
+ * the new copy.
537
+ *
538
+ * Parameters:
539
+ * (HTMLElement) elem - A DOM element.
540
+ *
541
+ * Returns:
542
+ * A new, copied DOM element tree.
543
+ */
544
+ createHtml (elem) {
545
+ let el;
546
+ if (elem.nodeType === Strophe.ElementType.NORMAL) {
547
+ const tag = elem.nodeName.toLowerCase(); // XHTML tags must be lower case.
548
+ if (Strophe.XHTML.validTag(tag)) {
549
+ try {
550
+ el = Strophe.xmlElement(tag);
551
+ for (let i=0; i < Strophe.XHTML.attributes[tag].length; i++) {
552
+ const attribute = Strophe.XHTML.attributes[tag][i];
553
+ let value = elem.getAttribute(attribute);
554
+ if (typeof value === 'undefined' || value === null || value === '' || value === false || value === 0) {
555
+ continue;
556
+ }
557
+ if (attribute === 'style' && typeof value === 'object' && typeof value.cssText !== 'undefined') {
558
+ value = value.cssText; // we're dealing with IE, need to get CSS out
559
+ }
560
+ // filter out invalid css styles
561
+ if (attribute === 'style') {
562
+ const css = [];
563
+ const cssAttrs = value.split(';');
564
+ for (let j=0; j < cssAttrs.length; j++) {
565
+ const attr = cssAttrs[j].split(':');
566
+ const cssName = attr[0].replace(/^\s*/, "").replace(/\s*$/, "").toLowerCase();
567
+ if(Strophe.XHTML.validCSS(cssName)) {
568
+ const cssValue = attr[1].replace(/^\s*/, "").replace(/\s*$/, "");
569
+ css.push(cssName + ': ' + cssValue);
570
+ }
571
+ }
572
+ if (css.length > 0) {
573
+ value = css.join('; ');
574
+ el.setAttribute(attribute, value);
575
+ }
576
+ } else {
577
+ el.setAttribute(attribute, value);
578
+ }
579
+ }
580
+ for (let i=0; i < elem.childNodes.length; i++) {
581
+ el.appendChild(Strophe.createHtml(elem.childNodes[i]));
582
+ }
583
+ } catch(e) { // invalid elements
584
+ el = Strophe.xmlTextNode('');
585
+ }
586
+ } else {
587
+ el = Strophe.xmlGenerator().createDocumentFragment();
588
+ for (let i=0; i < elem.childNodes.length; i++) {
589
+ el.appendChild(Strophe.createHtml(elem.childNodes[i]));
590
+ }
591
+ }
592
+ } else if (elem.nodeType === Strophe.ElementType.FRAGMENT) {
593
+ el = Strophe.xmlGenerator().createDocumentFragment();
594
+ for (let i=0; i < elem.childNodes.length; i++) {
595
+ el.appendChild(Strophe.createHtml(elem.childNodes[i]));
596
+ }
597
+ } else if (elem.nodeType === Strophe.ElementType.TEXT) {
598
+ el = Strophe.xmlTextNode(elem.nodeValue);
599
+ }
600
+ return el;
601
+ },
602
+
603
+ /** Function: escapeNode
604
+ * Escape the node part (also called local part) of a JID.
605
+ *
606
+ * Parameters:
607
+ * (String) node - A node (or local part).
608
+ *
609
+ * Returns:
610
+ * An escaped node (or local part).
611
+ */
612
+ escapeNode (node) {
613
+ if (typeof node !== "string") { return node; }
614
+ return node.replace(/^\s+|\s+$/g, '')
615
+ .replace(/\\/g, "\\5c")
616
+ .replace(/ /g, "\\20")
617
+ .replace(/\"/g, "\\22")
618
+ .replace(/\&/g, "\\26")
619
+ .replace(/\'/g, "\\27")
620
+ .replace(/\//g, "\\2f")
621
+ .replace(/:/g, "\\3a")
622
+ .replace(/</g, "\\3c")
623
+ .replace(/>/g, "\\3e")
624
+ .replace(/@/g, "\\40");
625
+ },
626
+
627
+ /** Function: unescapeNode
628
+ * Unescape a node part (also called local part) of a JID.
629
+ *
630
+ * Parameters:
631
+ * (String) node - A node (or local part).
632
+ *
633
+ * Returns:
634
+ * An unescaped node (or local part).
635
+ */
636
+ unescapeNode (node) {
637
+ if (typeof node !== "string") { return node; }
638
+ return node.replace(/\\20/g, " ")
639
+ .replace(/\\22/g, '"')
640
+ .replace(/\\26/g, "&")
641
+ .replace(/\\27/g, "'")
642
+ .replace(/\\2f/g, "/")
643
+ .replace(/\\3a/g, ":")
644
+ .replace(/\\3c/g, "<")
645
+ .replace(/\\3e/g, ">")
646
+ .replace(/\\40/g, "@")
647
+ .replace(/\\5c/g, "\\");
648
+ },
649
+
650
+ /** Function: getNodeFromJid
651
+ * Get the node portion of a JID String.
652
+ *
653
+ * Parameters:
654
+ * (String) jid - A JID.
655
+ *
656
+ * Returns:
657
+ * A String containing the node.
658
+ */
659
+ getNodeFromJid (jid) {
660
+ if (jid.indexOf("@") < 0) { return null; }
661
+ return jid.split("@")[0];
662
+ },
663
+
664
+ /** Function: getDomainFromJid
665
+ * Get the domain portion of a JID String.
666
+ *
667
+ * Parameters:
668
+ * (String) jid - A JID.
669
+ *
670
+ * Returns:
671
+ * A String containing the domain.
672
+ */
673
+ getDomainFromJid (jid) {
674
+ const bare = Strophe.getBareJidFromJid(jid);
675
+ if (bare.indexOf("@") < 0) {
676
+ return bare;
677
+ } else {
678
+ const parts = bare.split("@");
679
+ parts.splice(0, 1);
680
+ return parts.join('@');
681
+ }
682
+ },
683
+
684
+ /** Function: getResourceFromJid
685
+ * Get the resource portion of a JID String.
686
+ *
687
+ * Parameters:
688
+ * (String) jid - A JID.
689
+ *
690
+ * Returns:
691
+ * A String containing the resource.
692
+ */
693
+ getResourceFromJid (jid) {
694
+ if (!jid) { return null; }
695
+ const s = jid.split("/");
696
+ if (s.length < 2) { return null; }
697
+ s.splice(0, 1);
698
+ return s.join('/');
699
+ },
700
+
701
+ /** Function: getBareJidFromJid
702
+ * Get the bare JID from a JID String.
703
+ *
704
+ * Parameters:
705
+ * (String) jid - A JID.
706
+ *
707
+ * Returns:
708
+ * A String containing the bare JID.
709
+ */
710
+ getBareJidFromJid (jid) {
711
+ return jid ? jid.split("/")[0] : null;
712
+ },
713
+
714
+ /** PrivateFunction: _handleError
715
+ * _Private_ function that properly logs an error to the console
716
+ */
717
+ _handleError (e) {
718
+ if (typeof e.stack !== "undefined") {
719
+ Strophe.fatal(e.stack);
720
+ }
721
+ if (e.sourceURL) {
722
+ Strophe.fatal("error: " + this.handler + " " + e.sourceURL + ":" +
723
+ e.line + " - " + e.name + ": " + e.message);
724
+ } else if (e.fileName) {
725
+ Strophe.fatal("error: " + this.handler + " " +
726
+ e.fileName + ":" + e.lineNumber + " - " +
727
+ e.name + ": " + e.message);
728
+ } else {
729
+ Strophe.fatal("error: " + e.message);
730
+ }
731
+ },
732
+
733
+ /** Function: log
734
+ * User overrideable logging function.
735
+ *
736
+ * This function is called whenever the Strophe library calls any
737
+ * of the logging functions. The default implementation of this
738
+ * function logs only fatal errors. If client code wishes to handle the logging
739
+ * messages, it should override this with
740
+ * > Strophe.log = function (level, msg) {
741
+ * > (user code here)
742
+ * > };
743
+ *
744
+ * Please note that data sent and received over the wire is logged
745
+ * via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().
746
+ *
747
+ * The different levels and their meanings are
748
+ *
749
+ * DEBUG - Messages useful for debugging purposes.
750
+ * INFO - Informational messages. This is mostly information like
751
+ * 'disconnect was called' or 'SASL auth succeeded'.
752
+ * WARN - Warnings about potential problems. This is mostly used
753
+ * to report transient connection errors like request timeouts.
754
+ * ERROR - Some error occurred.
755
+ * FATAL - A non-recoverable fatal error occurred.
756
+ *
757
+ * Parameters:
758
+ * (Integer) level - The log level of the log message. This will
759
+ * be one of the values in Strophe.LogLevel.
760
+ * (String) msg - The log message.
761
+ */
762
+ log (level, msg) {
763
+ if (level === this.LogLevel.FATAL &&
764
+ typeof window.console === 'object' &&
765
+ typeof window.console.error === 'function') {
766
+ window.console.error(msg);
767
+ }
768
+ },
769
+
770
+ /** Function: debug
771
+ * Log a message at the Strophe.LogLevel.DEBUG level.
772
+ *
773
+ * Parameters:
774
+ * (String) msg - The log message.
775
+ */
776
+ debug (msg) {
777
+ this.log(this.LogLevel.DEBUG, msg);
778
+ },
779
+
780
+ /** Function: info
781
+ * Log a message at the Strophe.LogLevel.INFO level.
782
+ *
783
+ * Parameters:
784
+ * (String) msg - The log message.
785
+ */
786
+ info (msg) {
787
+ this.log(this.LogLevel.INFO, msg);
788
+ },
789
+
790
+ /** Function: warn
791
+ * Log a message at the Strophe.LogLevel.WARN level.
792
+ *
793
+ * Parameters:
794
+ * (String) msg - The log message.
795
+ */
796
+ warn (msg) {
797
+ this.log(this.LogLevel.WARN, msg);
798
+ },
799
+
800
+ /** Function: error
801
+ * Log a message at the Strophe.LogLevel.ERROR level.
802
+ *
803
+ * Parameters:
804
+ * (String) msg - The log message.
805
+ */
806
+ error (msg) {
807
+ this.log(this.LogLevel.ERROR, msg);
808
+ },
809
+
810
+ /** Function: fatal
811
+ * Log a message at the Strophe.LogLevel.FATAL level.
812
+ *
813
+ * Parameters:
814
+ * (String) msg - The log message.
815
+ */
816
+ fatal (msg) {
817
+ this.log(this.LogLevel.FATAL, msg);
818
+ },
819
+
820
+ /** Function: serialize
821
+ * Render a DOM element and all descendants to a String.
822
+ *
823
+ * Parameters:
824
+ * (XMLElement) elem - A DOM element.
825
+ *
826
+ * Returns:
827
+ * The serialized element tree as a String.
828
+ */
829
+ serialize (elem) {
830
+ if (!elem) { return null; }
831
+ if (typeof(elem.tree) === "function") {
832
+ elem = elem.tree();
833
+ }
834
+ const names = [...Array(elem.attributes.length).keys()].map(i => elem.attributes[i].nodeName);
835
+ names.sort();
836
+ let result = names.reduce(
837
+ (a, n) => `${a} ${n}="${Strophe.xmlescape(elem.attributes.getNamedItem(n).value)}"`,
838
+ `<${elem.nodeName}`
839
+ );
840
+
841
+ if (elem.childNodes.length > 0) {
842
+ result += ">";
843
+ for (let i=0; i < elem.childNodes.length; i++) {
844
+ const child = elem.childNodes[i];
845
+ switch (child.nodeType) {
846
+ case Strophe.ElementType.NORMAL:
847
+ // normal element, so recurse
848
+ result += Strophe.serialize(child);
849
+ break;
850
+ case Strophe.ElementType.TEXT:
851
+ // text element to escape values
852
+ result += Strophe.xmlescape(child.nodeValue);
853
+ break;
854
+ case Strophe.ElementType.CDATA:
855
+ // cdata section so don't escape values
856
+ result += "<![CDATA["+child.nodeValue+"]]>";
857
+ }
858
+ }
859
+ result += "</" + elem.nodeName + ">";
860
+ } else {
861
+ result += "/>";
862
+ }
863
+ return result;
864
+ },
865
+
866
+ /** PrivateVariable: _requestId
867
+ * _Private_ variable that keeps track of the request ids for
868
+ * connections.
869
+ */
870
+ _requestId: 0,
871
+
872
+ /** PrivateVariable: Strophe.connectionPlugins
873
+ * _Private_ variable Used to store plugin names that need
874
+ * initialization on Strophe.Connection construction.
875
+ */
876
+ _connectionPlugins: {},
877
+
878
+ /** Function: addConnectionPlugin
879
+ * Extends the Strophe.Connection object with the given plugin.
880
+ *
881
+ * Parameters:
882
+ * (String) name - The name of the extension.
883
+ * (Object) ptype - The plugin's prototype.
884
+ */
885
+ addConnectionPlugin (name, ptype) {
886
+ Strophe._connectionPlugins[name] = ptype;
887
+ }
888
+ };
889
+
890
+ /** Class: Strophe.Builder
891
+ * XML DOM builder.
892
+ *
893
+ * This object provides an interface similar to JQuery but for building
894
+ * DOM elements easily and rapidly. All the functions except for toString()
895
+ * and tree() return the object, so calls can be chained. Here's an
896
+ * example using the $iq() builder helper.
897
+ * > $iq({to: 'you', from: 'me', type: 'get', id: '1'})
898
+ * > .c('query', {xmlns: 'strophe:example'})
899
+ * > .c('example')
900
+ * > .toString()
901
+ *
902
+ * The above generates this XML fragment
903
+ * > <iq to='you' from='me' type='get' id='1'>
904
+ * > <query xmlns='strophe:example'>
905
+ * > <example/>
906
+ * > </query>
907
+ * > </iq>
908
+ * The corresponding DOM manipulations to get a similar fragment would be
909
+ * a lot more tedious and probably involve several helper variables.
910
+ *
911
+ * Since adding children makes new operations operate on the child, up()
912
+ * is provided to traverse up the tree. To add two children, do
913
+ * > builder.c('child1', ...).up().c('child2', ...)
914
+ * The next operation on the Builder will be relative to the second child.
915
+ */
916
+
917
+ /** Constructor: Strophe.Builder
918
+ * Create a Strophe.Builder object.
919
+ *
920
+ * The attributes should be passed in object notation. For example
921
+ * > let b = new Builder('message', {to: 'you', from: 'me'});
922
+ * or
923
+ * > let b = new Builder('messsage', {'xml:lang': 'en'});
924
+ *
925
+ * Parameters:
926
+ * (String) name - The name of the root element.
927
+ * (Object) attrs - The attributes for the root element in object notation.
928
+ *
929
+ * Returns:
930
+ * A new Strophe.Builder.
931
+ */
932
+
933
+ Strophe.Builder = class Builder {
934
+
935
+ constructor (name, attrs) {
936
+ // Set correct namespace for jabber:client elements
937
+ if (name === "presence" || name === "message" || name === "iq") {
938
+ if (attrs && !attrs.xmlns) {
939
+ attrs.xmlns = Strophe.NS.CLIENT;
940
+ } else if (!attrs) {
941
+ attrs = {xmlns: Strophe.NS.CLIENT};
942
+ }
943
+ }
944
+ // Holds the tree being built.
945
+ this.nodeTree = Strophe.xmlElement(name, attrs);
946
+ // Points to the current operation node.
947
+ this.node = this.nodeTree;
948
+ }
949
+
950
+ /** Function: tree
951
+ * Return the DOM tree.
952
+ *
953
+ * This function returns the current DOM tree as an element object. This
954
+ * is suitable for passing to functions like Strophe.Connection.send().
955
+ *
956
+ * Returns:
957
+ * The DOM tree as a element object.
958
+ */
959
+ tree () {
960
+ return this.nodeTree;
961
+ }
962
+
963
+ /** Function: toString
964
+ * Serialize the DOM tree to a String.
965
+ *
966
+ * This function returns a string serialization of the current DOM
967
+ * tree. It is often used internally to pass data to a
968
+ * Strophe.Request object.
969
+ *
970
+ * Returns:
971
+ * The serialized DOM tree in a String.
972
+ */
973
+ toString () {
974
+ return Strophe.serialize(this.nodeTree);
975
+ }
976
+
977
+ /** Function: up
978
+ * Make the current parent element the new current element.
979
+ *
980
+ * This function is often used after c() to traverse back up the tree.
981
+ * For example, to add two children to the same element
982
+ * > builder.c('child1', {}).up().c('child2', {});
983
+ *
984
+ * Returns:
985
+ * The Stophe.Builder object.
986
+ */
987
+ up () {
988
+ this.node = this.node.parentNode;
989
+ return this;
990
+ }
991
+
992
+ /** Function: root
993
+ * Make the root element the new current element.
994
+ *
995
+ * When at a deeply nested element in the tree, this function can be used
996
+ * to jump back to the root of the tree, instead of having to repeatedly
997
+ * call up().
998
+ *
999
+ * Returns:
1000
+ * The Stophe.Builder object.
1001
+ */
1002
+ root () {
1003
+ this.node = this.nodeTree;
1004
+ return this;
1005
+ }
1006
+
1007
+ /** Function: attrs
1008
+ * Add or modify attributes of the current element.
1009
+ *
1010
+ * The attributes should be passed in object notation. This function
1011
+ * does not move the current element pointer.
1012
+ *
1013
+ * Parameters:
1014
+ * (Object) moreattrs - The attributes to add/modify in object notation.
1015
+ *
1016
+ * Returns:
1017
+ * The Strophe.Builder object.
1018
+ */
1019
+ attrs (moreattrs) {
1020
+ for (const k in moreattrs) {
1021
+ if (Object.prototype.hasOwnProperty.call(moreattrs, k)) {
1022
+ if (moreattrs[k] === undefined) {
1023
+ this.node.removeAttribute(k);
1024
+ } else {
1025
+ this.node.setAttribute(k, moreattrs[k]);
1026
+ }
1027
+ }
1028
+ }
1029
+ return this;
1030
+ }
1031
+
1032
+ /** Function: c
1033
+ * Add a child to the current element and make it the new current
1034
+ * element.
1035
+ *
1036
+ * This function moves the current element pointer to the child,
1037
+ * unless text is provided. If you need to add another child, it
1038
+ * is necessary to use up() to go back to the parent in the tree.
1039
+ *
1040
+ * Parameters:
1041
+ * (String) name - The name of the child.
1042
+ * (Object) attrs - The attributes of the child in object notation.
1043
+ * (String) text - The text to add to the child.
1044
+ *
1045
+ * Returns:
1046
+ * The Strophe.Builder object.
1047
+ */
1048
+ c (name, attrs, text) {
1049
+ const child = Strophe.xmlElement(name, attrs, text);
1050
+ this.node.appendChild(child);
1051
+ if (typeof text !== "string" && typeof text !=="number") {
1052
+ this.node = child;
1053
+ }
1054
+ return this;
1055
+ }
1056
+
1057
+ /** Function: cnode
1058
+ * Add a child to the current element and make it the new current
1059
+ * element.
1060
+ *
1061
+ * This function is the same as c() except that instead of using a
1062
+ * name and an attributes object to create the child it uses an
1063
+ * existing DOM element object.
1064
+ *
1065
+ * Parameters:
1066
+ * (XMLElement) elem - A DOM element.
1067
+ *
1068
+ * Returns:
1069
+ * The Strophe.Builder object.
1070
+ */
1071
+ cnode (elem) {
1072
+ let impNode;
1073
+ const xmlGen = Strophe.xmlGenerator();
1074
+ try {
1075
+ impNode = (xmlGen.importNode !== undefined);
1076
+ } catch (e) {
1077
+ impNode = false;
1078
+ }
1079
+ const newElem = impNode ? xmlGen.importNode(elem, true) : Strophe.copyElement(elem);
1080
+ this.node.appendChild(newElem);
1081
+ this.node = newElem;
1082
+ return this;
1083
+ }
1084
+
1085
+ /** Function: t
1086
+ * Add a child text element.
1087
+ *
1088
+ * This *does not* make the child the new current element since there
1089
+ * are no children of text elements.
1090
+ *
1091
+ * Parameters:
1092
+ * (String) text - The text data to append to the current element.
1093
+ *
1094
+ * Returns:
1095
+ * The Strophe.Builder object.
1096
+ */
1097
+ t (text) {
1098
+ const child = Strophe.xmlTextNode(text);
1099
+ this.node.appendChild(child);
1100
+ return this;
1101
+ }
1102
+
1103
+ /** Function: h
1104
+ * Replace current element contents with the HTML passed in.
1105
+ *
1106
+ * This *does not* make the child the new current element
1107
+ *
1108
+ * Parameters:
1109
+ * (String) html - The html to insert as contents of current element.
1110
+ *
1111
+ * Returns:
1112
+ * The Strophe.Builder object.
1113
+ */
1114
+ h (html) {
1115
+ const fragment = Strophe.xmlGenerator().createElement('body');
1116
+ // force the browser to try and fix any invalid HTML tags
1117
+ fragment.innerHTML = html;
1118
+ // copy cleaned html into an xml dom
1119
+ const xhtml = Strophe.createHtml(fragment);
1120
+ while (xhtml.childNodes.length > 0) {
1121
+ this.node.appendChild(xhtml.childNodes[0]);
1122
+ }
1123
+ return this;
1124
+ }
1125
+ };
1126
+
1127
+ /** PrivateClass: Strophe.Handler
1128
+ * _Private_ helper class for managing stanza handlers.
1129
+ *
1130
+ * A Strophe.Handler encapsulates a user provided callback function to be
1131
+ * executed when matching stanzas are received by the connection.
1132
+ * Handlers can be either one-off or persistant depending on their
1133
+ * return value. Returning true will cause a Handler to remain active, and
1134
+ * returning false will remove the Handler.
1135
+ *
1136
+ * Users will not use Strophe.Handler objects directly, but instead they
1137
+ * will use Strophe.Connection.addHandler() and
1138
+ * Strophe.Connection.deleteHandler().
1139
+ */
1140
+
1141
+ /** PrivateConstructor: Strophe.Handler
1142
+ * Create and initialize a new Strophe.Handler.
1143
+ *
1144
+ * Parameters:
1145
+ * (Function) handler - A function to be executed when the handler is run.
1146
+ * (String) ns - The namespace to match.
1147
+ * (String) name - The element name to match.
1148
+ * (String) type - The element type to match.
1149
+ * (String) id - The element id attribute to match.
1150
+ * (String) from - The element from attribute to match.
1151
+ * (Object) options - Handler options
1152
+ *
1153
+ * Returns:
1154
+ * A new Strophe.Handler object.
1155
+ */
1156
+ Strophe.Handler = function (handler, ns, name, type, id, from, options) {
1157
+ this.handler = handler;
1158
+ this.ns = ns;
1159
+ this.name = name;
1160
+ this.type = type;
1161
+ this.id = id;
1162
+ this.options = options || {'matchBareFromJid': false, 'ignoreNamespaceFragment': false};
1163
+ // BBB: Maintain backward compatibility with old `matchBare` option
1164
+ if (this.options.matchBare) {
1165
+ Strophe.warn('The "matchBare" option is deprecated, use "matchBareFromJid" instead.');
1166
+ this.options.matchBareFromJid = this.options.matchBare;
1167
+ delete this.options.matchBare;
1168
+ }
1169
+ if (this.options.matchBareFromJid) {
1170
+ this.from = from ? Strophe.getBareJidFromJid(from) : null;
1171
+ } else {
1172
+ this.from = from;
1173
+ }
1174
+ // whether the handler is a user handler or a system handler
1175
+ this.user = true;
1176
+ };
1177
+
1178
+ Strophe.Handler.prototype = {
1179
+ /** PrivateFunction: getNamespace
1180
+ * Returns the XML namespace attribute on an element.
1181
+ * If `ignoreNamespaceFragment` was passed in for this handler, then the
1182
+ * URL fragment will be stripped.
1183
+ *
1184
+ * Parameters:
1185
+ * (XMLElement) elem - The XML element with the namespace.
1186
+ *
1187
+ * Returns:
1188
+ * The namespace, with optionally the fragment stripped.
1189
+ */
1190
+ getNamespace (elem) {
1191
+ let elNamespace = elem.getAttribute("xmlns");
1192
+ if (elNamespace && this.options.ignoreNamespaceFragment) {
1193
+ elNamespace = elNamespace.split('#')[0];
1194
+ }
1195
+ return elNamespace;
1196
+ },
1197
+
1198
+ /** PrivateFunction: namespaceMatch
1199
+ * Tests if a stanza matches the namespace set for this Strophe.Handler.
1200
+ *
1201
+ * Parameters:
1202
+ * (XMLElement) elem - The XML element to test.
1203
+ *
1204
+ * Returns:
1205
+ * true if the stanza matches and false otherwise.
1206
+ */
1207
+ namespaceMatch (elem) {
1208
+ let nsMatch = false;
1209
+ if (!this.ns) {
1210
+ return true;
1211
+ } else {
1212
+ Strophe.forEachChild(elem, null, (elem) => {
1213
+ if (this.getNamespace(elem) === this.ns) {
1214
+ nsMatch = true;
1215
+ }
1216
+ });
1217
+ return nsMatch || this.getNamespace(elem) === this.ns;
1218
+ }
1219
+ },
1220
+
1221
+ /** PrivateFunction: isMatch
1222
+ * Tests if a stanza matches the Strophe.Handler.
1223
+ *
1224
+ * Parameters:
1225
+ * (XMLElement) elem - The XML element to test.
1226
+ *
1227
+ * Returns:
1228
+ * true if the stanza matches and false otherwise.
1229
+ */
1230
+ isMatch (elem) {
1231
+ let from = elem.getAttribute('from');
1232
+ if (this.options.matchBareFromJid) {
1233
+ from = Strophe.getBareJidFromJid(from);
1234
+ }
1235
+ const elem_type = elem.getAttribute("type");
1236
+ if (this.namespaceMatch(elem) &&
1237
+ (!this.name || Strophe.isTagEqual(elem, this.name)) &&
1238
+ (!this.type || (Array.isArray(this.type) ? this.type.indexOf(elem_type) !== -1 : elem_type === this.type)) &&
1239
+ (!this.id || elem.getAttribute("id") === this.id) &&
1240
+ (!this.from || from === this.from)) {
1241
+ return true;
1242
+ }
1243
+ return false;
1244
+ },
1245
+
1246
+ /** PrivateFunction: run
1247
+ * Run the callback on a matching stanza.
1248
+ *
1249
+ * Parameters:
1250
+ * (XMLElement) elem - The DOM element that triggered the
1251
+ * Strophe.Handler.
1252
+ *
1253
+ * Returns:
1254
+ * A boolean indicating if the handler should remain active.
1255
+ */
1256
+ run (elem) {
1257
+ let result = null;
1258
+ try {
1259
+ result = this.handler(elem);
1260
+ } catch (e) {
1261
+ Strophe._handleError(e);
1262
+ throw e;
1263
+ }
1264
+ return result;
1265
+ },
1266
+
1267
+ /** PrivateFunction: toString
1268
+ * Get a String representation of the Strophe.Handler object.
1269
+ *
1270
+ * Returns:
1271
+ * A String.
1272
+ */
1273
+ toString () {
1274
+ return "{Handler: " + this.handler + "(" + this.name + "," +
1275
+ this.id + "," + this.ns + ")}";
1276
+ }
1277
+ };
1278
+
1279
+ /** PrivateClass: Strophe.TimedHandler
1280
+ * _Private_ helper class for managing timed handlers.
1281
+ *
1282
+ * A Strophe.TimedHandler encapsulates a user provided callback that
1283
+ * should be called after a certain period of time or at regular
1284
+ * intervals. The return value of the callback determines whether the
1285
+ * Strophe.TimedHandler will continue to fire.
1286
+ *
1287
+ * Users will not use Strophe.TimedHandler objects directly, but instead
1288
+ * they will use Strophe.Connection.addTimedHandler() and
1289
+ * Strophe.Connection.deleteTimedHandler().
1290
+ */
1291
+ Strophe.TimedHandler = class TimedHandler {
1292
+
1293
+ /** PrivateConstructor: Strophe.TimedHandler
1294
+ * Create and initialize a new Strophe.TimedHandler object.
1295
+ *
1296
+ * Parameters:
1297
+ * (Integer) period - The number of milliseconds to wait before the
1298
+ * handler is called.
1299
+ * (Function) handler - The callback to run when the handler fires. This
1300
+ * function should take no arguments.
1301
+ *
1302
+ * Returns:
1303
+ * A new Strophe.TimedHandler object.
1304
+ */
1305
+ constructor (period, handler) {
1306
+ this.period = period;
1307
+ this.handler = handler;
1308
+ this.lastCalled = new Date().getTime();
1309
+ this.user = true;
1310
+ }
1311
+
1312
+ /** PrivateFunction: run
1313
+ * Run the callback for the Strophe.TimedHandler.
1314
+ *
1315
+ * Returns:
1316
+ * true if the Strophe.TimedHandler should be called again, and false
1317
+ * otherwise.
1318
+ */
1319
+ run () {
1320
+ this.lastCalled = new Date().getTime();
1321
+ return this.handler();
1322
+ }
1323
+
1324
+ /** PrivateFunction: reset
1325
+ * Reset the last called time for the Strophe.TimedHandler.
1326
+ */
1327
+ reset () {
1328
+ this.lastCalled = new Date().getTime();
1329
+ }
1330
+
1331
+ /** PrivateFunction: toString
1332
+ * Get a string representation of the Strophe.TimedHandler object.
1333
+ *
1334
+ * Returns:
1335
+ * The string representation.
1336
+ */
1337
+ toString () {
1338
+ return "{TimedHandler: " + this.handler + "(" + this.period +")}";
1339
+ }
1340
+ };
1341
+
1342
+ /** Class: Strophe.Connection
1343
+ * XMPP Connection manager.
1344
+ *
1345
+ * This class is the main part of Strophe. It manages a BOSH or websocket
1346
+ * connection to an XMPP server and dispatches events to the user callbacks
1347
+ * as data arrives. It supports SASL PLAIN, SASL SCRAM-SHA-1
1348
+ * and legacy authentication.
1349
+ *
1350
+ * After creating a Strophe.Connection object, the user will typically
1351
+ * call connect() with a user supplied callback to handle connection level
1352
+ * events like authentication failure, disconnection, or connection
1353
+ * complete.
1354
+ *
1355
+ * The user will also have several event handlers defined by using
1356
+ * addHandler() and addTimedHandler(). These will allow the user code to
1357
+ * respond to interesting stanzas or do something periodically with the
1358
+ * connection. These handlers will be active once authentication is
1359
+ * finished.
1360
+ *
1361
+ * To send data to the connection, use send().
1362
+ */
1363
+
1364
+ /** Constructor: Strophe.Connection
1365
+ * Create and initialize a Strophe.Connection object.
1366
+ *
1367
+ * The transport-protocol for this connection will be chosen automatically
1368
+ * based on the given service parameter. URLs starting with "ws://" or
1369
+ * "wss://" will use WebSockets, URLs starting with "http://", "https://"
1370
+ * or without a protocol will use BOSH.
1371
+ *
1372
+ * To make Strophe connect to the current host you can leave out the protocol
1373
+ * and host part and just pass the path, e.g.
1374
+ *
1375
+ * > let conn = new Strophe.Connection("/http-bind/");
1376
+ *
1377
+ * Options common to both Websocket and BOSH:
1378
+ * ------------------------------------------
1379
+ *
1380
+ * cookies:
1381
+ *
1382
+ * The *cookies* option allows you to pass in cookies to be added to the
1383
+ * document. These cookies will then be included in the BOSH XMLHttpRequest
1384
+ * or in the websocket connection.
1385
+ *
1386
+ * The passed in value must be a map of cookie names and string values.
1387
+ *
1388
+ * > { "myCookie": {
1389
+ * > "value": "1234",
1390
+ * > "domain": ".example.org",
1391
+ * > "path": "/",
1392
+ * > "expires": expirationDate
1393
+ * > }
1394
+ * > }
1395
+ *
1396
+ * Note that cookies can't be set in this way for other domains (i.e. cross-domain).
1397
+ * Those cookies need to be set under those domains, for example they can be
1398
+ * set server-side by making a XHR call to that domain to ask it to set any
1399
+ * necessary cookies.
1400
+ *
1401
+ * mechanisms:
1402
+ *
1403
+ * The *mechanisms* option allows you to specify the SASL mechanisms that this
1404
+ * instance of Strophe.Connection (and therefore your XMPP client) will
1405
+ * support.
1406
+ *
1407
+ * The value must be an array of objects with Strophe.SASLMechanism
1408
+ * prototypes.
1409
+ *
1410
+ * If nothing is specified, then the following mechanisms (and their
1411
+ * priorities) are registered:
1412
+ *
1413
+ * SCRAM-SHA-1 - 60
1414
+ * PLAIN - 50
1415
+ * OAUTHBEARER - 40
1416
+ * X-OAUTH2 - 30
1417
+ * ANONYMOUS - 20
1418
+ * EXTERNAL - 10
1419
+ *
1420
+ * explicitResourceBinding:
1421
+ *
1422
+ * If `explicitResourceBinding` is set to a truthy value, then the XMPP client
1423
+ * needs to explicitly call `Strophe.Connection.prototype.bind` once the XMPP
1424
+ * server has advertised the "urn:ietf:params:xml:ns:xmpp-bind" feature.
1425
+ *
1426
+ * Making this step explicit allows client authors to first finish other
1427
+ * stream related tasks, such as setting up an XEP-0198 Stream Management
1428
+ * session, before binding the JID resource for this session.
1429
+ *
1430
+ * WebSocket options:
1431
+ * ------------------
1432
+ *
1433
+ * protocol:
1434
+ *
1435
+ * If you want to connect to the current host with a WebSocket connection you
1436
+ * can tell Strophe to use WebSockets through a "protocol" attribute in the
1437
+ * optional options parameter. Valid values are "ws" for WebSocket and "wss"
1438
+ * for Secure WebSocket.
1439
+ * So to connect to "wss://CURRENT_HOSTNAME/xmpp-websocket" you would call
1440
+ *
1441
+ * > let conn = new Strophe.Connection("/xmpp-websocket/", {protocol: "wss"});
1442
+ *
1443
+ * Note that relative URLs _NOT_ starting with a "/" will also include the path
1444
+ * of the current site.
1445
+ *
1446
+ * Also because downgrading security is not permitted by browsers, when using
1447
+ * relative URLs both BOSH and WebSocket connections will use their secure
1448
+ * variants if the current connection to the site is also secure (https).
1449
+ *
1450
+ * worker:
1451
+ *
1452
+ * Set this option to URL from where the shared worker script should be loaded.
1453
+ *
1454
+ * To run the websocket connection inside a shared worker.
1455
+ * This allows you to share a single websocket-based connection between
1456
+ * multiple Strophe.Connection instances, for example one per browser tab.
1457
+ *
1458
+ * The script to use is the one in `src/shared-connection-worker.js`.
1459
+ *
1460
+ * BOSH options:
1461
+ * -------------
1462
+ *
1463
+ * By adding "sync" to the options, you can control if requests will
1464
+ * be made synchronously or not. The default behaviour is asynchronous.
1465
+ * If you want to make requests synchronous, make "sync" evaluate to true.
1466
+ * > let conn = new Strophe.Connection("/http-bind/", {sync: true});
1467
+ *
1468
+ * You can also toggle this on an already established connection.
1469
+ * > conn.options.sync = true;
1470
+ *
1471
+ * The *customHeaders* option can be used to provide custom HTTP headers to be
1472
+ * included in the XMLHttpRequests made.
1473
+ *
1474
+ * The *keepalive* option can be used to instruct Strophe to maintain the
1475
+ * current BOSH session across interruptions such as webpage reloads.
1476
+ *
1477
+ * It will do this by caching the sessions tokens in sessionStorage, and when
1478
+ * "restore" is called it will check whether there are cached tokens with
1479
+ * which it can resume an existing session.
1480
+ *
1481
+ * The *withCredentials* option should receive a Boolean value and is used to
1482
+ * indicate wether cookies should be included in ajax requests (by default
1483
+ * they're not).
1484
+ * Set this value to true if you are connecting to a BOSH service
1485
+ * and for some reason need to send cookies to it.
1486
+ * In order for this to work cross-domain, the server must also enable
1487
+ * credentials by setting the Access-Control-Allow-Credentials response header
1488
+ * to "true". For most usecases however this setting should be false (which
1489
+ * is the default).
1490
+ * Additionally, when using Access-Control-Allow-Credentials, the
1491
+ * Access-Control-Allow-Origin header can't be set to the wildcard "*", but
1492
+ * instead must be restricted to actual domains.
1493
+ *
1494
+ * The *contentType* option can be set to change the default Content-Type
1495
+ * of "text/xml; charset=utf-8", which can be useful to reduce the amount of
1496
+ * CORS preflight requests that are sent to the server.
1497
+ *
1498
+ * Parameters:
1499
+ * (String) service - The BOSH or WebSocket service URL.
1500
+ * (Object) options - A hash of configuration options
1501
+ *
1502
+ * Returns:
1503
+ * A new Strophe.Connection object.
1504
+ */
1505
+
1506
+ Strophe.Connection = class Connection {
1507
+
1508
+ constructor (service, options) {
1509
+ // The service URL
1510
+ this.service = service;
1511
+ // Configuration options
1512
+ this.options = options || {};
1513
+ const proto = this.options.protocol || "";
1514
+
1515
+ // Select protocal based on service or options
1516
+ if (this.options.worker) {
1517
+ this._proto = new Strophe.WorkerWebsocket(this);
1518
+ } else if (
1519
+ service.indexOf("ws:") === 0 ||
1520
+ service.indexOf("wss:") === 0 ||
1521
+ proto.indexOf("ws") === 0) {
1522
+ this._proto = new Strophe.Websocket(this);
1523
+ } else {
1524
+ this._proto = new Strophe.Bosh(this);
1525
+ }
1526
+
1527
+ /* The connected JID. */
1528
+ this.jid = "";
1529
+ /* the JIDs domain */
1530
+ this.domain = null;
1531
+ /* stream:features */
1532
+ this.features = null;
1533
+
1534
+ // SASL
1535
+ this._sasl_data = {};
1536
+ this.do_bind = false;
1537
+ this.do_session = false;
1538
+ this.mechanisms = {}
1539
+
1540
+ // handler lists
1541
+ this.timedHandlers = [];
1542
+ this.handlers = [];
1543
+ this.removeTimeds = [];
1544
+ this.removeHandlers = [];
1545
+ this.addTimeds = [];
1546
+ this.addHandlers = [];
1547
+ this.protocolErrorHandlers = {
1548
+ 'HTTP': {},
1549
+ 'websocket': {}
1550
+ };
1551
+
1552
+ this._idleTimeout = null;
1553
+ this._disconnectTimeout = null;
1554
+
1555
+ this.authenticated = false;
1556
+ this.connected = false;
1557
+ this.disconnecting = false;
1558
+ this.do_authentication = true;
1559
+ this.paused = false;
1560
+ this.restored = false;
1561
+
1562
+ this._data = [];
1563
+ this._uniqueId = 0;
1564
+
1565
+ this._sasl_success_handler = null;
1566
+ this._sasl_failure_handler = null;
1567
+ this._sasl_challenge_handler = null;
1568
+
1569
+ // Max retries before disconnecting
1570
+ this.maxRetries = 5;
1571
+
1572
+ // Call onIdle callback every 1/10th of a second
1573
+ this._idleTimeout = setTimeout(() => this._onIdle(), 100);
1574
+
1575
+ utils.addCookies(this.options.cookies);
1576
+ this.registerSASLMechanisms(this.options.mechanisms);
1577
+
1578
+ // initialize plugins
1579
+ for (const k in Strophe._connectionPlugins) {
1580
+ if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {
1581
+ const F = function () {};
1582
+ F.prototype = Strophe._connectionPlugins[k];
1583
+ this[k] = new F();
1584
+ this[k].init(this);
1585
+ }
1586
+ }
1587
+ }
1588
+
1589
+
1590
+ /** Function: reset
1591
+ * Reset the connection.
1592
+ *
1593
+ * This function should be called after a connection is disconnected
1594
+ * before that connection is reused.
1595
+ */
1596
+ reset () {
1597
+ this._proto._reset();
1598
+
1599
+ // SASL
1600
+ this.do_session = false;
1601
+ this.do_bind = false;
1602
+
1603
+ // handler lists
1604
+ this.timedHandlers = [];
1605
+ this.handlers = [];
1606
+ this.removeTimeds = [];
1607
+ this.removeHandlers = [];
1608
+ this.addTimeds = [];
1609
+ this.addHandlers = [];
1610
+
1611
+ this.authenticated = false;
1612
+ this.connected = false;
1613
+ this.disconnecting = false;
1614
+ this.restored = false;
1615
+
1616
+ this._data = [];
1617
+ this._requests = [];
1618
+ this._uniqueId = 0;
1619
+ }
1620
+
1621
+ /** Function: pause
1622
+ * Pause the request manager.
1623
+ *
1624
+ * This will prevent Strophe from sending any more requests to the
1625
+ * server. This is very useful for temporarily pausing
1626
+ * BOSH-Connections while a lot of send() calls are happening quickly.
1627
+ * This causes Strophe to send the data in a single request, saving
1628
+ * many request trips.
1629
+ */
1630
+ pause () {
1631
+ this.paused = true;
1632
+ }
1633
+
1634
+ /** Function: resume
1635
+ * Resume the request manager.
1636
+ *
1637
+ * This resumes after pause() has been called.
1638
+ */
1639
+ resume () {
1640
+ this.paused = false;
1641
+ }
1642
+
1643
+ /** Function: getUniqueId
1644
+ * Generate a unique ID for use in <iq/> elements.
1645
+ *
1646
+ * All <iq/> stanzas are required to have unique id attributes. This
1647
+ * function makes creating these easy. Each connection instance has
1648
+ * a counter which starts from zero, and the value of this counter
1649
+ * plus a colon followed by the suffix becomes the unique id. If no
1650
+ * suffix is supplied, the counter is used as the unique id.
1651
+ *
1652
+ * Suffixes are used to make debugging easier when reading the stream
1653
+ * data, and their use is recommended. The counter resets to 0 for
1654
+ * every new connection for the same reason. For connections to the
1655
+ * same server that authenticate the same way, all the ids should be
1656
+ * the same, which makes it easy to see changes. This is useful for
1657
+ * automated testing as well.
1658
+ *
1659
+ * Parameters:
1660
+ * (String) suffix - A optional suffix to append to the id.
1661
+ *
1662
+ * Returns:
1663
+ * A unique string to be used for the id attribute.
1664
+ */
1665
+ getUniqueId (suffix) { // eslint-disable-line class-methods-use-this
1666
+ const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
1667
+ const r = Math.random() * 16 | 0,
1668
+ v = c === 'x' ? r : r & 0x3 | 0x8;
1669
+ return v.toString(16);
1670
+ });
1671
+ if (typeof(suffix) === "string" || typeof(suffix) === "number") {
1672
+ return uuid + ":" + suffix;
1673
+ } else {
1674
+ return uuid + "";
1675
+ }
1676
+ }
1677
+
1678
+ /** Function: addProtocolErrorHandler
1679
+ * Register a handler function for when a protocol (websocker or HTTP)
1680
+ * error occurs.
1681
+ *
1682
+ * NOTE: Currently only HTTP errors for BOSH requests are handled.
1683
+ * Patches that handle websocket errors would be very welcome.
1684
+ *
1685
+ * Parameters:
1686
+ * (String) protocol - 'HTTP' or 'websocket'
1687
+ * (Integer) status_code - Error status code (e.g 500, 400 or 404)
1688
+ * (Function) callback - Function that will fire on Http error
1689
+ *
1690
+ * Example:
1691
+ * function onError(err_code){
1692
+ * //do stuff
1693
+ * }
1694
+ *
1695
+ * let conn = Strophe.connect('http://example.com/http-bind');
1696
+ * conn.addProtocolErrorHandler('HTTP', 500, onError);
1697
+ * // Triggers HTTP 500 error and onError handler will be called
1698
+ * conn.connect('user_jid@incorrect_jabber_host', 'secret', onConnect);
1699
+ */
1700
+ addProtocolErrorHandler (protocol, status_code, callback){
1701
+ this.protocolErrorHandlers[protocol][status_code] = callback;
1702
+ }
1703
+
1704
+ /** Function: connect
1705
+ * Starts the connection process.
1706
+ *
1707
+ * As the connection process proceeds, the user supplied callback will
1708
+ * be triggered multiple times with status updates. The callback
1709
+ * should take two arguments - the status code and the error condition.
1710
+ *
1711
+ * The status code will be one of the values in the Strophe.Status
1712
+ * constants. The error condition will be one of the conditions
1713
+ * defined in RFC 3920 or the condition 'strophe-parsererror'.
1714
+ *
1715
+ * The Parameters _wait_, _hold_ and _route_ are optional and only relevant
1716
+ * for BOSH connections. Please see XEP 124 for a more detailed explanation
1717
+ * of the optional parameters.
1718
+ *
1719
+ * Parameters:
1720
+ * (String) jid - The user's JID. This may be a bare JID,
1721
+ * or a full JID. If a node is not supplied, SASL OAUTHBEARER or
1722
+ * SASL ANONYMOUS authentication will be attempted (OAUTHBEARER will
1723
+ * process the provided password value as an access token).
1724
+ * (String) pass - The user's password.
1725
+ * (Function) callback - The connect callback function.
1726
+ * (Integer) wait - The optional HTTPBIND wait value. This is the
1727
+ * time the server will wait before returning an empty result for
1728
+ * a request. The default setting of 60 seconds is recommended.
1729
+ * (Integer) hold - The optional HTTPBIND hold value. This is the
1730
+ * number of connections the server will hold at one time. This
1731
+ * should almost always be set to 1 (the default).
1732
+ * (String) route - The optional route value.
1733
+ * (String) authcid - The optional alternative authentication identity
1734
+ * (username) if intending to impersonate another user.
1735
+ * When using the SASL-EXTERNAL authentication mechanism, for example
1736
+ * with client certificates, then the authcid value is used to
1737
+ * determine whether an authorization JID (authzid) should be sent to
1738
+ * the server. The authzid should NOT be sent to the server if the
1739
+ * authzid and authcid are the same. So to prevent it from being sent
1740
+ * (for example when the JID is already contained in the client
1741
+ * certificate), set authcid to that same JID. See XEP-178 for more
1742
+ * details.
1743
+ */
1744
+ connect (jid, pass, callback, onLog, wait, hold, route, authcid) {
1745
+ this.jid = jid;
1746
+ /** Variable: authzid
1747
+ * Authorization identity.
1748
+ */
1749
+ this.authzid = Strophe.getBareJidFromJid(this.jid);
1750
+
1751
+ /** Variable: authcid
1752
+ * Authentication identity (User name).
1753
+ */
1754
+ this.authcid = authcid || Strophe.getNodeFromJid(this.jid);
1755
+
1756
+ /** Variable: pass
1757
+ * Authentication identity (User password).
1758
+ */
1759
+ this.pass = pass;
1760
+
1761
+ this.clientLogger = onLog || (msg => console.log(msg));
1762
+ this.connect_callback = callback;
1763
+ this.disconnecting = false;
1764
+ this.connected = false;
1765
+ this.authenticated = false;
1766
+ this.restored = false;
1767
+
1768
+ // parse jid for domain
1769
+ this.domain = Strophe.getDomainFromJid(this.jid);
1770
+
1771
+ this._changeConnectStatus(Strophe.Status.CONNECTING, null);
1772
+
1773
+ this._proto._connect(wait, hold, route);
1774
+
1775
+ this.clientLogger('Strophe connect method is completed.');
1776
+ }
1777
+
1778
+ /** Function: attach
1779
+ * Attach to an already created and authenticated BOSH session.
1780
+ *
1781
+ * This function is provided to allow Strophe to attach to BOSH
1782
+ * sessions which have been created externally, perhaps by a Web
1783
+ * application. This is often used to support auto-login type features
1784
+ * without putting user credentials into the page.
1785
+ *
1786
+ * Parameters:
1787
+ * (String) jid - The full JID that is bound by the session.
1788
+ * (String) sid - The SID of the BOSH session.
1789
+ * (String) rid - The current RID of the BOSH session. This RID
1790
+ * will be used by the next request.
1791
+ * (Function) callback The connect callback function.
1792
+ * (Integer) wait - The optional HTTPBIND wait value. This is the
1793
+ * time the server will wait before returning an empty result for
1794
+ * a request. The default setting of 60 seconds is recommended.
1795
+ * Other settings will require tweaks to the Strophe.TIMEOUT value.
1796
+ * (Integer) hold - The optional HTTPBIND hold value. This is the
1797
+ * number of connections the server will hold at one time. This
1798
+ * should almost always be set to 1 (the default).
1799
+ * (Integer) wind - The optional HTTBIND window value. This is the
1800
+ * allowed range of request ids that are valid. The default is 5.
1801
+ */
1802
+ attach (jid, sid, rid, callback, wait, hold, wind) {
1803
+ if (this._proto._attach) {
1804
+ return this._proto._attach(jid, sid, rid, callback, wait, hold, wind);
1805
+ } else {
1806
+ const error = new Error('The "attach" method is not available for your connection protocol');
1807
+ error.name = 'StropheSessionError';
1808
+ throw error;
1809
+ }
1810
+ }
1811
+
1812
+ /** Function: restore
1813
+ * Attempt to restore a cached BOSH session.
1814
+ *
1815
+ * This function is only useful in conjunction with providing the
1816
+ * "keepalive":true option when instantiating a new Strophe.Connection.
1817
+ *
1818
+ * When "keepalive" is set to true, Strophe will cache the BOSH tokens
1819
+ * RID (Request ID) and SID (Session ID) and then when this function is
1820
+ * called, it will attempt to restore the session from those cached
1821
+ * tokens.
1822
+ *
1823
+ * This function must therefore be called instead of connect or attach.
1824
+ *
1825
+ * For an example on how to use it, please see examples/restore.js
1826
+ *
1827
+ * Parameters:
1828
+ * (String) jid - The user's JID. This may be a bare JID or a full JID.
1829
+ * (Function) callback - The connect callback function.
1830
+ * (Integer) wait - The optional HTTPBIND wait value. This is the
1831
+ * time the server will wait before returning an empty result for
1832
+ * a request. The default setting of 60 seconds is recommended.
1833
+ * (Integer) hold - The optional HTTPBIND hold value. This is the
1834
+ * number of connections the server will hold at one time. This
1835
+ * should almost always be set to 1 (the default).
1836
+ * (Integer) wind - The optional HTTBIND window value. This is the
1837
+ * allowed range of request ids that are valid. The default is 5.
1838
+ */
1839
+ restore (jid, callback, wait, hold, wind) {
1840
+ if (this._sessionCachingSupported()) {
1841
+ this._proto._restore(jid, callback, wait, hold, wind);
1842
+ } else {
1843
+ const error = new Error('The "restore" method can only be used with a BOSH connection.');
1844
+ error.name = 'StropheSessionError';
1845
+ throw error;
1846
+ }
1847
+ }
1848
+
1849
+ /** PrivateFunction: _sessionCachingSupported
1850
+ * Checks whether sessionStorage and JSON are supported and whether we're
1851
+ * using BOSH.
1852
+ */
1853
+ _sessionCachingSupported () {
1854
+ if (this._proto instanceof Strophe.Bosh) {
1855
+ if (!JSON) { return false; }
1856
+ try {
1857
+ sessionStorage.setItem('_strophe_', '_strophe_');
1858
+ sessionStorage.removeItem('_strophe_');
1859
+ } catch (e) {
1860
+ return false;
1861
+ }
1862
+ return true;
1863
+ }
1864
+ return false;
1865
+ }
1866
+
1867
+ /** Function: xmlInput
1868
+ * User overrideable function that receives XML data coming into the
1869
+ * connection.
1870
+ *
1871
+ * The default function does nothing. User code can override this with
1872
+ * > Strophe.Connection.xmlInput = function (elem) {
1873
+ * > (user code)
1874
+ * > };
1875
+ *
1876
+ * Due to limitations of current Browsers' XML-Parsers the opening and closing
1877
+ * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
1878
+ *
1879
+ * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
1880
+ * <Strophe.Bosh.strip> if you want to strip this tag.
1881
+ *
1882
+ * Parameters:
1883
+ * (XMLElement) elem - The XML data received by the connection.
1884
+ */
1885
+ xmlInput (elem) { // eslint-disable-line
1886
+ return;
1887
+ }
1888
+
1889
+ /** Function: xmlOutput
1890
+ * User overrideable function that receives XML data sent to the
1891
+ * connection.
1892
+ *
1893
+ * The default function does nothing. User code can override this with
1894
+ * > Strophe.Connection.xmlOutput = function (elem) {
1895
+ * > (user code)
1896
+ * > };
1897
+ *
1898
+ * Due to limitations of current Browsers' XML-Parsers the opening and closing
1899
+ * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
1900
+ *
1901
+ * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
1902
+ * <Strophe.Bosh.strip> if you want to strip this tag.
1903
+ *
1904
+ * Parameters:
1905
+ * (XMLElement) elem - The XMLdata sent by the connection.
1906
+ */
1907
+ xmlOutput (elem) { // eslint-disable-line
1908
+ return;
1909
+ }
1910
+
1911
+ /** Function: rawInput
1912
+ * User overrideable function that receives raw data coming into the
1913
+ * connection.
1914
+ *
1915
+ * The default function does nothing. User code can override this with
1916
+ * > Strophe.Connection.rawInput = function (data) {
1917
+ * > (user code)
1918
+ * > };
1919
+ *
1920
+ * Parameters:
1921
+ * (String) data - The data received by the connection.
1922
+ */
1923
+ rawInput (data) { // eslint-disable-line
1924
+ return;
1925
+ }
1926
+
1927
+ /** Function: rawOutput
1928
+ * User overrideable function that receives raw data sent to the
1929
+ * connection.
1930
+ *
1931
+ * The default function does nothing. User code can override this with
1932
+ * > Strophe.Connection.rawOutput = function (data) {
1933
+ * > (user code)
1934
+ * > };
1935
+ *
1936
+ * Parameters:
1937
+ * (String) data - The data sent by the connection.
1938
+ */
1939
+ rawOutput (data) { // eslint-disable-line
1940
+ return;
1941
+ }
1942
+
1943
+ /** Function: nextValidRid
1944
+ * User overrideable function that receives the new valid rid.
1945
+ *
1946
+ * The default function does nothing. User code can override this with
1947
+ * > Strophe.Connection.nextValidRid = function (rid) {
1948
+ * > (user code)
1949
+ * > };
1950
+ *
1951
+ * Parameters:
1952
+ * (Number) rid - The next valid rid
1953
+ */
1954
+ nextValidRid (rid) { // eslint-disable-line
1955
+ return;
1956
+ }
1957
+
1958
+ /** Function: send
1959
+ * Send a stanza.
1960
+ *
1961
+ * This function is called to push data onto the send queue to
1962
+ * go out over the wire. Whenever a request is sent to the BOSH
1963
+ * server, all pending data is sent and the queue is flushed.
1964
+ *
1965
+ * Parameters:
1966
+ * (XMLElement |
1967
+ * [XMLElement] |
1968
+ * Strophe.Builder) elem - The stanza to send.
1969
+ */
1970
+ send (elem) {
1971
+ if (elem === null) { return ; }
1972
+ if (typeof(elem.sort) === "function") {
1973
+ for (let i=0; i < elem.length; i++) {
1974
+ this._queueData(elem[i]);
1975
+ }
1976
+ } else if (typeof(elem.tree) === "function") {
1977
+ this._queueData(elem.tree());
1978
+ } else {
1979
+ this._queueData(elem);
1980
+ }
1981
+ this._proto._send();
1982
+ }
1983
+
1984
+ /** Function: flush
1985
+ * Immediately send any pending outgoing data.
1986
+ *
1987
+ * Normally send() queues outgoing data until the next idle period
1988
+ * (100ms), which optimizes network use in the common cases when
1989
+ * several send()s are called in succession. flush() can be used to
1990
+ * immediately send all pending data.
1991
+ */
1992
+ flush () {
1993
+ // cancel the pending idle period and run the idle function
1994
+ // immediately
1995
+ clearTimeout(this._idleTimeout);
1996
+ this._onIdle();
1997
+ }
1998
+
1999
+ /** Function: sendPresence
2000
+ * Helper function to send presence stanzas. The main benefit is for
2001
+ * sending presence stanzas for which you expect a responding presence
2002
+ * stanza with the same id (for example when leaving a chat room).
2003
+ *
2004
+ * Parameters:
2005
+ * (XMLElement) elem - The stanza to send.
2006
+ * (Function) callback - The callback function for a successful request.
2007
+ * (Function) errback - The callback function for a failed or timed
2008
+ * out request. On timeout, the stanza will be null.
2009
+ * (Integer) timeout - The time specified in milliseconds for a
2010
+ * timeout to occur.
2011
+ *
2012
+ * Returns:
2013
+ * The id used to send the presence.
2014
+ */
2015
+ sendPresence (elem, callback, errback, timeout) {
2016
+ let timeoutHandler = null;
2017
+ if (typeof(elem.tree) === "function") {
2018
+ elem = elem.tree();
2019
+ }
2020
+ let id = elem.getAttribute('id');
2021
+ if (!id) { // inject id if not found
2022
+ id = this.getUniqueId("sendPresence");
2023
+ elem.setAttribute("id", id);
2024
+ }
2025
+
2026
+ if (typeof callback === "function" || typeof errback === "function") {
2027
+ const handler = this.addHandler(stanza => {
2028
+ // remove timeout handler if there is one
2029
+ if (timeoutHandler) {
2030
+ this.deleteTimedHandler(timeoutHandler);
2031
+ }
2032
+ if (stanza.getAttribute('type') === 'error') {
2033
+ if (errback) {
2034
+ errback(stanza);
2035
+ }
2036
+ } else if (callback) {
2037
+ callback(stanza);
2038
+ }
2039
+ }, null, 'presence', null, id);
2040
+
2041
+ // if timeout specified, set up a timeout handler.
2042
+ if (timeout) {
2043
+ timeoutHandler = this.addTimedHandler(timeout, () => {
2044
+ // get rid of normal handler
2045
+ this.deleteHandler(handler);
2046
+ // call errback on timeout with null stanza
2047
+ if (errback) {
2048
+ errback(null);
2049
+ }
2050
+ return false;
2051
+ });
2052
+ }
2053
+ }
2054
+ this.send(elem);
2055
+ return id;
2056
+ }
2057
+
2058
+ /** Function: sendIQ
2059
+ * Helper function to send IQ stanzas.
2060
+ *
2061
+ * Parameters:
2062
+ * (XMLElement) elem - The stanza to send.
2063
+ * (Function) callback - The callback function for a successful request.
2064
+ * (Function) errback - The callback function for a failed or timed
2065
+ * out request. On timeout, the stanza will be null.
2066
+ * (Integer) timeout - The time specified in milliseconds for a
2067
+ * timeout to occur.
2068
+ *
2069
+ * Returns:
2070
+ * The id used to send the IQ.
2071
+ */
2072
+ sendIQ (elem, callback, errback, timeout) {
2073
+ let timeoutHandler = null;
2074
+ if (typeof(elem.tree) === "function") {
2075
+ elem = elem.tree();
2076
+ }
2077
+ let id = elem.getAttribute('id');
2078
+ if (!id) { // inject id if not found
2079
+ id = this.getUniqueId("sendIQ");
2080
+ elem.setAttribute("id", id);
2081
+ }
2082
+
2083
+ if (typeof callback === "function" || typeof errback === "function") {
2084
+ const handler = this.addHandler(stanza => {
2085
+ // remove timeout handler if there is one
2086
+ if (timeoutHandler) {
2087
+ this.deleteTimedHandler(timeoutHandler);
2088
+ }
2089
+ const iqtype = stanza.getAttribute('type');
2090
+ if (iqtype === 'result') {
2091
+ if (callback) {
2092
+ callback(stanza);
2093
+ }
2094
+ } else if (iqtype === 'error') {
2095
+ if (errback) {
2096
+ errback(stanza);
2097
+ }
2098
+ } else {
2099
+ const error = new Error(`Got bad IQ type of ${iqtype}`);
2100
+ error.name = "StropheError";
2101
+ throw(error);
2102
+ }
2103
+ }, null, 'iq', ['error', 'result'], id);
2104
+
2105
+ // if timeout specified, set up a timeout handler.
2106
+ if (timeout) {
2107
+ timeoutHandler = this.addTimedHandler(timeout, () => {
2108
+ // get rid of normal handler
2109
+ this.deleteHandler(handler);
2110
+ // call errback on timeout with null stanza
2111
+ if (errback) {
2112
+ errback(null);
2113
+ }
2114
+ return false;
2115
+ });
2116
+ }
2117
+ }
2118
+ this.send(elem);
2119
+ return id;
2120
+ }
2121
+
2122
+ /** PrivateFunction: _queueData
2123
+ * Queue outgoing data for later sending. Also ensures that the data
2124
+ * is a DOMElement.
2125
+ */
2126
+ _queueData (element) {
2127
+ if (element === null ||
2128
+ !element.tagName ||
2129
+ !element.childNodes) {
2130
+ const error = new Error("Cannot queue non-DOMElement.");
2131
+ error.name = "StropheError";
2132
+ throw(error);
2133
+ }
2134
+ this._data.push(element);
2135
+ }
2136
+
2137
+ /** PrivateFunction: _sendRestart
2138
+ * Send an xmpp:restart stanza.
2139
+ */
2140
+ _sendRestart () {
2141
+ this._data.push("restart");
2142
+ this._proto._sendRestart();
2143
+ this._idleTimeout = setTimeout(() => this._onIdle(), 100);
2144
+ }
2145
+
2146
+ /** Function: addTimedHandler
2147
+ * Add a timed handler to the connection.
2148
+ *
2149
+ * This function adds a timed handler. The provided handler will
2150
+ * be called every period milliseconds until it returns false,
2151
+ * the connection is terminated, or the handler is removed. Handlers
2152
+ * that wish to continue being invoked should return true.
2153
+ *
2154
+ * Because of method binding it is necessary to save the result of
2155
+ * this function if you wish to remove a handler with
2156
+ * deleteTimedHandler().
2157
+ *
2158
+ * Note that user handlers are not active until authentication is
2159
+ * successful.
2160
+ *
2161
+ * Parameters:
2162
+ * (Integer) period - The period of the handler.
2163
+ * (Function) handler - The callback function.
2164
+ *
2165
+ * Returns:
2166
+ * A reference to the handler that can be used to remove it.
2167
+ */
2168
+ addTimedHandler (period, handler) {
2169
+ const thand = new Strophe.TimedHandler(period, handler);
2170
+ this.addTimeds.push(thand);
2171
+ return thand;
2172
+ }
2173
+
2174
+ /** Function: deleteTimedHandler
2175
+ * Delete a timed handler for a connection.
2176
+ *
2177
+ * This function removes a timed handler from the connection. The
2178
+ * handRef parameter is *not* the function passed to addTimedHandler(),
2179
+ * but is the reference returned from addTimedHandler().
2180
+ *
2181
+ * Parameters:
2182
+ * (Strophe.TimedHandler) handRef - The handler reference.
2183
+ */
2184
+ deleteTimedHandler (handRef) {
2185
+ // this must be done in the Idle loop so that we don't change
2186
+ // the handlers during iteration
2187
+ this.removeTimeds.push(handRef);
2188
+ }
2189
+
2190
+ /** Function: addHandler
2191
+ * Add a stanza handler for the connection.
2192
+ *
2193
+ * This function adds a stanza handler to the connection. The
2194
+ * handler callback will be called for any stanza that matches
2195
+ * the parameters. Note that if multiple parameters are supplied,
2196
+ * they must all match for the handler to be invoked.
2197
+ *
2198
+ * The handler will receive the stanza that triggered it as its argument.
2199
+ * *The handler should return true if it is to be invoked again;
2200
+ * returning false will remove the handler after it returns.*
2201
+ *
2202
+ * As a convenience, the ns parameters applies to the top level element
2203
+ * and also any of its immediate children. This is primarily to make
2204
+ * matching /iq/query elements easy.
2205
+ *
2206
+ * Options
2207
+ * ~~~~~~~
2208
+ * With the options argument, you can specify boolean flags that affect how
2209
+ * matches are being done.
2210
+ *
2211
+ * Currently two flags exist:
2212
+ *
2213
+ * - matchBareFromJid:
2214
+ * When set to true, the from parameter and the
2215
+ * from attribute on the stanza will be matched as bare JIDs instead
2216
+ * of full JIDs. To use this, pass {matchBareFromJid: true} as the
2217
+ * value of options. The default value for matchBareFromJid is false.
2218
+ *
2219
+ * - ignoreNamespaceFragment:
2220
+ * When set to true, a fragment specified on the stanza's namespace
2221
+ * URL will be ignored when it's matched with the one configured for
2222
+ * the handler.
2223
+ *
2224
+ * This means that if you register like this:
2225
+ * > connection.addHandler(
2226
+ * > handler,
2227
+ * > 'http://jabber.org/protocol/muc',
2228
+ * > null, null, null, null,
2229
+ * > {'ignoreNamespaceFragment': true}
2230
+ * > );
2231
+ *
2232
+ * Then a stanza with XML namespace of
2233
+ * 'http://jabber.org/protocol/muc#user' will also be matched. If
2234
+ * 'ignoreNamespaceFragment' is false, then only stanzas with
2235
+ * 'http://jabber.org/protocol/muc' will be matched.
2236
+ *
2237
+ * Deleting the handler
2238
+ * ~~~~~~~~~~~~~~~~~~~~
2239
+ * The return value should be saved if you wish to remove the handler
2240
+ * with deleteHandler().
2241
+ *
2242
+ * Parameters:
2243
+ * (Function) handler - The user callback.
2244
+ * (String) ns - The namespace to match.
2245
+ * (String) name - The stanza name to match.
2246
+ * (String|Array) type - The stanza type (or types if an array) to match.
2247
+ * (String) id - The stanza id attribute to match.
2248
+ * (String) from - The stanza from attribute to match.
2249
+ * (String) options - The handler options
2250
+ *
2251
+ * Returns:
2252
+ * A reference to the handler that can be used to remove it.
2253
+ */
2254
+ addHandler (handler, ns, name, type, id, from, options) {
2255
+ const hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
2256
+ this.addHandlers.push(hand);
2257
+ return hand;
2258
+ }
2259
+
2260
+ /** Function: deleteHandler
2261
+ * Delete a stanza handler for a connection.
2262
+ *
2263
+ * This function removes a stanza handler from the connection. The
2264
+ * handRef parameter is *not* the function passed to addHandler(),
2265
+ * but is the reference returned from addHandler().
2266
+ *
2267
+ * Parameters:
2268
+ * (Strophe.Handler) handRef - The handler reference.
2269
+ */
2270
+ deleteHandler (handRef) {
2271
+ // this must be done in the Idle loop so that we don't change
2272
+ // the handlers during iteration
2273
+ this.removeHandlers.push(handRef);
2274
+ // If a handler is being deleted while it is being added,
2275
+ // prevent it from getting added
2276
+ const i = this.addHandlers.indexOf(handRef);
2277
+ if (i >= 0) {
2278
+ this.addHandlers.splice(i, 1);
2279
+ }
2280
+ }
2281
+
2282
+ /** Function: registerSASLMechanisms
2283
+ *
2284
+ * Register the SASL mechanisms which will be supported by this instance of
2285
+ * Strophe.Connection (i.e. which this XMPP client will support).
2286
+ *
2287
+ * Parameters:
2288
+ * (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes
2289
+ *
2290
+ */
2291
+ registerSASLMechanisms (mechanisms) {
2292
+ this.mechanisms = {};
2293
+ mechanisms = mechanisms || [
2294
+ Strophe.SASLAnonymous,
2295
+ Strophe.SASLExternal,
2296
+ Strophe.SASLOAuthBearer,
2297
+ Strophe.SASLXOAuth2,
2298
+ Strophe.SASLPlain,
2299
+ Strophe.SASLSHA1
2300
+ ];
2301
+ mechanisms.forEach(m => this.registerSASLMechanism(m));
2302
+ }
2303
+
2304
+ /** Function: registerSASLMechanism
2305
+ *
2306
+ * Register a single SASL mechanism, to be supported by this client.
2307
+ *
2308
+ * Parameters:
2309
+ * (Object) mechanism - Object with a Strophe.SASLMechanism prototype
2310
+ *
2311
+ */
2312
+ registerSASLMechanism (Mechanism) {
2313
+ const mechanism = new Mechanism()
2314
+ this.mechanisms[mechanism.mechname] = mechanism;
2315
+ }
2316
+
2317
+ /** Function: disconnect
2318
+ * Start the graceful disconnection process.
2319
+ *
2320
+ * This function starts the disconnection process. This process starts
2321
+ * by sending unavailable presence and sending BOSH body of type
2322
+ * terminate. A timeout handler makes sure that disconnection happens
2323
+ * even if the BOSH server does not respond.
2324
+ * If the Connection object isn't connected, at least tries to abort all pending requests
2325
+ * so the connection object won't generate successful requests (which were already opened).
2326
+ *
2327
+ * The user supplied connection callback will be notified of the
2328
+ * progress as this process happens.
2329
+ *
2330
+ * Parameters:
2331
+ * (String) reason - The reason the disconnect is occuring.
2332
+ */
2333
+ disconnect (reason) {
2334
+ this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);
2335
+ if (reason) {
2336
+ Strophe.warn("Disconnect was called because: " + reason);
2337
+ } else {
2338
+ Strophe.info("Disconnect was called");
2339
+ }
2340
+ if (this.connected) {
2341
+ let pres = false;
2342
+ this.disconnecting = true;
2343
+ if (this.authenticated) {
2344
+ pres = $pres({
2345
+ 'xmlns': Strophe.NS.CLIENT,
2346
+ 'type': 'unavailable'
2347
+ });
2348
+ }
2349
+ // setup timeout handler
2350
+ this._disconnectTimeout = this._addSysTimedHandler(
2351
+ 3000, this._onDisconnectTimeout.bind(this));
2352
+ this._proto._disconnect(pres);
2353
+ } else {
2354
+ Strophe.warn("Disconnect was called before Strophe connected to the server");
2355
+ this._proto._abortAllRequests();
2356
+ this._doDisconnect();
2357
+ }
2358
+ }
2359
+
2360
+ /** PrivateFunction: _changeConnectStatus
2361
+ * _Private_ helper function that makes sure plugins and the user's
2362
+ * callback are notified of connection status changes.
2363
+ *
2364
+ * Parameters:
2365
+ * (Integer) status - the new connection status, one of the values
2366
+ * in Strophe.Status
2367
+ * (String) condition - the error condition or null
2368
+ * (XMLElement) elem - The triggering stanza.
2369
+ */
2370
+ _changeConnectStatus (status, condition, elem) {
2371
+ // notify all plugins listening for status changes
2372
+ for (const k in Strophe._connectionPlugins) {
2373
+ if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {
2374
+ const plugin = this[k];
2375
+ if (plugin.statusChanged) {
2376
+ try {
2377
+ plugin.statusChanged(status, condition);
2378
+ } catch (err) {
2379
+ Strophe.error(`${k} plugin caused an exception changing status: ${err}`);
2380
+ }
2381
+ }
2382
+ }
2383
+ }
2384
+ // notify the user's callback
2385
+ if (this.connect_callback) {
2386
+ try {
2387
+ this.connect_callback(status, condition, elem);
2388
+ } catch (e) {
2389
+ Strophe._handleError(e);
2390
+ Strophe.error(`User connection callback caused an exception: ${e}`);
2391
+ this.clientLogger(`User connection callback caused an exception: ${e}`);
2392
+ }
2393
+ }
2394
+ this.clientLogger('_changeConnectStatus is completed. Connection status changed to: ' + status);
2395
+ }
2396
+
2397
+ /** PrivateFunction: _doDisconnect
2398
+ * _Private_ function to disconnect.
2399
+ *
2400
+ * This is the last piece of the disconnection logic. This resets the
2401
+ * connection and alerts the user's connection callback.
2402
+ */
2403
+ _doDisconnect (condition) {
2404
+ if (typeof this._idleTimeout === "number") {
2405
+ clearTimeout(this._idleTimeout);
2406
+ }
2407
+
2408
+ // Cancel Disconnect Timeout
2409
+ if (this._disconnectTimeout !== null) {
2410
+ this.deleteTimedHandler(this._disconnectTimeout);
2411
+ this._disconnectTimeout = null;
2412
+ }
2413
+
2414
+ Strophe.debug("_doDisconnect was called");
2415
+ this._proto._doDisconnect();
2416
+
2417
+ this.authenticated = false;
2418
+ this.disconnecting = false;
2419
+ this.restored = false;
2420
+
2421
+ // delete handlers
2422
+ this.handlers = [];
2423
+ this.timedHandlers = [];
2424
+ this.removeTimeds = [];
2425
+ this.removeHandlers = [];
2426
+ this.addTimeds = [];
2427
+ this.addHandlers = [];
2428
+
2429
+ // tell the parent we disconnected
2430
+ this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);
2431
+ this.connected = false;
2432
+ }
2433
+
2434
+ /** PrivateFunction: _dataRecv
2435
+ * _Private_ handler to processes incoming data from the the connection.
2436
+ *
2437
+ * Except for _connect_cb handling the initial connection request,
2438
+ * this function handles the incoming data for all requests. This
2439
+ * function also fires stanza handlers that match each incoming
2440
+ * stanza.
2441
+ *
2442
+ * Parameters:
2443
+ * (Strophe.Request) req - The request that has data ready.
2444
+ * (string) req - The stanza a raw string (optiona).
2445
+ */
2446
+ _dataRecv (req, raw) {
2447
+ const elem = this._proto._reqToData(req);
2448
+ if (elem === null) { return; }
2449
+
2450
+ if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
2451
+ if (elem.nodeName === this._proto.strip && elem.childNodes.length) {
2452
+ this.xmlInput(elem.childNodes[0]);
2453
+ } else {
2454
+ this.xmlInput(elem);
2455
+ }
2456
+ }
2457
+ if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
2458
+ if (raw) {
2459
+ this.rawInput(raw);
2460
+ } else {
2461
+ this.rawInput(Strophe.serialize(elem));
2462
+ }
2463
+ }
2464
+
2465
+ // remove handlers scheduled for deletion
2466
+ while (this.removeHandlers.length > 0) {
2467
+ const hand = this.removeHandlers.pop();
2468
+ const i = this.handlers.indexOf(hand);
2469
+ if (i >= 0) {
2470
+ this.handlers.splice(i, 1);
2471
+ }
2472
+ }
2473
+
2474
+ // add handlers scheduled for addition
2475
+ while (this.addHandlers.length > 0) {
2476
+ this.handlers.push(this.addHandlers.pop());
2477
+ }
2478
+
2479
+ // handle graceful disconnect
2480
+ if (this.disconnecting && this._proto._emptyQueue()) {
2481
+ this._doDisconnect();
2482
+ return;
2483
+ }
2484
+
2485
+ const type = elem.getAttribute("type");
2486
+ if (type !== null && type === "terminate") {
2487
+ // Don't process stanzas that come in after disconnect
2488
+ if (this.disconnecting) {
2489
+ return;
2490
+ }
2491
+ // an error occurred
2492
+ let cond = elem.getAttribute("condition");
2493
+ const conflict = elem.getElementsByTagName("conflict");
2494
+ if (cond !== null) {
2495
+ if (cond === "remote-stream-error" && conflict.length > 0) {
2496
+ cond = "conflict";
2497
+ }
2498
+ this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
2499
+ } else {
2500
+ this._changeConnectStatus(
2501
+ Strophe.Status.CONNFAIL,
2502
+ Strophe.ErrorCondition.UNKOWN_REASON
2503
+ );
2504
+ }
2505
+ this._doDisconnect(cond);
2506
+ return;
2507
+ }
2508
+
2509
+ // send each incoming stanza through the handler chain
2510
+ Strophe.forEachChild(elem, null, child => {
2511
+ // process handlers
2512
+ const newList = this.handlers;
2513
+ this.handlers = [];
2514
+ for (let i=0; i < newList.length; i++) {
2515
+ const hand = newList[i];
2516
+ // encapsulate 'handler.run' not to lose the whole handler list if
2517
+ // one of the handlers throws an exception
2518
+ try {
2519
+ if (hand.isMatch(child) &&
2520
+ (this.authenticated || !hand.user)) {
2521
+ if (hand.run(child)) {
2522
+ this.handlers.push(hand);
2523
+ }
2524
+ } else {
2525
+ this.handlers.push(hand);
2526
+ }
2527
+ } catch(e) {
2528
+ // if the handler throws an exception, we consider it as false
2529
+ Strophe.warn('Removing Strophe handlers due to uncaught exception: '+e.message);
2530
+ }
2531
+ }
2532
+ });
2533
+ }
2534
+
2535
+ /** PrivateFunction: _connect_cb
2536
+ * _Private_ handler for initial connection request.
2537
+ *
2538
+ * This handler is used to process the initial connection request
2539
+ * response from the BOSH server. It is used to set up authentication
2540
+ * handlers and start the authentication process.
2541
+ *
2542
+ * SASL authentication will be attempted if available, otherwise
2543
+ * the code will fall back to legacy authentication.
2544
+ *
2545
+ * Parameters:
2546
+ * (Strophe.Request) req - The current request.
2547
+ * (Function) _callback - low level (xmpp) connect callback function.
2548
+ * Useful for plugins with their own xmpp connect callback (when they
2549
+ * want to do something special).
2550
+ */
2551
+ _connect_cb (req, _callback, raw) {
2552
+ Strophe.debug("_connect_cb was called");
2553
+ this.connected = true;
2554
+
2555
+ let bodyWrap;
2556
+ try {
2557
+ bodyWrap = this._proto._reqToData(req);
2558
+ } catch (e) {
2559
+ if (e.name !== Strophe.ErrorCondition.BAD_FORMAT) { throw e; }
2560
+ this._changeConnectStatus(
2561
+ Strophe.Status.CONNFAIL,
2562
+ Strophe.ErrorCondition.BAD_FORMAT
2563
+ );
2564
+ this._doDisconnect(Strophe.ErrorCondition.BAD_FORMAT);
2565
+ }
2566
+ if (!bodyWrap) { return; }
2567
+
2568
+ if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
2569
+ if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {
2570
+ this.xmlInput(bodyWrap.childNodes[0]);
2571
+ } else {
2572
+ this.xmlInput(bodyWrap);
2573
+ }
2574
+ }
2575
+ if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
2576
+ if (raw) {
2577
+ this.rawInput(raw);
2578
+ } else {
2579
+ this.rawInput(Strophe.serialize(bodyWrap));
2580
+ }
2581
+ }
2582
+
2583
+ const conncheck = this._proto._connect_cb(bodyWrap);
2584
+ if (conncheck === Strophe.Status.CONNFAIL) {
2585
+ return;
2586
+ }
2587
+
2588
+ // Check for the stream:features tag
2589
+ let hasFeatures;
2590
+ if (bodyWrap.getElementsByTagNameNS) {
2591
+ hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "features").length > 0;
2592
+ } else {
2593
+ hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 ||
2594
+ bodyWrap.getElementsByTagName("features").length > 0;
2595
+ }
2596
+ if (!hasFeatures) {
2597
+ this._proto._no_auth_received(_callback);
2598
+ return;
2599
+ }
2600
+
2601
+ const matched = Array.from(bodyWrap.getElementsByTagName("mechanism"))
2602
+ .map(m => this.mechanisms[m.textContent])
2603
+ .filter(m => m);
2604
+
2605
+ if (matched.length === 0) {
2606
+ if (bodyWrap.getElementsByTagName("auth").length === 0) {
2607
+ // There are no matching SASL mechanisms and also no legacy
2608
+ // auth available.
2609
+ this._proto._no_auth_received(_callback);
2610
+ return;
2611
+ }
2612
+ }
2613
+ if (this.do_authentication !== false) {
2614
+ this.authenticate(matched);
2615
+ }
2616
+ }
2617
+
2618
+ /** Function: sortMechanismsByPriority
2619
+ *
2620
+ * Sorts an array of objects with prototype SASLMechanism according to
2621
+ * their priorities.
2622
+ *
2623
+ * Parameters:
2624
+ * (Array) mechanisms - Array of SASL mechanisms.
2625
+ *
2626
+ */
2627
+ sortMechanismsByPriority (mechanisms) { // eslint-disable-line class-methods-use-this
2628
+ // Sorting mechanisms according to priority.
2629
+ for (let i=0; i < mechanisms.length - 1; ++i) {
2630
+ let higher = i;
2631
+ for (let j=i + 1; j < mechanisms.length; ++j) {
2632
+ if (mechanisms[j].priority > mechanisms[higher].priority) {
2633
+ higher = j;
2634
+ }
2635
+ }
2636
+ if (higher !== i) {
2637
+ const swap = mechanisms[i];
2638
+ mechanisms[i] = mechanisms[higher];
2639
+ mechanisms[higher] = swap;
2640
+ }
2641
+ }
2642
+ return mechanisms;
2643
+ }
2644
+
2645
+ /** Function: authenticate
2646
+ * Set up authentication
2647
+ *
2648
+ * Continues the initial connection request by setting up authentication
2649
+ * handlers and starting the authentication process.
2650
+ *
2651
+ * SASL authentication will be attempted if available, otherwise
2652
+ * the code will fall back to legacy authentication.
2653
+ *
2654
+ * Parameters:
2655
+ * (Array) matched - Array of SASL mechanisms supported.
2656
+ *
2657
+ */
2658
+ authenticate (matched) {
2659
+ if (!this._attemptSASLAuth(matched)) {
2660
+ this._attemptLegacyAuth();
2661
+ }
2662
+ }
2663
+
2664
+ /** PrivateFunction: _attemptSASLAuth
2665
+ *
2666
+ * Iterate through an array of SASL mechanisms and attempt authentication
2667
+ * with the highest priority (enabled) mechanism.
2668
+ *
2669
+ * Parameters:
2670
+ * (Array) mechanisms - Array of SASL mechanisms.
2671
+ *
2672
+ * Returns:
2673
+ * (Boolean) mechanism_found - true or false, depending on whether a
2674
+ * valid SASL mechanism was found with which authentication could be
2675
+ * started.
2676
+ */
2677
+ _attemptSASLAuth (mechanisms) {
2678
+ mechanisms = this.sortMechanismsByPriority(mechanisms || []);
2679
+ let mechanism_found = false;
2680
+ for (let i=0; i < mechanisms.length; ++i) {
2681
+ if (!mechanisms[i].test(this)) {
2682
+ continue;
2683
+ }
2684
+ this._sasl_success_handler = this._addSysHandler(
2685
+ this._sasl_success_cb.bind(this), null,
2686
+ "success", null, null);
2687
+ this._sasl_failure_handler = this._addSysHandler(
2688
+ this._sasl_failure_cb.bind(this), null,
2689
+ "failure", null, null);
2690
+ this._sasl_challenge_handler = this._addSysHandler(
2691
+ this._sasl_challenge_cb.bind(this), null,
2692
+ "challenge", null, null);
2693
+
2694
+ this._sasl_mechanism = mechanisms[i];
2695
+ this._sasl_mechanism.onStart(this);
2696
+
2697
+ const request_auth_exchange = $build("auth", {
2698
+ 'xmlns': Strophe.NS.SASL,
2699
+ 'mechanism': this._sasl_mechanism.mechname
2700
+ });
2701
+ if (this._sasl_mechanism.isClientFirst) {
2702
+ const response = this._sasl_mechanism.onChallenge(this, null);
2703
+ request_auth_exchange.t(btoa(response));
2704
+ }
2705
+ this.send(request_auth_exchange.tree());
2706
+ mechanism_found = true;
2707
+ break;
2708
+ }
2709
+ return mechanism_found;
2710
+ }
2711
+
2712
+ /** PrivateFunction: _sasl_challenge_cb
2713
+ * _Private_ handler for the SASL challenge
2714
+ *
2715
+ */
2716
+ _sasl_challenge_cb (elem) {
2717
+ const challenge = atob(Strophe.getText(elem));
2718
+ const response = this._sasl_mechanism.onChallenge(this, challenge);
2719
+ const stanza = $build('response', {'xmlns': Strophe.NS.SASL});
2720
+ if (response !== "") {
2721
+ stanza.t(btoa(response));
2722
+ }
2723
+ this.send(stanza.tree());
2724
+ return true;
2725
+ }
2726
+
2727
+ /** PrivateFunction: _attemptLegacyAuth
2728
+ *
2729
+ * Attempt legacy (i.e. non-SASL) authentication.
2730
+ */
2731
+ _attemptLegacyAuth () {
2732
+ if (Strophe.getNodeFromJid(this.jid) === null) {
2733
+ // we don't have a node, which is required for non-anonymous
2734
+ // client connections
2735
+ this._changeConnectStatus(
2736
+ Strophe.Status.CONNFAIL,
2737
+ Strophe.ErrorCondition.MISSING_JID_NODE
2738
+ );
2739
+ this.disconnect(Strophe.ErrorCondition.MISSING_JID_NODE);
2740
+ } else {
2741
+ // Fall back to legacy authentication
2742
+ this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
2743
+ this._addSysHandler(
2744
+ this._onLegacyAuthIQResult.bind(this),
2745
+ null, null, null, "_auth_1"
2746
+ );
2747
+ this.send($iq({
2748
+ 'type': "get",
2749
+ 'to': this.domain,
2750
+ 'id': "_auth_1"
2751
+ }).c("query", {xmlns: Strophe.NS.AUTH})
2752
+ .c("username", {}).t(Strophe.getNodeFromJid(this.jid))
2753
+ .tree());
2754
+ }
2755
+ }
2756
+
2757
+ /** PrivateFunction: _onLegacyAuthIQResult
2758
+ * _Private_ handler for legacy authentication.
2759
+ *
2760
+ * This handler is called in response to the initial <iq type='get'/>
2761
+ * for legacy authentication. It builds an authentication <iq/> and
2762
+ * sends it, creating a handler (calling back to _auth2_cb()) to
2763
+ * handle the result
2764
+ *
2765
+ * Parameters:
2766
+ * (XMLElement) elem - The stanza that triggered the callback.
2767
+ *
2768
+ * Returns:
2769
+ * false to remove the handler.
2770
+ */
2771
+ _onLegacyAuthIQResult (elem) { // eslint-disable-line no-unused-vars
2772
+ // build plaintext auth iq
2773
+ const iq = $iq({type: "set", id: "_auth_2"})
2774
+ .c('query', {xmlns: Strophe.NS.AUTH})
2775
+ .c('username', {}).t(Strophe.getNodeFromJid(this.jid))
2776
+ .up()
2777
+ .c('password').t(this.pass);
2778
+
2779
+ if (!Strophe.getResourceFromJid(this.jid)) {
2780
+ // since the user has not supplied a resource, we pick
2781
+ // a default one here. unlike other auth methods, the server
2782
+ // cannot do this for us.
2783
+ this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';
2784
+ }
2785
+ iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));
2786
+
2787
+ this._addSysHandler(this._auth2_cb.bind(this), null, null, null, "_auth_2");
2788
+ this.send(iq.tree());
2789
+ return false;
2790
+ }
2791
+
2792
+ /** PrivateFunction: _sasl_success_cb
2793
+ * _Private_ handler for succesful SASL authentication.
2794
+ *
2795
+ * Parameters:
2796
+ * (XMLElement) elem - The matching stanza.
2797
+ *
2798
+ * Returns:
2799
+ * false to remove the handler.
2800
+ */
2801
+ _sasl_success_cb (elem) {
2802
+ if (this._sasl_data["server-signature"]) {
2803
+ let serverSignature;
2804
+ const success = atob(Strophe.getText(elem));
2805
+ const attribMatch = /([a-z]+)=([^,]+)(,|$)/;
2806
+ const matches = success.match(attribMatch);
2807
+ if (matches[1] === "v") {
2808
+ serverSignature = matches[2];
2809
+ }
2810
+ if (serverSignature !== this._sasl_data["server-signature"]) {
2811
+ // remove old handlers
2812
+ this.deleteHandler(this._sasl_failure_handler);
2813
+ this._sasl_failure_handler = null;
2814
+ if (this._sasl_challenge_handler) {
2815
+ this.deleteHandler(this._sasl_challenge_handler);
2816
+ this._sasl_challenge_handler = null;
2817
+ }
2818
+ this._sasl_data = {};
2819
+ return this._sasl_failure_cb(null);
2820
+ }
2821
+ }
2822
+ Strophe.info("SASL authentication succeeded.");
2823
+
2824
+ if (this._sasl_mechanism) {
2825
+ this._sasl_mechanism.onSuccess();
2826
+ }
2827
+ // remove old handlers
2828
+ this.deleteHandler(this._sasl_failure_handler);
2829
+ this._sasl_failure_handler = null;
2830
+ if (this._sasl_challenge_handler) {
2831
+ this.deleteHandler(this._sasl_challenge_handler);
2832
+ this._sasl_challenge_handler = null;
2833
+ }
2834
+ const streamfeature_handlers = [];
2835
+ const wrapper = (handlers, elem) => {
2836
+ while (handlers.length) {
2837
+ this.deleteHandler(handlers.pop());
2838
+ }
2839
+ this._onStreamFeaturesAfterSASL(elem);
2840
+ return false;
2841
+ };
2842
+ streamfeature_handlers.push(
2843
+ this._addSysHandler(elem => wrapper(streamfeature_handlers, elem),
2844
+ null, "stream:features", null, null)
2845
+ );
2846
+
2847
+ streamfeature_handlers.push(
2848
+ this._addSysHandler(elem => wrapper(streamfeature_handlers, elem),
2849
+ Strophe.NS.STREAM, "features", null, null)
2850
+ );
2851
+
2852
+ // we must send an xmpp:restart now
2853
+ this._sendRestart();
2854
+ return false;
2855
+ }
2856
+
2857
+ /** PrivateFunction: _onStreamFeaturesAfterSASL
2858
+ * Parameters:
2859
+ * (XMLElement) elem - The matching stanza.
2860
+ *
2861
+ * Returns:
2862
+ * false to remove the handler.
2863
+ */
2864
+ _onStreamFeaturesAfterSASL (elem) {
2865
+ // save stream:features for future usage
2866
+ this.features = elem;
2867
+ for (let i=0; i < elem.childNodes.length; i++) {
2868
+ const child = elem.childNodes[i];
2869
+ if (child.nodeName === 'bind') {
2870
+ this.do_bind = true;
2871
+ }
2872
+ if (child.nodeName === 'session') {
2873
+ this.do_session = true;
2874
+ }
2875
+ }
2876
+
2877
+ if (!this.do_bind) {
2878
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
2879
+ return false;
2880
+ } else if (!this.options.explicitResourceBinding) {
2881
+ this.bind();
2882
+ } else {
2883
+ this._changeConnectStatus(Strophe.Status.BINDREQUIRED, null);
2884
+ }
2885
+ return false;
2886
+ }
2887
+
2888
+ /** Function: bind
2889
+ *
2890
+ * Sends an IQ to the XMPP server to bind a JID resource for this session.
2891
+ *
2892
+ * https://tools.ietf.org/html/rfc6120#section-7.5
2893
+ *
2894
+ * If `explicitResourceBinding` was set to a truthy value in the options
2895
+ * passed to the Strophe.Connection constructor, then this function needs
2896
+ * to be called explicitly by the client author.
2897
+ *
2898
+ * Otherwise it'll be called automatically as soon as the XMPP server
2899
+ * advertises the "urn:ietf:params:xml:ns:xmpp-bind" stream feature.
2900
+ */
2901
+ bind () {
2902
+ if (!this.do_bind) {
2903
+ Strophe.log(
2904
+ Strophe.LogLevel.INFO,
2905
+ `Strophe.Connection.prototype.bind called but "do_bind" is false`
2906
+ );
2907
+ return;
2908
+ }
2909
+ this._addSysHandler(
2910
+ this._onResourceBindResultIQ.bind(this),
2911
+ null, null, null, "_bind_auth_2");
2912
+
2913
+ const resource = Strophe.getResourceFromJid(this.jid);
2914
+ if (resource) {
2915
+ this.send($iq({type: "set", id: "_bind_auth_2"})
2916
+ .c('bind', {xmlns: Strophe.NS.BIND})
2917
+ .c('resource', {}).t(resource).tree());
2918
+ } else {
2919
+ this.send($iq({type: "set", id: "_bind_auth_2"})
2920
+ .c('bind', {xmlns: Strophe.NS.BIND})
2921
+ .tree());
2922
+ }
2923
+ }
2924
+
2925
+ /** PrivateFunction: _onResourceBindIQ
2926
+ * _Private_ handler for binding result and session start.
2927
+ *
2928
+ * Parameters:
2929
+ * (XMLElement) elem - The matching stanza.
2930
+ *
2931
+ * Returns:
2932
+ * false to remove the handler.
2933
+ */
2934
+ _onResourceBindResultIQ (elem) {
2935
+ if (elem.getAttribute("type") === "error") {
2936
+ Strophe.warn("Resource binding failed.");
2937
+ const conflict = elem.getElementsByTagName("conflict");
2938
+ let condition;
2939
+ if (conflict.length > 0) {
2940
+ condition = Strophe.ErrorCondition.CONFLICT;
2941
+ }
2942
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition, elem);
2943
+ return false;
2944
+ }
2945
+ // TODO - need to grab errors
2946
+ const bind = elem.getElementsByTagName("bind");
2947
+ if (bind.length > 0) {
2948
+ const jidNode = bind[0].getElementsByTagName("jid");
2949
+ if (jidNode.length > 0) {
2950
+ this.authenticated = true;
2951
+ this.jid = Strophe.getText(jidNode[0]);
2952
+ if (this.do_session) {
2953
+ this._establishSession();
2954
+ } else {
2955
+ this._changeConnectStatus(Strophe.Status.CONNECTED, null);
2956
+ }
2957
+ }
2958
+ } else {
2959
+ Strophe.warn("Resource binding failed.");
2960
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
2961
+ return false;
2962
+ }
2963
+ }
2964
+
2965
+ /** PrivateFunction: _establishSession
2966
+ * Send IQ request to establish a session with the XMPP server.
2967
+ *
2968
+ * See https://xmpp.org/rfcs/rfc3921.html#session
2969
+ *
2970
+ * Note: The protocol for session establishment has been determined as
2971
+ * unnecessary and removed in RFC-6121.
2972
+ */
2973
+ _establishSession () {
2974
+ if (!this.do_session) {
2975
+ throw new Error(`Strophe.Connection.prototype._establishSession `+
2976
+ `called but apparently ${Strophe.NS.SESSION} wasn't advertised by the server`);
2977
+ }
2978
+ this._addSysHandler(
2979
+ this._onSessionResultIQ.bind(this),
2980
+ null, null, null, "_session_auth_2");
2981
+
2982
+ this.send(
2983
+ $iq({type: "set", id: "_session_auth_2"})
2984
+ .c('session', {xmlns: Strophe.NS.SESSION})
2985
+ .tree());
2986
+ }
2987
+
2988
+ /** PrivateFunction: _onSessionResultIQ
2989
+ * _Private_ handler for the server's IQ response to a client's session
2990
+ * request.
2991
+ *
2992
+ * This sets Connection.authenticated to true on success, which
2993
+ * starts the processing of user handlers.
2994
+ *
2995
+ * See https://xmpp.org/rfcs/rfc3921.html#session
2996
+ *
2997
+ * Note: The protocol for session establishment has been determined as
2998
+ * unnecessary and removed in RFC-6121.
2999
+ *
3000
+ * Parameters:
3001
+ * (XMLElement) elem - The matching stanza.
3002
+ *
3003
+ * Returns:
3004
+ * false to remove the handler.
3005
+ */
3006
+ _onSessionResultIQ (elem) {
3007
+ if (elem.getAttribute("type") === "result") {
3008
+ this.authenticated = true;
3009
+ this._changeConnectStatus(Strophe.Status.CONNECTED, null);
3010
+ } else if (elem.getAttribute("type") === "error") {
3011
+ this.authenticated = false;
3012
+ Strophe.warn("Session creation failed.");
3013
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
3014
+ return false;
3015
+ }
3016
+ return false;
3017
+ }
3018
+
3019
+ /** PrivateFunction: _sasl_failure_cb
3020
+ * _Private_ handler for SASL authentication failure.
3021
+ *
3022
+ * Parameters:
3023
+ * (XMLElement) elem - The matching stanza.
3024
+ *
3025
+ * Returns:
3026
+ * false to remove the handler.
3027
+ */
3028
+ _sasl_failure_cb (elem) {
3029
+ // delete unneeded handlers
3030
+ if (this._sasl_success_handler) {
3031
+ this.deleteHandler(this._sasl_success_handler);
3032
+ this._sasl_success_handler = null;
3033
+ }
3034
+ if (this._sasl_challenge_handler) {
3035
+ this.deleteHandler(this._sasl_challenge_handler);
3036
+ this._sasl_challenge_handler = null;
3037
+ }
3038
+
3039
+ if(this._sasl_mechanism)
3040
+ this._sasl_mechanism.onFailure();
3041
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
3042
+ return false;
3043
+ }
3044
+
3045
+ /** PrivateFunction: _auth2_cb
3046
+ * _Private_ handler to finish legacy authentication.
3047
+ *
3048
+ * This handler is called when the result from the jabber:iq:auth
3049
+ * <iq/> stanza is returned.
3050
+ *
3051
+ * Parameters:
3052
+ * (XMLElement) elem - The stanza that triggered the callback.
3053
+ *
3054
+ * Returns:
3055
+ * false to remove the handler.
3056
+ */
3057
+ _auth2_cb (elem) {
3058
+ if (elem.getAttribute("type") === "result") {
3059
+ this.authenticated = true;
3060
+ this._changeConnectStatus(Strophe.Status.CONNECTED, null);
3061
+ } else if (elem.getAttribute("type") === "error") {
3062
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
3063
+ this.disconnect('authentication failed');
3064
+ }
3065
+ return false;
3066
+ }
3067
+
3068
+ /** PrivateFunction: _addSysTimedHandler
3069
+ * _Private_ function to add a system level timed handler.
3070
+ *
3071
+ * This function is used to add a Strophe.TimedHandler for the
3072
+ * library code. System timed handlers are allowed to run before
3073
+ * authentication is complete.
3074
+ *
3075
+ * Parameters:
3076
+ * (Integer) period - The period of the handler.
3077
+ * (Function) handler - The callback function.
3078
+ */
3079
+ _addSysTimedHandler (period, handler) {
3080
+ const thand = new Strophe.TimedHandler(period, handler);
3081
+ thand.user = false;
3082
+ this.addTimeds.push(thand);
3083
+ return thand;
3084
+ }
3085
+
3086
+ /** PrivateFunction: _addSysHandler
3087
+ * _Private_ function to add a system level stanza handler.
3088
+ *
3089
+ * This function is used to add a Strophe.Handler for the
3090
+ * library code. System stanza handlers are allowed to run before
3091
+ * authentication is complete.
3092
+ *
3093
+ * Parameters:
3094
+ * (Function) handler - The callback function.
3095
+ * (String) ns - The namespace to match.
3096
+ * (String) name - The stanza name to match.
3097
+ * (String) type - The stanza type attribute to match.
3098
+ * (String) id - The stanza id attribute to match.
3099
+ */
3100
+ _addSysHandler (handler, ns, name, type, id) {
3101
+ const hand = new Strophe.Handler(handler, ns, name, type, id);
3102
+ hand.user = false;
3103
+ this.addHandlers.push(hand);
3104
+ return hand;
3105
+ }
3106
+
3107
+ /** PrivateFunction: _onDisconnectTimeout
3108
+ * _Private_ timeout handler for handling non-graceful disconnection.
3109
+ *
3110
+ * If the graceful disconnect process does not complete within the
3111
+ * time allotted, this handler finishes the disconnect anyway.
3112
+ *
3113
+ * Returns:
3114
+ * false to remove the handler.
3115
+ */
3116
+ _onDisconnectTimeout () {
3117
+ Strophe.debug("_onDisconnectTimeout was called");
3118
+ this._changeConnectStatus(Strophe.Status.CONNTIMEOUT, null);
3119
+ this._proto._onDisconnectTimeout();
3120
+ // actually disconnect
3121
+ this._doDisconnect();
3122
+ return false;
3123
+ }
3124
+
3125
+ /** PrivateFunction: _onIdle
3126
+ * _Private_ handler to process events during idle cycle.
3127
+ *
3128
+ * This handler is called every 100ms to fire timed handlers that
3129
+ * are ready and keep poll requests going.
3130
+ */
3131
+ _onIdle () {
3132
+ // add timed handlers scheduled for addition
3133
+ // NOTE: we add before remove in the case a timed handler is
3134
+ // added and then deleted before the next _onIdle() call.
3135
+ while (this.addTimeds.length > 0) {
3136
+ this.timedHandlers.push(this.addTimeds.pop());
3137
+ }
3138
+
3139
+ // remove timed handlers that have been scheduled for deletion
3140
+ while (this.removeTimeds.length > 0) {
3141
+ const thand = this.removeTimeds.pop();
3142
+ const i = this.timedHandlers.indexOf(thand);
3143
+ if (i >= 0) {
3144
+ this.timedHandlers.splice(i, 1);
3145
+ }
3146
+ }
3147
+
3148
+ // call ready timed handlers
3149
+ const now = new Date().getTime();
3150
+ const newList = [];
3151
+ for (let i=0; i < this.timedHandlers.length; i++) {
3152
+ const thand = this.timedHandlers[i];
3153
+ if (this.authenticated || !thand.user) {
3154
+ const since = thand.lastCalled + thand.period;
3155
+ if (since - now <= 0) {
3156
+ if (thand.run()) {
3157
+ newList.push(thand);
3158
+ }
3159
+ } else {
3160
+ newList.push(thand);
3161
+ }
3162
+ }
3163
+ }
3164
+ this.timedHandlers = newList;
3165
+ clearTimeout(this._idleTimeout);
3166
+ this._proto._onIdle();
3167
+
3168
+ // reactivate the timer only if connected
3169
+ if (this.connected) {
3170
+ this._idleTimeout = setTimeout(() => this._onIdle(), 100);
3171
+ }
3172
+ }
3173
+ };
3174
+
3175
+ /** Class: Strophe.SASLMechanism
3176
+ *
3177
+ * Encapsulates an SASL authentication mechanism.
3178
+ *
3179
+ * User code may override the priority for each mechanism or disable it completely.
3180
+ * See <priority> for information about changing priority and <test> for informatian on
3181
+ * how to disable a mechanism.
3182
+ *
3183
+ * By default, all mechanisms are enabled and the priorities are
3184
+ *
3185
+ * SCRAM-SHA-1 - 60
3186
+ * PLAIN - 50
3187
+ * OAUTHBEARER - 40
3188
+ * X-OAUTH2 - 30
3189
+ * ANONYMOUS - 20
3190
+ * EXTERNAL - 10
3191
+ *
3192
+ * See: Strophe.Connection.addSupportedSASLMechanisms
3193
+ */
3194
+ Strophe.SASLMechanism = class SASLMechanism {
3195
+
3196
+ /**
3197
+ * PrivateConstructor: Strophe.SASLMechanism
3198
+ * SASL auth mechanism abstraction.
3199
+ *
3200
+ * Parameters:
3201
+ * (String) name - SASL Mechanism name.
3202
+ * (Boolean) isClientFirst - If client should send response first without challenge.
3203
+ * (Number) priority - Priority.
3204
+ *
3205
+ * Returns:
3206
+ * A new Strophe.SASLMechanism object.
3207
+ */
3208
+ constructor (name, isClientFirst, priority) {
3209
+ /** PrivateVariable: mechname
3210
+ * Mechanism name.
3211
+ */
3212
+ this.mechname = name;
3213
+
3214
+ /** PrivateVariable: isClientFirst
3215
+ * If client sends response without initial server challenge.
3216
+ */
3217
+ this.isClientFirst = isClientFirst;
3218
+
3219
+ /** Variable: priority
3220
+ * Determines which <SASLMechanism> is chosen for authentication (Higher is better).
3221
+ * Users may override this to prioritize mechanisms differently.
3222
+ *
3223
+ * Example: (This will cause Strophe to choose the mechanism that the server sent first)
3224
+ *
3225
+ * > Strophe.SASLPlain.priority = Strophe.SASLSHA1.priority;
3226
+ *
3227
+ * See <SASL mechanisms> for a list of available mechanisms.
3228
+ *
3229
+ */
3230
+ this.priority = priority;
3231
+ }
3232
+
3233
+ /**
3234
+ * Function: test
3235
+ * Checks if mechanism able to run.
3236
+ * To disable a mechanism, make this return false;
3237
+ *
3238
+ * To disable plain authentication run
3239
+ * > Strophe.SASLPlain.test = function() {
3240
+ * > return false;
3241
+ * > }
3242
+ *
3243
+ * See <SASL mechanisms> for a list of available mechanisms.
3244
+ *
3245
+ * Parameters:
3246
+ * (Strophe.Connection) connection - Target Connection.
3247
+ *
3248
+ * Returns:
3249
+ * (Boolean) If mechanism was able to run.
3250
+ */
3251
+ test () { // eslint-disable-line class-methods-use-this
3252
+ return true;
3253
+ }
3254
+
3255
+ /** PrivateFunction: onStart
3256
+ * Called before starting mechanism on some connection.
3257
+ *
3258
+ * Parameters:
3259
+ * (Strophe.Connection) connection - Target Connection.
3260
+ */
3261
+ onStart (connection) {
3262
+ this._connection = connection;
3263
+ }
3264
+
3265
+ /** PrivateFunction: onChallenge
3266
+ * Called by protocol implementation on incoming challenge. If client is
3267
+ * first (isClientFirst === true) challenge will be null on the first call.
3268
+ *
3269
+ * Parameters:
3270
+ * (Strophe.Connection) connection - Target Connection.
3271
+ * (String) challenge - current challenge to handle.
3272
+ *
3273
+ * Returns:
3274
+ * (String) Mechanism response.
3275
+ */
3276
+ onChallenge (connection, challenge) { // eslint-disable-line
3277
+ throw new Error("You should implement challenge handling!");
3278
+ }
3279
+
3280
+ /** PrivateFunction: onFailure
3281
+ * Protocol informs mechanism implementation about SASL failure.
3282
+ */
3283
+ onFailure () {
3284
+ this._connection = null;
3285
+ }
3286
+
3287
+ /** PrivateFunction: onSuccess
3288
+ * Protocol informs mechanism implementation about SASL success.
3289
+ */
3290
+ onSuccess () {
3291
+ this._connection = null;
3292
+ }
3293
+ };
3294
+
3295
+ /** Constants: SASL mechanisms
3296
+ * Available authentication mechanisms
3297
+ *
3298
+ * Strophe.SASLAnonymous - SASL ANONYMOUS authentication.
3299
+ * Strophe.SASLPlain - SASL PLAIN authentication.
3300
+ * Strophe.SASLSHA1 - SASL SCRAM-SHA-1 authentication
3301
+ * Strophe.SASLOAuthBearer - SASL OAuth Bearer authentication
3302
+ * Strophe.SASLExternal - SASL EXTERNAL authentication
3303
+ * Strophe.SASLXOAuth2 - SASL X-OAuth2 authentication
3304
+ */
3305
+
3306
+ // Building SASL callbacks
3307
+
3308
+ Strophe.SASLAnonymous = class SASLAnonymous extends Strophe.SASLMechanism {
3309
+
3310
+ /** PrivateConstructor: SASLAnonymous
3311
+ * SASL ANONYMOUS authentication.
3312
+ */
3313
+ constructor (mechname='ANONYMOUS', isClientFirst=false, priority=20) {
3314
+ super(mechname, isClientFirst, priority);
3315
+ }
3316
+
3317
+ test (connection) { // eslint-disable-line class-methods-use-this
3318
+ return connection.authcid === null;
3319
+ }
3320
+ }
3321
+
3322
+
3323
+ Strophe.SASLPlain = class SASLPlain extends Strophe.SASLMechanism {
3324
+
3325
+ /** PrivateConstructor: SASLPlain
3326
+ * SASL PLAIN authentication.
3327
+ */
3328
+ constructor (mechname='PLAIN', isClientFirst=true, priority=50) {
3329
+ super(mechname, isClientFirst, priority);
3330
+ }
3331
+
3332
+ test (connection) { // eslint-disable-line class-methods-use-this
3333
+ return connection.authcid !== null;
3334
+ }
3335
+
3336
+ onChallenge (connection) { // eslint-disable-line class-methods-use-this
3337
+ const { authcid, authzid, domain, pass } = connection;
3338
+ if (!domain) {
3339
+ throw new Error("SASLPlain onChallenge: domain is not defined!");
3340
+ }
3341
+ // Only include authzid if it differs from authcid.
3342
+ // See: https://tools.ietf.org/html/rfc6120#section-6.3.8
3343
+ let auth_str = (authzid !== `${authcid}@${domain}`) ? authzid : '';
3344
+ auth_str = auth_str + "\u0000";
3345
+ auth_str = auth_str + authcid;
3346
+ auth_str = auth_str + "\u0000";
3347
+ auth_str = auth_str + pass;
3348
+ return utils.utf16to8(auth_str);
3349
+ }
3350
+ }
3351
+
3352
+
3353
+ Strophe.SASLSHA1 = class SASLSHA1 extends Strophe.SASLMechanism {
3354
+
3355
+ /** PrivateConstructor: SASLSHA1
3356
+ * SASL SCRAM SHA 1 authentication.
3357
+ */
3358
+ constructor (mechname='SCRAM-SHA-1', isClientFirst=true, priority=60) {
3359
+ super(mechname, isClientFirst, priority);
3360
+ }
3361
+
3362
+ test (connection) { // eslint-disable-line class-methods-use-this
3363
+ return connection.authcid !== null;
3364
+ }
3365
+
3366
+ onChallenge (connection, challenge, test_cnonce) {
3367
+ const cnonce = test_cnonce || MD5.hexdigest("" + (Math.random() * 1234567890));
3368
+ let auth_str = "n=" + utils.utf16to8(connection.authcid);
3369
+ auth_str += ",r=";
3370
+ auth_str += cnonce;
3371
+ connection._sasl_data.cnonce = cnonce;
3372
+ connection._sasl_data["client-first-message-bare"] = auth_str;
3373
+ auth_str = "n,," + auth_str;
3374
+
3375
+ this.onChallenge = (connection, challenge) => {
3376
+ let nonce, salt, iter, Hi, U, U_old, i, k;
3377
+ let responseText = "c=biws,";
3378
+ let authMessage = `${connection._sasl_data["client-first-message-bare"]},${challenge},`;
3379
+ const cnonce = connection._sasl_data.cnonce;
3380
+ const attribMatch = /([a-z]+)=([^,]+)(,|$)/;
3381
+
3382
+ while (challenge.match(attribMatch)) {
3383
+ const matches = challenge.match(attribMatch);
3384
+ challenge = challenge.replace(matches[0], "");
3385
+ switch (matches[1]) {
3386
+ case "r":
3387
+ nonce = matches[2];
3388
+ break;
3389
+ case "s":
3390
+ salt = matches[2];
3391
+ break;
3392
+ case "i":
3393
+ iter = matches[2];
3394
+ break;
3395
+ }
3396
+ }
3397
+
3398
+ if (nonce.substr(0, cnonce.length) !== cnonce) {
3399
+ connection._sasl_data = {};
3400
+ return connection._sasl_failure_cb();
3401
+ }
3402
+
3403
+ responseText += "r=" + nonce;
3404
+ authMessage += responseText;
3405
+
3406
+ salt = atob(salt);
3407
+ salt += "\x00\x00\x00\x01";
3408
+
3409
+ const pass = utils.utf16to8(connection.pass);
3410
+ Hi = U_old = SHA1.core_hmac_sha1(pass, salt);
3411
+ for (i=1; i<iter; i++) {
3412
+ U = SHA1.core_hmac_sha1(pass, SHA1.binb2str(U_old));
3413
+ for (k = 0; k < 5; k++) {
3414
+ Hi[k] ^= U[k];
3415
+ }
3416
+ U_old = U;
3417
+ }
3418
+ Hi = SHA1.binb2str(Hi);
3419
+
3420
+ const clientKey = SHA1.core_hmac_sha1(Hi, "Client Key");
3421
+ const serverKey = SHA1.str_hmac_sha1(Hi, "Server Key");
3422
+ const clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage);
3423
+ connection._sasl_data["server-signature"] = SHA1.b64_hmac_sha1(serverKey, authMessage);
3424
+
3425
+ for (k = 0; k < 5; k++) {
3426
+ clientKey[k] ^= clientSignature[k];
3427
+ }
3428
+ responseText += ",p=" + btoa(SHA1.binb2str(clientKey));
3429
+ return responseText;
3430
+ }
3431
+ return auth_str;
3432
+ }
3433
+ }
3434
+
3435
+
3436
+ Strophe.SASLOAuthBearer = class SASLOAuthBearer extends Strophe.SASLMechanism {
3437
+
3438
+ /** PrivateConstructor: SASLOAuthBearer
3439
+ * SASL OAuth Bearer authentication.
3440
+ */
3441
+ constructor (mechname='OAUTHBEARER', isClientFirst=true, priority=40) {
3442
+ super(mechname, isClientFirst, priority);
3443
+ }
3444
+
3445
+ test (connection) { // eslint-disable-line class-methods-use-this
3446
+ return connection.pass !== null;
3447
+ }
3448
+
3449
+ onChallenge (connection) { // eslint-disable-line class-methods-use-this
3450
+ let auth_str = 'n,';
3451
+ if (connection.authcid !== null) {
3452
+ auth_str = auth_str + 'a=' + connection.authzid;
3453
+ }
3454
+ auth_str = auth_str + ',';
3455
+ auth_str = auth_str + "\u0001";
3456
+ auth_str = auth_str + 'auth=Bearer ';
3457
+ auth_str = auth_str + connection.pass;
3458
+ auth_str = auth_str + "\u0001";
3459
+ auth_str = auth_str + "\u0001";
3460
+ return utils.utf16to8(auth_str);
3461
+ }
3462
+ }
3463
+
3464
+
3465
+ Strophe.SASLExternal = class SASLExternal extends Strophe.SASLMechanism {
3466
+
3467
+ /** PrivateConstructor: SASLExternal
3468
+ * SASL EXTERNAL authentication.
3469
+ *
3470
+ * The EXTERNAL mechanism allows a client to request the server to use
3471
+ * credentials established by means external to the mechanism to
3472
+ * authenticate the client. The external means may be, for instance,
3473
+ * TLS services.
3474
+ */
3475
+ constructor (mechname='EXTERNAL', isClientFirst=true, priority=10) {
3476
+ super(mechname, isClientFirst, priority);
3477
+ }
3478
+
3479
+ onChallenge (connection) { // eslint-disable-line class-methods-use-this
3480
+ /** According to XEP-178, an authzid SHOULD NOT be presented when the
3481
+ * authcid contained or implied in the client certificate is the JID (i.e.
3482
+ * authzid) with which the user wants to log in as.
3483
+ *
3484
+ * To NOT send the authzid, the user should therefore set the authcid equal
3485
+ * to the JID when instantiating a new Strophe.Connection object.
3486
+ */
3487
+ return connection.authcid === connection.authzid ? '' : connection.authzid;
3488
+ }
3489
+ };
3490
+
3491
+
3492
+ Strophe.SASLXOAuth2 = class SASLXOAuth2 extends Strophe.SASLMechanism {
3493
+
3494
+ /** PrivateConstructor: SASLXOAuth2
3495
+ * SASL X-OAuth2 authentication.
3496
+ */
3497
+ constructor (mechname='X-OAUTH2', isClientFirst=true, priority=30) {
3498
+ super(mechname, isClientFirst, priority);
3499
+ }
3500
+
3501
+ test (connection) { // eslint-disable-line class-methods-use-this
3502
+ return connection.pass !== null;
3503
+ }
3504
+
3505
+ onChallenge (connection) { // eslint-disable-line class-methods-use-this
3506
+ let auth_str = '\u0000';
3507
+ if (connection.authcid !== null) {
3508
+ auth_str = auth_str + connection.authzid;
3509
+ }
3510
+ auth_str = auth_str + "\u0000";
3511
+ auth_str = auth_str + connection.pass;
3512
+ return utils.utf16to8(auth_str);
3513
+ }
3514
+ };
3515
+
3516
+ export { SHA1, MD5 };
3517
+
3518
+ export default {
3519
+ 'Strophe': Strophe,
3520
+ '$build': $build,
3521
+ '$iq': $iq,
3522
+ '$msg': $msg,
3523
+ '$pres': $pres,
3524
+ 'SHA1': SHA1,
3525
+ 'MD5': MD5,
3526
+ 'b64_hmac_sha1': SHA1.b64_hmac_sha1,
3527
+ 'b64_sha1': SHA1.b64_sha1,
3528
+ 'str_hmac_sha1': SHA1.str_hmac_sha1,
3529
+ 'str_sha1': SHA1.str_sha1
3530
+ };