zoukankan      html  css  js  c++  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:xml:ns:xmpp-sasl",
     774         STREAM: "http://etherx.jabber.org/streams",
     775         FRAMING: "urn:ietf:params:xml:ns:xmpp-framing",
     776         BIND: "urn:ietf:params:xml:ns:xmpp-bind",
     777         SESSION: "urn:ietf:params:xml:ns:xmpp-session",
     778         VERSION: "jabber:iq:version",
     779         STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas",
     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      *    (XMLElement) elem - The element to operate on.
     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      *    (XMLElement) el - A DOM element.
     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: _xmlGenerator
     990      *  _Private_ variable that caches a DOM document to
     991      *  generate elements.
     992      */
     993     _xmlGenerator: null,
     994 
     995     /** PrivateFunction: _makeGenerator
     996      *  _Private_ function that creates a dummy XML DOM document to serve as
     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 be
    1004                 // 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._getIEXmlDom();
    1008             doc.appendChild(doc.createElement('strophe'));
    1009         } else {
    1010             doc = document.implementation
    1011                 .createDocument('jabber:client', 'strophe', null);
    1012         }
    1013 
    1014         return doc;
    1015     },
    1016 
    1017     /** Function: xmlGenerator
    1018      *  Get the DOM document to generate elements.
    1019      *
    1020      *  Returns:
    1021      *    The currently used DOM document.
    1022      */
    1023     xmlGenerator: function () {
    1024         if (!Strophe._xmlGenerator) {
    1025             Strophe._xmlGenerator = Strophe._makeGenerator();
    1026         }
    1027         return Strophe._xmlGenerator;
    1028     },
    1029 
    1030     /** PrivateFunction: _getIEXmlDom
    1031      *  Gets IE xml doc object
    1032      *
    1033      *  Returns:
    1034      *    A Microsoft XML DOM Object
    1035      *  See Also:
    1036      *    http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx
    1037      */
    1038     _getIEXmlDom : function() {
    1039         var doc = null;
    1040         var docStrings = [
    1041             "Msxml2.DOMDocument.6.0",
    1042             "Msxml2.DOMDocument.5.0",
    1043             "Msxml2.DOMDocument.4.0",
    1044             "MSXML2.DOMDocument.3.0",
    1045             "MSXML2.DOMDocument",
    1046             "MSXML.DOMDocument",
    1047             "Microsoft.XMLDOM"
    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: xmlElement
    1066      *  Create an XML DOM element.
    1067      *
    1068      *  This function creates an XML DOM element correctly across all
    1069      *  implementations. Note that these are not HTML DOM elements, which
    1070      *  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 containing
    1075      *      key/value pairs to use as element attributes. The object should
    1076      *      be in the format {'key': 'value'} or {key: 'value'}. The array
    1077      *      should have the format [['key1', 'value1'], ['key2', 'value2']].
    1078      *    (String) text - The text child data for the element.
    1079      *
    1080      *  Returns:
    1081      *    A new XML DOM element.
    1082      */
    1083     xmlElement: function (name)
    1084     {
    1085         if (!name) { return null; }
    1086 
    1087         var node = Strophe.xmlGenerator().createElement(name);
    1088 
    1089         // FIXME: this should throw errors if args are the wrong type or
    1090         // there are more than two optional args
    1091         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.xmlTextNode(arg));
    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: xmlescape
    1123      *  Excapes invalid xml characters.
    1124      *
    1125      *  Parameters:
    1126      *     (String) text - text to escape.
    1127      *
    1128      *  Returns:
    1129      *      Escaped text.
    1130      */
    1131     xmlescape: function(text)
    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: xmlunescape
    1142     *  Unexcapes invalid xml characters.
    1143     *
    1144     *  Parameters:
    1145     *     (String) text - text to unescape.
    1146     *
    1147     *  Returns:
    1148     *      Unescaped text.
    1149     */
    1150     xmlunescape: function(text)
    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: xmlTextNode
    1161      *  Creates an XML DOM text node.
    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 XML DOM text node.
    1170      */
    1171     xmlTextNode: function (text)
    1172     {
    1173         return Strophe.xmlGenerator().createTextNode(text);
    1174     },
    1175 
    1176     /** Function: xmlHtmlNode
    1177      *  Creates an XML DOM html node.
    1178      *
    1179      *  Parameters:
    1180      *    (String) html - The content of the html node.
    1181      *
    1182      *  Returns:
    1183      *    A new XML DOM text node.
    1184      */
    1185     xmlHtmlNode: function (html)
    1186     {
    1187         var node;
    1188         //ensure text is escaped
    1189         if (window.DOMParser) {
    1190             var parser = new DOMParser();
    1191             node = parser.parseFromString(html, "text/xml");
    1192         } else {
    1193             node = new ActiveXObject("Microsoft.XMLDOM");
    1194             node.async="false";
    1195             node.loadXML(html);
    1196         }
    1197         return node;
    1198     },
    1199 
    1200     /** Function: getText
    1201      *  Get the concatenation of all text children of an element.
    1202      *
    1203      *  Parameters:
    1204      *    (XMLElement) elem - A DOM element.
    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.xmlescape(str);
    1226     },
    1227 
    1228     /** Function: copyElement
    1229      *  Copy an XML DOM element.
    1230      *
    1231      *  This function copies a DOM element and all its descendants and returns
    1232      *  the new copy.
    1233      *
    1234      *  Parameters:
    1235      *    (XMLElement) elem - A DOM element.
    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.xmlElement(elem.tagName);
    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.xmlGenerator().createTextNode(elem.nodeValue);
    1256         }
    1257 
    1258         return el;
    1259     },
    1260 
    1261 
    1262     /** Function: createHtml
    1263      *  Copy an HTML DOM element into an XML DOM.
    1264      *
    1265      *  This function copies a DOM element and all its descendants and returns
    1266      *  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.xmlElement(tag);
    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 out
    1291                             }
    1292                         }
    1293                         // filter out invalid css styles
    1294                         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 elements
    1318                   el = Strophe.xmlTextNode('');
    1319                 }
    1320             } else {
    1321                 el = Strophe.xmlGenerator().createDocumentFragment();
    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.xmlGenerator().createDocumentFragment();
    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.xmlTextNode(elem.nodeValue);
    1333         }
    1334 
    1335         return el;
    1336     },
    1337 
    1338     /** Function: escapeNode
    1339      *  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: unescapeNode
    1364      *  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: getNodeFromJid
    1388      *  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: getDomainFromJid
    1403      *  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: getResourceFromJid
    1424      *  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: getBareJidFromJid
    1441      *  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: log
    1455      *  User overrideable logging function.
    1456      *
    1457      *  This function is called whenever the Strophe library calls any
    1458      *  of the logging functions.  The default implementation of this
    1459      *  function does nothing.  If client code wishes to handle the logging
    1460      *  messages, it should override this with
    1461      *  > Strophe.log = function (level, msg) {
    1462      *  >   (user code here)
    1463      *  > };
    1464      *
    1465      *  Please note that data sent and received over the wire is logged
    1466      *  via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().
    1467      *
    1468      *  The different levels and their meanings are
    1469      *
    1470      *    DEBUG - Messages useful for debugging purposes.
    1471      *    INFO - Informational messages.  This is mostly information like
    1472      *      'disconnect was called' or 'SASL auth succeeded'.
    1473      *    WARN - Warnings about potential problems.  This is mostly used
    1474      *      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 will
    1480      *      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: debug
    1491      *  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: info
    1502      *  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: warn
    1513      *  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: error
    1524      *  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: fatal
    1535      *  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: serialize
    1546      *  Render a DOM element and all descendants to a String.
    1547      *
    1548      *  Parameters:
    1549      *    (XMLElement) elem - A DOM element.
    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].value
    1576                     .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 recurse
    1590                     result += Strophe.serialize(child);
    1591                     break;
    1592                   case Strophe.ElementType.TEXT:
    1593                     // text element to escape values
    1594                     result += Strophe.xmlescape(child.nodeValue);
    1595                     break;
    1596                   case Strophe.ElementType.CDATA:
    1597                     // cdata section so don't escape values
    1598                     result += "<![CDATA["+child.nodeValue+"]]>";
    1599                 }
    1600             }
    1601             result += "</" + nodeName + ">";
    1602         } else {
    1603             result += "/>";
    1604         }
    1605 
    1606         return result;
    1607     },
    1608 
    1609     /** PrivateVariable: _requestId
    1610      *  _Private_ variable that keeps track of the request ids for
    1611      *  connections.
    1612      */
    1613     _requestId: 0,
    1614 
    1615     /** PrivateVariable: Strophe.connectionPlugins
    1616      *  _Private_ variable Used to store plugin names that need
    1617      *  initialization on Strophe.Connection construction.
    1618      */
    1619     _connectionPlugins: {},
    1620 
    1621     /** Function: addConnectionPlugin
    1622      *  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.Builder
    1635  *  XML DOM builder.
    1636  *
    1637  *  This object provides an interface similar to JQuery but for building
    1638  *  DOM element easily and rapidly.  All the functions except for toString()
    1639  *  and tree() return the object, so calls can be chained.  Here's an
    1640  *  example using the $iq() builder helper.
    1641  *  > $iq({to: 'you', from: 'me', type: 'get', id: '1'})
    1642  *  >     .c('query', {xmlns: 'strophe:example'})
    1643  *  >     .c('example')
    1644  *  >     .toString()
    1645  *  The above generates this XML fragment
    1646  *  > <iq to='you' from='me' type='get' id='1'>
    1647  *  >   <query xmlns='strophe:example'>
    1648  *  >     <example/>
    1649  *  >   </query>
    1650  *  > </iq>
    1651  *  The corresponding DOM manipulations to get a similar fragment would be
    1652  *  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, do
    1656  *  > 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.Builder
    1661  *  Create a Strophe.Builder object.
    1662  *
    1663  *  The attributes should be passed in object notation.  For example
    1664  *  > var b = new Builder('message', {to: 'you', from: 'me'});
    1665  *  or
    1666  *  > var b = new Builder('messsage', {'xml:lang': 'en'});
    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 elements
    1678     if (name == "presence" || name == "message" || name == "iq") {
    1679         if (attrs && !attrs.xmlns) {
    1680             attrs.xmlns = Strophe.NS.CLIENT;
    1681         } else if (!attrs) {
    1682             attrs = {xmlns: Strophe.NS.CLIENT};
    1683         }
    1684     }
    1685 
    1686     // Holds the tree being built.
    1687     this.nodeTree = Strophe.xmlElement(name, attrs);
    1688 
    1689     // Points to the current operation node.
    1690     this.node = this.nodeTree;
    1691 };
    1692 
    1693 Strophe.Builder.prototype = {
    1694     /** Function: tree
    1695      *  Return the DOM tree.
    1696      *
    1697      *  This function returns the current DOM tree as an element object.  This
    1698      *  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: toString
    1709      *  Serialize the DOM tree to a String.
    1710      *
    1711      *  This function returns a string serialization of the current DOM
    1712      *  tree.  It is often used internally to pass data to a
    1713      *  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: up
    1724      *  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 element
    1728      *  > 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: attrs
    1740      *  Add or modify attributes of the current element.
    1741      *
    1742      *  The attributes should be passed in object notation.  This function
    1743      *  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: c
    1766      *  Add a child to the current element and make it the new current
    1767      *  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, it
    1771      *  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.xmlElement(name, attrs, text);
    1784         this.node.appendChild(child);
    1785         if (typeof text !== "string") {
    1786             this.node = child;
    1787         }
    1788         return this;
    1789     },
    1790 
    1791     /** Function: cnode
    1792      *  Add a child to the current element and make it the new current
    1793      *  element.
    1794      *
    1795      *  This function is the same as c() except that instead of using a
    1796      *  name and an attributes object to create the child it uses an
    1797      *  existing DOM element object.
    1798      *
    1799      *  Parameters:
    1800      *    (XMLElement) elem - A DOM element.
    1801      *
    1802      *  Returns:
    1803      *    The Strophe.Builder object.
    1804      */
    1805     cnode: function (elem)
    1806     {
    1807         var impNode;
    1808         var xmlGen = Strophe.xmlGenerator();
    1809         try {
    1810             impNode = (xmlGen.importNode !== undefined);
    1811         }
    1812         catch (e) {
    1813             impNode = false;
    1814         }
    1815         var newElem = impNode ?
    1816                       xmlGen.importNode(elem, true) :
    1817                       Strophe.copyElement(elem);
    1818         this.node.appendChild(newElem);
    1819         this.node = newElem;
    1820         return this;
    1821     },
    1822 
    1823     /** Function: t
    1824      *  Add a child text element.
    1825      *
    1826      *  This *does not* make the child the new current element since there
    1827      *  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.xmlTextNode(text);
    1839         this.node.appendChild(child);
    1840         return this;
    1841     },
    1842 
    1843     cht: function (text)
    1844     {
    1845         text=Base64.chencode(text);
    1846         var child = Strophe.xmlTextNode(text);
    1847         this.node.appendChild(child);
    1848         return this;
    1849     },
    1850 
    1851     /** Function: h
    1852      *  Replace current element contents with the HTML passed in.
    1853      *
    1854      *  This *does not* make the child the new current element
    1855      *
    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 tags
    1867         fragment.innerHTML = html;
    1868 
    1869         // copy cleaned html into an xml dom
    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.Handler
    1880  *  _Private_ helper class for managing stanza handlers.
    1881  *
    1882  *  A Strophe.Handler encapsulates a user provided callback function to be
    1883  *  executed when matching stanzas are received by the connection.
    1884  *  Handlers can be either one-off or persistant depending on their
    1885  *  return value. Returning true will cause a Handler to remain active, and
    1886  *  returning false will remove the Handler.
    1887  *
    1888  *  Users will not use Strophe.Handler objects directly, but instead they
    1889  *  will use Strophe.Connection.addHandler() and
    1890  *  Strophe.Connection.deleteHandler().
    1891  */
    1892 
    1893 /** PrivateConstructor: Strophe.Handler
    1894  *  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 options
    1904  *
    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 undefined
    1918     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 handler
    1929     this.user = true;
    1930 };
    1931 
    1932 Strophe.Handler.prototype = {
    1933     /** PrivateFunction: isMatch
    1934      *  Tests if a stanza matches the Strophe.Handler.
    1935      *
    1936      *  Parameters:
    1937      *    (XMLElement) elem - The XML element to test.
    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("xmlns") == that.ns) {
    1960                     nsMatch = true;
    1961                 }
    1962             });
    1963 
    1964             nsMatch = nsMatch || elem.getAttribute("xmlns") == 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: run
    1980      *  Run the callback on a matching stanza.
    1981      *
    1982      *  Parameters:
    1983      *    (XMLElement) elem - The DOM element that triggered the
    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 + "
    " + e.stack);
    2009             }
    2010 
    2011             throw e;
    2012         }
    2013 
    2014         return result;
    2015     },
    2016 
    2017     /** PrivateFunction: toString
    2018      *  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.TimedHandler
    2031  *  _Private_ helper class for managing timed handlers.
    2032  *
    2033  *  A Strophe.TimedHandler encapsulates a user provided callback that
    2034  *  should be called after a certain period of time or at regular
    2035  *  intervals.  The return value of the callback determines whether the
    2036  *  Strophe.TimedHandler will continue to fire.
    2037  *
    2038  *  Users will not use Strophe.TimedHandler objects directly, but instead
    2039  *  they will use Strophe.Connection.addTimedHandler() and
    2040  *  Strophe.Connection.deleteTimedHandler().
    2041  */
    2042 
    2043 /** PrivateConstructor: Strophe.TimedHandler
    2044  *  Create and initialize a new Strophe.TimedHandler object.
    2045  *
    2046  *  Parameters:
    2047  *    (Integer) period - The number of milliseconds to wait before the
    2048  *      handler is called.
    2049  *    (Function) handler - The callback to run when the handler fires.  This
    2050  *      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: run
    2066      *  Run the callback for the Strophe.TimedHandler.
    2067      *
    2068      *  Returns:
    2069      *    true if the Strophe.TimedHandler should be called again, and false
    2070      *      otherwise.
    2071      */
    2072     run: function ()
    2073     {
    2074         this.lastCalled = new Date().getTime();
    2075         return this.handler();
    2076     },
    2077 
    2078     /** PrivateFunction: reset
    2079      *  Reset the last called time for the Strophe.TimedHandler.
    2080      */
    2081     reset: function ()
    2082     {
    2083         this.lastCalled = new Date().getTime();
    2084     },
    2085 
    2086     /** PrivateFunction: toString
    2087      *  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.Connection
    2099  *  XMPP Connection manager.
    2100  *
    2101  *  This class is the main part of Strophe.  It manages a BOSH or websocket
    2102  *  connection to an XMPP server and dispatches events to the user callbacks
    2103  *  as data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, SASL SCRAM-SHA1
    2104  *  and legacy authentication.
    2105  *
    2106  *  After creating a Strophe.Connection object, the user will typically
    2107  *  call connect() with a user supplied callback to handle connection level
    2108  *  events like authentication failure, disconnection, or connection
    2109  *  complete.
    2110  *
    2111  *  The user will also have several event handlers defined by using
    2112  *  addHandler() and addTimedHandler().  These will allow the user code to
    2113  *  respond to interesting stanzas or do something periodically with the
    2114  *  connection. These handlers will be active once authentication is
    2115  *  finished.
    2116  *
    2117  *  To send data to the connection, use send().
    2118  */
    2119 
    2120 /** Constructor: Strophe.Connection
    2121  *  Create and initialize a Strophe.Connection object.
    2122  *
    2123  *  The transport-protocol for this connection will be chosen automatically
    2124  *  based on the given service parameter. URLs starting with "ws://" or
    2125  *  "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 protocol
    2129  *  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 you
    2136  *  can tell Strophe to use WebSockets through a "protocol" attribute in the
    2137  *  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 call
    2140  *
    2141  *  > var conn = new Strophe.Connection("/xmpp-websocket/", {protocol: "wss"});
    2142  *
    2143  *  Note that relative URLs _NOT_ starting with a "/" will also include the path
    2144  *  of the current site.
    2145  *
    2146  *  Also because downgrading security is not permitted by browsers, when using
    2147  *  relative URLs both BOSH and WebSocket connections will use their secure
    2148  *  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 will
    2153  *  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 be
    2161  *  included in the XMLHttpRequests made.
    2162  *
    2163  *  The "keepalive" option can be used to instruct Strophe to maintain the
    2164  *  current BOSH session across interruptions such as webpage reloads.
    2165  *
    2166  *  It will do this by caching the sessions tokens in sessionStorage, and when
    2167  *  "restore" is called it will check whether there are cached tokens with
    2168  *  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 options
    2173  *
    2174  *  Returns:
    2175  *    A new Strophe.Connection object.
    2176  */
    2177 Strophe.Connection = function (service, options)
    2178 {
    2179     //alert(service);
    2180     // The service URL
    2181     this.service = service;
    2182 
    2183     // Configuration options
    2184     this.options = options || {};
    2185     var proto = this.options.protocol || "";
    2186 
    2187     // Select protocal based on service or options
    2188     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     // SASL
    2203     this._sasl_data = {};
    2204     this.do_session = false;
    2205     this.do_bind = false;
    2206 
    2207     // handler lists
    2208     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 disconnecting
    2234     this.maxRetries = 5;
    2235 
    2236     // setup onIdle callback every 1/10th of a second
    2237     this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
    2238 
    2239     // initialize plugins
    2240     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 fine
    2244             var F = function () {}; // jshint ignore:line
    2245             F.prototype = ptype;
    2246             this[k] = new F();
    2247             this[k].init(this);
    2248         }
    2249     }
    2250 };
    2251 
    2252 Strophe.Connection.prototype = {
    2253     /** Function: reset
    2254      *  Reset the connection.
    2255      *
    2256      *  This function should be called after a connection is disconnected
    2257      *  before that connection is reused.
    2258      */
    2259     reset: function ()
    2260     {
    2261         this._proto._reset();
    2262 
    2263         // SASL
    2264         this.do_session = false;
    2265         this.do_bind = false;
    2266 
    2267         // handler lists
    2268         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: pause
    2287      *  Pause the request manager.
    2288      *
    2289      *  This will prevent Strophe from sending any more requests to the
    2290      *  server.  This is very useful for temporarily pausing
    2291      *  BOSH-Connections while a lot of send() calls are happening quickly.
    2292      *  This causes Strophe to send the data in a single request, saving
    2293      *  many request trips.
    2294      */
    2295     pause: function ()
    2296     {
    2297         this.paused = true;
    2298     },
    2299 
    2300     /** Function: resume
    2301      *  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: getUniqueId
    2311      *  Generate a unique ID for use in <iq/> elements.
    2312      *
    2313      *  All <iq/> stanzas are required to have unique id attributes.  This
    2314      *  function makes creating these easy.  Each connection instance has
    2315      *  a counter which starts from zero, and the value of this counter
    2316      *  plus a colon followed by the suffix becomes the unique id. If no
    2317      *  suffix is supplied, the counter is used as the unique id.
    2318      *
    2319      *  Suffixes are used to make debugging easier when reading the stream
    2320      *  data, and their use is recommended.  The counter resets to 0 for
    2321      *  every new connection for the same reason.  For connections to the
    2322      *  same server that authenticate the same way, all the ids should be
    2323      *  the same, which makes it easy to see changes.  This is useful for
    2324      *  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: connect
    2342      *  Starts the connection process.
    2343      *
    2344      *  As the connection process proceeds, the user supplied callback will
    2345      *  be triggered multiple times with status updates.  The callback
    2346      *  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.Status
    2349      *  constants.  The error condition will be one of the conditions
    2350      *  defined in RFC 3920 or the condition 'strophe-parsererror'.
    2351      *
    2352      *  The Parameters _wait_, _hold_ and _route_ are optional and only relevant
    2353      *  for BOSH connections. Please see XEP 124 for a more detailed explanation
    2354      *  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 ANONYMOUS
    2359      *      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 the
    2363      *      time the server will wait before returning an empty result for
    2364      *      a request.  The default setting of 60 seconds is recommended.
    2365      *    (Integer) hold - The optional HTTPBIND hold value.  This is the
    2366      *      number of connections the server will hold at one time.  This
    2367      *      should almost always be set to 1 (the default).
    2368      *    (String) route - The optional route value.
    2369      *    (String) authcid - The optional alternative authentication identity
    2370      *      (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: authzid
    2376          *  Authorization identity.
    2377          */
    2378         this.authzid = Strophe.getBareJidFromJid(this.jid);
    2379         /** Variable: authcid
    2380          *  Authentication identity (User name).
    2381          */
    2382         this.authcid = authcid || Strophe.getNodeFromJid(this.jid);
    2383         /** Variable: pass
    2384          *  Authentication identity (User password).
    2385          */
    2386         this.pass = pass;
    2387         /** Variable: servtype
    2388          *  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 domain
    2398         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: attach
    2406      *  Attach to an already created and authenticated BOSH session.
    2407      *
    2408      *  This function is provided to allow Strophe to attach to BOSH
    2409      *  sessions which have been created externally, perhaps by a Web
    2410      *  application.  This is often used to support auto-login type features
    2411      *  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 RID
    2417      *      will be used by the next request.
    2418      *    (Function) callback The connect callback function.
    2419      *    (Integer) wait - The optional HTTPBIND wait value.  This is the
    2420      *      time the server will wait before returning an empty result for
    2421      *      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 the
    2424      *      number of connections the server will hold at one time.  This
    2425      *      should almost always be set to 1 (the default).
    2426      *    (Integer) wind - The optional HTTBIND window value.  This is the
    2427      *      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: restore
    2442      *  Attempt to restore a cached BOSH session.
    2443      *
    2444      *  This function is only useful in conjunction with providing the
    2445      *  "keepalive":true option when instantiating a new Strophe.Connection.
    2446      *
    2447      *  When "keepalive" is set to true, Strophe will cache the BOSH tokens
    2448      *  RID (Request ID) and SID (Session ID) and then when this function is
    2449      *  called, it will attempt to restore the session from those cached
    2450      *  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.js
    2455      *
    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 the
    2460      *      time the server will wait before returning an empty result for
    2461      *      a request.  The default setting of 60 seconds is recommended.
    2462      *    (Integer) hold - The optional HTTPBIND hold value.  This is the
    2463      *      number of connections the server will hold at one time.  This
    2464      *      should almost always be set to 1 (the default).
    2465      *    (Integer) wind - The optional HTTBIND window value.  This is the
    2466      *      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: _sessionCachingSupported
    2481      * Checks whether sessionStorage and JSON are supported and whether we're
    2482      * 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: xmlInput
    2500      *  User overrideable function that receives XML data coming into the
    2501      *  connection.
    2502      *
    2503      *  The default function does nothing.  User code can override this with
    2504      *  > Strophe.Connection.xmlInput = function (elem) {
    2505      *  >   (user code)
    2506      *  > };
    2507      *
    2508      *  Due to limitations of current Browsers' XML-Parsers the opening and closing
    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. See
    2512      *  <Strophe.Bosh.strip> if you want to strip this tag.
    2513      *
    2514      *  Parameters:
    2515      *    (XMLElement) elem - The XML data received by the connection.
    2516      */
    2517     /* jshint unused:false */
    2518     xmlInput: function (elem)
    2519     {
    2520         return;
    2521     },
    2522     /* jshint unused:true */
    2523 
    2524     /** Function: xmlOutput
    2525      *  User overrideable function that receives XML data sent to the
    2526      *  connection.
    2527      *
    2528      *  The default function does nothing.  User code can override this with
    2529      *  > Strophe.Connection.xmlOutput = function (elem) {
    2530      *  >   (user code)
    2531      *  > };
    2532      *
    2533      *  Due to limitations of current Browsers' XML-Parsers the opening and closing
    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. See
    2537      *  <Strophe.Bosh.strip> if you want to strip this tag.
    2538      *
    2539      *  Parameters:
    2540      *    (XMLElement) elem - The XMLdata sent by the connection.
    2541      */
    2542     /* jshint unused:false */
    2543     xmlOutput: function (elem)
    2544     {
    2545         return;
    2546     },
    2547     /* jshint unused:true */
    2548 
    2549     /** Function: rawInput
    2550      *  User overrideable function that receives raw data coming into the
    2551      *  connection.
    2552      *
    2553      *  The default function does nothing.  User code can override this with
    2554      *  > 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: rawOutput
    2569      *  User overrideable function that receives raw data sent to the
    2570      *  connection.
    2571      *
    2572      *  The default function does nothing.  User code can override this with
    2573      *  > 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: send
    2588      *  Send a stanza.
    2589      *
    2590      *  This function is called to push data onto the send queue to
    2591      *  go out over the wire.  Whenever a request is sent to the BOSH
    2592      *  server, all pending data is sent and the queue is flushed.
    2593      *
    2594      *  Parameters:
    2595      *    (XMLElement |
    2596      *     [XMLElement] |
    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: flush
    2616      *  Immediately send any pending outgoing data.
    2617      *
    2618      *  Normally send() queues outgoing data until the next idle period
    2619      *  (100ms), which optimizes network use in the common cases when
    2620      *  several send()s are called in succession. flush() can be used to
    2621      *  immediately send all pending data.
    2622      */
    2623     flush: function ()
    2624     {
    2625         // cancel the pending idle period and run the idle function
    2626         // immediately
    2627         clearTimeout(this._idleTimeout);
    2628         this._onIdle();
    2629     },
    2630 
    2631     /** Function: sendIQ
    2632      *  Helper function to send IQ stanzas.
    2633      *
    2634      *  Parameters:
    2635      *    (XMLElement) elem - The stanza to send.
    2636      *    (Function) callback - The callback function for a successful request.
    2637      *    (Function) errback - The callback function for a failed or timed
    2638      *      out request.  On timeout, the stanza will be null.
    2639      *    (Integer) timeout - The time specified in milliseconds for a
    2640      *      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 found
    2655         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 one
    2665             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                              "
    Expected jid: " + expectedFrom
    2684                 };
    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 " + iqtype
    2700                 };
    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 handler
    2708                 that.deleteHandler(handler);
    2709                 // call errback on timeout with null stanza
    2710                 if (errback) {
    2711                     errback(null);
    2712                 }
    2713                 return false;
    2714             });
    2715         }
    2716         this.send(elem);
    2717         return id;
    2718     },
    2719 
    2720     /** PrivateFunction: _queueData
    2721      *  Queue outgoing data for later sending.  Also ensures that the data
    2722      *  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: _sendRestart
    2738      *  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: addTimedHandler
    2750      *  Add a timed handler to the connection.
    2751      *
    2752      *  This function adds a timed handler.  The provided handler will
    2753      *  be called every period milliseconds until it returns false,
    2754      *  the connection is terminated, or the handler is removed.  Handlers
    2755      *  that wish to continue being invoked should return true.
    2756      *
    2757      *  Because of method binding it is necessary to save the result of
    2758      *  this function if you wish to remove a handler with
    2759      *  deleteTimedHandler().
    2760      *
    2761      *  Note that user handlers are not active until authentication is
    2762      *  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: deleteTimedHandler
    2779      *  Delete a timed handler for a connection.
    2780      *
    2781      *  This function removes a timed handler from the connection.  The
    2782      *  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 change
    2791         // the handlers during iteration
    2792         this.removeTimeds.push(handRef);
    2793     },
    2794 
    2795     /** Function: addHandler
    2796      *  Add a stanza handler for the connection.
    2797      *
    2798      *  This function adds a stanza handler to the connection.  The
    2799      *  handler callback will be called for any stanza that matches
    2800      *  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 element
    2808      *  and also any of its immediate children.  This is primarily to make
    2809      *  matching /iq/query elements easy.
    2810      *
    2811      *  The options argument contains handler matching flags that affect how
    2812      *  matches are determined. Currently the only flag is matchBare (a
    2813      *  boolean). When matchBare is true, the from parameter and the from
    2814      *  attribute on the stanza will be matched as bare JIDs instead of
    2815      *  full JIDs. To use this, pass {matchBare: true} as the value of
    2816      *  options. The default value for matchBare is false.
    2817      *
    2818      *  The return value should be saved if you wish to remove the handler
    2819      *  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 options
    2829      *
    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: deleteHandler
    2841      *  Delete a stanza handler for a connection.
    2842      *
    2843      *  This function removes a stanza handler from the connection.  The
    2844      *  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 change
    2853         // the handlers during iteration
    2854         this.removeHandlers.push(handRef);
    2855         // If a handler is being deleted while it is being added,
    2856         // prevent it from getting added
    2857         var i = this.addHandlers.indexOf(handRef);
    2858         if (i >= 0) {
    2859             this.addHandlers.splice(i, 1);
    2860         }
    2861     },
    2862 
    2863     /** Function: disconnect
    2864      *  Start the graceful disconnection process.
    2865      *
    2866      *  This function starts the disconnection process.  This process starts
    2867      *  by sending unavailable presence and sending BOSH body of type
    2868      *  terminate.  A timeout handler makes sure that disconnection happens
    2869      *  even if the BOSH server does not respond.
    2870      *  If the Connection object isn't connected, at least tries to abort all pending requests
    2871      *  so the connection object won't generate successful requests (which were already opened).
    2872      *
    2873      *  The user supplied connection callback will be notified of the
    2874      *  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                     xmlns: Strophe.NS.CLIENT,
    2890                     type: 'unavailable'
    2891                 });
    2892             }
    2893             // setup timeout handler
    2894             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: _changeConnectStatus
    2904      *  _Private_ helper function that makes sure plugins and the user's
    2905      *  callback are notified of connection status changes.
    2906      *
    2907      *  Parameters:
    2908      *    (Integer) status - the new connection status, one of the values
    2909      *      in Strophe.Status
    2910      *    (String) condition - the error condition or null
    2911      */
    2912     _changeConnectStatus: function (status, condition)
    2913     {
    2914         // notify all plugins listening for status changes
    2915         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 callback
    2930         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: _doDisconnect
    2941      *  _Private_ function to disconnect.
    2942      *
    2943      *  This is the last piece of the disconnection logic.  This resets the
    2944      *  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 Timeout
    2953         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 handlers
    2966         this.handlers = [];
    2967         this.timedHandlers = [];
    2968         this.removeTimeds = [];
    2969         this.removeHandlers = [];
    2970         this.addTimeds = [];
    2971         this.addHandlers = [];
    2972 
    2973         // tell the parent we disconnected
    2974         this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);
    2975         this.connected = false;
    2976     },
    2977 
    2978     /** PrivateFunction: _dataRecv
    2979      *  _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.  This
    2983      *  function also fires stanza handlers that match each incoming
    2984      *  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.xmlInput !== Strophe.Connection.prototype.xmlInput) {
    2997             if (elem.nodeName === this._proto.strip && elem.childNodes.length) {
    2998                 this.xmlInput(elem.childNodes[0]);
    2999             } else {
    3000                 this.xmlInput(elem);
    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 deletion
    3012         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 addition
    3022         while (this.addHandlers.length > 0) {
    3023             this.handlers.push(this.addHandlers.pop());
    3024         }
    3025 
    3026         // handle graceful disconnect
    3027         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 disconnect
    3036             if (this.disconnecting) {
    3037                 return;
    3038             }
    3039 
    3040             // an error occurred
    3041             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 chain
    3056         var that = this;
    3057         Strophe.forEachChild(elem, null, function (child) {
    3058             var i, newList;
    3059             // process handlers
    3060             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 if
    3065                 // one of the handlers throws an exception
    3066                 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 false
    3077                     Strophe.warn('Removing Strophe handlers due to uncaught exception: ' + e.message);
    3078                 }
    3079             }
    3080         });
    3081     },
    3082 
    3083 
    3084     /** Attribute: mechanisms
    3085      *  SASL Mechanisms available for Conncection.
    3086      */
    3087     mechanisms: {},
    3088 
    3089     /** PrivateFunction: _connect_cb
    3090      *  _Private_ handler for initial connection request.
    3091      *
    3092      *  This handler is used to process the initial connection request
    3093      *  response from the BOSH server. It is used to set up authentication
    3094      *  handlers and start the authentication process.
    3095      *
    3096      *  SASL authentication will be attempted if available, otherwise
    3097      *  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.xmlInput !== Strophe.Connection.prototype.xmlInput) {
    3115             if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {
    3116                 this.xmlInput(bodyWrap.childNodes[0]);
    3117             } else {
    3118                 this.xmlInput(bodyWrap);
    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 tag
    3142         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: authenticate
    3174      * Set up authentication
    3175      *
    3176      *  Contiunues the initial connection request by setting up authentication
    3177      *  handlers and start the authentication process.
    3178      *
    3179      *  SASL authentication will be attempted if available, otherwise
    3180      *  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 mechanism
    3202       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           xmlns: Strophe.NS.SASL,
    3221           mechanism: this._sasl_mechanism.name
    3222         });
    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 worked
    3237         if (Strophe.getNodeFromJid(this.jid) === null) {
    3238             // we don't have a node, which is required for non-anonymous
    3239             // client connections
    3240             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 authentication
    3245           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             xmlns: Strophe.NS.AUTH
    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           xmlns: Strophe.NS.SASL
    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_cb
    3277      *  _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/> and
    3281      *  sends it, creating a handler (calling back to _auth2_cb()) to
    3282      *  handle the result
    3283      *
    3284      *  Parameters:
    3285      *    (XMLElement) elem - The stanza that triggered the callback.
    3286      *
    3287      *  Returns:
    3288      *    false to remove the handler.
    3289      */
    3290     /* jshint unused:false */
    3291     _auth1_cb: function (elem)
    3292     {
    3293         // build plaintext auth iq
    3294         var iq = $iq({type: "set", id: "_auth_2"})
    3295             .c('query', {xmlns: Strophe.NS.AUTH})
    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 pick
    3302             // a default one here.  unlike other auth methods, the server
    3303             // 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_cb
    3318      *  _Private_ handler for succesful SASL authentication.
    3319      *
    3320      *  Parameters:
    3321      *    (XMLElement) elem - The matching stanza.
    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 handlers
    3339               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 handlers
    3357         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 now
    3380         this._sendRestart();
    3381 
    3382         return false;
    3383     },
    3384 
    3385     /** PrivateFunction: _sasl_auth1_cb
    3386      *  _Private_ handler to start stream binding.
    3387      *
    3388      *  Parameters:
    3389      *    (XMLElement) elem - The matching stanza.
    3390      *
    3391      *  Returns:
    3392      *    false to remove the handler.
    3393      */
    3394     _sasl_auth1_cb: function (elem)
    3395     {
    3396         // save stream:features for future usage
    3397         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', {xmlns: Strophe.NS.BIND})
    3423                           .c('resource', {}).t(resource).tree());
    3424             } else {
    3425                 this.send($iq({type: "set", id: "_bind_auth_2"})
    3426                           .c('bind', {xmlns: Strophe.NS.BIND})
    3427                           .tree());
    3428             }
    3429         }
    3430 
    3431         return false;
    3432     },
    3433 
    3434     /** PrivateFunction: _sasl_bind_cb
    3435      *  _Private_ handler for binding result and session start.
    3436      *
    3437      *  Parameters:
    3438      *    (XMLElement) elem - The matching stanza.
    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 errors
    3456         var bind = elem.getElementsByTagName("bind");
    3457         var jidNode;
    3458         if (bind.length > 0) {
    3459             // Grab jid
    3460             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', {xmlns: Strophe.NS.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_cb
    3484      *  _Private_ handler to finish successful SASL connection.
    3485      *
    3486      *  This sets Connection.authenticated to true on success, which
    3487      *  starts the processing of user handlers.
    3488      *
    3489      *  Parameters:
    3490      *    (XMLElement) elem - The matching stanza.
    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_cb
    3510      *  _Private_ handler for SASL authentication failure.
    3511      *
    3512      *  Parameters:
    3513      *    (XMLElement) elem - The matching stanza.
    3514      *
    3515      *  Returns:
    3516      *    false to remove the handler.
    3517      */
    3518     /* jshint unused:false */
    3519     _sasl_failure_cb: function (elem)
    3520     {
    3521         // delete unneeded handlers
    3522         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_cb
    3539      *  _Private_ handler to finish legacy authentication.
    3540      *
    3541      *  This handler is called when the result from the jabber:iq:auth
    3542      *  <iq/> stanza is returned.
    3543      *
    3544      *  Parameters:
    3545      *    (XMLElement) elem - The stanza that triggered the callback.
    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: _addSysTimedHandler
    3564      *  _Private_ function to add a system level timed handler.
    3565      *
    3566      *  This function is used to add a Strophe.TimedHandler for the
    3567      *  library code.  System timed handlers are allowed to run before
    3568      *  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: _addSysHandler
    3583      *  _Private_ function to add a system level stanza handler.
    3584      *
    3585      *  This function is used to add a Strophe.Handler for the
    3586      *  library code.  System stanza handlers are allowed to run before
    3587      *  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: _onDisconnectTimeout
    3605      *  _Private_ timeout handler for handling non-graceful disconnection.
    3606      *
    3607      *  If the graceful disconnect process does not complete within the
    3608      *  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 disconnect
    3620         this._doDisconnect();
    3621 
    3622         return false;
    3623     },
    3624 
    3625     /** PrivateFunction: _onIdle
    3626      *  _Private_ handler to process events during idle cycle.
    3627      *
    3628      *  This handler is called every 100ms to fire timed handlers that
    3629      *  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 addition
    3636         // NOTE: we add before remove in the case a timed handler is
    3637         // 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 deletion
    3643         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 handlers
    3652         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 connected
    3674         if (this.connected) {
    3675             this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
    3676         }
    3677     }
    3678 };
    3679 
    3680 /** Class: Strophe.SASLMechanism
    3681  *
    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 on
    3686  *  how to disable a mechanism.
    3687  *
    3688  *  By default, all mechanisms are enabled and the priorities are
    3689  *
    3690  *  SCRAM-SHA1 - 40
    3691  *  DIGEST-MD5 - 30
    3692  *  Plain - 20
    3693  */
    3694 
    3695 /**
    3696  * PrivateConstructor: Strophe.SASLMechanism
    3697  * 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: name
    3709    *  Mechanism name.
    3710    */
    3711   this.name = name;
    3712   /** PrivateVariable: isClientFirst
    3713    *  If client sends response without initial server challenge.
    3714    */
    3715   this.isClientFirst = isClientFirst;
    3716   /** Variable: priority
    3717    *  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 are
    3721    *
    3722    *  SCRAM-SHA1 - 40
    3723    *  DIGEST-MD5 - 30
    3724    *  Plain - 20
    3725    *
    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: test
    3739    *  Checks if mechanism able to run.
    3740    *  To disable a mechanism, make this return false;
    3741    *
    3742    *  To disable plain authentication run
    3743    *  > 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: onStart
    3762    *  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: onChallenge
    3773    *  Called by protocol implementation on incoming challenge. If client is
    3774    *  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: onFailure
    3790    *  Protocol informs mechanism implementation about SASL failure.
    3791    */
    3792   onFailure: function() {
    3793     this._connection = null;
    3794   },
    3795 
    3796   /** PrivateFunction: onSuccess
    3797    *  Protocol informs mechanism implementation about SASL success.
    3798    */
    3799   onSuccess: function() {
    3800     this._connection = null;
    3801   }
    3802 };
    3803 
    3804   /** Constants: SASL mechanisms
    3805    *  Available authentication mechanisms
    3806    *
    3807    *  Strophe.SASLAnonymous - SASL Anonymous authentication.
    3808    *  Strophe.SASLPlain - SASL Plain authentication.
    3809    *  Strophe.SASLMD5 - SASL Digest-MD5 authentication
    3810    *  Strophe.SASLSHA1 - SASL SCRAM-SHA1 authentication
    3811    */
    3812 
    3813 // Building SASL callbacks
    3814 
    3815 /** PrivateConstructor: SASLAnonymous
    3816  *  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: SASLPlain
    3829  *  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: SASLSHA1
    3851  *  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 exchange
    3857  * when the client doesn't support channel bindings (username 'user' and
    3858  * password 'pencil' are used):
    3859  *
    3860  * C: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
    3861  * S: r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,
    3862  * i=4096
    3863  * 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 += "x00x00x00x01";
    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: SASLMD5
    3954  *  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: _quote
    3965  *  _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 string
    3972  */
    3973 Strophe.SASLMD5.prototype._quote = function (str)
    3974   {
    3975     return '"' + str.replace(/\/g, "\\").replace(/"/g, '\"') + '"';
    3976     //" end string workaround for emacs
    3977   };
    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, LLC
    4061 */
    4062 
    4063 /* jshint undef: true, unused: true:, noarg: true, latedef: true */
    4064 /* global define, window, setTimeout, clearTimeout, XMLHttpRequest, ActiveXObject, Strophe, $build */
    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.$build
    4072             );
    4073         });
    4074     } else {
    4075         // Browser globals
    4076         return factory(Strophe, $build);
    4077     }
    4078 }(this, function (Strophe, $build) {
    4079 
    4080 /** PrivateClass: Strophe.Request
    4081  *  _Private_ helper class that provides a cross implementation abstraction
    4082  *  for a BOSH related XMLHttpRequest.
    4083  *
    4084  *  The Strophe.Request class is used internally to encapsulate BOSH request
    4085  *  information.  It is not meant to be used from user's code.
    4086  */
    4087 
    4088 /** PrivateConstructor: Strophe.Request
    4089  *  Create and initialize a new Strophe.Request object.
    4090  *
    4091  *  Parameters:
    4092  *    (XMLElement) elem - The XML data to be sent in the request.
    4093  *    (Function) func - The function that will be called when the
    4094  *      XMLHttpRequest readyState changes.
    4095  *    (Integer) rid - The BOSH rid attribute associated with this request.
    4096  *    (Integer) sends - The number of times this same request has been
    4097  *      sent.
    4098  */
    4099 Strophe.Request = function (elem, func, rid, sends)
    4100 {
    4101     this.id = ++Strophe._requestId;
    4102     this.xmlData = elem;
    4103     this.data = Strophe.serialize(elem);
    4104     // save original function in case we need to make a new request
    4105     // 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: getResponse
    4129      *  Get a response from the underlying XMLHttpRequest.
    4130      *
    4131      *  This function attempts to get a response from the request and checks
    4132      *  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.responseXML && this.xhr.responseXML.documentElement) {
    4144             node = this.xhr.responseXML.documentElement;
    4145             if (node.tagName == "parsererror") {
    4146                 Strophe.error("invalid response received");
    4147                 Strophe.error("responseText: " + this.xhr.responseText);
    4148                 Strophe.error("responseXML: " +
    4149                               Strophe.serialize(this.xhr.responseXML));
    4150                 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("responseXML: " +
    4156                           Strophe.serialize(this.xhr.responseXML));
    4157         }
    4158 
    4159         return node;
    4160     },
    4161 
    4162     /** PrivateFunction: _newXHR
    4163      *  _Private_ helper function to create XMLHttpRequests.
    4164      *
    4165      *  This function creates XMLHttpRequests across all implementations.
    4166      *
    4167      *  Returns:
    4168      *    A new XMLHttpRequest.
    4169      */
    4170     _newXHR: function ()
    4171     {
    4172         var xhr = null;
    4173         if (window.XMLHttpRequest) {
    4174             xhr = new XMLHttpRequest();
    4175             if (xhr.overrideMimeType) {
    4176                 xhr.overrideMimeType("text/xml; charset=utf-8");
    4177             }
    4178         } else if (window.ActiveXObject) {
    4179             xhr = new ActiveXObject("Microsoft.XMLHTTP");
    4180         }
    4181 
    4182         // use Function.bind() to prepend ourselves as an argument
    4183         xhr.onreadystatechange = this.func.bind(null, this);
    4184 
    4185         return xhr;
    4186     }
    4187 };
    4188 
    4189 /** Class: Strophe.Bosh
    4190  *  _Private_ helper class that handles BOSH Connections
    4191  *
    4192  *  The Strophe.Bosh class is used internally by Strophe.Connection
    4193  *  to encapsulate BOSH sessions. It is not meant to be used from user's code.
    4194  */
    4195 
    4196 /** File: bosh.js
    4197  *  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.Bosh
    4205  *  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 values
    4221     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: strip
    4231      *
    4232      *  BOSH-Connections will have all stanzas wrapped in a <body> tag when
    4233      *  passed to <Strophe.Connection.xmlInput> or <Strophe.Connection.xmlOutput>.
    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 both
    4239      *  <Strophe.Connection.xmlInput> and <Strophe.Connection.xmlOutput>.
    4240      */
    4241     strip: null,
    4242 
    4243     /** PrivateFunction: _buildBody
    4244      *  _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             xmlns: Strophe.NS.HTTPBIND
    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: _reset
    4265      *  Reset the connection.
    4266      *
    4267      *  This function is called by the reset function of the Strophe Connection
    4268      */
    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: _connect
    4278      *  _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 tag
    4289         var body = this._buildBody().attrs({
    4290             to: this._conn.domain,
    4291             "xml:lang": "en",
    4292             wait: this.wait,
    4293             hold: this.hold,
    4294             content: "text/xml; charset=utf-8",
    4295             ver: "1.6",
    4296             "xmpp:version": "1.0",
    4297             "xmlns:xmpp": Strophe.NS.BOSH
    4298         });
    4299 
    4300         if(route){
    4301             body.attrs({
    4302                 route: route
    4303             });
    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: _attach
    4317      *  Attach to an already created and authenticated BOSH session.
    4318      *
    4319      *  This function is provided to allow Strophe to attach to BOSH
    4320      *  sessions which have been created externally, perhaps by a Web
    4321      *  application.  This is often used to support auto-login type features
    4322      *  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 RID
    4328      *      will be used by the next request.
    4329      *    (Function) callback The connect callback function.
    4330      *    (Integer) wait - The optional HTTPBIND wait value.  This is the
    4331      *      time the server will wait before returning an empty result for
    4332      *      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 the
    4335      *      number of connections the server will hold at one time.  This
    4336      *      should almost always be set to 1 (the default).
    4337      *    (Integer) wind - The optional HTTBIND window value.  This is the
    4338      *      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: _restore
    4361      *  Attempt to restore a cached BOSH session
    4362      *
    4363      *  Parameters:
    4364      *    (String) jid - The full JID that is bound by the session.
    4365      *      This parameter is optional but recommended, specifically in cases
    4366      *      where prebinded BOSH sessions are used where it's important to know
    4367      *      that the right session is being restored.
    4368      *    (Function) callback The connect callback function.
    4369      *    (Integer) wait - The optional HTTPBIND wait value.  This is the
    4370      *      time the server will wait before returning an empty result for
    4371      *      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 the
    4374      *      number of connections the server will hold at one time.  This
    4375      *      should almost always be set to 1 (the default).
    4376      *    (Integer) wind - The optional HTTBIND window value.  This is the
    4377      *      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: _cacheSession
    4397      *  _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.sid
    4411                 }));
    4412             }
    4413         } else {
    4414             window.sessionStorage.removeItem('strophe-bosh-session');
    4415         }
    4416     },
    4417 
    4418     /** PrivateFunction: _connect_cb
    4419      *  _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 occurred
    4431             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 is
    4447         // called multiple times in the case of missing stream:features
    4448         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: _disconnect
    4460      *  _Private_ part of Connection.disconnect for Bosh
    4461      *
    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: _doDisconnect
    4471      *  _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: _emptyQueue
    4483      * _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: _hitError
    4494      *  _Private_ function to handle the error count.
    4495      *
    4496      *  Requests are resent automatically until their error count reaches
    4497      *  5.  Each time an error is encountered, this function is called to
    4498      *  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_received
    4514      *
    4515      * Called on stream start/restart when no stream:features
    4516      * 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: _onDisconnectTimeout
    4535      *  _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: _abortAllRequests
    4544      *  _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 func
    4553             // is necessary for IE6
    4554             req.xhr.onreadystatechange = function () {}; // jshint ignore:line
    4555         }
    4556     },
    4557 
    4558     /** PrivateFunction: _onIdle
    4559      *  _Private_ handler called by Strophe.Connection._onIdle
    4560      *
    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, poll
    4567         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                             "xml:lang": "en",
    4586                             "xmpp:restart": "true",
    4587                             "xmlns:xmpp": Strophe.NS.BOSH
    4588                         });
    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: _onRequestStateChange
    4624      *  _Private_ handler for Strophe.Request state changes.
    4625      *
    4626      *  This function is called when the XMLHttpRequest readyState changes.
    4627      *  It contains a lot of error handling logic for the many ways that
    4628      *  requests can fail, and calls the request callback when requests
    4629      *  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 complete
    4647         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.  works
    4654                 // around a browser bug
    4655             }
    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 queue
    4673                 this._removeRequest(req);
    4674                 Strophe.debug("request id " +
    4675                               req.id +
    4676                               " should now be removed");
    4677             }
    4678 
    4679             // request succeeded
    4680             if (reqStatus == 200) {
    4681                 // if request 1 finished, or request 0 finished and request
    4682                 // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to
    4683                 // restart the other - both will be in the first spot, as the
    4684                 // completed request has been removed from the queue already
    4685                 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 handler
    4691                 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: _processRequest
    4720      *  _Private_ function to process a request in the queue.
    4721      *
    4722      *  This function takes requests off the queue and sends them and
    4723      *  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 retries
    4748         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 function
    4771             req.xhr.onreadystatechange = function () {};
    4772             this._requests[i] = new Strophe.Request(req.xmlData,
    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/xml; charset=utf-8");
    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 immediately
    4797             // or on a gradually expanding retry window for reconnects
    4798             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 instantaneous
    4813             if (req.sends > 1) {
    4814                 // Using a cube of the retry number creates a nicely
    4815                 // expanding retry window
    4816                 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.xmlOutput !== Strophe.Connection.prototype.xmlOutput) {
    4826                 if (req.xmlData.nodeName === this.strip && req.xmlData.childNodes.length) {
    4827                     this._conn.xmlOutput(req.xmlData.childNodes[0]);
    4828                 } else {
    4829                     this._conn.xmlOutput(req.xmlData);
    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: _removeRequest
    4844      *  _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 function
    4861         req.xhr.onreadystatechange = function () {};
    4862 
    4863         this._throttledRequestHandler();
    4864     },
    4865 
    4866     /** PrivateFunction: _restartRequest
    4867      *  _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: _reqToData
    4883      * _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: _sendTerminate
    4905      *  _Private_ function to send initial disconnect sequence.
    4906      *
    4907      *  This is the first step in a graceful disconnect.  It sends
    4908      *  the BOSH server a terminate body and includes an unavailable
    4909      *  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: _send
    4930      *  _Private_ part of the Connection.send function for BOSH
    4931      *
    4932      * Just triggers the RequestHandler to send the messages that are in the queue
    4933      */
    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: _sendRestart
    4941      *
    4942      *  Send an xmpp:restart stanza.
    4943      */
    4944     _sendRestart: function ()
    4945     {
    4946         this._throttledRequestHandler();
    4947         clearTimeout(this._conn._idleTimeout);
    4948     },
    4949 
    4950     /** PrivateFunction: _throttledRequestHandler
    4951      *  _Private_ function to throttle requests to the connection window.
    4952      *
    4953      *  This function makes sure we don't send requests so fast that the
    4954      *  request ids overflow the connection window in the case that one
    4955      *  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, LLC
    4990 */
    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.$build
    5001             );
    5002         });
    5003     } else {
    5004         // Browser globals
    5005         return factory(Strophe, $build);
    5006     }
    5007 }(this, function (Strophe, $build) {
    5008 
    5009 /** Class: Strophe.WebSocket
    5010  *  _Private_ helper class that handles WebSocket Connections
    5011  *
    5012  *  The Strophe.WebSocket class is used internally by Strophe.Connection
    5013  *  to encapsulate WebSocket sessions. It is not meant to be used from user's code.
    5014  */
    5015 
    5016 /** File: websocket.js
    5017  *  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/rfc7395
    5024  *
    5025  *  WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de)
    5026  */
    5027 
    5028 /** PrivateConstructor: Strophe.Websocket
    5029  *  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 absolute
    5046         // 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: _buildStream
    5069      *  _Private_ helper function to generate the <stream> start tag for WebSockets
    5070      *
    5071      *  Returns:
    5072      *    A Strophe.Builder with a <stream> element.
    5073      */
    5074     _buildStream: function ()
    5075     {
    5076         return $build("open", {
    5077             "xmlns": Strophe.NS.FRAMING,
    5078             "to": this._conn.domain,
    5079             "version": '1.0'
    5080         });
    5081     },
    5082 
    5083     /** PrivateFunction: _check_streamerror
    5084      * _Private_ checks a message for stream:error
    5085      *
    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:xml:ns:xmpp-streams";
    5108         for (var i = 0; i < error.childNodes.length; i++) {
    5109             var e = error.childNodes[i];
    5110             if (e.getAttribute("xmlns") !== 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_error
    5134         this._conn._changeConnectStatus(connectstatus, condition);
    5135         this._conn._doDisconnect();
    5136         return true;
    5137     },
    5138 
    5139     /** PrivateFunction: _reset
    5140      *  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: _connect
    5151      *  _Private_ function called by Strophe.Connection.connect
    5152      *
    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 WobSocket
    5161         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_cb
    5169      *  _Private_ function called by Strophe.Connection._connect_cb
    5170      *
    5171      * checks for stream:error
    5172      *
    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: _handleStreamStart
    5184      * _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 /> tag
    5195         var ns = message.getAttribute("xmlns");
    5196         if (typeof ns !== "string") {
    5197             error = "Missing xmlns in <open />";
    5198         } else if (ns !== Strophe.NS.FRAMING) {
    5199             error = "Wrong xmlns in <open />: " + 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_wrapper
    5219      * _Private_ function that handles the first connection messages.
    5220      *
    5221      * On receiving an opening stream tag this callback replaces itself with the real
    5222      * 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("<?xml") === 0) {
    5226             // Strip the XML Declaration, if there is one
    5227             var data = message.data.replace(/^(<?.*??>s*)*/, "");
    5228             if (data === '') return;
    5229 
    5230             var streamStart = new DOMParser().parseFromString(data, "text/xml").documentElement;
    5231             this._conn.xmlInput(streamStart);
    5232             this._conn.rawInput(message.data);
    5233 
    5234             //_handleStreamSteart will check for XML errors and disconnect on error
    5235             if (this._handleStreamStart(streamStart)) {
    5236                 //_connect_cb will check for stream:error and disconnect on error
    5237                 this._connect_cb(streamStart);
    5238             }
    5239         } else if (message.data.indexOf("<close ") === 0) { //'<close xmlns="urn:ietf:params:xml:ns:xmpp-framing />') {
    5240             this._conn.rawInput(message.data);
    5241             this._conn.xmlInput(message);
    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/xml").documentElement;
    5255             this.socket.onmessage = this._onMessage.bind(this);
    5256             this._conn._connect_cb(elem, null, message.data);
    5257         }
    5258     },
    5259 
    5260     /** PrivateFunction: _disconnect
    5261      *  _Private_ function called by Strophe.Connection.disconnect
    5262      *
    5263      *  Disconnects and sends a last stanza if one is given
    5264      *
    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", { "xmlns": Strophe.NS.FRAMING, });
    5275             this._conn.xmlOutput(close);
    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: _doDisconnect
    5288      *  _Private_ function to disconnect.
    5289      *
    5290      *  Just closes the Socket for WebSockets
    5291      */
    5292     _doDisconnect: function ()
    5293     {
    5294         Strophe.info("WebSockets _doDisconnect was called");
    5295         this._closeSocket();
    5296     },
    5297 
    5298     /** PrivateFunction _streamWrap
    5299      *  _Private_ helper function to wrap a stanza in a <stream> tag.
    5300      *  This is used so Strophe can process stanzas from WebSockets like BOSH
    5301      */
    5302     _streamWrap: function (stanza)
    5303     {
    5304         return "<wrapper>" + stanza + '</wrapper>';
    5305     },
    5306 
    5307 
    5308     /** PrivateFunction: _closeSocket
    5309      *  _Private_ function to close the WebSocket.
    5310      *
    5311      *  Closes the socket if it is still open and deletes it
    5312      */
    5313     _closeSocket: function ()
    5314     {
    5315         if (this.socket) { try {
    5316             this.socket.close();
    5317         } catch (e) {} }
    5318         this.socket = null;
    5319     },
    5320 
    5321     /** PrivateFunction: _emptyQueue
    5322      * _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: _onClose
    5333      * _Private_ function to handle websockets closing.
    5334      *
    5335      * Nothing to do here for WebSockets
    5336      */
    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_received
    5347      *
    5348      * Called on stream start/restart when no stream:features
    5349      * 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: _onDisconnectTimeout
    5363      *  _Private_ timeout handler for handling non-graceful disconnection.
    5364      *
    5365      *  This does nothing for WebSockets
    5366      */
    5367     _onDisconnectTimeout: function () {},
    5368 
    5369     /** PrivateFunction: _abortAllRequests
    5370      *  _Private_ helper function that makes sure all pending requests are aborted.
    5371      */
    5372     _abortAllRequests: function () {},
    5373 
    5374     /** PrivateFunction: _onError
    5375      * _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: _onIdle
    5387      *  _Private_ function called by Strophe.Connection._onIdle
    5388      *
    5389      *  sends all queued stanzas
    5390      */
    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.xmlOutput(stanza);
    5404                     this._conn.rawOutput(rawStanza);
    5405                     this.socket.send(rawStanza);
    5406                 }
    5407             }
    5408             this._conn._data = [];
    5409         }
    5410     },
    5411 
    5412     /** PrivateFunction: _onMessage
    5413      * _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' xml:lang='en' xmlns='jabber:client' xmlns:stream='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 stream
    5427         var close = '<close xmlns="urn:ietf:params:xml:ns:xmpp-framing" />';
    5428         if (message.data === close) {
    5429             this._conn.rawInput(close);
    5430             this._conn.xmlInput(message);
    5431             if (!this._conn.disconnecting) {
    5432                 this._conn._doDisconnect();
    5433             }
    5434             return;
    5435         } else if (message.data.search("<open ") === 0) {
    5436             // This handles stream restarts
    5437             elem = new DOMParser().parseFromString(message.data, "text/xml").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/xml").documentElement;
    5445         }
    5446 
    5447         if (this._check_streamerror(elem, Strophe.Status.ERROR)) {
    5448             return;
    5449         }
    5450 
    5451         //handle unavailable presence stanza before disconnecting
    5452         if (this._conn.disconnecting &&
    5453                 elem.firstChild.nodeName === "presence" &&
    5454                 elem.firstChild.getAttribute("type") === "unavailable") {
    5455             this._conn.xmlInput(elem);
    5456             this._conn.rawInput(Strophe.serialize(elem));
    5457             // if we are already disconnecting we will ignore the unavailable stanza and
    5458             // wait for the </stream:stream> tag before we close the connection
    5459             return;
    5460         }
    5461         this._conn._dataRecv(elem, message.data);
    5462     },
    5463 
    5464     /** PrivateFunction: _onOpen
    5465      * _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.xmlOutput(start.tree());
    5473 
    5474         var startString = Strophe.serialize(start);
    5475         this._conn.rawOutput(startString);
    5476         this.socket.send(startString);
    5477     },
    5478 
    5479     /** PrivateFunction: _reqToData
    5480      * _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: _send
    5496      *  _Private_ part of the Connection.send function for WebSocket
    5497      *
    5498      * Just flushes the messages that are in the queue
    5499      */
    5500     _send: function () {
    5501         this._conn.flush();
    5502     },
    5503 
    5504     /** PrivateFunction: _sendRestart
    5505      *
    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",{xmlns:"query-factorylogin"}).
       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
  • 相关阅读:
    Spring面试,IoC和AOP的理解
    WEB打印(jsp版)
    Spring事务管理机制的实现原理-动态代理
    spring面试题
    oracle PLSQL基础学习
    oracle创建表空间
    WM_CONCAT字符超过4000的处理办法
    Oracle 数据泵使用详解
    Oracle 数据泵详解
    linux下启动oracle
  • 原文地址:https://www.cnblogs.com/Anlycp/p/4904019.html
Copyright © 2011-2022 走看看