你的位置:首页 > Java教程

[Java教程]Openfire Strophe开发中文乱码问题


网站上有很多Openfire Web方案,之前想用Smack 但是jar包支持客户端版本的,还有JDK版本问题  一直没调试成功  估计成功的方法只能拜读源码进行修改了。

SparkWeb 官网代码很久没维护  CSDN上下来个版本但jar包路径不对  花了不少时间总算能跑起来,不过版本是flex3版本,太老了   自己花精力升级有点费时间呀

最后采用存脚本开发Strophejs,下面网站写的很详细

学习的网站:http://www.dotblogs.com.tw/sungnoone/archive/2014/06/20/145642.aspx

 

Strophejs中文发送到服务器端老会出现乱码问题,这个问题网上也没好的解决方案,在这里我分享下我的方法。

1、修改Strophe.js

添加chencode中文编码,cht IQ请求节点中文内容,添加utf16to8方法

 

  1 /** File: strophe.js  2  * A JavaScript library for XMPP BOSH/XMPP over Websocket.  3  *  4  * This is the JavaScript version of the Strophe library. Since JavaScript  5  * had no facilities for persistent TCP connections, this library uses  6  * Bidirectional-streams Over Synchronous HTTP (BOSH) to emulate  7  * a persistent, stateful, two-way connection to an XMPP server. More  8  * information on BOSH can be found in XEP 124.  9  * 10  * This version of Strophe also works with WebSockets. 11  * For more information on XMPP-over WebSocket see this RFC: 12  * http://tools.ietf.org/html/rfc7395 13 */ 14  15 /* All of the Strophe globals are defined in this special function below so 16  * that references to the globals become closures. This will ensure that 17  * on page reload, these references will still be available to callbacks 18  * that are still executing. 19 */ 20  21 /* jshint ignore:start */ 22 (function (callback) { 23 /* jshint ignore:end */ 24  25 // This code was written by Tyler Akins and has been placed in the 26 // public domain. It would be nice if you left this header intact. 27 // Base64 code from Tyler Akins -- http://rumkin.com 28  29 (function (root, factory) { 30   if (typeof define === 'function' && define.amd) { 31     define('strophe-base64', function () { 32       return factory(); 33     }); 34   } else { 35     // Browser globals 36     root.Base64 = factory(); 37   } 38 }(this, function () { 39   var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 40  41   var obj = { 42     /** 43      * Encodes a string in base64 44      * @param {String} input The string to encode in base64. 45     */ 46     encode: function (input) { 47       var output = ""; 48       var chr1, chr2, chr3; 49       var enc1, enc2, enc3, enc4; 50       var i = 0; 51  52       do { 53         chr1 = input.charCodeAt(i++); 54         chr2 = input.charCodeAt(i++); 55         chr3 = input.charCodeAt(i++); 56  57         enc1 = chr1 >> 2; 58         enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 59         enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 60         enc4 = chr3 & 63; 61  62         if (isNaN(chr2)) { 63           enc2 = ((chr1 & 3) << 4); 64           enc3 = enc4 = 64; 65         } else if (isNaN(chr3)) { 66           enc4 = 64; 67         } 68  69         output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + 70           keyStr.charAt(enc3) + keyStr.charAt(enc4); 71       } while (i < input.length); 72  73       return output; 74     }, 75     chencode:function(input) { 76       input = utf16to8(input); 77       var output = ""; 78       var chr1, chr2, chr3; 79       var enc1, enc2, enc3, enc4; 80       var i = 0; 81       do { 82         chr1 = input.charCodeAt(i++); 83         chr2 = input.charCodeAt(i++); 84         chr3 = input.charCodeAt(i++); 85  86         enc1 = chr1 >> 2; 87         enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 88         enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 89         enc4 = chr3 & 63; 90  91         if (isNaN(chr2)) { 92           enc2 = ((chr1 & 3) << 4); 93           enc3 = enc4 = 64; 94         } else if (isNaN(chr3)) { 95           enc4 = 64; 96         } 97  98         output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + 99           keyStr.charAt(enc3) + keyStr.charAt(enc4); 100       } while (i < input.length); 101       return output; 102     }, 103     /** 104      * Decodes a base64 string. 105      * @param {String} input The string to decode. 106     */ 107     decode: function (input) { 108       var output = ""; 109       var chr1, chr2, chr3; 110       var enc1, enc2, enc3, enc4; 111       var i = 0; 112  113       // remove all characters that are not A-Z, a-z, 0-9, +, /, or = 114       input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 115  116       do { 117         enc1 = keyStr.indexOf(input.charAt(i++)); 118         enc2 = keyStr.indexOf(input.charAt(i++)); 119         enc3 = keyStr.indexOf(input.charAt(i++)); 120         enc4 = keyStr.indexOf(input.charAt(i++)); 121  122         chr1 = (enc1 << 2) | (enc2 >> 4); 123         chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 124         chr3 = ((enc3 & 3) << 6) | enc4; 125  126         output = output + String.fromCharCode(chr1); 127  128         if (enc3 != 64) { 129           output = output + String.fromCharCode(chr2); 130         } 131         if (enc4 != 64) { 132           output = output + String.fromCharCode(chr3); 133         } 134       } while (i < input.length); 135  136       return output; 137     } 138   }; 139   return obj; 140 })); 141  142 /* 143  * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined 144  * in FIPS PUB 180-1 145  * Version 2.1a Copyright Paul Johnston 2000 - 2002. 146  * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 147  * Distributed under the BSD License 148  * See http://pajhome.org.uk/crypt/md5 for details. 149 */ 150  151 /* jshint undef: true, unused: true:, noarg: true, latedef: true */ 152 /* global define */ 153  154 /* Some functions and variables have been stripped for use with Strophe */ 155  156 (function (root, factory) { 157   if (typeof define === 'function' && define.amd) { 158     define('strophe-sha1', function () { 159       return factory(); 160     }); 161   } else { 162     // Browser globals 163     root.SHA1 = factory(); 164   } 165 }(this, function () { 166  167 /* 168  * Calculate the SHA-1 of an array of big-endian words, and a bit length 169 */ 170 function core_sha1(x, len) 171 { 172  /* append padding */ 173  x[len >> 5] |= 0x80 << (24 - len % 32); 174  x[((len + 64 >> 9) << 4) + 15] = len; 175  176  var w = new Array(80); 177  var a = 1732584193; 178  var b = -271733879; 179  var c = -1732584194; 180  var d = 271733878; 181  var e = -1009589776; 182  183  var i, j, t, olda, oldb, oldc, oldd, olde; 184  for (i = 0; i < x.length; i += 16) 185  { 186   olda = a; 187   oldb = b; 188   oldc = c; 189   oldd = d; 190   olde = e; 191  192   for (j = 0; j < 80; j++) 193   { 194    if (j < 16) { w[j] = x[i + j]; } 195    else { w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); } 196    t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), 197             safe_add(safe_add(e, w[j]), sha1_kt(j))); 198    e = d; 199    d = c; 200    c = rol(b, 30); 201    b = a; 202    a = t; 203   } 204  205   a = safe_add(a, olda); 206   b = safe_add(b, oldb); 207   c = safe_add(c, oldc); 208   d = safe_add(d, oldd); 209   e = safe_add(e, olde); 210  } 211  return [a, b, c, d, e]; 212 } 213  214 /* 215  * Perform the appropriate triplet combination function for the current 216  * iteration 217 */ 218 function sha1_ft(t, b, c, d) 219 { 220  if (t < 20) { return (b & c) | ((~b) & d); } 221  if (t < 40) { return b ^ c ^ d; } 222  if (t < 60) { return (b & c) | (b & d) | (c & d); } 223  return b ^ c ^ d; 224 } 225  226 /* 227  * Determine the appropriate additive constant for the current iteration 228 */ 229 function sha1_kt(t) 230 { 231  return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : 232     (t < 60) ? -1894007588 : -899497514; 233 } 234  235 /* 236  * Calculate the HMAC-SHA1 of a key and some data 237 */ 238 function core_hmac_sha1(key, data) 239 { 240  var bkey = str2binb(key); 241  if (bkey.length > 16) { bkey = core_sha1(bkey, key.length * 8); } 242  243  var ipad = new Array(16), opad = new Array(16); 244  for (var i = 0; i < 16; i++) 245  { 246   ipad[i] = bkey[i] ^ 0x36363636; 247   opad[i] = bkey[i] ^ 0x5C5C5C5C; 248  } 249  250  var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8); 251  return core_sha1(opad.concat(hash), 512 + 160); 252 } 253  254 /* 255  * Add integers, wrapping at 2^32. This uses 16-bit operations internally 256  * to work around bugs in some JS interpreters. 257 */ 258 function safe_add(x, y) 259 { 260  var lsw = (x & 0xFFFF) + (y & 0xFFFF); 261  var msw = (x >> 16) + (y >> 16) + (lsw >> 16); 262  return (msw << 16) | (lsw & 0xFFFF); 263 } 264  265 /* 266  * Bitwise rotate a 32-bit number to the left. 267 */ 268 function rol(num, cnt) 269 { 270  return (num << cnt) | (num >>> (32 - cnt)); 271 } 272  273 /* 274  * Convert an 8-bit or 16-bit string to an array of big-endian words 275  * In 8-bit function, characters >255 have their hi-byte silently ignored. 276 */ 277 function str2binb(str) 278 { 279  var bin = []; 280  var mask = 255; 281  for (var i = 0; i < str.length * 8; i += 8) 282  { 283   bin[i>>5] |= (str.charCodeAt(i / 8) & mask) << (24 - i%32); 284  } 285  return bin; 286 } 287  288 /* 289  * Convert an array of big-endian words to a string 290 */ 291 function binb2str(bin) 292 { 293  var str = ""; 294  var mask = 255; 295  for (var i = 0; i < bin.length * 32; i += 8) 296  { 297   str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask); 298  } 299  return str; 300 } 301  302 /* 303  * Convert an array of big-endian words to a base-64 string 304 */ 305 function binb2b64(binarray) 306 { 307  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 308  var str = ""; 309  var triplet, j; 310  for (var i = 0; i < binarray.length * 4; i += 3) 311  { 312   triplet = (((binarray[i  >> 2] >> 8 * (3 - i  %4)) & 0xFF) << 16) | 313        (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) | 314        ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); 315   for (j = 0; j < 4; j++) 316   { 317    if (i * 8 + j * 6 > binarray.length * 32) { str += "="; } 318    else { str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } 319   } 320  } 321  return str; 322 } 323  324 /* 325  * These are the functions you'll usually want to call 326  * They take string arguments and return either hex or base-64 encoded strings 327 */ 328 return { 329   b64_hmac_sha1: function (key, data){ return binb2b64(core_hmac_sha1(key, data)); }, 330   b64_sha1:    function (s) { return binb2b64(core_sha1(str2binb(s),s.length * 8)); }, 331   binb2str:    binb2str, 332   core_hmac_sha1: core_hmac_sha1, 333   str_hmac_sha1: function (key, data){ return binb2str(core_hmac_sha1(key, data)); }, 334   str_sha1:    function (s) { return binb2str(core_sha1(str2binb(s),s.length * 8)); }, 335 }; 336 })); 337  338 /* 339  * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message 340  * Digest Algorithm, as defined in RFC 1321. 341  * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002. 342  * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 343  * Distributed under the BSD License 344  * See http://pajhome.org.uk/crypt/md5 for more info. 345 */ 346  347 /* 348  * Everything that isn't used by Strophe has been stripped here! 349 */ 350  351 (function (root, factory) { 352   if (typeof define === 'function' && define.amd) { 353     define('strophe-md5', function () { 354       return factory(); 355     }); 356   } else { 357     // Browser globals 358     root.MD5 = factory(); 359   } 360 }(this, function (b) { 361   /* 362    * Add integers, wrapping at 2^32. This uses 16-bit operations internally 363    * to work around bugs in some JS interpreters. 364   */ 365   var safe_add = function (x, y) { 366     var lsw = (x & 0xFFFF) + (y & 0xFFFF); 367     var msw = (x >> 16) + (y >> 16) + (lsw >> 16); 368     return (msw << 16) | (lsw & 0xFFFF); 369   }; 370  371   /* 372    * Bitwise rotate a 32-bit number to the left. 373   */ 374   var bit_rol = function (num, cnt) { 375     return (num << cnt) | (num >>> (32 - cnt)); 376   }; 377  378   /* 379    * Convert a string to an array of little-endian words 380   */ 381   var str2binl = function (str) { 382     var bin = []; 383     for(var i = 0; i < str.length * 8; i += 8) 384     { 385       bin[i>>5] |= (str.charCodeAt(i / 8) & 255) << (i%32); 386     } 387     return bin; 388   }; 389  390   /* 391    * Convert an array of little-endian words to a string 392   */ 393   var binl2str = function (bin) { 394     var str = ""; 395     for(var i = 0; i < bin.length * 32; i += 8) 396     { 397       str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & 255); 398     } 399     return str; 400   }; 401  402   /* 403    * Convert an array of little-endian words to a hex string. 404   */ 405   var binl2hex = function (binarray) { 406     var hex_tab = "0123456789abcdef"; 407     var str = ""; 408     for(var i = 0; i < binarray.length * 4; i++) 409     { 410       str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + 411         hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF); 412     } 413     return str; 414   }; 415  416   /* 417    * These functions implement the four basic operations the algorithm uses. 418   */ 419   var md5_cmn = function (q, a, b, x, s, t) { 420     return safe_add(bit_rol(safe_add(safe_add(a, q),safe_add(x, t)), s),b); 421   }; 422  423   var md5_ff = function (a, b, c, d, x, s, t) { 424     return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); 425   }; 426  427   var md5_gg = function (a, b, c, d, x, s, t) { 428     return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); 429   }; 430  431   var md5_hh = function (a, b, c, d, x, s, t) { 432     return md5_cmn(b ^ c ^ d, a, b, x, s, t); 433   }; 434  435   var md5_ii = function (a, b, c, d, x, s, t) { 436     return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); 437   }; 438  439   /* 440    * Calculate the MD5 of an array of little-endian words, and a bit length 441   */ 442   var core_md5 = function (x, len) { 443     /* append padding */ 444     x[len >> 5] |= 0x80 << ((len) % 32); 445     x[(((len + 64) >>> 9) << 4) + 14] = len; 446  447     var a = 1732584193; 448     var b = -271733879; 449     var c = -1732584194; 450     var d = 271733878; 451  452     var olda, oldb, oldc, oldd; 453     for (var i = 0; i < x.length; i += 16) 454     { 455       olda = a; 456       oldb = b; 457       oldc = c; 458       oldd = d; 459  460       a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); 461       d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); 462       c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); 463       b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); 464       a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); 465       d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); 466       c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); 467       b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); 468       a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); 469       d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); 470       c = md5_ff(c, d, a, b, x[i+10], 17, -42063); 471       b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); 472       a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); 473       d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); 474       c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); 475       b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); 476  477       a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); 478       d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); 479       c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); 480       b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); 481       a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); 482       d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); 483       c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); 484       b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); 485       a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); 486       d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); 487       c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); 488       b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); 489       a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); 490       d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); 491       c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); 492       b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); 493  494       a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); 495       d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); 496       c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); 497       b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); 498       a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); 499       d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); 500       c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); 501       b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); 502       a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); 503       d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); 504       c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); 505       b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); 506       a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); 507       d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); 508       c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); 509       b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); 510  511       a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); 512       d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); 513       c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); 514       b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); 515       a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); 516       d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); 517       c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); 518       b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); 519       a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); 520       d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); 521       c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); 522       b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); 523       a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); 524       d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); 525       c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); 526       b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); 527  528       a = safe_add(a, olda); 529       b = safe_add(b, oldb); 530       c = safe_add(c, oldc); 531       d = safe_add(d, oldd); 532     } 533     return [a, b, c, d]; 534   }; 535  536   var obj = { 537     /* 538      * These are the functions you'll usually want to call. 539      * They take string arguments and return either hex or base-64 encoded 540      * strings. 541     */ 542     hexdigest: function (s) { 543       return binl2hex(core_md5(str2binl(s), s.length * 8)); 544     }, 545  546     hash: function (s) { 547       return binl2str(core_md5(str2binl(s), s.length * 8)); 548     } 549   }; 550   return obj; 551 })); 552  553 /* 554   This program is distributed under the terms of the MIT license. 555   Please see the LICENSE file for details. 556  557   Copyright 2006-2008, OGG, LLC 558 */ 559  560 /* jshint undef: true, unused: true:, noarg: true, latedef: true */ 561  562 /** PrivateFunction: Function.prototype.bind 563  * Bind a function to an instance. 564  * 565  * This Function object extension method creates a bound method similar 566  * to those in Python. This means that the 'this' object will point 567  * to the instance you want. See 568  * <a href='https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind'>MDC's bind() documentation</a> and 569  * <a href='http://benjamin.smedbergs.us/blog/2007-01-03/bound-functions-and-function-imports-in-javascript/'>Bound Functions and Function Imports in JavaScript</a> 570  * for a complete explanation. 571  * 572  * This extension already exists in some browsers (namely, Firefox 3), but 573  * we provide it to support those that don't. 574  * 575  * Parameters: 576  *  (Object) obj - The object that will become 'this' in the bound function. 577  *  (Object) argN - An option argument that will be prepended to the 578  *   arguments given for the function call 579  * 580  * Returns: 581  *  The bound function. 582 */ 583 if (!Function.prototype.bind) { 584   Function.prototype.bind = function (obj /*, arg1, arg2, ... */) 585   { 586     var func = this; 587     var _slice = Array.prototype.slice; 588     var _concat = Array.prototype.concat; 589     var _args = _slice.call(arguments, 1); 590  591     return function () { 592       return func.apply(obj ? obj : this, 593                _concat.call(_args, 594                      _slice.call(arguments, 0))); 595     }; 596   }; 597 } 598  599 /** PrivateFunction: Array.isArray 600  * This is a polyfill for the ES5 Array.isArray method. 601 */ 602 if (!Array.isArray) { 603   Array.isArray = function(arg) { 604     return Object.prototype.toString.call(arg) === '[object Array]'; 605   }; 606 } 607  608 /** PrivateFunction: Array.prototype.indexOf 609  * Return the index of an object in an array. 610  * 611  * This function is not supplied by some JavaScript implementations, so 612  * we provide it if it is missing. This code is from: 613  * http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference:Objects:Array:indexOf 614  * 615  * Parameters: 616  *  (Object) elt - The object to look for. 617  *  (Integer) from - The index from which to start looking. (optional). 618  * 619  * Returns: 620  *  The index of elt in the array or -1 if not found. 621 */ 622 if (!Array.prototype.indexOf) 623   { 624     Array.prototype.indexOf = function(elt /*, from*/) 625     { 626       var len = this.length; 627  628       var from = Number(arguments[1]) || 0; 629       from = (from < 0) ? Math.ceil(from) : Math.floor(from); 630       if (from < 0) { 631         from += len; 632       } 633  634       for (; from < len; from++) { 635         if (from in this && this[from] === elt) { 636           return from; 637         } 638       } 639  640       return -1; 641     }; 642   } 643  644 /* 645   This program is distributed under the terms of the MIT license. 646   Please see the LICENSE file for details. 647  648   Copyright 2006-2008, OGG, LLC 649 */ 650  651 /* jshint undef: true, unused: true:, noarg: true, latedef: true */ 652 /*global define, document, window, setTimeout, clearTimeout, console, ActiveXObject, DOMParser */ 653  654 (function (root, factory) { 655   if (typeof define === 'function' && define.amd) { 656     define('strophe-core', [ 657       'strophe-sha1', 658       'strophe-base64', 659       'strophe-md5', 660       "strophe-polyfill" 661     ], function () { 662       return factory.apply(this, arguments); 663     }); 664   } else { 665     // Browser globals 666     var o = factory(root.SHA1, root.Base64, root.MD5); 667     window.Strophe =    o.Strophe; 668     window.$build =     o.$build; 669     window.$iq =      o.$iq; 670     window.$msg =      o.$msg; 671     window.$pres =     o.$pres; 672     window.SHA1 =      o.SHA1; 673     window.Base64 =     o.Base64; 674     window.MD5 =      o.MD5; 675     window.b64_hmac_sha1 = o.SHA1.b64_hmac_sha1; 676     window.b64_sha1 =    o.SHA1.b64_sha1; 677     window.str_hmac_sha1 = o.SHA1.str_hmac_sha1; 678     window.str_sha1 =    o.SHA1.str_sha1; 679   } 680 }(this, function (SHA1, Base64, MD5) { 681  682 var Strophe; 683  684 /** Function: $build 685  * Create a Strophe.Builder. 686  * This is an alias for 'new Strophe.Builder(name, attrs)'. 687  * 688  * Parameters: 689  *  (String) name - The root element name. 690  *  (Object) attrs - The attributes for the root element in object notation. 691  * 692  * Returns: 693  *  A new Strophe.Builder object. 694 */ 695 function $build(name, attrs) { return new Strophe.Builder(name, attrs); } 696  697 /** Function: $msg 698  * Create a Strophe.Builder with a <message/> element as the root. 699  * 700  * Parmaeters: 701  *  (Object) attrs - The <message/> element attributes in object notation. 702  * 703  * Returns: 704  *  A new Strophe.Builder object. 705 */ 706 function $msg(attrs) { return new Strophe.Builder("message", attrs); } 707  708 /** Function: $iq 709  * Create a Strophe.Builder with an <iq/> element as the root. 710  * 711  * Parameters: 712  *  (Object) attrs - The <iq/> element attributes in object notation. 713  * 714  * Returns: 715  *  A new Strophe.Builder object. 716 */ 717 function $iq(attrs) { return new Strophe.Builder("iq", attrs); } 718  719 /** Function: $pres 720  * Create a Strophe.Builder with a <presence/> element as the root. 721  * 722  * Parameters: 723  *  (Object) attrs - The <presence/> element attributes in object notation. 724  * 725  * Returns: 726  *  A new Strophe.Builder object. 727 */ 728 function $pres(attrs) { return new Strophe.Builder("presence", attrs); } 729  730 /** Class: Strophe 731  * An object container for all Strophe library functions. 732  * 733  * This class is just a container for all the objects and constants 734  * used in the library. It is not meant to be instantiated, but to 735  * provide a namespace for library objects, constants, and functions. 736 */ 737 Strophe = { 738   /** Constant: VERSION 739    * The version of the Strophe library. Unreleased builds will have 740    * a version of head-HASH where HASH is a partial revision. 741   */ 742   VERSION: "1.2.2", 743  744   /** Constants: XMPP Namespace Constants 745    * Common namespace constants from the XMPP RFCs and XEPs. 746    * 747    * NS.HTTPBIND - HTTP BIND namespace from XEP 124. 748    * NS.BOSH - BOSH namespace from XEP 206. 749    * NS.CLIENT - Main XMPP client namespace. 750    * NS.AUTH - Legacy authentication namespace. 751    * NS.ROSTER - Roster operations namespace. 752    * NS.PROFILE - Profile namespace. 753    * NS.DISCO_INFO - Service discovery info namespace from XEP 30. 754    * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30. 755    * NS.MUC - Multi-User Chat namespace from XEP 45. 756    * NS.SASL - XMPP SASL namespace from RFC 3920. 757    * NS.STREAM - XMPP Streams namespace from RFC 3920. 758    * NS.BIND - XMPP Binding namespace from RFC 3920. 759    * NS.SESSION - XMPP Session namespace from RFC 3920. 760    * NS.XHTML_IM - XHTML-IM namespace from XEP 71. 761    * NS.XHTML - XHTML body namespace from XEP 71. 762   */ 763   NS: { 764     HTTPBIND: "http://jabber.org/protocol/httpbind", 765     BOSH: "urn:xmpp:xbosh", 766     CLIENT: "jabber:client", 767     AUTH: "jabber:iq:auth", 768     ROSTER: "jabber:iq:roster", 769     PROFILE: "jabber:iq:profile", 770     DISCO_INFO: "http://jabber.org/protocol/disco#info", 771     DISCO_ITEMS: "http://jabber.org/protocol/disco#items", 772     MUC: "http://jabber.org/protocol/muc", 773     SASL: "urn:ietf:params:", 774     STREAM: "http://etherx.jabber.org/streams", 775     FRAMING: "urn:ietf:params:", 776     BIND: "urn:ietf:params:", 777     SESSION: "urn:ietf:params:", 778     VERSION: "jabber:iq:version", 779     STANZAS: "urn:ietf:params:", 780     XHTML_IM: "http://jabber.org/protocol/xhtml-im", 781     XHTML: "http://www.w3.org/1999/xhtml" 782   }, 783  784  785   /** Constants: XHTML_IM Namespace 786    * contains allowed tags, tag attributes, and css properties. 787    * Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset. 788    * See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended 789    * allowed tags and their attributes. 790   */ 791   XHTML: { 792         tags: ['a','blockquote','br','cite','em','img','li','ol','p','span','strong','ul','body'], 793         attributes: { 794             'a':     ['href'], 795             'blockquote': ['style'], 796             'br':     [], 797             'cite':    ['style'], 798             'em':     [], 799             'img':    ['src', 'alt', 'style', 'height', 'width'], 800             'li':     ['style'], 801             'ol':     ['style'], 802             'p':     ['style'], 803             'span':    ['style'], 804             'strong':   [], 805             'ul':     ['style'], 806             'body':    [] 807         }, 808         css: ['background-color','color','font-family','font-size','font-style','font-weight','margin-left','margin-right','text-align','text-decoration'], 809         /** Function: XHTML.validTag 810          * 811          * Utility method to determine whether a tag is allowed 812          * in the XHTML_IM namespace. 813          * 814          * XHTML tag names are case sensitive and must be lower case. 815         */ 816         validTag: function(tag) { 817             for (var i = 0; i < Strophe.XHTML.tags.length; i++) { 818                 if (tag == Strophe.XHTML.tags[i]) { 819                     return true; 820                 } 821             } 822             return false; 823         }, 824         /** Function: XHTML.validAttribute 825          * 826          * Utility method to determine whether an attribute is allowed 827          * as recommended per XEP-0071 828          * 829          * XHTML attribute names are case sensitive and must be lower case. 830         */ 831         validAttribute: function(tag, attribute) { 832             if(typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) { 833                 for(var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) { 834                     if(attribute == Strophe.XHTML.attributes[tag][i]) { 835                         return true; 836                     } 837                 } 838             } 839             return false; 840         }, 841         validCSS: function(style) 842         { 843             for(var i = 0; i < Strophe.XHTML.css.length; i++) { 844                 if(style == Strophe.XHTML.css[i]) { 845                     return true; 846                 } 847             } 848             return false; 849         } 850   }, 851  852   /** Constants: Connection Status Constants 853    * Connection status constants for use by the connection handler 854    * callback. 855    * 856    * Status.ERROR - An error has occurred 857    * Status.CONNECTING - The connection is currently being made 858    * Status.CONNFAIL - The connection attempt failed 859    * Status.AUTHENTICATING - The connection is authenticating 860    * Status.AUTHFAIL - The authentication attempt failed 861    * Status.CONNECTED - The connection has succeeded 862    * Status.DISCONNECTED - The connection has been terminated 863    * Status.DISCONNECTING - The connection is currently being terminated 864    * Status.ATTACHED - The connection has been attached 865   */ 866   Status: { 867     ERROR: 0, 868     CONNECTING: 1, 869     CONNFAIL: 2, 870     AUTHENTICATING: 3, 871     AUTHFAIL: 4, 872     CONNECTED: 5, 873     DISCONNECTED: 6, 874     DISCONNECTING: 7, 875     ATTACHED: 8, 876     REDIRECT: 9 877   }, 878  879   /** Constants: Log Level Constants 880    * Logging level indicators. 881    * 882    * LogLevel.DEBUG - Debug output 883    * LogLevel.INFO - Informational output 884    * LogLevel.WARN - Warnings 885    * LogLevel.ERROR - Errors 886    * LogLevel.FATAL - Fatal errors 887   */ 888   LogLevel: { 889     DEBUG: 0, 890     INFO: 1, 891     WARN: 2, 892     ERROR: 3, 893     FATAL: 4 894   }, 895  896   /** PrivateConstants: DOM Element Type Constants 897    * DOM element types. 898    * 899    * ElementType.NORMAL - Normal element. 900    * ElementType.TEXT - Text data element. 901    * ElementType.FRAGMENT - XHTML fragment element. 902   */ 903   ElementType: { 904     NORMAL: 1, 905     TEXT: 3, 906     CDATA: 4, 907     FRAGMENT: 11 908   }, 909  910   /** PrivateConstants: Timeout Values 911    * Timeout values for error states. These values are in seconds. 912    * These should not be changed unless you know exactly what you are 913    * doing. 914    * 915    * TIMEOUT - Timeout multiplier. A waiting request will be considered 916    *   failed after Math.floor(TIMEOUT * wait) seconds have elapsed. 917    *   This defaults to 1.1, and with default wait, 66 seconds. 918    * SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where 919    *   Strophe can detect early failure, it will consider the request 920    *   failed if it doesn't return after 921    *   Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed. 922    *   This defaults to 0.1, and with default wait, 6 seconds. 923   */ 924   TIMEOUT: 1.1, 925   SECONDARY_TIMEOUT: 0.1, 926  927   /** Function: addNamespace 928    * This function is used to extend the current namespaces in 929    * Strophe.NS. It takes a key and a value with the key being the 930    * name of the new namespace, with its actual value. 931    * For example: 932    * Strophe.addNamespace('PUBSUB', "http://jabber.org/protocol/pubsub"); 933    * 934    * Parameters: 935    *  (String) name - The name under which the namespace will be 936    *   referenced under Strophe.NS 937    *  (String) value - The actual namespace. 938   */ 939   addNamespace: function (name, value) 940   { 941    Strophe.NS[name] = value; 942   }, 943  944   /** Function: forEachChild 945    * Map a function over some or all child elements of a given element. 946    * 947    * This is a small convenience function for mapping a function over 948    * some or all of the children of an element. If elemName is null, all 949    * children will be passed to the function, otherwise only children 950    * whose tag names match elemName will be passed. 951    * 952    * Parameters: 953    *  ( 954    *  (String) elemName - The child element tag name filter. 955    *  (Function) func - The function to apply to each child. This 956    *   function should take a single argument, a DOM element. 957   */ 958   forEachChild: function (elem, elemName, func) 959   { 960     var i, childNode; 961  962     for (i = 0; i < elem.childNodes.length; i++) { 963       childNode = elem.childNodes[i]; 964       if (childNode.nodeType == Strophe.ElementType.NORMAL && 965         (!elemName || this.isTagEqual(childNode, elemName))) { 966         func(childNode); 967       } 968     } 969   }, 970  971   /** Function: isTagEqual 972    * Compare an element's tag name with a string. 973    * 974    * This function is case sensitive. 975    * 976    * Parameters: 977    *  ( 978    *  (String) name - The element name. 979    * 980    * Returns: 981    *  true if the element's tag name matches _el_, and false 982    *  otherwise. 983   */ 984   isTagEqual: function (el, name) 985   { 986     return el.tagName == name; 987   }, 988  989   /** PrivateVariable: _ 990    * _Private_ variable that caches a DOM document to 991    * generate elements. 992   */ 993   _null, 994  995   /** PrivateFunction: _makeGenerator 996    * _Private_ function that creates a dummy  997    * an element and text node generator. 998   */ 999   _makeGenerator: function () {1000     var doc;1001 1002     // IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload.1003     // Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be1004         // less than 10 in the case of IE9 and below.1005     if (document.implementation.createDocument === undefined ||1006             document.implementation.createDocument && document.documentMode && document.documentMode < 10) {1007       doc = this._getIE1008       doc.appendChild(doc.createElement('strophe'));1009     } else {1010       doc = document.implementation1011         .createDocument('jabber:client', 'strophe', null);1012     }1013 1014     return doc;1015   },1016 1017   /** Function: 1018    * Get the DOM document to generate elements.1019    *1020    * Returns:1021    *  The currently used DOM document.1022   */1023   1024     if (!Strophe._1025       Strophe._ Strophe._makeGenerator();1026     }1027     return Strophe._1028   },1029 1030   /** PrivateFunction: _getIE1031    * Gets IE 1032    *1033    * Returns:1034    *  A Microsoft 1035    * See Also:1036    *  http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx1037   */1038   _getIE1039     var doc = null;1040     var docStrings = [1041       "Ms",1042       "Ms",1043       "Ms",1044       "MS",1045       "MS",1046       "MS",1047       "Microsoft."1048     ];1049 1050     for (var d = 0; d < docStrings.length; d++) {1051       if (doc === null) {1052         try {1053           doc = new ActiveXObject(docStrings[d]);1054         } catch (e) {1055           doc = null;1056         }1057       } else {1058         break;1059       }1060     }1061 1062     return doc;1063   },1064 1065   /** Function: 1066    * Create an 1067    *1068    * This function creates an 1069    * implementations. Note that these are not HTML DOM elements, which1070    * aren't appropriate for XMPP stanzas.1071    *1072    * Parameters:1073    *  (String) name - The name for the element.1074    *  (Array|Object) attrs - An optional array or object containing1075    *   key/value pairs to use as element attributes. The object should1076    *   be in the format {'key': 'value'} or {key: 'value'}. The array1077    *   should have the format [['key1', 'value1'], ['key2', 'value2']].1078    *  (String) text - The text child data for the element.1079    *1080    * Returns:1081    *  A new 1082   */1083   1084   {1085     if (!name) { return null; }1086 1087     var node = Strophe.1088 1089     // FIXME: this should throw errors if args are the wrong type or1090     // there are more than two optional args1091     var a, i, k;1092     for (a = 1; a < arguments.length; a++) {1093       var arg = arguments[a];1094       if (!arg) { continue; }1095       if (typeof(arg) == "string" ||1096         typeof(arg) == "number") {1097         node.appendChild(Strophe.1098       } else if (typeof(arg) == "object" &&1099            typeof(arg.sort) == "function") {1100         for (i = 0; i < arg.length; i++) {1101           var attr = arg[i];1102           if (typeof(attr) == "object" &&1103             typeof(attr.sort) == "function" &&1104             attr[1] !== undefined) {1105             node.setAttribute(attr[0], attr[1]);1106           }1107         }1108       } else if (typeof(arg) == "object") {1109         for (k in arg) {1110           if (arg.hasOwnProperty(k)) {1111             if (arg[k] !== undefined) {1112               node.setAttribute(k, arg[k]);1113             }1114           }1115         }1116       }1117     }1118 1119     return node;1120   },1121 1122   /* Function: 1123    * Excapes invalid 1124    *1125    * Parameters:1126    *   (String) text - text to escape.1127    *1128    * Returns:1129    *   Escaped text.1130   */1131   1132   {1133     text = text.replace(/\&/g, "&amp;");1134     text = text.replace(/</g, "&lt;");1135     text = text.replace(/>/g, "&gt;");1136     text = text.replace(/'/g, "&apos;");1137     text = text.replace(/"/g, "&quot;");1138     return text;1139   },1140 1141   /* Function: 1142   * Unexcapes invalid 1143   *1144   * Parameters:1145   *   (String) text - text to unescape.1146   *1147   * Returns:1148   *   Unescaped text.1149   */1150   1151   {1152     text = text.replace(/\&amp;/g, "&");1153     text = text.replace(/&lt;/g, "<");1154     text = text.replace(/&gt;/g, ">");1155     text = text.replace(/&apos;/g, "'");1156     text = text.replace(/&quot;/g, "\"");1157     return text;1158   },1159 1160   /** Function: 1161    * Creates an 1162    *1163    * Provides a cross implementation version of document.createTextNode.1164    *1165    * Parameters:1166    *  (String) text - The content of the text node.1167    *1168    * Returns:1169    *  A new 1170   */1171   1172   {1173     return Strophe.1174   },1175 1176   /** Function: 1177    * Creates an 1178    *1179    * Parameters:1180    *  (String) html - The content of the html node.1181    *1182    * Returns:1183    *  A new 1184   */1185   1186   {1187     var node;1188     //ensure text is escaped1189     if (window.DOMParser) {1190       var parser = new DOMParser();1191       node = parser.parseFromString(html, "text/");1192     } else {1193       node = new ActiveXObject("Microsoft.");1194       node.async="false";1195       node.load1196     }1197     return node;1198   },1199 1200   /** Function: getText1201    * Get the concatenation of all text children of an element.1202    *1203    * Parameters:1204    *  (1205    *1206    * Returns:1207    *  A String with the concatenated text of all text element children.1208   */1209   getText: function (elem)1210   {1211     if (!elem) { return null; }1212 1213     var str = "";1214     if (elem.childNodes.length === 0 && elem.nodeType ==1215       Strophe.ElementType.TEXT) {1216       str += elem.nodeValue;1217     }1218 1219     for (var i = 0; i < elem.childNodes.length; i++) {1220       if (elem.childNodes[i].nodeType == Strophe.ElementType.TEXT) {1221         str += elem.childNodes[i].nodeValue;1222       }1223     }1224 1225     return Strophe.1226   },1227 1228   /** Function: copyElement1229    * Copy an 1230    *1231    * This function copies a DOM element and all its descendants and returns1232    * the new copy.1233    *1234    * Parameters:1235    *  (1236    *1237    * Returns:1238    *  A new, copied DOM element tree.1239   */1240   copyElement: function (elem)1241   {1242     var i, el;1243     if (elem.nodeType == Strophe.ElementType.NORMAL) {1244       el = Strophe.1245 1246       for (i = 0; i < elem.attributes.length; i++) {1247         el.setAttribute(elem.attributes[i].nodeName,1248                 elem.attributes[i].value);1249       }1250 1251       for (i = 0; i < elem.childNodes.length; i++) {1252         el.appendChild(Strophe.copyElement(elem.childNodes[i]));1253       }1254     } else if (elem.nodeType == Strophe.ElementType.TEXT) {1255       el = Strophe.1256     }1257 1258     return el;1259   },1260 1261 1262   /** Function: createHtml1263    * Copy an HTML DOM element into an 1264    *1265    * This function copies a DOM element and all its descendants and returns1266    * the new copy.1267    *1268    * Parameters:1269    *  (HTMLElement) elem - A DOM element.1270    *1271    * Returns:1272    *  A new, copied DOM element tree.1273   */1274   createHtml: function (elem)1275   {1276     var i, el, j, tag, attribute, value, css, cssAttrs, attr, cssName, cssValue;1277     if (elem.nodeType == Strophe.ElementType.NORMAL) {1278       tag = elem.nodeName.toLowerCase(); // XHTML tags must be lower case.1279       if(Strophe.XHTML.validTag(tag)) {1280         try {1281           el = Strophe.1282           for(i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {1283             attribute = Strophe.XHTML.attributes[tag][i];1284             value = elem.getAttribute(attribute);1285             if(typeof value == 'undefined' || value === null || value === '' || value === false || value === 0) {1286               continue;1287             }1288             if(attribute == 'style' && typeof value == 'object') {1289               if(typeof value.cssText != 'undefined') {1290                 value = value.cssText; // we're dealing with IE, need to get CSS out1291               }1292             }1293             // filter out invalid css styles1294             if(attribute == 'style') {1295               css = [];1296               cssAttrs = value.split(';');1297               for(j = 0; j < cssAttrs.length; j++) {1298                 attr = cssAttrs[j].split(':');1299                 cssName = attr[0].replace(/^\s*/, "").replace(/\s*$/, "").toLowerCase();1300                 if(Strophe.XHTML.validCSS(cssName)) {1301                   cssValue = attr[1].replace(/^\s*/, "").replace(/\s*$/, "");1302                   css.push(cssName + ': ' + cssValue);1303                 }1304               }1305               if(css.length > 0) {1306                 value = css.join('; ');1307                 el.setAttribute(attribute, value);1308               }1309             } else {1310               el.setAttribute(attribute, value);1311             }1312           }1313 1314           for (i = 0; i < elem.childNodes.length; i++) {1315             el.appendChild(Strophe.createHtml(elem.childNodes[i]));1316           }1317         } catch(e) { // invalid elements1318          el = Strophe.'');1319         }1320       } else {1321         el = Strophe.1322         for (i = 0; i < elem.childNodes.length; i++) {1323           el.appendChild(Strophe.createHtml(elem.childNodes[i]));1324         }1325       }1326     } else if (elem.nodeType == Strophe.ElementType.FRAGMENT) {1327       el = Strophe.1328       for (i = 0; i < elem.childNodes.length; i++) {1329         el.appendChild(Strophe.createHtml(elem.childNodes[i]));1330       }1331     } else if (elem.nodeType == Strophe.ElementType.TEXT) {1332       el = Strophe.1333     }1334 1335     return el;1336   },1337 1338   /** Function: escapeNode1339    * Escape the node part (also called local part) of a JID.1340    *1341    * Parameters:1342    *  (String) node - A node (or local part).1343    *1344    * Returns:1345    *  An escaped node (or local part).1346   */1347   escapeNode: function (node)1348   {1349     if (typeof node !== "string") { return node; }1350     return node.replace(/^\s+|\s+$/g, '')1351       .replace(/\\/g, "\\5c")1352       .replace(/ /g,  "\\20")1353       .replace(/\"/g, "\\22")1354       .replace(/\&/g, "\\26")1355       .replace(/\'/g, "\\27")1356       .replace(/\//g, "\\2f")1357       .replace(/:/g,  "\\3a")1358       .replace(/</g,  "\\3c")1359       .replace(/>/g,  "\\3e")1360       .replace(/@/g,  "\\40");1361   },1362 1363   /** Function: unescapeNode1364    * Unescape a node part (also called local part) of a JID.1365    *1366    * Parameters:1367    *  (String) node - A node (or local part).1368    *1369    * Returns:1370    *  An unescaped node (or local part).1371   */1372   unescapeNode: function (node)1373   {1374     if (typeof node !== "string") { return node; }1375     return node.replace(/\\20/g, " ")1376       .replace(/\\22/g, '"')1377       .replace(/\\26/g, "&")1378       .replace(/\\27/g, "'")1379       .replace(/\\2f/g, "/")1380       .replace(/\\3a/g, ":")1381       .replace(/\\3c/g, "<")1382       .replace(/\\3e/g, ">")1383       .replace(/\\40/g, "@")1384       .replace(/\\5c/g, "\\");1385   },1386 1387   /** Function: getNodeFromJid1388    * Get the node portion of a JID String.1389    *1390    * Parameters:1391    *  (String) jid - A JID.1392    *1393    * Returns:1394    *  A String containing the node.1395   */1396   getNodeFromJid: function (jid)1397   {1398     if (jid.indexOf("@") < 0) { return null; }1399     return jid.split("@")[0];1400   },1401 1402   /** Function: getDomainFromJid1403    * Get the domain portion of a JID String.1404    *1405    * Parameters:1406    *  (String) jid - A JID.1407    *1408    * Returns:1409    *  A String containing the domain.1410   */1411   getDomainFromJid: function (jid)1412   {1413     var bare = Strophe.getBareJidFromJid(jid);1414     if (bare.indexOf("@") < 0) {1415       return bare;1416     } else {1417       var parts = bare.split("@");1418       parts.splice(0, 1);1419       return parts.join('@');1420     }1421   },1422 1423   /** Function: getResourceFromJid1424    * Get the resource portion of a JID String.1425    *1426    * Parameters:1427    *  (String) jid - A JID.1428    *1429    * Returns:1430    *  A String containing the resource.1431   */1432   getResourceFromJid: function (jid)1433   {1434     var s = jid.split("/");1435     if (s.length < 2) { return null; }1436     s.splice(0, 1);1437     return s.join('/');1438   },1439 1440   /** Function: getBareJidFromJid1441    * Get the bare JID from a JID String.1442    *1443    * Parameters:1444    *  (String) jid - A JID.1445    *1446    * Returns:1447    *  A String containing the bare JID.1448   */1449   getBareJidFromJid: function (jid)1450   {1451     return jid ? jid.split("/")[0] : null;1452   },1453 1454   /** Function: log1455    * User overrideable logging function.1456    *1457    * This function is called whenever the Strophe library calls any1458    * of the logging functions. The default implementation of this1459    * function does nothing. If client code wishes to handle the logging1460    * messages, it should override this with1461    * > Strophe.log = function (level, msg) {1462    * >  (user code here)1463    * > };1464    *1465    * Please note that data sent and received over the wire is logged1466    * via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().1467    *1468    * The different levels and their meanings are1469    *1470    *  DEBUG - Messages useful for debugging purposes.1471    *  INFO - Informational messages. This is mostly information like1472    *   'disconnect was called' or 'SASL auth succeeded'.1473    *  WARN - Warnings about potential problems. This is mostly used1474    *   to report transient connection errors like request timeouts.1475    *  ERROR - Some error occurred.1476    *  FATAL - A non-recoverable fatal error occurred.1477    *1478    * Parameters:1479    *  (Integer) level - The log level of the log message. This will1480    *   be one of the values in Strophe.LogLevel.1481    *  (String) msg - The log message.1482   */1483   /* jshint ignore:start */1484   log: function (level, msg)1485   {1486     return;1487   },1488   /* jshint ignore:end */1489 1490   /** Function: debug1491    * Log a message at the Strophe.LogLevel.DEBUG level.1492    *1493    * Parameters:1494    *  (String) msg - The log message.1495   */1496   debug: function(msg)1497   {1498     this.log(this.LogLevel.DEBUG, msg);1499   },1500 1501   /** Function: info1502    * Log a message at the Strophe.LogLevel.INFO level.1503    *1504    * Parameters:1505    *  (String) msg - The log message.1506   */1507   info: function (msg)1508   {1509     this.log(this.LogLevel.INFO, msg);1510   },1511 1512   /** Function: warn1513    * Log a message at the Strophe.LogLevel.WARN level.1514    *1515    * Parameters:1516    *  (String) msg - The log message.1517   */1518   warn: function (msg)1519   {1520     this.log(this.LogLevel.WARN, msg);1521   },1522 1523   /** Function: error1524    * Log a message at the Strophe.LogLevel.ERROR level.1525    *1526    * Parameters:1527    *  (String) msg - The log message.1528   */1529   error: function (msg)1530   {1531     this.log(this.LogLevel.ERROR, msg);1532   },1533 1534   /** Function: fatal1535    * Log a message at the Strophe.LogLevel.FATAL level.1536    *1537    * Parameters:1538    *  (String) msg - The log message.1539   */1540   fatal: function (msg)1541   {1542     this.log(this.LogLevel.FATAL, msg);1543   },1544 1545   /** Function: serialize1546    * Render a DOM element and all descendants to a String.1547    *1548    * Parameters:1549    *  (1550    *1551    * Returns:1552    *  The serialized element tree as a String.1553   */1554   serialize: function (elem)1555   {1556     var result;1557 1558     if (!elem) { return null; }1559 1560     if (typeof(elem.tree) === "function") {1561       elem = elem.tree();1562     }1563 1564     var nodeName = elem.nodeName;1565     var i, child;1566 1567     if (elem.getAttribute("_realname")) {1568       nodeName = elem.getAttribute("_realname");1569     }1570 1571     result = "<" + nodeName;1572     for (i = 0; i < elem.attributes.length; i++) {1573        if(elem.attributes[i].nodeName != "_realname") {1574         result += " " + elem.attributes[i].nodeName +1575         "='" + elem.attributes[i].value1576           .replace(/&/g, "&amp;")1577            .replace(/\'/g, "&apos;")1578            .replace(/>/g, "&gt;")1579            .replace(/</g, "&lt;") + "'";1580         }1581     }1582 1583     if (elem.childNodes.length > 0) {1584       result += ">";1585       for (i = 0; i < elem.childNodes.length; i++) {1586         child = elem.childNodes[i];1587         switch( child.nodeType ){1588          case Strophe.ElementType.NORMAL:1589           // normal element, so recurse1590           result += Strophe.serialize(child);1591           break;1592          case Strophe.ElementType.TEXT:1593           // text element to escape values1594           result += Strophe.1595           break;1596          case Strophe.ElementType.CDATA:1597           // cdata section so don't escape values1598           result += "<![CDATA["+child.nodeValue+"]]>";1599         }1600       }1601       result += "</" + nodeName + ">";1602     } else {1603       result += "/>";1604     }1605 1606     return result;1607   },1608 1609   /** PrivateVariable: _requestId1610    * _Private_ variable that keeps track of the request ids for1611    * connections.1612   */1613   _requestId: 0,1614 1615   /** PrivateVariable: Strophe.connectionPlugins1616    * _Private_ variable Used to store plugin names that need1617    * initialization on Strophe.Connection construction.1618   */1619   _connectionPlugins: {},1620 1621   /** Function: addConnectionPlugin1622    * Extends the Strophe.Connection object with the given plugin.1623    *1624    * Parameters:1625    *  (String) name - The name of the extension.1626    *  (Object) ptype - The plugin's prototype.1627   */1628   addConnectionPlugin: function (name, ptype)1629   {1630     Strophe._connectionPlugins[name] = ptype;1631   }1632 };1633 1634 /** Class: Strophe.Builder1635  * 1636  *1637  * This object provides an interface similar to JQuery but for building1638  * DOM element easily and rapidly. All the functions except for toString()1639  * and tree() return the object, so calls can be chained. Here's an1640  * example using the $iq() builder helper.1641  * > $iq({to: 'you', from: 'me', type: 'get', id: '1'})1642  * >   .c('query', {1643  * >   .c('example')1644  * >   .toString()1645  * The above generates this 1646  * > <iq to='you' from='me' type='get' id='1'>1647  * >  <query 1648  * >   <example/>1649  * >  </query>1650  * > </iq>1651  * The corresponding DOM manipulations to get a similar fragment would be1652  * a lot more tedious and probably involve several helper variables.1653  *1654  * Since adding children makes new operations operate on the child, up()1655  * is provided to traverse up the tree. To add two children, do1656  * > builder.c('child1', ...).up().c('child2', ...)1657  * The next operation on the Builder will be relative to the second child.1658 */1659 1660 /** Constructor: Strophe.Builder1661  * Create a Strophe.Builder object.1662  *1663  * The attributes should be passed in object notation. For example1664  * > var b = new Builder('message', {to: 'you', from: 'me'});1665  * or1666  * > var b = new Builder('messsage', {'1667  *1668  * Parameters:1669  *  (String) name - The name of the root element.1670  *  (Object) attrs - The attributes for the root element in object notation.1671  *1672  * Returns:1673  *  A new Strophe.Builder.1674 */1675 Strophe.Builder = function (name, attrs)1676 {1677   // Set correct namespace for jabber:client elements1678   if (name == "presence" || name == "message" || name == "iq") {1679     if (attrs && !attrs.1680       attrs. Strophe.NS.CLIENT;1681     } else if (!attrs) {1682       attrs = {1683     }1684   }1685 1686   // Holds the tree being built.1687   this.nodeTree = Strophe.1688 1689   // Points to the current operation node.1690   this.node = this.nodeTree;1691 };1692 1693 Strophe.Builder.prototype = {1694   /** Function: tree1695    * Return the DOM tree.1696    *1697    * This function returns the current DOM tree as an element object. This1698    * is suitable for passing to functions like Strophe.Connection.send().1699    *1700    * Returns:1701    *  The DOM tree as a element object.1702   */1703   tree: function ()1704   {1705     return this.nodeTree;1706   },1707 1708   /** Function: toString1709    * Serialize the DOM tree to a String.1710    *1711    * This function returns a string serialization of the current DOM1712    * tree. It is often used internally to pass data to a1713    * Strophe.Request object.1714    *1715    * Returns:1716    *  The serialized DOM tree in a String.1717   */1718   toString: function ()1719   {1720     return Strophe.serialize(this.nodeTree);1721   },1722 1723   /** Function: up1724    * Make the current parent element the new current element.1725    *1726    * This function is often used after c() to traverse back up the tree.1727    * For example, to add two children to the same element1728    * > builder.c('child1', {}).up().c('child2', {});1729    *1730    * Returns:1731    *  The Stophe.Builder object.1732   */1733   up: function ()1734   {1735     this.node = this.node.parentNode;1736     return this;1737   },1738 1739   /** Function: attrs1740    * Add or modify attributes of the current element.1741    *1742    * The attributes should be passed in object notation. This function1743    * does not move the current element pointer.1744    *1745    * Parameters:1746    *  (Object) moreattrs - The attributes to add/modify in object notation.1747    *1748    * Returns:1749    *  The Strophe.Builder object.1750   */1751   attrs: function (moreattrs)1752   {1753     for (var k in moreattrs) {1754       if (moreattrs.hasOwnProperty(k)) {1755         if (moreattrs[k] === undefined) {1756           this.node.removeAttribute(k);1757         } else {1758           this.node.setAttribute(k, moreattrs[k]);1759         }1760       }1761     }1762     return this;1763   },1764 1765   /** Function: c1766    * Add a child to the current element and make it the new current1767    * element.1768    *1769    * This function moves the current element pointer to the child,1770    * unless text is provided. If you need to add another child, it1771    * is necessary to use up() to go back to the parent in the tree.1772    *1773    * Parameters:1774    *  (String) name - The name of the child.1775    *  (Object) attrs - The attributes of the child in object notation.1776    *  (String) text - The text to add to the child.1777    *1778    * Returns:1779    *  The Strophe.Builder object.1780   */1781   c: function (name, attrs, text)1782   {1783     var child = Strophe.1784     this.node.appendChild(child);1785     if (typeof text !== "string") {1786       this.node = child;1787     }1788     return this;1789   },1790 1791   /** Function: cnode1792    * Add a child to the current element and make it the new current1793    * element.1794    *1795    * This function is the same as c() except that instead of using a1796    * name and an attributes object to create the child it uses an1797    * existing DOM element object.1798    *1799    * Parameters:1800    *  (1801    *1802    * Returns:1803    *  The Strophe.Builder object.1804   */1805   cnode: function (elem)1806   {1807     var impNode;1808     var  Strophe.1809     try {1810       impNode = ( undefined);1811     }1812     catch (e) {1813       impNode = false;1814     }1815     var newElem = impNode ?1816            true) :1817            Strophe.copyElement(elem);1818     this.node.appendChild(newElem);1819     this.node = newElem;1820     return this;1821   },1822 1823   /** Function: t1824    * Add a child text element.1825    *1826    * This *does not* make the child the new current element since there1827    * are no children of text elements.1828    *1829    * Parameters:1830    *  (String) text - The text data to append to the current element.1831    *1832    * Returns:1833    *  The Strophe.Builder object.1834   */1835   t: function (text)1836   {1837     //text=CHEncode(text);1838     var child = Strophe.1839     this.node.appendChild(child);1840     return this;1841   },1842 1843   cht: function (text)1844   {1845     text=Base64.chencode(text);1846     var child = Strophe.1847     this.node.appendChild(child);1848     return this;1849   },1850 1851   /** Function: h1852    * Replace current element contents with the HTML passed in.1853    *1854    * This *does not* make the child the new current element1855    *1856    * Parameters:1857    *  (String) html - The html to insert as contents of current element.1858    *1859    * Returns:1860    *  The Strophe.Builder object.1861   */1862   h: function (html)1863   {1864     var fragment = document.createElement('body');1865 1866     // force the browser to try and fix any invalid HTML tags1867     fragment.innerHTML = html;1868 1869     // copy cleaned html into an 1870     var xhtml = Strophe.createHtml(fragment);1871 1872     while(xhtml.childNodes.length > 0) {1873       this.node.appendChild(xhtml.childNodes[0]);1874     }1875     return this;1876   }1877 };1878 1879 /** PrivateClass: Strophe.Handler1880  * _Private_ helper class for managing stanza handlers.1881  *1882  * A Strophe.Handler encapsulates a user provided callback function to be1883  * executed when matching stanzas are received by the connection.1884  * Handlers can be either one-off or persistant depending on their1885  * return value. Returning true will cause a Handler to remain active, and1886  * returning false will remove the Handler.1887  *1888  * Users will not use Strophe.Handler objects directly, but instead they1889  * will use Strophe.Connection.addHandler() and1890  * Strophe.Connection.deleteHandler().1891 */1892 1893 /** PrivateConstructor: Strophe.Handler1894  * Create and initialize a new Strophe.Handler.1895  *1896  * Parameters:1897  *  (Function) handler - A function to be executed when the handler is run.1898  *  (String) ns - The namespace to match.1899  *  (String) name - The element name to match.1900  *  (String) type - The element type to match.1901  *  (String) id - The element id attribute to match.1902  *  (String) from - The element from attribute to match.1903  *  (Object) options - Handler options1904  *1905  * Returns:1906  *  A new Strophe.Handler object.1907 */1908 Strophe.Handler = function (handler, ns, name, type, id, from, options)1909 {1910   this.handler = handler;1911   this.ns = ns;1912   this.name = name;1913   this.type = type;1914   this.id = id;1915   this.options = options || {matchBare: false};1916 1917   // default matchBare to false if undefined1918   if (!this.options.matchBare) {1919     this.options.matchBare = false;1920   }1921 1922   if (this.options.matchBare) {1923     this.from = from ? Strophe.getBareJidFromJid(from) : null;1924   } else {1925     this.from = from;1926   }1927 1928   // whether the handler is a user handler or a system handler1929   this.user = true;1930 };1931 1932 Strophe.Handler.prototype = {1933   /** PrivateFunction: isMatch1934    * Tests if a stanza matches the Strophe.Handler.1935    *1936    * Parameters:1937    *  (1938    *1939    * Returns:1940    *  true if the stanza matches and false otherwise.1941   */1942   isMatch: function (elem)1943   {1944     var nsMatch;1945     var from = null;1946 1947     if (this.options.matchBare) {1948       from = Strophe.getBareJidFromJid(elem.getAttribute('from'));1949     } else {1950       from = elem.getAttribute('from');1951     }1952 1953     nsMatch = false;1954     if (!this.ns) {1955       nsMatch = true;1956     } else {1957       var that = this;1958       Strophe.forEachChild(elem, null, function (elem) {1959         if (elem.getAttribute("") == that.ns) {1960           nsMatch = true;1961         }1962       });1963 1964       nsMatch = nsMatch || elem.getAttribute("") == this.ns;1965     }1966 1967     var elem_type = elem.getAttribute("type");1968     if (nsMatch &&1969       (!this.name || Strophe.isTagEqual(elem, this.name)) &&1970       (!this.type || (Array.isArray(this.type) ? this.type.indexOf(elem_type) != -1 : elem_type == this.type)) &&1971       (!this.id || elem.getAttribute("id") == this.id) &&1972       (!this.from || from == this.from)) {1973         return true;1974     }1975 1976     return false;1977   },1978 1979   /** PrivateFunction: run1980    * Run the callback on a matching stanza.1981    *1982    * Parameters:1983    *  (1984    *   Strophe.Handler.1985    *1986    * Returns:1987    *  A boolean indicating if the handler should remain active.1988   */1989   run: function (elem)1990   {1991     var result = null;1992     try {1993       result = this.handler(elem);1994     } catch (e) {1995       if (e.sourceURL) {1996         Strophe.fatal("error: " + this.handler +1997                " " + e.sourceURL + ":" +1998                e.line + " - " + e.name + ": " + e.message);1999       } else if (e.fileName) {2000         if (typeof(console) != "undefined") {2001           console.trace();2002           console.error(this.handler, " - error - ", e, e.message);2003         }2004         Strophe.fatal("error: " + this.handler + " " +2005                e.fileName + ":" + e.lineNumber + " - " +2006                e.name + ": " + e.message);2007       } else {2008         Strophe.fatal("error: " + e.message + "\n" + e.stack);2009       }2010 2011       throw e;2012     }2013 2014     return result;2015   },2016 2017   /** PrivateFunction: toString2018    * Get a String representation of the Strophe.Handler object.2019    *2020    * Returns:2021    *  A String.2022   */2023   toString: function ()2024   {2025     return "{Handler: " + this.handler + "(" + this.name + "," +2026       this.id + "," + this.ns + ")}";2027   }2028 };2029 2030 /** PrivateClass: Strophe.TimedHandler2031  * _Private_ helper class for managing timed handlers.2032  *2033  * A Strophe.TimedHandler encapsulates a user provided callback that2034  * should be called after a certain period of time or at regular2035  * intervals. The return value of the callback determines whether the2036  * Strophe.TimedHandler will continue to fire.2037  *2038  * Users will not use Strophe.TimedHandler objects directly, but instead2039  * they will use Strophe.Connection.addTimedHandler() and2040  * Strophe.Connection.deleteTimedHandler().2041 */2042 2043 /** PrivateConstructor: Strophe.TimedHandler2044  * Create and initialize a new Strophe.TimedHandler object.2045  *2046  * Parameters:2047  *  (Integer) period - The number of milliseconds to wait before the2048  *   handler is called.2049  *  (Function) handler - The callback to run when the handler fires. This2050  *   function should take no arguments.2051  *2052  * Returns:2053  *  A new Strophe.TimedHandler object.2054 */2055 Strophe.TimedHandler = function (period, handler)2056 {2057   this.period = period;2058   this.handler = handler;2059 2060   this.lastCalled = new Date().getTime();2061   this.user = true;2062 };2063 2064 Strophe.TimedHandler.prototype = {2065   /** PrivateFunction: run2066    * Run the callback for the Strophe.TimedHandler.2067    *2068    * Returns:2069    *  true if the Strophe.TimedHandler should be called again, and false2070    *   otherwise.2071   */2072   run: function ()2073   {2074     this.lastCalled = new Date().getTime();2075     return this.handler();2076   },2077 2078   /** PrivateFunction: reset2079    * Reset the last called time for the Strophe.TimedHandler.2080   */2081   reset: function ()2082   {2083     this.lastCalled = new Date().getTime();2084   },2085 2086   /** PrivateFunction: toString2087    * Get a string representation of the Strophe.TimedHandler object.2088    *2089    * Returns:2090    *  The string representation.2091   */2092   toString: function ()2093   {2094     return "{TimedHandler: " + this.handler + "(" + this.period +")}";2095   }2096 };2097 2098 /** Class: Strophe.Connection2099  * XMPP Connection manager.2100  *2101  * This class is the main part of Strophe. It manages a BOSH or websocket2102  * connection to an XMPP server and dispatches events to the user callbacks2103  * as data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, SASL SCRAM-SHA12104  * and legacy authentication.2105  *2106  * After creating a Strophe.Connection object, the user will typically2107  * call connect() with a user supplied callback to handle connection level2108  * events like authentication failure, disconnection, or connection2109  * complete.2110  *2111  * The user will also have several event handlers defined by using2112  * addHandler() and addTimedHandler(). These will allow the user code to2113  * respond to interesting stanzas or do something periodically with the2114  * connection. These handlers will be active once authentication is2115  * finished.2116  *2117  * To send data to the connection, use send().2118 */2119 2120 /** Constructor: Strophe.Connection2121  * Create and initialize a Strophe.Connection object.2122  *2123  * The transport-protocol for this connection will be chosen automatically2124  * based on the given service parameter. URLs starting with "ws://" or2125  * "wss://" will use WebSockets, URLs starting with "http://", "https://"2126  * or without a protocol will use BOSH.2127  *2128  * To make Strophe connect to the current host you can leave out the protocol2129  * and host part and just pass the path, e.g.2130  *2131  * > var conn = new Strophe.Connection("/http-bind/");2132  *2133  * WebSocket options:2134  *2135  * If you want to connect to the current host with a WebSocket connection you2136  * can tell Strophe to use WebSockets through a "protocol" attribute in the2137  * optional options parameter. Valid values are "ws" for WebSocket and "wss"2138  * for Secure WebSocket.2139  * So to connect to "wss://CURRENT_HOSTNAME/xmpp-websocket" you would call2140  *2141  * > var conn = new Strophe.Connection("/xmpp-websocket/", {protocol: "wss"});2142  *2143  * Note that relative URLs _NOT_ starting with a "/" will also include the path2144  * of the current site.2145  *2146  * Also because downgrading security is not permitted by browsers, when using2147  * relative URLs both BOSH and WebSocket connections will use their secure2148  * variants if the current connection to the site is also secure (https).2149  *2150  * BOSH options:2151  *2152  * By adding "sync" to the options, you can control if requests will2153  * be made synchronously or not. The default behaviour is asynchronous.2154  * If you want to make requests synchronous, make "sync" evaluate to true:2155  * > var conn = new Strophe.Connection("/http-bind/", {sync: true});2156  *2157  * You can also toggle this on an already established connection:2158  * > conn.options.sync = true;2159  *2160  * The "customHeaders" option can be used to provide custom HTTP headers to be2161  * included in the 2162  *2163  * The "keepalive" option can be used to instruct Strophe to maintain the2164  * current BOSH session across interruptions such as webpage reloads.2165  *2166  * It will do this by caching the sessions tokens in sessionStorage, and when2167  * "restore" is called it will check whether there are cached tokens with2168  * which it can resume an existing session.2169  *2170  * Parameters:2171  *  (String) service - The BOSH or WebSocket service URL.2172  *  (Object) options - A hash of configuration options2173  *2174  * Returns:2175  *  A new Strophe.Connection object.2176 */2177 Strophe.Connection = function (service, options)2178 {2179   //alert(service);2180   // The service URL2181   this.service = service;2182 2183   // Configuration options2184   this.options = options || {};2185   var proto = this.options.protocol || "";2186 2187   // Select protocal based on service or options2188   if (service.indexOf("ws:") === 0 || service.indexOf("wss:") === 0 ||2189       proto.indexOf("ws") === 0) {2190     this._proto = new Strophe.Websocket(this);2191   } else {2192     this._proto = new Strophe.Bosh(this);2193   }2194 2195   /* The connected JID. */2196   this.jid = "";2197   /* the JIDs domain */2198   this.domain = null;2199   /* stream:features */2200   this.features = null;2201 2202   // SASL2203   this._sasl_data = {};2204   this.do_session = false;2205   this.do_bind = false;2206 2207   // handler lists2208   this.timedHandlers = [];2209   this.handlers = [];2210   this.removeTimeds = [];2211   this.removeHandlers = [];2212   this.addTimeds = [];2213   this.addHandlers = [];2214 2215   this._authentication = {};2216   this._idleTimeout = null;2217   this._disconnectTimeout = null;2218 2219   this.authenticated = false;2220   this.connected = false;2221   this.disconnecting = false;2222   this.do_authentication = true;2223   this.paused = false;2224   this.restored = false;2225 2226   this._data = [];2227   this._uniqueId = 0;2228 2229   this._sasl_success_handler = null;2230   this._sasl_failure_handler = null;2231   this._sasl_challenge_handler = null;2232 2233   // Max retries before disconnecting2234   this.maxRetries = 5;2235 2236   // setup onIdle callback every 1/10th of a second2237   this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);2238 2239   // initialize plugins2240   for (var k in Strophe._connectionPlugins) {2241     if (Strophe._connectionPlugins.hasOwnProperty(k)) {2242       var ptype = Strophe._connectionPlugins[k];2243       // jslint complaints about the below line, but this is fine2244       var F = function () {}; // jshint ignore:line2245       F.prototype = ptype;2246       this[k] = new F();2247       this[k].init(this);2248     }2249   }2250 };2251 2252 Strophe.Connection.prototype = {2253   /** Function: reset2254    * Reset the connection.2255    *2256    * This function should be called after a connection is disconnected2257    * before that connection is reused.2258   */2259   reset: function ()2260   {2261     this._proto._reset();2262 2263     // SASL2264     this.do_session = false;2265     this.do_bind = false;2266 2267     // handler lists2268     this.timedHandlers = [];2269     this.handlers = [];2270     this.removeTimeds = [];2271     this.removeHandlers = [];2272     this.addTimeds = [];2273     this.addHandlers = [];2274     this._authentication = {};2275 2276     this.authenticated = false;2277     this.connected = false;2278     this.disconnecting = false;2279     this.restored = false;2280 2281     this._data = [];2282     this._requests = [];2283     this._uniqueId = 0;2284   },2285 2286   /** Function: pause2287    * Pause the request manager.2288    *2289    * This will prevent Strophe from sending any more requests to the2290    * server. This is very useful for temporarily pausing2291    * BOSH-Connections while a lot of send() calls are happening quickly.2292    * This causes Strophe to send the data in a single request, saving2293    * many request trips.2294   */2295   pause: function ()2296   {2297     this.paused = true;2298   },2299 2300   /** Function: resume2301    * Resume the request manager.2302    *2303    * This resumes after pause() has been called.2304   */2305   resume: function ()2306   {2307     this.paused = false;2308   },2309 2310   /** Function: getUniqueId2311    * Generate a unique ID for use in <iq/> elements.2312    *2313    * All <iq/> stanzas are required to have unique id attributes. This2314    * function makes creating these easy. Each connection instance has2315    * a counter which starts from zero, and the value of this counter2316    * plus a colon followed by the suffix becomes the unique id. If no2317    * suffix is supplied, the counter is used as the unique id.2318    *2319    * Suffixes are used to make debugging easier when reading the stream2320    * data, and their use is recommended. The counter resets to 0 for2321    * every new connection for the same reason. For connections to the2322    * same server that authenticate the same way, all the ids should be2323    * the same, which makes it easy to see changes. This is useful for2324    * automated testing as well.2325    *2326    * Parameters:2327    *  (String) suffix - A optional suffix to append to the id.2328    *2329    * Returns:2330    *  A unique string to be used for the id attribute.2331   */2332   getUniqueId: function (suffix)2333   {2334     if (typeof(suffix) == "string" || typeof(suffix) == "number") {2335       return ++this._uniqueId + ":" + suffix;2336     } else {2337       return ++this._uniqueId + "";2338     }2339   },2340 2341   /** Function: connect2342    * Starts the connection process.2343    *2344    * As the connection process proceeds, the user supplied callback will2345    * be triggered multiple times with status updates. The callback2346    * should take two arguments - the status code and the error condition.2347    *2348    * The status code will be one of the values in the Strophe.Status2349    * constants. The error condition will be one of the conditions2350    * defined in RFC 3920 or the condition 'strophe-parsererror'.2351    *2352    * The Parameters _wait_, _hold_ and _route_ are optional and only relevant2353    * for BOSH connections. Please see XEP 124 for a more detailed explanation2354    * of the optional parameters.2355    *2356    * Parameters:2357    *  (String) jid - The user's JID. This may be a bare JID,2358    *   or a full JID. If a node is not supplied, SASL ANONYMOUS2359    *   authentication will be attempted.2360    *  (String) pass - The user's password.2361    *  (Function) callback - The connect callback function.2362    *  (Integer) wait - The optional HTTPBIND wait value. This is the2363    *   time the server will wait before returning an empty result for2364    *   a request. The default setting of 60 seconds is recommended.2365    *  (Integer) hold - The optional HTTPBIND hold value. This is the2366    *   number of connections the server will hold at one time. This2367    *   should almost always be set to 1 (the default).2368    *  (String) route - The optional route value.2369    *  (String) authcid - The optional alternative authentication identity2370    *   (username) if intending to impersonate another user.2371   */2372   connect: function (jid, pass, callback, wait, hold, route, authcid)2373   {2374     this.jid = jid;2375     /** Variable: authzid2376      * Authorization identity.2377     */2378     this.authzid = Strophe.getBareJidFromJid(this.jid);2379     /** Variable: authcid2380      * Authentication identity (User name).2381     */2382     this.authcid = authcid || Strophe.getNodeFromJid(this.jid);2383     /** Variable: pass2384      * Authentication identity (User password).2385     */2386     this.pass = pass;2387     /** Variable: servtype2388      * Digest MD5 compatibility.2389     */2390     this.servtype = "xmpp";2391     this.connect_callback = callback;2392     this.disconnecting = false;2393     this.connected = false;2394     this.authenticated = false;2395     this.restored = false;2396 2397     // parse jid for domain2398     this.domain = Strophe.getDomainFromJid(this.jid);2399 2400     this._changeConnectStatus(Strophe.Status.CONNECTING, null);2401 2402     this._proto._connect(wait, hold, route);2403   },2404 2405   /** Function: attach2406    * Attach to an already created and authenticated BOSH session.2407    *2408    * This function is provided to allow Strophe to attach to BOSH2409    * sessions which have been created externally, perhaps by a Web2410    * application. This is often used to support auto-login type features2411    * without putting user credentials into the page.2412    *2413    * Parameters:2414    *  (String) jid - The full JID that is bound by the session.2415    *  (String) sid - The SID of the BOSH session.2416    *  (String) rid - The current RID of the BOSH session. This RID2417    *   will be used by the next request.2418    *  (Function) callback The connect callback function.2419    *  (Integer) wait - The optional HTTPBIND wait value. This is the2420    *   time the server will wait before returning an empty result for2421    *   a request. The default setting of 60 seconds is recommended.2422    *   Other settings will require tweaks to the Strophe.TIMEOUT value.2423    *  (Integer) hold - The optional HTTPBIND hold value. This is the2424    *   number of connections the server will hold at one time. This2425    *   should almost always be set to 1 (the default).2426    *  (Integer) wind - The optional HTTBIND window value. This is the2427    *   allowed range of request ids that are valid. The default is 5.2428   */2429   attach: function (jid, sid, rid, callback, wait, hold, wind)2430   {2431     if (this._proto instanceof Strophe.Bosh) {2432       this._proto._attach(jid, sid, rid, callback, wait, hold, wind);2433     } else {2434       throw {2435         name: 'StropheSessionError',2436         message: 'The "attach" method can only be used with a BOSH connection.'2437       };2438     }2439   },2440 2441   /** Function: restore2442    * Attempt to restore a cached BOSH session.2443    *2444    * This function is only useful in conjunction with providing the2445    * "keepalive":true option when instantiating a new Strophe.Connection.2446    *2447    * When "keepalive" is set to true, Strophe will cache the BOSH tokens2448    * RID (Request ID) and SID (Session ID) and then when this function is2449    * called, it will attempt to restore the session from those cached2450    * tokens.2451    *2452    * This function must therefore be called instead of connect or attach.2453    *2454    * For an example on how to use it, please see examples/restore.js2455    *2456    * Parameters:2457    *  (String) jid - The user's JID. This may be a bare JID or a full JID.2458    *  (Function) callback - The connect callback function.2459    *  (Integer) wait - The optional HTTPBIND wait value. This is the2460    *   time the server will wait before returning an empty result for2461    *   a request. The default setting of 60 seconds is recommended.2462    *  (Integer) hold - The optional HTTPBIND hold value. This is the2463    *   number of connections the server will hold at one time. This2464    *   should almost always be set to 1 (the default).2465    *  (Integer) wind - The optional HTTBIND window value. This is the2466    *   allowed range of request ids that are valid. The default is 5.2467   */2468   restore: function (jid, callback, wait, hold, wind)2469   {2470     if (this._sessionCachingSupported()) {2471       this._proto._restore(jid, callback, wait, hold, wind);2472     } else {2473       throw {2474         name: 'StropheSessionError',2475         message: 'The "restore" method can only be used with a BOSH connection.'2476       };2477     }2478   },2479 2480   /** PrivateFunction: _sessionCachingSupported2481    * Checks whether sessionStorage and JSON are supported and whether we're2482    * using BOSH.2483   */2484   _sessionCachingSupported: function ()2485   {2486     if (this._proto instanceof Strophe.Bosh) {2487       if (!JSON) { return false; }2488       try {2489         window.sessionStorage.setItem('_strophe_', '_strophe_');2490         window.sessionStorage.removeItem('_strophe_');2491       } catch (e) {2492         return false;2493       }2494       return true;2495     }2496     return false;2497   },2498 2499   /** Function: 2500    * User overrideable function that receives 2501    * connection.2502    *2503    * The default function does nothing. User code can override this with2504    * > Strophe.Connection.2505    * >  (user code)2506    * > };2507    *2508    * Due to limitations of current Browsers' 2509    * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.2510    *2511    * BOSH-Connections will have all stanzas wrapped in a <body> tag. See2512    * <Strophe.Bosh.strip> if you want to strip this tag.2513    *2514    * Parameters:2515    *  (2516   */2517   /* jshint unused:false */2518   2519   {2520     return;2521   },2522   /* jshint unused:true */2523 2524   /** Function: 2525    * User overrideable function that receives 2526    * connection.2527    *2528    * The default function does nothing. User code can override this with2529    * > Strophe.Connection.2530    * >  (user code)2531    * > };2532    *2533    * Due to limitations of current Browsers' 2534    * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.2535    *2536    * BOSH-Connections will have all stanzas wrapped in a <body> tag. See2537    * <Strophe.Bosh.strip> if you want to strip this tag.2538    *2539    * Parameters:2540    *  (2541   */2542   /* jshint unused:false */2543   2544   {2545     return;2546   },2547   /* jshint unused:true */2548 2549   /** Function: rawInput2550    * User overrideable function that receives raw data coming into the2551    * connection.2552    *2553    * The default function does nothing. User code can override this with2554    * > Strophe.Connection.rawInput = function (data) {2555    * >  (user code)2556    * > };2557    *2558    * Parameters:2559    *  (String) data - The data received by the connection.2560   */2561   /* jshint unused:false */2562   rawInput: function (data)2563   {2564     return;2565   },2566   /* jshint unused:true */2567 2568   /** Function: rawOutput2569    * User overrideable function that receives raw data sent to the2570    * connection.2571    *2572    * The default function does nothing. User code can override this with2573    * > Strophe.Connection.rawOutput = function (data) {2574    * >  (user code)2575    * > };2576    *2577    * Parameters:2578    *  (String) data - The data sent by the connection.2579   */2580   /* jshint unused:false */2581   rawOutput: function (data)2582   {2583     return;2584   },2585   /* jshint unused:true */2586 2587   /** Function: send2588    * Send a stanza.2589    *2590    * This function is called to push data onto the send queue to2591    * go out over the wire. Whenever a request is sent to the BOSH2592    * server, all pending data is sent and the queue is flushed.2593    *2594    * Parameters:2595    *  (2596    *   [2597    *   Strophe.Builder) elem - The stanza to send.2598   */2599   send: function (elem)2600   {2601     if (elem === null) { return ; }2602     if (typeof(elem.sort) === "function") {2603       for (var i = 0; i < elem.length; i++) {2604         this._queueData(elem[i]);2605       }2606     } else if (typeof(elem.tree) === "function") {2607       this._queueData(elem.tree());2608     } else {2609       this._queueData(elem);2610     }2611 2612     this._proto._send();2613   },2614 2615   /** Function: flush2616    * Immediately send any pending outgoing data.2617    *2618    * Normally send() queues outgoing data until the next idle period2619    * (100ms), which optimizes network use in the common cases when2620    * several send()s are called in succession. flush() can be used to2621    * immediately send all pending data.2622   */2623   flush: function ()2624   {2625     // cancel the pending idle period and run the idle function2626     // immediately2627     clearTimeout(this._idleTimeout);2628     this._onIdle();2629   },2630 2631   /** Function: sendIQ2632    * Helper function to send IQ stanzas.2633    *2634    * Parameters:2635    *  (2636    *  (Function) callback - The callback function for a successful request.2637    *  (Function) errback - The callback function for a failed or timed2638    *   out request. On timeout, the stanza will be null.2639    *  (Integer) timeout - The time specified in milliseconds for a2640    *   timeout to occur.2641    *2642    * Returns:2643    *  The id used to send the IQ.2644   */2645   sendIQ: function(elem, callback, errback, timeout) {2646     var timeoutHandler = null;2647     var that = this;2648 2649     if (typeof(elem.tree) === "function") {2650       elem = elem.tree();2651     }2652     var id = elem.getAttribute('id');2653 2654     // inject id if not found2655     if (!id) {2656       id = this.getUniqueId("sendIQ");2657       elem.setAttribute("id", id);2658     }2659 2660     var expectedFrom = elem.getAttribute("to");2661     var fulljid = this.jid;2662 2663     var handler = this.addHandler(function (stanza) {2664       // remove timeout handler if there is one2665       if (timeoutHandler) {2666         that.deleteTimedHandler(timeoutHandler);2667       }2668 2669       var acceptable = false;2670       var from = stanza.getAttribute("from");2671       if (from === expectedFrom ||2672        (expectedFrom === null &&2673          (from === Strophe.getBareJidFromJid(fulljid) ||2674           from === Strophe.getDomainFromJid(fulljid) ||2675           from === fulljid))) {2676         acceptable = true;2677       }2678 2679       if (!acceptable) {2680         throw {2681           name: "StropheError",2682           message: "Got answer to IQ from wrong jid:" + from +2683               "\nExpected jid: " + expectedFrom2684         };2685       }2686 2687       var iqtype = stanza.getAttribute('type');2688       if (iqtype == 'result') {2689         if (callback) {2690           callback(stanza);2691         }2692       } else if (iqtype == 'error') {2693         if (errback) {2694           errback(stanza);2695         }2696       } else {2697         throw {2698           name: "StropheError",2699           message: "Got bad IQ type of " + iqtype2700         };2701       }2702     }, null, 'iq', ['error', 'result'], id);2703 2704     // if timeout specified, setup timeout handler.2705     if (timeout) {2706       timeoutHandler = this.addTimedHandler(timeout, function () {2707         // get rid of normal handler2708         that.deleteHandler(handler);2709         // call errback on timeout with null stanza2710         if (errback) {2711           errback(null);2712         }2713         return false;2714       });2715     }2716     this.send(elem);2717     return id;2718   },2719 2720   /** PrivateFunction: _queueData2721    * Queue outgoing data for later sending. Also ensures that the data2722    * is a DOMElement.2723   */2724   _queueData: function (element) {2725     if (element === null ||2726       !element.tagName ||2727       !element.childNodes) {2728       throw {2729         name: "StropheError",2730         message: "Cannot queue non-DOMElement."2731       };2732     }2733 2734     this._data.push(element);2735   },2736 2737   /** PrivateFunction: _sendRestart2738    * Send an xmpp:restart stanza.2739   */2740   _sendRestart: function ()2741   {2742     this._data.push("restart");2743 2744     this._proto._sendRestart();2745 2746     this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);2747   },2748 2749   /** Function: addTimedHandler2750    * Add a timed handler to the connection.2751    *2752    * This function adds a timed handler. The provided handler will2753    * be called every period milliseconds until it returns false,2754    * the connection is terminated, or the handler is removed. Handlers2755    * that wish to continue being invoked should return true.2756    *2757    * Because of method binding it is necessary to save the result of2758    * this function if you wish to remove a handler with2759    * deleteTimedHandler().2760    *2761    * Note that user handlers are not active until authentication is2762    * successful.2763    *2764    * Parameters:2765    *  (Integer) period - The period of the handler.2766    *  (Function) handler - The callback function.2767    *2768    * Returns:2769    *  A reference to the handler that can be used to remove it.2770   */2771   addTimedHandler: function (period, handler)2772   {2773     var thand = new Strophe.TimedHandler(period, handler);2774     this.addTimeds.push(thand);2775     return thand;2776   },2777 2778   /** Function: deleteTimedHandler2779    * Delete a timed handler for a connection.2780    *2781    * This function removes a timed handler from the connection. The2782    * handRef parameter is *not* the function passed to addTimedHandler(),2783    * but is the reference returned from addTimedHandler().2784    *2785    * Parameters:2786    *  (Strophe.TimedHandler) handRef - The handler reference.2787   */2788   deleteTimedHandler: function (handRef)2789   {2790     // this must be done in the Idle loop so that we don't change2791     // the handlers during iteration2792     this.removeTimeds.push(handRef);2793   },2794 2795   /** Function: addHandler2796    * Add a stanza handler for the connection.2797    *2798    * This function adds a stanza handler to the connection. The2799    * handler callback will be called for any stanza that matches2800    * the parameters. Note that if multiple parameters are supplied,2801    * they must all match for the handler to be invoked.2802    *2803    * The handler will receive the stanza that triggered it as its argument.2804    * *The handler should return true if it is to be invoked again;2805    * returning false will remove the handler after it returns.*2806    *2807    * As a convenience, the ns parameters applies to the top level element2808    * and also any of its immediate children. This is primarily to make2809    * matching /iq/query elements easy.2810    *2811    * The options argument contains handler matching flags that affect how2812    * matches are determined. Currently the only flag is matchBare (a2813    * boolean). When matchBare is true, the from parameter and the from2814    * attribute on the stanza will be matched as bare JIDs instead of2815    * full JIDs. To use this, pass {matchBare: true} as the value of2816    * options. The default value for matchBare is false.2817    *2818    * The return value should be saved if you wish to remove the handler2819    * with deleteHandler().2820    *2821    * Parameters:2822    *  (Function) handler - The user callback.2823    *  (String) ns - The namespace to match.2824    *  (String) name - The stanza name to match.2825    *  (String) type - The stanza type attribute to match.2826    *  (String) id - The stanza id attribute to match.2827    *  (String) from - The stanza from attribute to match.2828    *  (String) options - The handler options2829    *2830    * Returns:2831    *  A reference to the handler that can be used to remove it.2832   */2833   addHandler: function (handler, ns, name, type, id, from, options)2834   {2835     var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);2836     this.addHandlers.push(hand);2837     return hand;2838   },2839 2840   /** Function: deleteHandler2841    * Delete a stanza handler for a connection.2842    *2843    * This function removes a stanza handler from the connection. The2844    * handRef parameter is *not* the function passed to addHandler(),2845    * but is the reference returned from addHandler().2846    *2847    * Parameters:2848    *  (Strophe.Handler) handRef - The handler reference.2849   */2850   deleteHandler: function (handRef)2851   {2852     // this must be done in the Idle loop so that we don't change2853     // the handlers during iteration2854     this.removeHandlers.push(handRef);2855     // If a handler is being deleted while it is being added,2856     // prevent it from getting added2857     var i = this.addHandlers.indexOf(handRef);2858     if (i >= 0) {2859       this.addHandlers.splice(i, 1);2860     }2861   },2862 2863   /** Function: disconnect2864    * Start the graceful disconnection process.2865    *2866    * This function starts the disconnection process. This process starts2867    * by sending unavailable presence and sending BOSH body of type2868    * terminate. A timeout handler makes sure that disconnection happens2869    * even if the BOSH server does not respond.2870    * If the Connection object isn't connected, at least tries to abort all pending requests2871    * so the connection object won't generate successful requests (which were already opened).2872    *2873    * The user supplied connection callback will be notified of the2874    * progress as this process happens.2875    *2876    * Parameters:2877    *  (String) reason - The reason the disconnect is occuring.2878   */2879   disconnect: function (reason)2880   {2881     this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);2882 2883     Strophe.info("Disconnect was called because: " + reason);2884     if (this.connected) {2885       var pres = false;2886       this.disconnecting = true;2887       if (this.authenticated) {2888         pres = $pres({2889           2890           type: 'unavailable'2891         });2892       }2893       // setup timeout handler2894       this._disconnectTimeout = this._addSysTimedHandler(2895         3000, this._onDisconnectTimeout.bind(this));2896       this._proto._disconnect(pres);2897     } else {2898       Strophe.info("Disconnect was called before Strophe connected to the server");2899       this._proto._abortAllRequests();2900     }2901   },2902 2903   /** PrivateFunction: _changeConnectStatus2904    * _Private_ helper function that makes sure plugins and the user's2905    * callback are notified of connection status changes.2906    *2907    * Parameters:2908    *  (Integer) status - the new connection status, one of the values2909    *   in Strophe.Status2910    *  (String) condition - the error condition or null2911   */2912   _changeConnectStatus: function (status, condition)2913   {2914     // notify all plugins listening for status changes2915     for (var k in Strophe._connectionPlugins) {2916       if (Strophe._connectionPlugins.hasOwnProperty(k)) {2917         var plugin = this[k];2918         if (plugin.statusChanged) {2919           try {2920             plugin.statusChanged(status, condition);2921           } catch (err) {2922             Strophe.error("" + k + " plugin caused an exception " +2923                    "changing status: " + err);2924           }2925         }2926       }2927     }2928 2929     // notify the user's callback2930     if (this.connect_callback) {2931       try {2932         this.connect_callback(status, condition);2933       } catch (e) {2934         Strophe.error("User connection callback caused an " +2935                "exception: " + e);2936       }2937     }2938   },2939 2940   /** PrivateFunction: _doDisconnect2941    * _Private_ function to disconnect.2942    *2943    * This is the last piece of the disconnection logic. This resets the2944    * connection and alerts the user's connection callback.2945   */2946   _doDisconnect: function (condition)2947   {2948     if (typeof this._idleTimeout == "number") {2949       clearTimeout(this._idleTimeout);2950     }2951 2952     // Cancel Disconnect Timeout2953     if (this._disconnectTimeout !== null) {2954       this.deleteTimedHandler(this._disconnectTimeout);2955       this._disconnectTimeout = null;2956     }2957 2958     Strophe.info("_doDisconnect was called");2959     this._proto._doDisconnect();2960 2961     this.authenticated = false;2962     this.disconnecting = false;2963     this.restored = false;2964 2965     // delete handlers2966     this.handlers = [];2967     this.timedHandlers = [];2968     this.removeTimeds = [];2969     this.removeHandlers = [];2970     this.addTimeds = [];2971     this.addHandlers = [];2972 2973     // tell the parent we disconnected2974     this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);2975     this.connected = false;2976   },2977 2978   /** PrivateFunction: _dataRecv2979    * _Private_ handler to processes incoming data from the the connection.2980    *2981    * Except for _connect_cb handling the initial connection request,2982    * this function handles the incoming data for all requests. This2983    * function also fires stanza handlers that match each incoming2984    * stanza.2985    *2986    * Parameters:2987    *  (Strophe.Request) req - The request that has data ready.2988    *  (string) req - The stanza a raw string (optiona).2989   */2990   _dataRecv: function (req, raw)2991   {2992     Strophe.info("_dataRecv called");2993     var elem = this._proto._reqToData(req);2994     if (elem === null) { return; }2995 2996     if (this. Strophe.Connection.prototype.2997       if (elem.nodeName === this._proto.strip && elem.childNodes.length) {2998         this.0]);2999       } else {3000         this.3001       }3002     }3003     if (this.rawInput !== Strophe.Connection.prototype.rawInput) {3004       if (raw) {3005         this.rawInput(raw);3006       } else {3007         this.rawInput(Strophe.serialize(elem));3008       }3009     }3010 3011     // remove handlers scheduled for deletion3012     var i, hand;3013     while (this.removeHandlers.length > 0) {3014       hand = this.removeHandlers.pop();3015       i = this.handlers.indexOf(hand);3016       if (i >= 0) {3017         this.handlers.splice(i, 1);3018       }3019     }3020 3021     // add handlers scheduled for addition3022     while (this.addHandlers.length > 0) {3023       this.handlers.push(this.addHandlers.pop());3024     }3025 3026     // handle graceful disconnect3027     if (this.disconnecting && this._proto._emptyQueue()) {3028       this._doDisconnect();3029       return;3030     }3031 3032     var type = elem.getAttribute("type");3033     var cond, conflict;3034     if (type !== null && type == "terminate") {3035       // Don't process stanzas that come in after disconnect3036       if (this.disconnecting) {3037         return;3038       }3039 3040       // an error occurred3041       cond = elem.getAttribute("condition");3042       conflict = elem.getElementsByTagName("conflict");3043       if (cond !== null) {3044         if (cond == "remote-stream-error" && conflict.length > 0) {3045           cond = "conflict";3046         }3047         this._changeConnectStatus(Strophe.Status.CONNFAIL, cond);3048       } else {3049         this._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");3050       }3051       this._doDisconnect(cond);3052       return;3053     }3054 3055     // send each incoming stanza through the handler chain3056     var that = this;3057     Strophe.forEachChild(elem, null, function (child) {3058       var i, newList;3059       // process handlers3060       newList = that.handlers;3061       that.handlers = [];3062       for (i = 0; i < newList.length; i++) {3063         var hand = newList[i];3064         // encapsulate 'handler.run' not to lose the whole handler list if3065         // one of the handlers throws an exception3066         try {3067           if (hand.isMatch(child) &&3068             (that.authenticated || !hand.user)) {3069             if (hand.run(child)) {3070               that.handlers.push(hand);3071             }3072           } else {3073             that.handlers.push(hand);3074           }3075         } catch(e) {3076           // if the handler throws an exception, we consider it as false3077           Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message);3078         }3079       }3080     });3081   },3082 3083 3084   /** Attribute: mechanisms3085    * SASL Mechanisms available for Conncection.3086   */3087   mechanisms: {},3088 3089   /** PrivateFunction: _connect_cb3090    * _Private_ handler for initial connection request.3091    *3092    * This handler is used to process the initial connection request3093    * response from the BOSH server. It is used to set up authentication3094    * handlers and start the authentication process.3095    *3096    * SASL authentication will be attempted if available, otherwise3097    * the code will fall back to legacy authentication.3098    *3099    * Parameters:3100    *  (Strophe.Request) req - The current request.3101    *  (Function) _callback - low level (xmpp) connect callback function.3102    *   Useful for plugins with their own xmpp connect callback (when their)3103    *   want to do something special).3104   */3105   _connect_cb: function (req, _callback, raw)3106   {3107     Strophe.info("_connect_cb was called");3108 3109     this.connected = true;3110 3111     var bodyWrap = this._proto._reqToData(req);3112     if (!bodyWrap) { return; }3113 3114     if (this. Strophe.Connection.prototype.3115       if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {3116         this.0]);3117       } else {3118         this.3119       }3120     }3121     if (this.rawInput !== Strophe.Connection.prototype.rawInput) {3122       if (raw) {3123         this.rawInput(raw);3124       } else {3125         this.rawInput(Strophe.serialize(bodyWrap));3126       }3127     }3128 3129     var conncheck = this._proto._connect_cb(bodyWrap);3130     if (conncheck === Strophe.Status.CONNFAIL) {3131       return;3132     }3133 3134     this._authentication.sasl_scram_sha1 = false;3135     this._authentication.sasl_plain = false;3136     this._authentication.sasl_digest_md5 = false;3137     this._authentication.sasl_anonymous = false;3138 3139     this._authentication.legacy_auth = false;3140 3141     // Check for the stream:features tag3142     var hasFeatures;3143     if (bodyWrap.getElementsByTagNameNS) {3144       hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "features").length > 0;3145     } else {3146       hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 || bodyWrap.getElementsByTagName("features").length > 0;3147     }3148     var mechanisms = bodyWrap.getElementsByTagName("mechanism");3149     var matched = [];3150     var i, mech, found_authentication = false;3151     if (!hasFeatures) {3152       this._proto._no_auth_received(_callback);3153       return;3154     }3155     if (mechanisms.length > 0) {3156       for (i = 0; i < mechanisms.length; i++) {3157         mech = Strophe.getText(mechanisms[i]);3158         if (this.mechanisms[mech]) matched.push(this.mechanisms[mech]);3159       }3160     }3161     this._authentication.legacy_auth =3162       bodyWrap.getElementsByTagName("auth").length > 0;3163     found_authentication = this._authentication.legacy_auth ||3164       matched.length > 0;3165     if (!found_authentication) {3166       this._proto._no_auth_received(_callback);3167       return;3168     }3169     if (this.do_authentication !== false)3170       this.authenticate(matched);3171   },3172 3173   /** Function: authenticate3174    * Set up authentication3175    *3176    * Contiunues the initial connection request by setting up authentication3177    * handlers and start the authentication process.3178    *3179    * SASL authentication will be attempted if available, otherwise3180    * the code will fall back to legacy authentication.3181    *3182   */3183   authenticate: function (matched)3184   {3185    var i;3186    // Sorting matched mechanisms according to priority.3187    for (i = 0; i < matched.length - 1; ++i) {3188     var higher = i;3189     for (var j = i + 1; j < matched.length; ++j) {3190      if (matched[j].prototype.priority > matched[higher].prototype.priority) {3191       higher = j;3192      }3193     }3194     if (higher != i) {3195      var swap = matched[i];3196      matched[i] = matched[higher];3197      matched[higher] = swap;3198     }3199    }3200 3201    // run each mechanism3202    var mechanism_found = false;3203    for (i = 0; i < matched.length; ++i) {3204     if (!matched[i].test(this)) continue;3205 3206     this._sasl_success_handler = this._addSysHandler(3207      this._sasl_success_cb.bind(this), null,3208      "success", null, null);3209     this._sasl_failure_handler = this._addSysHandler(3210      this._sasl_failure_cb.bind(this), null,3211      "failure", null, null);3212     this._sasl_challenge_handler = this._addSysHandler(3213      this._sasl_challenge_cb.bind(this), null,3214      "challenge", null, null);3215 3216     this._sasl_mechanism = new matched[i]();3217     this._sasl_mechanism.onStart(this);3218 3219     var request_auth_exchange = $build("auth", {3220      3221      mechanism: this._sasl_mechanism.name3222     });3223 3224     if (this._sasl_mechanism.isClientFirst) {3225      var response = this._sasl_mechanism.onChallenge(this, null);3226      request_auth_exchange.t(Base64.encode(response));3227     }3228 3229     this.send(request_auth_exchange.tree());3230 3231     mechanism_found = true;3232     break;3233    }3234 3235    if (!mechanism_found) {3236     // if none of the mechanism worked3237     if (Strophe.getNodeFromJid(this.jid) === null) {3238       // we don't have a node, which is required for non-anonymous3239       // client connections3240       this._changeConnectStatus(Strophe.Status.CONNFAIL,3241                    'x-strophe-bad-non-anon-jid');3242       this.disconnect('x-strophe-bad-non-anon-jid');3243     } else {3244      // fall back to legacy authentication3245      this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);3246      this._addSysHandler(this._auth1_cb.bind(this), null, null,3247                null, "_auth_1");3248 3249      this.send($iq({3250       type: "get",3251       to: this.domain,3252       id: "_auth_1"3253      }).c("query", {3254       3255      }).c("username", {}).t(Strophe.getNodeFromJid(this.jid)).tree());3256     }3257    }3258 3259   },3260 3261   _sasl_challenge_cb: function(elem) {3262    var challenge = Base64.decode(Strophe.getText(elem));3263    var response = this._sasl_mechanism.onChallenge(this, challenge);3264 3265    var stanza = $build('response', {3266      3267    });3268    if (response !== "") {3269     stanza.t(Base64.encode(response));3270    }3271    this.send(stanza.tree());3272 3273    return true;3274   },3275 3276   /** PrivateFunction: _auth1_cb3277    * _Private_ handler for legacy authentication.3278    *3279    * This handler is called in response to the initial <iq type='get'/>3280    * for legacy authentication. It builds an authentication <iq/> and3281    * sends it, creating a handler (calling back to _auth2_cb()) to3282    * handle the result3283    *3284    * Parameters:3285    *  (3286    *3287    * Returns:3288    *  false to remove the handler.3289   */3290   /* jshint unused:false */3291   _auth1_cb: function (elem)3292   {3293     // build plaintext auth iq3294     var iq = $iq({type: "set", id: "_auth_2"})3295       .c('query', {3296       .c('username', {}).t(Strophe.getNodeFromJid(this.jid))3297       .up()3298       .c('password').t(this.pass);3299 3300     if (!Strophe.getResourceFromJid(this.jid)) {3301       // since the user has not supplied a resource, we pick3302       // a default one here. unlike other auth methods, the server3303       // cannot do this for us.3304       this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';3305     }3306     iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));3307 3308     this._addSysHandler(this._auth2_cb.bind(this), null,3309               null, null, "_auth_2");3310 3311     this.send(iq.tree());3312 3313     return false;3314   },3315   /* jshint unused:true */3316 3317   /** PrivateFunction: _sasl_success_cb3318    * _Private_ handler for succesful SASL authentication.3319    *3320    * Parameters:3321    *  (3322    *3323    * Returns:3324    *  false to remove the handler.3325   */3326   _sasl_success_cb: function (elem)3327   {3328     if (this._sasl_data["server-signature"]) {3329       var serverSignature;3330       var success = Base64.decode(Strophe.getText(elem));3331       var attribMatch = /([a-z]+)=([^,]+)(,|$)/;3332       var matches = success.match(attribMatch);3333       if (matches[1] == "v") {3334         serverSignature = matches[2];3335       }3336 3337       if (serverSignature != this._sasl_data["server-signature"]) {3338        // remove old handlers3339        this.deleteHandler(this._sasl_failure_handler);3340        this._sasl_failure_handler = null;3341        if (this._sasl_challenge_handler) {3342         this.deleteHandler(this._sasl_challenge_handler);3343         this._sasl_challenge_handler = null;3344        }3345 3346        this._sasl_data = {};3347        return this._sasl_failure_cb(null);3348       }3349     }3350 3351     Strophe.info("SASL authentication succeeded.");3352 3353     if(this._sasl_mechanism)3354      this._sasl_mechanism.onSuccess();3355 3356     // remove old handlers3357     this.deleteHandler(this._sasl_failure_handler);3358     this._sasl_failure_handler = null;3359     if (this._sasl_challenge_handler) {3360       this.deleteHandler(this._sasl_challenge_handler);3361       this._sasl_challenge_handler = null;3362     }3363 3364     var streamfeature_handlers = [];3365     var wrapper = function(handlers, elem) {3366       while (handlers.length) {3367         this.deleteHandler(handlers.pop());3368       }3369       this._sasl_auth1_cb.bind(this)(elem);3370       return false;3371     };3372     streamfeature_handlers.push(this._addSysHandler(function(elem) {3373       wrapper.bind(this)(streamfeature_handlers, elem);3374     }.bind(this), null, "stream:features", null, null));3375     streamfeature_handlers.push(this._addSysHandler(function(elem) {3376       wrapper.bind(this)(streamfeature_handlers, elem);3377     }.bind(this), Strophe.NS.STREAM, "features", null, null));3378 3379     // we must send an xmpp:restart now3380     this._sendRestart();3381 3382     return false;3383   },3384 3385   /** PrivateFunction: _sasl_auth1_cb3386    * _Private_ handler to start stream binding.3387    *3388    * Parameters:3389    *  (3390    *3391    * Returns:3392    *  false to remove the handler.3393   */3394   _sasl_auth1_cb: function (elem)3395   {3396     // save stream:features for future usage3397     this.features = elem;3398 3399     var i, child;3400 3401     for (i = 0; i < elem.childNodes.length; i++) {3402       child = elem.childNodes[i];3403       if (child.nodeName == 'bind') {3404         this.do_bind = true;3405       }3406 3407       if (child.nodeName == 'session') {3408         this.do_session = true;3409       }3410     }3411 3412     if (!this.do_bind) {3413       this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);3414       return false;3415     } else {3416       this._addSysHandler(this._sasl_bind_cb.bind(this), null, null,3417                 null, "_bind_auth_2");3418 3419       var resource = Strophe.getResourceFromJid(this.jid);3420       if (resource) {3421         this.send($iq({type: "set", id: "_bind_auth_2"})3422              .c('bind', {3423              .c('resource', {}).t(resource).tree());3424       } else {3425         this.send($iq({type: "set", id: "_bind_auth_2"})3426              .c('bind', {3427              .tree());3428       }3429     }3430 3431     return false;3432   },3433 3434   /** PrivateFunction: _sasl_bind_cb3435    * _Private_ handler for binding result and session start.3436    *3437    * Parameters:3438    *  (3439    *3440    * Returns:3441    *  false to remove the handler.3442   */3443   _sasl_bind_cb: function (elem)3444   {3445     if (elem.getAttribute("type") == "error") {3446       Strophe.info("SASL binding failed.");3447       var conflict = elem.getElementsByTagName("conflict"), condition;3448       if (conflict.length > 0) {3449         condition = 'conflict';3450       }3451       this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition);3452       return false;3453     }3454 3455     // TODO - need to grab errors3456     var bind = elem.getElementsByTagName("bind");3457     var jidNode;3458     if (bind.length > 0) {3459       // Grab jid3460       jidNode = bind[0].getElementsByTagName("jid");3461       if (jidNode.length > 0) {3462         this.jid = Strophe.getText(jidNode[0]);3463 3464         if (this.do_session) {3465           this._addSysHandler(this._sasl_session_cb.bind(this),3466                     null, null, null, "_session_auth_2");3467 3468           this.send($iq({type: "set", id: "_session_auth_2"})3469                  .c('session', {3470                  .tree());3471         } else {3472           this.authenticated = true;3473           this._changeConnectStatus(Strophe.Status.CONNECTED, null);3474         }3475       }3476     } else {3477       Strophe.info("SASL binding failed.");3478       this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);3479       return false;3480     }3481   },3482 3483   /** PrivateFunction: _sasl_session_cb3484    * _Private_ handler to finish successful SASL connection.3485    *3486    * This sets Connection.authenticated to true on success, which3487    * starts the processing of user handlers.3488    *3489    * Parameters:3490    *  (3491    *3492    * Returns:3493    *  false to remove the handler.3494   */3495   _sasl_session_cb: function (elem)3496   {3497     if (elem.getAttribute("type") == "result") {3498       this.authenticated = true;3499       this._changeConnectStatus(Strophe.Status.CONNECTED, null);3500     } else if (elem.getAttribute("type") == "error") {3501       Strophe.info("Session creation failed.");3502       this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);3503       return false;3504     }3505 3506     return false;3507   },3508 3509   /** PrivateFunction: _sasl_failure_cb3510    * _Private_ handler for SASL authentication failure.3511    *3512    * Parameters:3513    *  (3514    *3515    * Returns:3516    *  false to remove the handler.3517   */3518   /* jshint unused:false */3519   _sasl_failure_cb: function (elem)3520   {3521     // delete unneeded handlers3522     if (this._sasl_success_handler) {3523       this.deleteHandler(this._sasl_success_handler);3524       this._sasl_success_handler = null;3525     }3526     if (this._sasl_challenge_handler) {3527       this.deleteHandler(this._sasl_challenge_handler);3528       this._sasl_challenge_handler = null;3529     }3530 3531     if(this._sasl_mechanism)3532      this._sasl_mechanism.onFailure();3533     this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);3534     return false;3535   },3536   /* jshint unused:true */3537 3538   /** PrivateFunction: _auth2_cb3539    * _Private_ handler to finish legacy authentication.3540    *3541    * This handler is called when the result from the jabber:iq:auth3542    * <iq/> stanza is returned.3543    *3544    * Parameters:3545    *  (3546    *3547    * Returns:3548    *  false to remove the handler.3549   */3550   _auth2_cb: function (elem)3551   {3552     if (elem.getAttribute("type") == "result") {3553       this.authenticated = true;3554       this._changeConnectStatus(Strophe.Status.CONNECTED, null);3555     } else if (elem.getAttribute("type") == "error") {3556       this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);3557       this.disconnect('authentication failed');3558     }3559 3560     return false;3561   },3562 3563   /** PrivateFunction: _addSysTimedHandler3564    * _Private_ function to add a system level timed handler.3565    *3566    * This function is used to add a Strophe.TimedHandler for the3567    * library code. System timed handlers are allowed to run before3568    * authentication is complete.3569    *3570    * Parameters:3571    *  (Integer) period - The period of the handler.3572    *  (Function) handler - The callback function.3573   */3574   _addSysTimedHandler: function (period, handler)3575   {3576     var thand = new Strophe.TimedHandler(period, handler);3577     thand.user = false;3578     this.addTimeds.push(thand);3579     return thand;3580   },3581 3582   /** PrivateFunction: _addSysHandler3583    * _Private_ function to add a system level stanza handler.3584    *3585    * This function is used to add a Strophe.Handler for the3586    * library code. System stanza handlers are allowed to run before3587    * authentication is complete.3588    *3589    * Parameters:3590    *  (Function) handler - The callback function.3591    *  (String) ns - The namespace to match.3592    *  (String) name - The stanza name to match.3593    *  (String) type - The stanza type attribute to match.3594    *  (String) id - The stanza id attribute to match.3595   */3596   _addSysHandler: function (handler, ns, name, type, id)3597   {3598     var hand = new Strophe.Handler(handler, ns, name, type, id);3599     hand.user = false;3600     this.addHandlers.push(hand);3601     return hand;3602   },3603 3604   /** PrivateFunction: _onDisconnectTimeout3605    * _Private_ timeout handler for handling non-graceful disconnection.3606    *3607    * If the graceful disconnect process does not complete within the3608    * time allotted, this handler finishes the disconnect anyway.3609    *3610    * Returns:3611    *  false to remove the handler.3612   */3613   _onDisconnectTimeout: function ()3614   {3615     Strophe.info("_onDisconnectTimeout was called");3616 3617     this._proto._onDisconnectTimeout();3618 3619     // actually disconnect3620     this._doDisconnect();3621 3622     return false;3623   },3624 3625   /** PrivateFunction: _onIdle3626    * _Private_ handler to process events during idle cycle.3627    *3628    * This handler is called every 100ms to fire timed handlers that3629    * are ready and keep poll requests going.3630   */3631   _onIdle: function ()3632   {3633     var i, thand, since, newList;3634 3635     // add timed handlers scheduled for addition3636     // NOTE: we add before remove in the case a timed handler is3637     // added and then deleted before the next _onIdle() call.3638     while (this.addTimeds.length > 0) {3639       this.timedHandlers.push(this.addTimeds.pop());3640     }3641 3642     // remove timed handlers that have been scheduled for deletion3643     while (this.removeTimeds.length > 0) {3644       thand = this.removeTimeds.pop();3645       i = this.timedHandlers.indexOf(thand);3646       if (i >= 0) {3647         this.timedHandlers.splice(i, 1);3648       }3649     }3650 3651     // call ready timed handlers3652     var now = new Date().getTime();3653     newList = [];3654     for (i = 0; i < this.timedHandlers.length; i++) {3655       thand = this.timedHandlers[i];3656       if (this.authenticated || !thand.user) {3657         since = thand.lastCalled + thand.period;3658         if (since - now <= 0) {3659           if (thand.run()) {3660             newList.push(thand);3661           }3662         } else {3663           newList.push(thand);3664         }3665       }3666     }3667     this.timedHandlers = newList;3668 3669     clearTimeout(this._idleTimeout);3670 3671     this._proto._onIdle();3672 3673     // reactivate the timer only if connected3674     if (this.connected) {3675       this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);3676     }3677   }3678 };3679 3680 /** Class: Strophe.SASLMechanism3681  *3682  * encapsulates SASL authentication mechanisms.3683  *3684  * User code may override the priority for each mechanism or disable it completely.3685  * See <priority> for information about changing priority and <test> for informatian on3686  * how to disable a mechanism.3687  *3688  * By default, all mechanisms are enabled and the priorities are3689  *3690  * SCRAM-SHA1 - 403691  * DIGEST-MD5 - 303692  * Plain - 203693 */3694 3695 /**3696  * PrivateConstructor: Strophe.SASLMechanism3697  * SASL auth mechanism abstraction.3698  *3699  * Parameters:3700  *  (String) name - SASL Mechanism name.3701  *  (Boolean) isClientFirst - If client should send response first without challenge.3702  *  (Number) priority - Priority.3703  *3704  * Returns:3705  *  A new Strophe.SASLMechanism object.3706 */3707 Strophe.SASLMechanism = function(name, isClientFirst, priority) {3708  /** PrivateVariable: name3709   * Mechanism name.3710  */3711  this.name = name;3712  /** PrivateVariable: isClientFirst3713   * If client sends response without initial server challenge.3714  */3715  this.isClientFirst = isClientFirst;3716  /** Variable: priority3717   * Determines which <SASLMechanism> is chosen for authentication (Higher is better).3718   * Users may override this to prioritize mechanisms differently.3719   *3720   * In the default configuration the priorities are3721   *3722   * SCRAM-SHA1 - 403723   * DIGEST-MD5 - 303724   * Plain - 203725   *3726   * Example: (This will cause Strophe to choose the mechanism that the server sent first)3727   *3728   * > Strophe.SASLMD5.priority = Strophe.SASLSHA1.priority;3729   *3730   * See <SASL mechanisms> for a list of available mechanisms.3731   *3732  */3733  this.priority = priority;3734 };3735 3736 Strophe.SASLMechanism.prototype = {3737  /**3738   * Function: test3739   * Checks if mechanism able to run.3740   * To disable a mechanism, make this return false;3741   *3742   * To disable plain authentication run3743   * > Strophe.SASLPlain.test = function() {3744   * >  return false;3745   * > }3746   *3747   * See <SASL mechanisms> for a list of available mechanisms.3748   *3749   * Parameters:3750   *  (Strophe.Connection) connection - Target Connection.3751   *3752   * Returns:3753   *  (Boolean) If mechanism was able to run.3754  */3755  /* jshint unused:false */3756  test: function(connection) {3757   return true;3758  },3759  /* jshint unused:true */3760 3761  /** PrivateFunction: onStart3762   * Called before starting mechanism on some connection.3763   *3764   * Parameters:3765   *  (Strophe.Connection) connection - Target Connection.3766  */3767  onStart: function(connection)3768  {3769   this._connection = connection;3770  },3771 3772  /** PrivateFunction: onChallenge3773   * Called by protocol implementation on incoming challenge. If client is3774   * first (isClientFirst == true) challenge will be null on the first call.3775   *3776   * Parameters:3777   *  (Strophe.Connection) connection - Target Connection.3778   *  (String) challenge - current challenge to handle.3779   *3780   * Returns:3781   *  (String) Mechanism response.3782  */3783  /* jshint unused:false */3784  onChallenge: function(connection, challenge) {3785   throw new Error("You should implement challenge handling!");3786  },3787  /* jshint unused:true */3788 3789  /** PrivateFunction: onFailure3790   * Protocol informs mechanism implementation about SASL failure.3791  */3792  onFailure: function() {3793   this._connection = null;3794  },3795 3796  /** PrivateFunction: onSuccess3797   * Protocol informs mechanism implementation about SASL success.3798  */3799  onSuccess: function() {3800   this._connection = null;3801  }3802 };3803 3804  /** Constants: SASL mechanisms3805   * Available authentication mechanisms3806   *3807   * Strophe.SASLAnonymous - SASL Anonymous authentication.3808   * Strophe.SASLPlain - SASL Plain authentication.3809   * Strophe.SASLMD5 - SASL Digest-MD5 authentication3810   * Strophe.SASLSHA1 - SASL SCRAM-SHA1 authentication3811  */3812 3813 // Building SASL callbacks3814 3815 /** PrivateConstructor: SASLAnonymous3816  * SASL Anonymous authentication.3817 */3818 Strophe.SASLAnonymous = function() {};3819 3820 Strophe.SASLAnonymous.prototype = new Strophe.SASLMechanism("ANONYMOUS", false, 10);3821 3822 Strophe.SASLAnonymous.test = function(connection) {3823  return connection.authcid === null;3824 };3825 3826 Strophe.Connection.prototype.mechanisms[Strophe.SASLAnonymous.prototype.name] = Strophe.SASLAnonymous;3827 3828 /** PrivateConstructor: SASLPlain3829  * SASL Plain authentication.3830 */3831 Strophe.SASLPlain = function() {};3832 3833 Strophe.SASLPlain.prototype = new Strophe.SASLMechanism("PLAIN", true, 20);3834 3835 Strophe.SASLPlain.test = function(connection) {3836  return connection.authcid !== null;3837 };3838 3839 Strophe.SASLPlain.prototype.onChallenge = function(connection) {3840  var auth_str = connection.authzid;3841  auth_str = auth_str + "\u0000";3842  auth_str = auth_str + connection.authcid;3843  auth_str = auth_str + "\u0000";3844  auth_str = auth_str + connection.pass;3845  return auth_str;3846 };3847 3848 Strophe.Connection.prototype.mechanisms[Strophe.SASLPlain.prototype.name] = Strophe.SASLPlain;3849 3850 /** PrivateConstructor: SASLSHA13851  * SASL SCRAM SHA 1 authentication.3852 */3853 Strophe.SASLSHA1 = function() {};3854 3855 /* TEST:3856  * This is a simple example of a SCRAM-SHA-1 authentication exchange3857  * when the client doesn't support channel bindings (username 'user' and3858  * password 'pencil' are used):3859  *3860  * C: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL3861  * S: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,3862  * i=40963863  * C: c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,3864  * p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=3865  * S: v=rmF9pqV8S7suAoZWja4dJRkFsKQ=3866  *3867 */3868 3869 Strophe.SASLSHA1.prototype = new Strophe.SASLMechanism("SCRAM-SHA-1", true, 40);3870 3871 Strophe.SASLSHA1.test = function(connection) {3872  return connection.authcid !== null;3873 };3874 3875 Strophe.SASLSHA1.prototype.onChallenge = function(connection, challenge, test_cnonce) {3876  var cnonce = test_cnonce || MD5.hexdigest(Math.random() * 1234567890);3877 3878  var auth_str = "n=" + connection.authcid;3879  auth_str += ",r=";3880  auth_str += cnonce;3881 3882  connection._sasl_data.cnonce = cnonce;3883  connection._sasl_data["client-first-message-bare"] = auth_str;3884 3885  auth_str = "n,," + auth_str;3886 3887  this.onChallenge = function (connection, challenge)3888  {3889   var nonce, salt, iter, Hi, U, U_old, i, k;3890   var clientKey, serverKey, clientSignature;3891   var responseText = "c=biws,";3892   var authMessage = connection._sasl_data["client-first-message-bare"] + "," +3893    challenge + ",";3894   var cnonce = connection._sasl_data.cnonce;3895   var attribMatch = /([a-z]+)=([^,]+)(,|$)/;3896 3897   while (challenge.match(attribMatch)) {3898    var matches = challenge.match(attribMatch);3899    challenge = challenge.replace(matches[0], "");3900    switch (matches[1]) {3901    case "r":3902     nonce = matches[2];3903     break;3904    case "s":3905     salt = matches[2];3906     break;3907    case "i":3908     iter = matches[2];3909     break;3910    }3911   }3912 3913   if (nonce.substr(0, cnonce.length) !== cnonce) {3914    connection._sasl_data = {};3915    return connection._sasl_failure_cb();3916   }3917 3918   responseText += "r=" + nonce;3919   authMessage += responseText;3920 3921   salt = Base64.decode(salt);3922   salt += "\x00\x00\x00\x01";3923 3924   Hi = U_old = SHA1.core_hmac_sha1(connection.pass, salt);3925   for (i = 1; i < iter; i++) {3926    U = SHA1.core_hmac_sha1(connection.pass, SHA1.binb2str(U_old));3927    for (k = 0; k < 5; k++) {3928     Hi[k] ^= U[k];3929    }3930    U_old = U;3931   }3932   Hi = SHA1.binb2str(Hi);3933 3934   clientKey = SHA1.core_hmac_sha1(Hi, "Client Key");3935   serverKey = SHA1.str_hmac_sha1(Hi, "Server Key");3936   clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage);3937   connection._sasl_data["server-signature"] = SHA1.b64_hmac_sha1(serverKey, authMessage);3938 3939   for (k = 0; k < 5; k++) {3940    clientKey[k] ^= clientSignature[k];3941   }3942 3943   responseText += ",p=" + Base64.encode(SHA1.binb2str(clientKey));3944 3945   return responseText;3946  }.bind(this);3947 3948  return auth_str;3949 };3950 3951 Strophe.Connection.prototype.mechanisms[Strophe.SASLSHA1.prototype.name] = Strophe.SASLSHA1;3952 3953 /** PrivateConstructor: SASLMD53954  * SASL DIGEST MD5 authentication.3955 */3956 Strophe.SASLMD5 = function() {};3957 3958 Strophe.SASLMD5.prototype = new Strophe.SASLMechanism("DIGEST-MD5", false, 30);3959 3960 Strophe.SASLMD5.test = function(connection) {3961  return connection.authcid !== null;3962 };3963 3964 /** PrivateFunction: _quote3965  * _Private_ utility function to backslash escape and quote strings.3966  *3967  * Parameters:3968  *  (String) str - The string to be quoted.3969  *3970  * Returns:3971  *  quoted string3972 */3973 Strophe.SASLMD5.prototype._quote = function (str)3974  {3975   return '"' + str.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';3976   //" end string workaround for emacs3977  };3978 3979 3980 Strophe.SASLMD5.prototype.onChallenge = function(connection, challenge, test_cnonce) {3981  var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/;3982  var cnonce = test_cnonce || MD5.hexdigest("" + (Math.random() * 1234567890));3983  var realm = "";3984  var host = null;3985  var nonce = "";3986  var qop = "";3987  var matches;3988 3989  while (challenge.match(attribMatch)) {3990   matches = challenge.match(attribMatch);3991   challenge = challenge.replace(matches[0], "");3992   matches[2] = matches[2].replace(/^"(.+)"$/, "$1");3993   switch (matches[1]) {3994   case "realm":3995    realm = matches[2];3996    break;3997   case "nonce":3998    nonce = matches[2];3999    break;4000   case "qop":4001    qop = matches[2];4002    break;4003   case "host":4004    host = matches[2];4005    break;4006   }4007  }4008 4009  var digest_uri = connection.servtype + "/" + connection.domain;4010  if (host !== null) {4011   digest_uri = digest_uri + "/" + host;4012  }4013 4014  var A1 = MD5.hash(connection.authcid +4015           ":" + realm + ":" + this._connection.pass) +4016   ":" + nonce + ":" + cnonce;4017  var A2 = 'AUTHENTICATE:' + digest_uri;4018 4019  var responseText = "";4020  responseText += 'charset=utf-8,';4021  responseText += 'username=' +4022   this._quote(connection.authcid) + ',';4023  responseText += 'realm=' + this._quote(realm) + ',';4024  responseText += 'nonce=' + this._quote(nonce) + ',';4025  responseText += 'nc=00000001,';4026  responseText += 'cnonce=' + this._quote(cnonce) + ',';4027  responseText += 'digest-uri=' + this._quote(digest_uri) + ',';4028  responseText += 'response=' + MD5.hexdigest(MD5.hexdigest(A1) + ":" +4029                        nonce + ":00000001:" +4030                        cnonce + ":auth:" +4031                        MD5.hexdigest(A2)) + ",";4032  responseText += 'qop=auth';4033 4034  this.onChallenge = function ()4035  {4036    return "";4037  }.bind(this);4038 4039  return responseText;4040 };4041 4042 Strophe.Connection.prototype.mechanisms[Strophe.SASLMD5.prototype.name] = Strophe.SASLMD5;4043 4044 return {4045   Strophe:    Strophe,4046   $build:     $build,4047   $msg:      $msg,4048   $iq:      $iq,4049   $pres:     $pres,4050   SHA1:      SHA1,4051   Base64:     Base64,4052   MD5:      MD5,4053 };4054 }));4055 4056 /*4057   This program is distributed under the terms of the MIT license.4058   Please see the LICENSE file for details.4059 4060   Copyright 2006-2008, OGG, LLC4061 */4062 4063 /* jshint undef: true, unused: true:, noarg: true, latedef: true */4064 /* global define, window, setTimeout, clearTimeout, */4065 4066 (function (root, factory) {4067   if (typeof define === 'function' && define.amd) {4068     define('strophe-bosh', ['strophe-core'], function (core) {4069       return factory(4070         core.Strophe,4071         core.$build4072       );4073     });4074   } else {4075     // Browser globals4076     return factory(Strophe, $build);4077   }4078 }(this, function (Strophe, $build) {4079 4080 /** PrivateClass: Strophe.Request4081  * _Private_ helper class that provides a cross implementation abstraction4082  * for a BOSH related 4083  *4084  * The Strophe.Request class is used internally to encapsulate BOSH request4085  * information. It is not meant to be used from user's code.4086 */4087 4088 /** PrivateConstructor: Strophe.Request4089  * Create and initialize a new Strophe.Request object.4090  *4091  * Parameters:4092  *  (4093  *  (Function) func - The function that will be called when the4094  *   4095  *  (Integer) rid - The BOSH rid attribute associated with this request.4096  *  (Integer) sends - The number of times this same request has been4097  *   sent.4098 */4099 Strophe.Request = function (elem, func, rid, sends)4100 {4101   this.id = ++Strophe._requestId;4102   this. elem;4103   this.data = Strophe.serialize(elem);4104   // save original function in case we need to make a new request4105   // from this one.4106   this.origFunc = func;4107   this.func = func;4108   this.rid = rid;4109   this.date = NaN;4110   this.sends = sends || 0;4111   this.abort = false;4112   this.dead = null;4113 4114   this.age = function () {4115     if (!this.date) { return 0; }4116     var now = new Date();4117     return (now - this.date) / 1000;4118   };4119   this.timeDead = function () {4120     if (!this.dead) { return 0; }4121     var now = new Date();4122     return (now - this.dead) / 1000;4123   };4124   this.xhr = this._newXHR();4125 };4126 4127 Strophe.Request.prototype = {4128   /** PrivateFunction: getResponse4129    * Get a response from the underlying 4130    *4131    * This function attempts to get a response from the request and checks4132    * for errors.4133    *4134    * Throws:4135    *  "parsererror" - A parser error occured.4136    *4137    * Returns:4138    *  The DOM element tree of the response.4139   */4140   getResponse: function ()4141   {4142     var node = null;4143     if (this.xhr.responsethis.xhr.response4144       node = this.xhr.response4145       if (node.tagName == "parsererror") {4146         Strophe.error("invalid response received");4147         Strophe.error("responseText: " + this.xhr.responseText);4148         Strophe.error("response" +4149                Strophe.serialize(this.xhr.response4150         throw "parsererror";4151       }4152     } else if (this.xhr.responseText) {4153       Strophe.error("invalid response received");4154       Strophe.error("responseText: " + this.xhr.responseText);4155       Strophe.error("response" +4156              Strophe.serialize(this.xhr.response4157     }4158 4159     return node;4160   },4161 4162   /** PrivateFunction: _newXHR4163    * _Private_ helper function to create 4164    *4165    * This function creates 4166    *4167    * Returns:4168    *  A new 4169   */4170   _newXHR: function ()4171   {4172     var xhr = null;4173     if (window.4174       xhr = new 4175       if (xhr.overrideMimeType) {4176         xhr.overrideMimeType("text/");4177       }4178     } else if (window.ActiveXObject) {4179       xhr = new ActiveXObject("Microsoft.");4180     }4181 4182     // use Function.bind() to prepend ourselves as an argument4183     xhr.onreadystatechange = this.func.bind(null, this);4184 4185     return xhr;4186   }4187 };4188 4189 /** Class: Strophe.Bosh4190  * _Private_ helper class that handles BOSH Connections4191  *4192  * The Strophe.Bosh class is used internally by Strophe.Connection4193  * to encapsulate BOSH sessions. It is not meant to be used from user's code.4194 */4195 4196 /** File: bosh.js4197  * A JavaScript library to enable BOSH in Strophejs.4198  *4199  * this library uses Bidirectional-streams Over Synchronous HTTP (BOSH)4200  * to emulate a persistent, stateful, two-way connection to an XMPP server.4201  * More information on BOSH can be found in XEP 124.4202 */4203 4204 /** PrivateConstructor: Strophe.Bosh4205  * Create and initialize a Strophe.Bosh object.4206  *4207  * Parameters:4208  *  (Strophe.Connection) connection - The Strophe.Connection that will use BOSH.4209  *4210  * Returns:4211  *  A new Strophe.Bosh object.4212 */4213 Strophe.Bosh = function(connection) {4214   this._conn = connection;4215   /* request id for body tags */4216   this.rid = Math.floor(Math.random() * 4294967295);4217   /* The current session ID. */4218   this.sid = null;4219 4220   // default BOSH values4221   this.hold = 1;4222   this.wait = 60;4223   this.window = 5;4224   this.errors = 0;4225 4226   this._requests = [];4227 };4228 4229 Strophe.Bosh.prototype = {4230   /** Variable: strip4231    *4232    * BOSH-Connections will have all stanzas wrapped in a <body> tag when4233    * passed to <Strophe.Connection.4234    * To strip this tag, User code can set <Strophe.Bosh.strip> to "body":4235    *4236    * > Strophe.Bosh.prototype.strip = "body";4237    *4238    * This will enable stripping of the body tag in both4239    * <Strophe.Connection.4240   */4241   strip: null,4242 4243   /** PrivateFunction: _buildBody4244    * _Private_ helper function to generate the <body/> wrapper for BOSH.4245    *4246    * Returns:4247    *  A Strophe.Builder with a <body/> element.4248   */4249   _buildBody: function ()4250   {4251     var bodyWrap = $build('body', {4252       rid: this.rid++,4253       4254     });4255     if (this.sid !== null) {4256       bodyWrap.attrs({sid: this.sid});4257     }4258     if (this._conn.options.keepalive) {4259       this._cacheSession();4260     }4261     return bodyWrap;4262   },4263 4264   /** PrivateFunction: _reset4265    * Reset the connection.4266    *4267    * This function is called by the reset function of the Strophe Connection4268   */4269   _reset: function ()4270   {4271     this.rid = Math.floor(Math.random() * 4294967295);4272     this.sid = null;4273     this.errors = 0;4274     window.sessionStorage.removeItem('strophe-bosh-session');4275   },4276 4277   /** PrivateFunction: _connect4278    * _Private_ function that initializes the BOSH connection.4279    *4280    * Creates and sends the Request that initializes the BOSH connection.4281   */4282   _connect: function (wait, hold, route)4283   {4284     this.wait = wait || this.wait;4285     this.hold = hold || this.hold;4286     this.errors = 0;4287 4288     // build the body tag4289     var body = this._buildBody().attrs({4290       to: this._conn.domain,4291       "": "en",4292       wait: this.wait,4293       hold: this.hold,4294       content: "text/",4295       ver: "1.6",4296       "xmpp:version": "1.0",4297       "": Strophe.NS.BOSH4298     });4299 4300     if(route){4301       body.attrs({4302         route: route4303       });4304     }4305 4306     var _connect_cb = this._conn._connect_cb;4307 4308     this._requests.push(4309       new Strophe.Request(body.tree(),4310                 this._onRequestStateChange.bind(4311                   this, _connect_cb.bind(this._conn)),4312                 body.tree().getAttribute("rid")));4313     this._throttledRequestHandler();4314   },4315 4316   /** PrivateFunction: _attach4317    * Attach to an already created and authenticated BOSH session.4318    *4319    * This function is provided to allow Strophe to attach to BOSH4320    * sessions which have been created externally, perhaps by a Web4321    * application. This is often used to support auto-login type features4322    * without putting user credentials into the page.4323    *4324    * Parameters:4325    *  (String) jid - The full JID that is bound by the session.4326    *  (String) sid - The SID of the BOSH session.4327    *  (String) rid - The current RID of the BOSH session. This RID4328    *   will be used by the next request.4329    *  (Function) callback The connect callback function.4330    *  (Integer) wait - The optional HTTPBIND wait value. This is the4331    *   time the server will wait before returning an empty result for4332    *   a request. The default setting of 60 seconds is recommended.4333    *   Other settings will require tweaks to the Strophe.TIMEOUT value.4334    *  (Integer) hold - The optional HTTPBIND hold value. This is the4335    *   number of connections the server will hold at one time. This4336    *   should almost always be set to 1 (the default).4337    *  (Integer) wind - The optional HTTBIND window value. This is the4338    *   allowed range of request ids that are valid. The default is 5.4339   */4340   _attach: function (jid, sid, rid, callback, wait, hold, wind)4341   {4342     this._conn.jid = jid;4343     this.sid = sid;4344     this.rid = rid;4345 4346     this._conn.connect_callback = callback;4347 4348     this._conn.domain = Strophe.getDomainFromJid(this._conn.jid);4349 4350     this._conn.authenticated = true;4351     this._conn.connected = true;4352 4353     this.wait = wait || this.wait;4354     this.hold = hold || this.hold;4355     this.window = wind || this.window;4356 4357     this._conn._changeConnectStatus(Strophe.Status.ATTACHED, null);4358   },4359 4360   /** PrivateFunction: _restore4361    * Attempt to restore a cached BOSH session4362    *4363    * Parameters:4364    *  (String) jid - The full JID that is bound by the session.4365    *   This parameter is optional but recommended, specifically in cases4366    *   where prebinded BOSH sessions are used where it's important to know4367    *   that the right session is being restored.4368    *  (Function) callback The connect callback function.4369    *  (Integer) wait - The optional HTTPBIND wait value. This is the4370    *   time the server will wait before returning an empty result for4371    *   a request. The default setting of 60 seconds is recommended.4372    *   Other settings will require tweaks to the Strophe.TIMEOUT value.4373    *  (Integer) hold - The optional HTTPBIND hold value. This is the4374    *   number of connections the server will hold at one time. This4375    *   should almost always be set to 1 (the default).4376    *  (Integer) wind - The optional HTTBIND window value. This is the4377    *   allowed range of request ids that are valid. The default is 5.4378   */4379   _restore: function (jid, callback, wait, hold, wind)4380   {4381     var session = JSON.parse(window.sessionStorage.getItem('strophe-bosh-session'));4382     if (typeof session !== "undefined" &&4383          session !== null &&4384          session.rid &&4385          session.sid &&4386          session.jid &&4387          (typeof jid === "undefined" || Strophe.getBareJidFromJid(session.jid) == Strophe.getBareJidFromJid(jid)))4388     {4389       this._conn.restored = true;4390       this._attach(session.jid, session.sid, session.rid, callback, wait, hold, wind);4391     } else {4392       throw { name: "StropheSessionError", message: "_restore: no restoreable session." };4393     }4394   },4395 4396   /** PrivateFunction: _cacheSession4397    * _Private_ handler for the beforeunload event.4398    *4399    * This handler is used to process the Bosh-part of the initial request.4400    * Parameters:4401    *  (Strophe.Request) bodyWrap - The received stanza.4402   */4403   _cacheSession: function ()4404   {4405     if (this._conn.authenticated) {4406       if (this._conn.jid && this.rid && this.sid) {4407         window.sessionStorage.setItem('strophe-bosh-session', JSON.stringify({4408           'jid': this._conn.jid,4409           'rid': this.rid,4410           'sid': this.sid4411         }));4412       }4413     } else {4414       window.sessionStorage.removeItem('strophe-bosh-session');4415     }4416   },4417 4418   /** PrivateFunction: _connect_cb4419    * _Private_ handler for initial connection request.4420    *4421    * This handler is used to process the Bosh-part of the initial request.4422    * Parameters:4423    *  (Strophe.Request) bodyWrap - The received stanza.4424   */4425   _connect_cb: function (bodyWrap)4426   {4427     var typ = bodyWrap.getAttribute("type");4428     var cond, conflict;4429     if (typ !== null && typ == "terminate") {4430       // an error occurred4431       cond = bodyWrap.getAttribute("condition");4432       Strophe.error("BOSH-Connection failed: " + cond);4433       conflict = bodyWrap.getElementsByTagName("conflict");4434       if (cond !== null) {4435         if (cond == "remote-stream-error" && conflict.length > 0) {4436           cond = "conflict";4437         }4438         this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, cond);4439       } else {4440         this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");4441       }4442       this._conn._doDisconnect(cond);4443       return Strophe.Status.CONNFAIL;4444     }4445 4446     // check to make sure we don't overwrite these if _connect_cb is4447     // called multiple times in the case of missing stream:features4448     if (!this.sid) {4449       this.sid = bodyWrap.getAttribute("sid");4450     }4451     var wind = bodyWrap.getAttribute('requests');4452     if (wind) { this.window = parseInt(wind, 10); }4453     var hold = bodyWrap.getAttribute('hold');4454     if (hold) { this.hold = parseInt(hold, 10); }4455     var wait = bodyWrap.getAttribute('wait');4456     if (wait) { this.wait = parseInt(wait, 10); }4457   },4458 4459   /** PrivateFunction: _disconnect4460    * _Private_ part of Connection.disconnect for Bosh4461    *4462    * Parameters:4463    *  (Request) pres - This stanza will be sent before disconnecting.4464   */4465   _disconnect: function (pres)4466   {4467     this._sendTerminate(pres);4468   },4469 4470   /** PrivateFunction: _doDisconnect4471    * _Private_ function to disconnect.4472    *4473    * Resets the SID and RID.4474   */4475   _doDisconnect: function ()4476   {4477     this.sid = null;4478     this.rid = Math.floor(Math.random() * 4294967295);4479     window.sessionStorage.removeItem('strophe-bosh-session');4480   },4481 4482   /** PrivateFunction: _emptyQueue4483    * _Private_ function to check if the Request queue is empty.4484    *4485    * Returns:4486    *  True, if there are no Requests queued, False otherwise.4487   */4488   _emptyQueue: function ()4489   {4490     return this._requests.length === 0;4491   },4492 4493   /** PrivateFunction: _hitError4494    * _Private_ function to handle the error count.4495    *4496    * Requests are resent automatically until their error count reaches4497    * 5. Each time an error is encountered, this function is called to4498    * increment the count and disconnect if the count is too high.4499    *4500    * Parameters:4501    *  (Integer) reqStatus - The request status.4502   */4503   _hitError: function (reqStatus)4504   {4505     this.errors++;4506     Strophe.warn("request errored, status: " + reqStatus +4507           ", number of errors: " + this.errors);4508     if (this.errors > 4) {4509       this._conn._onDisconnectTimeout();4510     }4511   },4512 4513   /** PrivateFunction: _no_auth_received4514    *4515    * Called on stream start/restart when no stream:features4516    * has been received and sends a blank poll request.4517   */4518   _no_auth_received: function (_callback)4519   {4520     if (_callback) {4521       _callback = _callback.bind(this._conn);4522     } else {4523       _callback = this._conn._connect_cb.bind(this._conn);4524     }4525     var body = this._buildBody();4526     this._requests.push(4527         new Strophe.Request(body.tree(),4528           this._onRequestStateChange.bind(4529             this, _callback.bind(this._conn)),4530           body.tree().getAttribute("rid")));4531     this._throttledRequestHandler();4532   },4533 4534   /** PrivateFunction: _onDisconnectTimeout4535    * _Private_ timeout handler for handling non-graceful disconnection.4536    *4537    * Cancels all remaining Requests and clears the queue.4538   */4539   _onDisconnectTimeout: function () {4540     this._abortAllRequests();4541   },4542 4543   /** PrivateFunction: _abortAllRequests4544    * _Private_ helper function that makes sure all pending requests are aborted.4545   */4546   _abortAllRequests: function _abortAllRequests() {4547     var req;4548     while (this._requests.length > 0) {4549       req = this._requests.pop();4550       req.abort = true;4551       req.xhr.abort();4552       // jslint complains, but this is fine. setting to empty func4553       // is necessary for IE64554       req.xhr.onreadystatechange = function () {}; // jshint ignore:line4555     }4556   },4557 4558   /** PrivateFunction: _onIdle4559    * _Private_ handler called by Strophe.Connection._onIdle4560    *4561    * Sends all queued Requests or polls with empty Request if there are none.4562   */4563   _onIdle: function () {4564     var data = this._conn._data;4565 4566     // if no requests are in progress, poll4567     if (this._conn.authenticated && this._requests.length === 0 &&4568       data.length === 0 && !this._conn.disconnecting) {4569       Strophe.info("no requests during idle cycle, sending " +4570             "blank request");4571       data.push(null);4572     }4573 4574     if (this._conn.paused) {4575       return;4576     }4577 4578     if (this._requests.length < 2 && data.length > 0) {4579       var body = this._buildBody();4580       for (var i = 0; i < data.length; i++) {4581         if (data[i] !== null) {4582           if (data[i] === "restart") {4583             body.attrs({4584               to: this._conn.domain,4585               "": "en",4586               "xmpp:restart": "true",4587               "": Strophe.NS.BOSH4588             });4589           } else {4590             body.cnode(data[i]).up();4591           }4592         }4593       }4594       delete this._conn._data;4595       this._conn._data = [];4596       this._requests.push(4597         new Strophe.Request(body.tree(),4598                   this._onRequestStateChange.bind(4599                     this, this._conn._dataRecv.bind(this._conn)),4600                   body.tree().getAttribute("rid")));4601       this._throttledRequestHandler();4602     }4603 4604     if (this._requests.length > 0) {4605       var time_elapsed = this._requests[0].age();4606       if (this._requests[0].dead !== null) {4607         if (this._requests[0].timeDead() >4608           Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) {4609           this._throttledRequestHandler();4610         }4611       }4612 4613       if (time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait)) {4614         Strophe.warn("Request " +4615               this._requests[0].id +4616               " timed out, over " + Math.floor(Strophe.TIMEOUT * this.wait) +4617               " seconds since last activity");4618         this._throttledRequestHandler();4619       }4620     }4621   },4622 4623   /** PrivateFunction: _onRequestStateChange4624    * _Private_ handler for Strophe.Request state changes.4625    *4626    * This function is called when the 4627    * It contains a lot of error handling logic for the many ways that4628    * requests can fail, and calls the request callback when requests4629    * succeed.4630    *4631    * Parameters:4632    *  (Function) func - The handler for the request.4633    *  (Strophe.Request) req - The request that is changing readyState.4634   */4635   _onRequestStateChange: function (func, req)4636   {4637     Strophe.debug("request id " + req.id +4638            "." + req.sends + " state changed to " +4639            req.xhr.readyState);4640 4641     if (req.abort) {4642       req.abort = false;4643       return;4644     }4645 4646     // request complete4647     var reqStatus;4648     if (req.xhr.readyState == 4) {4649       reqStatus = 0;4650       try {4651         reqStatus = req.xhr.status;4652       } catch (e) {4653         // ignore errors from undefined status attribute. works4654         // around a browser bug4655       }4656 4657       if (typeof(reqStatus) == "undefined") {4658         reqStatus = 0;4659       }4660 4661       if (this.disconnecting) {4662         if (reqStatus >= 400) {4663           this._hitError(reqStatus);4664           return;4665         }4666       }4667 4668       var reqIs0 = (this._requests[0] == req);4669       var reqIs1 = (this._requests[1] == req);4670 4671       if ((reqStatus > 0 && reqStatus < 500) || req.sends > 5) {4672         // remove from internal queue4673         this._removeRequest(req);4674         Strophe.debug("request id " +4675                req.id +4676                " should now be removed");4677       }4678 4679       // request succeeded4680       if (reqStatus == 200) {4681         // if request 1 finished, or request 0 finished and request4682         // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to4683         // restart the other - both will be in the first spot, as the4684         // completed request has been removed from the queue already4685         if (reqIs1 ||4686           (reqIs0 && this._requests.length > 0 &&4687           this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait))) {4688           this._restartRequest(0);4689         }4690         // call handler4691         Strophe.debug("request id " +4692                req.id + "." +4693                req.sends + " got 200");4694         func(req);4695         this.errors = 0;4696       } else {4697         Strophe.error("request id " +4698                req.id + "." +4699                req.sends + " error " + reqStatus +4700                " happened");4701         if (reqStatus === 0 ||4702           (reqStatus >= 400 && reqStatus < 600) ||4703           reqStatus >= 12000) {4704           this._hitError(reqStatus);4705           if (reqStatus >= 400 && reqStatus < 500) {4706             this._conn._changeConnectStatus(Strophe.Status.DISCONNECTING, null);4707             this._conn._doDisconnect();4708           }4709         }4710       }4711 4712       if (!((reqStatus > 0 && reqStatus < 500) ||4713          req.sends > 5)) {4714         this._throttledRequestHandler();4715       }4716     }4717   },4718 4719   /** PrivateFunction: _processRequest4720    * _Private_ function to process a request in the queue.4721    *4722    * This function takes requests off the queue and sends them and4723    * restarts dead requests.4724    *4725    * Parameters:4726    *  (Integer) i - The index of the request in the queue.4727   */4728   _processRequest: function (i)4729   {4730     var self = this;4731     var req = this._requests[i];4732     var reqStatus = -1;4733 4734     try {4735       if (req.xhr.readyState == 4) {4736         reqStatus = req.xhr.status;4737       }4738     } catch (e) {4739       Strophe.error("caught an error in _requests[" + i +4740              "], reqStatus: " + reqStatus);4741     }4742 4743     if (typeof(reqStatus) == "undefined") {4744       reqStatus = -1;4745     }4746 4747     // make sure we limit the number of retries4748     if (req.sends > this._conn.maxRetries) {4749       this._conn._onDisconnectTimeout();4750       return;4751     }4752 4753     var time_elapsed = req.age();4754     var primaryTimeout = (!isNaN(time_elapsed) &&4755                time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait));4756     var secondaryTimeout = (req.dead !== null &&4757                 req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait));4758     var requestCompletedWithServerError = (req.xhr.readyState == 4 &&4759                        (reqStatus < 1 ||4760                         reqStatus >= 500));4761     if (primaryTimeout || secondaryTimeout ||4762       requestCompletedWithServerError) {4763       if (secondaryTimeout) {4764         Strophe.error("Request " +4765                this._requests[i].id +4766                " timed out (secondary), restarting");4767       }4768       req.abort = true;4769       req.xhr.abort();4770       // setting to null fails on IE6, so set to empty function4771       req.xhr.onreadystatechange = function () {};4772       this._requests[i] = new Strophe.Request(req.4773                           req.origFunc,4774                           req.rid,4775                           req.sends);4776       req = this._requests[i];4777     }4778 4779     if (req.xhr.readyState === 0) {4780       Strophe.debug("request id " + req.id +4781              "." + req.sends + " posting");4782 4783       try {4784         req.xhr.open("POST", this._conn.service, this._conn.options.sync ? false : true);4785         req.xhr.setRequestHeader("Content-Type", "text/");4786       } catch (e2) {4787         Strophe.error("XHR open failed.");4788         if (!this._conn.connected) {4789           this._conn._changeConnectStatus(Strophe.Status.CONNFAIL,4790                        "bad-service");4791         }4792         this._conn.disconnect();4793         return;4794       }4795 4796       // Fires the XHR request -- may be invoked immediately4797       // or on a gradually expanding retry window for reconnects4798       var sendFunc = function () {4799         req.date = new Date();4800         if (self._conn.options.customHeaders){4801           var headers = self._conn.options.customHeaders;4802           for (var header in headers) {4803             if (headers.hasOwnProperty(header)) {4804               req.xhr.setRequestHeader(header, headers[header]);4805             }4806           }4807         }4808         req.xhr.send(req.data);4809       };4810 4811       // Implement progressive backoff for reconnects --4812       // First retry (send == 1) should also be instantaneous4813       if (req.sends > 1) {4814         // Using a cube of the retry number creates a nicely4815         // expanding retry window4816         var backoff = Math.min(Math.floor(Strophe.TIMEOUT * this.wait),4817                    Math.pow(req.sends, 3)) * 1000;4818         setTimeout(sendFunc, backoff);4819       } else {4820         sendFunc();4821       }4822 4823       req.sends++;4824 4825       if (this._conn. Strophe.Connection.prototype.4826         if (req.this.strip && req.4827           this._conn.0]);4828         } else {4829           this._conn.4830         }4831       }4832       if (this._conn.rawOutput !== Strophe.Connection.prototype.rawOutput) {4833         this._conn.rawOutput(req.data);4834       }4835     } else {4836       Strophe.debug("_processRequest: " +4837              (i === 0 ? "first" : "second") +4838              " request has readyState of " +4839              req.xhr.readyState);4840     }4841   },4842 4843   /** PrivateFunction: _removeRequest4844    * _Private_ function to remove a request from the queue.4845    *4846    * Parameters:4847    *  (Strophe.Request) req - The request to remove.4848   */4849   _removeRequest: function (req)4850   {4851     Strophe.debug("removing request");4852 4853     var i;4854     for (i = this._requests.length - 1; i >= 0; i--) {4855       if (req == this._requests[i]) {4856         this._requests.splice(i, 1);4857       }4858     }4859 4860     // IE6 fails on setting to null, so set to empty function4861     req.xhr.onreadystatechange = function () {};4862 4863     this._throttledRequestHandler();4864   },4865 4866   /** PrivateFunction: _restartRequest4867    * _Private_ function to restart a request that is presumed dead.4868    *4869    * Parameters:4870    *  (Integer) i - The index of the request in the queue.4871   */4872   _restartRequest: function (i)4873   {4874     var req = this._requests[i];4875     if (req.dead === null) {4876       req.dead = new Date();4877     }4878 4879     this._processRequest(i);4880   },4881 4882   /** PrivateFunction: _reqToData4883    * _Private_ function to get a stanza out of a request.4884    *4885    * Tries to extract a stanza out of a Request Object.4886    * When this fails the current connection will be disconnected.4887    *4888    * Parameters:4889    *  (Object) req - The Request.4890    *4891    * Returns:4892    *  The stanza that was passed.4893   */4894   _reqToData: function (req)4895   {4896     try {4897       return req.getResponse();4898     } catch (e) {4899       if (e != "parsererror") { throw e; }4900       this._conn.disconnect("strophe-parsererror");4901     }4902   },4903 4904   /** PrivateFunction: _sendTerminate4905    * _Private_ function to send initial disconnect sequence.4906    *4907    * This is the first step in a graceful disconnect. It sends4908    * the BOSH server a terminate body and includes an unavailable4909    * presence if authentication has completed.4910   */4911   _sendTerminate: function (pres)4912   {4913     Strophe.info("_sendTerminate was called");4914     var body = this._buildBody().attrs({type: "terminate"});4915 4916     if (pres) {4917       body.cnode(pres.tree());4918     }4919 4920     var req = new Strophe.Request(body.tree(),4921                    this._onRequestStateChange.bind(4922                      this, this._conn._dataRecv.bind(this._conn)),4923                    body.tree().getAttribute("rid"));4924 4925     this._requests.push(req);4926     this._throttledRequestHandler();4927   },4928 4929   /** PrivateFunction: _send4930    * _Private_ part of the Connection.send function for BOSH4931    *4932    * Just triggers the RequestHandler to send the messages that are in the queue4933   */4934   _send: function () {4935     clearTimeout(this._conn._idleTimeout);4936     this._throttledRequestHandler();4937     this._conn._idleTimeout = setTimeout(this._conn._onIdle.bind(this._conn), 100);4938   },4939 4940   /** PrivateFunction: _sendRestart4941    *4942    * Send an xmpp:restart stanza.4943   */4944   _sendRestart: function ()4945   {4946     this._throttledRequestHandler();4947     clearTimeout(this._conn._idleTimeout);4948   },4949 4950   /** PrivateFunction: _throttledRequestHandler4951    * _Private_ function to throttle requests to the connection window.4952    *4953    * This function makes sure we don't send requests so fast that the4954    * request ids overflow the connection window in the case that one4955    * request died.4956   */4957   _throttledRequestHandler: function ()4958   {4959     if (!this._requests) {4960       Strophe.debug("_throttledRequestHandler called with " +4961              "undefined requests");4962     } else {4963       Strophe.debug("_throttledRequestHandler called with " +4964              this._requests.length + " requests");4965     }4966 4967     if (!this._requests || this._requests.length === 0) {4968       return;4969     }4970 4971     if (this._requests.length > 0) {4972       this._processRequest(0);4973     }4974 4975     if (this._requests.length > 1 &&4976       Math.abs(this._requests[0].rid -4977           this._requests[1].rid) < this.window) {4978       this._processRequest(1);4979     }4980   }4981 };4982 return Strophe;4983 }));4984 4985 /*4986   This program is distributed under the terms of the MIT license.4987   Please see the LICENSE file for details.4988 4989   Copyright 2006-2008, OGG, LLC4990 */4991 4992 /* jshint undef: true, unused: true:, noarg: true, latedef: true */4993 /* global define, window, clearTimeout, WebSocket, DOMParser, Strophe, $build */4994 4995 (function (root, factory) {4996   if (typeof define === 'function' && define.amd) {4997     define('strophe-websocket', ['strophe-core'], function (core) {4998       return factory(4999         core.Strophe,5000         core.$build5001       );5002     });5003   } else {5004     // Browser globals5005     return factory(Strophe, $build);5006   }5007 }(this, function (Strophe, $build) {5008 5009 /** Class: Strophe.WebSocket5010  * _Private_ helper class that handles WebSocket Connections5011  *5012  * The Strophe.WebSocket class is used internally by Strophe.Connection5013  * to encapsulate WebSocket sessions. It is not meant to be used from user's code.5014 */5015 5016 /** File: websocket.js5017  * A JavaScript library to enable XMPP over Websocket in Strophejs.5018  *5019  * This file implements XMPP over WebSockets for Strophejs.5020  * If a Connection is established with a Websocket url (ws://...)5021  * Strophe will use WebSockets.5022  * For more information on XMPP-over-WebSocket see RFC 7395:5023  * http://tools.ietf.org/html/rfc73955024  *5025  * WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de)5026 */5027 5028 /** PrivateConstructor: Strophe.Websocket5029  * Create and initialize a Strophe.WebSocket object.5030  * Currently only sets the connection Object.5031  *5032  * Parameters:5033  *  (Strophe.Connection) connection - The Strophe.Connection that will use WebSockets.5034  *5035  * Returns:5036  *  A new Strophe.WebSocket object.5037 */5038 Strophe.Websocket = function(connection) {5039   alert(window.location.host);5040   this._conn = connection;5041   this.strip = "wrapper";5042 5043   var service = connection.service;5044   if (service.indexOf("ws:") !== 0 && service.indexOf("wss:") !== 0) {5045     // If the service is not an absolute URL, assume it is a path and put the absolute5046     // URL together from options, current URL and the path.5047     var new_service = "";5048 5049     if (connection.options.protocol === "ws" && window.location.protocol !== "https:") {5050       new_service += "ws";5051     } else {5052       new_service += "wss";5053     }5054 5055     new_service += "://" + window.location.host;5056 5057     if (service.indexOf("/") !== 0) {5058       new_service += window.location.pathname + service;5059     } else {5060       new_service += service;5061     }5062 5063     connection.service = new_service;5064   }5065 };5066 5067 Strophe.Websocket.prototype = {5068   /** PrivateFunction: _buildStream5069    * _Private_ helper function to generate the <stream> start tag for WebSockets5070    *5071    * Returns:5072    *  A Strophe.Builder with a <stream> element.5073   */5074   _buildStream: function ()5075   {5076     return $build("open", {5077       "": Strophe.NS.FRAMING,5078       "to": this._conn.domain,5079       "version": '1.0'5080     });5081   },5082 5083   /** PrivateFunction: _check_streamerror5084    * _Private_ checks a message for stream:error5085    *5086    * Parameters:5087    *  (Strophe.Request) bodyWrap - The received stanza.5088    *  connectstatus - The ConnectStatus that will be set on error.5089    * Returns:5090    *   true if there was a streamerror, false otherwise.5091   */5092   _check_streamerror: function (bodyWrap, connectstatus) {5093     var errors;5094     if (bodyWrap.getElementsByTagNameNS) {5095       errors = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "error");5096     } else {5097       errors = bodyWrap.getElementsByTagName("stream:error");5098     }5099     if (errors.length === 0) {5100       return false;5101     }5102     var error = errors[0];5103 5104     var condition = "";5105     var text = "";5106 5107     var ns = "urn:ietf:params:";5108     for (var i = 0; i < error.childNodes.length; i++) {5109       var e = error.childNodes[i];5110       if (e.getAttribute("") !== ns) {5111         break;5112       } if (e.nodeName === "text") {5113         text = e.textContent;5114       } else {5115         condition = e.nodeName;5116       }5117     }5118 5119     var errorString = "WebSocket stream error: ";5120 5121     if (condition) {5122       errorString += condition;5123     } else {5124       errorString += "unknown";5125     }5126 5127     if (text) {5128       errorString += " - " + condition;5129     }5130 5131     Strophe.error(errorString);5132 5133     // close the connection on stream_error5134     this._conn._changeConnectStatus(connectstatus, condition);5135     this._conn._doDisconnect();5136     return true;5137   },5138 5139   /** PrivateFunction: _reset5140    * Reset the connection.5141    *5142    * This function is called by the reset function of the Strophe Connection.5143    * Is not needed by WebSockets.5144   */5145   _reset: function ()5146   {5147     return;5148   },5149 5150   /** PrivateFunction: _connect5151    * _Private_ function called by Strophe.Connection.connect5152    *5153    * Creates a WebSocket for a connection and assigns Callbacks to it.5154    * Does nothing if there already is a WebSocket.5155   */5156   _connect: function () {5157     // Ensure that there is no open WebSocket from a previous Connection.5158     this._closeSocket();5159 5160     // Create the new WobSocket5161     this.socket = new WebSocket(this._conn.service, "xmpp");5162     this.socket.onopen = this._onOpen.bind(this);5163     this.socket.onerror = this._onError.bind(this);5164     this.socket.onclose = this._onClose.bind(this);5165     this.socket.onmessage = this._connect_cb_wrapper.bind(this);5166   },5167 5168   /** PrivateFunction: _connect_cb5169    * _Private_ function called by Strophe.Connection._connect_cb5170    *5171    * checks for stream:error5172    *5173    * Parameters:5174    *  (Strophe.Request) bodyWrap - The received stanza.5175   */5176   _connect_cb: function(bodyWrap) {5177     var error = this._check_streamerror(bodyWrap, Strophe.Status.CONNFAIL);5178     if (error) {5179       return Strophe.Status.CONNFAIL;5180     }5181   },5182 5183   /** PrivateFunction: _handleStreamStart5184    * _Private_ function that checks the opening <open /> tag for errors.5185    *5186    * Disconnects if there is an error and returns false, true otherwise.5187    *5188    * Parameters:5189    *  (Node) message - Stanza containing the <open /> tag.5190   */5191   _handleStreamStart: function(message) {5192     var error = false;5193 5194     // Check for errors in the <open /> tag5195     var ns = message.getAttribute("");5196     if (typeof ns !== "string") {5197       error = "Missing ";5198     } else if (ns !== Strophe.NS.FRAMING) {5199       error = "Wrong " + ns;5200     }5201 5202     var ver = message.getAttribute("version");5203     if (typeof ver !== "string") {5204       error = "Missing version in <open />";5205     } else if (ver !== "1.0") {5206       error = "Wrong version in <open />: " + ver;5207     }5208 5209     if (error) {5210       this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, error);5211       this._conn._doDisconnect();5212       return false;5213     }5214 5215     return true;5216   },5217 5218   /** PrivateFunction: _connect_cb_wrapper5219    * _Private_ function that handles the first connection messages.5220    *5221    * On receiving an opening stream tag this callback replaces itself with the real5222    * message handler. On receiving a stream error the connection is terminated.5223   */5224   _connect_cb_wrapper: function(message) {5225     if (message.data.indexOf("<open ") === 0 || message.data.indexOf("<?") === 0) {5226       // Strip the 5227       var data = message.data.replace(/^(<\?.*?\?>\s*)*/, "");5228       if (data === '') return;5229 5230       var streamStart = new DOMParser().parseFromString(data, "text/").documentElement;5231       this._conn.5232       this._conn.rawInput(message.data);5233 5234       //_handleStreamSteart will check for 5235       if (this._handleStreamStart(streamStart)) {5236         //_connect_cb will check for stream:error and disconnect on error5237         this._connect_cb(streamStart);5238       }5239     } else if (message.data.indexOf("<close ") === 0) { //'<close 5240       this._conn.rawInput(message.data);5241       this._conn.5242       var see_uri = message.getAttribute("see-other-uri");5243       if (see_uri) {5244         this._conn._changeConnectStatus(Strophe.Status.REDIRECT, "Received see-other-uri, resetting connection");5245         this._conn.reset();5246         this._conn.service = see_uri;5247         this._connect();5248       } else {5249         this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "Received closing stream");5250         this._conn._doDisconnect();5251       }5252     } else {5253       var string = this._streamWrap(message.data);5254       var elem = new DOMParser().parseFromString(string, "text/").documentElement;5255       this.socket.onmessage = this._onMessage.bind(this);5256       this._conn._connect_cb(elem, null, message.data);5257     }5258   },5259 5260   /** PrivateFunction: _disconnect5261    * _Private_ function called by Strophe.Connection.disconnect5262    *5263    * Disconnects and sends a last stanza if one is given5264    *5265    * Parameters:5266    *  (Request) pres - This stanza will be sent before disconnecting.5267   */5268   _disconnect: function (pres)5269   {5270     if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {5271       if (pres) {5272         this._conn.send(pres);5273       }5274       var close = $build("close", { "": Strophe.NS.FRAMING, });5275       this._conn.5276       var closeString = Strophe.serialize(close);5277       this._conn.rawOutput(closeString);5278       try {5279         this.socket.send(closeString);5280       } catch (e) {5281         Strophe.info("Couldn't send <close /> tag.");5282       }5283     }5284     this._conn._doDisconnect();5285   },5286 5287   /** PrivateFunction: _doDisconnect5288    * _Private_ function to disconnect.5289    *5290    * Just closes the Socket for WebSockets5291   */5292   _doDisconnect: function ()5293   {5294     Strophe.info("WebSockets _doDisconnect was called");5295     this._closeSocket();5296   },5297 5298   /** PrivateFunction _streamWrap5299    * _Private_ helper function to wrap a stanza in a <stream> tag.5300    * This is used so Strophe can process stanzas from WebSockets like BOSH5301   */5302   _streamWrap: function (stanza)5303   {5304     return "<wrapper>" + stanza + '</wrapper>';5305   },5306 5307 5308   /** PrivateFunction: _closeSocket5309    * _Private_ function to close the WebSocket.5310    *5311    * Closes the socket if it is still open and deletes it5312   */5313   _closeSocket: function ()5314   {5315     if (this.socket) { try {5316       this.socket.close();5317     } catch (e) {} }5318     this.socket = null;5319   },5320 5321   /** PrivateFunction: _emptyQueue5322    * _Private_ function to check if the message queue is empty.5323    *5324    * Returns:5325    *  True, because WebSocket messages are send immediately after queueing.5326   */5327   _emptyQueue: function ()5328   {5329     return true;5330   },5331 5332   /** PrivateFunction: _onClose5333    * _Private_ function to handle websockets closing.5334    *5335    * Nothing to do here for WebSockets5336   */5337   _onClose: function() {5338     if(this._conn.connected && !this._conn.disconnecting) {5339       Strophe.error("Websocket closed unexcectedly");5340       this._conn._doDisconnect();5341     } else {5342       Strophe.info("Websocket closed");5343     }5344   },5345 5346   /** PrivateFunction: _no_auth_received5347    *5348    * Called on stream start/restart when no stream:features5349    * has been received.5350   */5351   _no_auth_received: function (_callback)5352   {5353     Strophe.error("Server did not send any auth methods");5354     this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "Server did not send any auth methods");5355     if (_callback) {5356       _callback = _callback.bind(this._conn);5357       _callback();5358     }5359     this._conn._doDisconnect();5360   },5361 5362   /** PrivateFunction: _onDisconnectTimeout5363    * _Private_ timeout handler for handling non-graceful disconnection.5364    *5365    * This does nothing for WebSockets5366   */5367   _onDisconnectTimeout: function () {},5368 5369   /** PrivateFunction: _abortAllRequests5370    * _Private_ helper function that makes sure all pending requests are aborted.5371   */5372   _abortAllRequests: function () {},5373 5374   /** PrivateFunction: _onError5375    * _Private_ function to handle websockets errors.5376    *5377    * Parameters:5378    * (Object) error - The websocket error.5379   */5380   _onError: function(error) {5381     Strophe.error("Websocket error " + error);5382     this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "The WebSocket connection could not be established was disconnected.");5383     this._disconnect();5384   },5385 5386   /** PrivateFunction: _onIdle5387    * _Private_ function called by Strophe.Connection._onIdle5388    *5389    * sends all queued stanzas5390   */5391   _onIdle: function () {5392     var data = this._conn._data;5393     if (data.length > 0 && !this._conn.paused) {5394       for (var i = 0; i < data.length; i++) {5395         if (data[i] !== null) {5396           var stanza, rawStanza;5397           if (data[i] === "restart") {5398             stanza = this._buildStream().tree();5399           } else {5400             stanza = data[i];5401           }5402           rawStanza = Strophe.serialize(stanza);5403           this._conn.5404           this._conn.rawOutput(rawStanza);5405           this.socket.send(rawStanza);5406         }5407       }5408       this._conn._data = [];5409     }5410   },5411 5412   /** PrivateFunction: _onMessage5413    * _Private_ function to handle websockets messages.5414    *5415    * This function parses each of the messages as if they are full documents. [TODO : We may actually want to use a SAX Push parser].5416    *5417    * Since all XMPP traffic starts with "<stream:stream version='1.0' http://etherx.jabber.org/streams' id='3697395463' from='SERVER'>"5418    * The first stanza will always fail to be parsed...5419    * Addtionnaly, the seconds stanza will always be a <stream:features> with the stream NS defined in the previous stanza... so we need to 'force' the inclusion of the NS in this stanza!5420    *5421    * Parameters:5422    * (string) message - The websocket message.5423   */5424   _onMessage: function(message) {5425     var elem, data;5426     // check for closing stream5427     var close = '<close ';5428     if (message.data === close) {5429       this._conn.rawInput(close);5430       this._conn.5431       if (!this._conn.disconnecting) {5432         this._conn._doDisconnect();5433       }5434       return;5435     } else if (message.data.search("<open ") === 0) {5436       // This handles stream restarts5437       elem = new DOMParser().parseFromString(message.data, "text/").documentElement;5438 5439       if (!this._handleStreamStart(elem)) {5440         return;5441       }5442     } else {5443       data = this._streamWrap(message.data);5444       elem = new DOMParser().parseFromString(data, "text/").documentElement;5445     }5446 5447     if (this._check_streamerror(elem, Strophe.Status.ERROR)) {5448       return;5449     }5450 5451     //handle unavailable presence stanza before disconnecting5452     if (this._conn.disconnecting &&5453         elem.firstChild.nodeName === "presence" &&5454         elem.firstChild.getAttribute("type") === "unavailable") {5455       this._conn.5456       this._conn.rawInput(Strophe.serialize(elem));5457       // if we are already disconnecting we will ignore the unavailable stanza and5458       // wait for the </stream:stream> tag before we close the connection5459       return;5460     }5461     this._conn._dataRecv(elem, message.data);5462   },5463 5464   /** PrivateFunction: _onOpen5465    * _Private_ function to handle websockets connection setup.5466    *5467    * The opening stream tag is sent here.5468   */5469   _onOpen: function() {5470     Strophe.info("Websocket open");5471     var start = this._buildStream();5472     this._conn.5473 5474     var startString = Strophe.serialize(start);5475     this._conn.rawOutput(startString);5476     this.socket.send(startString);5477   },5478 5479   /** PrivateFunction: _reqToData5480    * _Private_ function to get a stanza out of a request.5481    *5482    * WebSockets don't use requests, so the passed argument is just returned.5483    *5484    * Parameters:5485    *  (Object) stanza - The stanza.5486    *5487    * Returns:5488    *  The stanza that was passed.5489   */5490   _reqToData: function (stanza)5491   {5492     return stanza;5493   },5494 5495   /** PrivateFunction: _send5496    * _Private_ part of the Connection.send function for WebSocket5497    *5498    * Just flushes the messages that are in the queue5499   */5500   _send: function () {5501     this._conn.flush();5502   },5503 5504   /** PrivateFunction: _sendRestart5505    *5506    * Send an xmpp:restart stanza.5507   */5508   _sendRestart: function ()5509   {5510     clearTimeout(this._conn._idleTimeout);5511     this._conn._onIdle.bind(this._conn)();5512   }5513 };5514 return Strophe;5515 }));5516 5517 /* jshint ignore:start */5518 if (callback) {5519   return callback(Strophe, $build, $msg, $iq, $pres);5520 }5521 5522 5523 })(function (Strophe, build, msg, iq, pres) {5524   window.Strophe = Strophe;5525   window.$build = build;5526   window.$msg = msg;5527   window.$iq = iq;5528   window.$pres = pres;5529 });5530 /* jshint ignore:end */5531 5532 5533 5534 /*utf*/5535 function utf16to8(str) { 5536   var out, i, len, c; 5537   out = ""; 5538   len = str.length; 5539   for(i = 0; i < len; i++) { 5540   c = str.charCodeAt(i); 5541   if ((c >= 0x0001) && (c <= 0x007F)) { 5542   out += str.charAt(i); 5543   } else if (c > 0x07FF) { 5544   out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F)); 5545   out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F)); 5546   out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); 5547   } else { 5548   out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F)); 5549   out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F)); 5550   } 5551   } 5552   return out; 5553   } 5554   //utf-16转utf-8 5555   function utf8to16(str) { 5556   var out, i, len, c; 5557   var char2, char3; 5558   out = ""; 5559   len = str.length; 5560   i = 0; 5561   while(i < len) { 5562   c = str.charCodeAt(i++); 5563   switch(c >> 4) 5564   { 5565   case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: 5566   // 0xxxxxxx 5567   out += str.charAt(i-1); 5568   break; 5569   case 12: case 13: 5570   // 110x xxxx 10xx xxxx 5571   char2 = str.charCodeAt(i++); 5572   out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); 5573   break; 5574   case 14: 5575   // 1110 xxxx 10xx xxxx 10xx xxxx 5576   char2 = str.charCodeAt(i++); 5577   char3 = str.charCodeAt(i++); 5578   out += String.fromCharCode(((c & 0x0F) << 12) | 5579   ((char2 & 0x3F) << 6) | 5580   ((char3 & 0x3F) << 0)); 5581   break; 5582   } 5583   } 5584   return out; 5585   } 

View Code

 2、后台插件进行编码解析

Web界面

var iq=$iq({id:"iqwd_factorylogin",type:"get"}).
   c("query",{   c("item").
   c("username").cht("张三");

Openfire插件

String username= Base64.getFromBase64(item.elementText("username"));

 

 1 package com.plugin.common; 2  3 import java.io.UnsupportedEncodingException; 4  5 import sun.misc.BASE64Decoder; 6 import sun.misc.BASE64Encoder; 7  8 public class Base64 { 9   // 加密 10   public static String getBase64(String str) { 11     byte[] b = null; 12     String s = null; 13     try { 14       b = str.getBytes("utf-8"); 15     } catch (UnsupportedEncodingException e) { 16       e.printStackTrace(); 17     } 18     if (b != null) { 19       s = new BASE64Encoder().encode(b); 20     } 21     return s; 22   } 23  24   // 解密 25   public static String getFromBase64(String s) { 26     byte[] b = null; 27     String result = null; 28     if (s != null) { 29       BASE64Decoder decoder = new BASE64Decoder(); 30       try { 31         b = decoder.decodeBuffer(s); 32         result = new String(b, "utf-8"); 33       } catch (Exception e) { 34         e.printStackTrace(); 35       } 36     } 37     return result; 38   } 39 40 }

View Code