Skip to content
This repository has been archived by the owner on Jan 13, 2022. It is now read-only.

Commit

Permalink
overhaul architecture for toolbar. more full-fledged xpcom service ru…
Browse files Browse the repository at this point in the history
…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 committed Oct 6, 2006
1 parent 40ddc33 commit 098a085
Show file tree
Hide file tree
Showing 11 changed files with 664 additions and 204 deletions.
211 changes: 189 additions & 22 deletions chrome/content/facebook.js
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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');
50 changes: 23 additions & 27 deletions chrome/content/login.js
Original file line number Diff line number Diff line change
@@ -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');
17 changes: 10 additions & 7 deletions chrome/content/sample.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions chrome/content/sample.xul
Original file line number Diff line number Diff line change
@@ -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)" />
Expand Down
37 changes: 37 additions & 0 deletions components/facebook.idl
Original file line number Diff line number Diff line change
@@ -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;
};
Loading

0 comments on commit 098a085

Please sign in to comment.