diff --git a/Gruntfile.js b/Gruntfile.js index de090db..772ef9e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -86,17 +86,17 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-jshint'); - grunt.registerTask('freedom-firefox', [ + grunt.registerTask('build', [ 'jshint:providers', 'uglify' ]); grunt.registerTask('writeJsonDir', 'Write', writeJsonDir); grunt.registerTask('build_test', [ - 'freedom-firefox', + 'build', 'copy:test', 'writeJsonDir' ]); - grunt.registerTask('default', ['freedom-firefox']); + grunt.registerTask('default', ['build']); // Write the contents of the data directory in the test extension // into a JSON file. We have to do this because files/directories diff --git a/package.json b/package.json index d721253..111b33b 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "freedom-for-firefox", "description": "Embracing a distributed web", - "version": "0.4.1", + "version": "0.4.2", "homepage": "http://freedomjs.org", "bugs": { "url": "http://github.com/freedomjs/freedom-for-firefox/issues", @@ -17,7 +17,7 @@ "url": "https://github.com/freedomjs/freedom-for-firefox" }, "devDependencies": { - "freedom": "~0.5.0", + "freedom": "~0.5.5", "grunt": "~0.4.2", "es5-shim": "^3.1.1", "es6-promise": "~0.1.1", @@ -27,7 +27,7 @@ "grunt-contrib-jshint": "~0.8.0" }, "peerDependencies": { - "freedom": "~0.5.0" + "freedom": "~0.5.5" }, "scripts": {} } diff --git a/providers/client_socket.js b/providers/client_socket.js index ee205a9..f9ca5c7 100644 --- a/providers/client_socket.js +++ b/providers/client_socket.js @@ -75,13 +75,15 @@ ClientSocket.prototype._setupTransport = function(transport) { }; -ClientSocket.prototype.connect = function(hostname, port) { +ClientSocket.prototype.connect = function(hostname, port, startTls) { if (typeof this.transport !== 'undefined') { throw new Error('Socket already connected'); } - var transport = socketTransportService.createTransport([null], - 0, + var socketTypes = startTls ? ['starttls'] : [null]; + var numSocketTypes = startTls ? 1 : 0; + var transport = socketTransportService.createTransport(socketTypes, + numSocketTypes, hostname, port, null); diff --git a/providers/tcp_socket.js b/providers/tcp_socket.js index 5759d9a..604822c 100644 --- a/providers/tcp_socket.js +++ b/providers/tcp_socket.js @@ -25,10 +25,36 @@ Socket_firefox.prototype.close = function(continuation) { continuation(); }; +// TODO: handle failures. Socket_firefox.prototype.connect = function(hostname, port, continuation) { this.clientSocket = new ClientSocket(); this.clientSocket.setOnDataListener(this._onData.bind(this)); - this.clientSocket.connect(hostname, port); + this.clientSocket.connect(hostname, port, false); + this.hostname = hostname; + this.port = port; + continuation(); +}; + +// TODO: handle failures. +Socket_firefox.prototype.secure = function(continuation) { + if (!this.hostname || !this.port || !this.clientSocket) { + continuation(undefined, { + "errcode": "SOCKET_NOT_CONNECTED", + "message": "Cannot Secure Not Connected Socket" + }); + return; + } + // Create a new ClientSocket (nsISocketTransport) object for the existing + // hostname and port, using type 'starttls'. This will upgrade the existing + // connection to TLS, rather than create a new connection. + // TODO: check to make sure this doesn't result in weird race conditions if + // we have 2 pieces of code both trying to connect to the same hostname/port + // and do a starttls flow (e.g. if there are 2 instances of a GTalk social + // provider that are both trying to connect to GTalk simultaneously with + // different logins). + this.clientSocket = new ClientSocket(); + this.clientSocket.setOnDataListener(this._onData.bind(this)); + this.clientSocket.connect(this.hostname, this.port, true); continuation(); }; diff --git a/test/data/firefox_tests/tcp_socket.spec.js b/test/data/firefox_tests/tcp_socket.spec.js index b59b6e9..46ff4fd 100644 --- a/test/data/firefox_tests/tcp_socket.spec.js +++ b/test/data/firefox_tests/tcp_socket.spec.js @@ -1,4 +1,4 @@ -describe("sockets", function() { + describe("tcp sockets", function() { var clientSocket, serverSocket; beforeEach(function() { serverSocket = new ServerSocket("localhost", 8081); @@ -11,7 +11,7 @@ describe("sockets", function() { serverSocket.disconnect(); done(); }; - clientSocket.connect("localhost", 8081); + clientSocket.connect("localhost", 8081, false); }); it("receives data", function(done) { @@ -24,7 +24,7 @@ describe("sockets", function() { done(); }); }; - clientSocket.connect("localhost", 8081); + clientSocket.connect("localhost", 8081, false); clientSocket.write(str2ab(stringMessage)); }); @@ -36,4 +36,44 @@ describe("sockets", function() { } return buf; } + function ab2str(buf) { + return String.fromCharCode.apply(null, new Uint8Array(buf)); + }; + + it("secures socket with starttls", function(done) { + var INIT_XMPP = ''; + var START_TLS = ''; + var X_FACEBOOK_PLATFORM_AUTH = + ''; + + // Test that we can connect to chat.facebook.com, then upgrade to a tls + // socket and get the challenge. If we fail to upgrade the socket to tls + // facebook will not return a challenge. + var onDataCount = 0; + var dispatchEvent = function (eventType, data) { + if (eventType == 'onData') { + var xmlString = ab2str(data.data); + if (xmlString.indexOf('= 0) { + done(); + } + ++onDataCount; + if (onDataCount == 1) { + socket.write(str2ab(START_TLS), continuation); + } else if (onDataCount == 2) { + socket.secure(continuation); + socket.write(str2ab(INIT_XMPP), continuation); + } else if (onDataCount == 3) { + socket.write(str2ab(X_FACEBOOK_PLATFORM_AUTH), continuation); + } + } + }; + var continuation = function() {}; + var socket = new Socket_firefox(undefined, dispatchEvent, undefined); + socket.connect('chat.facebook.com', 5222, continuation); + socket.write(str2ab(INIT_XMPP), continuation); + }); }); diff --git a/test/lib/main.js b/test/lib/main.js index cc5cb4a..e68398e 100644 --- a/test/lib/main.js +++ b/test/lib/main.js @@ -11,11 +11,13 @@ const jasmine = require("jasmine.js"); var providers = ["firefox_providers/client_socket.js", "firefox_providers/server_socket.js", + "firefox_providers/tcp_socket.js", "firefox_providers/udp_socket.js"]; var tests = [ "firefox_tests/client_socket.spec.js", - "firefox_tests/udp_socket.spec.js" + "firefox_tests/udp_socket.spec.js", + "firefox_tests/tcp_socket.spec.js" ];