quickblox 2.17.2-logger → 2.17.4-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 +36571 -38627
  3. package/quickblox.min.js +1 -1
  4. package/src/libs/strophe/strophe.common.js +6668 -0
  5. package/src/libs/strophe/strophe.esm.js +6660 -0
  6. package/src/libs/strophe/strophe.umd.js +6873 -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 +8634 -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 +3535 -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 +561 -0
  58. package/strophejs-1.4.0/src/worker-websocket.js +152 -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,3535 @@
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
+ if (this.clientLogger) this.clientLogger('Strophe call unexpected method restore.');
1848
+ }
1849
+
1850
+ /** PrivateFunction: _sessionCachingSupported
1851
+ * Checks whether sessionStorage and JSON are supported and whether we're
1852
+ * using BOSH.
1853
+ */
1854
+ _sessionCachingSupported () {
1855
+ if (this._proto instanceof Strophe.Bosh) {
1856
+ if (!JSON) { return false; }
1857
+ try {
1858
+ sessionStorage.setItem('_strophe_', '_strophe_');
1859
+ sessionStorage.removeItem('_strophe_');
1860
+ } catch (e) {
1861
+ return false;
1862
+ }
1863
+ return true;
1864
+ }
1865
+ return false;
1866
+ }
1867
+
1868
+ /** Function: xmlInput
1869
+ * User overrideable function that receives XML data coming into the
1870
+ * connection.
1871
+ *
1872
+ * The default function does nothing. User code can override this with
1873
+ * > Strophe.Connection.xmlInput = function (elem) {
1874
+ * > (user code)
1875
+ * > };
1876
+ *
1877
+ * Due to limitations of current Browsers' XML-Parsers the opening and closing
1878
+ * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
1879
+ *
1880
+ * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
1881
+ * <Strophe.Bosh.strip> if you want to strip this tag.
1882
+ *
1883
+ * Parameters:
1884
+ * (XMLElement) elem - The XML data received by the connection.
1885
+ */
1886
+ xmlInput (elem) { // eslint-disable-line
1887
+ return;
1888
+ }
1889
+
1890
+ /** Function: xmlOutput
1891
+ * User overrideable function that receives XML data sent to the
1892
+ * connection.
1893
+ *
1894
+ * The default function does nothing. User code can override this with
1895
+ * > Strophe.Connection.xmlOutput = function (elem) {
1896
+ * > (user code)
1897
+ * > };
1898
+ *
1899
+ * Due to limitations of current Browsers' XML-Parsers the opening and closing
1900
+ * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
1901
+ *
1902
+ * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
1903
+ * <Strophe.Bosh.strip> if you want to strip this tag.
1904
+ *
1905
+ * Parameters:
1906
+ * (XMLElement) elem - The XMLdata sent by the connection.
1907
+ */
1908
+ xmlOutput (elem) { // eslint-disable-line
1909
+ return;
1910
+ }
1911
+
1912
+ /** Function: rawInput
1913
+ * User overrideable function that receives raw data coming into the
1914
+ * connection.
1915
+ *
1916
+ * The default function does nothing. User code can override this with
1917
+ * > Strophe.Connection.rawInput = function (data) {
1918
+ * > (user code)
1919
+ * > };
1920
+ *
1921
+ * Parameters:
1922
+ * (String) data - The data received by the connection.
1923
+ */
1924
+ rawInput (data) { // eslint-disable-line
1925
+ return;
1926
+ }
1927
+
1928
+ /** Function: rawOutput
1929
+ * User overrideable function that receives raw data sent to the
1930
+ * connection.
1931
+ *
1932
+ * The default function does nothing. User code can override this with
1933
+ * > Strophe.Connection.rawOutput = function (data) {
1934
+ * > (user code)
1935
+ * > };
1936
+ *
1937
+ * Parameters:
1938
+ * (String) data - The data sent by the connection.
1939
+ */
1940
+ rawOutput (data) { // eslint-disable-line
1941
+ return;
1942
+ }
1943
+
1944
+ /** Function: nextValidRid
1945
+ * User overrideable function that receives the new valid rid.
1946
+ *
1947
+ * The default function does nothing. User code can override this with
1948
+ * > Strophe.Connection.nextValidRid = function (rid) {
1949
+ * > (user code)
1950
+ * > };
1951
+ *
1952
+ * Parameters:
1953
+ * (Number) rid - The next valid rid
1954
+ */
1955
+ nextValidRid (rid) { // eslint-disable-line
1956
+ return;
1957
+ }
1958
+
1959
+ /** Function: send
1960
+ * Send a stanza.
1961
+ *
1962
+ * This function is called to push data onto the send queue to
1963
+ * go out over the wire. Whenever a request is sent to the BOSH
1964
+ * server, all pending data is sent and the queue is flushed.
1965
+ *
1966
+ * Parameters:
1967
+ * (XMLElement |
1968
+ * [XMLElement] |
1969
+ * Strophe.Builder) elem - The stanza to send.
1970
+ */
1971
+ send (elem) {
1972
+ if (elem === null) { return ; }
1973
+ if (typeof(elem.sort) === "function") {
1974
+ for (let i=0; i < elem.length; i++) {
1975
+ this._queueData(elem[i]);
1976
+ }
1977
+ } else if (typeof(elem.tree) === "function") {
1978
+ this._queueData(elem.tree());
1979
+ } else {
1980
+ this._queueData(elem);
1981
+ }
1982
+ this._proto._send();
1983
+ }
1984
+
1985
+ /** Function: flush
1986
+ * Immediately send any pending outgoing data.
1987
+ *
1988
+ * Normally send() queues outgoing data until the next idle period
1989
+ * (100ms), which optimizes network use in the common cases when
1990
+ * several send()s are called in succession. flush() can be used to
1991
+ * immediately send all pending data.
1992
+ */
1993
+ flush () {
1994
+ // cancel the pending idle period and run the idle function
1995
+ // immediately
1996
+ clearTimeout(this._idleTimeout);
1997
+ this._onIdle();
1998
+ }
1999
+
2000
+ /** Function: sendPresence
2001
+ * Helper function to send presence stanzas. The main benefit is for
2002
+ * sending presence stanzas for which you expect a responding presence
2003
+ * stanza with the same id (for example when leaving a chat room).
2004
+ *
2005
+ * Parameters:
2006
+ * (XMLElement) elem - The stanza to send.
2007
+ * (Function) callback - The callback function for a successful request.
2008
+ * (Function) errback - The callback function for a failed or timed
2009
+ * out request. On timeout, the stanza will be null.
2010
+ * (Integer) timeout - The time specified in milliseconds for a
2011
+ * timeout to occur.
2012
+ *
2013
+ * Returns:
2014
+ * The id used to send the presence.
2015
+ */
2016
+ sendPresence (elem, callback, errback, timeout) {
2017
+ let timeoutHandler = null;
2018
+ if (typeof(elem.tree) === "function") {
2019
+ elem = elem.tree();
2020
+ }
2021
+ let id = elem.getAttribute('id');
2022
+ if (!id) { // inject id if not found
2023
+ id = this.getUniqueId("sendPresence");
2024
+ elem.setAttribute("id", id);
2025
+ }
2026
+
2027
+ if (typeof callback === "function" || typeof errback === "function") {
2028
+ const handler = this.addHandler(stanza => {
2029
+ // remove timeout handler if there is one
2030
+ if (timeoutHandler) {
2031
+ this.deleteTimedHandler(timeoutHandler);
2032
+ }
2033
+ if (stanza.getAttribute('type') === 'error') {
2034
+ if (errback) {
2035
+ errback(stanza);
2036
+ }
2037
+ } else if (callback) {
2038
+ callback(stanza);
2039
+ }
2040
+ }, null, 'presence', null, id);
2041
+
2042
+ // if timeout specified, set up a timeout handler.
2043
+ if (timeout) {
2044
+ timeoutHandler = this.addTimedHandler(timeout, () => {
2045
+ // get rid of normal handler
2046
+ this.deleteHandler(handler);
2047
+ // call errback on timeout with null stanza
2048
+ if (errback) {
2049
+ errback(null);
2050
+ }
2051
+ return false;
2052
+ });
2053
+ }
2054
+ }
2055
+ this.send(elem);
2056
+ return id;
2057
+ }
2058
+
2059
+ /** Function: sendIQ
2060
+ * Helper function to send IQ stanzas.
2061
+ *
2062
+ * Parameters:
2063
+ * (XMLElement) elem - The stanza to send.
2064
+ * (Function) callback - The callback function for a successful request.
2065
+ * (Function) errback - The callback function for a failed or timed
2066
+ * out request. On timeout, the stanza will be null.
2067
+ * (Integer) timeout - The time specified in milliseconds for a
2068
+ * timeout to occur.
2069
+ *
2070
+ * Returns:
2071
+ * The id used to send the IQ.
2072
+ */
2073
+ sendIQ (elem, callback, errback, timeout) {
2074
+ let timeoutHandler = null;
2075
+ if (typeof(elem.tree) === "function") {
2076
+ elem = elem.tree();
2077
+ }
2078
+ let id = elem.getAttribute('id');
2079
+ if (!id) { // inject id if not found
2080
+ id = this.getUniqueId("sendIQ");
2081
+ elem.setAttribute("id", id);
2082
+ }
2083
+
2084
+ if (typeof callback === "function" || typeof errback === "function") {
2085
+ const handler = this.addHandler(stanza => {
2086
+ // remove timeout handler if there is one
2087
+ if (timeoutHandler) {
2088
+ this.deleteTimedHandler(timeoutHandler);
2089
+ }
2090
+ const iqtype = stanza.getAttribute('type');
2091
+ if (iqtype === 'result') {
2092
+ if (callback) {
2093
+ callback(stanza);
2094
+ }
2095
+ } else if (iqtype === 'error') {
2096
+ if (errback) {
2097
+ errback(stanza);
2098
+ }
2099
+ } else {
2100
+ const error = new Error(`Got bad IQ type of ${iqtype}`);
2101
+ error.name = "StropheError";
2102
+ throw(error);
2103
+ }
2104
+ }, null, 'iq', ['error', 'result'], id);
2105
+
2106
+ // if timeout specified, set up a timeout handler.
2107
+ if (timeout) {
2108
+ timeoutHandler = this.addTimedHandler(timeout, () => {
2109
+ // get rid of normal handler
2110
+ this.deleteHandler(handler);
2111
+ // call errback on timeout with null stanza
2112
+ if (errback) {
2113
+ errback(null);
2114
+ }
2115
+ return false;
2116
+ });
2117
+ }
2118
+ }
2119
+ this.send(elem);
2120
+ return id;
2121
+ }
2122
+
2123
+ /** PrivateFunction: _queueData
2124
+ * Queue outgoing data for later sending. Also ensures that the data
2125
+ * is a DOMElement.
2126
+ */
2127
+ _queueData (element) {
2128
+ if (element === null ||
2129
+ !element.tagName ||
2130
+ !element.childNodes) {
2131
+ const error = new Error("Cannot queue non-DOMElement.");
2132
+ error.name = "StropheError";
2133
+ throw(error);
2134
+ }
2135
+ this._data.push(element);
2136
+ }
2137
+
2138
+ /** PrivateFunction: _sendRestart
2139
+ * Send an xmpp:restart stanza.
2140
+ */
2141
+ _sendRestart () {
2142
+ this._data.push("restart");
2143
+ this._proto._sendRestart();
2144
+ this._idleTimeout = setTimeout(() => this._onIdle(), 100);
2145
+ if (this.clientLogger) this.clientLogger('Strophe _sendRestart method is called.');
2146
+ }
2147
+
2148
+ /** Function: addTimedHandler
2149
+ * Add a timed handler to the connection.
2150
+ *
2151
+ * This function adds a timed handler. The provided handler will
2152
+ * be called every period milliseconds until it returns false,
2153
+ * the connection is terminated, or the handler is removed. Handlers
2154
+ * that wish to continue being invoked should return true.
2155
+ *
2156
+ * Because of method binding it is necessary to save the result of
2157
+ * this function if you wish to remove a handler with
2158
+ * deleteTimedHandler().
2159
+ *
2160
+ * Note that user handlers are not active until authentication is
2161
+ * successful.
2162
+ *
2163
+ * Parameters:
2164
+ * (Integer) period - The period of the handler.
2165
+ * (Function) handler - The callback function.
2166
+ *
2167
+ * Returns:
2168
+ * A reference to the handler that can be used to remove it.
2169
+ */
2170
+ addTimedHandler (period, handler) {
2171
+ const thand = new Strophe.TimedHandler(period, handler);
2172
+ this.addTimeds.push(thand);
2173
+ return thand;
2174
+ }
2175
+
2176
+ /** Function: deleteTimedHandler
2177
+ * Delete a timed handler for a connection.
2178
+ *
2179
+ * This function removes a timed handler from the connection. The
2180
+ * handRef parameter is *not* the function passed to addTimedHandler(),
2181
+ * but is the reference returned from addTimedHandler().
2182
+ *
2183
+ * Parameters:
2184
+ * (Strophe.TimedHandler) handRef - The handler reference.
2185
+ */
2186
+ deleteTimedHandler (handRef) {
2187
+ // this must be done in the Idle loop so that we don't change
2188
+ // the handlers during iteration
2189
+ this.removeTimeds.push(handRef);
2190
+ }
2191
+
2192
+ /** Function: addHandler
2193
+ * Add a stanza handler for the connection.
2194
+ *
2195
+ * This function adds a stanza handler to the connection. The
2196
+ * handler callback will be called for any stanza that matches
2197
+ * the parameters. Note that if multiple parameters are supplied,
2198
+ * they must all match for the handler to be invoked.
2199
+ *
2200
+ * The handler will receive the stanza that triggered it as its argument.
2201
+ * *The handler should return true if it is to be invoked again;
2202
+ * returning false will remove the handler after it returns.*
2203
+ *
2204
+ * As a convenience, the ns parameters applies to the top level element
2205
+ * and also any of its immediate children. This is primarily to make
2206
+ * matching /iq/query elements easy.
2207
+ *
2208
+ * Options
2209
+ * ~~~~~~~
2210
+ * With the options argument, you can specify boolean flags that affect how
2211
+ * matches are being done.
2212
+ *
2213
+ * Currently two flags exist:
2214
+ *
2215
+ * - matchBareFromJid:
2216
+ * When set to true, the from parameter and the
2217
+ * from attribute on the stanza will be matched as bare JIDs instead
2218
+ * of full JIDs. To use this, pass {matchBareFromJid: true} as the
2219
+ * value of options. The default value for matchBareFromJid is false.
2220
+ *
2221
+ * - ignoreNamespaceFragment:
2222
+ * When set to true, a fragment specified on the stanza's namespace
2223
+ * URL will be ignored when it's matched with the one configured for
2224
+ * the handler.
2225
+ *
2226
+ * This means that if you register like this:
2227
+ * > connection.addHandler(
2228
+ * > handler,
2229
+ * > 'http://jabber.org/protocol/muc',
2230
+ * > null, null, null, null,
2231
+ * > {'ignoreNamespaceFragment': true}
2232
+ * > );
2233
+ *
2234
+ * Then a stanza with XML namespace of
2235
+ * 'http://jabber.org/protocol/muc#user' will also be matched. If
2236
+ * 'ignoreNamespaceFragment' is false, then only stanzas with
2237
+ * 'http://jabber.org/protocol/muc' will be matched.
2238
+ *
2239
+ * Deleting the handler
2240
+ * ~~~~~~~~~~~~~~~~~~~~
2241
+ * The return value should be saved if you wish to remove the handler
2242
+ * with deleteHandler().
2243
+ *
2244
+ * Parameters:
2245
+ * (Function) handler - The user callback.
2246
+ * (String) ns - The namespace to match.
2247
+ * (String) name - The stanza name to match.
2248
+ * (String|Array) type - The stanza type (or types if an array) to match.
2249
+ * (String) id - The stanza id attribute to match.
2250
+ * (String) from - The stanza from attribute to match.
2251
+ * (String) options - The handler options
2252
+ *
2253
+ * Returns:
2254
+ * A reference to the handler that can be used to remove it.
2255
+ */
2256
+ addHandler (handler, ns, name, type, id, from, options) {
2257
+ const hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
2258
+ this.addHandlers.push(hand);
2259
+ return hand;
2260
+ }
2261
+
2262
+ /** Function: deleteHandler
2263
+ * Delete a stanza handler for a connection.
2264
+ *
2265
+ * This function removes a stanza handler from the connection. The
2266
+ * handRef parameter is *not* the function passed to addHandler(),
2267
+ * but is the reference returned from addHandler().
2268
+ *
2269
+ * Parameters:
2270
+ * (Strophe.Handler) handRef - The handler reference.
2271
+ */
2272
+ deleteHandler (handRef) {
2273
+ // this must be done in the Idle loop so that we don't change
2274
+ // the handlers during iteration
2275
+ this.removeHandlers.push(handRef);
2276
+ // If a handler is being deleted while it is being added,
2277
+ // prevent it from getting added
2278
+ const i = this.addHandlers.indexOf(handRef);
2279
+ if (i >= 0) {
2280
+ this.addHandlers.splice(i, 1);
2281
+ }
2282
+ }
2283
+
2284
+ /** Function: registerSASLMechanisms
2285
+ *
2286
+ * Register the SASL mechanisms which will be supported by this instance of
2287
+ * Strophe.Connection (i.e. which this XMPP client will support).
2288
+ *
2289
+ * Parameters:
2290
+ * (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes
2291
+ *
2292
+ */
2293
+ registerSASLMechanisms (mechanisms) {
2294
+ this.mechanisms = {};
2295
+ mechanisms = mechanisms || [
2296
+ Strophe.SASLAnonymous,
2297
+ Strophe.SASLExternal,
2298
+ Strophe.SASLOAuthBearer,
2299
+ Strophe.SASLXOAuth2,
2300
+ Strophe.SASLPlain,
2301
+ Strophe.SASLSHA1
2302
+ ];
2303
+ mechanisms.forEach(m => this.registerSASLMechanism(m));
2304
+ }
2305
+
2306
+ /** Function: registerSASLMechanism
2307
+ *
2308
+ * Register a single SASL mechanism, to be supported by this client.
2309
+ *
2310
+ * Parameters:
2311
+ * (Object) mechanism - Object with a Strophe.SASLMechanism prototype
2312
+ *
2313
+ */
2314
+ registerSASLMechanism (Mechanism) {
2315
+ const mechanism = new Mechanism()
2316
+ this.mechanisms[mechanism.mechname] = mechanism;
2317
+ }
2318
+
2319
+ /** Function: disconnect
2320
+ * Start the graceful disconnection process.
2321
+ *
2322
+ * This function starts the disconnection process. This process starts
2323
+ * by sending unavailable presence and sending BOSH body of type
2324
+ * terminate. A timeout handler makes sure that disconnection happens
2325
+ * even if the BOSH server does not respond.
2326
+ * If the Connection object isn't connected, at least tries to abort all pending requests
2327
+ * so the connection object won't generate successful requests (which were already opened).
2328
+ *
2329
+ * The user supplied connection callback will be notified of the
2330
+ * progress as this process happens.
2331
+ *
2332
+ * Parameters:
2333
+ * (String) reason - The reason the disconnect is occuring.
2334
+ */
2335
+ disconnect (reason) {
2336
+ this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);
2337
+ if (reason) {
2338
+ Strophe.warn("Disconnect was called because: " + reason);
2339
+ if (this.clientLogger) this.clientLogger('Disconnect was called because: ' + reason);
2340
+ } else {
2341
+ Strophe.info("Disconnect was called");
2342
+ if (this.clientLogger) this.clientLogger('Disconnect was called');
2343
+ }
2344
+ if (this.connected) {
2345
+ let pres = false;
2346
+ this.disconnecting = true;
2347
+ if (this.authenticated) {
2348
+ pres = $pres({
2349
+ 'xmlns': Strophe.NS.CLIENT,
2350
+ 'type': 'unavailable'
2351
+ });
2352
+ }
2353
+ // setup timeout handler
2354
+ this._disconnectTimeout = this._addSysTimedHandler(
2355
+ 3000, this._onDisconnectTimeout.bind(this));
2356
+ this._proto._disconnect(pres);
2357
+ } else {
2358
+ Strophe.warn("Disconnect was called before Strophe connected to the server");
2359
+ if (this.clientLogger) this.clientLogger('Disconnect was called before Strophe connected to the server');
2360
+ this._proto._abortAllRequests();
2361
+ this._doDisconnect();
2362
+ }
2363
+ }
2364
+
2365
+ /** PrivateFunction: _changeConnectStatus
2366
+ * _Private_ helper function that makes sure plugins and the user's
2367
+ * callback are notified of connection status changes.
2368
+ *
2369
+ * Parameters:
2370
+ * (Integer) status - the new connection status, one of the values
2371
+ * in Strophe.Status
2372
+ * (String) condition - the error condition or null
2373
+ * (XMLElement) elem - The triggering stanza.
2374
+ */
2375
+ _changeConnectStatus (status, condition, elem) {
2376
+ // notify all plugins listening for status changes
2377
+ for (const k in Strophe._connectionPlugins) {
2378
+ if (Object.prototype.hasOwnProperty.call(Strophe._connectionPlugins, k)) {
2379
+ const plugin = this[k];
2380
+ if (plugin.statusChanged) {
2381
+ try {
2382
+ plugin.statusChanged(status, condition);
2383
+ } catch (err) {
2384
+ Strophe.error(`${k} plugin caused an exception changing status: ${err}`);
2385
+ }
2386
+ }
2387
+ }
2388
+ }
2389
+ // notify the user's callback
2390
+ if (this.connect_callback) {
2391
+ try {
2392
+ this.connect_callback(status, condition, elem);
2393
+ } catch (e) {
2394
+ Strophe._handleError(e);
2395
+ Strophe.error(`User connection callback caused an exception: ${e}`);
2396
+ this.clientLogger(`User connection callback caused an exception: ${e}`);
2397
+ }
2398
+ }
2399
+ this.clientLogger('_changeConnectStatus is completed. Connection status changed to: ' + status);
2400
+ }
2401
+
2402
+ /** PrivateFunction: _doDisconnect
2403
+ * _Private_ function to disconnect.
2404
+ *
2405
+ * This is the last piece of the disconnection logic. This resets the
2406
+ * connection and alerts the user's connection callback.
2407
+ */
2408
+ _doDisconnect (condition) {
2409
+ if (typeof this._idleTimeout === "number") {
2410
+ clearTimeout(this._idleTimeout);
2411
+ }
2412
+
2413
+ // Cancel Disconnect Timeout
2414
+ if (this._disconnectTimeout !== null) {
2415
+ this.deleteTimedHandler(this._disconnectTimeout);
2416
+ this._disconnectTimeout = null;
2417
+ }
2418
+
2419
+ Strophe.debug("_doDisconnect was called");
2420
+ this._proto._doDisconnect();
2421
+
2422
+ this.authenticated = false;
2423
+ this.disconnecting = false;
2424
+ this.restored = false;
2425
+
2426
+ // delete handlers
2427
+ this.handlers = [];
2428
+ this.timedHandlers = [];
2429
+ this.removeTimeds = [];
2430
+ this.removeHandlers = [];
2431
+ this.addTimeds = [];
2432
+ this.addHandlers = [];
2433
+
2434
+ // tell the parent we disconnected
2435
+ this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);
2436
+ this.connected = false;
2437
+ }
2438
+
2439
+ /** PrivateFunction: _dataRecv
2440
+ * _Private_ handler to processes incoming data from the the connection.
2441
+ *
2442
+ * Except for _connect_cb handling the initial connection request,
2443
+ * this function handles the incoming data for all requests. This
2444
+ * function also fires stanza handlers that match each incoming
2445
+ * stanza.
2446
+ *
2447
+ * Parameters:
2448
+ * (Strophe.Request) req - The request that has data ready.
2449
+ * (string) req - The stanza a raw string (optiona).
2450
+ */
2451
+ _dataRecv (req, raw) {
2452
+ const elem = this._proto._reqToData(req);
2453
+ if (elem === null) { return; }
2454
+
2455
+ if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
2456
+ if (elem.nodeName === this._proto.strip && elem.childNodes.length) {
2457
+ this.xmlInput(elem.childNodes[0]);
2458
+ } else {
2459
+ this.xmlInput(elem);
2460
+ }
2461
+ }
2462
+ if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
2463
+ if (raw) {
2464
+ this.rawInput(raw);
2465
+ } else {
2466
+ this.rawInput(Strophe.serialize(elem));
2467
+ }
2468
+ }
2469
+
2470
+ // remove handlers scheduled for deletion
2471
+ while (this.removeHandlers.length > 0) {
2472
+ const hand = this.removeHandlers.pop();
2473
+ const i = this.handlers.indexOf(hand);
2474
+ if (i >= 0) {
2475
+ this.handlers.splice(i, 1);
2476
+ }
2477
+ }
2478
+
2479
+ // add handlers scheduled for addition
2480
+ while (this.addHandlers.length > 0) {
2481
+ this.handlers.push(this.addHandlers.pop());
2482
+ }
2483
+
2484
+ // handle graceful disconnect
2485
+ if (this.disconnecting && this._proto._emptyQueue()) {
2486
+ this._doDisconnect();
2487
+ return;
2488
+ }
2489
+
2490
+ const type = elem.getAttribute("type");
2491
+ if (type !== null && type === "terminate") {
2492
+ // Don't process stanzas that come in after disconnect
2493
+ if (this.disconnecting) {
2494
+ return;
2495
+ }
2496
+ // an error occurred
2497
+ let cond = elem.getAttribute("condition");
2498
+ const conflict = elem.getElementsByTagName("conflict");
2499
+ if (cond !== null) {
2500
+ if (cond === "remote-stream-error" && conflict.length > 0) {
2501
+ cond = "conflict";
2502
+ }
2503
+ this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
2504
+ } else {
2505
+ this._changeConnectStatus(
2506
+ Strophe.Status.CONNFAIL,
2507
+ Strophe.ErrorCondition.UNKOWN_REASON
2508
+ );
2509
+ }
2510
+ this._doDisconnect(cond);
2511
+ return;
2512
+ }
2513
+
2514
+ // send each incoming stanza through the handler chain
2515
+ Strophe.forEachChild(elem, null, child => {
2516
+ // process handlers
2517
+ const newList = this.handlers;
2518
+ this.handlers = [];
2519
+ for (let i=0; i < newList.length; i++) {
2520
+ const hand = newList[i];
2521
+ // encapsulate 'handler.run' not to lose the whole handler list if
2522
+ // one of the handlers throws an exception
2523
+ try {
2524
+ if (hand.isMatch(child) &&
2525
+ (this.authenticated || !hand.user)) {
2526
+ if (hand.run(child)) {
2527
+ this.handlers.push(hand);
2528
+ }
2529
+ } else {
2530
+ this.handlers.push(hand);
2531
+ }
2532
+ } catch(e) {
2533
+ // if the handler throws an exception, we consider it as false
2534
+ Strophe.warn('Removing Strophe handlers due to uncaught exception: '+e.message);
2535
+ }
2536
+ }
2537
+ });
2538
+ }
2539
+
2540
+ /** PrivateFunction: _connect_cb
2541
+ * _Private_ handler for initial connection request.
2542
+ *
2543
+ * This handler is used to process the initial connection request
2544
+ * response from the BOSH server. It is used to set up authentication
2545
+ * handlers and start the authentication process.
2546
+ *
2547
+ * SASL authentication will be attempted if available, otherwise
2548
+ * the code will fall back to legacy authentication.
2549
+ *
2550
+ * Parameters:
2551
+ * (Strophe.Request) req - The current request.
2552
+ * (Function) _callback - low level (xmpp) connect callback function.
2553
+ * Useful for plugins with their own xmpp connect callback (when they
2554
+ * want to do something special).
2555
+ */
2556
+ _connect_cb (req, _callback, raw) {
2557
+ Strophe.debug("_connect_cb was called");
2558
+ this.connected = true;
2559
+
2560
+ let bodyWrap;
2561
+ try {
2562
+ bodyWrap = this._proto._reqToData(req);
2563
+ } catch (e) {
2564
+ if (e.name !== Strophe.ErrorCondition.BAD_FORMAT) { throw e; }
2565
+ this._changeConnectStatus(
2566
+ Strophe.Status.CONNFAIL,
2567
+ Strophe.ErrorCondition.BAD_FORMAT
2568
+ );
2569
+ this._doDisconnect(Strophe.ErrorCondition.BAD_FORMAT);
2570
+ }
2571
+ if (!bodyWrap) { return; }
2572
+
2573
+ if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
2574
+ if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {
2575
+ this.xmlInput(bodyWrap.childNodes[0]);
2576
+ } else {
2577
+ this.xmlInput(bodyWrap);
2578
+ }
2579
+ }
2580
+ if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
2581
+ if (raw) {
2582
+ this.rawInput(raw);
2583
+ } else {
2584
+ this.rawInput(Strophe.serialize(bodyWrap));
2585
+ }
2586
+ }
2587
+
2588
+ const conncheck = this._proto._connect_cb(bodyWrap);
2589
+ if (conncheck === Strophe.Status.CONNFAIL) {
2590
+ return;
2591
+ }
2592
+
2593
+ // Check for the stream:features tag
2594
+ let hasFeatures;
2595
+ if (bodyWrap.getElementsByTagNameNS) {
2596
+ hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "features").length > 0;
2597
+ } else {
2598
+ hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 ||
2599
+ bodyWrap.getElementsByTagName("features").length > 0;
2600
+ }
2601
+ if (!hasFeatures) {
2602
+ this._proto._no_auth_received(_callback);
2603
+ return;
2604
+ }
2605
+
2606
+ const matched = Array.from(bodyWrap.getElementsByTagName("mechanism"))
2607
+ .map(m => this.mechanisms[m.textContent])
2608
+ .filter(m => m);
2609
+
2610
+ if (matched.length === 0) {
2611
+ if (bodyWrap.getElementsByTagName("auth").length === 0) {
2612
+ // There are no matching SASL mechanisms and also no legacy
2613
+ // auth available.
2614
+ this._proto._no_auth_received(_callback);
2615
+ return;
2616
+ }
2617
+ }
2618
+ if (this.do_authentication !== false) {
2619
+ this.authenticate(matched);
2620
+ }
2621
+ }
2622
+
2623
+ /** Function: sortMechanismsByPriority
2624
+ *
2625
+ * Sorts an array of objects with prototype SASLMechanism according to
2626
+ * their priorities.
2627
+ *
2628
+ * Parameters:
2629
+ * (Array) mechanisms - Array of SASL mechanisms.
2630
+ *
2631
+ */
2632
+ sortMechanismsByPriority (mechanisms) { // eslint-disable-line class-methods-use-this
2633
+ // Sorting mechanisms according to priority.
2634
+ for (let i=0; i < mechanisms.length - 1; ++i) {
2635
+ let higher = i;
2636
+ for (let j=i + 1; j < mechanisms.length; ++j) {
2637
+ if (mechanisms[j].priority > mechanisms[higher].priority) {
2638
+ higher = j;
2639
+ }
2640
+ }
2641
+ if (higher !== i) {
2642
+ const swap = mechanisms[i];
2643
+ mechanisms[i] = mechanisms[higher];
2644
+ mechanisms[higher] = swap;
2645
+ }
2646
+ }
2647
+ return mechanisms;
2648
+ }
2649
+
2650
+ /** Function: authenticate
2651
+ * Set up authentication
2652
+ *
2653
+ * Continues the initial connection request by setting up authentication
2654
+ * handlers and starting the authentication process.
2655
+ *
2656
+ * SASL authentication will be attempted if available, otherwise
2657
+ * the code will fall back to legacy authentication.
2658
+ *
2659
+ * Parameters:
2660
+ * (Array) matched - Array of SASL mechanisms supported.
2661
+ *
2662
+ */
2663
+ authenticate (matched) {
2664
+ if (!this._attemptSASLAuth(matched)) {
2665
+ this._attemptLegacyAuth();
2666
+ }
2667
+ }
2668
+
2669
+ /** PrivateFunction: _attemptSASLAuth
2670
+ *
2671
+ * Iterate through an array of SASL mechanisms and attempt authentication
2672
+ * with the highest priority (enabled) mechanism.
2673
+ *
2674
+ * Parameters:
2675
+ * (Array) mechanisms - Array of SASL mechanisms.
2676
+ *
2677
+ * Returns:
2678
+ * (Boolean) mechanism_found - true or false, depending on whether a
2679
+ * valid SASL mechanism was found with which authentication could be
2680
+ * started.
2681
+ */
2682
+ _attemptSASLAuth (mechanisms) {
2683
+ mechanisms = this.sortMechanismsByPriority(mechanisms || []);
2684
+ let mechanism_found = false;
2685
+ for (let i=0; i < mechanisms.length; ++i) {
2686
+ if (!mechanisms[i].test(this)) {
2687
+ continue;
2688
+ }
2689
+ this._sasl_success_handler = this._addSysHandler(
2690
+ this._sasl_success_cb.bind(this), null,
2691
+ "success", null, null);
2692
+ this._sasl_failure_handler = this._addSysHandler(
2693
+ this._sasl_failure_cb.bind(this), null,
2694
+ "failure", null, null);
2695
+ this._sasl_challenge_handler = this._addSysHandler(
2696
+ this._sasl_challenge_cb.bind(this), null,
2697
+ "challenge", null, null);
2698
+
2699
+ this._sasl_mechanism = mechanisms[i];
2700
+ this._sasl_mechanism.onStart(this);
2701
+
2702
+ const request_auth_exchange = $build("auth", {
2703
+ 'xmlns': Strophe.NS.SASL,
2704
+ 'mechanism': this._sasl_mechanism.mechname
2705
+ });
2706
+ if (this._sasl_mechanism.isClientFirst) {
2707
+ const response = this._sasl_mechanism.onChallenge(this, null);
2708
+ request_auth_exchange.t(btoa(response));
2709
+ }
2710
+ this.send(request_auth_exchange.tree());
2711
+ mechanism_found = true;
2712
+ break;
2713
+ }
2714
+ return mechanism_found;
2715
+ }
2716
+
2717
+ /** PrivateFunction: _sasl_challenge_cb
2718
+ * _Private_ handler for the SASL challenge
2719
+ *
2720
+ */
2721
+ _sasl_challenge_cb (elem) {
2722
+ const challenge = atob(Strophe.getText(elem));
2723
+ const response = this._sasl_mechanism.onChallenge(this, challenge);
2724
+ const stanza = $build('response', {'xmlns': Strophe.NS.SASL});
2725
+ if (response !== "") {
2726
+ stanza.t(btoa(response));
2727
+ }
2728
+ this.send(stanza.tree());
2729
+ return true;
2730
+ }
2731
+
2732
+ /** PrivateFunction: _attemptLegacyAuth
2733
+ *
2734
+ * Attempt legacy (i.e. non-SASL) authentication.
2735
+ */
2736
+ _attemptLegacyAuth () {
2737
+ if (Strophe.getNodeFromJid(this.jid) === null) {
2738
+ // we don't have a node, which is required for non-anonymous
2739
+ // client connections
2740
+ this._changeConnectStatus(
2741
+ Strophe.Status.CONNFAIL,
2742
+ Strophe.ErrorCondition.MISSING_JID_NODE
2743
+ );
2744
+ this.disconnect(Strophe.ErrorCondition.MISSING_JID_NODE);
2745
+ } else {
2746
+ // Fall back to legacy authentication
2747
+ this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
2748
+ this._addSysHandler(
2749
+ this._onLegacyAuthIQResult.bind(this),
2750
+ null, null, null, "_auth_1"
2751
+ );
2752
+ this.send($iq({
2753
+ 'type': "get",
2754
+ 'to': this.domain,
2755
+ 'id': "_auth_1"
2756
+ }).c("query", {xmlns: Strophe.NS.AUTH})
2757
+ .c("username", {}).t(Strophe.getNodeFromJid(this.jid))
2758
+ .tree());
2759
+ }
2760
+ }
2761
+
2762
+ /** PrivateFunction: _onLegacyAuthIQResult
2763
+ * _Private_ handler for legacy authentication.
2764
+ *
2765
+ * This handler is called in response to the initial <iq type='get'/>
2766
+ * for legacy authentication. It builds an authentication <iq/> and
2767
+ * sends it, creating a handler (calling back to _auth2_cb()) to
2768
+ * handle the result
2769
+ *
2770
+ * Parameters:
2771
+ * (XMLElement) elem - The stanza that triggered the callback.
2772
+ *
2773
+ * Returns:
2774
+ * false to remove the handler.
2775
+ */
2776
+ _onLegacyAuthIQResult (elem) { // eslint-disable-line no-unused-vars
2777
+ // build plaintext auth iq
2778
+ const iq = $iq({type: "set", id: "_auth_2"})
2779
+ .c('query', {xmlns: Strophe.NS.AUTH})
2780
+ .c('username', {}).t(Strophe.getNodeFromJid(this.jid))
2781
+ .up()
2782
+ .c('password').t(this.pass);
2783
+
2784
+ if (!Strophe.getResourceFromJid(this.jid)) {
2785
+ // since the user has not supplied a resource, we pick
2786
+ // a default one here. unlike other auth methods, the server
2787
+ // cannot do this for us.
2788
+ this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';
2789
+ }
2790
+ iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));
2791
+
2792
+ this._addSysHandler(this._auth2_cb.bind(this), null, null, null, "_auth_2");
2793
+ this.send(iq.tree());
2794
+ return false;
2795
+ }
2796
+
2797
+ /** PrivateFunction: _sasl_success_cb
2798
+ * _Private_ handler for succesful SASL authentication.
2799
+ *
2800
+ * Parameters:
2801
+ * (XMLElement) elem - The matching stanza.
2802
+ *
2803
+ * Returns:
2804
+ * false to remove the handler.
2805
+ */
2806
+ _sasl_success_cb (elem) {
2807
+ if (this._sasl_data["server-signature"]) {
2808
+ let serverSignature;
2809
+ const success = atob(Strophe.getText(elem));
2810
+ const attribMatch = /([a-z]+)=([^,]+)(,|$)/;
2811
+ const matches = success.match(attribMatch);
2812
+ if (matches[1] === "v") {
2813
+ serverSignature = matches[2];
2814
+ }
2815
+ if (serverSignature !== this._sasl_data["server-signature"]) {
2816
+ // remove old handlers
2817
+ this.deleteHandler(this._sasl_failure_handler);
2818
+ this._sasl_failure_handler = null;
2819
+ if (this._sasl_challenge_handler) {
2820
+ this.deleteHandler(this._sasl_challenge_handler);
2821
+ this._sasl_challenge_handler = null;
2822
+ }
2823
+ this._sasl_data = {};
2824
+ return this._sasl_failure_cb(null);
2825
+ }
2826
+ }
2827
+ Strophe.info("SASL authentication succeeded.");
2828
+
2829
+ if (this._sasl_mechanism) {
2830
+ this._sasl_mechanism.onSuccess();
2831
+ }
2832
+ // remove old handlers
2833
+ this.deleteHandler(this._sasl_failure_handler);
2834
+ this._sasl_failure_handler = null;
2835
+ if (this._sasl_challenge_handler) {
2836
+ this.deleteHandler(this._sasl_challenge_handler);
2837
+ this._sasl_challenge_handler = null;
2838
+ }
2839
+ const streamfeature_handlers = [];
2840
+ const wrapper = (handlers, elem) => {
2841
+ while (handlers.length) {
2842
+ this.deleteHandler(handlers.pop());
2843
+ }
2844
+ this._onStreamFeaturesAfterSASL(elem);
2845
+ return false;
2846
+ };
2847
+ streamfeature_handlers.push(
2848
+ this._addSysHandler(elem => wrapper(streamfeature_handlers, elem),
2849
+ null, "stream:features", null, null)
2850
+ );
2851
+
2852
+ streamfeature_handlers.push(
2853
+ this._addSysHandler(elem => wrapper(streamfeature_handlers, elem),
2854
+ Strophe.NS.STREAM, "features", null, null)
2855
+ );
2856
+
2857
+ // we must send an xmpp:restart now
2858
+ this._sendRestart();
2859
+ return false;
2860
+ }
2861
+
2862
+ /** PrivateFunction: _onStreamFeaturesAfterSASL
2863
+ * Parameters:
2864
+ * (XMLElement) elem - The matching stanza.
2865
+ *
2866
+ * Returns:
2867
+ * false to remove the handler.
2868
+ */
2869
+ _onStreamFeaturesAfterSASL (elem) {
2870
+ // save stream:features for future usage
2871
+ this.features = elem;
2872
+ for (let i=0; i < elem.childNodes.length; i++) {
2873
+ const child = elem.childNodes[i];
2874
+ if (child.nodeName === 'bind') {
2875
+ this.do_bind = true;
2876
+ }
2877
+ if (child.nodeName === 'session') {
2878
+ this.do_session = true;
2879
+ }
2880
+ }
2881
+
2882
+ if (!this.do_bind) {
2883
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
2884
+ return false;
2885
+ } else if (!this.options.explicitResourceBinding) {
2886
+ this.bind();
2887
+ } else {
2888
+ this._changeConnectStatus(Strophe.Status.BINDREQUIRED, null);
2889
+ }
2890
+ return false;
2891
+ }
2892
+
2893
+ /** Function: bind
2894
+ *
2895
+ * Sends an IQ to the XMPP server to bind a JID resource for this session.
2896
+ *
2897
+ * https://tools.ietf.org/html/rfc6120#section-7.5
2898
+ *
2899
+ * If `explicitResourceBinding` was set to a truthy value in the options
2900
+ * passed to the Strophe.Connection constructor, then this function needs
2901
+ * to be called explicitly by the client author.
2902
+ *
2903
+ * Otherwise it'll be called automatically as soon as the XMPP server
2904
+ * advertises the "urn:ietf:params:xml:ns:xmpp-bind" stream feature.
2905
+ */
2906
+ bind () {
2907
+ if (!this.do_bind) {
2908
+ Strophe.log(
2909
+ Strophe.LogLevel.INFO,
2910
+ `Strophe.Connection.prototype.bind called but "do_bind" is false`
2911
+ );
2912
+ return;
2913
+ }
2914
+ this._addSysHandler(
2915
+ this._onResourceBindResultIQ.bind(this),
2916
+ null, null, null, "_bind_auth_2");
2917
+
2918
+ const resource = Strophe.getResourceFromJid(this.jid);
2919
+ if (resource) {
2920
+ this.send($iq({type: "set", id: "_bind_auth_2"})
2921
+ .c('bind', {xmlns: Strophe.NS.BIND})
2922
+ .c('resource', {}).t(resource).tree());
2923
+ } else {
2924
+ this.send($iq({type: "set", id: "_bind_auth_2"})
2925
+ .c('bind', {xmlns: Strophe.NS.BIND})
2926
+ .tree());
2927
+ }
2928
+ }
2929
+
2930
+ /** PrivateFunction: _onResourceBindIQ
2931
+ * _Private_ handler for binding result and session start.
2932
+ *
2933
+ * Parameters:
2934
+ * (XMLElement) elem - The matching stanza.
2935
+ *
2936
+ * Returns:
2937
+ * false to remove the handler.
2938
+ */
2939
+ _onResourceBindResultIQ (elem) {
2940
+ if (elem.getAttribute("type") === "error") {
2941
+ Strophe.warn("Resource binding failed.");
2942
+ const conflict = elem.getElementsByTagName("conflict");
2943
+ let condition;
2944
+ if (conflict.length > 0) {
2945
+ condition = Strophe.ErrorCondition.CONFLICT;
2946
+ }
2947
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition, elem);
2948
+ return false;
2949
+ }
2950
+ // TODO - need to grab errors
2951
+ const bind = elem.getElementsByTagName("bind");
2952
+ if (bind.length > 0) {
2953
+ const jidNode = bind[0].getElementsByTagName("jid");
2954
+ if (jidNode.length > 0) {
2955
+ this.authenticated = true;
2956
+ this.jid = Strophe.getText(jidNode[0]);
2957
+ if (this.do_session) {
2958
+ this._establishSession();
2959
+ } else {
2960
+ this._changeConnectStatus(Strophe.Status.CONNECTED, null);
2961
+ }
2962
+ }
2963
+ } else {
2964
+ Strophe.warn("Resource binding failed.");
2965
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
2966
+ return false;
2967
+ }
2968
+ }
2969
+
2970
+ /** PrivateFunction: _establishSession
2971
+ * Send IQ request to establish a session with the XMPP server.
2972
+ *
2973
+ * See https://xmpp.org/rfcs/rfc3921.html#session
2974
+ *
2975
+ * Note: The protocol for session establishment has been determined as
2976
+ * unnecessary and removed in RFC-6121.
2977
+ */
2978
+ _establishSession () {
2979
+ if (!this.do_session) {
2980
+ throw new Error(`Strophe.Connection.prototype._establishSession `+
2981
+ `called but apparently ${Strophe.NS.SESSION} wasn't advertised by the server`);
2982
+ }
2983
+ this._addSysHandler(
2984
+ this._onSessionResultIQ.bind(this),
2985
+ null, null, null, "_session_auth_2");
2986
+
2987
+ this.send(
2988
+ $iq({type: "set", id: "_session_auth_2"})
2989
+ .c('session', {xmlns: Strophe.NS.SESSION})
2990
+ .tree());
2991
+ }
2992
+
2993
+ /** PrivateFunction: _onSessionResultIQ
2994
+ * _Private_ handler for the server's IQ response to a client's session
2995
+ * request.
2996
+ *
2997
+ * This sets Connection.authenticated to true on success, which
2998
+ * starts the processing of user handlers.
2999
+ *
3000
+ * See https://xmpp.org/rfcs/rfc3921.html#session
3001
+ *
3002
+ * Note: The protocol for session establishment has been determined as
3003
+ * unnecessary and removed in RFC-6121.
3004
+ *
3005
+ * Parameters:
3006
+ * (XMLElement) elem - The matching stanza.
3007
+ *
3008
+ * Returns:
3009
+ * false to remove the handler.
3010
+ */
3011
+ _onSessionResultIQ (elem) {
3012
+ if (elem.getAttribute("type") === "result") {
3013
+ this.authenticated = true;
3014
+ this._changeConnectStatus(Strophe.Status.CONNECTED, null);
3015
+ } else if (elem.getAttribute("type") === "error") {
3016
+ this.authenticated = false;
3017
+ Strophe.warn("Session creation failed.");
3018
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
3019
+ return false;
3020
+ }
3021
+ return false;
3022
+ }
3023
+
3024
+ /** PrivateFunction: _sasl_failure_cb
3025
+ * _Private_ handler for SASL authentication failure.
3026
+ *
3027
+ * Parameters:
3028
+ * (XMLElement) elem - The matching stanza.
3029
+ *
3030
+ * Returns:
3031
+ * false to remove the handler.
3032
+ */
3033
+ _sasl_failure_cb (elem) {
3034
+ // delete unneeded handlers
3035
+ if (this._sasl_success_handler) {
3036
+ this.deleteHandler(this._sasl_success_handler);
3037
+ this._sasl_success_handler = null;
3038
+ }
3039
+ if (this._sasl_challenge_handler) {
3040
+ this.deleteHandler(this._sasl_challenge_handler);
3041
+ this._sasl_challenge_handler = null;
3042
+ }
3043
+
3044
+ if(this._sasl_mechanism)
3045
+ this._sasl_mechanism.onFailure();
3046
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
3047
+ return false;
3048
+ }
3049
+
3050
+ /** PrivateFunction: _auth2_cb
3051
+ * _Private_ handler to finish legacy authentication.
3052
+ *
3053
+ * This handler is called when the result from the jabber:iq:auth
3054
+ * <iq/> stanza is returned.
3055
+ *
3056
+ * Parameters:
3057
+ * (XMLElement) elem - The stanza that triggered the callback.
3058
+ *
3059
+ * Returns:
3060
+ * false to remove the handler.
3061
+ */
3062
+ _auth2_cb (elem) {
3063
+ if (elem.getAttribute("type") === "result") {
3064
+ this.authenticated = true;
3065
+ this._changeConnectStatus(Strophe.Status.CONNECTED, null);
3066
+ } else if (elem.getAttribute("type") === "error") {
3067
+ this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
3068
+ this.disconnect('authentication failed');
3069
+ }
3070
+ return false;
3071
+ }
3072
+
3073
+ /** PrivateFunction: _addSysTimedHandler
3074
+ * _Private_ function to add a system level timed handler.
3075
+ *
3076
+ * This function is used to add a Strophe.TimedHandler for the
3077
+ * library code. System timed handlers are allowed to run before
3078
+ * authentication is complete.
3079
+ *
3080
+ * Parameters:
3081
+ * (Integer) period - The period of the handler.
3082
+ * (Function) handler - The callback function.
3083
+ */
3084
+ _addSysTimedHandler (period, handler) {
3085
+ const thand = new Strophe.TimedHandler(period, handler);
3086
+ thand.user = false;
3087
+ this.addTimeds.push(thand);
3088
+ return thand;
3089
+ }
3090
+
3091
+ /** PrivateFunction: _addSysHandler
3092
+ * _Private_ function to add a system level stanza handler.
3093
+ *
3094
+ * This function is used to add a Strophe.Handler for the
3095
+ * library code. System stanza handlers are allowed to run before
3096
+ * authentication is complete.
3097
+ *
3098
+ * Parameters:
3099
+ * (Function) handler - The callback function.
3100
+ * (String) ns - The namespace to match.
3101
+ * (String) name - The stanza name to match.
3102
+ * (String) type - The stanza type attribute to match.
3103
+ * (String) id - The stanza id attribute to match.
3104
+ */
3105
+ _addSysHandler (handler, ns, name, type, id) {
3106
+ const hand = new Strophe.Handler(handler, ns, name, type, id);
3107
+ hand.user = false;
3108
+ this.addHandlers.push(hand);
3109
+ return hand;
3110
+ }
3111
+
3112
+ /** PrivateFunction: _onDisconnectTimeout
3113
+ * _Private_ timeout handler for handling non-graceful disconnection.
3114
+ *
3115
+ * If the graceful disconnect process does not complete within the
3116
+ * time allotted, this handler finishes the disconnect anyway.
3117
+ *
3118
+ * Returns:
3119
+ * false to remove the handler.
3120
+ */
3121
+ _onDisconnectTimeout () {
3122
+ Strophe.debug("_onDisconnectTimeout was called");
3123
+ this._changeConnectStatus(Strophe.Status.CONNTIMEOUT, null);
3124
+ this._proto._onDisconnectTimeout();
3125
+ // actually disconnect
3126
+ this._doDisconnect();
3127
+ return false;
3128
+ }
3129
+
3130
+ /** PrivateFunction: _onIdle
3131
+ * _Private_ handler to process events during idle cycle.
3132
+ *
3133
+ * This handler is called every 100ms to fire timed handlers that
3134
+ * are ready and keep poll requests going.
3135
+ */
3136
+ _onIdle () {
3137
+ // add timed handlers scheduled for addition
3138
+ // NOTE: we add before remove in the case a timed handler is
3139
+ // added and then deleted before the next _onIdle() call.
3140
+ while (this.addTimeds.length > 0) {
3141
+ this.timedHandlers.push(this.addTimeds.pop());
3142
+ }
3143
+
3144
+ // remove timed handlers that have been scheduled for deletion
3145
+ while (this.removeTimeds.length > 0) {
3146
+ const thand = this.removeTimeds.pop();
3147
+ const i = this.timedHandlers.indexOf(thand);
3148
+ if (i >= 0) {
3149
+ this.timedHandlers.splice(i, 1);
3150
+ }
3151
+ }
3152
+
3153
+ // call ready timed handlers
3154
+ const now = new Date().getTime();
3155
+ const newList = [];
3156
+ for (let i=0; i < this.timedHandlers.length; i++) {
3157
+ const thand = this.timedHandlers[i];
3158
+ if (this.authenticated || !thand.user) {
3159
+ const since = thand.lastCalled + thand.period;
3160
+ if (since - now <= 0) {
3161
+ if (thand.run()) {
3162
+ newList.push(thand);
3163
+ }
3164
+ } else {
3165
+ newList.push(thand);
3166
+ }
3167
+ }
3168
+ }
3169
+ this.timedHandlers = newList;
3170
+ clearTimeout(this._idleTimeout);
3171
+ this._proto._onIdle();
3172
+
3173
+ // reactivate the timer only if connected
3174
+ if (this.connected) {
3175
+ this._idleTimeout = setTimeout(() => this._onIdle(), 100);
3176
+ }
3177
+ }
3178
+ };
3179
+
3180
+ /** Class: Strophe.SASLMechanism
3181
+ *
3182
+ * Encapsulates an SASL authentication mechanism.
3183
+ *
3184
+ * User code may override the priority for each mechanism or disable it completely.
3185
+ * See <priority> for information about changing priority and <test> for informatian on
3186
+ * how to disable a mechanism.
3187
+ *
3188
+ * By default, all mechanisms are enabled and the priorities are
3189
+ *
3190
+ * SCRAM-SHA-1 - 60
3191
+ * PLAIN - 50
3192
+ * OAUTHBEARER - 40
3193
+ * X-OAUTH2 - 30
3194
+ * ANONYMOUS - 20
3195
+ * EXTERNAL - 10
3196
+ *
3197
+ * See: Strophe.Connection.addSupportedSASLMechanisms
3198
+ */
3199
+ Strophe.SASLMechanism = class SASLMechanism {
3200
+
3201
+ /**
3202
+ * PrivateConstructor: Strophe.SASLMechanism
3203
+ * SASL auth mechanism abstraction.
3204
+ *
3205
+ * Parameters:
3206
+ * (String) name - SASL Mechanism name.
3207
+ * (Boolean) isClientFirst - If client should send response first without challenge.
3208
+ * (Number) priority - Priority.
3209
+ *
3210
+ * Returns:
3211
+ * A new Strophe.SASLMechanism object.
3212
+ */
3213
+ constructor (name, isClientFirst, priority) {
3214
+ /** PrivateVariable: mechname
3215
+ * Mechanism name.
3216
+ */
3217
+ this.mechname = name;
3218
+
3219
+ /** PrivateVariable: isClientFirst
3220
+ * If client sends response without initial server challenge.
3221
+ */
3222
+ this.isClientFirst = isClientFirst;
3223
+
3224
+ /** Variable: priority
3225
+ * Determines which <SASLMechanism> is chosen for authentication (Higher is better).
3226
+ * Users may override this to prioritize mechanisms differently.
3227
+ *
3228
+ * Example: (This will cause Strophe to choose the mechanism that the server sent first)
3229
+ *
3230
+ * > Strophe.SASLPlain.priority = Strophe.SASLSHA1.priority;
3231
+ *
3232
+ * See <SASL mechanisms> for a list of available mechanisms.
3233
+ *
3234
+ */
3235
+ this.priority = priority;
3236
+ }
3237
+
3238
+ /**
3239
+ * Function: test
3240
+ * Checks if mechanism able to run.
3241
+ * To disable a mechanism, make this return false;
3242
+ *
3243
+ * To disable plain authentication run
3244
+ * > Strophe.SASLPlain.test = function() {
3245
+ * > return false;
3246
+ * > }
3247
+ *
3248
+ * See <SASL mechanisms> for a list of available mechanisms.
3249
+ *
3250
+ * Parameters:
3251
+ * (Strophe.Connection) connection - Target Connection.
3252
+ *
3253
+ * Returns:
3254
+ * (Boolean) If mechanism was able to run.
3255
+ */
3256
+ test () { // eslint-disable-line class-methods-use-this
3257
+ return true;
3258
+ }
3259
+
3260
+ /** PrivateFunction: onStart
3261
+ * Called before starting mechanism on some connection.
3262
+ *
3263
+ * Parameters:
3264
+ * (Strophe.Connection) connection - Target Connection.
3265
+ */
3266
+ onStart (connection) {
3267
+ this._connection = connection;
3268
+ }
3269
+
3270
+ /** PrivateFunction: onChallenge
3271
+ * Called by protocol implementation on incoming challenge. If client is
3272
+ * first (isClientFirst === true) challenge will be null on the first call.
3273
+ *
3274
+ * Parameters:
3275
+ * (Strophe.Connection) connection - Target Connection.
3276
+ * (String) challenge - current challenge to handle.
3277
+ *
3278
+ * Returns:
3279
+ * (String) Mechanism response.
3280
+ */
3281
+ onChallenge (connection, challenge) { // eslint-disable-line
3282
+ throw new Error("You should implement challenge handling!");
3283
+ }
3284
+
3285
+ /** PrivateFunction: onFailure
3286
+ * Protocol informs mechanism implementation about SASL failure.
3287
+ */
3288
+ onFailure () {
3289
+ this._connection = null;
3290
+ }
3291
+
3292
+ /** PrivateFunction: onSuccess
3293
+ * Protocol informs mechanism implementation about SASL success.
3294
+ */
3295
+ onSuccess () {
3296
+ this._connection = null;
3297
+ }
3298
+ };
3299
+
3300
+ /** Constants: SASL mechanisms
3301
+ * Available authentication mechanisms
3302
+ *
3303
+ * Strophe.SASLAnonymous - SASL ANONYMOUS authentication.
3304
+ * Strophe.SASLPlain - SASL PLAIN authentication.
3305
+ * Strophe.SASLSHA1 - SASL SCRAM-SHA-1 authentication
3306
+ * Strophe.SASLOAuthBearer - SASL OAuth Bearer authentication
3307
+ * Strophe.SASLExternal - SASL EXTERNAL authentication
3308
+ * Strophe.SASLXOAuth2 - SASL X-OAuth2 authentication
3309
+ */
3310
+
3311
+ // Building SASL callbacks
3312
+
3313
+ Strophe.SASLAnonymous = class SASLAnonymous extends Strophe.SASLMechanism {
3314
+
3315
+ /** PrivateConstructor: SASLAnonymous
3316
+ * SASL ANONYMOUS authentication.
3317
+ */
3318
+ constructor (mechname='ANONYMOUS', isClientFirst=false, priority=20) {
3319
+ super(mechname, isClientFirst, priority);
3320
+ }
3321
+
3322
+ test (connection) { // eslint-disable-line class-methods-use-this
3323
+ return connection.authcid === null;
3324
+ }
3325
+ }
3326
+
3327
+
3328
+ Strophe.SASLPlain = class SASLPlain extends Strophe.SASLMechanism {
3329
+
3330
+ /** PrivateConstructor: SASLPlain
3331
+ * SASL PLAIN authentication.
3332
+ */
3333
+ constructor (mechname='PLAIN', isClientFirst=true, priority=50) {
3334
+ super(mechname, isClientFirst, priority);
3335
+ }
3336
+
3337
+ test (connection) { // eslint-disable-line class-methods-use-this
3338
+ return connection.authcid !== null;
3339
+ }
3340
+
3341
+ onChallenge (connection) { // eslint-disable-line class-methods-use-this
3342
+ const { authcid, authzid, domain, pass } = connection;
3343
+ if (!domain) {
3344
+ throw new Error("SASLPlain onChallenge: domain is not defined!");
3345
+ }
3346
+ // Only include authzid if it differs from authcid.
3347
+ // See: https://tools.ietf.org/html/rfc6120#section-6.3.8
3348
+ let auth_str = (authzid !== `${authcid}@${domain}`) ? authzid : '';
3349
+ auth_str = auth_str + "\u0000";
3350
+ auth_str = auth_str + authcid;
3351
+ auth_str = auth_str + "\u0000";
3352
+ auth_str = auth_str + pass;
3353
+ return utils.utf16to8(auth_str);
3354
+ }
3355
+ }
3356
+
3357
+
3358
+ Strophe.SASLSHA1 = class SASLSHA1 extends Strophe.SASLMechanism {
3359
+
3360
+ /** PrivateConstructor: SASLSHA1
3361
+ * SASL SCRAM SHA 1 authentication.
3362
+ */
3363
+ constructor (mechname='SCRAM-SHA-1', isClientFirst=true, priority=60) {
3364
+ super(mechname, isClientFirst, priority);
3365
+ }
3366
+
3367
+ test (connection) { // eslint-disable-line class-methods-use-this
3368
+ return connection.authcid !== null;
3369
+ }
3370
+
3371
+ onChallenge (connection, challenge, test_cnonce) {
3372
+ const cnonce = test_cnonce || MD5.hexdigest("" + (Math.random() * 1234567890));
3373
+ let auth_str = "n=" + utils.utf16to8(connection.authcid);
3374
+ auth_str += ",r=";
3375
+ auth_str += cnonce;
3376
+ connection._sasl_data.cnonce = cnonce;
3377
+ connection._sasl_data["client-first-message-bare"] = auth_str;
3378
+ auth_str = "n,," + auth_str;
3379
+
3380
+ this.onChallenge = (connection, challenge) => {
3381
+ let nonce, salt, iter, Hi, U, U_old, i, k;
3382
+ let responseText = "c=biws,";
3383
+ let authMessage = `${connection._sasl_data["client-first-message-bare"]},${challenge},`;
3384
+ const cnonce = connection._sasl_data.cnonce;
3385
+ const attribMatch = /([a-z]+)=([^,]+)(,|$)/;
3386
+
3387
+ while (challenge.match(attribMatch)) {
3388
+ const matches = challenge.match(attribMatch);
3389
+ challenge = challenge.replace(matches[0], "");
3390
+ switch (matches[1]) {
3391
+ case "r":
3392
+ nonce = matches[2];
3393
+ break;
3394
+ case "s":
3395
+ salt = matches[2];
3396
+ break;
3397
+ case "i":
3398
+ iter = matches[2];
3399
+ break;
3400
+ }
3401
+ }
3402
+
3403
+ if (nonce.substr(0, cnonce.length) !== cnonce) {
3404
+ connection._sasl_data = {};
3405
+ return connection._sasl_failure_cb();
3406
+ }
3407
+
3408
+ responseText += "r=" + nonce;
3409
+ authMessage += responseText;
3410
+
3411
+ salt = atob(salt);
3412
+ salt += "\x00\x00\x00\x01";
3413
+
3414
+ const pass = utils.utf16to8(connection.pass);
3415
+ Hi = U_old = SHA1.core_hmac_sha1(pass, salt);
3416
+ for (i=1; i<iter; i++) {
3417
+ U = SHA1.core_hmac_sha1(pass, SHA1.binb2str(U_old));
3418
+ for (k = 0; k < 5; k++) {
3419
+ Hi[k] ^= U[k];
3420
+ }
3421
+ U_old = U;
3422
+ }
3423
+ Hi = SHA1.binb2str(Hi);
3424
+
3425
+ const clientKey = SHA1.core_hmac_sha1(Hi, "Client Key");
3426
+ const serverKey = SHA1.str_hmac_sha1(Hi, "Server Key");
3427
+ const clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage);
3428
+ connection._sasl_data["server-signature"] = SHA1.b64_hmac_sha1(serverKey, authMessage);
3429
+
3430
+ for (k = 0; k < 5; k++) {
3431
+ clientKey[k] ^= clientSignature[k];
3432
+ }
3433
+ responseText += ",p=" + btoa(SHA1.binb2str(clientKey));
3434
+ return responseText;
3435
+ }
3436
+ return auth_str;
3437
+ }
3438
+ }
3439
+
3440
+
3441
+ Strophe.SASLOAuthBearer = class SASLOAuthBearer extends Strophe.SASLMechanism {
3442
+
3443
+ /** PrivateConstructor: SASLOAuthBearer
3444
+ * SASL OAuth Bearer authentication.
3445
+ */
3446
+ constructor (mechname='OAUTHBEARER', isClientFirst=true, priority=40) {
3447
+ super(mechname, isClientFirst, priority);
3448
+ }
3449
+
3450
+ test (connection) { // eslint-disable-line class-methods-use-this
3451
+ return connection.pass !== null;
3452
+ }
3453
+
3454
+ onChallenge (connection) { // eslint-disable-line class-methods-use-this
3455
+ let auth_str = 'n,';
3456
+ if (connection.authcid !== null) {
3457
+ auth_str = auth_str + 'a=' + connection.authzid;
3458
+ }
3459
+ auth_str = auth_str + ',';
3460
+ auth_str = auth_str + "\u0001";
3461
+ auth_str = auth_str + 'auth=Bearer ';
3462
+ auth_str = auth_str + connection.pass;
3463
+ auth_str = auth_str + "\u0001";
3464
+ auth_str = auth_str + "\u0001";
3465
+ return utils.utf16to8(auth_str);
3466
+ }
3467
+ }
3468
+
3469
+
3470
+ Strophe.SASLExternal = class SASLExternal extends Strophe.SASLMechanism {
3471
+
3472
+ /** PrivateConstructor: SASLExternal
3473
+ * SASL EXTERNAL authentication.
3474
+ *
3475
+ * The EXTERNAL mechanism allows a client to request the server to use
3476
+ * credentials established by means external to the mechanism to
3477
+ * authenticate the client. The external means may be, for instance,
3478
+ * TLS services.
3479
+ */
3480
+ constructor (mechname='EXTERNAL', isClientFirst=true, priority=10) {
3481
+ super(mechname, isClientFirst, priority);
3482
+ }
3483
+
3484
+ onChallenge (connection) { // eslint-disable-line class-methods-use-this
3485
+ /** According to XEP-178, an authzid SHOULD NOT be presented when the
3486
+ * authcid contained or implied in the client certificate is the JID (i.e.
3487
+ * authzid) with which the user wants to log in as.
3488
+ *
3489
+ * To NOT send the authzid, the user should therefore set the authcid equal
3490
+ * to the JID when instantiating a new Strophe.Connection object.
3491
+ */
3492
+ return connection.authcid === connection.authzid ? '' : connection.authzid;
3493
+ }
3494
+ };
3495
+
3496
+
3497
+ Strophe.SASLXOAuth2 = class SASLXOAuth2 extends Strophe.SASLMechanism {
3498
+
3499
+ /** PrivateConstructor: SASLXOAuth2
3500
+ * SASL X-OAuth2 authentication.
3501
+ */
3502
+ constructor (mechname='X-OAUTH2', isClientFirst=true, priority=30) {
3503
+ super(mechname, isClientFirst, priority);
3504
+ }
3505
+
3506
+ test (connection) { // eslint-disable-line class-methods-use-this
3507
+ return connection.pass !== null;
3508
+ }
3509
+
3510
+ onChallenge (connection) { // eslint-disable-line class-methods-use-this
3511
+ let auth_str = '\u0000';
3512
+ if (connection.authcid !== null) {
3513
+ auth_str = auth_str + connection.authzid;
3514
+ }
3515
+ auth_str = auth_str + "\u0000";
3516
+ auth_str = auth_str + connection.pass;
3517
+ return utils.utf16to8(auth_str);
3518
+ }
3519
+ };
3520
+
3521
+ export { SHA1, MD5 };
3522
+
3523
+ export default {
3524
+ 'Strophe': Strophe,
3525
+ '$build': $build,
3526
+ '$iq': $iq,
3527
+ '$msg': $msg,
3528
+ '$pres': $pres,
3529
+ 'SHA1': SHA1,
3530
+ 'MD5': MD5,
3531
+ 'b64_hmac_sha1': SHA1.b64_hmac_sha1,
3532
+ 'b64_sha1': SHA1.b64_sha1,
3533
+ 'str_hmac_sha1': SHA1.str_hmac_sha1,
3534
+ 'str_sha1': SHA1.str_sha1
3535
+ };