/* * jquery.xmpp.js * * Copyright 2011 Alvaro Garcia * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ (function($) { // Intercept XHRs coming from IE8+ and send via XDomainRequest. $.ajaxTransport("+*", function( options, originalOptions, jqXHR ) { // If this is IE and XDomainRequest is supported. if(navigator.appVersion.indexOf("MSIE") != -1 && window.XDomainRequest) { var xdr; return { send: function( headers, completeCallback ) { // Use Microsoft XDR xdr = new XDomainRequest(); // Open the remote URL. xdr.open("post", options.url); xdr.onload = function() { completeCallback(200, "success", [this.responseText]); }; xdr.ontimeout = function(){ completeCallback(408, "error", ["The request timed out."]); }; xdr.onerror = function(errorText){ completeCallback(404, "error: " + errorText, ["The requested resource could not be found."]); }; // Submit the data to the site. xdr.send(options.data); }, abort: function() { if(xdr)xdr.abort(); } }; } }); $.xmpp ={ rid:null, sid:null, jid:null, url: null, uri: null, listening: false, onMessage: null, onIq: null, onPresence: null, onError: null, connections: 0, resource: null, connected: false, checkNetorkErrors: false, wait: 60, inactivity: 60, _jsTimeout: null, //Used to save the javascript timeout _timeoutMilis: 500, __lastAjaxRequest: null, /** * Connect to the server * @params Object * {jid:"user@domain.com", * password:"qwerty", * resource:"Chat", * url:"/http-bind", * wait: 60, * inactivity: 60, * onDisconnect:function(){}, * onConnect: function(data){}, * onIq: function(iq){}, * onMessage: function(message){}, * onPresence: function(presence){} * onError: function(error, data){} * } */ connect: function(options){ this.rid = Math.round(Math.random()*Math.pow(10,10)); this.jid = options.jid; var split = options.jid.split("@"); var domain = split[1]; var xmpp = this; if(options.url == null) this.url = '/http-bind' else this.url = options.url; if(options.checkNetorkErrors != null) this.checkNetorkErrors = options.checkNetorkErrors if(!isNaN(options.wait)){ this.wait = options.wait; } this._timeoutMilis = xmpp.wait * 1000; if(!isNaN(options.inactivity)){ this.inactivity = options.inactivity } this.uri = this.jid; if(options.resource == null) this.resource = ""; else{ this.resource = options.resource; this.uri += "/" + this.resource; } //Events this.onMessage = options.onMessage; this.onIq = options.onIq; this.onPresence = options.onPresence; this.onError = options.onError; this.onDisconnect = options.onDisconnect; this.onConnect = options.onConnect; //Init connection var msg = ""; $.post(this.url,msg,function(data){ var response = $(xmpp.fixBody(data)); xmpp.sid = response.attr("sid"); if(response.find("mechanism:contains('PLAIN')").length){ xmpp.loginPlain(options); }else if(response.find("mechanism:contains('DIGEST-MD5')").length){ xmpp.loginDigestMD5(options); }else{ if(xmpp.onError != null){ xmpp.onError({error:"No auth method supported", data:data}); } } }, 'text'); }, /** * Attach to existing session * @params Object * {jid:"", * sid:"", * rid:"", * resource:"", * url:"", * wait: 60, * onDisconnect:function(){}, * onConnect: function(data){}, * onIq: function(iq){}, * onMessage: function(message){}, * onPresence: function(presence){} * onError: function(error, data){} * } */ attach: function(options){ this.jid = options.jid; this.sid = options.sid; this.rid = options.rid; var xmpp = this; if(options.url == null) this.url = '/http-bind' else this.url = options.url; this.uri = this.jid; if(options.resource == null) this.resource = ""; else{ this.resource = options.resource; this.uri += "/" + this.resource; } //Events this.onMessage = options.onMessage; this.onIq = options.onIq; this.onPresence = options.onPresence; this.onError = options.onError; this.onDisconnect = options.onDisconnect; this.onConnect = options.onConnect; if(!isNaN(options.wait)){ this.wait = options.wait } this._timeoutMilis = xmpp.wait * 1000; if(options.onConnect != null) xmpp.connected = true; options.onConnect(); xmpp.listen(); }, /** * Disconnect from the server using synchronous Ajax * @params function callback */ disconnectSync: function(callback){ var xmpp = this; xmpp.rid = xmpp.rid + 1; this.listening = true; xmpp.connections = xmpp.connections + 1; var msg = ""; $.ajax({ type: 'POST', url: this.url, data: msg, success: function(data){ xmpp.connections = xmpp.connections - 1; xmpp.messageHandler(data); xmpp.listening = false; //Do not listen anymore! //Two callbacks if(callback != null) callback(data); if(xmpp.onDisconnect != null) xmpp.connected = false; xmpp.onDisconnect(data); }, dataType: 'text', async:false }); }, /** * Disconnect from the server * @params function callback */ disconnect: function(callback){ var xmpp = this; xmpp.rid = xmpp.rid + 1; this.listening = true; xmpp.connections = xmpp.connections + 1; var msg = ""; $.post(this.url,msg,function(data){ xmpp.connections = xmpp.connections - 1; xmpp.messageHandler(data); xmpp.listening = false; //Do not listen anymore! //Two callbacks if(callback != null) callback(data); if(xmpp.onDisconnect != null) xmpp.connected = false; xmpp.onDisconnect(data); }, 'text'); }, /** * Do a MD5 Digest authentication */ loginDigestMD5: function(options){ var xmpp = this; this.rid++; var msg = ""; $.post(this.url,msg,function(data){ var response = $(data); var split = options.jid.split("@"); var domain = split[1]; var username = split[0]; //Code bases on Strophe var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/; var challenge = Base64.decode(response.text()); var cnonce = MD5.hexdigest("" + (Math.random() * 1234567890)); var realm = ""; var host = null; var nonce = ""; var qop = ""; var matches; while (challenge.match(attribMatch)) { matches = challenge.match(attribMatch); challenge = challenge.replace(matches[0], ""); matches[2] = matches[2].replace(/^"(.+)"$/, "$1"); switch (matches[1]) { case "realm": realm = matches[2]; break; case "nonce": nonce = matches[2]; break; case "qop": qop = matches[2]; break; case "host": host = matches[2]; break; } } var digest_uri = "xmpp/" + domain; if (host !== null) { digest_uri = digest_uri + "/" + host; } var A1 = MD5.hash(username + ":" + realm + ":" + options.password) + ":" + nonce + ":" + cnonce; var A2 = 'AUTHENTICATE:' + digest_uri; var responseText = ""; responseText += 'username=' + xmpp._quote(username) + ','; responseText += 'realm=' + xmpp._quote(realm) + ','; responseText += 'nonce=' + xmpp._quote(nonce) + ','; responseText += 'cnonce=' + xmpp._quote(cnonce) + ','; responseText += 'nc="00000001",'; responseText += 'qop="auth",'; responseText += 'digest-uri=' + xmpp._quote(digest_uri) + ','; responseText += 'response=' + xmpp._quote( MD5.hexdigest(MD5.hexdigest(A1) + ":" + nonce + ":00000001:" + cnonce + ":auth:" + MD5.hexdigest(A2))) + ','; responseText += 'charset="utf-8"'; xmpp.rid++; var msg =""+Base64.encode(responseText)+""; $.post(this.url,msg,function(data){ var response = $(xmpp.fixBody(data)); if(!response.find("failure").length){ xmpp.rid++; var msg =""; $.post(this.url,msg,function(data){ var response = $(xmpp.fixBody(data)); if(response.find("success").length){ xmpp.rid++; var msg =""; $.post(this.url,msg,function(data){ xmpp.rid++; var msg ="" + xmpp.resource +""; $.post(this.url,msg,function(data){ xmpp.rid++; var msg = ""; $.post(this.url,msg,function(data){ if(options.onConnect != null) xmpp.connected = true; options.onConnect(data); xmpp.listen(); }, 'text'); }, 'text'); }, 'text'); }else{ if(xmpp.onError != null) xmpp.onError({error: "Invalid credentials", data:data}); } }, 'text'); }else{ if(xmpp.onError != null) xmpp.onError({error: "Invalid credentials", data:data}); } }, 'text'); }, 'text'); }, /** * Returns the quoted string * @prams string * @return quoted string */ _quote: function(string){ return '"'+string+'"'; }, /** * Do a plain authentication */ loginPlain: function(options){ this.rid++; var split = options.jid.split("@"); var user = split[0]; var domain = split[1]; var xmpp = this; var text = ""+Base64.encode(this.jid+"\u0000"+user+"\u0000"+options.password)+""; var url = this.url; $.post(this.url,text,function(data){ var response = $(xmpp.fixBody(data)); if(response.find("success").length){ xmpp.rid++; text =""; $.post(url,text,function(data){ //xmpp.messageHandler(data); xmpp.rid++; text ="" + xmpp.resource +""; $.post(url,text,function(data){ //xmpp.messageHandler(data); xmpp.rid++; text = ""; $.post(url,text,function(data){ if(options.onConnect != null) xmpp.connected = true; options.onConnect(data); xmpp.listen(); }, 'text'); }, 'text'); }, 'text'); }else{ if(options.onError != null) options.onError({error: "Invalid credentials", data:data}); } }, 'text'); }, /** * Disconnected cause a network problem */ __networkError: function(){ //Notify the errors and change the state to disconnected if($.xmpp.onError != null){ $.xmpp.onError({error:"Network error"}) } if($.xmpp.onDisconnect != null){ $.xmpp.onDisconnect() } $.xmpp.__lastAjaxRequest.abort(); $.xmpp.connections = $.xmpp.connections - 1; $.xmpp.listening = false; $.xmpp.connected = false }, /** * Wait for a new event */ listen: function(){ var xmpp = this; if(!this.listening){ this.listening = true; xmpp = this; if(xmpp.connections === 0) { //To detect networks problems if(xmpp.checkNetorkErrors){ clearTimeout(xmpp._jsTimeout); xmpp._jsTimeout = setTimeout(xmpp.__networkError,xmpp._timeoutMilis); } // this.rid = this.rid+1; xmpp.connections = xmpp.connections + 1; xmpp.__lastAjaxRequest = $.ajax({ type: "POST", url: this.url, data: "", success: function(data){ xmpp.connections = xmpp.connections - 1; xmpp.listening = false; var body = $(xmpp.fixBody(data)); //When timeout the connections are 0 //When listener is aborted because you send message (or something) // the body children are 0 but connections are > 0 if(body.children().length > 0) { xmpp.messageHandler(data); } if ( xmpp.connections === 0 ) { xmpp.listen(); } }, error: function(XMLHttpRequest, textStatus, errorThrown) { if(xmpp.onError != null){ xmpp.onError({error: errorThrown, data:textStatus}); } }, dataType: 'text' }); } } }, /** * Send a raw command * @params String Raw command as plain text * @params callback function callback */ sendCommand: function(rawCommand, callback){ var self = this; this.rid = this.rid + 1; this.listening = true; this.connections = this.connections + 1; var command = ""+ rawCommand+""; $.post(self.url,command,function(data){ self.connections = self.connections - 1; self.messageHandler(data); self.listening = false; self.listen(); if(callback != null) callback(data); }, 'text'); }, /** * Send a text message * @params Object * {body: "Hey dude!", * to: "someone@somewhere.com" * resource: "Chat", * otherAttr: "value" * } * @params data: Extra information such errors * @params callback: function(){} */ sendMessage: function(options, data, callback){ var toJid = options.to; var body = options.body; if(options.resource != null) toJid = toJid+"/"+options.resource; else if(this.resource != "") toJid = toJid+"/"+this.resource; //Remove used paramteres delete options.to; delete options.body; delete options.resource; //Other data var dataObj = $(""); dataObj.append(data); //Add all parameters to the message var messageObj = $("bodyCont"); messageObj.find("message").attr(options); //Use raw text because jquery "destroy" the body tag var message = messageObj.html().split("bodyCont"); var msg = message[0]+""+body+""+dataObj.html()+""; this.sendCommand(msg,callback); }, /** * Change the presence * @params String The common presences are: null, away, dnd * @params callback: function(){} */ setPresence: function(type, callback){ var msg; if(type == null) msg = ""; else msg = ""+type+""; this.sendCommand(msg,callback); }, /** * Get if you are connected */ isConnected: function(){ return this.connected; }, /** * Do a roster request */ getRoster: function(callback){ //Some XMPP server send a blank list. Use onIq in these cases var msg = ""; this.sendCommand(msg,function(data){ var roster = []; $.each($(data).find("item"), function(i,item){ var jItem = $(item); roster.push({name: jItem.attr("name"), subscription: jItem.attr("subscription"), jid: jItem.attr("jid")}); }); if(callback) callback(roster); }); }, /** * Get who is online * When presence it received the event onPresence is triggered */ getPresence: function(){ var msg = ""; var self = this; this.sendCommand(msg,function(data){ self.messageHandler(data,self) }); }, messageHandler: function(data, context){ var xmpp = this; var response = $(xmpp.fixBody(data)); $.each(response.find("message"),function(i,element){ try{ var e = $(element); xmpp.onMessage({from: e.attr("from"), body: e.find(".body").html(),attributes:e[0].attributes,data:response.children()}); }catch(e){} }); $.each(response.find("iq"),function(i,element){ if(xmpp.onIq){ var e = $(element); if(e.find('ping').length==1){ xmpp.handlePing(e); } else{ xmpp.onIq(element); } } }); $.each(response.find("presence"),function(i,element){ try{ var e = $(element); var status; if(e.attr("type") != null){ status = e.attr("type") }else{ status = e.find("show").html() } xmpp.onPresence({from: e.attr("from"), to: e.attr("to"), show: status}); }catch(e){} }); }, /** * Replaces tags because jquery does not "parse" this tag * @params String * @return String */ fixBody: function(html){ html = html.replace(/<\/body>/ig, "") html = html.replace(/"); } } })(jQuery); //Dependencies, you can use an external file // This code was written by Tyler Akins and has been placed in the // public domain. It would be nice if you left this header intact. // Base64 code from Tyler Akins -- http://rumkin.com var Base64 = (function () { var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; var obj = { /** * Encodes a string in base64 * @param {String} input The string to encode in base64. */ encode: function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; do { chr1 = input.charCodeAt(i++); chr2 = input.charCodeAt(i++); chr3 = input.charCodeAt(i++); enc1 = chr1 >> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); enc4 = chr3 & 63; if (isNaN(chr2)) { enc3 = enc4 = 64; } else if (isNaN(chr3)) { enc4 = 64; } output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + keyStr.charAt(enc3) + keyStr.charAt(enc4); } while (i < input.length); return output; }, /** * Decodes a base64 string. * @param {String} input The string to decode. */ decode: function (input) { var output = ""; var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; var i = 0; // remove all characters that are not A-Z, a-z, 0-9, +, /, or = input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); do { enc1 = keyStr.indexOf(input.charAt(i++)); enc2 = keyStr.indexOf(input.charAt(i++)); enc3 = keyStr.indexOf(input.charAt(i++)); enc4 = keyStr.indexOf(input.charAt(i++)); chr1 = (enc1 << 2) | (enc2 >> 4); chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; output = output + String.fromCharCode(chr1); if (enc3 != 64) { output = output + String.fromCharCode(chr2); } if (enc4 != 64) { output = output + String.fromCharCode(chr3); } } while (i < input.length); return output; } }; return obj; })(); /* * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message * Digest Algorithm, as defined in RFC 1321. * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002. * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet * Distributed under the BSD License * See http://pajhome.org.uk/crypt/md5 for more info. */ var MD5 = (function () { /* * Configurable variables. You may need to tweak these to be compatible with * the server-side, but the defaults work in most cases. */ var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ var safe_add = function (x, y) { var lsw = (x & 0xFFFF) + (y & 0xFFFF); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xFFFF); }; /* * Bitwise rotate a 32-bit number to the left. */ var bit_rol = function (num, cnt) { return (num << cnt) | (num >>> (32 - cnt)); }; /* * Convert a string to an array of little-endian words * If chrsz is ASCII, characters >255 have their hi-byte silently ignored. */ var str2binl = function (str) { var bin = []; var mask = (1 << chrsz) - 1; for(var i = 0; i < str.length * chrsz; i += chrsz) { bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32); } return bin; }; /* * Convert an array of little-endian words to a string */ var binl2str = function (bin) { var str = ""; var mask = (1 << chrsz) - 1; for(var i = 0; i < bin.length * 32; i += chrsz) { str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); } return str; }; /* * Convert an array of little-endian words to a hex string. */ var binl2hex = function (binarray) { var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; var str = ""; for(var i = 0; i < binarray.length * 4; i++) { str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF); } return str; }; /* * Convert an array of little-endian words to a base-64 string */ var binl2b64 = function (binarray) { var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var str = ""; var triplet, j; for(var i = 0; i < binarray.length * 4; i += 3) { triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); for(j = 0; j < 4; j++) { if(i * 8 + j * 6 > binarray.length * 32) { str += b64pad; } else { str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); } } } return str; }; /* * These functions implement the four basic operations the algorithm uses. */ var md5_cmn = function (q, a, b, x, s, t) { return safe_add(bit_rol(safe_add(safe_add(a, q),safe_add(x, t)), s),b); }; var md5_ff = function (a, b, c, d, x, s, t) { return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); }; var md5_gg = function (a, b, c, d, x, s, t) { return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); }; var md5_hh = function (a, b, c, d, x, s, t) { return md5_cmn(b ^ c ^ d, a, b, x, s, t); }; var md5_ii = function (a, b, c, d, x, s, t) { return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); }; /* * Calculate the MD5 of an array of little-endian words, and a bit length */ var core_md5 = function (x, len) { /* append padding */ x[len >> 5] |= 0x80 << ((len) % 32); x[(((len + 64) >>> 9) << 4) + 14] = len; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; var olda, oldb, oldc, oldd; for (var i = 0; i < x.length; i += 16) { olda = a; oldb = b; oldc = c; oldd = d; a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); c = md5_ff(c, d, a, b, x[i+10], 17, -42063); b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); a = safe_add(a, olda); b = safe_add(b, oldb); c = safe_add(c, oldc); d = safe_add(d, oldd); } return [a, b, c, d]; }; /* * Calculate the HMAC-MD5, of a key and some data */ var core_hmac_md5 = function (key, data) { var bkey = str2binl(key); if(bkey.length > 16) { bkey = core_md5(bkey, key.length * chrsz); } var ipad = new Array(16), opad = new Array(16); for(var i = 0; i < 16; i++) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5C5C5C5C; } var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); return core_md5(opad.concat(hash), 512 + 128); }; var obj = { /* * These are the functions you'll usually want to call. * They take string arguments and return either hex or base-64 encoded * strings. */ hexdigest: function (s) { return binl2hex(core_md5(str2binl(s), s.length * chrsz)); }, b64digest: function (s) { return binl2b64(core_md5(str2binl(s), s.length * chrsz)); }, hash: function (s) { return binl2str(core_md5(str2binl(s), s.length * chrsz)); }, hmac_hexdigest: function (key, data) { return binl2hex(core_hmac_md5(key, data)); }, hmac_b64digest: function (key, data) { return binl2b64(core_hmac_md5(key, data)); }, hmac_hash: function (key, data) { return binl2str(core_hmac_md5(key, data)); }, /* * Perform a simple self-test to see if the VM is working */ test: function () { return MD5.hexdigest("abc") === "900150983cd24fb0d6963f7d28e17f72"; } }; return obj; })();