Permalink
Browse files

overhaul architecture for toolbar. more full-fledged xpcom service ru…

…ns in

the background for the duration of the browser session, polling facebook every
5 minutes.  next up: notification service to establish communication between
back-end and front-end.
  • Loading branch information...
Ari Steinberg
Ari Steinberg committed Oct 6, 2006
1 parent 40ddc33 commit 098a0859aad1cb37f76bbe5eceeca2f504e439c7
View
@@ -1,30 +1,26 @@
-function FacebookRestClient() {
- this.settings = Components.classes['@facebook.com/simple-service;1']
- .getService()
- .QueryInterface(Components.interfaces.fbISimpleFacebookService);
+// Note that this file is intended for login-related API function calls only
+// (ie facebook.auth.*). Other API calls go through the Facebook xpcom service.
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+function FacebookLoginClient() {
+ this.fbSvc = Cc['@facebook.com/facebook-service;1'].getService().QueryInterface(Ci.fbIFacebookService);
}
-FacebookRestClient.prototype = {
- generateSig: function (params, useSessionSecret) {
+FacebookLoginClient.prototype = {
+ generateSig: function (params) {
var str = '';
params.sort();
for (var i = 0; i < params.length; i++) {
str += params[i];
}
- if (useSessionSecret) {
- str += this.settings.sessionSecret;
- } else {
- str += this.settings.secret;
- }
+ str += this.fbSvc.secret;
return MD5(str);
},
callMethod: function (method, params, callback) {
- var sessionMethod = (method.indexOf('facebook.auth') == -1);
params.push('method=' + method);
- params.push('session_key=' + this.settings.sessionKey);
- params.push('api_key=' + this.settings.apiKey);
+ params.push('api_key=' + this.fbSvc.apiKey);
params.push('call_id=' + (new Date()).getTime());
- params.push('sig=' + this.generateSig(params, sessionMethod));
+ params.push('sig=' + this.generateSig(params));
var req = new XMLHttpRequest();
req.onreadystatechange = function (event) {
@@ -44,17 +40,188 @@ FacebookRestClient.prototype = {
}
};
try {
- if (sessionMethod) {
- var protocol = 'http';
- } else {
- var protocol = 'https';
- }
- req.open('POST', protocol + '://api.facebook.com/restserver.php', true);
+ req.open('POST', 'https://api.facebook.com/restserver.php', true);
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
req.send(params.join('&'));
} catch (e) {
- dump(e);
+ dump('Exception sending REST request: ' + e + '\n');
}
},
+};
+
+/* =====MD5 CODE FROM HERE DOWN=====
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Copyright (C) Paul Johnston 1999 - 2000.
+ * Updated by Greg Holt 2000 - 2001.
+ * See http://pajhome.org.uk/site/legal.html for details.
+ */
+
+/*
+ * Convert a 32-bit number to a hex string with ls-byte first
+ */
+var hex_chr = "0123456789abcdef";
+function rhex(num)
+{
+ str = "";
+ for(j = 0; j <= 3; j++)
+ str += hex_chr.charAt((num >> (j * 8 + 4)) & 0x0F) +
+ hex_chr.charAt((num >> (j * 8)) & 0x0F);
+ return str;
+}
+
+/*
+ * Convert a string to a sequence of 16-word blocks, stored as an array.
+ * Append padding bits and the length, as described in the MD5 standard.
+ */
+function str2blks_MD5(str)
+{
+ nblk = ((str.length + 8) >> 6) + 1;
+ blks = new Array(nblk * 16);
+ for(i = 0; i < nblk * 16; i++) blks[i] = 0;
+ for(i = 0; i < str.length; i++)
+ blks[i >> 2] |= str.charCodeAt(i) << ((i % 4) * 8);
+ blks[i >> 2] |= 0x80 << ((i % 4) * 8);
+ blks[nblk * 16 - 2] = str.length * 8;
+ return blks;
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function add(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
+ */
+function rol(num, cnt)
+{
+ return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * These functions implement the basic operation for each round of the
+ * algorithm.
+ */
+function cmn(q, a, b, x, s, t)
+{
+ return add(rol(add(add(a, q), add(x, t)), s), b);
}
+function ff(a, b, c, d, x, s, t)
+{
+ return cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function gg(a, b, c, d, x, s, t)
+{
+ return cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function hh(a, b, c, d, x, s, t)
+{
+ return cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function ii(a, b, c, d, x, s, t)
+{
+ return cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Take a string and return the hex representation of its MD5.
+ */
+function MD5(str)
+{
+ x = str2blks_MD5(str);
+ var a = 1732584193;
+ var b = -271733879;
+ var c = -1732584194;
+ var d = 271733878;
+
+ for(i = 0; i < x.length; i += 16)
+ {
+ var olda = a;
+ var oldb = b;
+ var oldc = c;
+ var oldd = d;
+
+ a = ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+ d = ff(d, a, b, c, x[i+ 1], 12, -389564586);
+ c = ff(c, d, a, b, x[i+ 2], 17, 606105819);
+ b = ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+ a = ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+ d = ff(d, a, b, c, x[i+ 5], 12, 1200080426);
+ c = ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+ b = ff(b, c, d, a, x[i+ 7], 22, -45705983);
+ a = ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
+ d = ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+ c = ff(c, d, a, b, x[i+10], 17, -42063);
+ b = ff(b, c, d, a, x[i+11], 22, -1990404162);
+ a = ff(a, b, c, d, x[i+12], 7 , 1804603682);
+ d = ff(d, a, b, c, x[i+13], 12, -40341101);
+ c = ff(c, d, a, b, x[i+14], 17, -1502002290);
+ b = ff(b, c, d, a, x[i+15], 22, 1236535329);
+
+ a = gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+ d = gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+ c = gg(c, d, a, b, x[i+11], 14, 643717713);
+ b = gg(b, c, d, a, x[i+ 0], 20, -373897302);
+ a = gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+ d = gg(d, a, b, c, x[i+10], 9 , 38016083);
+ c = gg(c, d, a, b, x[i+15], 14, -660478335);
+ b = gg(b, c, d, a, x[i+ 4], 20, -405537848);
+ a = gg(a, b, c, d, x[i+ 9], 5 , 568446438);
+ d = gg(d, a, b, c, x[i+14], 9 , -1019803690);
+ c = gg(c, d, a, b, x[i+ 3], 14, -187363961);
+ b = gg(b, c, d, a, x[i+ 8], 20, 1163531501);
+ a = gg(a, b, c, d, x[i+13], 5 , -1444681467);
+ d = gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+ c = gg(c, d, a, b, x[i+ 7], 14, 1735328473);
+ b = gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+ a = hh(a, b, c, d, x[i+ 5], 4 , -378558);
+ d = hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+ c = hh(c, d, a, b, x[i+11], 16, 1839030562);
+ b = hh(b, c, d, a, x[i+14], 23, -35309556);
+ a = hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+ d = hh(d, a, b, c, x[i+ 4], 11, 1272893353);
+ c = hh(c, d, a, b, x[i+ 7], 16, -155497632);
+ b = hh(b, c, d, a, x[i+10], 23, -1094730640);
+ a = hh(a, b, c, d, x[i+13], 4 , 681279174);
+ d = hh(d, a, b, c, x[i+ 0], 11, -358537222);
+ c = hh(c, d, a, b, x[i+ 3], 16, -722521979);
+ b = hh(b, c, d, a, x[i+ 6], 23, 76029189);
+ a = hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+ d = hh(d, a, b, c, x[i+12], 11, -421815835);
+ c = hh(c, d, a, b, x[i+15], 16, 530742520);
+ b = hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+ a = ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+ d = ii(d, a, b, c, x[i+ 7], 10, 1126891415);
+ c = ii(c, d, a, b, x[i+14], 15, -1416354905);
+ b = ii(b, c, d, a, x[i+ 5], 21, -57434055);
+ a = ii(a, b, c, d, x[i+12], 6 , 1700485571);
+ d = ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+ c = ii(c, d, a, b, x[i+10], 15, -1051523);
+ b = ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+ a = ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
+ d = ii(d, a, b, c, x[i+15], 10, -30611744);
+ c = ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+ b = ii(b, c, d, a, x[i+13], 21, 1309151649);
+ a = ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+ d = ii(d, a, b, c, x[i+11], 10, -1120210379);
+ c = ii(c, d, a, b, x[i+ 2], 15, 718787259);
+ b = ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+ a = add(a, olda);
+ b = add(b, oldb);
+ c = add(c, oldc);
+ d = add(d, oldd);
+ }
+ return rhex(a) + rhex(b) + rhex(c) + rhex(d);
+}
+
dump('loaded facebook.js\n');
View
@@ -1,49 +1,45 @@
-var client = new FacebookRestClient();
+function debug(s) { dump('** login.js: ' + s + '\n'); }
+var client = new FacebookLoginClient();
function startup() {
- if (client.settings.sessionKey) {
- dump('already logged in!\n');
+ if (client.fbSvc.loggedIn) {
+ debug('already logged in!');
window.close();
- } else if (!client.settings.authToken) {
- client.settings.apiKey = '64f19267b0e6177ea503046d801c00df';
- client.settings.secret = 'a8a5a57a9f9cd57473797c4612418908';
- dump('requesting token\n');
+ } else if (!client.authToken) {
+ debug('requesting token');
try {
client.callMethod('facebook.auth.createToken', [], function(req) {
- dump('received token response:\n');
+ debug('received token response:');
dump(req.responseText);
- client.settings.authToken = req.xmldata.token;
- dump('token is: '+client.settings.authToken+'\n');
+ client.authToken = req.xmldata.token;
+ debug('token is: '+client.authToken);
startup();
});
} catch (e) {
- dump('exception: ' + e + '\n');
+ debug('exception: ' + e);
}
} else {
document.getElementById('facebook-login-body').
setAttribute('src', 'http://api.facebook.com/login.php?api_key=' +
- client.settings.apiKey + '&auth_token=' +
- client.settings.authToken);
- dump('loading login page\n');
+ client.fbSvc.apiKey + '&auth_token=' + client.authToken);
+ debug('loading login page');
}
}
window.addEventListener('load', startup, false);
function done() {
- dump('done called\n');
- client.callMethod('facebook.auth.getSession', ['auth_token='+client.settings.authToken], function(req) {
- dump('received session response:\n');
+ debug('done()');
+ client.callMethod('facebook.auth.getSession', ['auth_token='+client.authToken], function(req) {
+ debug('received session response:');
dump(req.responseText);
- client.settings.sessionKey = req.xmldata.session_key;
- client.settings.uid = req.xmldata.uid;
- client.settings.sessionSecret = req.xmldata.secret;
- client.settings.authToken = null;
- dump('session: ' + client.settings.sessionKey + ', uid: ' + client.settings.uid + ', secret: ' + client.settings.sessionSecret + '\n');
+ var sessionKey = req.xmldata.session_key;
+ var sessionSecret = req.xmldata.secret;
+ var uid = req.xmldata.uid;
+ client.fbSvc.sessionStart(sessionKey, sessionSecret, uid);
+ client.authToken = null;
+ debug('session: ' + sessionKey + ', uid: ' + uid + ', secret: ' + sessionSecret);
window.setTimeout("window.close();",1); // for some reason calling window.close directly does not work
- parent.getElementById('facebook-panel').label = 'logged in!';
+ parent.getElementById('facebook-panel').label = 'logged in!'; // XXX will be handled by observer
});
}
-function ready() {
- dump('readystatechanged\n');
-}
-dump('loaded login.js\n');
+debug('loaded login.js');
View
@@ -1,6 +1,9 @@
-var client = new FacebookRestClient();
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+
+var fbSvc = Cc['@facebook.com/facebook-service;1'].getService().QueryInterface(Ci.fbIFacebookService);
function startup() {
- if (client.settings.sessionKey) {
+ if (fbSvc.loggedIn) {
document.getElementById('facebook-panel').label = 'logged in!';
} else {
document.getElementById('facebook-panel').label = 'logged out';
@@ -9,16 +12,16 @@ function startup() {
window.addEventListener('load', startup, false);
function FacebookLogin(statusElem) {
- if (client.settings.sessionKey) {
+ if (fbSvc.loggedIn) {
dump('logging out\n');
- client.settings.sessionKey = null;
- document.getElementById('facebook-panel').label = 'logged out';
+ fbSvc.sessionEnd();
+ document.getElementById('facebook-panel').label = 'logged out'; // XXX: move to signal facebook-session-end
} else {
window.open('chrome://facebook/content/login.xul', '',
'chrome,centerscreen,width=780,height=500,modal=yes,dialog=yes,close=no');
// since the above is modal the following won't get run until the dialog is closed
- if (client.settings.sessionKey) {
- document.getElementById('facebook-panel').label = 'logged in!';
+ if (fbSvc.loggedIn) {
+ document.getElementById('facebook-panel').label = 'logged in!'; // XXX: move to signal facebook-session-start
}
}
}
@@ -1,7 +1,5 @@
<?xml version="1.0"?>
<overlay id="facebook" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
- <script src="chrome://facebook/content/md5.js"/>
- <script src="chrome://facebook/content/facebook.js"/>
<script src="chrome://facebook/content/sample.js"/>
<statusbar id="status-bar">
<statusbarpanel id="facebook-panel" onmousedown="if (event.button == 0) FacebookLogin(this)" />
View
@@ -0,0 +1,37 @@
+#include "nsISupports.idl"
+
+/*
+[scriptable, uuid(c4051391-2b3a-427d-8cd0-8398d168858d)]
+interface fbIFacebookCallListener : nsISupports
+{
+ void onFail(in string result);
+ void onSuccess(in string result);
+};
+*/
+
+[scriptable, uuid(19baf81b-d1bd-4a99-9010-dd6f478bfd22)]
+interface fbIFacebookService : nsISupports
+{
+ readonly attribute AString apiKey;
+ readonly attribute AString secret;
+
+ // login.xul is responsible for performing the login sequence. Once that
+ // is finished, it should call sessionStart, which initiates the polling
+ // process. If the session ever ends, the polling will stop until
+ // sessionStart is called again. Aside from the facebook.auth methods,
+ // all other Facebook API calls should be done inside this service.
+ void sessionStart(in AString sessionKey, in AString sessionSecret, in AString uid);
+ void sessionEnd();
+
+ // All other interaction comes by windows listing themselves as observers
+ // and notifications that get triggered by the polling process.
+ // Notifications that we fire:
+ // facebook-session-start
+ // facebook-session-end
+ // facebook-new-message
+ // facebook-new-poke
+ // Windows can also query the most recent status of these things via the
+ // readonly attributes below:
+ readonly attribute boolean loggedIn;
+ readonly attribute long numMsgs;
+};
Oops, something went wrong.

0 comments on commit 098a085

Please sign in to comment.