Commits

Ivan Vučica committed 31aa7c6

Facebook platform authentication.

Comments (0)

Files changed (7)

 		"domain": <?=isset($_GET["domain"]) ? '"' . $_GET["domain"] . '"' : "window.location.hostname"?>,
 		"boshwait": 15
 	}
+	var fbcfg = {
+		"bind-url": "punjab-bind/",
+		"route": "xmpp:chat.facebook.com:5222",
+		"domain": "chat.facebook.com",
+		"boshwait": 15
+	}
 	var cfg = <?=isset($_GET["cfg"]) ? $_GET["cfg"] : "relativecfg"?>;
 
 	zxmpp = new zxmppClass();

http_build_query.js

+function http_build_query (formdata, numeric_prefix, arg_separator) {
+  // http://kevin.vanzonneveld.net
+  // +   original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +   improved by: Legaev Andrey
+  // +   improved by: Michael White (http://getsprink.com)
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +   improved by: Brett Zamir (http://brett-zamir.me)
+  // +    revised by: stag019
+  // +   input by: Dreamer
+  // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
+  // +   bugfixed by: MIO_KODUKI (http://mio-koduki.blogspot.com/)
+  // %        note 1: If the value is null, key and value is skipped in http_build_query of PHP. But, phpjs is not.
+  // -    depends on: urlencode
+  // *     example 1: http_build_query({foo: 'bar', php: 'hypertext processor', baz: 'boom', cow: 'milk'}, '', '&amp;');
+  // *     returns 1: 'foo=bar&amp;php=hypertext+processor&amp;baz=boom&amp;cow=milk'
+  // *     example 2: http_build_query({'php': 'hypertext processor', 0: 'foo', 1: 'bar', 2: 'baz', 3: 'boom', 'cow': 'milk'}, 'myvar_');
+  // *     returns 2: 'php=hypertext+processor&myvar_0=foo&myvar_1=bar&myvar_2=baz&myvar_3=boom&cow=milk'
+  var value, key, tmp = [],
+    that = this;
+
+  var _http_build_query_helper = function (key, val, arg_separator) {
+    var k, tmp = [];
+    if (val === true) {
+      val = "1";
+    } else if (val === false) {
+      val = "0";
+    }
+    if (val != null) {
+      if(typeof val === "object") {
+        for (k in val) {
+          if (val[k] != null) {
+            tmp.push(_http_build_query_helper(key + "[" + k + "]", val[k], arg_separator));
+          }
+        }
+        return tmp.join(arg_separator);
+      } else if (typeof val !== "function") {
+        return that.urlencode(key) + "=" + that.urlencode(val);
+      } else {
+        throw new Error('There was an error processing for http_build_query().');
+      }
+    } else {
+      return '';
+    }
+  };
+
+  if (!arg_separator) {
+    arg_separator = "&";
+  }
+  for (key in formdata) {
+    value = formdata[key];
+    if (numeric_prefix && !isNaN(key)) {
+      key = String(numeric_prefix) + key;
+    }
+    var query=_http_build_query_helper(key, value, arg_separator);
+    if(query !== '') {
+      tmp.push(query);
+    }
+  }
+
+  return tmp.join(arg_separator);
+}
+function parse_str (str, array) {
+  // http://kevin.vanzonneveld.net
+  // +   original by: Cagri Ekin
+  // +   improved by: Michael White (http://getsprink.com)
+  // +    tweaked by: Jack
+  // +   bugfixed by: Onno Marsman
+  // +   reimplemented by: stag019
+  // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
+  // +   bugfixed by: stag019
+  // +   input by: Dreamer
+  // +   bugfixed by: Brett Zamir (http://brett-zamir.me)
+  // +   bugfixed by: MIO_KODUKI (http://mio-koduki.blogspot.com/)
+  // +   input by: Zaide (http://zaidesthings.com/)
+  // +   input by: David Pesta (http://davidpesta.com/)
+  // +   input by: jeicquest
+  // +   improved by: Brett Zamir (http://brett-zamir.me)
+  // %        note 1: When no argument is specified, will put variables in global scope.
+  // %        note 1: When a particular argument has been passed, and the returned value is different parse_str of PHP. For example, a=b=c&d====c
+  // *     example 1: var arr = {};
+  // *     example 1: parse_str('first=foo&second=bar', arr);
+  // *     results 1: arr == { first: 'foo', second: 'bar' }
+  // *     example 2: var arr = {};
+  // *     example 2: parse_str('str_a=Jack+and+Jill+didn%27t+see+the+well.', arr);
+  // *     results 2: arr == { str_a: "Jack and Jill didn't see the well." }
+  // *     example 3: var abc = {3:'a'};
+  // *     example 3: parse_str('abc[a][b]["c"]=def&abc[q]=t+5');
+  // *     results 3: JSON.stringify(abc) === '{"3":"a","a":{"b":{"c":"def"}},"q":"t 5"}';
+
+
+  var strArr = String(str).replace(/^&/, '').replace(/&$/, '').split('&'),
+    sal = strArr.length,
+    i, j, ct, p, lastObj, obj, lastIter, undef, chr, tmp, key, value,
+    postLeftBracketPos, keys, keysLen,
+    fixStr = function (str) {
+      return decodeURIComponent(str.replace(/\+/g, '%20'));
+    };
+
+  if (!array) {
+    array = this.window;
+  }
+
+  for (i = 0; i < sal; i++) {
+    tmp = strArr[i].split('=');
+    key = fixStr(tmp[0]);
+    value = (tmp.length < 2) ? '' : fixStr(tmp[1]);
+
+    while (key.charAt(0) === ' ') {
+      key = key.slice(1);
+    }
+    if (key.indexOf('\x00') > -1) {
+      key = key.slice(0, key.indexOf('\x00'));
+    }
+    if (key && key.charAt(0) !== '[') {
+      keys = [];
+      postLeftBracketPos = 0;
+      for (j = 0; j < key.length; j++) {
+        if (key.charAt(j) === '[' && !postLeftBracketPos) {
+          postLeftBracketPos = j + 1;
+        }
+        else if (key.charAt(j) === ']') {
+          if (postLeftBracketPos) {
+            if (!keys.length) {
+              keys.push(key.slice(0, postLeftBracketPos - 1));
+            }
+            keys.push(key.substr(postLeftBracketPos, j - postLeftBracketPos));
+            postLeftBracketPos = 0;
+            if (key.charAt(j + 1) !== '[') {
+              break;
+            }
+          }
+        }
+      }
+      if (!keys.length) {
+        keys = [key];
+      }
+      for (j = 0; j < keys[0].length; j++) {
+        chr = keys[0].charAt(j);
+        if (chr === ' ' || chr === '.' || chr === '[') {
+          keys[0] = keys[0].substr(0, j) + '_' + keys[0].substr(j + 1);
+        }
+        if (chr === '[') {
+          break;
+        }
+      }
+
+      obj = array;
+      for (j = 0, keysLen = keys.length; j < keysLen; j++) {
+        key = keys[j].replace(/^['"]/, '').replace(/['"]$/, '');
+        lastIter = j !== keys.length - 1;
+        lastObj = obj;
+        if ((key !== '' && key !== ' ') || j === 0) {
+          if (obj[key] === undef) {
+            obj[key] = {};
+          }
+          obj = obj[key];
+        }
+        else { // To insert new dimension
+          ct = -1;
+          for (p in obj) {
+            if (obj.hasOwnProperty(p)) {
+              if (+p > ct && p.match(/^\d+$/g)) {
+                ct = +p;
+              }
+            }
+          }
+          key = ct + 1;
+        }
+      }
+      lastObj[key] = value;
+    }
+  }
+}
 
 		'zxmpp_authplain.js',
 		'zxmpp_authdigestmd5.js',
-		'zxmpp_authanonymous.js'
+		'zxmpp_authanonymous.js',
+		'zxmpp_authfb.js'
 		);
 }
 
 	$scripts[] = 'md5_2.js';
 	$scripts[] = 'sdptojingle.js';
 
+	// phpjs functions, for authfb:
+	$scripts[] = 'parse_str.js';
+	$scripts[] = 'http_build_query.js';
+	$scripts[] = 'urlencode.js'; // http_build_query depends on this
+
 	return $scripts;
 }
 
+function urlencode (str) {
+  // http://kevin.vanzonneveld.net
+  // +   original by: Philip Peterson
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +      input by: AJ
+  // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +   improved by: Brett Zamir (http://brett-zamir.me)
+  // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +      input by: travc
+  // +      input by: Brett Zamir (http://brett-zamir.me)
+  // +   bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
+  // +   improved by: Lars Fischer
+  // +      input by: Ratheous
+  // +      reimplemented by: Brett Zamir (http://brett-zamir.me)
+  // +   bugfixed by: Joris
+  // +      reimplemented by: Brett Zamir (http://brett-zamir.me)
+  // %          note 1: This reflects PHP 5.3/6.0+ behavior
+  // %        note 2: Please be aware that this function expects to encode into UTF-8 encoded strings, as found on
+  // %        note 2: pages served as UTF-8
+  // *     example 1: urlencode('Kevin van Zonneveld!');
+  // *     returns 1: 'Kevin+van+Zonneveld%21'
+  // *     example 2: urlencode('http://kevin.vanzonneveld.net/');
+  // *     returns 2: 'http%3A%2F%2Fkevin.vanzonneveld.net%2F'
+  // *     example 3: urlencode('http://www.google.nl/search?q=php.js&ie=utf-8&oe=utf-8&aq=t&rls=com.ubuntu:en-US:unofficial&client=firefox-a');
+  // *     returns 3: 'http%3A%2F%2Fwww.google.nl%2Fsearch%3Fq%3Dphp.js%26ie%3Dutf-8%26oe%3Dutf-8%26aq%3Dt%26rls%3Dcom.ubuntu%3Aen-US%3Aunofficial%26client%3Dfirefox-a'
+  str = (str + '').toString();
+
+  // Tilde should be allowed unescaped in future versions of PHP (as reflected below), but if you want to reflect current
+  // PHP behavior, you would need to add ".replace(/~/g, '%7E');" to the following.
+  return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').
+  replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+');
+}
+/* 
+ * Z-XMPP
+ * A Javascript XMPP client.
+ *
+ * (c) 2010 Ivan Vucica
+ * License is located in the LICENSE file
+ * in Z-XMPP distribution/repository.
+ * Use not complying to those terms is a
+ * violation of various national and
+ * international copyright laws.
+ */
+
+
+// Facebook authentication mechanism
+// Specify appid as username and accesstoken as password.
+
+zxmppClass.prototype.authFB = function (zxmpp)
+{
+	this.zxmpp = zxmpp;
+	this.type = "authFB";
+
+
+	this.hasSentAuth = false;
+	this.authSuccess = undefined;
+	
+	this.startAuth = function zxmppAuthFB_startAuth()
+	{	
+		if(!this.hasSentAuth)
+		{
+			
+			if(this.zxmpp.stream.features["urn:ietf:params:xml:ns:xmpp-sasl"] && 
+			   this.zxmpp.stream.features["urn:ietf:params:xml:ns:xmpp-sasl"]["mechanisms"]["set"]["X-FACEBOOK-PLATFORM"])
+			{
+				this.sendFBAuth("poll");
+			}
+			else
+			{
+				zxmppConsole.error("zxmpp::authFB::doStep(): x-facebook-platform authentication mechanism unsupported. giving up");
+
+				this.zxmpp.stream.terminate();
+	
+				var code = "terminate/no-supported-auth";
+				var humanreadable = "No supported authentication mechanism provided by the server.";
+				this.zxmpp.notifyConnectionTerminate(code, humanreadable);
+
+			}
+			
+		}
+	}
+	this.decodeChallenge = function zxmppAuthFB_decodeChallenge(xml)
+	{
+		if(!xml || !xml.firstChild)
+		{
+			zxmppConsole.error("zxmpp::authFB::handleChallenge: no challenge content");
+			this.zxmpp.stream.terminate();
+			this.zxmpp.notifyConnectionTerminate("terminate/invalid-sasl-challenge", "Server has sent an invalid SASL challenge.");
+			return;
+		}
+
+		var content = xml.firstChild;
+		if(!content.nodeValue)
+		{
+			zxmppConsole.error("zxmpp::authFB::handleChallenge: no challenge content nodevalue");
+			this.zxmpp.stream.terminate();
+			this.zxmpp.notifyConnectionTerminate("terminate/invalid-sasl-challenge", "Server has sent an invalid SASL challenge.");
+			return;
+		}
+
+		content = content.nodeValue;
+		content = this.zxmpp.util.decode64(content);
+		if(!content)
+		{
+			// TODO copypaste code from above for throwing error
+			zxmppConsole.error("zxmpp::authFB::handleChallenge: could not base64decode challenge content");
+			this.zxmpp.stream.terminate();
+			this.zxmpp.notifyConnectionTerminate("terminate/invalid-sasl-challenge", "Server has sent an invalid SASL challenge.");
+			return;
+		}
+
+		var challenge_array = {};
+		parse_str(content, challenge_array);
+		return challenge_array;
+
+	}
+	this.handleChallenge = function zxmppAuthFB_handleChallenge(xml)
+	{
+		var contentDict = this.decodeChallenge(xml);
+		var response = {
+			'method' : contentDict['method'],
+			'nonce' : contentDict['nonce'],
+			'api_key' : this.zxmpp.username,
+			'access_token' : this.zxmpp.password,
+			'call_id' : '0',
+			'v' : '1.0'
+		};
+		response = http_build_query(response);
+		this.sendResponseBody(response);
+	}
+	this.sendResponseBody = function zxmppAuthFB_sendResponseBody(responseBody)
+	{
+		// prepare auth send
+		var packet = new this.zxmpp.packet(this.zxmpp);
+		
+		var authnode = packet.xml.createElementNS("urn:ietf:params:xml:ns:xmpp-sasl", "response");
+		packet.xml_body.appendChild(authnode);
+
+		// create child text node
+		if(responseBody)
+		{
+			var authnode_text = packet.xml.createTextNode(
+				this.zxmpp.util.encode64(
+					responseBody
+				));
+			authnode.appendChild(authnode_text);
+		}
+
+		// send
+		packet.send();
+	}
+	this.handleSuccess = function zxmppAuthFB_handleSuccess(xml)
+	{
+		this.authSuccess = true;
+	}
+	this.handleFailure = function zxmppAuthFB_handleFailure(xml)
+	{
+		this.authSuccess = false;
+	}
+	this.handleAbort = function zxmppAuthFB_handleAbort(xml)
+	{
+		this.authSuccess = false;
+	}
+	this.sendFBAuth = function zxmppAuthFB_sendFBAuth()
+	{
+		// send authorization
+		var packet = new this.zxmpp.packet(this.zxmpp);
+		
+		var authnode = packet.xml.createElementNS("urn:ietf:params:xml:ns:xmpp-sasl", "auth");
+		authnode.setAttribute("mechanism", "X-FACEBOOK-PLATFORM");
+		packet.xml_body.appendChild(authnode);
+
+		this.hasSentAuth=true;
+		packet.send();
+	}
+	this.authSucceeded = function zxmppAuthFB_authSucceeded()
+	{
+		return this.authSuccess;
+	}
+	this.toJSON = function zxmppAuthFB_toJSON(key)
+	{
+
+		oldzxmpp = this.zxmpp;
+		//oldcontent = this.oldcontent;
+
+		delete this.zxmpp;
+		//delete this.oldcontent;
+		
+		var oldfuncs = {};
+		for(var i in this)
+		{
+			if(typeof this[i] == "function")
+			{
+				oldfuncs[i] = this[i];
+				delete this[i];
+			}
+		}
+		var ret = oldzxmpp.util.cloneObject(this);
+
+		this.zxmpp = oldzxmpp;
+		//this.content = oldcontent;
+		
+		for(var i in oldfuncs)
+		{
+			this[i] = oldfuncs[i];
+		}
+
+		this.zxmpp.util.describeWhatCantYouStringify("zxmppAuthFB_toJSON()", ret)
+		return ret;
+	}
+
+	this.wakeUp = function zxmppAuthFB_wakeUp()
+	{
+	}
+}
 				'ANONYMOUS' : this.zxmpp.authAnonymous,
 				'PLAIN' : this.zxmpp.authPlain,
 				'DIGEST-MD5' : this.zxmpp.authDigestMD5,
+				'X-FACEBOOK-PLATFORM': this.zxmpp.authFB
 			};
 
 			// Weighted, ordered list of supported mechanisms.
 				for (var mechanismId in mechanisms)
 				{
 					var mechanism = mechanisms[mechanismId];
+					zxmppConsole.log("Was offered " + mechanism);
 					if(knownMechanisms[mechanism])
 					{
 						supportedMechanisms.push([mechanism, knownMechanisms[mechanism]]);
+						zxmppConsole.log("Matched SASL mechanism: " + mechanism);
 					}
 				}
 			}